quota.c revision 1.41 1 /* $NetBSD: quota.c,v 1.41 2011/11/30 16:07:28 dholland Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Robert Elz at The University of Melbourne.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
38 The Regents of the University of California. All rights reserved.");
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)quota.c 8.4 (Berkeley) 4/28/95";
44 #else
45 __RCSID("$NetBSD: quota.c,v 1.41 2011/11/30 16:07:28 dholland Exp $");
46 #endif
47 #endif /* not lint */
48
49 /*
50 * Disk quota reporting program.
51 */
52 #include <sys/param.h>
53 #include <sys/types.h>
54 #include <sys/file.h>
55 #include <sys/stat.h>
56 #include <sys/mount.h>
57 #include <sys/socket.h>
58
59 #include <ctype.h>
60 #include <err.h>
61 #include <errno.h>
62 #include <fstab.h>
63 #include <grp.h>
64 #include <netdb.h>
65 #include <pwd.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <time.h>
70 #include <unistd.h>
71
72 #include <quota/quotaprop.h>
73 #include <quota/quota.h>
74
75 #include "printquota.h"
76 #include "getvfsquota.h"
77
78 struct quotause {
79 struct quotause *next;
80 long flags;
81 uid_t id;
82 struct quotaval qv[QUOTA_NLIMITS];
83 char fsname[MAXPATHLEN + 1];
84 };
85 #define FOUND 0x01
86 #define QUOTA2 0x02
87
88 static struct quotause *getprivs(id_t, int);
89 static void heading(int, id_t, const char *, const char *);
90 static void showgid(gid_t);
91 static void showgrpname(const char *);
92 static void showonequota(int, id_t, const char *, struct quotause *);
93 static void showquotas(int, id_t, const char *);
94 static void showuid(uid_t);
95 static void showusrname(const char *);
96 static void usage(void) __dead;
97
98 static int qflag = 0;
99 static int vflag = 0;
100 static int hflag = 0;
101 static int dflag = 0;
102 static int Dflag = 0;
103 static uid_t myuid;
104 static int needheading;
105
106 int
107 main(int argc, char *argv[])
108 {
109 int ngroups;
110 gid_t mygid, gidset[NGROUPS];
111 int i, gflag = 0, uflag = 0;
112 int ch;
113
114 myuid = getuid();
115 while ((ch = getopt(argc, argv, "Ddhugvq")) != -1) {
116 switch(ch) {
117 case 'g':
118 gflag++;
119 break;
120 case 'u':
121 uflag++;
122 break;
123 case 'v':
124 vflag++;
125 break;
126 case 'q':
127 qflag++;
128 break;
129 case 'h':
130 hflag++;
131 break;
132 case 'd':
133 dflag++;
134 break;
135 case 'D':
136 Dflag++;
137 break;
138 default:
139 usage();
140 }
141 }
142 argc -= optind;
143 argv += optind;
144 if (!uflag && !gflag)
145 uflag++;
146 if (dflag) {
147 #if 0
148 if (myuid != 0)
149 errx(1, "-d: permission denied");
150 #endif
151 if (uflag)
152 showquotas(QUOTA_CLASS_USER, 0, "");
153 if (gflag)
154 showquotas(QUOTA_CLASS_GROUP, 0, "");
155 return 0;
156 }
157 if (argc == 0) {
158 if (uflag)
159 showuid(myuid);
160 if (gflag) {
161 if (dflag)
162 showgid(0);
163 else {
164 mygid = getgid();
165 ngroups = getgroups(NGROUPS, gidset);
166 if (ngroups < 0)
167 err(1, "getgroups");
168 showgid(mygid);
169 for (i = 0; i < ngroups; i++)
170 if (gidset[i] != mygid)
171 showgid(gidset[i]);
172 }
173 }
174 return 0;
175 }
176 if (uflag && gflag)
177 usage();
178 if (uflag) {
179 for (; argc > 0; argc--, argv++) {
180 if (alldigits(*argv))
181 showuid((uid_t)atoi(*argv));
182 else
183 showusrname(*argv);
184 }
185 return 0;
186 }
187 if (gflag) {
188 for (; argc > 0; argc--, argv++) {
189 if (alldigits(*argv))
190 showgid((gid_t)atoi(*argv));
191 else
192 showgrpname(*argv);
193 }
194 return 0;
195 }
196 /* NOTREACHED */
197 return 0;
198 }
199
200 static void
201 usage(void)
202 {
203 const char *p = getprogname();
204 fprintf(stderr, "Usage: %s [-Dhguqv]\n"
205 "\t%s [-Dhqv] -u username ...\n"
206 "\t%s [-Dhqv] -g groupname ...\n"
207 "\t%s -d [-Dhguqv]\n", p, p, p, p);
208 exit(1);
209 }
210
211 /*
212 * Print out quotas for a specified user identifier.
213 */
214 static void
215 showuid(uid_t uid)
216 {
217 struct passwd *pwd = getpwuid(uid);
218 const char *name;
219
220 if (pwd == NULL)
221 name = "(no account)";
222 else
223 name = pwd->pw_name;
224 if (uid != myuid && myuid != 0) {
225 warnx("%s (uid %d): permission denied", name, uid);
226 return;
227 }
228 showquotas(QUOTA_CLASS_USER, uid, name);
229 }
230
231 /*
232 * Print out quotas for a specified user name.
233 */
234 static void
235 showusrname(const char *name)
236 {
237 struct passwd *pwd = getpwnam(name);
238
239 if (pwd == NULL) {
240 warnx("%s: unknown user", name);
241 return;
242 }
243 if (pwd->pw_uid != myuid && myuid != 0) {
244 warnx("%s (uid %d): permission denied", name, pwd->pw_uid);
245 return;
246 }
247 showquotas(QUOTA_CLASS_USER, pwd->pw_uid, name);
248 }
249
250 /*
251 * Print out quotas for a specified group identifier.
252 */
253 static void
254 showgid(gid_t gid)
255 {
256 struct group *grp = getgrgid(gid);
257 int ngroups;
258 gid_t mygid, gidset[NGROUPS];
259 int i;
260 const char *name;
261
262 if (grp == NULL)
263 name = "(no entry)";
264 else
265 name = grp->gr_name;
266 mygid = getgid();
267 ngroups = getgroups(NGROUPS, gidset);
268 if (ngroups < 0) {
269 warn("getgroups");
270 return;
271 }
272 if (gid != mygid) {
273 for (i = 0; i < ngroups; i++)
274 if (gid == gidset[i])
275 break;
276 if (i >= ngroups && myuid != 0) {
277 warnx("%s (gid %d): permission denied", name, gid);
278 return;
279 }
280 }
281 showquotas(QUOTA_CLASS_GROUP, gid, name);
282 }
283
284 /*
285 * Print out quotas for a specified group name.
286 */
287 static void
288 showgrpname(const char *name)
289 {
290 struct group *grp = getgrnam(name);
291 int ngroups;
292 gid_t mygid, gidset[NGROUPS];
293 int i;
294
295 if (grp == NULL) {
296 warnx("%s: unknown group", name);
297 return;
298 }
299 mygid = getgid();
300 ngroups = getgroups(NGROUPS, gidset);
301 if (ngroups < 0) {
302 warn("getgroups");
303 return;
304 }
305 if (grp->gr_gid != mygid) {
306 for (i = 0; i < ngroups; i++)
307 if (grp->gr_gid == gidset[i])
308 break;
309 if (i >= ngroups && myuid != 0) {
310 warnx("%s (gid %d): permission denied",
311 name, grp->gr_gid);
312 return;
313 }
314 }
315 showquotas(QUOTA_CLASS_GROUP, grp->gr_gid, name);
316 }
317
318 static void
319 showquotas(int type, id_t id, const char *name)
320 {
321 struct quotause *qup;
322 struct quotause *quplist;
323
324 needheading = 1;
325
326 quplist = getprivs(id, type);
327 for (qup = quplist; qup; qup = qup->next) {
328 showonequota(type, id, name, qup);
329 }
330 if (!qflag) {
331 /* In case nothing printed, issue a header saying "none" */
332 heading(type, id, name, "none");
333 }
334 }
335
336 static void
337 showonequota(int type, id_t id, const char *name, struct quotause *qup)
338 {
339 char b0[20], b1[20], b2[20], b3[20];
340 const char *msgi, *msgb, *nam, *timemsg;
341 int ql_stat;
342 struct quotaval *q = qup->qv;
343 static time_t now;
344
345 if (now == 0) {
346 time(&now);
347 }
348
349 if (!vflag &&
350 q[QUOTA_LIMIT_BLOCK].qv_softlimit == UQUAD_MAX &&
351 q[QUOTA_LIMIT_BLOCK].qv_hardlimit == UQUAD_MAX &&
352 q[QUOTA_LIMIT_FILE].qv_softlimit == UQUAD_MAX &&
353 q[QUOTA_LIMIT_FILE].qv_hardlimit == UQUAD_MAX)
354 return;
355 ql_stat = quota_check_limit(q[QUOTA_LIMIT_FILE].qv_usage, 1,
356 q[QUOTA_LIMIT_FILE].qv_softlimit,
357 q[QUOTA_LIMIT_FILE].qv_hardlimit,
358 q[QUOTA_LIMIT_FILE].qv_expiretime, now);
359 switch(QL_STATUS(ql_stat)) {
360 case QL_S_DENY_HARD:
361 msgi = "File limit reached on";
362 break;
363 case QL_S_DENY_GRACE:
364 msgi = "Over file quota on";
365 break;
366 case QL_S_ALLOW_SOFT:
367 msgi = "In file grace period on";
368 break;
369 default:
370 msgi = NULL;
371 }
372 ql_stat = quota_check_limit(q[QUOTA_LIMIT_BLOCK].qv_usage, 1,
373 q[QUOTA_LIMIT_BLOCK].qv_softlimit,
374 q[QUOTA_LIMIT_BLOCK].qv_hardlimit,
375 q[QUOTA_LIMIT_BLOCK].qv_expiretime, now);
376 switch(QL_STATUS(ql_stat)) {
377 case QL_S_DENY_HARD:
378 msgb = "Block limit reached on";
379 break;
380 case QL_S_DENY_GRACE:
381 msgb = "Over block quota on";
382 break;
383 case QL_S_ALLOW_SOFT:
384 msgb = "In block grace period on";
385 break;
386 default:
387 msgb = NULL;
388 }
389 if (qflag) {
390 if (msgi != NULL) {
391 heading(type, id, name, "");
392 printf("\t%s %s\n", msgi, qup->fsname);
393 }
394 if (msgb != NULL) {
395 heading(type, id, name, "");
396 printf("\t%s %s\n", msgb, qup->fsname);
397 }
398 return;
399 }
400 if (vflag || dflag || msgi || msgb ||
401 q[QUOTA_LIMIT_BLOCK].qv_usage ||
402 q[QUOTA_LIMIT_FILE].qv_usage) {
403 heading(type, id, name, "");
404 nam = qup->fsname;
405 if (strlen(qup->fsname) > 4) {
406 printf("%s\n", qup->fsname);
407 nam = "";
408 }
409 if (msgb)
410 timemsg = timeprt(b0, 9, now,
411 q[QUOTA_LIMIT_BLOCK].qv_expiretime);
412 else if ((qup->flags & QUOTA2) != 0 && vflag)
413 timemsg = timeprt(b0, 9, 0,
414 q[QUOTA_LIMIT_BLOCK].qv_grace);
415 else
416 timemsg = "";
417
418 printf("%12s%9s%c%8s%9s%8s",
419 nam,
420 intprt(b1, 9, q[QUOTA_LIMIT_BLOCK].qv_usage,
421 HN_B, hflag),
422 (msgb == NULL) ? ' ' : '*',
423 intprt(b2, 9, q[QUOTA_LIMIT_BLOCK].qv_softlimit,
424 HN_B, hflag),
425 intprt(b3, 9, q[QUOTA_LIMIT_BLOCK].qv_hardlimit,
426 HN_B, hflag),
427 timemsg);
428
429 if (msgi)
430 timemsg = timeprt(b0, 9, now,
431 q[QUOTA_LIMIT_FILE].qv_expiretime);
432 else if ((qup->flags & QUOTA2) != 0 && vflag)
433 timemsg = timeprt(b0, 9, 0,
434 q[QUOTA_LIMIT_FILE].qv_grace);
435 else
436 timemsg = "";
437
438 printf("%8s%c%7s%8s%8s\n",
439 intprt(b1, 8, q[QUOTA_LIMIT_FILE].qv_usage, 0,
440 hflag),
441 (msgi == NULL) ? ' ' : '*',
442 intprt(b2, 8, q[QUOTA_LIMIT_FILE].qv_softlimit,
443 0, hflag),
444 intprt(b3, 8, q[QUOTA_LIMIT_FILE].qv_hardlimit,
445 0, hflag),
446 timemsg);
447 return;
448 }
449 }
450
451 static void
452 heading(int type, id_t id, const char *name, const char *tag)
453 {
454 if (needheading == 0)
455 return;
456 needheading = 0;
457
458 if (dflag)
459 printf("Default %s disk quotas: %s\n",
460 ufs_quota_class_names[type], tag);
461 else
462 printf("Disk quotas for %s %s (%cid %u): %s\n",
463 ufs_quota_class_names[type], name,
464 *ufs_quota_class_names[type], id, tag);
465
466 if (!qflag && tag[0] == '\0') {
467 printf("%12s%9s %8s%9s%8s%8s %7s%8s%8s\n"
468 , "Filesystem"
469 , "blocks"
470 , "quota"
471 , "limit"
472 , "grace"
473 , "files"
474 , "quota"
475 , "limit"
476 , "grace"
477 );
478 }
479 }
480
481 /*
482 * Collect the requested quota information.
483 */
484 static struct quotause *
485 getprivs(id_t id, int quotatype)
486 {
487 struct quotause *qup, *quptail;
488 struct quotause *quphead;
489 struct statvfs *fst;
490 int nfst, i;
491 int8_t version;
492
493 qup = quphead = quptail = NULL;
494
495 nfst = getmntinfo(&fst, MNT_WAIT);
496 if (nfst == 0)
497 errx(2, "no filesystems mounted!");
498 for (i = 0; i < nfst; i++) {
499 if (qup == NULL) {
500 if ((qup = malloc(sizeof *qup)) == NULL)
501 err(1, "out of memory");
502 }
503 if (strncmp(fst[i].f_fstypename, "nfs",
504 sizeof(fst[i].f_fstypename)) == 0) {
505 version = 0;
506 if (getnfsquota(fst[i].f_mntfromname,
507 qup->qv, id, ufs_quota_class_names[quotatype]) != 1)
508 continue;
509 } else if ((fst[i].f_flag & ST_QUOTA) != 0) {
510 if (getvfsquota(fst[i].f_mntonname, qup->qv, &version,
511 id, quotatype, dflag, Dflag) != 1)
512 continue;
513 } else
514 continue;
515 (void)strncpy(qup->fsname, fst[i].f_mntonname,
516 sizeof(qup->fsname) - 1);
517 if (version == 2)
518 qup->flags |= QUOTA2;
519 if (quphead == NULL)
520 quphead = qup;
521 else
522 quptail->next = qup;
523 quptail = qup;
524 quptail->next = 0;
525 qup = NULL;
526 }
527 free(qup);
528 return quphead;
529 }
530