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