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