0
|
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 "@(#)repquota.c 1.29 05/12/05 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 }
|