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