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 |
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 |
|
|
extern char *optarg; |
81 |
|
|
extern int optind; |
82 |
|
|
char *qfnp; |
83 |
|
|
int ch; |
84 |
|
|
|
85 |
|
|
while ((ch = getopt(argc, argv, "aguv")) != -1) { |
86 |
|
|
switch(ch) { |
87 |
|
|
case 'a': |
88 |
|
|
aflag++; |
89 |
|
|
break; |
90 |
|
|
case 'g': |
91 |
|
|
gflag++; |
92 |
|
|
break; |
93 |
|
|
case 'u': |
94 |
|
|
uflag++; |
95 |
|
|
break; |
96 |
|
|
case 'v': |
97 |
|
|
vflag++; |
98 |
|
|
break; |
99 |
|
|
default: |
100 |
|
|
usage(); |
101 |
|
|
} |
102 |
|
|
} |
103 |
|
|
argc -= optind; |
104 |
|
|
argv += optind; |
105 |
|
|
if ((argc == 0) == (aflag == 0)) |
106 |
|
|
usage(); |
107 |
|
|
if (!gflag && !uflag) { |
108 |
|
|
if (aflag) |
109 |
|
|
gflag++; |
110 |
|
|
uflag++; |
111 |
|
|
} |
112 |
|
|
if (gflag) { |
113 |
|
|
setgrent(); |
114 |
|
|
while ((gr = getgrent()) != 0) |
115 |
|
|
(void) addid((uid_t)gr->gr_gid, GRPQUOTA, gr->gr_name); |
116 |
|
|
endgrent(); |
117 |
|
|
} |
118 |
|
|
if (uflag) { |
119 |
|
|
setpwent(); |
120 |
|
|
while ((pw = getpwent()) != 0) |
121 |
|
|
(void) addid(pw->pw_uid, USRQUOTA, pw->pw_name); |
122 |
|
|
endpwent(); |
123 |
|
|
} |
124 |
|
|
setfsent(); |
125 |
|
|
while ((fs = getfsent()) != NULL) { |
126 |
|
|
if (strcmp(fs->fs_vfstype, "ffs") && |
127 |
|
|
strcmp(fs->fs_vfstype, "ufs") && |
128 |
|
|
strcmp(fs->fs_vfstype, "mfs")) |
129 |
|
|
continue; |
130 |
|
|
if (aflag) { |
131 |
|
|
if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) |
132 |
|
|
errs += repquota(fs, GRPQUOTA, qfnp); |
133 |
|
|
if (uflag && hasquota(fs, USRQUOTA, &qfnp)) |
134 |
|
|
errs += repquota(fs, USRQUOTA, qfnp); |
135 |
|
|
continue; |
136 |
|
|
} |
137 |
|
|
if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || |
138 |
|
|
(argnum = oneof(fs->fs_spec, argv, argc)) >= 0) { |
139 |
|
|
done |= 1 << argnum; |
140 |
|
|
if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) |
141 |
|
|
errs += repquota(fs, GRPQUOTA, qfnp); |
142 |
|
|
if (uflag && hasquota(fs, USRQUOTA, &qfnp)) |
143 |
|
|
errs += repquota(fs, USRQUOTA, qfnp); |
144 |
|
|
} |
145 |
|
|
} |
146 |
|
|
endfsent(); |
147 |
|
|
for (i = 0; i < argc; i++) |
148 |
|
|
if ((done & (1 << i)) == 0) |
149 |
|
|
fprintf(stderr, "%s not found in fstab\n", argv[i]); |
150 |
|
|
exit(errs); |
151 |
|
|
} |
152 |
|
|
|
153 |
|
|
void |
154 |
|
|
usage(void) |
155 |
|
|
{ |
156 |
|
|
extern char *__progname; |
157 |
|
|
fprintf(stderr, "usage: %s [-aguv] filesystem ...\n", __progname); |
158 |
|
|
exit(1); |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
int |
162 |
|
|
repquota(struct fstab *fs, int type, char *qfpathname) |
163 |
|
|
{ |
164 |
|
|
struct fileusage *fup; |
165 |
|
|
FILE *qf; |
166 |
|
|
uid_t id; |
167 |
|
|
struct dqblk dqbuf; |
168 |
|
|
char *timeprt(time_t); |
169 |
|
|
static struct dqblk zerodqblk; |
170 |
|
|
static int warned = 0; |
171 |
|
|
static int multiple = 0; |
172 |
|
|
|
173 |
|
|
if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 && |
174 |
|
|
errno == EOPNOTSUPP && !warned && vflag) { |
175 |
|
|
warned++; |
176 |
|
|
fprintf(stdout, |
177 |
|
|
"*** Warning: Quotas are not compiled into this kernel\n"); |
178 |
|
|
} |
179 |
|
|
if (multiple++) |
180 |
|
|
printf("\n"); |
181 |
|
|
if (vflag) |
182 |
|
|
fprintf(stdout, "*** Report for %s quotas on %s (%s)\n", |
183 |
|
|
qfextension[type], fs->fs_file, fs->fs_spec); |
184 |
|
|
if ((qf = fopen(qfpathname, "r")) == NULL) { |
185 |
|
|
perror(qfpathname); |
186 |
|
|
return (1); |
187 |
|
|
} |
188 |
|
|
for (id = 0; ; id++) { |
189 |
|
|
fread(&dqbuf, sizeof(struct dqblk), 1, qf); |
190 |
|
|
if (feof(qf)) |
191 |
|
|
break; |
192 |
|
|
if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0) |
193 |
|
|
continue; |
194 |
|
|
if ((fup = lookup(id, type)) == 0) |
195 |
|
|
fup = addid(id, type, NULL); |
196 |
|
|
fup->fu_dqblk = dqbuf; |
197 |
|
|
} |
198 |
|
|
fclose(qf); |
199 |
|
|
printf(" KByte limits File limits\n"); |
200 |
|
|
printf("User used soft hard grace used soft hard grace\n"); |
201 |
|
|
for (id = 0; id <= highid[type]; id++) { |
202 |
|
|
fup = lookup(id, type); |
203 |
|
|
if (fup == 0) |
204 |
|
|
continue; |
205 |
|
|
if (fup->fu_dqblk.dqb_curinodes == 0 && |
206 |
|
|
fup->fu_dqblk.dqb_curblocks == 0) |
207 |
|
|
continue; |
208 |
|
|
printf("%-10s", fup->fu_name); |
209 |
|
|
printf("%c%c %7d %7d %7d %6s", |
210 |
|
|
fup->fu_dqblk.dqb_bsoftlimit && |
211 |
|
|
fup->fu_dqblk.dqb_curblocks >= |
212 |
|
|
fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-', |
213 |
|
|
fup->fu_dqblk.dqb_isoftlimit && |
214 |
|
|
fup->fu_dqblk.dqb_curinodes >= |
215 |
|
|
fup->fu_dqblk.dqb_isoftlimit ? '+' : '-', |
216 |
|
|
(int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_curblocks) |
217 |
|
|
/ 1024), |
218 |
|
|
(int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bsoftlimit) |
219 |
|
|
/ 1024), |
220 |
|
|
(int)(dbtob((u_quad_t)fup->fu_dqblk.dqb_bhardlimit) |
221 |
|
|
/ 1024), |
222 |
|
|
fup->fu_dqblk.dqb_bsoftlimit && |
223 |
|
|
fup->fu_dqblk.dqb_curblocks >= |
224 |
|
|
fup->fu_dqblk.dqb_bsoftlimit ? |
225 |
|
|
timeprt(fup->fu_dqblk.dqb_btime) : ""); |
226 |
|
|
printf(" %6d %5d %5d %6s\n", |
227 |
|
|
fup->fu_dqblk.dqb_curinodes, |
228 |
|
|
fup->fu_dqblk.dqb_isoftlimit, |
229 |
|
|
fup->fu_dqblk.dqb_ihardlimit, |
230 |
|
|
fup->fu_dqblk.dqb_isoftlimit && |
231 |
|
|
fup->fu_dqblk.dqb_curinodes >= |
232 |
|
|
fup->fu_dqblk.dqb_isoftlimit ? |
233 |
|
|
timeprt(fup->fu_dqblk.dqb_itime) : ""); |
234 |
|
|
fup->fu_dqblk = zerodqblk; |
235 |
|
|
} |
236 |
|
|
return (0); |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
/* |
240 |
|
|
* Check to see if target appears in list of size cnt. |
241 |
|
|
*/ |
242 |
|
|
int |
243 |
|
|
oneof(char *target, char *list[], int cnt) |
244 |
|
|
{ |
245 |
|
|
int i; |
246 |
|
|
|
247 |
|
|
for (i = 0; i < cnt; i++) |
248 |
|
|
if (strcmp(target, list[i]) == 0) |
249 |
|
|
return (i); |
250 |
|
|
return (-1); |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
/* |
254 |
|
|
* Check to see if a particular quota is to be enabled. |
255 |
|
|
*/ |
256 |
|
|
int |
257 |
|
|
hasquota(struct fstab *fs, int type, char **qfnamep) |
258 |
|
|
{ |
259 |
|
|
char *opt; |
260 |
|
|
char *cp; |
261 |
|
|
static char initname, usrname[100], grpname[100]; |
262 |
|
|
static char buf[BUFSIZ]; |
263 |
|
|
|
264 |
|
|
if (!initname) { |
265 |
|
|
(void)snprintf(usrname, sizeof usrname, "%s%s", |
266 |
|
|
qfextension[USRQUOTA], qfname); |
267 |
|
|
(void)snprintf(grpname, sizeof grpname, "%s%s", |
268 |
|
|
qfextension[GRPQUOTA], qfname); |
269 |
|
|
initname = 1; |
270 |
|
|
} |
271 |
|
|
strlcpy(buf, fs->fs_mntops, sizeof buf); |
272 |
|
|
for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { |
273 |
|
|
if ((cp = strchr(opt, '='))) |
274 |
|
|
*cp++ = '\0'; |
275 |
|
|
if (type == USRQUOTA && strcmp(opt, usrname) == 0) |
276 |
|
|
break; |
277 |
|
|
if (type == GRPQUOTA && strcmp(opt, grpname) == 0) |
278 |
|
|
break; |
279 |
|
|
} |
280 |
|
|
if (!opt) |
281 |
|
|
return (0); |
282 |
|
|
if (cp) { |
283 |
|
|
*qfnamep = cp; |
284 |
|
|
return (1); |
285 |
|
|
} |
286 |
|
|
(void)snprintf(buf, sizeof buf, "%s/%s.%s", |
287 |
|
|
fs->fs_file, qfname, qfextension[type]); |
288 |
|
|
*qfnamep = buf; |
289 |
|
|
return (1); |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
/* |
293 |
|
|
* Routines to manage the file usage table. |
294 |
|
|
* |
295 |
|
|
* Lookup an id of a specific type. |
296 |
|
|
*/ |
297 |
|
|
struct fileusage * |
298 |
|
|
lookup(uid_t id, int type) |
299 |
|
|
{ |
300 |
|
|
struct fileusage *fup; |
301 |
|
|
|
302 |
|
|
for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) |
303 |
|
|
if (fup->fu_id == id) |
304 |
|
|
return (fup); |
305 |
|
|
return (NULL); |
306 |
|
|
} |
307 |
|
|
|
308 |
|
|
/* |
309 |
|
|
* Add a new file usage id if it does not already exist. |
310 |
|
|
*/ |
311 |
|
|
struct fileusage * |
312 |
|
|
addid(uid_t id, int type, char *name) |
313 |
|
|
{ |
314 |
|
|
struct fileusage *fup, **fhp; |
315 |
|
|
size_t len; |
316 |
|
|
|
317 |
|
|
if ((fup = lookup(id, type))) |
318 |
|
|
return (fup); |
319 |
|
|
if (name) |
320 |
|
|
len = strlen(name); |
321 |
|
|
else |
322 |
|
|
len = 10; |
323 |
|
|
if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) { |
324 |
|
|
fprintf(stderr, "out of memory for fileusage structures\n"); |
325 |
|
|
exit(1); |
326 |
|
|
} |
327 |
|
|
fhp = &fuhead[type][id & (FUHASH - 1)]; |
328 |
|
|
fup->fu_next = *fhp; |
329 |
|
|
*fhp = fup; |
330 |
|
|
fup->fu_id = id; |
331 |
|
|
if (id > highid[type]) |
332 |
|
|
highid[type] = id; |
333 |
|
|
if (name) { |
334 |
|
|
bcopy(name, fup->fu_name, len + 1); |
335 |
|
|
} else { |
336 |
|
|
snprintf(fup->fu_name, len, "%u", id); |
337 |
|
|
} |
338 |
|
|
return (fup); |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
/* |
342 |
|
|
* Calculate the grace period and return a printable string for it. |
343 |
|
|
*/ |
344 |
|
|
char * |
345 |
|
|
timeprt(time_t seconds) |
346 |
|
|
{ |
347 |
|
|
int hours, minutes; |
348 |
|
|
static char buf[20]; |
349 |
|
|
static time_t now; |
350 |
|
|
|
351 |
|
|
if (now == 0) |
352 |
|
|
time(&now); |
353 |
|
|
if (now > seconds) |
354 |
|
|
return ("none"); |
355 |
|
|
seconds -= now; |
356 |
|
|
minutes = (seconds + 30) / 60; |
357 |
|
|
hours = (minutes + 30) / 60; |
358 |
|
|
if (hours >= 36) { |
359 |
|
|
snprintf(buf, sizeof buf, "%ddays", (hours + 12) / 24); |
360 |
|
|
return (buf); |
361 |
|
|
} |
362 |
|
|
if (minutes >= 60) { |
363 |
|
|
snprintf(buf, sizeof buf, "%2d:%d", minutes / 60, |
364 |
|
|
minutes % 60); |
365 |
|
|
return (buf); |
366 |
|
|
} |
367 |
|
|
snprintf(buf, sizeof buf, "%2d", minutes); |
368 |
|
|
return (buf); |
369 |
|
|
} |