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