Home | History | Annotate | Line # | Download | only in yppush
      1  1.25   andvar /*	$NetBSD: yppush.c,v 1.25 2021/07/24 21:31:39 andvar Exp $	*/
      2   1.1  thorpej 
      3   1.1  thorpej /*
      4   1.8    lukem  * Copyright (c) 1997 Charles D. Cranor
      5   1.1  thorpej  * All rights reserved.
      6   1.1  thorpej  *
      7   1.1  thorpej  * Redistribution and use in source and binary forms, with or without
      8   1.1  thorpej  * modification, are permitted provided that the following conditions
      9   1.1  thorpej  * are met:
     10   1.1  thorpej  * 1. Redistributions of source code must retain the above copyright
     11   1.1  thorpej  *    notice, this list of conditions and the following disclaimer.
     12   1.1  thorpej  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1  thorpej  *    notice, this list of conditions and the following disclaimer in the
     14   1.1  thorpej  *    documentation and/or other materials provided with the distribution.
     15   1.1  thorpej  *
     16   1.8    lukem  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17   1.8    lukem  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18   1.8    lukem  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19   1.8    lukem  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20   1.8    lukem  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21   1.8    lukem  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22   1.8    lukem  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23   1.8    lukem  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24   1.8    lukem  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25   1.8    lukem  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26   1.8    lukem  */
     27   1.1  thorpej 
     28   1.8    lukem /*
     29   1.8    lukem  * yppush
     30  1.23    chuck  * author: Chuck Cranor <chuck@netbsd>
     31   1.8    lukem  * date: 05-Nov-97
     32   1.8    lukem  *
     33   1.8    lukem  * notes: this is a full rewrite of Mats O Jansson <moj (at) stacken.kth.se>'s
     34   1.8    lukem  * yppush.c.   i have restructured and cleaned up the entire file.
     35   1.8    lukem  */
     36   1.1  thorpej #include <sys/types.h>
     37   1.8    lukem #include <sys/param.h>
     38   1.1  thorpej #include <sys/stat.h>
     39   1.8    lukem #include <sys/time.h>
     40   1.4  thorpej #include <sys/wait.h>
     41   1.1  thorpej 
     42   1.4  thorpej #include <ctype.h>
     43   1.1  thorpej #include <err.h>
     44  1.10   kleink #include <errno.h>
     45   1.8    lukem #include <signal.h>
     46   1.1  thorpej #include <stdio.h>
     47  1.13     matt #include <stdlib.h>
     48   1.9  thorpej #include <string.h>
     49  1.16    lukem #include <syslog.h>
     50   1.1  thorpej #include <unistd.h>
     51   1.1  thorpej 
     52   1.1  thorpej #include <rpc/rpc.h>
     53   1.1  thorpej #include <rpcsvc/yp_prot.h>
     54   1.1  thorpej #include <rpcsvc/ypclnt.h>
     55   1.1  thorpej 
     56   1.8    lukem #include "ypdb.h"
     57   1.8    lukem #include "ypdef.h"
     58   1.1  thorpej #include "yplib_host.h"
     59   1.1  thorpej #include "yppush.h"
     60   1.1  thorpej 
     61   1.8    lukem /*
     62   1.8    lukem  * yppush: push a new YP map out YP servers
     63   1.8    lukem  *
     64   1.8    lukem  * usage:
     65   1.8    lukem  *   yppush [-d domain] [-h host] [-v] mapname
     66   1.8    lukem  *
     67   1.8    lukem  *   -d: the domainname the map lives in [if different from default]
     68   1.8    lukem  *   -h: push only to this host [otherwise, push to all hosts]
     69   1.8    lukem  *   -v: verbose
     70   1.8    lukem  */
     71   1.8    lukem 
     72   1.8    lukem /*
     73   1.8    lukem  * structures
     74   1.8    lukem  */
     75   1.8    lukem 
     76   1.8    lukem struct yppush_info {
     77   1.8    lukem 	char   *ourdomain;	/* domain of interest */
     78   1.8    lukem 	char   *map;		/* map we are pushing */
     79   1.8    lukem 	char   *owner;		/* owner of map */
     80   1.8    lukem 	int     order;		/* order number of map (version) */
     81   1.8    lukem };
     82   1.8    lukem /*
     83   1.8    lukem  * global vars
     84   1.8    lukem  */
     85   1.8    lukem 
     86   1.8    lukem int     verbo = 0;		/* verbose */
     87   1.8    lukem 
     88   1.8    lukem /*
     89   1.8    lukem  * prototypes
     90   1.8    lukem  */
     91   1.1  thorpej 
     92  1.24    joerg static int	pushit(int, char *, int, char *, int, char *);
     93  1.18      wiz void	push(char *, int, struct yppush_info *);
     94  1.18      wiz void	_svc_run(void);
     95  1.24    joerg __dead static void	usage(void);
     96   1.1  thorpej 
     97   1.1  thorpej 
     98   1.8    lukem /*
     99   1.8    lukem  * main
    100   1.8    lukem  */
    101   1.1  thorpej 
    102   1.1  thorpej int
    103  1.18      wiz main(int argc, char *argv[])
    104   1.8    lukem 
    105   1.1  thorpej {
    106   1.8    lukem 	char   *targhost = NULL;
    107   1.8    lukem 	struct yppush_info ypi = {NULL, NULL, NULL, 0};
    108   1.8    lukem 	int     c, rv;
    109   1.8    lukem 	const char *cp;
    110   1.8    lukem 	char   *master;
    111   1.8    lukem 	DBM    *ypdb;
    112  1.22    lukem 	datum   dat;
    113   1.8    lukem 	CLIENT *ypserv;
    114   1.8    lukem 	struct timeval tv;
    115   1.8    lukem 	enum clnt_stat retval;
    116   1.8    lukem 	struct ypall_callback ypallcb;
    117   1.1  thorpej 
    118   1.8    lukem 	/*
    119   1.8    lukem          * parse command line
    120   1.8    lukem          */
    121   1.1  thorpej 	while ((c = getopt(argc, argv, "d:h:v")) != -1) {
    122   1.8    lukem 		switch (c) {
    123   1.1  thorpej 		case 'd':
    124   1.8    lukem 			ypi.ourdomain = optarg;
    125   1.1  thorpej 			break;
    126   1.1  thorpej 		case 'h':
    127   1.8    lukem 			targhost = optarg;
    128   1.1  thorpej 			break;
    129   1.8    lukem 		case 'v':
    130   1.8    lukem 			verbo = 1;
    131   1.1  thorpej 			break;
    132   1.1  thorpej 		default:
    133   1.8    lukem 			usage();
    134   1.8    lukem 			/* NOTREACHED */
    135   1.1  thorpej 		}
    136   1.1  thorpej 	}
    137   1.8    lukem 	argc -= optind;
    138   1.8    lukem 	argv += optind;
    139   1.1  thorpej 	if (argc != 1)
    140   1.1  thorpej 		usage();
    141  1.15    lukem 	openlog("yppush", LOG_PID, LOG_DAEMON);
    142   1.8    lukem 	ypi.map = argv[0];
    143   1.8    lukem 	if (strlen(ypi.map) > YPMAXMAP)
    144   1.8    lukem 		errx(1, "%s: map name too long (limit %d)", ypi.map, YPMAXMAP);
    145   1.8    lukem 
    146   1.8    lukem 	/*
    147   1.8    lukem          * ensure we have a domain
    148   1.8    lukem          */
    149   1.8    lukem 	if (ypi.ourdomain == NULL) {
    150   1.8    lukem 		c = yp_get_default_domain(&ypi.ourdomain);
    151   1.8    lukem 		if (ypi.ourdomain == NULL)
    152   1.8    lukem 			errx(1, "unable to get default domain: %s",
    153   1.3  thorpej 			    yperr_string(c));
    154   1.8    lukem 	}
    155   1.8    lukem 	/*
    156  1.25   andvar          * verify that the domain and specified database exists
    157   1.8    lukem          *
    158   1.8    lukem          * XXXCDC: this effectively prevents us from pushing from any
    159   1.8    lukem          * host but the master.   an alternate plan is to find the master
    160   1.8    lukem          * host for a map, clear it, ask for the order number, and then
    161   1.8    lukem          * send xfr requests.   if that was used we would not need local
    162   1.8    lukem          * file access.
    163   1.8    lukem          */
    164   1.8    lukem 	if (chdir(YP_DB_PATH) < 0)
    165   1.8    lukem 		err(1, "%s", YP_DB_PATH);
    166   1.8    lukem 	if (chdir(ypi.ourdomain) < 0)
    167   1.8    lukem 		err(1, "%s/%s", YP_DB_PATH, ypi.ourdomain);
    168   1.8    lukem 
    169   1.8    lukem 	/*
    170   1.8    lukem          * now open the database so we can extract "order number"
    171   1.8    lukem          * (i.e. timestamp) of the map.
    172   1.8    lukem          */
    173  1.21    lukem 	ypdb = ypdb_open(ypi.map);
    174   1.8    lukem 	if (ypdb == NULL)
    175   1.8    lukem 		err(1, "ypdb_open %s/%s/%s", YP_DB_PATH, ypi.ourdomain,
    176   1.8    lukem 		    ypi.map);
    177  1.22    lukem 	dat.dptr = YP_LAST_KEY;
    178  1.22    lukem 	dat.dsize = YP_LAST_LEN;
    179  1.22    lukem 	dat = ypdb_fetch(ypdb, dat);
    180  1.22    lukem 	if (dat.dptr == NULL)
    181   1.8    lukem 		errx(1,
    182   1.8    lukem 		    "unable to fetch %s key: check database with 'makedbm -u'",
    183   1.8    lukem 		    YP_LAST_KEY);
    184   1.8    lukem 	ypi.order = 0;
    185  1.22    lukem 	cp = dat.dptr;
    186  1.22    lukem 	while (cp < dat.dptr + dat.dsize) {
    187  1.19      dsl 		if (!isdigit((unsigned char)*cp))
    188   1.8    lukem 			errx(1,
    189   1.8    lukem 		    "invalid order number: check database with 'makedbm -u'");
    190   1.8    lukem 		ypi.order = (ypi.order * 10) + *cp - '0';
    191   1.8    lukem 		cp++;
    192   1.8    lukem 	}
    193  1.11    lukem 	ypdb_close(ypdb);
    194   1.1  thorpej 
    195   1.8    lukem 	if (verbo)
    196   1.8    lukem 		printf("pushing %s [order=%d] in domain %s\n", ypi.map,
    197   1.8    lukem 		    ypi.order, ypi.ourdomain);
    198   1.8    lukem 
    199   1.8    lukem 	/*
    200   1.8    lukem          * ok, we are ready to do it.   first we send a clear_2 request
    201   1.8    lukem          * to the local server [should be the master] to make sure it has
    202   1.8    lukem          * the correct database open.
    203   1.8    lukem          *
    204   1.8    lukem          * XXXCDC: note that yp_bind_local exits on failure so ypserv can't
    205   1.8    lukem          * be null.   this makes it difficult to print a useful error message.
    206   1.8    lukem          * [it will print "clntudp_create: no contact with localhost"]
    207   1.8    lukem          */
    208   1.8    lukem 	tv.tv_sec = 10;
    209   1.8    lukem 	tv.tv_usec = 0;
    210   1.8    lukem 	ypserv = yp_bind_local(YPPROG, YPVERS);
    211   1.8    lukem 	retval = clnt_call(ypserv, YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, tv);
    212   1.8    lukem 	if (retval != RPC_SUCCESS)
    213   1.8    lukem 		errx(1, "clnt_call CLEAR to local ypserv: %s",
    214   1.8    lukem 		    clnt_sperrno(retval));
    215   1.8    lukem 	clnt_destroy(ypserv);
    216   1.8    lukem 
    217   1.8    lukem 	/*
    218   1.8    lukem          * now use normal yplib functions to bind to the domain.
    219   1.8    lukem          */
    220   1.8    lukem 	rv = yp_bind(ypi.ourdomain);
    221   1.8    lukem 	if (rv)
    222   1.8    lukem 		errx(1, "error binding to %s: %s", ypi.ourdomain,
    223   1.8    lukem 		    yperr_string(rv));
    224   1.8    lukem 
    225   1.8    lukem 	/*
    226   1.8    lukem          * find 'owner' of the map (see pushit for usage)
    227   1.8    lukem          */
    228   1.8    lukem 	rv = yp_master(ypi.ourdomain, ypi.map, &ypi.owner);
    229   1.8    lukem 	if (rv)
    230   1.8    lukem 		errx(1, "error finding master for %s in %s: %s", ypi.map,
    231   1.8    lukem 		    ypi.ourdomain, yperr_string(rv));
    232   1.8    lukem 
    233   1.8    lukem 	/*
    234   1.8    lukem          * inform user of our progress
    235   1.8    lukem          */
    236   1.8    lukem 	if (verbo) {
    237   1.8    lukem 		printf("pushing map %s in %s: order=%d, owner=%s\n", ypi.map,
    238   1.8    lukem 		    ypi.ourdomain, ypi.order, ypi.owner);
    239   1.8    lukem 		printf("pushing to %s\n",
    240   1.8    lukem 		    (targhost) ? targhost : "<all ypservs>");
    241   1.8    lukem 	}
    242   1.1  thorpej 
    243   1.8    lukem 	/*
    244   1.8    lukem          * finally, do it.
    245   1.8    lukem          */
    246   1.8    lukem 	if (targhost) {
    247   1.8    lukem 		push(targhost, strlen(targhost), &ypi);
    248   1.8    lukem 	} else {
    249   1.8    lukem 
    250   1.8    lukem 		/*
    251   1.8    lukem 	         * no host specified, do all hosts the master knows about via
    252   1.8    lukem 	         * the ypservers map.
    253   1.8    lukem 	         */
    254   1.8    lukem 		rv = yp_master(ypi.ourdomain, "ypservers", &master);
    255   1.8    lukem 		if (rv)
    256   1.8    lukem 			errx(1, "error finding master for ypservers in %s: %s",
    257   1.8    lukem 			    ypi.ourdomain, yperr_string(rv));
    258   1.8    lukem 
    259   1.8    lukem 		if (verbo)
    260   1.8    lukem 			printf(
    261   1.8    lukem 		"contacting ypservers %s master on %s for list of ypservs...\n",
    262   1.8    lukem 			    ypi.ourdomain, master);
    263   1.8    lukem 
    264   1.8    lukem 		ypserv = yp_bind_host(master, YPPROG, YPVERS, 0, 1);
    265   1.8    lukem 
    266   1.8    lukem 		ypallcb.foreach = pushit;	/* callback function */
    267   1.8    lukem 		ypallcb.data = (char *) &ypi;	/* data to pass into callback */
    268   1.8    lukem 
    269   1.8    lukem 		rv = yp_all_host(ypserv, ypi.ourdomain, "ypservers", &ypallcb);
    270   1.8    lukem 		if (rv)
    271   1.8    lukem 			errx(1, "pushing %s in %s failed: %s", ypi.map,
    272   1.8    lukem 			    ypi.ourdomain, yperr_string(rv));
    273   1.1  thorpej 	}
    274   1.8    lukem 	exit(0);
    275   1.1  thorpej }
    276   1.1  thorpej 
    277   1.8    lukem /*
    278   1.8    lukem  * usage: print usage and exit
    279   1.8    lukem  */
    280  1.24    joerg static void
    281  1.18      wiz usage(void)
    282   1.1  thorpej {
    283   1.8    lukem 	fprintf(stderr, "usage: %s [-d domain] [-h host] [-v] map\n",
    284  1.17      cgd 	    getprogname());
    285   1.1  thorpej 	exit(1);
    286   1.1  thorpej }
    287   1.1  thorpej 
    288   1.8    lukem /*
    289   1.8    lukem  * pushit: called from yp_all_host to push a specific host.
    290   1.8    lukem  * the key/value pairs are from the ypservers map.
    291   1.8    lukem  */
    292  1.24    joerg static int
    293  1.18      wiz pushit(int instatus, char *inkey, int inkeylen, char *inval,
    294  1.18      wiz        int invallen, char *indata)
    295   1.1  thorpej {
    296   1.8    lukem 	struct yppush_info *ypi = (struct yppush_info *) indata;
    297   1.1  thorpej 
    298   1.8    lukem 	push(inkey, inkeylen, ypi);		/* do it! */
    299   1.8    lukem 	return (0);
    300   1.1  thorpej }
    301   1.1  thorpej 
    302   1.8    lukem /*
    303   1.8    lukem  * push: push a specific map on a specific host
    304   1.8    lukem  */
    305   1.1  thorpej void
    306  1.18      wiz push(char *host, int hostlen, struct yppush_info *ypi)
    307   1.8    lukem {
    308   1.8    lukem 	char    target[YPMAXPEER];
    309   1.8    lukem 	CLIENT *ypserv;
    310   1.1  thorpej 	SVCXPRT *transp;
    311   1.8    lukem 	int     prog, pid, rv;
    312   1.1  thorpej 	struct timeval tv;
    313   1.8    lukem 	struct ypreq_xfr req;
    314   1.1  thorpej 
    315   1.8    lukem 	/*
    316   1.8    lukem          * get our target host in a null terminated string
    317   1.8    lukem          */
    318   1.8    lukem 	snprintf(target, sizeof(target), "%*.*s", hostlen, hostlen, host);
    319   1.8    lukem 
    320   1.8    lukem 	/*
    321   1.8    lukem          * XXXCDC: arg!  we would like to use yp_bind_host here, except that
    322   1.8    lukem          * it exits on failure and we don't want to give up just because
    323   1.8    lukem          * one host fails.  thus, we have to do it the hard way.
    324   1.8    lukem          */
    325   1.8    lukem 	ypserv = clnt_create(target, YPPROG, YPVERS, "tcp");
    326   1.8    lukem 	if (ypserv == NULL) {
    327   1.8    lukem 		clnt_pcreateerror(target);
    328   1.1  thorpej 		return;
    329   1.1  thorpej 	}
    330   1.1  thorpej 
    331   1.8    lukem 	/*
    332   1.8    lukem          * our XFR rpc request to the client just starts the transfer.
    333   1.8    lukem          * when the client is done, it wants to call a procedure that
    334   1.8    lukem          * we are serving to tell us that it is done.   so we must create
    335   1.8    lukem          * and register a procedure for us for it to call.
    336   1.8    lukem          */
    337   1.8    lukem 	transp = svcudp_create(RPC_ANYSOCK);
    338   1.1  thorpej 	if (transp == NULL) {
    339   1.8    lukem 		warnx("callback svcudp_create failed");
    340   1.8    lukem 		goto error;
    341   1.1  thorpej 	}
    342   1.1  thorpej 
    343   1.8    lukem 	/* register it with portmap */
    344   1.1  thorpej 	for (prog = 0x40000000; prog < 0x5fffffff; prog++) {
    345   1.8    lukem 		if (svc_register(transp, prog, 1, yppush_xfrrespprog_1,
    346   1.8    lukem 		    IPPROTO_UDP))
    347   1.1  thorpej 			break;
    348   1.1  thorpej 	}
    349   1.8    lukem 	if (prog >= 0x5fffffff) {
    350   1.8    lukem 		warnx("unable to register callback");
    351   1.8    lukem 		goto error;
    352   1.8    lukem 	}
    353   1.1  thorpej 
    354   1.8    lukem 	/*
    355   1.8    lukem          * now fork off a server to catch our reply
    356   1.8    lukem          */
    357   1.8    lukem 	pid = fork();
    358   1.8    lukem 	if (pid == -1) {
    359   1.8    lukem 		svc_unregister(prog, 1);	/* drop our mapping with
    360   1.8    lukem 						 * portmap */
    361   1.8    lukem 		warn("fork failed");
    362   1.8    lukem 		goto error;
    363   1.1  thorpej 	}
    364   1.1  thorpej 
    365   1.8    lukem 	/*
    366   1.8    lukem          * child process becomes the server
    367   1.8    lukem          */
    368   1.8    lukem 	if (pid == 0) {
    369   1.1  thorpej 		_svc_run();
    370   1.1  thorpej 		exit(0);
    371   1.8    lukem 	}
    372   1.8    lukem 
    373   1.8    lukem 	/*
    374   1.8    lukem          * we are the parent process: send XFR request to server.
    375   1.8    lukem          * the "owner" field isn't used by ypserv (and shouldn't be, since
    376   1.8    lukem          * the ypserv has no idea if we are a legitimate yppush or not).
    377   1.8    lukem          * instead, the owner of the map is determined by the master value
    378   1.8    lukem          * currently cached on the slave server.
    379   1.8    lukem          */
    380  1.12     fvdl 	close(transp->xp_fd);	/* close child's socket, we don't need it */
    381   1.8    lukem 	/* don't wait for anything here, we will wait for child's exit */
    382   1.8    lukem 	tv.tv_sec = 0;
    383   1.8    lukem 	tv.tv_usec = 0;
    384   1.8    lukem 	req.map_parms.domain = ypi->ourdomain;
    385   1.8    lukem 	req.map_parms.map = ypi->map;
    386   1.8    lukem 	req.map_parms.owner = ypi->owner;	/* NOT USED */
    387   1.8    lukem 	req.map_parms.ordernum = ypi->order;
    388   1.8    lukem 	req.transid = (u_int) pid;
    389   1.8    lukem 	req.proto = prog;
    390   1.8    lukem 	req.port = transp->xp_port;
    391   1.8    lukem 
    392   1.8    lukem 	if (verbo)
    393   1.8    lukem 		printf("asking host %s to transfer map (xid=%d)\n", target,
    394   1.8    lukem 		    req.transid);
    395   1.8    lukem 
    396   1.8    lukem 	rv = clnt_call(ypserv, YPPROC_XFR, xdr_ypreq_xfr, &req,
    397   1.8    lukem 	    		xdr_void, NULL, tv);			/* do it! */
    398   1.1  thorpej 
    399   1.8    lukem 	if (rv != RPC_SUCCESS && rv != RPC_TIMEDOUT) {
    400   1.8    lukem 		warnx("unable to xfr to host %s: %s", target, clnt_sperrno(rv));
    401   1.8    lukem 		kill(pid, SIGTERM);
    402   1.1  thorpej 	}
    403   1.8    lukem 
    404   1.8    lukem 	/*
    405   1.8    lukem          * now wait for child to get the reply and exit
    406   1.8    lukem          */
    407   1.8    lukem 	wait4(pid, NULL, 0, NULL);
    408   1.8    lukem 	svc_unregister(prog, 1);
    409   1.8    lukem 
    410   1.8    lukem 	/*
    411   1.8    lukem          * ... and we are done.   fall through
    412   1.8    lukem          */
    413   1.8    lukem 
    414   1.8    lukem error:
    415   1.8    lukem 	if (transp)
    416   1.8    lukem 		svc_destroy(transp);
    417   1.8    lukem 	clnt_destroy(ypserv);
    418   1.8    lukem 	return;
    419   1.1  thorpej }
    420   1.1  thorpej 
    421   1.8    lukem /*
    422   1.8    lukem  * _svc_run: this is the main loop for the RPC server that we fork off
    423   1.8    lukem  * to await the reply from ypxfr.
    424   1.8    lukem  */
    425   1.8    lukem void
    426  1.18      wiz _svc_run(void)
    427   1.1  thorpej {
    428   1.8    lukem 	fd_set  readfds;
    429   1.8    lukem 	struct timeval tv;
    430   1.8    lukem 	int     rv, nfds;
    431   1.1  thorpej 
    432   1.8    lukem 	nfds = sysconf(_SC_OPEN_MAX);
    433   1.8    lukem 	while (1) {
    434   1.1  thorpej 
    435   1.8    lukem 		readfds = svc_fdset;	/* structure copy from global var */
    436   1.8    lukem 		tv.tv_sec = 60;
    437   1.8    lukem 		tv.tv_usec = 0;
    438   1.8    lukem 
    439   1.8    lukem 		rv = select(nfds, &readfds, NULL, NULL, &tv);
    440   1.8    lukem 
    441   1.8    lukem 		if (rv < 0) {
    442   1.8    lukem 			if (errno == EINTR)
    443   1.8    lukem 				continue;
    444   1.8    lukem 			warn("_svc_run: select failed");
    445   1.8    lukem 			return;
    446   1.8    lukem 		}
    447   1.8    lukem 		if (rv == 0)
    448   1.8    lukem 			errx(0, "_svc_run: callback timed out");
    449   1.1  thorpej 
    450   1.8    lukem 		/*
    451   1.8    lukem 	         * got something
    452   1.8    lukem 	         */
    453   1.8    lukem 		svc_getreqset(&readfds);
    454   1.1  thorpej 
    455   1.8    lukem 	}
    456   1.1  thorpej }
    457