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