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