comparison usr/src/cmd/fs.d/ufs/repquota/repquota.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
comparison
equal deleted inserted replaced
-1:000000000000 0:c9caec207d52
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, &quota);
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 }