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