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[] = "@(#)repquota.c 8.1 (Berkeley) 6/6/93"; 45 #endif /* not lint */ 46 47 /* 48 * Quota report 49 */ 50 #include <sys/param.h> 51 #include <sys/stat.h> 52 #include <ufs/ufs/quota.h> 53 #include <fstab.h> 54 #include <pwd.h> 55 #include <grp.h> 56 #include <stdio.h> 57 #include <errno.h> 58 59 char *qfname = QUOTAFILENAME; 60 char *qfextension[] = INITQFNAMES; 61 62 struct fileusage { 63 struct fileusage *fu_next; 64 struct dqblk fu_dqblk; 65 u_long fu_id; 66 char fu_name[1]; 67 /* actually bigger */ 68 }; 69 #define FUHASH 1024 /* must be power of two */ 70 struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 71 struct fileusage *lookup(); 72 struct fileusage *addid(); 73 u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 74 75 int vflag; /* verbose */ 76 int aflag; /* all file systems */ 77 78 main(argc, argv) 79 int argc; 80 char **argv; 81 { 82 register struct fstab *fs; 83 register struct passwd *pw; 84 register struct group *gr; 85 int gflag = 0, uflag = 0, errs = 0; 86 long i, argnum, done = 0; 87 extern char *optarg; 88 extern int optind; 89 char ch, *qfnp; 90 91 while ((ch = getopt(argc, argv, "aguv")) != EOF) { 92 switch(ch) { 93 case 'a': 94 aflag++; 95 break; 96 case 'g': 97 gflag++; 98 break; 99 case 'u': 100 uflag++; 101 break; 102 case 'v': 103 vflag++; 104 break; 105 default: 106 usage(); 107 } 108 } 109 argc -= optind; 110 argv += optind; 111 if (argc == 0 && !aflag) 112 usage(); 113 if (!gflag && !uflag) { 114 if (aflag) 115 gflag++; 116 uflag++; 117 } 118 if (gflag) { 119 setgrent(); 120 while ((gr = getgrent()) != 0) 121 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 122 endgrent(); 123 } 124 if (uflag) { 125 setpwent(); 126 while ((pw = getpwent()) != 0) 127 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); 128 endpwent(); 129 } 130 setfsent(); 131 while ((fs = getfsent()) != NULL) { 132 if (strcmp(fs->fs_vfstype, "ufs")) 133 continue; 134 if (aflag) { 135 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) 136 errs += repquota(fs, GRPQUOTA, qfnp); 137 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) 138 errs += repquota(fs, USRQUOTA, qfnp); 139 continue; 140 } 141 if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 142 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) { 143 done |= 1 << argnum; 144 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) 145 errs += repquota(fs, GRPQUOTA, qfnp); 146 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) 147 errs += repquota(fs, USRQUOTA, qfnp); 148 } 149 } 150 endfsent(); 151 for (i = 0; i < argc; i++) 152 if ((done & (1 << i)) == 0) 153 fprintf(stderr, "%s not found in fstab\n", argv[i]); 154 exit(errs); 155 } 156 157 usage() 158 { 159 fprintf(stderr, "Usage:\n\t%s\n\t%s\n", 160 "repquota [-v] [-g] [-u] -a", 161 "repquota [-v] [-g] [-u] filesys ..."); 162 exit(1); 163 } 164 165 repquota(fs, type, qfpathname) 166 register struct fstab *fs; 167 int type; 168 char *qfpathname; 169 { 170 register struct fileusage *fup; 171 FILE *qf; 172 u_long id; 173 struct dqblk dqbuf; 174 char *timeprt(); 175 static struct dqblk zerodqblk; 176 static int warned = 0; 177 static int multiple = 0; 178 extern int errno; 179 180 if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 && 181 errno == EOPNOTSUPP && !warned && vflag) { 182 warned++; 183 fprintf(stdout, 184 "*** Warning: Quotas are not compiled into this kernel\n"); 185 } 186 if (multiple++) 187 printf("\n"); 188 if (vflag) 189 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n", 190 qfextension[type], fs->fs_file, fs->fs_spec); 191 if ((qf = fopen(qfpathname, "r")) == NULL) { 192 perror(qfpathname); 193 return (1); 194 } 195 for (id = 0; ; id++) { 196 fread(&dqbuf, sizeof(struct dqblk), 1, qf); 197 if (feof(qf)) 198 break; 199 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0) 200 continue; 201 if ((fup = lookup(id, type)) == 0) 202 fup = addid(id, type, (char *)0); 203 fup->fu_dqblk = dqbuf; 204 } 205 fclose(qf); 206 printf(" Block limits File limits\n"); 207 printf("User used soft hard grace used soft hard grace\n"); 208 for (id = 0; id <= highid[type]; id++) { 209 fup = lookup(id, type); 210 if (fup == 0) 211 continue; 212 if (fup->fu_dqblk.dqb_curinodes == 0 && 213 fup->fu_dqblk.dqb_curblocks == 0) 214 continue; 215 printf("%-10s", fup->fu_name); 216 printf("%c%c%8d%8d%8d%7s", 217 fup->fu_dqblk.dqb_bsoftlimit && 218 fup->fu_dqblk.dqb_curblocks >= 219 fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-', 220 fup->fu_dqblk.dqb_isoftlimit && 221 fup->fu_dqblk.dqb_curinodes >= 222 fup->fu_dqblk.dqb_isoftlimit ? '+' : '-', 223 dbtob(fup->fu_dqblk.dqb_curblocks) / 1024, 224 dbtob(fup->fu_dqblk.dqb_bsoftlimit) / 1024, 225 dbtob(fup->fu_dqblk.dqb_bhardlimit) / 1024, 226 fup->fu_dqblk.dqb_bsoftlimit && 227 fup->fu_dqblk.dqb_curblocks >= 228 fup->fu_dqblk.dqb_bsoftlimit ? 229 timeprt(fup->fu_dqblk.dqb_btime) : ""); 230 printf(" %6d%6d%6d%7s\n", 231 fup->fu_dqblk.dqb_curinodes, 232 fup->fu_dqblk.dqb_isoftlimit, 233 fup->fu_dqblk.dqb_ihardlimit, 234 fup->fu_dqblk.dqb_isoftlimit && 235 fup->fu_dqblk.dqb_curinodes >= 236 fup->fu_dqblk.dqb_isoftlimit ? 237 timeprt(fup->fu_dqblk.dqb_itime) : ""); 238 fup->fu_dqblk = zerodqblk; 239 } 240 return (0); 241 } 242 243 /* 244 * Check to see if target appears in list of size cnt. 245 */ 246 oneof(target, list, cnt) 247 register char *target, *list[]; 248 int cnt; 249 { 250 register int i; 251 252 for (i = 0; i < cnt; i++) 253 if (strcmp(target, list[i]) == 0) 254 return (i); 255 return (-1); 256 } 257 258 /* 259 * Check to see if a particular quota is to be enabled. 260 */ 261 hasquota(fs, type, qfnamep) 262 register struct fstab *fs; 263 int type; 264 char **qfnamep; 265 { 266 register char *opt; 267 char *cp, *index(), *strtok(); 268 static char initname, usrname[100], grpname[100]; 269 static char buf[BUFSIZ]; 270 271 if (!initname) { 272 sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 273 sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 274 initname = 1; 275 } 276 strcpy(buf, fs->fs_mntops); 277 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 278 if (cp = index(opt, '=')) 279 *cp++ = '\0'; 280 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 281 break; 282 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 283 break; 284 } 285 if (!opt) 286 return (0); 287 if (cp) { 288 *qfnamep = cp; 289 return (1); 290 } 291 (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 292 *qfnamep = buf; 293 return (1); 294 } 295 296 /* 297 * Routines to manage the file usage table. 298 * 299 * Lookup an id of a specific type. 300 */ 301 struct fileusage * 302 lookup(id, type) 303 u_long id; 304 int type; 305 { 306 register struct fileusage *fup; 307 308 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 309 if (fup->fu_id == id) 310 return (fup); 311 return ((struct fileusage *)0); 312 } 313 314 /* 315 * Add a new file usage id if it does not already exist. 316 */ 317 struct fileusage * 318 addid(id, type, name) 319 u_long id; 320 int type; 321 char *name; 322 { 323 struct fileusage *fup, **fhp; 324 int len; 325 extern char *calloc(); 326 327 if (fup = lookup(id, type)) 328 return (fup); 329 if (name) 330 len = strlen(name); 331 else 332 len = 10; 333 if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) { 334 fprintf(stderr, "out of memory for fileusage structures\n"); 335 exit(1); 336 } 337 fhp = &fuhead[type][id & (FUHASH - 1)]; 338 fup->fu_next = *fhp; 339 *fhp = fup; 340 fup->fu_id = id; 341 if (id > highid[type]) 342 highid[type] = id; 343 if (name) { 344 bcopy(name, fup->fu_name, len + 1); 345 } else { 346 sprintf(fup->fu_name, "%u", id); 347 } 348 return (fup); 349 } 350 351 /* 352 * Calculate the grace period and return a printable string for it. 353 */ 354 char * 355 timeprt(seconds) 356 time_t seconds; 357 { 358 time_t hours, minutes; 359 static char buf[20]; 360 static time_t now; 361 362 if (now == 0) 363 time(&now); 364 if (now > seconds) 365 return ("none"); 366 seconds -= now; 367 minutes = (seconds + 30) / 60; 368 hours = (minutes + 30) / 60; 369 if (hours >= 36) { 370 sprintf(buf, "%ddays", (hours + 12) / 24); 371 return (buf); 372 } 373 if (minutes >= 60) { 374 sprintf(buf, "%2d:%d", minutes / 60, minutes % 60); 375 return (buf); 376 } 377 sprintf(buf, "%2d", minutes); 378 return (buf); 379 } 380