2 * Copyright (c) 1980, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Robert Elz at The University of Melbourne.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * @(#) Copyright (c) 1980, 1990, 1993 The Regents of the University of California. All rights reserved.
37 * @(#)quota.c 8.1 (Berkeley) 6/6/93
38 * $FreeBSD: src/usr.bin/quota/quota.c,v 1.11.2.5 2002/11/30 23:54:21 iedowse Exp $
39 * $DragonFly: src/usr.bin/quota/quota.c,v 1.7 2006/04/03 01:59:28 dillon Exp $
43 * Disk quota reporting program.
46 #include <sys/param.h>
47 #include <sys/types.h>
50 #include <sys/mount.h>
51 #include <sys/socket.h>
54 #include <rpc/pmap_prot.h>
55 #include <rpcsvc/rquota.h>
57 #include <vfs/ufs/quota.h>
70 const char *qfname
= QUOTAFILENAME
;
71 const char *qfextension
[] = INITQFNAMES
;
74 struct quotause
*next
;
76 struct ufs_dqblk dqblk
;
77 char fsname
[MAXPATHLEN
+ 1];
81 static const char *timeprt(time_t);
82 static struct quotause
*getprivs(long, int);
83 static void usage(void);
84 static void showuid(u_long
);
85 static void showgid(u_long
);
86 static int alldigits(char *);
87 static void showusrname(char *);
88 static void showgrpname(char *);
89 static void showquotas(int, u_long
, const char *);
90 static void heading(int, u_long
, const char *, const char *);
91 static int ufshasquota(struct fstab
*, int, char **);
92 static int getufsquota(struct fstab
*, struct quotause
*, long, int);
93 static int getnfsquota(struct statfs
*, struct quotause
*, long, int);
94 static int callaurpc(char *, int, int, int, xdrproc_t
, char *, xdrproc_t
,
102 main(int argc
, char *argv
[])
105 gid_t mygid
, gidset
[NGROUPS
];
106 int i
, gflag
= 0, uflag
= 0;
109 while ((ch
= getopt(argc
, argv
, "glquv")) != -1) {
132 if (!uflag
&& !gflag
)
139 ngroups
= getgroups(NGROUPS
, gidset
);
143 for (i
= 0; i
< ngroups
; i
++)
144 if (gidset
[i
] != mygid
)
152 for (; argc
> 0; argc
--, argv
++) {
153 if (alldigits(*argv
))
154 showuid(atoi(*argv
));
161 for (; argc
> 0; argc
--, argv
++) {
162 if (alldigits(*argv
))
163 showgid(atoi(*argv
));
175 fprintf(stderr
, "%s\n%s\n%s\n",
176 "usage: quota [-glu] [-v | -q]",
177 " quota [-lu] [-v | -q] user ...",
178 " quota -g [-l] [-v | -q] group ...");
183 * Print out quotas for a specified user identifier.
188 struct passwd
*pwd
= getpwuid(uid
);
193 name
= "(no account)";
197 if (uid
!= myuid
&& myuid
!= 0) {
198 printf("quota: %s (uid %lu): permission denied\n", name
, uid
);
201 showquotas(USRQUOTA
, uid
, name
);
205 * Print out quotas for a specifed user name.
208 showusrname(char *name
)
210 struct passwd
*pwd
= getpwnam(name
);
214 warnx("%s: unknown user", name
);
218 if (pwd
->pw_uid
!= myuid
&& myuid
!= 0) {
219 warnx("%s (uid %u): permission denied", name
, pwd
->pw_uid
);
222 showquotas(USRQUOTA
, pwd
->pw_uid
, name
);
226 * Print out quotas for a specified group identifier.
231 struct group
*grp
= getgrgid(gid
);
233 gid_t mygid
, gidset
[NGROUPS
];
242 ngroups
= getgroups(NGROUPS
, gidset
);
248 for (i
= 0; i
< ngroups
; i
++)
249 if (gid
== gidset
[i
])
251 if (i
>= ngroups
&& getuid() != 0) {
252 warnx("%s (gid %lu): permission denied", name
, gid
);
256 showquotas(GRPQUOTA
, gid
, name
);
260 * Print out quotas for a specifed group name.
263 showgrpname(char *name
)
265 struct group
*grp
= getgrnam(name
);
267 gid_t mygid
, gidset
[NGROUPS
];
271 warnx("%s: unknown group", name
);
275 ngroups
= getgroups(NGROUPS
, gidset
);
280 if (grp
->gr_gid
!= mygid
) {
281 for (i
= 0; i
< ngroups
; i
++)
282 if (grp
->gr_gid
== gidset
[i
])
284 if (i
>= ngroups
&& getuid() != 0) {
285 warnx("%s (gid %u): permission denied", name
,
290 showquotas(GRPQUOTA
, grp
->gr_gid
, name
);
294 showquotas(int type
, u_long id
, const char *name
)
296 struct quotause
*qup
;
297 struct quotause
*quplist
;
298 const char *msgi
, *msgb
;
305 quplist
= getprivs(id
, type
);
306 for (qup
= quplist
; qup
; qup
= qup
->next
) {
308 qup
->dqblk
.dqb_isoftlimit
== 0 &&
309 qup
->dqblk
.dqb_ihardlimit
== 0 &&
310 qup
->dqblk
.dqb_bsoftlimit
== 0 &&
311 qup
->dqblk
.dqb_bhardlimit
== 0)
314 if (qup
->dqblk
.dqb_ihardlimit
&&
315 qup
->dqblk
.dqb_curinodes
>= qup
->dqblk
.dqb_ihardlimit
)
316 msgi
= "File limit reached on";
317 else if (qup
->dqblk
.dqb_isoftlimit
&&
318 qup
->dqblk
.dqb_curinodes
>= qup
->dqblk
.dqb_isoftlimit
) {
319 if (qup
->dqblk
.dqb_itime
> now
)
320 msgi
= "In file grace period on";
322 msgi
= "Over file quota on";
325 if (qup
->dqblk
.dqb_bhardlimit
&&
326 qup
->dqblk
.dqb_curblocks
>= qup
->dqblk
.dqb_bhardlimit
)
327 msgb
= "Block limit reached on";
328 else if (qup
->dqblk
.dqb_bsoftlimit
&&
329 qup
->dqblk
.dqb_curblocks
>= qup
->dqblk
.dqb_bsoftlimit
) {
330 if (qup
->dqblk
.dqb_btime
> now
)
331 msgb
= "In block grace period on";
333 msgb
= "Over block quota on";
336 if ((msgi
!= (char *)0 || msgb
!= (char *)0) &&
338 heading(type
, id
, name
, "");
339 if (msgi
!= (char *)0)
340 printf("\t%s %s\n", msgi
, qup
->fsname
);
341 if (msgb
!= (char *)0)
342 printf("\t%s %s\n", msgb
, qup
->fsname
);
346 qup
->dqblk
.dqb_curblocks
||
347 qup
->dqblk
.dqb_curinodes
) {
349 heading(type
, id
, name
, "");
351 if (strlen(qup
->fsname
) > 15) {
352 printf("%s\n", qup
->fsname
);
355 printf("%15s%8lu%c%7lu%8lu%8s"
357 , (u_long
) (dbtob(qup
->dqblk
.dqb_curblocks
)
359 , (msgb
== (char *)0) ? ' ' : '*'
360 , (u_long
) (dbtob(qup
->dqblk
.dqb_bsoftlimit
)
362 , (u_long
) (dbtob(qup
->dqblk
.dqb_bhardlimit
)
364 , (msgb
== (char *)0) ? ""
365 :timeprt(qup
->dqblk
.dqb_btime
));
366 printf("%8lu%c%7lu%8lu%8s\n"
367 , (u_long
)qup
->dqblk
.dqb_curinodes
368 , (msgi
== (char *)0) ? ' ' : '*'
369 , (u_long
)qup
->dqblk
.dqb_isoftlimit
370 , (u_long
)qup
->dqblk
.dqb_ihardlimit
371 , (msgi
== (char *)0) ? ""
372 : timeprt(qup
->dqblk
.dqb_itime
)
377 if (!qflag
&& lines
== 0)
378 heading(type
, id
, name
, "none");
382 heading(int type
, u_long id
, const char *name
, const char *tag
)
385 printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension
[type
],
386 name
, *qfextension
[type
], id
, tag
);
387 if (!qflag
&& tag
[0] == '\0') {
388 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
403 * Calculate the grace period and return a printable string for it.
406 timeprt(time_t seconds
)
408 time_t hours
, minutes
;
417 minutes
= (seconds
+ 30) / 60;
418 hours
= (minutes
+ 30) / 60;
420 sprintf(buf
, "%lddays", ((long)hours
+ 12) / 24);
424 sprintf(buf
, "%2ld:%ld", (long)minutes
/ 60,
428 sprintf(buf
, "%2ld", (long)minutes
);
433 * Collect the requested quota information.
435 static struct quotause
*
436 getprivs(long id
, int quotatype
)
438 struct quotause
*qup
, *quptail
= NULL
;
440 struct quotause
*quphead
;
444 qup
= quphead
= (struct quotause
*)0;
446 nfst
= getmntinfo(&fst
, MNT_NOWAIT
);
448 errx(2, "no filesystems mounted!");
450 for (i
=0; i
<nfst
; i
++) {
452 if ((qup
= (struct quotause
*)malloc(sizeof *qup
))
454 errx(2, "out of memory");
456 if (strcmp(fst
[i
].f_fstypename
, "nfs") == 0) {
459 if (getnfsquota(&fst
[i
], qup
, id
, quotatype
)
462 } else if (strcmp(fst
[i
].f_fstypename
, "ufs") == 0) {
465 * UFS filesystems must be in /etc/fstab, and must
466 * indicate that they have quotas on (?!) This is quite
467 * unlike SunOS where quotas can be enabled/disabled
468 * on a filesystem independent of /etc/fstab, and it
469 * will still print quotas for them.
471 if ((fs
= getfsspec(fst
[i
].f_mntfromname
)) == NULL
)
473 if (getufsquota(fs
, qup
, id
, quotatype
) == 0)
477 strcpy(qup
->fsname
, fst
[i
].f_mntonname
);
493 * Check to see if a particular quota is to be enabled.
496 ufshasquota(struct fstab
*fs
, int type
, char **qfnamep
)
498 static char initname
, usrname
[100], grpname
[100];
499 static char buf
[BUFSIZ
];
503 sprintf(usrname
, "%s%s", qfextension
[USRQUOTA
], qfname
);
504 sprintf(grpname
, "%s%s", qfextension
[GRPQUOTA
], qfname
);
507 strcpy(buf
, fs
->fs_mntops
);
508 for (opt
= strtok(buf
, ","); opt
; opt
= strtok(NULL
, ",")) {
509 if ((cp
= strchr(opt
, '=')))
511 if (type
== USRQUOTA
&& strcmp(opt
, usrname
) == 0)
513 if (type
== GRPQUOTA
&& strcmp(opt
, grpname
) == 0)
522 sprintf(buf
, "%s/%s.%s", fs
->fs_file
, qfname
, qfextension
[type
]);
528 getufsquota(struct fstab
*fs
, struct quotause
*qup
, long id
, int quotatype
)
533 qcmd
= QCMD(Q_GETQUOTA
, quotatype
);
534 if (!ufshasquota(fs
, quotatype
, &qfpathname
))
537 if (quotactl(fs
->fs_file
, qcmd
, id
, (char *)&qup
->dqblk
) != 0) {
538 if ((fd
= open(qfpathname
, O_RDONLY
)) < 0) {
539 warn("%s", qfpathname
);
542 lseek(fd
, (off_t
)(id
* sizeof(struct ufs_dqblk
)), L_SET
);
543 switch (read(fd
, &qup
->dqblk
, sizeof(struct ufs_dqblk
))) {
546 * Convert implicit 0 quota (EOF)
547 * into an explicit one (zero'ed dqblk)
549 bzero(&qup
->dqblk
, sizeof(struct ufs_dqblk
));
551 case sizeof(struct ufs_dqblk
): /* OK */
554 warn("read error: %s", qfpathname
);
564 getnfsquota(struct statfs
*fst
, struct quotause
*qup
, long id
, int quotatype
)
566 struct getquota_args gq_args
;
567 struct getquota_rslt gq_rslt
;
568 struct ufs_dqblk
*dqp
= &qup
->dqblk
;
572 if (fst
->f_flags
& MNT_LOCAL
)
576 * rpc.rquotad does not support group quotas
578 if (quotatype
!= USRQUOTA
)
582 * must be some form of "hostname:/path"
584 cp
= strchr(fst
->f_mntfromname
, ':');
586 warnx("cannot find hostname for %s", fst
->f_mntfromname
);
591 if (*(cp
+1) != '/') {
596 /* Avoid attempting the RPC for special amd(8) filesystems. */
597 if (strncmp(fst
->f_mntfromname
, "pid", 3) == 0 &&
598 strchr(fst
->f_mntfromname
, '@') != NULL
) {
603 gq_args
.gqa_pathp
= cp
+ 1;
604 gq_args
.gqa_uid
= id
;
605 if (callaurpc(fst
->f_mntfromname
, RQUOTAPROG
, RQUOTAVERS
,
606 RQUOTAPROC_GETQUOTA
, (xdrproc_t
)xdr_getquota_args
, (char *)&gq_args
,
607 (xdrproc_t
)xdr_getquota_rslt
, (char *)&gq_rslt
) != 0) {
612 switch (gq_rslt
.status
) {
616 warnx("quota permission error, host: %s",
620 gettimeofday(&tv
, NULL
);
622 dqp
->dqb_bhardlimit
=
623 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
*
624 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsize
/ DEV_BSIZE
;
625 dqp
->dqb_bsoftlimit
=
626 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
*
627 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsize
/ DEV_BSIZE
;
629 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
*
630 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsize
/ DEV_BSIZE
;
632 dqp
->dqb_ihardlimit
=
633 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_fhardlimit
;
634 dqp
->dqb_isoftlimit
=
635 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_fsoftlimit
;
637 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_curfiles
;
640 tv
.tv_sec
+ gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_btimeleft
;
642 tv
.tv_sec
+ gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_ftimeleft
;
646 warnx("bad rpc result, host: %s", fst
->f_mntfromname
);
654 callaurpc(char *host
, int prognum
, int versnum
, int procnum
,
655 xdrproc_t inproc
, char *in
, xdrproc_t outproc
, char *out
)
657 struct sockaddr_in server_addr
;
658 enum clnt_stat clnt_stat
;
660 struct timeval timeout
, tottimeout
;
662 CLIENT
*client
= NULL
;
663 int sock
= RPC_ANYSOCK
;
665 if ((hp
= gethostbyname(host
)) == NULL
)
666 return ((int) RPC_UNKNOWNHOST
);
669 bcopy(hp
->h_addr
, &server_addr
.sin_addr
,
670 MIN(hp
->h_length
,(int)sizeof(server_addr
.sin_addr
)));
671 server_addr
.sin_family
= AF_INET
;
672 server_addr
.sin_port
= 0;
674 if ((client
= clntudp_create(&server_addr
, prognum
,
675 versnum
, timeout
, &sock
)) == NULL
)
676 return ((int) rpc_createerr
.cf_stat
);
678 client
->cl_auth
= authunix_create_default();
679 tottimeout
.tv_sec
= 25;
680 tottimeout
.tv_usec
= 0;
681 clnt_stat
= clnt_call(client
, procnum
, inproc
, in
,
682 outproc
, out
, tottimeout
);
684 return ((int) clnt_stat
);
696 } while ((c
= *s
++));