1 /* $NetBSD: yppush.c,v 1.25 2021/07/24 21:31:39 andvar 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 static int pushit(int, char *, int, char *, int, char *); 93 void push(char *, int, struct yppush_info *); 94 void _svc_run(void); 95 __dead static void usage(void); 96 97 98 /* 99 * main 100 */ 101 102 int 103 main(int argc, char *argv[]) 104 105 { 106 char *targhost = NULL; 107 struct yppush_info ypi = {NULL, NULL, NULL, 0}; 108 int c, rv; 109 const char *cp; 110 char *master; 111 DBM *ypdb; 112 datum dat; 113 CLIENT *ypserv; 114 struct timeval tv; 115 enum clnt_stat retval; 116 struct ypall_callback ypallcb; 117 118 /* 119 * parse command line 120 */ 121 while ((c = getopt(argc, argv, "d:h:v")) != -1) { 122 switch (c) { 123 case 'd': 124 ypi.ourdomain = optarg; 125 break; 126 case 'h': 127 targhost = optarg; 128 break; 129 case 'v': 130 verbo = 1; 131 break; 132 default: 133 usage(); 134 /* NOTREACHED */ 135 } 136 } 137 argc -= optind; 138 argv += optind; 139 if (argc != 1) 140 usage(); 141 openlog("yppush", LOG_PID, LOG_DAEMON); 142 ypi.map = argv[0]; 143 if (strlen(ypi.map) > YPMAXMAP) 144 errx(1, "%s: map name too long (limit %d)", ypi.map, YPMAXMAP); 145 146 /* 147 * ensure we have a domain 148 */ 149 if (ypi.ourdomain == NULL) { 150 c = yp_get_default_domain(&ypi.ourdomain); 151 if (ypi.ourdomain == NULL) 152 errx(1, "unable to get default domain: %s", 153 yperr_string(c)); 154 } 155 /* 156 * verify that the domain and specified database exists 157 * 158 * XXXCDC: this effectively prevents us from pushing from any 159 * host but the master. an alternate plan is to find the master 160 * host for a map, clear it, ask for the order number, and then 161 * send xfr requests. if that was used we would not need local 162 * file access. 163 */ 164 if (chdir(YP_DB_PATH) < 0) 165 err(1, "%s", YP_DB_PATH); 166 if (chdir(ypi.ourdomain) < 0) 167 err(1, "%s/%s", YP_DB_PATH, ypi.ourdomain); 168 169 /* 170 * now open the database so we can extract "order number" 171 * (i.e. timestamp) of the map. 172 */ 173 ypdb = ypdb_open(ypi.map); 174 if (ypdb == NULL) 175 err(1, "ypdb_open %s/%s/%s", YP_DB_PATH, ypi.ourdomain, 176 ypi.map); 177 dat.dptr = YP_LAST_KEY; 178 dat.dsize = YP_LAST_LEN; 179 dat = ypdb_fetch(ypdb, dat); 180 if (dat.dptr == NULL) 181 errx(1, 182 "unable to fetch %s key: check database with 'makedbm -u'", 183 YP_LAST_KEY); 184 ypi.order = 0; 185 cp = dat.dptr; 186 while (cp < dat.dptr + dat.dsize) { 187 if (!isdigit((unsigned char)*cp)) 188 errx(1, 189 "invalid order number: check database with 'makedbm -u'"); 190 ypi.order = (ypi.order * 10) + *cp - '0'; 191 cp++; 192 } 193 ypdb_close(ypdb); 194 195 if (verbo) 196 printf("pushing %s [order=%d] in domain %s\n", ypi.map, 197 ypi.order, ypi.ourdomain); 198 199 /* 200 * ok, we are ready to do it. first we send a clear_2 request 201 * to the local server [should be the master] to make sure it has 202 * the correct database open. 203 * 204 * XXXCDC: note that yp_bind_local exits on failure so ypserv can't 205 * be null. this makes it difficult to print a useful error message. 206 * [it will print "clntudp_create: no contact with localhost"] 207 */ 208 tv.tv_sec = 10; 209 tv.tv_usec = 0; 210 ypserv = yp_bind_local(YPPROG, YPVERS); 211 retval = clnt_call(ypserv, YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, tv); 212 if (retval != RPC_SUCCESS) 213 errx(1, "clnt_call CLEAR to local ypserv: %s", 214 clnt_sperrno(retval)); 215 clnt_destroy(ypserv); 216 217 /* 218 * now use normal yplib functions to bind to the domain. 219 */ 220 rv = yp_bind(ypi.ourdomain); 221 if (rv) 222 errx(1, "error binding to %s: %s", ypi.ourdomain, 223 yperr_string(rv)); 224 225 /* 226 * find 'owner' of the map (see pushit for usage) 227 */ 228 rv = yp_master(ypi.ourdomain, ypi.map, &ypi.owner); 229 if (rv) 230 errx(1, "error finding master for %s in %s: %s", ypi.map, 231 ypi.ourdomain, yperr_string(rv)); 232 233 /* 234 * inform user of our progress 235 */ 236 if (verbo) { 237 printf("pushing map %s in %s: order=%d, owner=%s\n", ypi.map, 238 ypi.ourdomain, ypi.order, ypi.owner); 239 printf("pushing to %s\n", 240 (targhost) ? targhost : "<all ypservs>"); 241 } 242 243 /* 244 * finally, do it. 245 */ 246 if (targhost) { 247 push(targhost, strlen(targhost), &ypi); 248 } else { 249 250 /* 251 * no host specified, do all hosts the master knows about via 252 * the ypservers map. 253 */ 254 rv = yp_master(ypi.ourdomain, "ypservers", &master); 255 if (rv) 256 errx(1, "error finding master for ypservers in %s: %s", 257 ypi.ourdomain, yperr_string(rv)); 258 259 if (verbo) 260 printf( 261 "contacting ypservers %s master on %s for list of ypservs...\n", 262 ypi.ourdomain, master); 263 264 ypserv = yp_bind_host(master, YPPROG, YPVERS, 0, 1); 265 266 ypallcb.foreach = pushit; /* callback function */ 267 ypallcb.data = (char *) &ypi; /* data to pass into callback */ 268 269 rv = yp_all_host(ypserv, ypi.ourdomain, "ypservers", &ypallcb); 270 if (rv) 271 errx(1, "pushing %s in %s failed: %s", ypi.map, 272 ypi.ourdomain, yperr_string(rv)); 273 } 274 exit(0); 275 } 276 277 /* 278 * usage: print usage and exit 279 */ 280 static void 281 usage(void) 282 { 283 fprintf(stderr, "usage: %s [-d domain] [-h host] [-v] map\n", 284 getprogname()); 285 exit(1); 286 } 287 288 /* 289 * pushit: called from yp_all_host to push a specific host. 290 * the key/value pairs are from the ypservers map. 291 */ 292 static int 293 pushit(int instatus, char *inkey, int inkeylen, char *inval, 294 int invallen, char *indata) 295 { 296 struct yppush_info *ypi = (struct yppush_info *) indata; 297 298 push(inkey, inkeylen, ypi); /* do it! */ 299 return (0); 300 } 301 302 /* 303 * push: push a specific map on a specific host 304 */ 305 void 306 push(char *host, int hostlen, struct yppush_info *ypi) 307 { 308 char target[YPMAXPEER]; 309 CLIENT *ypserv; 310 SVCXPRT *transp; 311 int prog, pid, rv; 312 struct timeval tv; 313 struct ypreq_xfr req; 314 315 /* 316 * get our target host in a null terminated string 317 */ 318 snprintf(target, sizeof(target), "%*.*s", hostlen, hostlen, host); 319 320 /* 321 * XXXCDC: arg! we would like to use yp_bind_host here, except that 322 * it exits on failure and we don't want to give up just because 323 * one host fails. thus, we have to do it the hard way. 324 */ 325 ypserv = clnt_create(target, YPPROG, YPVERS, "tcp"); 326 if (ypserv == NULL) { 327 clnt_pcreateerror(target); 328 return; 329 } 330 331 /* 332 * our XFR rpc request to the client just starts the transfer. 333 * when the client is done, it wants to call a procedure that 334 * we are serving to tell us that it is done. so we must create 335 * and register a procedure for us for it to call. 336 */ 337 transp = svcudp_create(RPC_ANYSOCK); 338 if (transp == NULL) { 339 warnx("callback svcudp_create failed"); 340 goto error; 341 } 342 343 /* register it with portmap */ 344 for (prog = 0x40000000; prog < 0x5fffffff; prog++) { 345 if (svc_register(transp, prog, 1, yppush_xfrrespprog_1, 346 IPPROTO_UDP)) 347 break; 348 } 349 if (prog >= 0x5fffffff) { 350 warnx("unable to register callback"); 351 goto error; 352 } 353 354 /* 355 * now fork off a server to catch our reply 356 */ 357 pid = fork(); 358 if (pid == -1) { 359 svc_unregister(prog, 1); /* drop our mapping with 360 * portmap */ 361 warn("fork failed"); 362 goto error; 363 } 364 365 /* 366 * child process becomes the server 367 */ 368 if (pid == 0) { 369 _svc_run(); 370 exit(0); 371 } 372 373 /* 374 * we are the parent process: send XFR request to server. 375 * the "owner" field isn't used by ypserv (and shouldn't be, since 376 * the ypserv has no idea if we are a legitimate yppush or not). 377 * instead, the owner of the map is determined by the master value 378 * currently cached on the slave server. 379 */ 380 close(transp->xp_fd); /* close child's socket, we don't need it */ 381 /* don't wait for anything here, we will wait for child's exit */ 382 tv.tv_sec = 0; 383 tv.tv_usec = 0; 384 req.map_parms.domain = ypi->ourdomain; 385 req.map_parms.map = ypi->map; 386 req.map_parms.owner = ypi->owner; /* NOT USED */ 387 req.map_parms.ordernum = ypi->order; 388 req.transid = (u_int) pid; 389 req.proto = prog; 390 req.port = transp->xp_port; 391 392 if (verbo) 393 printf("asking host %s to transfer map (xid=%d)\n", target, 394 req.transid); 395 396 rv = clnt_call(ypserv, YPPROC_XFR, xdr_ypreq_xfr, &req, 397 xdr_void, NULL, tv); /* do it! */ 398 399 if (rv != RPC_SUCCESS && rv != RPC_TIMEDOUT) { 400 warnx("unable to xfr to host %s: %s", target, clnt_sperrno(rv)); 401 kill(pid, SIGTERM); 402 } 403 404 /* 405 * now wait for child to get the reply and exit 406 */ 407 wait4(pid, NULL, 0, NULL); 408 svc_unregister(prog, 1); 409 410 /* 411 * ... and we are done. fall through 412 */ 413 414 error: 415 if (transp) 416 svc_destroy(transp); 417 clnt_destroy(ypserv); 418 return; 419 } 420 421 /* 422 * _svc_run: this is the main loop for the RPC server that we fork off 423 * to await the reply from ypxfr. 424 */ 425 void 426 _svc_run(void) 427 { 428 fd_set readfds; 429 struct timeval tv; 430 int rv, nfds; 431 432 nfds = sysconf(_SC_OPEN_MAX); 433 while (1) { 434 435 readfds = svc_fdset; /* structure copy from global var */ 436 tv.tv_sec = 60; 437 tv.tv_usec = 0; 438 439 rv = select(nfds, &readfds, NULL, NULL, &tv); 440 441 if (rv < 0) { 442 if (errno == EINTR) 443 continue; 444 warn("_svc_run: select failed"); 445 return; 446 } 447 if (rv == 0) 448 errx(0, "_svc_run: callback timed out"); 449 450 /* 451 * got something 452 */ 453 svc_getreqset(&readfds); 454 455 } 456 } 457