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 #include <sys/cdefs.h> 34 #ifndef lint 35 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"); 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)repquota.c 8.2 (Berkeley) 11/22/94"; 42 #else 43 __RCSID("$NetBSD: repquota.c,v 1.22 2005/03/05 14:46:29 jdolecek Exp $"); 44 #endif 45 #endif /* not lint */ 46 47 /* 48 * Quota report 49 */ 50 #include <sys/param.h> 51 #include <sys/stat.h> 52 #include <sys/queue.h> 53 #include <ufs/ufs/quota.h> 54 #include <errno.h> 55 #include <fstab.h> 56 #include <grp.h> 57 #include <pwd.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <unistd.h> 62 63 char *qfname = QUOTAFILENAME; 64 char *qfextension[] = INITQFNAMES; 65 66 struct fileusage { 67 struct fileusage *fu_next; 68 struct dqblk fu_dqblk; 69 u_long fu_id; 70 char fu_name[1]; 71 /* actually bigger */ 72 }; 73 #define FUHASH 1024 /* must be power of two */ 74 struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 75 u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 76 77 int vflag; /* verbose */ 78 int aflag; /* all file systems */ 79 80 struct fileusage *addid __P((u_long, int, const char *)); 81 int hasquota __P((struct fstab *, int, char **)); 82 struct fileusage *lookup __P((u_long, int)); 83 int main __P((int, char **)); 84 int oneof __P((const char *, char **, int)); 85 int repquota __P((struct fstab *, int, char *)); 86 char *timeprt __P((time_t)); 87 void usage __P((void)); 88 89 int 90 main(argc, argv) 91 int argc; 92 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 *qfnp; 100 int ch; 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()) != 0) 132 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 133 endgrent(); 134 } 135 if (uflag) { 136 setpwent(); 137 while ((pw = getpwent()) != 0) 138 (void) 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, "ffs")) 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 fprintf(stderr, "%s not found in fstab\n", argv[i]); 165 exit(errs); 166 } 167 168 void 169 usage() 170 { 171 fprintf(stderr, "usage:\n\t%s\n\t%s\n", 172 "repquota [-v] [-g] [-u] -a", 173 "repquota [-v] [-g] [-u] filesys ..."); 174 exit(1); 175 } 176 177 int 178 repquota(fs, type, qfpathname) 179 struct fstab *fs; 180 int type; 181 char *qfpathname; 182 { 183 struct fileusage *fup; 184 FILE *qf; 185 u_long id; 186 struct dqblk dqbuf; 187 static struct dqblk zerodqblk; 188 static int warned = 0; 189 static int multiple = 0; 190 191 if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 && 192 errno == EOPNOTSUPP && !warned && vflag) { 193 warned++; 194 fprintf(stdout, 195 "*** Warning: Quotas are not compiled into this kernel\n"); 196 } 197 if (multiple++) 198 printf("\n"); 199 if (vflag) 200 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n", 201 qfextension[type], fs->fs_file, fs->fs_spec); 202 if ((qf = fopen(qfpathname, "r")) == NULL) { 203 perror(qfpathname); 204 return (1); 205 } 206 for (id = 0; ; id++) { 207 fread(&dqbuf, sizeof(struct dqblk), 1, qf); 208 if (feof(qf)) 209 break; 210 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0) 211 continue; 212 if ((fup = lookup(id, type)) == 0) 213 fup = addid(id, type, (char *)0); 214 fup->fu_dqblk = dqbuf; 215 } 216 fclose(qf); 217 printf(" Block limits File limits\n"); 218 printf(type == USRQUOTA ? "User " : "Group"); 219 printf(" used soft hard grace used soft hard grace\n"); 220 for (id = 0; id <= highid[type]; id++) { 221 fup = lookup(id, type); 222 if (fup == 0) 223 continue; 224 if (fup->fu_dqblk.dqb_curinodes == 0 && 225 fup->fu_dqblk.dqb_curblocks == 0) 226 continue; 227 if (strlen(fup->fu_name) > 9) 228 printf("%s ", fup->fu_name); 229 else 230 printf("%-10s", fup->fu_name); 231 printf("%c%c%8d%8d%8d%7s", 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 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_curblocks) / 1024), 239 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bsoftlimit) / 1024), 240 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bhardlimit) / 1024), 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(" %8d%8d%8d%7s\n", 246 fup->fu_dqblk.dqb_curinodes, 247 fup->fu_dqblk.dqb_isoftlimit, 248 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(target, list, cnt) 263 const char *target; 264 char *list[]; 265 int cnt; 266 { 267 int i; 268 269 for (i = 0; i < cnt; i++) 270 if (strcmp(target, list[i]) == 0) 271 return (i); 272 return (-1); 273 } 274 275 /* 276 * Check to see if a particular quota is to be enabled. 277 */ 278 int 279 hasquota(fs, type, qfnamep) 280 struct fstab *fs; 281 int type; 282 char **qfnamep; 283 { 284 char *opt; 285 char *cp = NULL; 286 static char initname, usrname[100], grpname[100]; 287 static char buf[BUFSIZ]; 288 289 if (!initname) { 290 sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 291 sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 292 initname = 1; 293 } 294 strcpy(buf, fs->fs_mntops); 295 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 296 if ((cp = strchr(opt, '=')) != NULL) 297 *cp++ = '\0'; 298 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 299 break; 300 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 301 break; 302 } 303 if (!opt) 304 return (0); 305 if (cp) { 306 *qfnamep = cp; 307 return (1); 308 } 309 (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 310 *qfnamep = buf; 311 return (1); 312 } 313 314 /* 315 * Routines to manage the file usage table. 316 * 317 * Lookup an id of a specific type. 318 */ 319 struct fileusage * 320 lookup(id, type) 321 u_long id; 322 int type; 323 { 324 struct fileusage *fup; 325 326 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 327 if (fup->fu_id == id) 328 return (fup); 329 return ((struct fileusage *)0); 330 } 331 332 /* 333 * Add a new file usage id if it does not already exist. 334 */ 335 struct fileusage * 336 addid(id, type, name) 337 u_long id; 338 int type; 339 const char *name; 340 { 341 struct fileusage *fup, **fhp; 342 int len; 343 344 if ((fup = lookup(id, type)) != NULL) 345 return (fup); 346 if (name) 347 len = strlen(name); 348 else 349 len = 10; 350 if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) { 351 fprintf(stderr, "out of memory for fileusage structures\n"); 352 exit(1); 353 } 354 fhp = &fuhead[type][id & (FUHASH - 1)]; 355 fup->fu_next = *fhp; 356 *fhp = fup; 357 fup->fu_id = id; 358 if (id > highid[type]) 359 highid[type] = id; 360 if (name) { 361 memmove(fup->fu_name, name, len + 1); 362 } else { 363 sprintf(fup->fu_name, "%lu", (u_long)id); 364 } 365 return (fup); 366 } 367 368 /* 369 * Calculate the grace period and return a printable string for it. 370 */ 371 char * 372 timeprt(seconds) 373 time_t seconds; 374 { 375 time_t hours, minutes; 376 static char buf[20]; 377 static time_t now; 378 379 if (now == 0) 380 time(&now); 381 if (now > seconds) 382 return ("none"); 383 seconds -= now; 384 minutes = (seconds + 30) / 60; 385 hours = (minutes + 30) / 60; 386 if (hours >= 36) { 387 sprintf(buf, "%lddays", (long)((hours + 12) / 24)); 388 return (buf); 389 } 390 if (minutes >= 60) { 391 sprintf(buf, "%2ld:%ld", (long)(minutes / 60), 392 (long)(minutes % 60)); 393 return (buf); 394 } 395 sprintf(buf, "%2ld", (long)minutes); 396 return (buf); 397 } 398