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 %lu): 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 %lu): 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%8lu%c%7lu%8lu%8s" 319 , qup->fsname 320 , (u_long) 321 (dbtob(qup->dqblk.dqb_curblocks) / 1024) 322 , (msgb == (char *)0) ? ' ' : '*' 323 , (u_long) 324 (dbtob(qup->dqblk.dqb_bsoftlimit) / 1024) 325 , (u_long) 326 (dbtob(qup->dqblk.dqb_bhardlimit) / 1024) 327 , (msgb == (char *)0) ? "" 328 : timeprt(qup->dqblk.dqb_btime)); 329 printf("%8lu%c%7lu%8lu%8s\n" 330 , qup->dqblk.dqb_curinodes 331 , (msgi == (char *)0) ? ' ' : '*' 332 , qup->dqblk.dqb_isoftlimit 333 , qup->dqblk.dqb_ihardlimit 334 , (msgi == (char *)0) ? "" 335 : timeprt(qup->dqblk.dqb_itime) 336 ); 337 continue; 338 } 339 } 340 if (!qflag && lines == 0) 341 heading(type, id, name, "none"); 342 } 343 344 heading(type, id, name, tag) 345 int type; 346 u_long id; 347 char *name, *tag; 348 { 349 350 printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type], 351 name, *qfextension[type], id, tag); 352 if (!qflag && tag[0] == '\0') { 353 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n" 354 , "Filesystem" 355 , "blocks" 356 , "quota" 357 , "limit" 358 , "grace" 359 , "files" 360 , "quota" 361 , "limit" 362 , "grace" 363 ); 364 } 365 } 366 367 /* 368 * Calculate the grace period and return a printable string for it. 369 */ 370 char * 371 timeprt(seconds) 372 time_t seconds; 373 { 374 time_t hours, minutes; 375 static char buf[20]; 376 static time_t now; 377 378 if (now == 0) 379 time(&now); 380 if (now > seconds) 381 return ("none"); 382 seconds -= now; 383 minutes = (seconds + 30) / 60; 384 hours = (minutes + 30) / 60; 385 if (hours >= 36) { 386 sprintf(buf, "%lddays", (hours + 12) / 24); 387 return (buf); 388 } 389 if (minutes >= 60) { 390 sprintf(buf, "%2ld:%ld", minutes / 60, minutes % 60); 391 return (buf); 392 } 393 sprintf(buf, "%2ld", minutes); 394 return (buf); 395 } 396 397 /* 398 * Collect the requested quota information. 399 */ 400 struct quotause * 401 getprivs(id, quotatype) 402 register long id; 403 int quotatype; 404 { 405 register struct fstab *fs; 406 register struct quotause *qup, *quptail; 407 struct quotause *quphead; 408 char *qfpathname; 409 int qcmd, fd; 410 411 setfsent(); 412 quphead = (struct quotause *)0; 413 qcmd = QCMD(Q_GETQUOTA, quotatype); 414 while (fs = getfsent()) { 415 if (strcmp(fs->fs_vfstype, "ufs")) 416 continue; 417 if (!hasquota(fs, quotatype, &qfpathname)) 418 continue; 419 if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) { 420 fprintf(stderr, "quota: out of memory\n"); 421 exit(2); 422 } 423 if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) { 424 if ((fd = open(qfpathname, O_RDONLY)) < 0) { 425 perror(qfpathname); 426 free(qup); 427 continue; 428 } 429 lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET); 430 switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { 431 case 0: /* EOF */ 432 /* 433 * Convert implicit 0 quota (EOF) 434 * into an explicit one (zero'ed dqblk) 435 */ 436 bzero((caddr_t)&qup->dqblk, 437 sizeof(struct dqblk)); 438 break; 439 440 case sizeof(struct dqblk): /* OK */ 441 break; 442 443 default: /* ERROR */ 444 fprintf(stderr, "quota: read error"); 445 perror(qfpathname); 446 close(fd); 447 free(qup); 448 continue; 449 } 450 close(fd); 451 } 452 strcpy(qup->fsname, fs->fs_file); 453 if (quphead == NULL) 454 quphead = qup; 455 else 456 quptail->next = qup; 457 quptail = qup; 458 qup->next = 0; 459 } 460 endfsent(); 461 return (quphead); 462 } 463 464 /* 465 * Check to see if a particular quota is to be enabled. 466 */ 467 hasquota(fs, type, qfnamep) 468 register struct fstab *fs; 469 int type; 470 char **qfnamep; 471 { 472 register char *opt; 473 char *cp, *index(), *strtok(); 474 static char initname, usrname[100], grpname[100]; 475 static char buf[BUFSIZ]; 476 477 if (!initname) { 478 sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 479 sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 480 initname = 1; 481 } 482 strcpy(buf, fs->fs_mntops); 483 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 484 if (cp = index(opt, '=')) 485 *cp++ = '\0'; 486 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 487 break; 488 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 489 break; 490 } 491 if (!opt) 492 return (0); 493 if (cp) { 494 *qfnamep = cp; 495 return (1); 496 } 497 (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 498 *qfnamep = buf; 499 return (1); 500 } 501 502 alldigits(s) 503 register char *s; 504 { 505 register c; 506 507 c = *s++; 508 do { 509 if (!isdigit(c)) 510 return (0); 511 } while (c = *s++); 512 return (1); 513 } 514