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