xref: /openbsd/usr.sbin/repquota/repquota.c (revision 91f110e0)
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>
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)
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, (char *)0);
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 ((struct fileusage *)0);
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 = (struct fileusage *)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 }
370