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