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