GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/quota/quota.c Lines: 0 315 0.0 %
Date: 2017-11-07 Branches: 0 216 0.0 %

Line Branch Exec Source
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
}