xref: /openbsd-src/usr.sbin/repquota/repquota.c (revision 10a7b75896683c64bfb039e81ab2b44ce1670578)
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