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