xref: /original-bsd/usr.sbin/edquota/edquota.c (revision 65ba69af)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)edquota.c	5.6 (Berkeley) 06/18/88";
26 #endif /* not lint */
27 
28 /*
29  * Disk quota editor.
30  */
31 #include <stdio.h>
32 #include <signal.h>
33 #include <errno.h>
34 #include <pwd.h>
35 #include <ctype.h>
36 #include <fstab.h>
37 
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <sys/file.h>
41 #include <sys/quota.h>
42 
43 #define	DEFEDITOR	"/usr/ucb/vi"
44 
45 struct	dquot dq[NMOUNT];
46 struct	dquot odq[NMOUNT];
47 char	dqf[NMOUNT][MAXPATHLEN + 1];
48 char	odqf[NMOUNT][MAXPATHLEN + 1];
49 
50 char	tmpfil[] = "/tmp/EdP.aXXXXX";
51 char	*qfname = "quotas";
52 char	*getenv();
53 
54 main(argc, argv)
55 	char **argv;
56 {
57 	int uid;
58 	char *arg0;
59 
60 	mktemp(tmpfil);
61 	close(creat(tmpfil, 0600));
62 	chown(tmpfil, getuid(), getgid());
63 	arg0 = *argv++;
64 	if (argc < 2) {
65 		fprintf(stderr, "Usage: %s [-p username] username ...\n", arg0);
66 		unlink(tmpfil);
67 		exit(1);
68 	}
69 	--argc;
70 	if (getuid()) {
71 		fprintf(stderr, "%s: permission denied\n", arg0);
72 		unlink(tmpfil);
73 		exit(1);
74 	}
75 	if (argc > 2 && strcmp(*argv, "-p") == 0) {
76 		argc--, argv++;
77 		uid = getentry(*argv++);
78 		if (uid < 0) {
79 			unlink(tmpfil);
80 			exit(1);
81 		}
82 		getprivs(uid);
83 		argc--;
84 		while (argc-- > 0) {
85 			uid = getentry(*argv++);
86 			if (uid < 0)
87 				continue;
88 			getdiscq(uid, odq, odqf);
89 			putprivs(uid);
90 		}
91 		unlink(tmpfil);
92 		exit(0);
93 	}
94 	while (--argc >= 0) {
95 		uid = getentry(*argv++);
96 		if (uid < 0)
97 			continue;
98 		getprivs(uid);
99 		if (editit())
100 			putprivs(uid);
101 	}
102 	unlink(tmpfil);
103 	exit(0);
104 }
105 
106 getentry(name)
107 	char *name;
108 {
109 	struct passwd *pw;
110 	int uid;
111 
112 	if (alldigits(name))
113 		uid = atoi(name);
114 	else if (pw = getpwnam(name))
115 		uid = pw->pw_uid;
116 	else {
117 		fprintf(stderr, "%s: no such user\n", name);
118 		sleep(1);
119 		return (-1);
120 	}
121 	return (uid);
122 }
123 
124 editit()
125 {
126 	register int pid, xpid;
127 	long omask;
128 	int stat;
129 
130 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
131  top:
132 	if ((pid = fork()) < 0) {
133 		extern errno;
134 
135 		if (errno == EPROCLIM) {
136 			fprintf(stderr, "You have too many processes\n");
137 			return(0);
138 		}
139 		if (errno == EAGAIN) {
140 			sleep(1);
141 			goto top;
142 		}
143 		perror("fork");
144 		return (0);
145 	}
146 	if (pid == 0) {
147 		register char *ed;
148 
149 		sigsetmask(omask);
150 		setgid(getgid());
151 		setuid(getuid());
152 		if ((ed = getenv("EDITOR")) == (char *)0)
153 			ed = DEFEDITOR;
154 		execlp(ed, ed, tmpfil, 0);
155 		perror(ed);
156 		exit(1);
157 	}
158 	while ((xpid = wait(&stat)) >= 0)
159 		if (xpid == pid)
160 			break;
161 	sigsetmask(omask);
162 	return (!stat);
163 }
164 
165 getprivs(uid)
166 	register uid;
167 {
168 	register i;
169 	FILE *fd;
170 
171 	getdiscq(uid, dq, dqf);
172 	for (i = 0; i < NMOUNT; i++) {
173 		odq[i] = dq[i];
174 		strcpy(odqf[i], dqf[i]);
175 	}
176 	if ((fd = fopen(tmpfil, "w")) == NULL) {
177 		fprintf(stderr, "edquota: ");
178 		perror(tmpfil);
179 		exit(1);
180 	}
181 	for (i = 0; i < NMOUNT; i++) {
182 		if (*dqf[i] == '\0')
183 			continue;
184 		fprintf(fd,
185 "fs %s blocks (soft = %d, hard = %d) inodes (soft = %d, hard = %d)\n"
186 			, dqf[i]
187 			, dbtob(dq[i].dq_bsoftlimit) / 1024
188 			, dbtob(dq[i].dq_bhardlimit) / 1024
189 			, dq[i].dq_isoftlimit
190 			, dq[i].dq_ihardlimit
191 		);
192 	}
193 	fclose(fd);
194 }
195 
196 putprivs(uid)
197 	register uid;
198 {
199 	register i, j;
200 	int n;
201 	FILE *fd;
202 	char line[BUFSIZ];
203 
204 	fd = fopen(tmpfil, "r");
205 	if (fd == NULL) {
206 		fprintf(stderr, "Can't re-read temp file!!\n");
207 		return;
208 	}
209 	for (i = 0; i < NMOUNT; i++) {
210 		char *cp, *dp, *next();
211 
212 		if (fgets(line, sizeof (line), fd) == NULL)
213 			break;
214 		cp = next(line, " \t");
215 		if (cp == NULL)
216 			break;
217 		*cp++ = '\0';
218 		while (*cp && *cp == '\t' && *cp == ' ')
219 			cp++;
220 		dp = cp, cp = next(cp, " \t");
221 		if (cp == NULL)
222 			break;
223 		*cp++ = '\0';
224 		while (*cp && *cp == '\t' && *cp == ' ')
225 			cp++;
226 		strcpy(dqf[i], dp);
227 		n = sscanf(cp,
228 "blocks (soft = %d, hard = %d) inodes (soft = %hd, hard = %hd)\n"
229 			, &dq[i].dq_bsoftlimit
230 			, &dq[i].dq_bhardlimit
231 			, &dq[i].dq_isoftlimit
232 			, &dq[i].dq_ihardlimit
233 		);
234 		if (n != 4) {
235 			fprintf(stderr, "%s: bad format\n", cp);
236 			continue;
237 		}
238 		dq[i].dq_bsoftlimit = btodb(dq[i].dq_bsoftlimit * 1024);
239 		dq[i].dq_bhardlimit = btodb(dq[i].dq_bhardlimit * 1024);
240 	}
241 	fclose(fd);
242 	n = i;
243 	for (i = 0; i < n; i++) {
244 		if (*dqf[i] == '\0')
245 			break;
246 		for (j = 0; j < NMOUNT; j++) {
247 			if (strcmp(dqf[i], odqf[j]) == 0)
248 				break;
249 		}
250 		if (j >= NMOUNT)
251 			continue;
252 		*odqf[j] = '\0';
253 		/*
254 		 * This isn't really good enough, it is quite likely
255 		 * to have changed while we have been away editing,
256 		 * but it's not important enough to worry about at
257 		 * the minute.
258 		 */
259 		dq[i].dq_curblocks = odq[j].dq_curblocks;
260 		dq[i].dq_curinodes = odq[j].dq_curinodes;
261 		/*
262 		 * If we've upped the inode or disk block limits
263 		 * and the guy is out of warnings, reinitialize.
264 		 */
265 		if (dq[i].dq_bsoftlimit > odq[j].dq_bsoftlimit &&
266 		    dq[i].dq_bwarn == 0)
267 			dq[i].dq_bwarn = MAX_DQ_WARN;
268 		if (dq[i].dq_isoftlimit > odq[j].dq_isoftlimit &&
269 		    dq[i].dq_iwarn == 0)
270 			dq[i].dq_iwarn = MAX_IQ_WARN;
271 	}
272 	if (i < NMOUNT) {
273 		for (j = 0; j < NMOUNT; j++) {
274 			if (*odqf[j] == '\0')
275 				continue;
276 			strcpy(dqf[i], odqf[j]);
277 			dq[i].dq_isoftlimit = 0;
278 			dq[i].dq_ihardlimit = 0;
279 			dq[i].dq_bsoftlimit = 0;
280 			dq[i].dq_bhardlimit = 0;
281 			/*
282 			 * Same applies as just above
283 			 * but matters not at all, as we are just
284 			 * turning quota'ing off for this filesys.
285 			 */
286 			dq[i].dq_curblocks = odq[j].dq_curblocks;
287 			dq[i].dq_curinodes = odq[j].dq_curinodes;
288 			if (++i >= NMOUNT)
289 				break;
290 		}
291 	}
292 	if (*dqf[0])
293 		putdiscq(uid, dq, dqf);
294 }
295 
296 char *
297 next(cp, match)
298 	register char *cp;
299 	char *match;
300 {
301 	register char *dp;
302 
303 	while (cp && *cp) {
304 		for (dp = match; dp && *dp; dp++)
305 			if (*dp == *cp)
306 				return (cp);
307 		cp++;
308 	}
309 	return ((char *)0);
310 }
311 
312 alldigits(s)
313 	register char *s;
314 {
315 	register c;
316 
317 	c = *s++;
318 	do {
319 		if (!isdigit(c))
320 			return (0);
321 	} while (c = *s++);
322 	return (1);
323 }
324 
325 getdiscq(uid, dq, dqf)
326 	register uid;
327 	register struct dquot *dq;
328 	register char (*dqf)[MAXPATHLEN + 1];
329 {
330 	register struct fstab *fs;
331 	char qfilename[MAXPATHLEN + 1];
332 	struct stat statb;
333 	struct dqblk dqblk;
334 	dev_t fsdev;
335 	int fd;
336 	static int warned = 0;
337 	extern int errno;
338 
339 	setfsent();
340 	while (fs = getfsent()) {
341 		if (stat(fs->fs_spec, &statb) < 0)
342 			continue;
343 		fsdev = statb.st_rdev;
344 		sprintf(qfilename, "%s/%s", fs->fs_file, qfname);
345 		if (stat(qfilename, &statb) < 0 || statb.st_dev != fsdev)
346 			continue;
347 		if (quota(Q_GETDLIM, uid, fsdev, &dqblk) != 0) {
348 	    		if (errno == EINVAL && !warned) {
349 				warned++;
350 				fprintf(stderr, "Warning: %s\n",
351 				    "Quotas are not compiled into this kernel");
352 				sleep(3);
353 			}
354 			fd = open(qfilename, O_RDONLY);
355 			if (fd < 0)
356 				continue;
357 			lseek(fd, (long)(uid * sizeof dqblk), L_SET);
358 			switch (read(fd, &dqblk, sizeof dqblk)) {
359 			case 0:			/* EOF */
360 				/*
361 				 * Convert implicit 0 quota (EOF)
362 				 * into an explicit one (zero'ed dqblk)
363 				 */
364 				bzero((caddr_t)&dqblk, sizeof dqblk);
365 				break;
366 
367 			case sizeof dqblk:	/* OK */
368 				break;
369 
370 			default:		/* ERROR */
371 				fprintf(stderr, "edquota: read error in ");
372 				perror(qfilename);
373 				close(fd);
374 				continue;
375 			}
376 			close(fd);
377 		}
378 		dq->dq_dqb = dqblk;
379 		dq->dq_dev = fsdev;
380 		strcpy(*dqf, fs->fs_file);
381 		dq++, dqf++;
382 	}
383 	endfsent();
384 	**dqf = '\0';
385 }
386 
387 putdiscq(uid, dq, dqf)
388 	register uid;
389 	register struct dquot *dq;
390 	register char (*dqf)[MAXPATHLEN + 1];
391 {
392 	register fd, cnt;
393 	struct stat sb;
394 	struct fstab *fs;
395 
396 	cnt = 0;
397 	for (cnt = 0; ++cnt <= NMOUNT && **dqf; dq++, dqf++) {
398 		fs = getfsfile(*dqf);
399 		if (fs == NULL) {
400 			fprintf(stderr, "%s: not in /etc/fstab\n", *dqf);
401 			continue;
402 		}
403 		strcat(*dqf, "/");
404 		strcat(*dqf, qfname);
405 		if (stat(*dqf, &sb) >= 0)
406 			quota(Q_SETDLIM, uid, sb.st_dev, &dq->dq_dqb);
407 		if ((fd = open(*dqf, 1)) < 0) {
408 			perror(*dqf);
409 		} else {
410 			lseek(fd, (long)uid * (long)sizeof (struct dqblk), 0);
411 			if (write(fd, &dq->dq_dqb, sizeof (struct dqblk)) !=
412 			    sizeof (struct dqblk)) {
413 				fprintf(stderr, "edquota: ");
414 				perror(*dqf);
415 			}
416 			close(fd);
417 		}
418 	}
419 }
420