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