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@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
main(int argc,char * argv[])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
usage(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
pushit(int instatus,char * inkey,int inkeylen,char * inval,int invallen,char * indata)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
push(char * host,int hostlen,struct yppush_info * ypi)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
_svc_run(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