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