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
main(int argc,char ** argv)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
repquota(char * fsdev,char * fsfile,char * qffile)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
header(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
prquota(uid_t uid,struct dqblk * dqp)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
fmttime(char * buf,long time)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
oneof(char * target,char ** olistp,int on)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 *
lookup(uid_t uid)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 *
adduid(uid_t uid)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
usage(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
quotactl(int cmd,char * special,uid_t uid,caddr_t addr)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 *
hasvfsopt(struct vfstab * vfs,char * opt)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