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