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