xref: /openbsd-src/usr.bin/quota/quota.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: quota.c,v 1.38 2016/03/16 15:41:11 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Robert Elz at The University of Melbourne.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*
36  * Disk quota reporting program.
37  */
38 #include <sys/param.h>	/* DEV_BSIZE dbtob */
39 #include <sys/types.h>
40 #include <sys/file.h>
41 #include <sys/stat.h>
42 #include <sys/mount.h>
43 #include <sys/socket.h>
44 
45 #include <ufs/ufs/quota.h>
46 #include <ctype.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fstab.h>
50 #include <grp.h>
51 #include <netdb.h>
52 #include <pwd.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <time.h>
57 #include <unistd.h>
58 
59 #include <rpc/rpc.h>
60 #include <rpc/pmap_prot.h>
61 #include <rpcsvc/rquota.h>
62 
63 char *qfname = QUOTAFILENAME;
64 char *qfextension[] = INITQFNAMES;
65 
66 struct quotause {
67 	struct	quotause *next;
68 	long	flags;
69 	struct	dqblk dqblk;
70 	char	fsname[PATH_MAX + 1];
71 };
72 #define	FOUND	0x01
73 
74 int	alldigits(char *);
75 int	callaurpc(char *, int, int, int, xdrproc_t, void *, xdrproc_t, void *);
76 int	getnfsquota(struct statfs *, struct fstab *, struct quotause *,
77 	    long, int);
78 struct quotause
79        *getprivs(long id, int quotatype);
80 int	getufsquota(struct statfs *, struct fstab *, struct quotause *,
81 	    long, int);
82 void	heading(int, u_long, const char *, const char *);
83 void	showgid(gid_t);
84 void	showgrpname(const char *);
85 void	showquotas(int, u_long, const char *);
86 void	showuid(uid_t);
87 void	showusrname(const char *);
88 char   *timeprt(time_t seconds);
89 int	ufshasquota(struct fstab *, int, char **);
90 void	usage(void);
91 
92 int	qflag;
93 int	vflag;
94 
95 int
96 main(int argc, char *argv[])
97 {
98 	int ngroups;
99 	gid_t mygid, gidset[NGROUPS_MAX];
100 	int i, gflag = 0, uflag = 0;
101 	int ch;
102 	extern char *optarg;
103 	extern int optind;
104 
105 	while ((ch = getopt(argc, argv, "ugvq")) != -1) {
106 		switch(ch) {
107 		case 'g':
108 			gflag = 1;
109 			break;
110 		case 'u':
111 			uflag = 1;
112 			break;
113 		case 'v':
114 			vflag = 1;
115 			break;
116 		case 'q':
117 			qflag = 1;
118 			break;
119 		default:
120 			usage();
121 		}
122 	}
123 	argc -= optind;
124 	argv += optind;
125 	if (!uflag && !gflag)
126 		uflag = 1;
127 	if (argc == 0) {
128 		if (uflag)
129 			showuid(getuid());
130 		if (gflag) {
131 			mygid = getgid();
132 			ngroups = getgroups(NGROUPS_MAX, gidset);
133 			if (ngroups < 0)
134 				err(1, "getgroups");
135 			showgid(mygid);
136 			for (i = 0; i < ngroups; i++)
137 				if (gidset[i] != mygid)
138 					showgid(gidset[i]);
139 		}
140 		exit(0);
141 	}
142 	if (uflag && gflag)
143 		usage();
144 	if (uflag) {
145 		for (; argc > 0; argc--, argv++) {
146 			if (alldigits(*argv))
147 				showuid(atoi(*argv));
148 			else
149 				showusrname(*argv);
150 		}
151 		exit(0);
152 	}
153 	if (gflag) {
154 		for (; argc > 0; argc--, argv++) {
155 			if (alldigits(*argv))
156 				showgid(atoi(*argv));
157 			else
158 				showgrpname(*argv);
159 		}
160 		exit(0);
161 	}
162 	/* NOTREACHED */
163 
164 	exit(1);
165 }
166 
167 void
168 usage(void)
169 {
170 	fprintf(stderr, "%s\n%s\n%s\n",
171 	    "usage: quota [-q | -v] [-gu]",
172 	    "       quota [-q | -v] -g group ...",
173 	    "       quota [-q | -v] -u user ...");
174 	exit(1);
175 }
176 
177 /*
178  * Print out quotas for a specified user identifier.
179  */
180 void
181 showuid(uid_t uid)
182 {
183 	struct passwd *pwd = getpwuid(uid);
184 	uid_t myuid;
185 	const char *name;
186 
187 	if (pwd == NULL)
188 		name = "(no account)";
189 	else
190 		name = pwd->pw_name;
191 	myuid = getuid();
192 	if (uid != myuid && myuid != 0) {
193 		warnx("%s (uid %u): permission denied", name, uid);
194 		return;
195 	}
196 	showquotas(USRQUOTA, uid, name);
197 }
198 
199 /*
200  * Print out quotas for a specified user name.
201  */
202 void
203 showusrname(const char *name)
204 {
205 	struct passwd *pwd = getpwnam(name);
206 	uid_t myuid;
207 
208 	if (pwd == NULL) {
209 		warnx("%s: unknown user", name);
210 		return;
211 	}
212 	myuid = getuid();
213 	if (pwd->pw_uid != myuid && myuid != 0) {
214 		warnx("%s (uid %u): permission denied", pwd->pw_name,
215 		    pwd->pw_uid);
216 		return;
217 	}
218 	showquotas(USRQUOTA, pwd->pw_uid, pwd->pw_name);
219 }
220 
221 /*
222  * Print out quotas for a specified group identifier.
223  */
224 void
225 showgid(gid_t gid)
226 {
227 	struct group *grp = getgrgid(gid);
228 	int ngroups;
229 	gid_t mygid, gidset[NGROUPS_MAX];
230 	int i;
231 	const char *name;
232 
233 	if (grp == NULL)
234 		name = "(no entry)";
235 	else
236 		name = grp->gr_name;
237 	mygid = getgid();
238 	ngroups = getgroups(NGROUPS_MAX, gidset);
239 	if (ngroups < 0) {
240 		warn("getgroups");
241 		return;
242 	}
243 	if (gid != mygid) {
244 		for (i = 0; i < ngroups; i++)
245 			if (gid == gidset[i])
246 				break;
247 		if (i >= ngroups && getuid() != 0) {
248 			warnx("%s (gid %u): permission denied", name, gid);
249 			return;
250 		}
251 	}
252 	showquotas(GRPQUOTA, gid, name);
253 }
254 
255 /*
256  * Print out quotas for a specified group name.
257  */
258 void
259 showgrpname(const char *name)
260 {
261 	struct group *grp = getgrnam(name);
262 	int ngroups;
263 	gid_t mygid, gidset[NGROUPS_MAX];
264 	int i;
265 
266 	if (grp == NULL) {
267 		warnx("%s: unknown group", name);
268 		return;
269 	}
270 	mygid = getgid();
271 	ngroups = getgroups(NGROUPS_MAX, gidset);
272 	if (ngroups < 0) {
273 		warn("getgroups");
274 		return;
275 	}
276 	if (grp->gr_gid != mygid) {
277 		for (i = 0; i < ngroups; i++)
278 			if (grp->gr_gid == gidset[i])
279 				break;
280 		if (i >= ngroups && getuid() != 0) {
281 			warnx("%s (gid %u): permission denied",
282 			    grp->gr_name, grp->gr_gid);
283 			return;
284 		}
285 	}
286 	showquotas(GRPQUOTA, grp->gr_gid, grp->gr_name);
287 }
288 
289 void
290 showquotas(int type, u_long id, const char *name)
291 {
292 	struct quotause *qup;
293 	struct quotause *quplist;
294 	char *msgi, *msgb, *nam;
295 	uid_t lines = 0;
296 	static time_t now;
297 
298 	if (now == 0)
299 		time(&now);
300 	quplist = getprivs(id, type);
301 	for (qup = quplist; qup; qup = qup->next) {
302 		if (!vflag &&
303 		    qup->dqblk.dqb_isoftlimit == 0 &&
304 		    qup->dqblk.dqb_ihardlimit == 0 &&
305 		    qup->dqblk.dqb_bsoftlimit == 0 &&
306 		    qup->dqblk.dqb_bhardlimit == 0)
307 			continue;
308 		msgi = NULL;
309 		if (qup->dqblk.dqb_ihardlimit &&
310 		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit)
311 			msgi = "File limit reached on";
312 		else if (qup->dqblk.dqb_isoftlimit &&
313 		    qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) {
314 			if (qup->dqblk.dqb_itime > now)
315 				msgi = "In file grace period on";
316 			else
317 				msgi = "Over file quota on";
318 		}
319 		msgb = NULL;
320 		if (qup->dqblk.dqb_bhardlimit &&
321 		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit)
322 			msgb = "Block limit reached on";
323 		else if (qup->dqblk.dqb_bsoftlimit &&
324 		    qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) {
325 			if (qup->dqblk.dqb_btime > now)
326 				msgb = "In block grace period on";
327 			else
328 				msgb = "Over block quota on";
329 		}
330 		if (qflag) {
331 			if ((msgi != NULL || msgb != NULL) &&
332 			    lines++ == 0)
333 				heading(type, id, name, "");
334 			if (msgi != NULL)
335 				printf("\t%s %s\n", msgi, qup->fsname);
336 			if (msgb != NULL)
337 				printf("\t%s %s\n", msgb, qup->fsname);
338 			continue;
339 		}
340 		if (vflag ||
341 		    qup->dqblk.dqb_curblocks ||
342 		    qup->dqblk.dqb_curinodes) {
343 			if (lines++ == 0)
344 				heading(type, id, name, "");
345 			nam = qup->fsname;
346 			if (strlen(qup->fsname) > 15) {
347 				printf("%s\n", qup->fsname);
348 				nam = "";
349 			}
350 			printf("%12s %7d%c %7d %7d %7s",
351 			    nam,
352 			    (int)(dbtob((u_quad_t)qup->dqblk.dqb_curblocks)
353 				/ 1024),
354 			    (msgb == NULL) ? ' ' : '*',
355 			    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bsoftlimit)
356 				/ 1024),
357 			    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bhardlimit)
358 				/ 1024),
359 			    (msgb == NULL) ? ""
360 			        : timeprt(qup->dqblk.dqb_btime));
361 			printf(" %7d%c %7d %7d %7s\n",
362 			    qup->dqblk.dqb_curinodes,
363 			    (msgi == NULL) ? ' ' : '*',
364 			    qup->dqblk.dqb_isoftlimit,
365 			    qup->dqblk.dqb_ihardlimit,
366 			    (msgi == NULL) ? ""
367 			        : timeprt(qup->dqblk.dqb_itime)
368 			);
369 			continue;
370 		}
371 	}
372 	if (!qflag && lines == 0)
373 		heading(type, id, name, "none");
374 }
375 
376 void
377 heading(int type, u_long id, const char *name, const char *tag)
378 {
379 
380 	printf("Disk quotas for %s %s (%cid %ld): %s\n", qfextension[type],
381 	    name, *qfextension[type], id, tag);
382 	if (!qflag && tag[0] == '\0') {
383 		printf("%12s%8s%9s%8s%8s%9s%8s%8s%8s\n",
384 		    "Filesystem",
385 		    "KBytes",
386 		    "quota",
387 		    "limit",
388 		    "grace",
389 		    "files",
390 		    "quota",
391 		    "limit",
392 		    "grace");
393 	}
394 }
395 
396 /*
397  * Calculate the grace period and return a printable string for it.
398  */
399 char *
400 timeprt(time_t seconds)
401 {
402 	time_t hours, minutes;
403 	static char buf[20];
404 	static time_t now;
405 
406 	if (now == 0)
407 		time(&now);
408 	if (now > seconds)
409 		return ("none");
410 	seconds -= now;
411 	minutes = (seconds + 30) / 60;
412 	hours = (minutes + 30) / 60;
413 	if (hours >= 36) {
414 		(void)snprintf(buf, sizeof buf, "%ddays",
415 		    (int)((hours + 12) / 24));
416 		return (buf);
417 	}
418 	if (minutes >= 60) {
419 		(void)snprintf(buf, sizeof buf, "%2d:%d",
420 		    (int)(minutes / 60), (int)(minutes % 60));
421 		return (buf);
422 	}
423 	(void)snprintf(buf, sizeof buf, "%2d", (int)minutes);
424 	return (buf);
425 }
426 
427 /*
428  * Collect the requested quota information.
429  */
430 struct quotause *
431 getprivs(long id, int quotatype)
432 {
433 	struct quotause *qup, *quptail;
434 	struct fstab *fs;
435 	struct quotause *quphead;
436 	struct statfs *fst;
437 	int nfst, i;
438 
439 	qup = quphead = NULL;
440 
441 	nfst = getmntinfo(&fst, MNT_WAIT);
442 	if (nfst == 0)
443 		errx(2, "no filesystems mounted!");
444 	setfsent();
445 	for (i = 0; i < nfst; i++) {
446 		if (qup == NULL) {
447 			if ((qup = malloc(sizeof *qup)) == NULL)
448 				errx(2, "out of memory");
449 		}
450 		if (strncmp(fst[i].f_fstypename, "nfs", MFSNAMELEN) == 0) {
451 			if (getnfsquota(&fst[i], NULL, qup, id, quotatype) == 0)
452 				continue;
453 		} else if (!strncmp(fst[i].f_fstypename, "ffs", MFSNAMELEN) ||
454 		    !strncmp(fst[i].f_fstypename, "ufs", MFSNAMELEN) ||
455 		    !strncmp(fst[i].f_fstypename, "mfs", MFSNAMELEN)) {
456 			/*
457 			 * XXX
458 			 * UFS filesystems must be in /etc/fstab, and must
459 			 * indicate that they have quotas on (?!) This is quite
460 			 * unlike SunOS where quotas can be enabled/disabled
461 			 * on a filesystem independent of /etc/fstab, and it
462 			 * will still print quotas for them.
463 			 */
464 			if ((fs = getfsspec(fst[i].f_mntfromspec)) == NULL)
465 				continue;
466 			if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0)
467 				continue;
468 		} else
469 			continue;
470 		strncpy(qup->fsname, fst[i].f_mntonname, sizeof qup->fsname-1);
471 		qup->fsname[sizeof qup->fsname-1] = '\0';
472 		if (quphead == NULL)
473 			quphead = qup;
474 		else
475 			quptail->next = qup;
476 		quptail = qup;
477 		quptail->next = 0;
478 		qup = NULL;
479 	}
480 	free(qup);
481 	endfsent();
482 	return (quphead);
483 }
484 
485 /*
486  * Check to see if a particular quota is to be enabled.
487  */
488 int
489 ufshasquota(struct fstab *fs, int type, char **qfnamep)
490 {
491 	static char initname, usrname[100], grpname[100];
492 	static char buf[BUFSIZ];
493 	char *opt, *cp;
494 
495 	cp = NULL;
496 	if (!initname) {
497 		(void)snprintf(usrname, sizeof usrname, "%s%s",
498 		    qfextension[USRQUOTA], qfname);
499 		(void)snprintf(grpname, sizeof grpname, "%s%s",
500 		    qfextension[GRPQUOTA], qfname);
501 		initname = 1;
502 	}
503 	strncpy(buf, fs->fs_mntops, sizeof buf);
504 	buf[sizeof(buf) - 1] = '\0';
505 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
506 		if ((cp = strchr(opt, '=')))
507 			*cp++ = '\0';
508 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
509 			break;
510 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
511 			break;
512 	}
513 	if (!opt)
514 		return (0);
515 	if (cp) {
516 		*qfnamep = cp;
517 		return (1);
518 	}
519 	(void)snprintf(buf, sizeof buf, "%s/%s.%s",
520 	    fs->fs_file, qfname, qfextension[type]);
521 	*qfnamep = buf;
522 	return (1);
523 }
524 
525 int
526 getufsquota(struct statfs *fst, struct fstab *fs, struct quotause *qup,
527     long id, int quotatype)
528 {
529 	char *qfpathname;
530 	int fd, qcmd;
531 
532 	qcmd = QCMD(Q_GETQUOTA, quotatype);
533 	if (!ufshasquota(fs, quotatype, &qfpathname))
534 		return (0);
535 
536 	if (quotactl(fs->fs_file, qcmd, id, (char *)&qup->dqblk) != 0) {
537 		if ((fd = open(qfpathname, O_RDONLY)) < 0) {
538 			warn("%s", qfpathname);
539 			return (0);
540 		}
541 		(void)lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET);
542 		switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
543 		case 0:				/* EOF */
544 			/*
545 			 * Convert implicit 0 quota (EOF)
546 			 * into an explicit one (zero'ed dqblk)
547 			 */
548 			memset((caddr_t)&qup->dqblk, 0, sizeof(struct dqblk));
549 			break;
550 		case sizeof(struct dqblk):	/* OK */
551 			break;
552 		default:		/* ERROR */
553 			warn("%s", qfpathname);
554 			close(fd);
555 			return (0);
556 		}
557 		close(fd);
558 	}
559 	return (1);
560 }
561 
562 int
563 getnfsquota(struct statfs *fst, struct fstab *fs, struct quotause *qup,
564     long id, int quotatype)
565 {
566 	struct getquota_args gq_args;
567 	struct getquota_rslt gq_rslt;
568 	struct dqblk *dqp = &qup->dqblk;
569 	struct timeval tv;
570 	char *cp;
571 
572 	if (fst->f_flags & MNT_LOCAL)
573 		return (0);
574 
575 	/*
576 	 * rpc.rquotad does not support group quotas
577 	 */
578 	if (quotatype != USRQUOTA)
579 		return (0);
580 
581 	/*
582 	 * must be some form of "hostname:/path"
583 	 */
584 	cp = strchr(fst->f_mntfromname, ':');
585 	if (cp == NULL) {
586 		warnx("cannot find hostname for %s", fst->f_mntfromname);
587 		return (0);
588 	}
589 
590 	*cp = '\0';
591 	if (cp[1] != '/') {
592 		*cp = ':';
593 		return (0);
594 	}
595 
596 	gq_args.gqa_pathp = &cp[1];
597 	gq_args.gqa_uid = id;
598 	if (callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
599 	    RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args,
600 	    xdr_getquota_rslt, &gq_rslt) != 0) {
601 		*cp = ':';
602 		return (0);
603 	}
604 
605 	switch (gq_rslt.status) {
606 	case Q_NOQUOTA:
607 		break;
608 	case Q_EPERM:
609 		warnx("permission error, host: %s", fst->f_mntfromname);
610 		break;
611 	case Q_OK:
612 		gettimeofday(&tv, NULL);
613 			/* blocks*/
614 		dqp->dqb_bhardlimit =
615 		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
616 		    (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
617 		dqp->dqb_bsoftlimit =
618 		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
619 		    (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
620 		dqp->dqb_curblocks =
621 		    gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
622 		    (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE);
623 			/* inodes */
624 		dqp->dqb_ihardlimit =
625 			gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
626 		dqp->dqb_isoftlimit =
627 			gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
628 		dqp->dqb_curinodes =
629 			gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
630 			/* grace times */
631 		dqp->dqb_btime =
632 		    tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
633 		dqp->dqb_itime =
634 		    tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
635 		*cp = ':';
636 		return (1);
637 	default:
638 		warnx("bad rpc result, host: %s", fst->f_mntfromname);
639 		break;
640 	}
641 	*cp = ':';
642 	return (0);
643 }
644 
645 int
646 callaurpc(char *host, int prognum, int versnum, int procnum,
647     xdrproc_t inproc, void *in, xdrproc_t outproc, void *out)
648 {
649 	struct sockaddr_in server_addr;
650 	enum clnt_stat clnt_stat;
651 	struct hostent *hp;
652 	struct timeval timeout, tottimeout;
653 
654 	CLIENT *client = NULL;
655 	int socket = RPC_ANYSOCK;
656 
657 	if ((hp = gethostbyname(host)) == NULL)
658 		return ((int) RPC_UNKNOWNHOST);
659 	timeout.tv_usec = 0;
660 	timeout.tv_sec = 6;
661 
662 	memset(&server_addr, 0, sizeof server_addr);
663 	memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
664 	server_addr.sin_family = AF_INET;
665 	server_addr.sin_port =  0;
666 
667 	if ((client = clntudp_create(&server_addr, prognum,
668 	    versnum, timeout, &socket)) == NULL)
669 		return ((int) rpc_createerr.cf_stat);
670 
671 	client->cl_auth = authunix_create_default();
672 	tottimeout.tv_sec = 25;
673 	tottimeout.tv_usec = 0;
674 	clnt_stat = clnt_call(client, procnum, inproc, in,
675 	    outproc, out, tottimeout);
676 
677 	return ((int) clnt_stat);
678 }
679 
680 int
681 alldigits(char *s)
682 {
683 	int c;
684 
685 	c = (unsigned char)*s++;
686 	do {
687 		if (!isdigit(c))
688 			return (0);
689 	} while ((c = (unsigned char)*s++));
690 	return (1);
691 }
692