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