1 /*
2 * Copyright (c) 1980, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Robert Elz at The University of Melbourne.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * Quota report
35 */
36 #include <sys/param.h> /* dbtob */
37 #include <sys/stat.h>
38 #include <ufs/ufs/quota.h>
39 #include <fstab.h>
40 #include <pwd.h>
41 #include <grp.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <stdlib.h>
47
48 char *qfname = QUOTAFILENAME;
49 char *qfextension[] = INITQFNAMES;
50
51 struct fileusage {
52 struct fileusage *fu_next;
53 struct dqblk fu_dqblk;
54 uid_t fu_id;
55 char fu_name[1];
56 /* actually bigger */
57 };
58 #define FUHASH 1024 /* must be power of two */
59 struct fileusage *fuhead[MAXQUOTAS][FUHASH];
60 struct fileusage *lookup(uid_t, int);
61 struct fileusage *addid(uid_t id, int type, char *name);
62 uid_t highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
63
64 int vflag; /* verbose */
65 int aflag; /* all file systems */
66
67 void usage(void);
68 int repquota(struct fstab *, int, char *);
69 int hasquota(struct fstab *, int, char **);
70 int oneof(char *, char *[], int);
71 char *timeprt(time_t);
72 int
main(int argc,char * argv[])73 main(int argc, char *argv[])
74 {
75 struct fstab *fs;
76 struct passwd *pw;
77 struct group *gr;
78 int gflag = 0, uflag = 0, errs = 0;
79 long i, argnum, done = 0;
80 char *qfnp;
81 int ch;
82
83 while ((ch = getopt(argc, argv, "aguv")) != -1) {
84 switch(ch) {
85 case 'a':
86 aflag++;
87 break;
88 case 'g':
89 gflag++;
90 break;
91 case 'u':
92 uflag++;
93 break;
94 case 'v':
95 vflag++;
96 break;
97 default:
98 usage();
99 }
100 }
101 argc -= optind;
102 argv += optind;
103 if ((argc == 0) == (aflag == 0))
104 usage();
105 if (!gflag && !uflag) {
106 if (aflag)
107 gflag++;
108 uflag++;
109 }
110 if (gflag) {
111 setgrent();
112 while ((gr = getgrent()) != 0)
113 (void) addid((uid_t)gr->gr_gid, GRPQUOTA, gr->gr_name);
114 endgrent();
115 }
116 if (uflag) {
117 setpwent();
118 while ((pw = getpwent()) != 0)
119 (void) addid(pw->pw_uid, USRQUOTA, pw->pw_name);
120 endpwent();
121 }
122 setfsent();
123 while ((fs = getfsent()) != NULL) {
124 if (strcmp(fs->fs_vfstype, "ffs") &&
125 strcmp(fs->fs_vfstype, "ufs") &&
126 strcmp(fs->fs_vfstype, "mfs"))
127 continue;
128 if (aflag) {
129 if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
130 errs += repquota(fs, GRPQUOTA, qfnp);
131 if (uflag && hasquota(fs, USRQUOTA, &qfnp))
132 errs += repquota(fs, USRQUOTA, qfnp);
133 continue;
134 }
135 if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
136 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) {
137 done |= 1 << argnum;
138 if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
139 errs += repquota(fs, GRPQUOTA, qfnp);
140 if (uflag && hasquota(fs, USRQUOTA, &qfnp))
141 errs += repquota(fs, USRQUOTA, qfnp);
142 }
143 }
144 endfsent();
145 for (i = 0; i < argc; i++)
146 if ((done & (1 << i)) == 0)
147 fprintf(stderr, "%s not found in fstab\n", argv[i]);
148 exit(errs);
149 }
150
151 void
usage(void)152 usage(void)
153 {
154 extern char *__progname;
155 fprintf(stderr, "usage: %s [-aguv] filesystem ...\n", __progname);
156 exit(1);
157 }
158
159 int
repquota(struct fstab * fs,int type,char * qfpathname)160 repquota(struct fstab *fs, int type, char *qfpathname)
161 {
162 struct fileusage *fup;
163 FILE *qf;
164 uid_t id;
165 struct dqblk dqbuf;
166 char *timeprt(time_t);
167 static struct dqblk zerodqblk;
168 static int warned = 0;
169 static int multiple = 0;
170
171 if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 &&
172 errno == EOPNOTSUPP && !warned && vflag) {
173 warned++;
174 fprintf(stdout,
175 "*** Warning: Quotas are not compiled into this kernel\n");
176 }
177 if (multiple++)
178 printf("\n");
179 if (vflag)
180 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n",
181 qfextension[type], fs->fs_file, fs->fs_spec);
182 if ((qf = fopen(qfpathname, "r")) == NULL) {
183 perror(qfpathname);
184 return (1);
185 }
186 for (id = 0; ; id++) {
187 fread(&dqbuf, sizeof(struct dqblk), 1, qf);
188 if (feof(qf))
189 break;
190 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0)
191 continue;
192 if ((fup = lookup(id, type)) == 0)
193 fup = addid(id, type, NULL);
194 fup->fu_dqblk = dqbuf;
195 }
196 fclose(qf);
197 printf(" KByte limits File limits\n");
198 printf("User used soft hard grace used soft hard grace\n");
199 for (id = 0; id <= highid[type]; id++) {
200 fup = lookup(id, type);
201 if (fup == 0)
202 continue;
203 if (fup->fu_dqblk.dqb_curinodes == 0 &&
204 fup->fu_dqblk.dqb_curblocks == 0)
205 continue;
206 printf("%-10s", fup->fu_name);
207 printf("%c%c %7d %7d %7d %6s",
208 fup->fu_dqblk.dqb_bsoftlimit &&
209 fup->fu_dqblk.dqb_curblocks >=
210 fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-',
211 fup->fu_dqblk.dqb_isoftlimit &&
212 fup->fu_dqblk.dqb_curinodes >=
213 fup->fu_dqblk.dqb_isoftlimit ? '+' : '-',
214 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_curblocks)
215 / 1024),
216 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bsoftlimit)
217 / 1024),
218 (int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bhardlimit)
219 / 1024),
220 fup->fu_dqblk.dqb_bsoftlimit &&
221 fup->fu_dqblk.dqb_curblocks >=
222 fup->fu_dqblk.dqb_bsoftlimit ?
223 timeprt(fup->fu_dqblk.dqb_btime) : "");
224 printf(" %6d %5d %5d %6s\n",
225 fup->fu_dqblk.dqb_curinodes,
226 fup->fu_dqblk.dqb_isoftlimit,
227 fup->fu_dqblk.dqb_ihardlimit,
228 fup->fu_dqblk.dqb_isoftlimit &&
229 fup->fu_dqblk.dqb_curinodes >=
230 fup->fu_dqblk.dqb_isoftlimit ?
231 timeprt(fup->fu_dqblk.dqb_itime) : "");
232 fup->fu_dqblk = zerodqblk;
233 }
234 return (0);
235 }
236
237 /*
238 * Check to see if target appears in list of size cnt.
239 */
240 int
oneof(char * target,char * list[],int cnt)241 oneof(char *target, char *list[], int cnt)
242 {
243 int i;
244
245 for (i = 0; i < cnt; i++)
246 if (strcmp(target, list[i]) == 0)
247 return (i);
248 return (-1);
249 }
250
251 /*
252 * Check to see if a particular quota is to be enabled.
253 */
254 int
hasquota(struct fstab * fs,int type,char ** qfnamep)255 hasquota(struct fstab *fs, int type, char **qfnamep)
256 {
257 char *opt;
258 char *cp;
259 static char initname, usrname[100], grpname[100];
260 static char buf[BUFSIZ];
261
262 if (!initname) {
263 (void)snprintf(usrname, sizeof usrname, "%s%s",
264 qfextension[USRQUOTA], qfname);
265 (void)snprintf(grpname, sizeof grpname, "%s%s",
266 qfextension[GRPQUOTA], qfname);
267 initname = 1;
268 }
269 strlcpy(buf, fs->fs_mntops, sizeof buf);
270 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
271 if ((cp = strchr(opt, '=')))
272 *cp++ = '\0';
273 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
274 break;
275 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
276 break;
277 }
278 if (!opt)
279 return (0);
280 if (cp) {
281 *qfnamep = cp;
282 return (1);
283 }
284 (void)snprintf(buf, sizeof buf, "%s/%s.%s",
285 fs->fs_file, qfname, qfextension[type]);
286 *qfnamep = buf;
287 return (1);
288 }
289
290 /*
291 * Routines to manage the file usage table.
292 *
293 * Lookup an id of a specific type.
294 */
295 struct fileusage *
lookup(uid_t id,int type)296 lookup(uid_t id, int type)
297 {
298 struct fileusage *fup;
299
300 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
301 if (fup->fu_id == id)
302 return (fup);
303 return (NULL);
304 }
305
306 /*
307 * Add a new file usage id if it does not already exist.
308 */
309 struct fileusage *
addid(uid_t id,int type,char * name)310 addid(uid_t id, int type, char *name)
311 {
312 struct fileusage *fup, **fhp;
313 size_t len;
314
315 if ((fup = lookup(id, type)))
316 return (fup);
317 if (name)
318 len = strlen(name);
319 else
320 len = 10;
321 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) {
322 fprintf(stderr, "out of memory for fileusage structures\n");
323 exit(1);
324 }
325 fhp = &fuhead[type][id & (FUHASH - 1)];
326 fup->fu_next = *fhp;
327 *fhp = fup;
328 fup->fu_id = id;
329 if (id > highid[type])
330 highid[type] = id;
331 if (name) {
332 bcopy(name, fup->fu_name, len + 1);
333 } else {
334 snprintf(fup->fu_name, len, "%u", id);
335 }
336 return (fup);
337 }
338
339 /*
340 * Calculate the grace period and return a printable string for it.
341 */
342 char *
timeprt(time_t seconds)343 timeprt(time_t seconds)
344 {
345 int hours, minutes;
346 static char buf[20];
347 static time_t now;
348
349 if (now == 0)
350 time(&now);
351 if (now > seconds)
352 return ("none");
353 seconds -= now;
354 minutes = (seconds + 30) / 60;
355 hours = (minutes + 30) / 60;
356 if (hours >= 36) {
357 snprintf(buf, sizeof buf, "%ddays", (hours + 12) / 24);
358 return (buf);
359 }
360 if (minutes >= 60) {
361 snprintf(buf, sizeof buf, "%2d:%d", minutes / 60,
362 minutes % 60);
363 return (buf);
364 }
365 snprintf(buf, sizeof buf, "%2d", minutes);
366 return (buf);
367 }
368