1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 #pragma ident "%Z%%M% %I% %E% SMI" 40 41 /* 42 * Quota report 43 */ 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 #include <string.h> 48 #include <strings.h> 49 #include <errno.h> 50 #include <sys/param.h> 51 #include <sys/types.h> 52 #include <fcntl.h> 53 #include <sys/filio.h> 54 #include <sys/mntent.h> 55 #include <sys/time.h> 56 #include <sys/fs/ufs_quota.h> 57 #include <sys/stat.h> 58 #include <sys/mnttab.h> 59 #include <sys/vfstab.h> 60 #include <pwd.h> 61 62 #define LOGINNAMESIZE 8 63 struct username { 64 struct username *u_next; 65 uid_t u_uid; 66 char u_name[LOGINNAMESIZE + 1]; 67 }; 68 #define UHASH 997 69 static struct username *uhead[UHASH]; 70 71 static struct username *lookup(uid_t); 72 static struct username *adduid(uid_t); 73 static int repquota(char *, char *, char *); 74 static void prquota(uid_t, struct dqblk *); 75 static void header(void); 76 static void usage(void); 77 static void fmttime(char *, long); 78 static char *hasvfsopt(struct vfstab *, char *); 79 static int quotactl(int, char *, uid_t, caddr_t); 80 static int oneof(char *, char **, int); 81 82 extern char *mntopt(); 83 extern char *hasmntopt(); 84 85 static int vflag; /* verbose */ 86 static int aflag; /* all file systems */ 87 static char **listbuf; 88 89 #define QFNAME "quotas" 90 #define CHUNK 50 91 92 #if DEV_BSIZE < 1024 93 #define dbtok(x) ((x) / (1024 / DEV_BSIZE)) 94 #else 95 #define dbtok(x) ((x) * (DEV_BSIZE / 1024)) 96 #endif 97 98 int 99 main(int argc, char **argv) 100 { 101 struct mnttab mntp; 102 struct vfstab vfsbuf; 103 char **listp; 104 int listcnt; 105 int listmax = 0; 106 char quotafile[MAXPATHLEN]; 107 FILE *mtab, *vfstab; 108 int errs = 0; 109 int opt; 110 111 if ((listbuf = malloc(sizeof (char *) * CHUNK)) == NULL) { 112 (void) fprintf(stderr, "Can't alloc lisbuf array."); 113 exit(31+1); 114 } 115 listmax = CHUNK; 116 while ((opt = getopt(argc, argv, "avV")) != EOF) 117 switch (opt) { 118 case 'v': 119 vflag++; 120 break; 121 122 case 'a': 123 aflag++; 124 break; 125 126 case 'V': { 127 /* Print command line */ 128 char *optt; 129 int optc; 130 131 (void) printf("repquota -F ufs "); 132 for (optc = 1; optc < argc; optc++) { 133 optt = argv[optc]; 134 if (optt) 135 (void) printf(" %s ", optt); 136 } 137 (void) putchar('\n'); 138 } 139 break; 140 141 case '?': 142 default: 143 usage(); 144 } 145 146 if (argc <= optind && !aflag) 147 usage(); 148 149 /* 150 * Sync quota information to disk (as userdata). On logging 151 * file systems, this operation does nothing because quota 152 * information is treated as metadata. Logging file systems 153 * are dealt with below in repquota(). 154 */ 155 if (quotactl(Q_ALLSYNC, NULL, 0, NULL) < 0 && errno == EINVAL && vflag) 156 (void) printf("Warning: " 157 "Quotas are not available in this kernel\n"); 158 159 /* 160 * If aflag go through vfstab and make a list of appropriate 161 * filesystems. 162 */ 163 if (aflag) { 164 listp = listbuf; 165 listcnt = 0; 166 if ((vfstab = fopen(VFSTAB, "r")) == NULL) { 167 (void) fprintf(stderr, "Can't open "); 168 perror(VFSTAB); 169 exit(31+8); 170 } 171 while (getvfsent(vfstab, &vfsbuf) == 0) { 172 173 if (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UFS) != 0 || 174 (vfsbuf.vfs_mntopts == 0) || 175 hasvfsopt(&vfsbuf, MNTOPT_RO) || 176 (!hasvfsopt(&vfsbuf, MNTOPT_RQ) && 177 !hasvfsopt(&vfsbuf, MNTOPT_QUOTA))) 178 continue; 179 180 *listp = malloc(strlen(vfsbuf.vfs_special) + 1); 181 (void) strcpy(*listp, vfsbuf.vfs_special); 182 listp++; 183 listcnt++; 184 /* grow listbuf if needed */ 185 if (listcnt >= listmax) { 186 listmax += CHUNK; 187 listbuf = realloc(listbuf, 188 sizeof (char *) * listmax); 189 if (listbuf == NULL) { 190 (void) fprintf(stderr, 191 "Can't grow listbuf.\n"); 192 exit(31+1); 193 } 194 listp = &listbuf[listcnt]; 195 } 196 } 197 (void) fclose(vfstab); 198 *listp = (char *)0; 199 listp = listbuf; 200 } else { 201 listp = &argv[optind]; 202 listcnt = argc - optind; 203 } 204 if ((mtab = fopen(MNTTAB, "r")) == NULL) { 205 (void) fprintf(stderr, "Can't open "); 206 perror(MNTTAB); 207 exit(31+8); 208 } 209 while (getmntent(mtab, &mntp) == 0) { 210 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) == 0 && 211 !hasmntopt(&mntp, MNTOPT_RO) && 212 (oneof(mntp.mnt_special, listp, listcnt) || 213 oneof(mntp.mnt_mountp, listp, listcnt))) { 214 (void) snprintf(quotafile, sizeof (quotafile), "%s/%s", 215 mntp.mnt_mountp, QFNAME); 216 errs += repquota(mntp.mnt_special, 217 mntp.mnt_mountp, quotafile); 218 } 219 } 220 (void) fclose(mtab); 221 while (listcnt--) { 222 if (*listp) 223 (void) fprintf(stderr, "Cannot report on %s\n", *listp); 224 listp++; 225 } 226 if (errs > 0) 227 exit(31+1); 228 return (0); 229 } 230 231 static int 232 repquota(char *fsdev, char *fsfile, char *qffile) 233 { 234 FILE *qf; 235 uid_t uid; 236 struct dqblk dqbuf; 237 struct stat64 statb; 238 239 if (vflag || aflag) 240 (void) printf("%s (%s):\n", fsdev, fsfile); 241 qf = fopen64(qffile, "r"); 242 if (qf == NULL) { 243 perror(qffile); 244 return (1); 245 } 246 if (fstat64(fileno(qf), &statb) < 0) { 247 perror(qffile); 248 (void) fclose(qf); 249 return (1); 250 } 251 /* 252 * Flush the file system. On logging file systems, this makes 253 * sure that the quota information (as metadata) gets rolled 254 * forward. 255 */ 256 if (ioctl(fileno(qf), _FIOFFS, NULL) == -1) { 257 perror(qffile); 258 (void) fprintf(stderr, "%s: cannot flush file system.\n", 259 qffile); 260 (void) fclose(qf); 261 return (1); 262 } 263 header(); 264 for (uid = 0; uid <= MAXUID && uid >= 0; uid++) { 265 (void) fread(&dqbuf, sizeof (struct dqblk), 1, qf); 266 if (feof(qf)) 267 break; 268 if (!vflag && 269 dqbuf.dqb_curfiles == 0 && dqbuf.dqb_curblocks == 0) 270 continue; 271 prquota(uid, &dqbuf); 272 } 273 (void) fclose(qf); 274 return (0); 275 } 276 277 static void 278 header(void) 279 { 280 (void) printf(" Block limits" 281 " File limits\n"); 282 (void) printf("User used soft hard timeleft" 283 " used soft hard timeleft\n"); 284 } 285 286 static void 287 prquota(uid_t uid, struct dqblk *dqp) 288 { 289 struct timeval tv; 290 struct username *up; 291 char ftimeleft[80], btimeleft[80]; 292 293 if (dqp->dqb_bsoftlimit == 0 && dqp->dqb_bhardlimit == 0 && 294 dqp->dqb_fsoftlimit == 0 && dqp->dqb_fhardlimit == 0) 295 return; 296 (void) time(&(tv.tv_sec)); 297 tv.tv_usec = 0; 298 up = lookup(uid); 299 if (up) 300 (void) printf("%-10s", up->u_name); 301 else 302 (void) printf("#%-9ld", uid); 303 if (dqp->dqb_bsoftlimit && 304 dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) { 305 if (dqp->dqb_btimelimit == 0) 306 (void) strcpy(btimeleft, "NOT STARTED"); 307 else if (dqp->dqb_btimelimit > tv.tv_sec) 308 fmttime(btimeleft, 309 (long)(dqp->dqb_btimelimit - tv.tv_sec)); 310 else 311 (void) strcpy(btimeleft, "EXPIRED"); 312 } else 313 btimeleft[0] = '\0'; 314 315 if (dqp->dqb_fsoftlimit && dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) { 316 if (dqp->dqb_ftimelimit == 0) 317 (void) strcpy(ftimeleft, "NOT STARTED"); 318 else if (dqp->dqb_ftimelimit > tv.tv_sec) 319 fmttime(ftimeleft, 320 (long)(dqp->dqb_ftimelimit - tv.tv_sec)); 321 else 322 (void) strcpy(ftimeleft, "EXPIRED"); 323 } else 324 ftimeleft[0] = '\0'; 325 326 (void) printf("%c%c %6lu %6lu %6lu %11s %7lu %6lu %6lu %11s\n", 327 (dqp->dqb_bsoftlimit && 328 dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) ? '+' : '-', 329 (dqp->dqb_fsoftlimit && 330 dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) ? '+' : '-', 331 dbtok(dqp->dqb_curblocks), 332 dbtok(dqp->dqb_bsoftlimit), 333 dbtok(dqp->dqb_bhardlimit), 334 btimeleft, 335 dqp->dqb_curfiles, 336 dqp->dqb_fsoftlimit, 337 dqp->dqb_fhardlimit, 338 ftimeleft); 339 } 340 341 static void 342 fmttime(char *buf, long time) 343 { 344 int i; 345 static struct { 346 int c_secs; /* conversion units in secs */ 347 char *c_str; /* unit string */ 348 } cunits [] = { 349 {60*60*24*28, "months"}, 350 {60*60*24*7, "weeks"}, 351 {60*60*24, "days"}, 352 {60*60, "hours"}, 353 {60, "mins"}, 354 {1, "secs"} 355 }; 356 357 if (time <= 0) { 358 (void) strcpy(buf, "EXPIRED"); 359 return; 360 } 361 for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++) { 362 if (time >= cunits[i].c_secs) 363 break; 364 } 365 (void) sprintf(buf, "%.1f %s", 366 (double)time / cunits[i].c_secs, cunits[i].c_str); 367 } 368 369 static int 370 oneof(char *target, char **olistp, int on) 371 { 372 char **listp = olistp; 373 int n = on; 374 375 while (n--) { 376 if (*listp && strcmp(target, *listp) == 0) { 377 *listp = (char *)0; 378 return (1); 379 } 380 listp++; 381 } 382 return (0); 383 } 384 385 static struct username * 386 lookup(uid_t uid) 387 { 388 struct passwd *pwp; 389 struct username *up; 390 391 for (up = uhead[uid % UHASH]; up != 0; up = up->u_next) 392 if (up->u_uid == uid) 393 return (up); 394 if ((pwp = getpwuid((uid_t)uid)) == NULL) 395 return ((struct username *)0); 396 up = adduid(pwp->pw_uid); 397 (void) strncpy(up->u_name, pwp->pw_name, sizeof (up->u_name)); 398 return (up); 399 } 400 401 /* 402 * adduid() should *ONLY* be called from lookup in order 403 * to avoid duplicate entries. 404 */ 405 static struct username * 406 adduid(uid_t uid) 407 { 408 struct username *up, **uhp; 409 410 up = calloc(1, sizeof (struct username)); 411 if (up == 0) { 412 (void) fprintf(stderr, 413 "out of memory for username structures\n"); 414 exit(31+1); 415 } 416 uhp = &uhead[uid % UHASH]; 417 up->u_next = *uhp; 418 *uhp = up; 419 up->u_uid = uid; 420 return (up); 421 } 422 423 static void 424 usage(void) 425 { 426 (void) fprintf(stderr, "ufs usage:\n"); 427 (void) fprintf(stderr, "\trepquota [-v] -a \n"); 428 (void) fprintf(stderr, "\trepquota [-v] filesys ...\n"); 429 exit(31+1); 430 } 431 432 static int 433 quotactl(int cmd, char *special, uid_t uid, caddr_t addr) 434 { 435 int fd; 436 int status; 437 struct quotctl quota; 438 char qfile[MAXPATHLEN]; 439 FILE *fstab; 440 struct mnttab mntp; 441 442 443 if ((special == NULL) && (cmd == Q_ALLSYNC)) { 444 /* 445 * Find the mount point of the special device. This is 446 * because the ioctl that implements the quotactl call has 447 * to go to a real file, and not to the block device. 448 */ 449 if ((fstab = fopen(MNTTAB, "r")) == NULL) { 450 (void) fprintf(stderr, "%s: ", MNTTAB); 451 perror("open"); 452 exit(31+1); 453 } 454 fd = -1; 455 while ((status = getmntent(fstab, &mntp)) == NULL) { 456 457 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 || 458 hasmntopt(&mntp, MNTOPT_RO)) 459 continue; 460 461 if ((strlcpy(qfile, mntp.mnt_mountp, 462 sizeof (qfile)) >= sizeof (qfile)) || 463 (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= 464 sizeof (qfile))) { 465 continue; 466 } 467 468 /* If we find *ANY* valid "quotas" file, use it */ 469 if ((fd = open64(qfile, O_RDONLY)) >= 0) 470 break; 471 } 472 (void) fclose(fstab); 473 if (fd == -1) { 474 errno = ENOENT; 475 (void) printf("quotactl: no quotas file " 476 "on any mounted file system\n"); 477 return (-1); 478 } 479 } 480 quota.op = cmd; 481 quota.uid = uid; 482 quota.addr = addr; 483 status = ioctl(fd, Q_QUOTACTL, "a); 484 (void) close(fd); 485 return (status); 486 } 487 488 static char * 489 hasvfsopt(struct vfstab *vfs, char *opt) 490 { 491 char *f, *opts; 492 static char *tmpopts; 493 494 if (tmpopts == 0) { 495 tmpopts = calloc(256, sizeof (char)); 496 if (tmpopts == 0) 497 return (0); 498 } 499 (void) strcpy(tmpopts, vfs->vfs_mntopts); 500 opts = tmpopts; 501 f = mntopt(&opts); 502 for (; *f; f = mntopt(&opts)) { 503 if (strncmp(opt, f, strlen(opt)) == 0) 504 return (f - tmpopts + vfs->vfs_mntopts); 505 } 506 return (NULL); 507 } 508