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