Fix up mix of man(7)/mdoc(7).
[netbsd-mini2440.git] / usr.bin / quota / quota.c
blobdae9ede12fc9494f6dfee3c3d34b41bfd883e631
1 /* $NetBSD: quota.c,v 1.32 2008/07/21 14:19:25 lukem Exp $ */
3 /*
4 * Copyright (c) 1980, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
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.
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.
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 */
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.32 2008/07/21 14:19:25 lukem Exp $");
46 #endif
47 #endif /* not lint */
50 * Disk quota reporting program.
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 #include <sys/queue.h>
60 #include <ufs/ufs/quota.h>
61 #include <ctype.h>
62 #include <err.h>
63 #include <errno.h>
64 #include <fstab.h>
65 #include <grp.h>
66 #include <netdb.h>
67 #include <pwd.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <time.h>
72 #include <unistd.h>
74 #include <rpc/rpc.h>
75 #include <rpc/pmap_prot.h>
76 #include <rpcsvc/rquota.h>
78 const char *qfname = QUOTAFILENAME;
79 const char *qfextension[] = INITQFNAMES;
81 struct quotause {
82 struct quotause *next;
83 long flags;
84 struct dqblk dqblk;
85 char fsname[MAXPATHLEN + 1];
87 #define FOUND 0x01
89 int alldigits __P((char *));
90 int callaurpc __P((char *, int, int, int, xdrproc_t, void *,
91 xdrproc_t, void *));
92 int main __P((int, char **));
93 int getnfsquota __P((struct statvfs *, struct fstab *, struct quotause *,
94 long, int));
95 struct quotause *getprivs __P((long id, int quotatype));
96 int getufsquota __P((struct statvfs *, struct fstab *, struct quotause *,
97 long, int));
98 void heading __P((int, u_long, const char *, const char *));
99 void showgid __P((gid_t));
100 void showgrpname __P((const char *));
101 void showquotas __P((int, u_long, const char *));
102 void showuid __P((uid_t));
103 void showusrname __P((const char *));
104 const char *timeprt __P((time_t seconds));
105 int ufshasquota __P((struct fstab *, int, char **));
106 void usage __P((void));
108 int qflag;
109 int vflag;
110 uid_t myuid;
113 main(argc, argv)
114 int argc;
115 char *argv[];
117 int ngroups;
118 gid_t mygid, gidset[NGROUPS];
119 int i, gflag = 0, uflag = 0;
120 int ch;
122 myuid = getuid();
123 while ((ch = getopt(argc, argv, "ugvq")) != -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 default:
138 usage();
141 argc -= optind;
142 argv += optind;
143 if (!uflag && !gflag)
144 uflag++;
145 if (argc == 0) {
146 if (uflag)
147 showuid(myuid);
148 if (gflag) {
149 mygid = getgid();
150 ngroups = getgroups(NGROUPS, gidset);
151 if (ngroups < 0)
152 err(1, "getgroups");
153 showgid(mygid);
154 for (i = 0; i < ngroups; i++)
155 if (gidset[i] != mygid)
156 showgid(gidset[i]);
158 exit(0);
160 if (uflag && gflag)
161 usage();
162 if (uflag) {
163 for (; argc > 0; argc--, argv++) {
164 if (alldigits(*argv))
165 showuid(atoi(*argv));
166 else
167 showusrname(*argv);
169 exit(0);
171 if (gflag) {
172 for (; argc > 0; argc--, argv++) {
173 if (alldigits(*argv))
174 showgid(atoi(*argv));
175 else
176 showgrpname(*argv);
178 exit(0);
180 /* NOTREACHED */
181 return (0);
184 void
185 usage()
188 fprintf(stderr, "%s\n%s\n%s\n",
189 "usage: quota [-guqv]",
190 "\tquota [-qv] -u username ...",
191 "\tquota [-qv] -g groupname ...");
192 exit(1);
196 * Print out quotas for a specified user identifier.
198 void
199 showuid(uid)
200 uid_t uid;
202 struct passwd *pwd = getpwuid(uid);
203 const char *name;
205 if (pwd == NULL)
206 name = "(no account)";
207 else
208 name = pwd->pw_name;
209 if (uid != myuid && myuid != 0) {
210 printf("quota: %s (uid %d): permission denied\n", name, uid);
211 return;
213 showquotas(USRQUOTA, uid, name);
217 * Print out quotas for a specified user name.
219 void
220 showusrname(name)
221 const char *name;
223 struct passwd *pwd = getpwnam(name);
225 if (pwd == NULL) {
226 warnx("%s: unknown user", name);
227 return;
229 if (pwd->pw_uid != myuid && myuid != 0) {
230 warnx("%s (uid %d): permission denied", name, pwd->pw_uid);
231 return;
233 showquotas(USRQUOTA, pwd->pw_uid, name);
237 * Print out quotas for a specified group identifier.
239 void
240 showgid(gid)
241 gid_t gid;
243 struct group *grp = getgrgid(gid);
244 int ngroups;
245 gid_t mygid, gidset[NGROUPS];
246 int i;
247 const char *name;
249 if (grp == NULL)
250 name = "(no entry)";
251 else
252 name = grp->gr_name;
253 mygid = getgid();
254 ngroups = getgroups(NGROUPS, gidset);
255 if (ngroups < 0) {
256 warn("getgroups");
257 return;
259 if (gid != mygid) {
260 for (i = 0; i < ngroups; i++)
261 if (gid == gidset[i])
262 break;
263 if (i >= ngroups && myuid != 0) {
264 warnx("%s (gid %d): permission denied", name, gid);
265 return;
268 showquotas(GRPQUOTA, gid, name);
272 * Print out quotas for a specified group name.
274 void
275 showgrpname(name)
276 const char *name;
278 struct group *grp = getgrnam(name);
279 int ngroups;
280 gid_t mygid, gidset[NGROUPS];
281 int i;
283 if (grp == NULL) {
284 warnx("%s: unknown group", name);
285 return;
287 mygid = getgid();
288 ngroups = getgroups(NGROUPS, gidset);
289 if (ngroups < 0) {
290 warn("getgroups");
291 return;
293 if (grp->gr_gid != mygid) {
294 for (i = 0; i < ngroups; i++)
295 if (grp->gr_gid == gidset[i])
296 break;
297 if (i >= ngroups && myuid != 0) {
298 warnx("%s (gid %d): permission denied",
299 name, grp->gr_gid);
300 return;
303 showquotas(GRPQUOTA, grp->gr_gid, name);
306 void
307 showquotas(type, id, name)
308 int type;
309 u_long id;
310 const char *name;
312 struct quotause *qup;
313 struct quotause *quplist;
314 const char *msgi, *msgb, *nam;
315 int lines = 0;
316 static time_t now;
318 if (now == 0)
319 time(&now);
320 quplist = getprivs(id, type);
321 for (qup = quplist; qup; qup = qup->next) {
322 if (!vflag &&
323 qup->dqblk.dqb_isoftlimit == 0 &&
324 qup->dqblk.dqb_ihardlimit == 0 &&
325 qup->dqblk.dqb_bsoftlimit == 0 &&
326 qup->dqblk.dqb_bhardlimit == 0)
327 continue;
328 msgi = NULL;
329 if (qup->dqblk.dqb_ihardlimit &&
330 qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit)
331 msgi = "File limit reached on";
332 else if (qup->dqblk.dqb_isoftlimit &&
333 qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) {
334 if (qup->dqblk.dqb_itime > now)
335 msgi = "In file grace period on";
336 else
337 msgi = "Over file quota on";
339 msgb = NULL;
340 if (qup->dqblk.dqb_bhardlimit &&
341 qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit)
342 msgb = "Block limit reached on";
343 else {
344 if (qup->dqblk.dqb_bsoftlimit
345 && qup->dqblk.dqb_curblocks
346 >= qup->dqblk.dqb_bsoftlimit) {
347 if (qup->dqblk.dqb_btime > now)
348 msgb = "In block grace period on";
349 else
350 msgb = "Over block quota on";
353 if (qflag) {
354 if ((msgi != NULL || msgb != NULL) &&
355 lines++ == 0)
356 heading(type, id, name, "");
357 if (msgi != NULL)
358 printf("\t%s %s\n", msgi, qup->fsname);
359 if (msgb != NULL)
360 printf("\t%s %s\n", msgb, qup->fsname);
361 continue;
363 if (vflag ||
364 qup->dqblk.dqb_curblocks ||
365 qup->dqblk.dqb_curinodes) {
366 if (lines++ == 0)
367 heading(type, id, name, "");
368 nam = qup->fsname;
369 if (strlen(qup->fsname) > 15) {
370 printf("%s\n", qup->fsname);
371 nam = "";
373 printf("%12s%9d%c%8d%9d%8s"
374 , nam
375 , (int)(dbtob((u_quad_t)qup->dqblk.dqb_curblocks)
376 / 1024)
377 , (msgb == NULL) ? ' ' : '*'
378 , (int)(dbtob((u_quad_t)qup->dqblk.dqb_bsoftlimit)
379 / 1024)
380 , (int)(dbtob((u_quad_t)qup->dqblk.dqb_bhardlimit)
381 / 1024)
382 , (msgb == NULL) ? ""
383 : timeprt(qup->dqblk.dqb_btime));
384 printf("%8d%c%7d%8d%8s\n"
385 , qup->dqblk.dqb_curinodes
386 , (msgi == NULL) ? ' ' : '*'
387 , qup->dqblk.dqb_isoftlimit
388 , qup->dqblk.dqb_ihardlimit
389 , (msgi == NULL) ? ""
390 : timeprt(qup->dqblk.dqb_itime)
392 continue;
395 if (!qflag && lines == 0)
396 heading(type, id, name, "none");
399 void
400 heading(type, id, name, tag)
401 int type;
402 u_long id;
403 const char *name, *tag;
406 printf("Disk quotas for %s %s (%cid %ld): %s\n", qfextension[type],
407 name, *qfextension[type], (u_long)id, tag);
408 if (!qflag && tag[0] == '\0') {
409 printf("%12s%9s %8s%9s%8s%8s %7s%8s%8s\n"
410 , "Filesystem"
411 , "blocks"
412 , "quota"
413 , "limit"
414 , "grace"
415 , "files"
416 , "quota"
417 , "limit"
418 , "grace"
424 * Calculate the grace period and return a printable string for it.
426 const char *
427 timeprt(seconds)
428 time_t seconds;
430 time_t hours, minutes;
431 static char buf[20];
432 static time_t now;
434 if (now == 0)
435 time(&now);
436 if (now > seconds)
437 return ("none");
438 seconds -= now;
439 minutes = (seconds + 30) / 60;
440 hours = (minutes + 30) / 60;
441 if (hours >= 36) {
442 (void)snprintf(buf, sizeof buf, "%ddays",
443 (int)((hours + 12) / 24));
444 return (buf);
446 if (minutes >= 60) {
447 (void)snprintf(buf, sizeof buf, "%2d:%d",
448 (int)(minutes / 60), (int)(minutes % 60));
449 return (buf);
451 (void)snprintf(buf, sizeof buf, "%2d", (int)minutes);
452 return (buf);
456 * Collect the requested quota information.
458 struct quotause *
459 getprivs(id, quotatype)
460 long id;
461 int quotatype;
463 struct quotause *qup, *quptail;
464 struct fstab *fs;
465 struct quotause *quphead;
466 struct statvfs *fst;
467 int nfst, i;
469 qup = quphead = quptail = NULL;
471 nfst = getmntinfo(&fst, MNT_WAIT);
472 if (nfst == 0)
473 errx(2, "no filesystems mounted!");
474 setfsent();
475 for (i = 0; i < nfst; i++) {
476 if (qup == NULL) {
477 if ((qup =
478 (struct quotause *)malloc(sizeof *qup)) == NULL)
479 errx(2, "out of memory");
481 if (strncmp(fst[i].f_fstypename, "nfs",
482 sizeof(fst[i].f_fstypename)) == 0) {
483 if (getnfsquota(&fst[i], NULL, qup, id, quotatype) == 0)
484 continue;
485 } else if (strncmp(fst[i].f_fstypename, "ffs",
486 sizeof(fst[i].f_fstypename)) == 0) {
488 * XXX
489 * UFS filesystems must be in /etc/fstab, and must
490 * indicate that they have quotas on (?!) This is quite
491 * unlike SunOS where quotas can be enabled/disabled
492 * on a filesystem independent of /etc/fstab, and it
493 * will still print quotas for them.
495 if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL)
496 continue;
497 if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0)
498 continue;
499 } else
500 continue;
501 (void)strncpy(qup->fsname, fst[i].f_mntonname,
502 sizeof(qup->fsname) - 1);
503 if (quphead == NULL)
504 quphead = qup;
505 else
506 quptail->next = qup;
507 quptail = qup;
508 quptail->next = 0;
509 qup = NULL;
511 if (qup)
512 free(qup);
513 endfsent();
514 return (quphead);
518 * Check to see if a particular quota is to be enabled.
521 ufshasquota(fs, type, qfnamep)
522 struct fstab *fs;
523 int type;
524 char **qfnamep;
526 static char initname, usrname[100], grpname[100];
527 static char buf[BUFSIZ];
528 char *opt, *cp;
530 cp = NULL;
531 if (!initname) {
532 (void)snprintf(usrname, sizeof usrname, "%s%s",
533 qfextension[USRQUOTA], qfname);
534 (void)snprintf(grpname, sizeof grpname, "%s%s",
535 qfextension[GRPQUOTA], qfname);
536 initname = 1;
538 (void)strlcpy(buf, fs->fs_mntops, sizeof(buf));
539 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
540 if ((cp = strchr(opt, '=')) != NULL)
541 *cp++ = '\0';
542 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
543 break;
544 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
545 break;
547 if (!opt)
548 return (0);
549 if (cp) {
550 *qfnamep = cp;
551 return (1);
553 (void)snprintf(buf, sizeof buf, "%s/%s.%s", fs->fs_file, qfname,
554 qfextension[type]);
555 *qfnamep = buf;
556 return (1);
560 getufsquota(fst, fs, qup, id, quotatype)
561 struct statvfs *fst;
562 struct fstab *fs;
563 struct quotause *qup;
564 long id;
565 int quotatype;
567 char *qfpathname;
568 int fd, qcmd;
570 qcmd = QCMD(Q_GETQUOTA, quotatype);
571 if (!ufshasquota(fs, quotatype, &qfpathname))
572 return (0);
574 if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
575 if ((fd = open(qfpathname, O_RDONLY)) < 0) {
576 warn("%s", qfpathname);
577 return (0);
579 (void)lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET);
580 switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
581 case 0: /* EOF */
583 * Convert implicit 0 quota (EOF)
584 * into an explicit one (zero'ed dqblk)
586 memset((caddr_t)&qup->dqblk, 0, sizeof(struct dqblk));
587 break;
588 case sizeof(struct dqblk): /* OK */
589 break;
590 default: /* ERROR */
591 warn("read error `%s'", qfpathname);
592 close(fd);
593 return (0);
595 close(fd);
597 return (1);
601 getnfsquota(fst, fs, qup, id, quotatype)
602 struct statvfs *fst;
603 struct fstab *fs;
604 struct quotause *qup;
605 long id;
606 int quotatype;
608 struct getquota_args gq_args;
609 struct ext_getquota_args ext_gq_args;
610 struct getquota_rslt gq_rslt;
611 struct dqblk *dqp = &qup->dqblk;
612 struct timeval tv;
613 char *cp;
614 int ret;
616 if (fst->f_flag & MNT_LOCAL)
617 return (0);
620 * must be some form of "hostname:/path"
622 cp = strchr(fst->f_mntfromname, ':');
623 if (cp == NULL) {
624 warnx("cannot find hostname for %s", fst->f_mntfromname);
625 return (0);
628 *cp = '\0';
629 if (*(cp+1) != '/') {
630 *cp = ':';
631 return (0);
634 ext_gq_args.gqa_pathp = cp + 1;
635 ext_gq_args.gqa_id = id;
636 ext_gq_args.gqa_type =
637 (quotatype == USRQUOTA) ? RQUOTA_USRQUOTA : RQUOTA_GRPQUOTA;
638 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, EXT_RQUOTAVERS,
639 RQUOTAPROC_GETQUOTA, xdr_ext_getquota_args, &ext_gq_args,
640 xdr_getquota_rslt, &gq_rslt);
641 if (ret == RPC_PROGVERSMISMATCH) {
642 if (quotatype != USRQUOTA) {
643 *cp = ':';
644 return (0);
646 /* try RQUOTAVERS */
647 gq_args.gqa_pathp = cp + 1;
648 gq_args.gqa_uid = id;
649 ret = callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
650 RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args,
651 xdr_getquota_rslt, &gq_rslt);
653 if (ret != RPC_SUCCESS) {
654 *cp = ':';
655 return (0);
658 switch (gq_rslt.status) {
659 case Q_NOQUOTA:
660 break;
661 case Q_EPERM:
662 warnx("quota permission error, host: %s", fst->f_mntfromname);
663 break;
664 case Q_OK:
665 gettimeofday(&tv, NULL);
666 /* blocks*/
667 dqp->dqb_bhardlimit =
668 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
669 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
670 dqp->dqb_bsoftlimit =
671 gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
672 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
673 dqp->dqb_curblocks =
674 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
675 (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
676 /* inodes */
677 dqp->dqb_ihardlimit =
678 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
679 dqp->dqb_isoftlimit =
680 gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
681 dqp->dqb_curinodes =
682 gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
683 /* grace times */
684 dqp->dqb_btime =
685 tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
686 dqp->dqb_itime =
687 tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
688 *cp = ':';
689 return (1);
690 default:
691 warnx("bad rpc result, host: %s", fst->f_mntfromname);
692 break;
694 *cp = ':';
695 return (0);
699 callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
700 char *host;
701 int prognum, versnum, procnum;
702 xdrproc_t inproc;
703 void *in;
704 xdrproc_t outproc;
705 void *out;
707 struct sockaddr_in server_addr;
708 enum clnt_stat clnt_stat;
709 struct hostent *hp;
710 struct timeval timeout, tottimeout;
712 CLIENT *client = NULL;
713 int sock = RPC_ANYSOCK;
715 if ((hp = gethostbyname(host)) == NULL)
716 return ((int) RPC_UNKNOWNHOST);
717 timeout.tv_usec = 0;
718 timeout.tv_sec = 6;
719 memmove(&server_addr.sin_addr, hp->h_addr, hp->h_length);
720 server_addr.sin_family = AF_INET;
721 server_addr.sin_port = 0;
723 if ((client = clntudp_create(&server_addr, prognum,
724 versnum, timeout, &sock)) == NULL)
725 return ((int) rpc_createerr.cf_stat);
727 client->cl_auth = authunix_create_default();
728 tottimeout.tv_sec = 25;
729 tottimeout.tv_usec = 0;
730 clnt_stat = clnt_call(client, procnum, inproc, in,
731 outproc, out, tottimeout);
733 return ((int) clnt_stat);
737 alldigits(s)
738 char *s;
740 int c;
742 c = *s++;
743 do {
744 if (!isdigit(c))
745 return (0);
746 } while ((c = *s++) != 0);
747 return (1);