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. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Quota report 35 */ 36 #include <sys/param.h> 37 #include <sys/stat.h> 38 #include <ufs/ufs/quota.h> 39 #include <fstab.h> 40 #include <pwd.h> 41 #include <grp.h> 42 #include <stdio.h> 43 #include <unistd.h> 44 #include <string.h> 45 #include <errno.h> 46 #include <stdlib.h> 47 48 char *qfname = QUOTAFILENAME; 49 char *qfextension[] = INITQFNAMES; 50 51 struct fileusage { 52 struct fileusage *fu_next; 53 struct dqblk fu_dqblk; 54 uid_t fu_id; 55 char fu_name[1]; 56 /* actually bigger */ 57 }; 58 #define FUHASH 1024 /* must be power of two */ 59 struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 60 struct fileusage *lookup(uid_t, int); 61 struct fileusage *addid(uid_t id, int type, char *name); 62 uid_t highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 63 64 int vflag; /* verbose */ 65 int aflag; /* all file systems */ 66 67 void usage(void); 68 int repquota(struct fstab *, int, char *); 69 int hasquota(struct fstab *, int, char **); 70 int oneof(char *, char *[], int); 71 char *timeprt(time_t); 72 int 73 main(int argc, char *argv[]) 74 { 75 struct fstab *fs; 76 struct passwd *pw; 77 struct group *gr; 78 int gflag = 0, uflag = 0, errs = 0; 79 long i, argnum, done = 0; 80 extern char *optarg; 81 extern int optind; 82 char *qfnp; 83 int ch; 84 85 while ((ch = getopt(argc, argv, "aguv")) != -1) { 86 switch(ch) { 87 case 'a': 88 aflag++; 89 break; 90 case 'g': 91 gflag++; 92 break; 93 case 'u': 94 uflag++; 95 break; 96 case 'v': 97 vflag++; 98 break; 99 default: 100 usage(); 101 } 102 } 103 argc -= optind; 104 argv += optind; 105 if (argc == 0 && !aflag) 106 usage(); 107 if (!gflag && !uflag) { 108 if (aflag) 109 gflag++; 110 uflag++; 111 } 112 if (gflag) { 113 setgrent(); 114 while ((gr = getgrent()) != 0) 115 (void) addid((uid_t)gr->gr_gid, GRPQUOTA, gr->gr_name); 116 endgrent(); 117 } 118 if (uflag) { 119 setpwent(); 120 while ((pw = getpwent()) != 0) 121 (void) addid(pw->pw_uid, USRQUOTA, pw->pw_name); 122 endpwent(); 123 } 124 setfsent(); 125 while ((fs = getfsent()) != NULL) { 126 if (strcmp(fs->fs_vfstype, "ffs") && 127 strcmp(fs->fs_vfstype, "ufs") && 128 strcmp(fs->fs_vfstype, "mfs")) 129 continue; 130 if (aflag) { 131 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) 132 errs += repquota(fs, GRPQUOTA, qfnp); 133 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) 134 errs += repquota(fs, USRQUOTA, qfnp); 135 continue; 136 } 137 if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 138 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) { 139 done |= 1 << argnum; 140 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) 141 errs += repquota(fs, GRPQUOTA, qfnp); 142 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) 143 errs += repquota(fs, USRQUOTA, qfnp); 144 } 145 } 146 endfsent(); 147 for (i = 0; i < argc; i++) 148 if ((done & (1 << i)) == 0) 149 fprintf(stderr, "%s not found in fstab\n", argv[i]); 150 exit(errs); 151 } 152 153 void 154 usage(void) 155 { 156 extern char *__progname; 157 fprintf(stderr, "usage: %s [-aguv] filesystem ...\n", __progname); 158 exit(1); 159 } 160 161 int 162 repquota(struct fstab *fs, int type, char *qfpathname) 163 { 164 struct fileusage *fup; 165 FILE *qf; 166 uid_t id; 167 struct dqblk dqbuf; 168 char *timeprt(time_t); 169 static struct dqblk zerodqblk; 170 static int warned = 0; 171 static int multiple = 0; 172 173 if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 && 174 errno == EOPNOTSUPP && !warned && vflag) { 175 warned++; 176 fprintf(stdout, 177 "*** Warning: Quotas are not compiled into this kernel\n"); 178 } 179 if (multiple++) 180 printf("\n"); 181 if (vflag) 182 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n", 183 qfextension[type], fs->fs_file, fs->fs_spec); 184 if ((qf = fopen(qfpathname, "r")) == NULL) { 185 perror(qfpathname); 186 return (1); 187 } 188 for (id = 0; ; id++) { 189 fread(&dqbuf, sizeof(struct dqblk), 1, qf); 190 if (feof(qf)) 191 break; 192 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0) 193 continue; 194 if ((fup = lookup(id, type)) == 0) 195 fup = addid(id, type, (char *)0); 196 fup->fu_dqblk = dqbuf; 197 } 198 fclose(qf); 199 printf(" KByte limits File limits\n"); 200 printf("User used soft hard grace used soft hard grace\n"); 201 for (id = 0; id <= highid[type]; id++) { 202 fup = lookup(id, type); 203 if (fup == 0) 204 continue; 205 if (fup->fu_dqblk.dqb_curinodes == 0 && 206 fup->fu_dqblk.dqb_curblocks == 0) 207 continue; 208 printf("%-10s", fup->fu_name); 209 printf("%c%c %7d %7d %7d %6s", 210 fup->fu_dqblk.dqb_bsoftlimit && 211 fup->fu_dqblk.dqb_curblocks >= 212 fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-', 213 fup->fu_dqblk.dqb_isoftlimit && 214 fup->fu_dqblk.dqb_curinodes >= 215 fup->fu_dqblk.dqb_isoftlimit ? '+' : '-', 216 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_curblocks) 217 / 1024), 218 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bsoftlimit) 219 / 1024), 220 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bhardlimit) 221 / 1024), 222 fup->fu_dqblk.dqb_bsoftlimit && 223 fup->fu_dqblk.dqb_curblocks >= 224 fup->fu_dqblk.dqb_bsoftlimit ? 225 timeprt(fup->fu_dqblk.dqb_btime) : ""); 226 printf(" %6d %5d %5d %6s\n", 227 fup->fu_dqblk.dqb_curinodes, 228 fup->fu_dqblk.dqb_isoftlimit, 229 fup->fu_dqblk.dqb_ihardlimit, 230 fup->fu_dqblk.dqb_isoftlimit && 231 fup->fu_dqblk.dqb_curinodes >= 232 fup->fu_dqblk.dqb_isoftlimit ? 233 timeprt(fup->fu_dqblk.dqb_itime) : ""); 234 fup->fu_dqblk = zerodqblk; 235 } 236 return (0); 237 } 238 239 /* 240 * Check to see if target appears in list of size cnt. 241 */ 242 int 243 oneof(char *target, char *list[], int cnt) 244 { 245 int i; 246 247 for (i = 0; i < cnt; i++) 248 if (strcmp(target, list[i]) == 0) 249 return (i); 250 return (-1); 251 } 252 253 /* 254 * Check to see if a particular quota is to be enabled. 255 */ 256 int 257 hasquota(struct fstab *fs, int type, char **qfnamep) 258 { 259 char *opt; 260 char *cp; 261 static char initname, usrname[100], grpname[100]; 262 static char buf[BUFSIZ]; 263 264 if (!initname) { 265 (void)snprintf(usrname, sizeof usrname, "%s%s", 266 qfextension[USRQUOTA], qfname); 267 (void)snprintf(grpname, sizeof grpname, "%s%s", 268 qfextension[GRPQUOTA], qfname); 269 initname = 1; 270 } 271 strlcpy(buf, fs->fs_mntops, sizeof buf); 272 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 273 if ((cp = strchr(opt, '='))) 274 *cp++ = '\0'; 275 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 276 break; 277 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 278 break; 279 } 280 if (!opt) 281 return (0); 282 if (cp) { 283 *qfnamep = cp; 284 return (1); 285 } 286 (void)snprintf(buf, sizeof buf, "%s/%s.%s", 287 fs->fs_file, qfname, qfextension[type]); 288 *qfnamep = buf; 289 return (1); 290 } 291 292 /* 293 * Routines to manage the file usage table. 294 * 295 * Lookup an id of a specific type. 296 */ 297 struct fileusage * 298 lookup(uid_t id, int type) 299 { 300 struct fileusage *fup; 301 302 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 303 if (fup->fu_id == id) 304 return (fup); 305 return ((struct fileusage *)0); 306 } 307 308 /* 309 * Add a new file usage id if it does not already exist. 310 */ 311 struct fileusage * 312 addid(uid_t id, int type, char *name) 313 { 314 struct fileusage *fup, **fhp; 315 size_t len; 316 317 if ((fup = lookup(id, type))) 318 return (fup); 319 if (name) 320 len = strlen(name); 321 else 322 len = 10; 323 if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) { 324 fprintf(stderr, "out of memory for fileusage structures\n"); 325 exit(1); 326 } 327 fhp = &fuhead[type][id & (FUHASH - 1)]; 328 fup->fu_next = *fhp; 329 *fhp = fup; 330 fup->fu_id = id; 331 if (id > highid[type]) 332 highid[type] = id; 333 if (name) { 334 bcopy(name, fup->fu_name, len + 1); 335 } else { 336 snprintf(fup->fu_name, len, "%u", id); 337 } 338 return (fup); 339 } 340 341 /* 342 * Calculate the grace period and return a printable string for it. 343 */ 344 char * 345 timeprt(time_t seconds) 346 { 347 int hours, minutes; 348 static char buf[20]; 349 static time_t now; 350 351 if (now == 0) 352 time(&now); 353 if (now > seconds) 354 return ("none"); 355 seconds -= now; 356 minutes = (seconds + 30) / 60; 357 hours = (minutes + 30) / 60; 358 if (hours >= 36) { 359 snprintf(buf, sizeof buf, "%ddays", (hours + 12) / 24); 360 return (buf); 361 } 362 if (minutes >= 60) { 363 snprintf(buf, sizeof buf, "%2d:%d", minutes / 60, 364 minutes % 60); 365 return (buf); 366 } 367 snprintf(buf, sizeof buf, "%2d", minutes); 368 return (buf); 369 } 370