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