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