quota.c revision 1.34 1 /* $NetBSD: quota.c,v 1.34 2011/03/06 17:08:42 bouyer Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
38 The Regents of the University of California. All rights reserved.");
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)quota.c 8.4 (Berkeley) 4/28/95";
44 #else
45 __RCSID("$NetBSD: quota.c,v 1.34 2011/03/06 17:08:42 bouyer Exp $");
46 #endif
47 #endif /* not lint */
48
49 /*
50 * Disk quota reporting program.
51 */
52 #include <sys/param.h>
53 #include <sys/types.h>
54 #include <sys/file.h>
55 #include <sys/stat.h>
56 #include <sys/mount.h>
57 #include <sys/socket.h>
58
59 #include <ctype.h>
60 #include <err.h>
61 #include <errno.h>
62 #include <fstab.h>
63 #include <grp.h>
64 #include <netdb.h>
65 #include <pwd.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <time.h>
70 #include <unistd.h>
71
72 #include <ufs/ufs/quota2.h>
73 #include <ufs/ufs/quota1.h>
74
75 #include <rpc/rpc.h>
76 #include <rpc/pmap_prot.h>
77 #include <rpcsvc/rquota.h>
78
79 #include <printquota.h>
80 #include <getvfsquota.h>
81
82 struct quotause {
83 struct quotause *next;
84 long flags;
85 struct quota2_entry q2e;
86 char fsname[MAXPATHLEN + 1];
87 };
88 #define FOUND 0x01
89 #define QUOTA2 0x02
90
91 int alldigits(char *);
92 int callaurpc(char *, int, int, int, xdrproc_t, void *, xdrproc_t, void *);
93 int main(int, char **);
94 int getnfsquota(struct statvfs *, struct fstab *, struct quotause *,
95 long, int);
96 struct quotause *getprivs(long id, int quotatype);
97 void heading(int, u_long, const char *, const char *);
98 void showgid(gid_t);
99 void showgrpname(const char *);
100 void showquotas(int, u_long, const char *);
101 void showuid(uid_t);
102 void showusrname(const char *);
103 void usage(void);
104
105 int qflag = 0;
106 int vflag = 0;
107 int hflag = 0;
108 int dflag = 0;
109 int Dflag = 0;
110 uid_t myuid;
111
112 int
113 main(argc, argv)
114 int argc;
115 char *argv[];
116 {
117 int ngroups;
118 gid_t mygid, gidset[NGROUPS];
119 int i, gflag = 0, uflag = 0;
120 int ch;
121
122 myuid = getuid();
123 while ((ch = getopt(argc, argv, "Ddhugvq")) != -1) {
124 switch(ch) {
125 case 'g':
126 gflag++;
127 break;
128 case 'u':
129 uflag++;
130 break;
131 case 'v':
132 vflag++;
133 break;
134 case 'q':
135 qflag++;
136 break;
137 case 'h':
138 hflag++;
139 break;
140 case 'd':
141 dflag++;
142 break;
143 case 'D':
144 Dflag++;
145 break;
146 default:
147 usage();
148 }
149 }
150 argc -= optind;
151 argv += optind;
152 if (!uflag && !gflag)
153 uflag++;
154 if (dflag) {
155 #if 0
156 if (myuid != 0) {
157 printf("quota: -d: permission denied\n");
158 exit(1);
159 }
160 #endif
161 if (uflag)
162 showquotas(USRQUOTA, 0, "");
163 if (gflag)
164 showquotas(GRPQUOTA, 0, "");
165 exit(0);
166 }
167 if (argc == 0) {
168 if (uflag)
169 showuid(myuid);
170 if (gflag) {
171 if (dflag)
172 showgid(0);
173 else {
174 mygid = getgid();
175 ngroups = getgroups(NGROUPS, gidset);
176 if (ngroups < 0)
177 err(1, "getgroups");
178 showgid(mygid);
179 for (i = 0; i < ngroups; i++)
180 if (gidset[i] != mygid)
181 showgid(gidset[i]);
182 }
183 }
184 exit(0);
185 }
186 if (uflag && gflag)
187 usage();
188 if (uflag) {
189 for (; argc > 0; argc--, argv++) {
190 if (alldigits(*argv))
191 showuid(atoi(*argv));
192 else
193 showusrname(*argv);
194 }
195 exit(0);
196 }
197 if (gflag) {
198 for (; argc > 0; argc--, argv++) {
199 if (alldigits(*argv))
200 showgid(atoi(*argv));
201 else
202 showgrpname(*argv);
203 }
204 exit(0);
205 }
206 /* NOTREACHED */
207 return (0);
208 }
209
210 void
211 usage()
212 {
213
214 fprintf(stderr, "%s\n%s\n%s\n%s\n",
215 "usage: quota [-Dhguqv]",
216 "\tquota [-Dhqv] -u username ...",
217 "\tquota [-Dhqv] -g groupname ...",
218 "\tquota -d [-Dhguqv]");
219 exit(1);
220 }
221
222 /*
223 * Print out quotas for a specified user identifier.
224 */
225 void
226 showuid(uid)
227 uid_t uid;
228 {
229 struct passwd *pwd = getpwuid(uid);
230 const char *name;
231
232 if (pwd == NULL)
233 name = "(no account)";
234 else
235 name = pwd->pw_name;
236 if (uid != myuid && myuid != 0) {
237 printf("quota: %s (uid %d): permission denied\n", name, uid);
238 return;
239 }
240 showquotas(USRQUOTA, uid, name);
241 }
242
243 /*
244 * Print out quotas for a specified user name.
245 */
246 void
247 showusrname(name)
248 const char *name;
249 {
250 struct passwd *pwd = getpwnam(name);
251
252 if (pwd == NULL) {
253 warnx("%s: unknown user", name);
254 return;
255 }
256 if (pwd->pw_uid != myuid && myuid != 0) {
257 warnx("%s (uid %d): permission denied", name, pwd->pw_uid);
258 return;
259 }
260 showquotas(USRQUOTA, pwd->pw_uid, name);
261 }
262
263 /*
264 * Print out quotas for a specified group identifier.
265 */
266 void
267 showgid(gid)
268 gid_t gid;
269 {
270 struct group *grp = getgrgid(gid);
271 int ngroups;
272 gid_t mygid, gidset[NGROUPS];
273 int i;
274 const char *name;
275
276 if (grp == NULL)
277 name = "(no entry)";
278 else
279 name = grp->gr_name;
280 mygid = getgid();
281 ngroups = getgroups(NGROUPS, gidset);
282 if (ngroups < 0) {
283 warn("getgroups");
284 return;
285 }
286 if (gid != mygid) {
287 for (i = 0; i < ngroups; i++)
288 if (gid == gidset[i])
289 break;
290 if (i >= ngroups && myuid != 0) {
291 warnx("%s (gid %d): permission denied", name, gid);
292 return;
293 }
294 }
295 showquotas(GRPQUOTA, gid, name);
296 }
297
298 /*
299 * Print out quotas for a specified group name.
300 */
301 void
302 showgrpname(name)
303 const char *name;
304 {
305 struct group *grp = getgrnam(name);
306 int ngroups;
307 gid_t mygid, gidset[NGROUPS];
308 int i;
309
310 if (grp == NULL) {
311 warnx("%s: unknown group", name);
312 return;
313 }
314 mygid = getgid();
315 ngroups = getgroups(NGROUPS, gidset);
316 if (ngroups < 0) {
317 warn("getgroups");
318 return;
319 }
320 if (grp->gr_gid != mygid) {
321 for (i = 0; i < ngroups; i++)
322 if (grp->gr_gid == gidset[i])
323 break;
324 if (i >= ngroups && myuid != 0) {
325 warnx("%s (gid %d): permission denied",
326 name, grp->gr_gid);
327 return;
328 }
329 }
330 showquotas(GRPQUOTA, grp->gr_gid, name);
331 }
332
333 void
334 showquotas(type, id, name)
335 int type;
336 u_long id;
337 const char *name;
338 {
339 struct quotause *qup;
340 struct quotause *quplist;
341 const char *msgi, *msgb, *nam, *timemsg;
342 int lines = 0;
343 static time_t now;
344
345 if (now == 0)
346 time(&now);
347 quplist = getprivs(id, type);
348 for (qup = quplist; qup; qup = qup->next) {
349 int ql_stat;
350 if (!vflag &&
351 qup->q2e.q2e_val[QL_BLOCK].q2v_softlimit == UQUAD_MAX &&
352 qup->q2e.q2e_val[QL_BLOCK].q2v_hardlimit == UQUAD_MAX &&
353 qup->q2e.q2e_val[QL_FILE].q2v_softlimit == UQUAD_MAX &&
354 qup->q2e.q2e_val[QL_FILE].q2v_hardlimit == UQUAD_MAX)
355 continue;
356 ql_stat = quota2_check_limit(&qup->q2e.q2e_val[QL_FILE],
357 1, now);
358 switch(QL_STATUS(ql_stat)) {
359 case QL_S_DENY_HARD:
360 msgi = "File limit reached on";
361 break;
362 case QL_S_DENY_GRACE:
363 msgi = "Over file quota on";
364 break;
365 case QL_S_ALLOW_SOFT:
366 msgi = "In file grace period on";
367 break;
368 default:
369 msgi = NULL;
370 }
371 ql_stat = quota2_check_limit(&qup->q2e.q2e_val[QL_BLOCK],
372 1, now);
373 switch(QL_STATUS(ql_stat)) {
374 case QL_S_DENY_HARD:
375 msgb = "Block limit reached on";
376 break;
377 case QL_S_DENY_GRACE:
378 msgb = "Over block quota on";
379 break;
380 case QL_S_ALLOW_SOFT:
381 msgb = "In block grace period on";
382 break;
383 default:
384 msgb = NULL;
385 }
386 if (qflag) {
387 if ((msgi != NULL || msgb != NULL) &&
388 lines++ == 0)
389 heading(type, id, name, "");
390 if (msgi != NULL)
391 printf("\t%s %s\n", msgi, qup->fsname);
392 if (msgb != NULL)
393 printf("\t%s %s\n", msgb, qup->fsname);
394 continue;
395 }
396 if (vflag || dflag || msgi || msgb ||
397 qup->q2e.q2e_val[QL_BLOCK].q2v_cur ||
398 qup->q2e.q2e_val[QL_FILE].q2v_cur) {
399 if (lines++ == 0)
400 heading(type, id, name, "");
401 nam = qup->fsname;
402 if (strlen(qup->fsname) > 4) {
403 printf("%s\n", qup->fsname);
404 nam = "";
405 }
406 if (msgb)
407 timemsg = timeprt(now,
408 qup->q2e.q2e_val[QL_BLOCK].q2v_time, 8);
409 else if ((qup->flags & QUOTA2) != 0 && vflag)
410 timemsg = timeprt(0,
411 qup->q2e.q2e_val[QL_BLOCK].q2v_grace, 8);
412 else
413 timemsg = "";
414
415 printf("%12s%9s%c%8s%9s%8s"
416 , nam
417 , intprt(qup->q2e.q2e_val[QL_BLOCK].q2v_cur
418 ,HN_B, hflag, 8)
419 , (msgb == NULL) ? ' ' : '*'
420 , intprt(qup->q2e.q2e_val[QL_BLOCK].q2v_softlimit
421 , HN_B, hflag, 8)
422 , intprt(qup->q2e.q2e_val[QL_BLOCK].q2v_hardlimit
423 , HN_B, hflag, 8)
424 , timemsg);
425
426 if (msgi)
427 timemsg = timeprt(now,
428 qup->q2e.q2e_val[QL_FILE].q2v_time, 8);
429 else if ((qup->flags & QUOTA2) != 0 && vflag)
430 timemsg = timeprt(0,
431 qup->q2e.q2e_val[QL_FILE].q2v_grace, 8);
432 else
433 timemsg = "";
434
435 printf("%8s%c%7s%8s%8s\n"
436 , intprt(qup->q2e.q2e_val[QL_FILE].q2v_cur
437 , 0, hflag, 7)
438 , (msgi == NULL) ? ' ' : '*'
439 , intprt(qup->q2e.q2e_val[QL_FILE].q2v_softlimit
440 , 0, hflag, 7)
441 , intprt(qup->q2e.q2e_val[QL_FILE].q2v_hardlimit
442 , 0, hflag, 7)
443 , timemsg);
444 continue;
445 }
446 }
447 if (!qflag && lines == 0)
448 heading(type, id, name, "none");
449 }
450
451 void
452 heading(type, id, name, tag)
453 int type;
454 u_long id;
455 const char *name, *tag;
456 {
457 if (dflag)
458 printf("Default %s disk quotas: %s\n",
459 qfextension[type], tag);
460 else
461 printf("Disk quotas for %s %s (%cid %ld): %s\n",
462 qfextension[type], name, *qfextension[type],
463 (u_long)id, tag);
464
465 if (!qflag && tag[0] == '\0') {
466 printf("%12s%9s %8s%9s%8s%8s %7s%8s%8s\n"
467 , "Filesystem"
468 , "blocks"
469 , "quota"
470 , "limit"
471 , "grace"
472 , "files"
473 , "quota"
474 , "limit"
475 , "grace"
476 );
477 }
478 }
479
480 /*
481 * Collect the requested quota information.
482 */
483 struct quotause *
484 getprivs(id, quotatype)
485 long id;
486 int quotatype;
487 {
488 struct quotause *qup, *quptail;
489 struct quotause *quphead;
490 struct statvfs *fst;
491 int nfst, i;
492 int8_t version;
493
494 qup = quphead = quptail = NULL;
495
496 nfst = getmntinfo(&fst, MNT_WAIT);
497 if (nfst == 0)
498 errx(2, "no filesystems mounted!");
499 for (i = 0; i < nfst; i++) {
500 if (qup == NULL) {
501 if ((qup =
502 (struct quotause *)malloc(sizeof *qup)) == NULL)
503 errx(2, "out of memory");
504 }
505 if (strncmp(fst[i].f_fstypename, "nfs",
506 sizeof(fst[i].f_fstypename)) == 0) {
507 version = 0;
508 if (getnfsquota(&fst[i], NULL, qup, id, quotatype) == 0)
509 continue;
510 } else if ((fst[i].f_flag & ST_QUOTA) != 0) {
511 if (getvfsquota(fst[i].f_mntonname, &qup->q2e, &version,
512 id, quotatype, dflag, Dflag) == 0)
513 continue;
514 } else
515 continue;
516 (void)strncpy(qup->fsname, fst[i].f_mntonname,
517 sizeof(qup->fsname) - 1);
518 if (version == 2)
519 qup->flags |= QUOTA2;
520 if (quphead == NULL)
521 quphead = qup;
522 else
523 quptail->next = qup;
524 quptail = qup;
525 quptail->next = 0;
526 qup = NULL;
527 }
528 if (qup)
529 free(qup);
530 return (quphead);
531 }
532
533 int
534 getnfsquota(fst, fs, qup, id, quotatype)
535 struct statvfs *fst;
536 struct fstab *fs;
537 struct quotause *qup;
538 long id;
539 int quotatype;
540 {
541 struct getquota_args gq_args;
542 struct ext_getquota_args ext_gq_args;
543 struct getquota_rslt gq_rslt;
544 struct quota2_entry *q2e = &qup->q2e;
545 struct dqblk dqblk;
546 struct timeval tv;
547 char *cp;
548 int ret;
549
550 if (fst->f_flag & MNT_LOCAL)
551 return (0);
552
553 /*
554 * must be some form of "hostname:/path"
555 */
556 cp = strchr(fst->f_mntfromname, ':');
557 if (cp == NULL) {
558 warnx("cannot find hostname for %s", fst->f_mntfromname);
559 return (0);
560 }
561
562 *cp = '\0';
563 if (*(cp+1) != '/') {
564 *cp = ':';
565 return (0);
566 }
567
568 ext_gq_args.gqa_pathp = cp + 1;
569 ext_gq_args.gqa_id = id;
570 ext_gq_args.gqa_type =
571 (quotatype == USRQUOTA) ? RQUOTA_USRQUOTA : RQUOTA_GRPQUOTA;
572 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, EXT_RQUOTAVERS,
573 RQUOTAPROC_GETQUOTA, xdr_ext_getquota_args, &ext_gq_args,
574 xdr_getquota_rslt, &gq_rslt);
575 if (ret == RPC_PROGVERSMISMATCH) {
576 if (quotatype != USRQUOTA) {
577 *cp = ':';
578 return (0);
579 }
580 /* try RQUOTAVERS */
581 gq_args.gqa_pathp = cp + 1;
582 gq_args.gqa_uid = id;
583 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
584 RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args,
585 xdr_getquota_rslt, &gq_rslt);
586 }
587 if (ret != RPC_SUCCESS) {
588 *cp = ':';
589 return (0);
590 }
591
592 switch (gq_rslt.status) {
593 case Q_NOQUOTA:
594 break;
595 case Q_EPERM:
596 warnx("quota permission error, host: %s", fst->f_mntfromname);
597 break;
598 case Q_OK:
599 gettimeofday(&tv, NULL);
600 /* blocks*/
601 q2e->q2e_val[QL_BLOCK].q2v_hardlimit =
602 dqblk.dqb_bhardlimit =
603 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
604 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
605 dqblk.dqb_bsoftlimit =
606 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
607 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
608 dqblk.dqb_curblocks =
609 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
610 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
611 /* inodes */
612 dqblk.dqb_ihardlimit =
613 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
614 dqblk.dqb_isoftlimit =
615 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
616 dqblk.dqb_curinodes =
617 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
618 /* grace times */
619 dqblk.dqb_btime =
620 tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
621 dqblk.dqb_itime =
622 tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
623 dqblk2q2e(&dqblk, q2e);
624 *cp = ':';
625 return (1);
626 default:
627 warnx("bad rpc result, host: %s", fst->f_mntfromname);
628 break;
629 }
630 *cp = ':';
631 return (0);
632 }
633
634 int
635 callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
636 char *host;
637 int prognum, versnum, procnum;
638 xdrproc_t inproc;
639 void *in;
640 xdrproc_t outproc;
641 void *out;
642 {
643 struct sockaddr_in server_addr;
644 enum clnt_stat clnt_stat;
645 struct hostent *hp;
646 struct timeval timeout, tottimeout;
647
648 CLIENT *client = NULL;
649 int sock = RPC_ANYSOCK;
650
651 if ((hp = gethostbyname(host)) == NULL)
652 return ((int) RPC_UNKNOWNHOST);
653 timeout.tv_usec = 0;
654 timeout.tv_sec = 6;
655 memmove(&server_addr.sin_addr, hp->h_addr, hp->h_length);
656 server_addr.sin_family = AF_INET;
657 server_addr.sin_port = 0;
658
659 if ((client = clntudp_create(&server_addr, prognum,
660 versnum, timeout, &sock)) == NULL)
661 return ((int) rpc_createerr.cf_stat);
662
663 client->cl_auth = authunix_create_default();
664 tottimeout.tv_sec = 25;
665 tottimeout.tv_usec = 0;
666 clnt_stat = clnt_call(client, procnum, inproc, in,
667 outproc, out, tottimeout);
668
669 return ((int) clnt_stat);
670 }
671
672 int
673 alldigits(s)
674 char *s;
675 {
676 int c;
677
678 c = *s++;
679 do {
680 if (!isdigit(c))
681 return (0);
682 } while ((c = *s++) != 0);
683 return (1);
684 }
685