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