quota.c revision 1.5 1 /*
2 * Copyright (c) 1980, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Robert Elz at The University of Melbourne.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
23 *
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
34 * SUCH DAMAGE.
35 */
36
37 #ifndef lint
38 static char copyright[] =
39 "@(#) Copyright (c) 1980, 1990, 1993\n\
40 The Regents of the University of California. All rights reserved.\n";
41 #endif /* not lint */
42
43 #ifndef lint
44 /*static char sccsid[] = "from: @(#)quota.c 8.1 (Berkeley) 6/6/93";*/
45 static char rcsid[] = "$Id: quota.c,v 1.5 1994/06/13 21:33:18 mycroft Exp $";
46 #endif /* not lint */
47
48 /*
49 * Disk quota reporting program.
50 */
51 #include <sys/param.h>
52 #include <sys/file.h>
53 #include <sys/stat.h>
54 #include <ufs/ufs/quota.h>
55 #include <stdio.h>
56 #include <fstab.h>
57 #include <ctype.h>
58 #include <pwd.h>
59 #include <grp.h>
60 #include <errno.h>
61
62 char *qfname = QUOTAFILENAME;
63 char *qfextension[] = INITQFNAMES;
64
65 struct quotause {
66 struct quotause *next;
67 long flags;
68 struct dqblk dqblk;
69 char fsname[MAXPATHLEN + 1];
70 } *getprivs();
71 #define FOUND 0x01
72
73 int qflag;
74 int vflag;
75
76 main(argc, argv)
77 char *argv[];
78 {
79 int ngroups;
80 gid_t gidset[NGROUPS];
81 int i, gflag = 0, uflag = 0;
82 char ch;
83 extern char *optarg;
84 extern int optind, errno;
85
86 if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) {
87 fprintf(stderr, "There are no quotas on this system\n");
88 exit(0);
89 }
90 while ((ch = getopt(argc, argv, "ugvq")) != EOF) {
91 switch(ch) {
92 case 'g':
93 gflag++;
94 break;
95 case 'u':
96 uflag++;
97 break;
98 case 'v':
99 vflag++;
100 break;
101 case 'q':
102 qflag++;
103 break;
104 default:
105 usage();
106 }
107 }
108 argc -= optind;
109 argv += optind;
110 if (!uflag && !gflag)
111 uflag++;
112 if (argc == 0) {
113 if (uflag)
114 showuid(getuid());
115 if (gflag) {
116 ngroups = getgroups(NGROUPS, gidset);
117 if (ngroups < 0) {
118 perror("quota: getgroups");
119 exit(1);
120 }
121 for (i = 1; i < ngroups; i++)
122 showgid(gidset[i]);
123 }
124 exit(0);
125 }
126 if (uflag && gflag)
127 usage();
128 if (uflag) {
129 for (; argc > 0; argc--, argv++) {
130 if (alldigits(*argv))
131 showuid(atoi(*argv));
132 else
133 showusrname(*argv);
134 }
135 exit(0);
136 }
137 if (gflag) {
138 for (; argc > 0; argc--, argv++) {
139 if (alldigits(*argv))
140 showgid(atoi(*argv));
141 else
142 showgrpname(*argv);
143 }
144 exit(0);
145 }
146 }
147
148 usage()
149 {
150
151 fprintf(stderr, "%s\n%s\n%s\n",
152 "Usage: quota [-guqv]",
153 "\tquota [-qv] -u username ...",
154 "\tquota [-qv] -g groupname ...");
155 exit(1);
156 }
157
158 /*
159 * Print out quotas for a specified user identifier.
160 */
161 showuid(uid)
162 u_long uid;
163 {
164 struct passwd *pwd = getpwuid(uid);
165 u_long myuid;
166 char *name;
167
168 if (pwd == NULL)
169 name = "(no account)";
170 else
171 name = pwd->pw_name;
172 myuid = getuid();
173 if (uid != myuid && myuid != 0) {
174 printf("quota: %s (uid %d): permission denied\n", name, uid);
175 return;
176 }
177 showquotas(USRQUOTA, uid, name);
178 }
179
180 /*
181 * Print out quotas for a specifed user name.
182 */
183 showusrname(name)
184 char *name;
185 {
186 struct passwd *pwd = getpwnam(name);
187 u_long myuid;
188
189 if (pwd == NULL) {
190 fprintf(stderr, "quota: %s: unknown user\n", name);
191 return;
192 }
193 myuid = getuid();
194 if (pwd->pw_uid != myuid && myuid != 0) {
195 fprintf(stderr, "quota: %s (uid %d): permission denied\n",
196 name, pwd->pw_uid);
197 return;
198 }
199 showquotas(USRQUOTA, pwd->pw_uid, name);
200 }
201
202 /*
203 * Print out quotas for a specified group identifier.
204 */
205 showgid(gid)
206 u_long gid;
207 {
208 struct group *grp = getgrgid(gid);
209 int ngroups;
210 gid_t gidset[NGROUPS];
211 register int i;
212 char *name;
213
214 if (grp == NULL)
215 name = "(no entry)";
216 else
217 name = grp->gr_name;
218 ngroups = getgroups(NGROUPS, gidset);
219 if (ngroups < 0) {
220 perror("quota: getgroups");
221 return;
222 }
223 for (i = 1; i < ngroups; i++)
224 if (gid == gidset[i])
225 break;
226 if (i >= ngroups && getuid() != 0) {
227 fprintf(stderr, "quota: %s (gid %d): permission denied\n",
228 name, gid);
229 return;
230 }
231 showquotas(GRPQUOTA, gid, name);
232 }
233
234 /*
235 * Print out quotas for a specifed group name.
236 */
237 showgrpname(name)
238 char *name;
239 {
240 struct group *grp = getgrnam(name);
241 int ngroups;
242 gid_t gidset[NGROUPS];
243 register int i;
244
245 if (grp == NULL) {
246 fprintf(stderr, "quota: %s: unknown group\n", name);
247 return;
248 }
249 ngroups = getgroups(NGROUPS, gidset);
250 if (ngroups < 0) {
251 perror("quota: getgroups");
252 return;
253 }
254 for (i = 1; i < ngroups; i++)
255 if (grp->gr_gid == gidset[i])
256 break;
257 if (i >= ngroups && getuid() != 0) {
258 fprintf(stderr, "quota: %s (gid %d): permission denied\n",
259 name, grp->gr_gid);
260 return;
261 }
262 showquotas(GRPQUOTA, grp->gr_gid, name);
263 }
264
265 showquotas(type, id, name)
266 int type;
267 u_long id;
268 char *name;
269 {
270 register struct quotause *qup;
271 struct quotause *quplist, *getprivs();
272 char *msgi, *msgb, *timeprt();
273 int myuid, fd, lines = 0;
274 static int first;
275 static time_t now;
276
277 if (now == 0)
278 time(&now);
279 quplist = getprivs(id, type);
280 for (qup = quplist; qup; qup = qup->next) {
281 if (!vflag &&
282 qup->dqblk.dqb_isoftlimit == 0 &&
283 qup->dqblk.dqb_ihardlimit == 0 &&
284 qup->dqblk.dqb_bsoftlimit == 0 &&
285 qup->dqblk.dqb_bhardlimit == 0)
286 continue;
287 msgi = (char *)0;
288 if (qup->dqblk.dqb_ihardlimit &&
289 qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit)
290 msgi = "File limit reached on";
291 else if (qup->dqblk.dqb_isoftlimit &&
292 qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
293 if (qup->dqblk.dqb_itime > now)
294 msgi = "In file grace period on";
295 else
296 msgi = "Over file quota on";
297 msgb = (char *)0;
298 if (qup->dqblk.dqb_bhardlimit &&
299 qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit)
300 msgb = "Block limit reached on";
301 else if (qup->dqblk.dqb_bsoftlimit &&
302 qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
303 if (qup->dqblk.dqb_btime > now)
304 msgb = "In block grace period on";
305 else
306 msgb = "Over block quota on";
307 if (qflag) {
308 if ((msgi != (char *)0 || msgb != (char *)0) &&
309 lines++ == 0)
310 heading(type, id, name, "");
311 if (msgi != (char *)0)
312 printf("\t%s %s\n", msgi, qup->fsname);
313 if (msgb != (char *)0)
314 printf("\t%s %s\n", msgb, qup->fsname);
315 continue;
316 }
317 if (vflag ||
318 qup->dqblk.dqb_curblocks ||
319 qup->dqblk.dqb_curinodes) {
320 if (lines++ == 0)
321 heading(type, id, name, "");
322 printf("%15s%8d%c%7d%8d%8s"
323 , qup->fsname
324 , dbtob(qup->dqblk.dqb_curblocks) / 1024
325 , (msgb == (char *)0) ? ' ' : '*'
326 , dbtob(qup->dqblk.dqb_bsoftlimit) / 1024
327 , dbtob(qup->dqblk.dqb_bhardlimit) / 1024
328 , (msgb == (char *)0) ? ""
329 : timeprt(qup->dqblk.dqb_btime));
330 printf("%8d%c%7d%8d%8s\n"
331 , qup->dqblk.dqb_curinodes
332 , (msgi == (char *)0) ? ' ' : '*'
333 , qup->dqblk.dqb_isoftlimit
334 , qup->dqblk.dqb_ihardlimit
335 , (msgi == (char *)0) ? ""
336 : timeprt(qup->dqblk.dqb_itime)
337 );
338 continue;
339 }
340 }
341 if (!qflag && lines == 0)
342 heading(type, id, name, "none");
343 }
344
345 heading(type, id, name, tag)
346 int type;
347 u_long id;
348 char *name, *tag;
349 {
350
351 printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type],
352 name, *qfextension[type], id, tag);
353 if (!qflag && tag[0] == '\0') {
354 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
355 , "Filesystem"
356 , "blocks"
357 , "quota"
358 , "limit"
359 , "grace"
360 , "files"
361 , "quota"
362 , "limit"
363 , "grace"
364 );
365 }
366 }
367
368 /*
369 * Calculate the grace period and return a printable string for it.
370 */
371 char *
372 timeprt(seconds)
373 time_t seconds;
374 {
375 time_t hours, minutes;
376 static char buf[20];
377 static time_t now;
378
379 if (now == 0)
380 time(&now);
381 if (now > seconds)
382 return ("none");
383 seconds -= now;
384 minutes = (seconds + 30) / 60;
385 hours = (minutes + 30) / 60;
386 if (hours >= 36) {
387 sprintf(buf, "%ddays", (hours + 12) / 24);
388 return (buf);
389 }
390 if (minutes >= 60) {
391 sprintf(buf, "%2d:%d", minutes / 60, minutes % 60);
392 return (buf);
393 }
394 sprintf(buf, "%2d", minutes);
395 return (buf);
396 }
397
398 /*
399 * Collect the requested quota information.
400 */
401 struct quotause *
402 getprivs(id, quotatype)
403 register long id;
404 int quotatype;
405 {
406 register struct fstab *fs;
407 register struct quotause *qup, *quptail;
408 struct quotause *quphead;
409 char *qfpathname;
410 int qcmd, fd;
411
412 setfsent();
413 quphead = (struct quotause *)0;
414 qcmd = QCMD(Q_GETQUOTA, quotatype);
415 while (fs = getfsent()) {
416 if (strcmp(fs->fs_vfstype, "ufs"))
417 continue;
418 if (!hasquota(fs, quotatype, &qfpathname))
419 continue;
420 if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) {
421 fprintf(stderr, "quota: out of memory\n");
422 exit(2);
423 }
424 if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
425 if ((fd = open(qfpathname, O_RDONLY)) < 0) {
426 perror(qfpathname);
427 free(qup);
428 continue;
429 }
430 lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET);
431 switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
432 case 0: /* EOF */
433 /*
434 * Convert implicit 0 quota (EOF)
435 * into an explicit one (zero'ed dqblk)
436 */
437 bzero((caddr_t)&qup->dqblk,
438 sizeof(struct dqblk));
439 break;
440
441 case sizeof(struct dqblk): /* OK */
442 break;
443
444 default: /* ERROR */
445 fprintf(stderr, "quota: read error");
446 perror(qfpathname);
447 close(fd);
448 free(qup);
449 continue;
450 }
451 close(fd);
452 }
453 strcpy(qup->fsname, fs->fs_file);
454 if (quphead == NULL)
455 quphead = qup;
456 else
457 quptail->next = qup;
458 quptail = qup;
459 qup->next = 0;
460 }
461 endfsent();
462 return (quphead);
463 }
464
465 /*
466 * Check to see if a particular quota is to be enabled.
467 */
468 hasquota(fs, type, qfnamep)
469 register struct fstab *fs;
470 int type;
471 char **qfnamep;
472 {
473 register char *opt;
474 char *cp, *index(), *strtok();
475 static char initname, usrname[100], grpname[100];
476 static char buf[BUFSIZ];
477
478 if (!initname) {
479 sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
480 sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
481 initname = 1;
482 }
483 strcpy(buf, fs->fs_mntops);
484 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
485 if (cp = index(opt, '='))
486 *cp++ = '\0';
487 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
488 break;
489 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
490 break;
491 }
492 if (!opt)
493 return (0);
494 if (cp) {
495 *qfnamep = cp;
496 return (1);
497 }
498 (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
499 *qfnamep = buf;
500 return (1);
501 }
502
503 alldigits(s)
504 register char *s;
505 {
506 register c;
507
508 c = *s++;
509 do {
510 if (!isdigit(c))
511 return (0);
512 } while (c = *s++);
513 return (1);
514 }
515