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