quota.c revision 1.36 1 /* $NetBSD: quota.c,v 1.36 2011/03/06 22:36:07 christos 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.36 2011/03/06 22:36:07 christos 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 "quotautil.h"
81 #include "getvfsquota.h"
82
83 struct quotause {
84 struct quotause *next;
85 long flags;
86 struct quota2_entry q2e;
87 char fsname[MAXPATHLEN + 1];
88 };
89 #define FOUND 0x01
90 #define QUOTA2 0x02
91
92 static int callaurpc(const char *, rpcprog_t, rpcvers_t, rpcproc_t,
93 xdrproc_t, void *, xdrproc_t, void *);
94 static int getnfsquota(struct statvfs *, struct quotause *, uint32_t, int);
95 static struct quotause *getprivs(uint32_t, int);
96 static void heading(int, uint32_t, const char *, const char *);
97 static void showgid(gid_t);
98 static void showgrpname(const char *);
99 static void showquotas(int, uint32_t, const char *);
100 static void showuid(uid_t);
101 static void showusrname(const char *);
102 static void usage(void) __attribute__((__noreturn__));
103
104 static int qflag = 0;
105 static int vflag = 0;
106 static int hflag = 0;
107 static int dflag = 0;
108 static int Dflag = 0;
109 static uid_t myuid;
110
111 int
112 main(int argc, char *argv[])
113 {
114 int ngroups;
115 gid_t mygid, gidset[NGROUPS];
116 int i, gflag = 0, uflag = 0;
117 int ch;
118
119 myuid = getuid();
120 while ((ch = getopt(argc, argv, "Ddhugvq")) != -1) {
121 switch(ch) {
122 case 'g':
123 gflag++;
124 break;
125 case 'u':
126 uflag++;
127 break;
128 case 'v':
129 vflag++;
130 break;
131 case 'q':
132 qflag++;
133 break;
134 case 'h':
135 hflag++;
136 break;
137 case 'd':
138 dflag++;
139 break;
140 case 'D':
141 Dflag++;
142 break;
143 default:
144 usage();
145 }
146 }
147 argc -= optind;
148 argv += optind;
149 if (!uflag && !gflag)
150 uflag++;
151 if (dflag) {
152 #if 0
153 if (myuid != 0)
154 errx(1, "-d: permission denied");
155 #endif
156 if (uflag)
157 showquotas(USRQUOTA, 0, "");
158 if (gflag)
159 showquotas(GRPQUOTA, 0, "");
160 return 0;
161 }
162 if (argc == 0) {
163 if (uflag)
164 showuid(myuid);
165 if (gflag) {
166 if (dflag)
167 showgid(0);
168 else {
169 mygid = getgid();
170 ngroups = getgroups(NGROUPS, gidset);
171 if (ngroups < 0)
172 err(1, "getgroups");
173 showgid(mygid);
174 for (i = 0; i < ngroups; i++)
175 if (gidset[i] != mygid)
176 showgid(gidset[i]);
177 }
178 }
179 return 0;
180 }
181 if (uflag && gflag)
182 usage();
183 if (uflag) {
184 for (; argc > 0; argc--, argv++) {
185 if (alldigits(*argv))
186 showuid((uid_t)atoi(*argv));
187 else
188 showusrname(*argv);
189 }
190 return 0;
191 }
192 if (gflag) {
193 for (; argc > 0; argc--, argv++) {
194 if (alldigits(*argv))
195 showgid((gid_t)atoi(*argv));
196 else
197 showgrpname(*argv);
198 }
199 return 0;
200 }
201 /* NOTREACHED */
202 return 0;
203 }
204
205 static void
206 usage(void)
207 {
208 const char *p = getprogname();
209 fprintf(stderr, "Usage: %s [-Dhguqv]\n"
210 "\t%s [-Dhqv] -u username ...\n"
211 "\t%s [-Dhqv] -g groupname ...\n"
212 "\t%s -d [-Dhguqv]\n", p, p, p, p);
213 exit(1);
214 }
215
216 /*
217 * Print out quotas for a specified user identifier.
218 */
219 static void
220 showuid(uid_t uid)
221 {
222 struct passwd *pwd = getpwuid(uid);
223 const char *name;
224
225 if (pwd == NULL)
226 name = "(no account)";
227 else
228 name = pwd->pw_name;
229 if (uid != myuid && myuid != 0) {
230 warnx("%s (uid %d): permission denied", name, uid);
231 return;
232 }
233 showquotas(USRQUOTA, uid, name);
234 }
235
236 /*
237 * Print out quotas for a specified user name.
238 */
239 static void
240 showusrname(const char *name)
241 {
242 struct passwd *pwd = getpwnam(name);
243
244 if (pwd == NULL) {
245 warnx("%s: unknown user", name);
246 return;
247 }
248 if (pwd->pw_uid != myuid && myuid != 0) {
249 warnx("%s (uid %d): permission denied", name, pwd->pw_uid);
250 return;
251 }
252 showquotas(USRQUOTA, pwd->pw_uid, name);
253 }
254
255 /*
256 * Print out quotas for a specified group identifier.
257 */
258 static void
259 showgid(gid_t gid)
260 {
261 struct group *grp = getgrgid(gid);
262 int ngroups;
263 gid_t mygid, gidset[NGROUPS];
264 int i;
265 const char *name;
266
267 if (grp == NULL)
268 name = "(no entry)";
269 else
270 name = grp->gr_name;
271 mygid = getgid();
272 ngroups = getgroups(NGROUPS, gidset);
273 if (ngroups < 0) {
274 warn("getgroups");
275 return;
276 }
277 if (gid != mygid) {
278 for (i = 0; i < ngroups; i++)
279 if (gid == gidset[i])
280 break;
281 if (i >= ngroups && myuid != 0) {
282 warnx("%s (gid %d): permission denied", name, gid);
283 return;
284 }
285 }
286 showquotas(GRPQUOTA, gid, name);
287 }
288
289 /*
290 * Print out quotas for a specified group name.
291 */
292 static void
293 showgrpname(const char *name)
294 {
295 struct group *grp = getgrnam(name);
296 int ngroups;
297 gid_t mygid, gidset[NGROUPS];
298 int i;
299
300 if (grp == NULL) {
301 warnx("%s: unknown group", name);
302 return;
303 }
304 mygid = getgid();
305 ngroups = getgroups(NGROUPS, gidset);
306 if (ngroups < 0) {
307 warn("getgroups");
308 return;
309 }
310 if (grp->gr_gid != mygid) {
311 for (i = 0; i < ngroups; i++)
312 if (grp->gr_gid == gidset[i])
313 break;
314 if (i >= ngroups && myuid != 0) {
315 warnx("%s (gid %d): permission denied",
316 name, grp->gr_gid);
317 return;
318 }
319 }
320 showquotas(GRPQUOTA, grp->gr_gid, name);
321 }
322
323 static void
324 showquotas(int type, uint32_t id, const char *name)
325 {
326 struct quotause *qup;
327 struct quotause *quplist;
328 const char *msgi, *msgb, *nam, *timemsg;
329 int lines = 0;
330 static time_t now;
331 char b0[20], b1[20], b2[20], b3[20];
332
333 if (now == 0)
334 time(&now);
335 quplist = getprivs(id, type);
336 for (qup = quplist; qup; qup = qup->next) {
337 int ql_stat;
338 struct quota2_val *q = qup->q2e.q2e_val;
339 if (!vflag &&
340 q[QL_BLOCK].q2v_softlimit == UQUAD_MAX &&
341 q[QL_BLOCK].q2v_hardlimit == UQUAD_MAX &&
342 q[QL_FILE].q2v_softlimit == UQUAD_MAX &&
343 q[QL_FILE].q2v_hardlimit == UQUAD_MAX)
344 continue;
345 ql_stat = quota2_check_limit(&q[QL_FILE], 1, now);
346 switch(QL_STATUS(ql_stat)) {
347 case QL_S_DENY_HARD:
348 msgi = "File limit reached on";
349 break;
350 case QL_S_DENY_GRACE:
351 msgi = "Over file quota on";
352 break;
353 case QL_S_ALLOW_SOFT:
354 msgi = "In file grace period on";
355 break;
356 default:
357 msgi = NULL;
358 }
359 ql_stat = quota2_check_limit(&q[QL_BLOCK], 1, now);
360 switch(QL_STATUS(ql_stat)) {
361 case QL_S_DENY_HARD:
362 msgb = "Block limit reached on";
363 break;
364 case QL_S_DENY_GRACE:
365 msgb = "Over block quota on";
366 break;
367 case QL_S_ALLOW_SOFT:
368 msgb = "In block grace period on";
369 break;
370 default:
371 msgb = NULL;
372 }
373 if (qflag) {
374 if ((msgi != NULL || msgb != NULL) &&
375 lines++ == 0)
376 heading(type, id, name, "");
377 if (msgi != NULL)
378 printf("\t%s %s\n", msgi, qup->fsname);
379 if (msgb != NULL)
380 printf("\t%s %s\n", msgb, qup->fsname);
381 continue;
382 }
383 if (vflag || dflag || msgi || msgb || q[QL_BLOCK].q2v_cur ||
384 q[QL_FILE].q2v_cur) {
385 if (lines++ == 0)
386 heading(type, id, name, "");
387 nam = qup->fsname;
388 if (strlen(qup->fsname) > 4) {
389 printf("%s\n", qup->fsname);
390 nam = "";
391 }
392 if (msgb)
393 timemsg = timeprt(b0, 9, now,
394 q[QL_BLOCK].q2v_time);
395 else if ((qup->flags & QUOTA2) != 0 && vflag)
396 timemsg = timeprt(b0, 9, 0,
397 q[QL_BLOCK].q2v_grace);
398 else
399 timemsg = "";
400
401 printf("%12s%9s%c%8s%9s%8s",
402 nam,
403 intprt(b1, 9, q[QL_BLOCK].q2v_cur,
404 HN_B, hflag),
405 (msgb == NULL) ? ' ' : '*',
406 intprt(b2, 9, q[QL_BLOCK].q2v_softlimit,
407 HN_B, hflag),
408 intprt(b3, 9, q[QL_BLOCK].q2v_hardlimit,
409 HN_B, hflag),
410 timemsg);
411
412 if (msgi)
413 timemsg = timeprt(b0, 9, now,
414 q[QL_FILE].q2v_time);
415 else if ((qup->flags & QUOTA2) != 0 && vflag)
416 timemsg = timeprt(b0, 9, 0,
417 q[QL_FILE].q2v_grace);
418 else
419 timemsg = "";
420
421 printf("%8s%c%7s%8s%8s\n",
422 intprt(b1, 8, q[QL_FILE].q2v_cur, 0, hflag),
423 (msgi == NULL) ? ' ' : '*',
424 intprt(b2, 8, q[QL_FILE].q2v_softlimit, 0, hflag),
425 intprt(b3, 8, q[QL_FILE].q2v_hardlimit, 0, hflag),
426 timemsg);
427 continue;
428 }
429 }
430 if (!qflag && lines == 0)
431 heading(type, id, name, "none");
432 }
433
434 static void
435 heading(int type, uint32_t id, const char *name, const char *tag)
436 {
437 if (dflag)
438 printf("Default %s disk quotas: %s\n",
439 qfextension[type], tag);
440 else
441 printf("Disk quotas for %s %s (%cid %u): %s\n",
442 qfextension[type], name, *qfextension[type],
443 id, tag);
444
445 if (!qflag && tag[0] == '\0') {
446 printf("%12s%9s %8s%9s%8s%8s %7s%8s%8s\n"
447 , "Filesystem"
448 , "blocks"
449 , "quota"
450 , "limit"
451 , "grace"
452 , "files"
453 , "quota"
454 , "limit"
455 , "grace"
456 );
457 }
458 }
459
460 /*
461 * Collect the requested quota information.
462 */
463 static struct quotause *
464 getprivs(uint32_t id, int quotatype)
465 {
466 struct quotause *qup, *quptail;
467 struct quotause *quphead;
468 struct statvfs *fst;
469 int nfst, i;
470 int8_t version;
471
472 qup = quphead = quptail = NULL;
473
474 nfst = getmntinfo(&fst, MNT_WAIT);
475 if (nfst == 0)
476 errx(2, "no filesystems mounted!");
477 for (i = 0; i < nfst; i++) {
478 if (qup == NULL) {
479 if ((qup = malloc(sizeof *qup)) == NULL)
480 err(1, "out of memory");
481 }
482 if (strncmp(fst[i].f_fstypename, "nfs",
483 sizeof(fst[i].f_fstypename)) == 0) {
484 version = 0;
485 if (getnfsquota(&fst[i], qup, id, quotatype) == 0)
486 continue;
487 } else if ((fst[i].f_flag & ST_QUOTA) != 0) {
488 if (getvfsquota(fst[i].f_mntonname, &qup->q2e, &version,
489 id, quotatype, dflag, Dflag) == 0)
490 continue;
491 } else
492 continue;
493 (void)strncpy(qup->fsname, fst[i].f_mntonname,
494 sizeof(qup->fsname) - 1);
495 if (version == 2)
496 qup->flags |= QUOTA2;
497 if (quphead == NULL)
498 quphead = qup;
499 else
500 quptail->next = qup;
501 quptail = qup;
502 quptail->next = 0;
503 qup = NULL;
504 }
505 free(qup);
506 return quphead;
507 }
508
509 static int
510 getnfsquota(struct statvfs *fst, struct quotause *qup,
511 uint32_t id, int quotatype)
512 {
513 struct getquota_args gq_args;
514 struct ext_getquota_args ext_gq_args;
515 struct getquota_rslt gq_rslt;
516 struct quota2_entry *q2e = &qup->q2e;
517 struct dqblk dqblk;
518 struct timeval tv;
519 char *cp;
520 int ret;
521
522 if (fst->f_flag & MNT_LOCAL)
523 return 0;
524
525 /*
526 * must be some form of "hostname:/path"
527 */
528 cp = strchr(fst->f_mntfromname, ':');
529 if (cp == NULL) {
530 warnx("cannot find hostname for %s", fst->f_mntfromname);
531 return 0;
532 }
533
534 *cp = '\0';
535 if (*(cp+1) != '/') {
536 *cp = ':';
537 return 0;
538 }
539
540 ext_gq_args.gqa_pathp = cp + 1;
541 ext_gq_args.gqa_id = id;
542 ext_gq_args.gqa_type =
543 (quotatype == USRQUOTA) ? RQUOTA_USRQUOTA : RQUOTA_GRPQUOTA;
544 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, EXT_RQUOTAVERS,
545 RQUOTAPROC_GETQUOTA, xdr_ext_getquota_args, &ext_gq_args,
546 xdr_getquota_rslt, &gq_rslt);
547 if (ret == RPC_PROGVERSMISMATCH) {
548 if (quotatype != USRQUOTA) {
549 *cp = ':';
550 return 0;
551 }
552 /* try RQUOTAVERS */
553 gq_args.gqa_pathp = cp + 1;
554 gq_args.gqa_uid = id;
555 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
556 RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args,
557 xdr_getquota_rslt, &gq_rslt);
558 }
559 if (ret != RPC_SUCCESS) {
560 *cp = ':';
561 return 0;
562 }
563
564 switch (gq_rslt.status) {
565 case Q_NOQUOTA:
566 break;
567 case Q_EPERM:
568 warnx("quota permission error, host: %s", fst->f_mntfromname);
569 break;
570 case Q_OK:
571 gettimeofday(&tv, NULL);
572
573 /* blocks*/
574 q2e->q2e_val[QL_BLOCK].q2v_hardlimit =
575 dqblk.dqb_bhardlimit =
576 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
577 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
578 dqblk.dqb_bsoftlimit =
579 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
580 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
581 dqblk.dqb_curblocks =
582 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
583 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
584
585 /* inodes */
586 dqblk.dqb_ihardlimit =
587 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
588 dqblk.dqb_isoftlimit =
589 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
590 dqblk.dqb_curinodes =
591 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
592
593 /* grace times */
594 dqblk.dqb_btime = (int)(tv.tv_sec +
595 gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft);
596 dqblk.dqb_itime = (int)(tv.tv_sec +
597 gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft);
598 dqblk2q2e(&dqblk, q2e);
599 *cp = ':';
600 return 1;
601 default:
602 warnx("bad rpc result, host: %s", fst->f_mntfromname);
603 break;
604 }
605 *cp = ':';
606 return 0;
607 }
608
609 static int
610 callaurpc(const char *host, rpcprog_t prognum, rpcvers_t versnum,
611 rpcproc_t procnum, xdrproc_t inproc, void *in, xdrproc_t outproc, void *out)
612 {
613 struct sockaddr_in server_addr;
614 enum clnt_stat clnt_stat;
615 struct hostent *hp;
616 struct timeval timeout, tottimeout;
617
618 CLIENT *client = NULL;
619 int sock = RPC_ANYSOCK;
620
621 if ((hp = gethostbyname(host)) == NULL)
622 return (int) RPC_UNKNOWNHOST;
623 timeout.tv_usec = 0;
624 timeout.tv_sec = 6;
625 memmove(&server_addr.sin_addr, hp->h_addr, hp->h_length);
626 server_addr.sin_family = AF_INET;
627 server_addr.sin_port = 0;
628
629 if ((client = clntudp_create(&server_addr, prognum,
630 versnum, timeout, &sock)) == NULL)
631 return (int) rpc_createerr.cf_stat;
632
633 client->cl_auth = authunix_create_default();
634 tottimeout.tv_sec = 25;
635 tottimeout.tv_usec = 0;
636 clnt_stat = clnt_call(client, procnum, inproc, in,
637 outproc, out, tottimeout);
638
639 return (int) clnt_stat;
640 }
641