1 /* $NetBSD: quota.c,v 1.32 2008/07/21 14:19:25 lukem Exp $ */
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
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
35 #include <sys/cdefs.h>
37 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
38 The Regents of the University of California. All rights reserved.");
43 static char sccsid
[] = "@(#)quota.c 8.4 (Berkeley) 4/28/95";
45 __RCSID("$NetBSD: quota.c,v 1.32 2008/07/21 14:19:25 lukem Exp $");
50 * Disk quota reporting program.
52 #include <sys/param.h>
53 #include <sys/types.h>
56 #include <sys/mount.h>
57 #include <sys/socket.h>
58 #include <sys/queue.h>
60 #include <ufs/ufs/quota.h>
75 #include <rpc/pmap_prot.h>
76 #include <rpcsvc/rquota.h>
78 const char *qfname
= QUOTAFILENAME
;
79 const char *qfextension
[] = INITQFNAMES
;
82 struct quotause
*next
;
85 char fsname
[MAXPATHLEN
+ 1];
89 int alldigits
__P((char *));
90 int callaurpc
__P((char *, int, int, int, xdrproc_t
, void *,
92 int main
__P((int, char **));
93 int getnfsquota
__P((struct statvfs
*, struct fstab
*, struct quotause
*,
95 struct quotause
*getprivs
__P((long id
, int quotatype
));
96 int getufsquota
__P((struct statvfs
*, struct fstab
*, struct quotause
*,
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));
118 gid_t mygid
, gidset
[NGROUPS
];
119 int i
, gflag
= 0, uflag
= 0;
123 while ((ch
= getopt(argc
, argv
, "ugvq")) != -1) {
143 if (!uflag
&& !gflag
)
150 ngroups
= getgroups(NGROUPS
, gidset
);
154 for (i
= 0; i
< ngroups
; i
++)
155 if (gidset
[i
] != mygid
)
163 for (; argc
> 0; argc
--, argv
++) {
164 if (alldigits(*argv
))
165 showuid(atoi(*argv
));
172 for (; argc
> 0; argc
--, argv
++) {
173 if (alldigits(*argv
))
174 showgid(atoi(*argv
));
188 fprintf(stderr
, "%s\n%s\n%s\n",
189 "usage: quota [-guqv]",
190 "\tquota [-qv] -u username ...",
191 "\tquota [-qv] -g groupname ...");
196 * Print out quotas for a specified user identifier.
202 struct passwd
*pwd
= getpwuid(uid
);
206 name
= "(no account)";
209 if (uid
!= myuid
&& myuid
!= 0) {
210 printf("quota: %s (uid %d): permission denied\n", name
, uid
);
213 showquotas(USRQUOTA
, uid
, name
);
217 * Print out quotas for a specified user name.
223 struct passwd
*pwd
= getpwnam(name
);
226 warnx("%s: unknown user", name
);
229 if (pwd
->pw_uid
!= myuid
&& myuid
!= 0) {
230 warnx("%s (uid %d): permission denied", name
, pwd
->pw_uid
);
233 showquotas(USRQUOTA
, pwd
->pw_uid
, name
);
237 * Print out quotas for a specified group identifier.
243 struct group
*grp
= getgrgid(gid
);
245 gid_t mygid
, gidset
[NGROUPS
];
254 ngroups
= getgroups(NGROUPS
, gidset
);
260 for (i
= 0; i
< ngroups
; i
++)
261 if (gid
== gidset
[i
])
263 if (i
>= ngroups
&& myuid
!= 0) {
264 warnx("%s (gid %d): permission denied", name
, gid
);
268 showquotas(GRPQUOTA
, gid
, name
);
272 * Print out quotas for a specified group name.
278 struct group
*grp
= getgrnam(name
);
280 gid_t mygid
, gidset
[NGROUPS
];
284 warnx("%s: unknown group", name
);
288 ngroups
= getgroups(NGROUPS
, gidset
);
293 if (grp
->gr_gid
!= mygid
) {
294 for (i
= 0; i
< ngroups
; i
++)
295 if (grp
->gr_gid
== gidset
[i
])
297 if (i
>= ngroups
&& myuid
!= 0) {
298 warnx("%s (gid %d): permission denied",
303 showquotas(GRPQUOTA
, grp
->gr_gid
, name
);
307 showquotas(type
, id
, name
)
312 struct quotause
*qup
;
313 struct quotause
*quplist
;
314 const char *msgi
, *msgb
, *nam
;
320 quplist
= getprivs(id
, type
);
321 for (qup
= quplist
; qup
; qup
= qup
->next
) {
323 qup
->dqblk
.dqb_isoftlimit
== 0 &&
324 qup
->dqblk
.dqb_ihardlimit
== 0 &&
325 qup
->dqblk
.dqb_bsoftlimit
== 0 &&
326 qup
->dqblk
.dqb_bhardlimit
== 0)
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";
337 msgi
= "Over file quota on";
340 if (qup
->dqblk
.dqb_bhardlimit
&&
341 qup
->dqblk
.dqb_curblocks
>= qup
->dqblk
.dqb_bhardlimit
)
342 msgb
= "Block limit reached on";
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";
350 msgb
= "Over block quota on";
354 if ((msgi
!= NULL
|| msgb
!= NULL
) &&
356 heading(type
, id
, name
, "");
358 printf("\t%s %s\n", msgi
, qup
->fsname
);
360 printf("\t%s %s\n", msgb
, qup
->fsname
);
364 qup
->dqblk
.dqb_curblocks
||
365 qup
->dqblk
.dqb_curinodes
) {
367 heading(type
, id
, name
, "");
369 if (strlen(qup
->fsname
) > 15) {
370 printf("%s\n", qup
->fsname
);
373 printf("%12s%9d%c%8d%9d%8s"
375 , (int)(dbtob((u_quad_t
)qup
->dqblk
.dqb_curblocks
)
377 , (msgb
== NULL
) ? ' ' : '*'
378 , (int)(dbtob((u_quad_t
)qup
->dqblk
.dqb_bsoftlimit
)
380 , (int)(dbtob((u_quad_t
)qup
->dqblk
.dqb_bhardlimit
)
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
)
395 if (!qflag
&& lines
== 0)
396 heading(type
, id
, name
, "none");
400 heading(type
, id
, name
, tag
)
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"
424 * Calculate the grace period and return a printable string for it.
430 time_t hours
, minutes
;
439 minutes
= (seconds
+ 30) / 60;
440 hours
= (minutes
+ 30) / 60;
442 (void)snprintf(buf
, sizeof buf
, "%ddays",
443 (int)((hours
+ 12) / 24));
447 (void)snprintf(buf
, sizeof buf
, "%2d:%d",
448 (int)(minutes
/ 60), (int)(minutes
% 60));
451 (void)snprintf(buf
, sizeof buf
, "%2d", (int)minutes
);
456 * Collect the requested quota information.
459 getprivs(id
, quotatype
)
463 struct quotause
*qup
, *quptail
;
465 struct quotause
*quphead
;
469 qup
= quphead
= quptail
= NULL
;
471 nfst
= getmntinfo(&fst
, MNT_WAIT
);
473 errx(2, "no filesystems mounted!");
475 for (i
= 0; i
< nfst
; i
++) {
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)
485 } else if (strncmp(fst
[i
].f_fstypename
, "ffs",
486 sizeof(fst
[i
].f_fstypename
)) == 0) {
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
)
497 if (getufsquota(&fst
[i
], fs
, qup
, id
, quotatype
) == 0)
501 (void)strncpy(qup
->fsname
, fst
[i
].f_mntonname
,
502 sizeof(qup
->fsname
) - 1);
518 * Check to see if a particular quota is to be enabled.
521 ufshasquota(fs
, type
, qfnamep
)
526 static char initname
, usrname
[100], grpname
[100];
527 static char buf
[BUFSIZ
];
532 (void)snprintf(usrname
, sizeof usrname
, "%s%s",
533 qfextension
[USRQUOTA
], qfname
);
534 (void)snprintf(grpname
, sizeof grpname
, "%s%s",
535 qfextension
[GRPQUOTA
], qfname
);
538 (void)strlcpy(buf
, fs
->fs_mntops
, sizeof(buf
));
539 for (opt
= strtok(buf
, ","); opt
; opt
= strtok(NULL
, ",")) {
540 if ((cp
= strchr(opt
, '=')) != NULL
)
542 if (type
== USRQUOTA
&& strcmp(opt
, usrname
) == 0)
544 if (type
== GRPQUOTA
&& strcmp(opt
, grpname
) == 0)
553 (void)snprintf(buf
, sizeof buf
, "%s/%s.%s", fs
->fs_file
, qfname
,
560 getufsquota(fst
, fs
, qup
, id
, quotatype
)
563 struct quotause
*qup
;
570 qcmd
= QCMD(Q_GETQUOTA
, quotatype
);
571 if (!ufshasquota(fs
, quotatype
, &qfpathname
))
574 if (quotactl(fs
->fs_file
, qcmd
, id
, &qup
->dqblk
) != 0) {
575 if ((fd
= open(qfpathname
, O_RDONLY
)) < 0) {
576 warn("%s", qfpathname
);
579 (void)lseek(fd
, (off_t
)(id
* sizeof(struct dqblk
)), SEEK_SET
);
580 switch (read(fd
, &qup
->dqblk
, sizeof(struct dqblk
))) {
583 * Convert implicit 0 quota (EOF)
584 * into an explicit one (zero'ed dqblk)
586 memset((caddr_t
)&qup
->dqblk
, 0, sizeof(struct dqblk
));
588 case sizeof(struct dqblk
): /* OK */
591 warn("read error `%s'", qfpathname
);
601 getnfsquota(fst
, fs
, qup
, id
, quotatype
)
604 struct quotause
*qup
;
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
;
616 if (fst
->f_flag
& MNT_LOCAL
)
620 * must be some form of "hostname:/path"
622 cp
= strchr(fst
->f_mntfromname
, ':');
624 warnx("cannot find hostname for %s", fst
->f_mntfromname
);
629 if (*(cp
+1) != '/') {
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
) {
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
) {
658 switch (gq_rslt
.status
) {
662 warnx("quota permission error, host: %s", fst
->f_mntfromname
);
665 gettimeofday(&tv
, NULL
);
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
);
674 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
*
675 (gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsize
/ DEV_BSIZE
);
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
;
682 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_curfiles
;
685 tv
.tv_sec
+ gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_btimeleft
;
687 tv
.tv_sec
+ gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_ftimeleft
;
691 warnx("bad rpc result, host: %s", fst
->f_mntfromname
);
699 callaurpc(host
, prognum
, versnum
, procnum
, inproc
, in
, outproc
, out
)
701 int prognum
, versnum
, procnum
;
707 struct sockaddr_in server_addr
;
708 enum clnt_stat clnt_stat
;
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
);
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
);
746 } while ((c
= *s
++) != 0);