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