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. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * @(#) Copyright (c) 1980, 1990, 1993 The Regents of the University of California. All rights reserved.
33 * @(#)quota.c 8.1 (Berkeley) 6/6/93
34 * $FreeBSD: src/usr.bin/quota/quota.c,v 1.11.2.5 2002/11/30 23:54:21 iedowse Exp $
38 * Disk quota reporting program.
41 #include <sys/param.h>
42 #include <sys/types.h>
45 #include <sys/mount.h>
46 #include <sys/socket.h>
49 #include <rpc/pmap_prot.h>
50 #include <rpcsvc/rquota.h>
52 #include <vfs/ufs/quota.h>
65 const char *qfname
= QUOTAFILENAME
;
66 const char *qfextension
[] = INITQFNAMES
;
69 struct quotause
*next
;
71 struct ufs_dqblk dqblk
;
72 char fsname
[MAXPATHLEN
+ 1];
76 static const char *timeprt(time_t);
77 static struct quotause
*getprivs(long, int);
78 static void usage(void);
79 static void showuid(u_long
);
80 static void showgid(u_long
);
81 static int alldigits(char *);
82 static void showusrname(char *);
83 static void showgrpname(char *);
84 static void showquotas(int, u_long
, const char *);
85 static void heading(int, u_long
, const char *, const char *);
86 static int ufshasquota(struct fstab
*, int, char **);
87 static int getufsquota(struct fstab
*, struct quotause
*, long, int);
88 static int getnfsquota(struct statfs
*, struct quotause
*, long, int);
89 static int callaurpc(char *, int, int, int, xdrproc_t
, char *, xdrproc_t
,
97 main(int argc
, char *argv
[])
100 gid_t mygid
, gidset
[NGROUPS
];
101 int i
, gflag
= 0, uflag
= 0;
104 while ((ch
= getopt(argc
, argv
, "glquv")) != -1) {
127 if (!uflag
&& !gflag
)
134 ngroups
= getgroups(NGROUPS
, gidset
);
138 for (i
= 0; i
< ngroups
; i
++)
139 if (gidset
[i
] != mygid
)
147 for (; argc
> 0; argc
--, argv
++) {
148 if (alldigits(*argv
))
149 showuid(atoi(*argv
));
156 for (; argc
> 0; argc
--, argv
++) {
157 if (alldigits(*argv
))
158 showgid(atoi(*argv
));
170 fprintf(stderr
, "%s\n%s\n%s\n",
171 "usage: quota [-glu] [-v | -q]",
172 " quota [-lu] [-v | -q] user ...",
173 " quota -g [-l] [-v | -q] group ...");
178 * Print out quotas for a specified user identifier.
183 struct passwd
*pwd
= getpwuid(uid
);
188 name
= "(no account)";
192 if (uid
!= myuid
&& myuid
!= 0) {
193 printf("quota: %s (uid %lu): permission denied\n", name
, uid
);
196 showquotas(USRQUOTA
, uid
, name
);
200 * Print out quotas for a specifed user name.
203 showusrname(char *name
)
205 struct passwd
*pwd
= getpwnam(name
);
209 warnx("%s: unknown user", name
);
213 if (pwd
->pw_uid
!= myuid
&& myuid
!= 0) {
214 warnx("%s (uid %u): permission denied", name
, pwd
->pw_uid
);
217 showquotas(USRQUOTA
, pwd
->pw_uid
, name
);
221 * Print out quotas for a specified group identifier.
226 struct group
*grp
= getgrgid(gid
);
228 gid_t mygid
, gidset
[NGROUPS
];
237 ngroups
= getgroups(NGROUPS
, gidset
);
243 for (i
= 0; i
< ngroups
; i
++)
244 if (gid
== gidset
[i
])
246 if (i
>= ngroups
&& getuid() != 0) {
247 warnx("%s (gid %lu): permission denied", name
, gid
);
251 showquotas(GRPQUOTA
, gid
, name
);
255 * Print out quotas for a specifed group name.
258 showgrpname(char *name
)
260 struct group
*grp
= getgrnam(name
);
262 gid_t mygid
, gidset
[NGROUPS
];
266 warnx("%s: unknown group", name
);
270 ngroups
= getgroups(NGROUPS
, gidset
);
275 if (grp
->gr_gid
!= mygid
) {
276 for (i
= 0; i
< ngroups
; i
++)
277 if (grp
->gr_gid
== gidset
[i
])
279 if (i
>= ngroups
&& getuid() != 0) {
280 warnx("%s (gid %u): permission denied", name
,
285 showquotas(GRPQUOTA
, grp
->gr_gid
, name
);
289 showquotas(int type
, u_long id
, const char *name
)
291 struct quotause
*qup
;
292 struct quotause
*quplist
;
293 const char *msgi
, *msgb
;
300 quplist
= getprivs(id
, type
);
301 for (qup
= quplist
; qup
; qup
= qup
->next
) {
303 qup
->dqblk
.dqb_isoftlimit
== 0 &&
304 qup
->dqblk
.dqb_ihardlimit
== 0 &&
305 qup
->dqblk
.dqb_bsoftlimit
== 0 &&
306 qup
->dqblk
.dqb_bhardlimit
== 0)
309 if (qup
->dqblk
.dqb_ihardlimit
&&
310 qup
->dqblk
.dqb_curinodes
>= qup
->dqblk
.dqb_ihardlimit
)
311 msgi
= "File limit reached on";
312 else if (qup
->dqblk
.dqb_isoftlimit
&&
313 qup
->dqblk
.dqb_curinodes
>= qup
->dqblk
.dqb_isoftlimit
) {
314 if (qup
->dqblk
.dqb_itime
> now
)
315 msgi
= "In file grace period on";
317 msgi
= "Over file quota on";
320 if (qup
->dqblk
.dqb_bhardlimit
&&
321 qup
->dqblk
.dqb_curblocks
>= qup
->dqblk
.dqb_bhardlimit
)
322 msgb
= "Block limit reached on";
323 else if (qup
->dqblk
.dqb_bsoftlimit
&&
324 qup
->dqblk
.dqb_curblocks
>= qup
->dqblk
.dqb_bsoftlimit
) {
325 if (qup
->dqblk
.dqb_btime
> now
)
326 msgb
= "In block grace period on";
328 msgb
= "Over block quota on";
331 if ((msgi
!= NULL
|| msgb
!= NULL
) &&
333 heading(type
, id
, name
, "");
335 printf("\t%s %s\n", msgi
, qup
->fsname
);
337 printf("\t%s %s\n", msgb
, qup
->fsname
);
341 qup
->dqblk
.dqb_curblocks
||
342 qup
->dqblk
.dqb_curinodes
) {
344 heading(type
, id
, name
, "");
346 if (strlen(qup
->fsname
) > 15) {
347 printf("%s\n", qup
->fsname
);
350 printf("%15s%8lu%c%7lu%8lu%8s"
352 , (u_long
) (dbtob(qup
->dqblk
.dqb_curblocks
)
354 , (msgb
== NULL
) ? ' ' : '*'
355 , (u_long
) (dbtob(qup
->dqblk
.dqb_bsoftlimit
)
357 , (u_long
) (dbtob(qup
->dqblk
.dqb_bhardlimit
)
359 , (msgb
== NULL
) ? ""
360 :timeprt(qup
->dqblk
.dqb_btime
));
361 printf("%8lu%c%7lu%8lu%8s\n"
362 , (u_long
)qup
->dqblk
.dqb_curinodes
363 , (msgi
== NULL
) ? ' ' : '*'
364 , (u_long
)qup
->dqblk
.dqb_isoftlimit
365 , (u_long
)qup
->dqblk
.dqb_ihardlimit
366 , (msgi
== NULL
) ? ""
367 : timeprt(qup
->dqblk
.dqb_itime
)
372 if (!qflag
&& lines
== 0)
373 heading(type
, id
, name
, "none");
377 heading(int type
, u_long id
, const char *name
, const char *tag
)
380 printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension
[type
],
381 name
, *qfextension
[type
], id
, tag
);
382 if (!qflag
&& tag
[0] == '\0') {
383 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
398 * Calculate the grace period and return a printable string for it.
401 timeprt(time_t seconds
)
403 time_t hours
, minutes
;
412 minutes
= (seconds
+ 30) / 60;
413 hours
= (minutes
+ 30) / 60;
415 sprintf(buf
, "%lddays", ((long)hours
+ 12) / 24);
419 sprintf(buf
, "%2ld:%ld", (long)minutes
/ 60,
423 sprintf(buf
, "%2ld", (long)minutes
);
428 * Collect the requested quota information.
430 static struct quotause
*
431 getprivs(long id
, int quotatype
)
433 struct quotause
*qup
, *quptail
= NULL
;
435 struct quotause
*quphead
;
439 qup
= quphead
= NULL
;
441 nfst
= getmntinfo(&fst
, MNT_NOWAIT
);
443 errx(2, "no filesystems mounted!");
445 for (i
=0; i
<nfst
; i
++) {
447 if ((qup
= (struct quotause
*)malloc(sizeof *qup
))
449 errx(2, "out of memory");
451 if (strcmp(fst
[i
].f_fstypename
, "nfs") == 0) {
454 if (getnfsquota(&fst
[i
], qup
, id
, quotatype
)
457 } else if (strcmp(fst
[i
].f_fstypename
, "ufs") == 0) {
460 * UFS filesystems must be in /etc/fstab, and must
461 * indicate that they have quotas on (?!) This is quite
462 * unlike SunOS where quotas can be enabled/disabled
463 * on a filesystem independent of /etc/fstab, and it
464 * will still print quotas for them.
466 if ((fs
= getfsspec(fst
[i
].f_mntfromname
)) == NULL
)
468 if (getufsquota(fs
, qup
, id
, quotatype
) == 0)
472 strcpy(qup
->fsname
, fst
[i
].f_mntonname
);
478 quptail
->next
= NULL
;
488 * Check to see if a particular quota is to be enabled.
491 ufshasquota(struct fstab
*fs
, int type
, char **qfnamep
)
493 static char initname
, usrname
[100], grpname
[100];
494 static char buf
[BUFSIZ
];
498 sprintf(usrname
, "%s%s", qfextension
[USRQUOTA
], qfname
);
499 sprintf(grpname
, "%s%s", qfextension
[GRPQUOTA
], qfname
);
502 strcpy(buf
, fs
->fs_mntops
);
503 for (opt
= strtok(buf
, ","); opt
; opt
= strtok(NULL
, ",")) {
504 if ((cp
= strchr(opt
, '=')))
506 if (type
== USRQUOTA
&& strcmp(opt
, usrname
) == 0)
508 if (type
== GRPQUOTA
&& strcmp(opt
, grpname
) == 0)
517 sprintf(buf
, "%s/%s.%s", fs
->fs_file
, qfname
, qfextension
[type
]);
523 getufsquota(struct fstab
*fs
, struct quotause
*qup
, long id
, int quotatype
)
528 qcmd
= QCMD(Q_GETQUOTA
, quotatype
);
529 if (!ufshasquota(fs
, quotatype
, &qfpathname
))
532 if (quotactl(fs
->fs_file
, qcmd
, id
, (char *)&qup
->dqblk
) != 0) {
533 if ((fd
= open(qfpathname
, O_RDONLY
)) < 0) {
534 warn("%s", qfpathname
);
537 lseek(fd
, (off_t
)(id
* sizeof(struct ufs_dqblk
)), L_SET
);
538 switch (read(fd
, &qup
->dqblk
, sizeof(struct ufs_dqblk
))) {
541 * Convert implicit 0 quota (EOF)
542 * into an explicit one (zero'ed dqblk)
544 bzero(&qup
->dqblk
, sizeof(struct ufs_dqblk
));
546 case sizeof(struct ufs_dqblk
): /* OK */
549 warn("read error: %s", qfpathname
);
559 getnfsquota(struct statfs
*fst
, struct quotause
*qup
, long id
, int quotatype
)
561 struct getquota_args gq_args
;
562 struct getquota_rslt gq_rslt
;
563 struct ufs_dqblk
*dqp
= &qup
->dqblk
;
567 if (fst
->f_flags
& MNT_LOCAL
)
571 * rpc.rquotad does not support group quotas
573 if (quotatype
!= USRQUOTA
)
577 * must be some form of "hostname:/path"
579 cp
= strchr(fst
->f_mntfromname
, ':');
581 warnx("cannot find hostname for %s", fst
->f_mntfromname
);
586 if (*(cp
+1) != '/') {
591 gq_args
.gqa_pathp
= cp
+ 1;
592 gq_args
.gqa_uid
= id
;
593 if (callaurpc(fst
->f_mntfromname
, RQUOTAPROG
, RQUOTAVERS
,
594 RQUOTAPROC_GETQUOTA
, (xdrproc_t
)xdr_getquota_args
, (char *)&gq_args
,
595 (xdrproc_t
)xdr_getquota_rslt
, (char *)&gq_rslt
) != 0) {
600 switch (gq_rslt
.status
) {
604 warnx("quota permission error, host: %s",
608 gettimeofday(&tv
, NULL
);
610 dqp
->dqb_bhardlimit
=
611 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
*
612 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsize
/ DEV_BSIZE
;
613 dqp
->dqb_bsoftlimit
=
614 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
*
615 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsize
/ DEV_BSIZE
;
617 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
*
618 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_bsize
/ DEV_BSIZE
;
620 dqp
->dqb_ihardlimit
=
621 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_fhardlimit
;
622 dqp
->dqb_isoftlimit
=
623 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_fsoftlimit
;
625 gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_curfiles
;
628 tv
.tv_sec
+ gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_btimeleft
;
630 tv
.tv_sec
+ gq_rslt
.getquota_rslt_u
.gqr_rquota
.rq_ftimeleft
;
634 warnx("bad rpc result, host: %s", fst
->f_mntfromname
);
642 callaurpc(char *host
, int prognum
, int versnum
, int procnum
,
643 xdrproc_t inproc
, char *in
, xdrproc_t outproc
, char *out
)
645 struct sockaddr_in server_addr
;
646 enum clnt_stat clnt_stat
;
648 struct timeval timeout
, tottimeout
;
650 CLIENT
*client
= NULL
;
651 int sock
= RPC_ANYSOCK
;
653 if ((hp
= gethostbyname(host
)) == NULL
)
654 return ((int) RPC_UNKNOWNHOST
);
657 bcopy(hp
->h_addr
, &server_addr
.sin_addr
,
658 MIN(hp
->h_length
,(int)sizeof(server_addr
.sin_addr
)));
659 server_addr
.sin_family
= AF_INET
;
660 server_addr
.sin_port
= 0;
662 if ((client
= clntudp_create(&server_addr
, prognum
,
663 versnum
, timeout
, &sock
)) == NULL
)
664 return ((int) rpc_createerr
.cf_stat
);
666 client
->cl_auth
= authunix_create_default();
667 tottimeout
.tv_sec
= 25;
668 tottimeout
.tv_usec
= 0;
669 clnt_stat
= clnt_call(client
, procnum
, inproc
, in
,
670 outproc
, out
, tottimeout
);
672 return ((int) clnt_stat
);
684 } while ((c
= *s
++));