Home | History | Annotate | Line # | Download | only in edquota
edquota.c revision 1.22
      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. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by the University of
     19  *	California, Berkeley and its contributors.
     20  * 4. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 #ifndef lint
     39 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\n\
     40 	The Regents of the University of California.  All rights reserved.\n");
     41 #endif /* not lint */
     42 
     43 #ifndef lint
     44 #if 0
     45 static char sccsid[] = "from: @(#)edquota.c	8.3 (Berkeley) 4/27/95";
     46 #else
     47 __RCSID("$NetBSD: edquota.c,v 1.22 2002/12/04 21:01:13 bouyer Exp $");
     48 #endif
     49 #endif /* not lint */
     50 
     51 /*
     52  * Disk quota editor.
     53  */
     54 #include <sys/param.h>
     55 #include <sys/stat.h>
     56 #include <sys/file.h>
     57 #include <sys/wait.h>
     58 #include <sys/queue.h>
     59 #include <ufs/ufs/quota.h>
     60 #include <err.h>
     61 #include <errno.h>
     62 #include <fstab.h>
     63 #include <pwd.h>
     64 #include <grp.h>
     65 #include <ctype.h>
     66 #include <signal.h>
     67 #include <stdio.h>
     68 #include <stdlib.h>
     69 #include <string.h>
     70 #include <unistd.h>
     71 #include "pathnames.h"
     72 
     73 char *qfname = QUOTAFILENAME;
     74 char *qfextension[] = INITQFNAMES;
     75 char *quotagroup = QUOTAGROUP;
     76 char tmpfil[] = _PATH_TMP;
     77 
     78 struct quotause {
     79 	struct	quotause *next;
     80 	long	flags;
     81 	struct	dqblk dqblk;
     82 	char	fsname[MAXPATHLEN + 1];
     83 	char	qfname[1];	/* actually longer */
     84 };
     85 #define	FOUND	0x01
     86 
     87 int	main __P((int, char **));
     88 void	usage __P((void));
     89 int	getentry __P((char *, int));
     90 struct quotause *
     91 	getprivs __P((long, int, char *));
     92 void	putprivs __P((long, int, struct quotause *));
     93 int	editit __P((char *));
     94 int	writeprivs __P((struct quotause *, int, char *, int));
     95 int	readprivs __P((struct quotause *, int));
     96 int	writetimes __P((struct quotause *, int, int));
     97 int	readtimes __P((struct quotause *, int));
     98 char *	cvtstoa __P((time_t));
     99 int	cvtatos __P((time_t, char *, time_t *));
    100 void	freeprivs __P((struct quotause *));
    101 int	alldigits __P((char *));
    102 int	hasquota __P((struct fstab *, int, char **));
    103 
    104 int
    105 main(argc, argv)
    106 	int argc;
    107 	char **argv;
    108 {
    109 	struct quotause *qup, *protoprivs, *curprivs;
    110 	long id, protoid;
    111 	int quotatype, tmpfd;
    112 	char *protoname;
    113 	char *soft = NULL, *hard = NULL;
    114 	char *fs = NULL;
    115 	int ch;
    116 	int tflag = 0, pflag = 0;
    117 
    118 	if (argc < 2)
    119 		usage();
    120 	if (getuid())
    121 		errx(1, "permission denied");
    122 	protoname = NULL;
    123 	quotatype = USRQUOTA;
    124 	while ((ch = getopt(argc, argv, "ugtp:s:h:f:")) != -1) {
    125 		switch(ch) {
    126 		case 'p':
    127 			protoname = optarg;
    128 			pflag++;
    129 			break;
    130 		case 'g':
    131 			quotatype = GRPQUOTA;
    132 			break;
    133 		case 'u':
    134 			quotatype = USRQUOTA;
    135 			break;
    136 		case 't':
    137 			tflag++;
    138 			break;
    139 		case 's':
    140 			soft = optarg;
    141 			break;
    142 		case 'h':
    143 			hard = optarg;
    144 			break;
    145 		case 'f':
    146 			fs = optarg;
    147 			break;
    148 		default:
    149 			usage();
    150 		}
    151 	}
    152 	argc -= optind;
    153 	argv += optind;
    154 	if (pflag) {
    155 		if (soft || hard)
    156 			usage();
    157 		if ((protoid = getentry(protoname, quotatype)) == -1)
    158 			exit(1);
    159 		protoprivs = getprivs(protoid, quotatype, fs);
    160 		for (qup = protoprivs; qup; qup = qup->next) {
    161 			qup->dqblk.dqb_btime = 0;
    162 			qup->dqblk.dqb_itime = 0;
    163 		}
    164 		while (argc-- > 0) {
    165 			if ((id = getentry(*argv++, quotatype)) < 0)
    166 				continue;
    167 			putprivs(id, quotatype, protoprivs);
    168 		}
    169 		exit(0);
    170 	}
    171 	if (soft || hard) {
    172 		struct quotause *qup;
    173 		u_int32_t softb, hardb, softi, hardi;
    174 		if (tflag)
    175 			usage();
    176 		if (soft) {
    177 			if (sscanf(soft, "%d/%d", &softb, &softi) != 2)
    178 				usage();
    179 			softb = btodb((u_quad_t)softb * 1024);
    180 		}
    181 		if (hard) {
    182 			if (sscanf(hard, "%d/%d", &hardb, &hardi) != 2)
    183 				usage();
    184 			hardb = btodb((u_quad_t)hardb * 1024);
    185 		}
    186 		for ( ; argc > 0; argc--, argv++) {
    187 			if ((id = getentry(*argv, quotatype)) == -1)
    188 				continue;
    189 			curprivs = getprivs(id, quotatype, fs);
    190 			for (qup = curprivs; qup; qup = qup->next) {
    191 				if (soft) {
    192 					if (softb &&
    193 					    qup->dqblk.dqb_curblocks >= softb &&
    194 					    (qup->dqblk.dqb_bsoftlimit == 0 ||
    195 					    qup->dqblk.dqb_curblocks <
    196 					    qup->dqblk.dqb_bsoftlimit))
    197 						qup->dqblk.dqb_btime = 0;
    198 					if (softi &&
    199 					    qup->dqblk.dqb_curinodes >= softi &&
    200 					    (qup->dqblk.dqb_isoftlimit == 0 ||
    201 					    qup->dqblk.dqb_curinodes <
    202 					    qup->dqblk.dqb_isoftlimit))
    203 						qup->dqblk.dqb_itime = 0;
    204 					qup->dqblk.dqb_bsoftlimit = softb;
    205 					qup->dqblk.dqb_isoftlimit = softi;
    206 				}
    207 				if (hard) {
    208 					qup->dqblk.dqb_bhardlimit = hardb;
    209 					qup->dqblk.dqb_ihardlimit = hardi;
    210 				}
    211 			}
    212 			putprivs(id, quotatype, curprivs);
    213 			freeprivs(curprivs);
    214 		}
    215 		exit(0);
    216 	}
    217 	tmpfd = mkstemp(tmpfil);
    218 	fchown(tmpfd, getuid(), getgid());
    219 	if (tflag) {
    220 		if (soft || hard)
    221 			usage();
    222 		protoprivs = getprivs(0, quotatype, fs);
    223 		if (writetimes(protoprivs, tmpfd, quotatype) == 0)
    224 			exit(1);
    225 		if (editit(tmpfil) && readtimes(protoprivs, tmpfd))
    226 			putprivs(0, quotatype, protoprivs);
    227 		freeprivs(protoprivs);
    228 		exit(0);
    229 	}
    230 	for ( ; argc > 0; argc--, argv++) {
    231 		if ((id = getentry(*argv, quotatype)) == -1)
    232 			continue;
    233 		curprivs = getprivs(id, quotatype, fs);
    234 		if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
    235 			continue;
    236 		if (editit(tmpfil) && readprivs(curprivs, tmpfd))
    237 			putprivs(id, quotatype, curprivs);
    238 		freeprivs(curprivs);
    239 	}
    240 	close(tmpfd);
    241 	unlink(tmpfil);
    242 	exit(0);
    243 }
    244 
    245 void
    246 usage()
    247 {
    248 	fprintf(stderr,
    249 	    "Usage: edquota [-u] [-p username] [-f filesystem] username ...\n"
    250 	    "\tedquota -g [-p groupname] [-f filesystem] groupname ...\n"
    251 	    "\tedquota [-u] [-f filesystem] [-s b#/i#] [-h b#/i#] username ...\n"
    252 	    "\tedquota -g [-f filesystem] [-s b#/i#] [-h b#/i#] groupname ...\n"
    253 	    "\tedquota [-u] [-f filesystem] -t\n"
    254 	    "\tedquota -g [-f filesystem] -t\n"
    255 	    );
    256 	exit(1);
    257 }
    258 
    259 /*
    260  * This routine converts a name for a particular quota type to
    261  * an identifier. This routine must agree with the kernel routine
    262  * getinoquota as to the interpretation of quota types.
    263  */
    264 int
    265 getentry(name, quotatype)
    266 	char *name;
    267 	int quotatype;
    268 {
    269 	struct passwd *pw;
    270 	struct group *gr;
    271 
    272 	if (alldigits(name))
    273 		return (atoi(name));
    274 	switch(quotatype) {
    275 	case USRQUOTA:
    276 		if ((pw = getpwnam(name)) != NULL)
    277 			return (pw->pw_uid);
    278 		warnx("%s: no such user", name);
    279 		break;
    280 	case GRPQUOTA:
    281 		if ((gr = getgrnam(name)) != NULL)
    282 			return (gr->gr_gid);
    283 		warnx("%s: no such group", name);
    284 		break;
    285 	default:
    286 		warnx("%d: unknown quota type", quotatype);
    287 		break;
    288 	}
    289 	sleep(1);
    290 	return (-1);
    291 }
    292 
    293 /*
    294  * Collect the requested quota information.
    295  */
    296 struct quotause *
    297 getprivs(id, quotatype, filesys)
    298 	long id;
    299 	int quotatype;
    300 	char *filesys;
    301 {
    302 	struct fstab *fs;
    303 	struct quotause *qup, *quptail;
    304 	struct quotause *quphead;
    305 	int qcmd, qupsize, fd;
    306 	char *qfpathname;
    307 	static int warned = 0;
    308 
    309 	setfsent();
    310 	quptail = NULL;
    311 	quphead = (struct quotause *)0;
    312 	qcmd = QCMD(Q_GETQUOTA, quotatype);
    313 	while ((fs = getfsent()) != NULL) {
    314 		if (strcmp(fs->fs_vfstype, "ffs"))
    315 			continue;
    316 		if (filesys && strcmp(fs->fs_spec, filesys) != 0 &&
    317 		    strcmp(fs->fs_file, filesys) != 0)
    318 			continue;
    319 		if (!hasquota(fs, quotatype, &qfpathname))
    320 			continue;
    321 		qupsize = sizeof(*qup) + strlen(qfpathname);
    322 		if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
    323 			errx(2, "out of memory");
    324 		if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
    325 	    		if (errno == EOPNOTSUPP && !warned) {
    326 				warned++;
    327 				warnx(
    328 				    "Quotas are not compiled into this kernel");
    329 				sleep(3);
    330 			}
    331 			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
    332 				fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
    333 				if (fd < 0 && errno != ENOENT) {
    334 					warnx("open `%s'", qfpathname);
    335 					free(qup);
    336 					continue;
    337 				}
    338 				warnx("Creating quota file %s", qfpathname);
    339 				sleep(3);
    340 				(void) fchown(fd, getuid(),
    341 				    getentry(quotagroup, GRPQUOTA));
    342 				(void) fchmod(fd, 0640);
    343 			}
    344 			(void)lseek(fd, (off_t)(id * sizeof(struct dqblk)),
    345 			    SEEK_SET);
    346 			switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
    347 			case 0:			/* EOF */
    348 				/*
    349 				 * Convert implicit 0 quota (EOF)
    350 				 * into an explicit one (zero'ed dqblk)
    351 				 */
    352 				memset((caddr_t)&qup->dqblk, 0,
    353 				    sizeof(struct dqblk));
    354 				break;
    355 
    356 			case sizeof(struct dqblk):	/* OK */
    357 				break;
    358 
    359 			default:		/* ERROR */
    360 				warn("read error in `%s'", qfpathname);
    361 				close(fd);
    362 				free(qup);
    363 				continue;
    364 			}
    365 			close(fd);
    366 		}
    367 		strcpy(qup->qfname, qfpathname);
    368 		strcpy(qup->fsname, fs->fs_file);
    369 		if (quphead == NULL)
    370 			quphead = qup;
    371 		else
    372 			quptail->next = qup;
    373 		quptail = qup;
    374 		qup->next = 0;
    375 	}
    376 	endfsent();
    377 	return (quphead);
    378 }
    379 
    380 /*
    381  * Store the requested quota information.
    382  */
    383 void
    384 putprivs(id, quotatype, quplist)
    385 	long id;
    386 	int quotatype;
    387 	struct quotause *quplist;
    388 {
    389 	struct quotause *qup;
    390 	int qcmd, fd;
    391 
    392 	qcmd = QCMD(Q_SETQUOTA, quotatype);
    393 	for (qup = quplist; qup; qup = qup->next) {
    394 		if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
    395 			continue;
    396 		if ((fd = open(qup->qfname, O_WRONLY)) < 0) {
    397 			warnx("open `%s'", qup->qfname);
    398 		} else {
    399 			(void)lseek(fd,
    400 			    (off_t)(id * (long)sizeof (struct dqblk)),
    401 			    SEEK_SET);
    402 			if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
    403 			    sizeof (struct dqblk))
    404 				warnx("writing `%s'", qup->qfname);
    405 			close(fd);
    406 		}
    407 	}
    408 }
    409 
    410 /*
    411  * Take a list of privileges and get it edited.
    412  */
    413 int
    414 editit(tmpfile)
    415 	char *tmpfile;
    416 {
    417 	long omask;
    418 	int pid, stat;
    419 
    420 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
    421  top:
    422 	if ((pid = fork()) < 0) {
    423 
    424 		if (errno == EPROCLIM) {
    425 			warnx("You have too many processes");
    426 			return(0);
    427 		}
    428 		if (errno == EAGAIN) {
    429 			sleep(1);
    430 			goto top;
    431 		}
    432 		warn("fork");
    433 		return (0);
    434 	}
    435 	if (pid == 0) {
    436 		char *ed;
    437 
    438 		sigsetmask(omask);
    439 		setgid(getgid());
    440 		setuid(getuid());
    441 		if ((ed = getenv("EDITOR")) == (char *)0)
    442 			ed = _PATH_VI;
    443 		execlp(ed, ed, tmpfile, 0);
    444 		err(1, "%s", ed);
    445 	}
    446 	waitpid(pid, &stat, 0);
    447 	sigsetmask(omask);
    448 	if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0)
    449 		return (0);
    450 	return (1);
    451 }
    452 
    453 /*
    454  * Convert a quotause list to an ASCII file.
    455  */
    456 int
    457 writeprivs(quplist, outfd, name, quotatype)
    458 	struct quotause *quplist;
    459 	int outfd;
    460 	char *name;
    461 	int quotatype;
    462 {
    463 	struct quotause *qup;
    464 	FILE *fd;
    465 
    466 	ftruncate(outfd, 0);
    467 	(void)lseek(outfd, (off_t)0, SEEK_SET);
    468 	if ((fd = fdopen(dup(outfd), "w")) == NULL)
    469 		errx(1, "fdopen `%s'", tmpfil);
    470 	fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
    471 	for (qup = quplist; qup; qup = qup->next) {
    472 		fprintf(fd, "%s: %s %d, limits (soft = %d, hard = %d)\n",
    473 		    qup->fsname, "blocks in use:",
    474 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_curblocks) / 1024),
    475 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bsoftlimit) / 1024),
    476 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bhardlimit) / 1024));
    477 		fprintf(fd, "%s %d, limits (soft = %d, hard = %d)\n",
    478 		    "\tinodes in use:", qup->dqblk.dqb_curinodes,
    479 		    qup->dqblk.dqb_isoftlimit, qup->dqblk.dqb_ihardlimit);
    480 	}
    481 	fclose(fd);
    482 	return (1);
    483 }
    484 
    485 /*
    486  * Merge changes to an ASCII file into a quotause list.
    487  */
    488 int
    489 readprivs(quplist, infd)
    490 	struct quotause *quplist;
    491 	int infd;
    492 {
    493 	struct quotause *qup;
    494 	FILE *fd;
    495 	int cnt;
    496 	char *cp;
    497 	struct dqblk dqblk;
    498 	char *fsp, line1[BUFSIZ], line2[BUFSIZ];
    499 
    500 	(void)lseek(infd, (off_t)0, SEEK_SET);
    501 	fd = fdopen(dup(infd), "r");
    502 	if (fd == NULL) {
    503 		warn("Can't re-read temp file");
    504 		return (0);
    505 	}
    506 	/*
    507 	 * Discard title line, then read pairs of lines to process.
    508 	 */
    509 	(void) fgets(line1, sizeof (line1), fd);
    510 	while (fgets(line1, sizeof (line1), fd) != NULL &&
    511 	       fgets(line2, sizeof (line2), fd) != NULL) {
    512 		if ((fsp = strtok(line1, " \t:")) == NULL) {
    513 			warnx("%s: bad format", line1);
    514 			return (0);
    515 		}
    516 		if ((cp = strtok((char *)0, "\n")) == NULL) {
    517 			warnx("%s: %s: bad format", fsp,
    518 			    &fsp[strlen(fsp) + 1]);
    519 			return (0);
    520 		}
    521 		cnt = sscanf(cp,
    522 		    " blocks in use: %d, limits (soft = %d, hard = %d)",
    523 		    &dqblk.dqb_curblocks, &dqblk.dqb_bsoftlimit,
    524 		    &dqblk.dqb_bhardlimit);
    525 		if (cnt != 3) {
    526 			warnx("%s:%s: bad format", fsp, cp);
    527 			return (0);
    528 		}
    529 		dqblk.dqb_curblocks = btodb((u_quad_t)
    530 		    dqblk.dqb_curblocks * 1024);
    531 		dqblk.dqb_bsoftlimit = btodb((u_quad_t)
    532 		    dqblk.dqb_bsoftlimit * 1024);
    533 		dqblk.dqb_bhardlimit = btodb((u_quad_t)
    534 		    dqblk.dqb_bhardlimit * 1024);
    535 		if ((cp = strtok(line2, "\n")) == NULL) {
    536 			warnx("%s: %s: bad format", fsp, line2);
    537 			return (0);
    538 		}
    539 		cnt = sscanf(cp,
    540 		    "\tinodes in use: %d, limits (soft = %d, hard = %d)",
    541 		    &dqblk.dqb_curinodes, &dqblk.dqb_isoftlimit,
    542 		    &dqblk.dqb_ihardlimit);
    543 		if (cnt != 3) {
    544 			warnx("%s: %s: bad format", fsp, line2);
    545 			return (0);
    546 		}
    547 		for (qup = quplist; qup; qup = qup->next) {
    548 			if (strcmp(fsp, qup->fsname))
    549 				continue;
    550 			/*
    551 			 * Cause time limit to be reset when the quota
    552 			 * is next used if previously had no soft limit
    553 			 * or were under it, but now have a soft limit
    554 			 * and are over it.
    555 			 */
    556 			if (dqblk.dqb_bsoftlimit &&
    557 			    qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
    558 			    (qup->dqblk.dqb_bsoftlimit == 0 ||
    559 			     qup->dqblk.dqb_curblocks <
    560 			     qup->dqblk.dqb_bsoftlimit))
    561 				qup->dqblk.dqb_btime = 0;
    562 			if (dqblk.dqb_isoftlimit &&
    563 			    qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
    564 			    (qup->dqblk.dqb_isoftlimit == 0 ||
    565 			     qup->dqblk.dqb_curinodes <
    566 			     qup->dqblk.dqb_isoftlimit))
    567 				qup->dqblk.dqb_itime = 0;
    568 			qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
    569 			qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
    570 			qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
    571 			qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
    572 			qup->flags |= FOUND;
    573 			if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
    574 			    dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
    575 				break;
    576 			warnx("%s: cannot change current allocation", fsp);
    577 			break;
    578 		}
    579 	}
    580 	fclose(fd);
    581 	/*
    582 	 * Disable quotas for any filesystems that have not been found.
    583 	 */
    584 	for (qup = quplist; qup; qup = qup->next) {
    585 		if (qup->flags & FOUND) {
    586 			qup->flags &= ~FOUND;
    587 			continue;
    588 		}
    589 		qup->dqblk.dqb_bsoftlimit = 0;
    590 		qup->dqblk.dqb_bhardlimit = 0;
    591 		qup->dqblk.dqb_isoftlimit = 0;
    592 		qup->dqblk.dqb_ihardlimit = 0;
    593 	}
    594 	return (1);
    595 }
    596 
    597 /*
    598  * Convert a quotause list to an ASCII file of grace times.
    599  */
    600 int
    601 writetimes(quplist, outfd, quotatype)
    602 	struct quotause *quplist;
    603 	int outfd;
    604 	int quotatype;
    605 {
    606 	struct quotause *qup;
    607 	FILE *fd;
    608 
    609 	ftruncate(outfd, 0);
    610 	(void)lseek(outfd, (off_t)0, SEEK_SET);
    611 	if ((fd = fdopen(dup(outfd), "w")) == NULL)
    612 		err(1, "fdopen `%s'", tmpfil);
    613 	fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
    614 	fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
    615 	    qfextension[quotatype]);
    616 	for (qup = quplist; qup; qup = qup->next) {
    617 		fprintf(fd, "%s: block grace period: %s, ",
    618 		    qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
    619 		fprintf(fd, "file grace period: %s\n",
    620 		    cvtstoa(qup->dqblk.dqb_itime));
    621 	}
    622 	fclose(fd);
    623 	return (1);
    624 }
    625 
    626 /*
    627  * Merge changes of grace times in an ASCII file into a quotause list.
    628  */
    629 int
    630 readtimes(quplist, infd)
    631 	struct quotause *quplist;
    632 	int infd;
    633 {
    634 	struct quotause *qup;
    635 	FILE *fd;
    636 	int cnt;
    637 	char *cp;
    638 	long litime, lbtime;
    639 	time_t itime, btime, iseconds, bseconds;
    640 	char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
    641 
    642 	(void)lseek(infd, (off_t)0, SEEK_SET);
    643 	fd = fdopen(dup(infd), "r");
    644 	if (fd == NULL) {
    645 		warnx("Can't re-read temp file!!");
    646 		return (0);
    647 	}
    648 	/*
    649 	 * Discard two title lines, then read lines to process.
    650 	 */
    651 	(void) fgets(line1, sizeof (line1), fd);
    652 	(void) fgets(line1, sizeof (line1), fd);
    653 	while (fgets(line1, sizeof (line1), fd) != NULL) {
    654 		if ((fsp = strtok(line1, " \t:")) == NULL) {
    655 			warnx("%s: bad format", line1);
    656 			return (0);
    657 		}
    658 		if ((cp = strtok((char *)0, "\n")) == NULL) {
    659 			warnx("%s: %s: bad format", fsp,
    660 			    &fsp[strlen(fsp) + 1]);
    661 			return (0);
    662 		}
    663 		cnt = sscanf(cp,
    664 		    " block grace period: %ld %s file grace period: %ld %s",
    665 		    &lbtime, bunits, &litime, iunits);
    666 		if (cnt != 4) {
    667 			warnx("%s:%s: bad format", fsp, cp);
    668 			return (0);
    669 		}
    670 		itime = (time_t)litime;
    671 		btime = (time_t)lbtime;
    672 		if (cvtatos(btime, bunits, &bseconds) == 0)
    673 			return (0);
    674 		if (cvtatos(itime, iunits, &iseconds) == 0)
    675 			return (0);
    676 		for (qup = quplist; qup; qup = qup->next) {
    677 			if (strcmp(fsp, qup->fsname))
    678 				continue;
    679 			qup->dqblk.dqb_btime = bseconds;
    680 			qup->dqblk.dqb_itime = iseconds;
    681 			qup->flags |= FOUND;
    682 			break;
    683 		}
    684 	}
    685 	fclose(fd);
    686 	/*
    687 	 * reset default grace periods for any filesystems
    688 	 * that have not been found.
    689 	 */
    690 	for (qup = quplist; qup; qup = qup->next) {
    691 		if (qup->flags & FOUND) {
    692 			qup->flags &= ~FOUND;
    693 			continue;
    694 		}
    695 		qup->dqblk.dqb_btime = 0;
    696 		qup->dqblk.dqb_itime = 0;
    697 	}
    698 	return (1);
    699 }
    700 
    701 /*
    702  * Convert seconds to ASCII times.
    703  */
    704 char *
    705 cvtstoa(time)
    706 	time_t time;
    707 {
    708 	static char buf[20];
    709 
    710 	if (time % (24 * 60 * 60) == 0) {
    711 		time /= 24 * 60 * 60;
    712 		snprintf(buf, sizeof buf, "%ld day%s", (long)time,
    713 		    time == 1 ? "" : "s");
    714 	} else if (time % (60 * 60) == 0) {
    715 		time /= 60 * 60;
    716 		sprintf(buf, "%ld hour%s", (long)time, time == 1 ? "" : "s");
    717 	} else if (time % 60 == 0) {
    718 		time /= 60;
    719 		sprintf(buf, "%ld minute%s", (long)time, time == 1 ? "" : "s");
    720 	} else
    721 		sprintf(buf, "%ld second%s", (long)time, time == 1 ? "" : "s");
    722 	return (buf);
    723 }
    724 
    725 /*
    726  * Convert ASCII input times to seconds.
    727  */
    728 int
    729 cvtatos(time, units, seconds)
    730 	time_t time;
    731 	char *units;
    732 	time_t *seconds;
    733 {
    734 
    735 	if (memcmp(units, "second", 6) == 0)
    736 		*seconds = time;
    737 	else if (memcmp(units, "minute", 6) == 0)
    738 		*seconds = time * 60;
    739 	else if (memcmp(units, "hour", 4) == 0)
    740 		*seconds = time * 60 * 60;
    741 	else if (memcmp(units, "day", 3) == 0)
    742 		*seconds = time * 24 * 60 * 60;
    743 	else {
    744 		printf("%s: bad units, specify %s\n", units,
    745 		    "days, hours, minutes, or seconds");
    746 		return (0);
    747 	}
    748 	return (1);
    749 }
    750 
    751 /*
    752  * Free a list of quotause structures.
    753  */
    754 void
    755 freeprivs(quplist)
    756 	struct quotause *quplist;
    757 {
    758 	struct quotause *qup, *nextqup;
    759 
    760 	for (qup = quplist; qup; qup = nextqup) {
    761 		nextqup = qup->next;
    762 		free(qup);
    763 	}
    764 }
    765 
    766 /*
    767  * Check whether a string is completely composed of digits.
    768  */
    769 int
    770 alldigits(s)
    771 	char *s;
    772 {
    773 	int c;
    774 
    775 	c = *s++;
    776 	do {
    777 		if (!isdigit(c))
    778 			return (0);
    779 	} while ((c = *s++) != 0);
    780 	return (1);
    781 }
    782 
    783 /*
    784  * Check to see if a particular quota is to be enabled.
    785  */
    786 int
    787 hasquota(fs, type, qfnamep)
    788 	struct fstab *fs;
    789 	int type;
    790 	char **qfnamep;
    791 {
    792 	char *opt;
    793 	char *cp;
    794 	static char initname, usrname[100], grpname[100];
    795 	static char buf[BUFSIZ];
    796 
    797 	if (!initname) {
    798 		sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
    799 		sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
    800 		initname = 1;
    801 	}
    802 	strcpy(buf, fs->fs_mntops);
    803 	cp = NULL;
    804 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
    805 		if ((cp = strchr(opt, '=')) != NULL)
    806 			*cp++ = '\0';
    807 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
    808 			break;
    809 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
    810 			break;
    811 	}
    812 	if (!opt)
    813 		return (0);
    814 	if (cp) {
    815 		*qfnamep = cp;
    816 		return (1);
    817 	}
    818 	(void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
    819 	*qfnamep = buf;
    820 	return (1);
    821 }
    822