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