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