1 /* $NetBSD: yppush.c,v 1.21 2008/02/29 03:00:47 lukem Exp $ */
5 * Copyright (c) 1997 Charles D. Cranor
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * author: Chuck Cranor <chuck@ccrc.wustl.edu>
36 * notes: this is a full rewrite of Mats O Jansson <moj@stacken.kth.se>'s
37 * yppush.c. i have restructured and cleaned up the entire file.
39 #include <sys/types.h>
40 #include <sys/param.h>
56 #include <rpcsvc/yp_prot.h>
57 #include <rpcsvc/ypclnt.h>
61 #include "yplib_host.h"
65 * yppush: push a new YP map out YP servers
68 * yppush [-d domain] [-h host] [-v] mapname
70 * -d: the domainname the map lives in [if different from default]
71 * -h: push only to this host [otherwise, push to all hosts]
80 char *ourdomain
; /* domain of interest */
81 char *map
; /* map we are pushing */
82 char *owner
; /* owner of map */
83 int order
; /* order number of map (version) */
89 int verbo
= 0; /* verbose */
95 int main(int, char *[]);
96 int pushit(int, char *, int, char *, int, char *);
97 void push(char *, int, struct yppush_info
*);
107 main(int argc
, char *argv
[])
110 char *targhost
= NULL
;
111 struct yppush_info ypi
= {NULL
, NULL
, NULL
, 0};
119 enum clnt_stat retval
;
120 struct ypall_callback ypallcb
;
125 while ((c
= getopt(argc
, argv
, "d:h:v")) != -1) {
128 ypi
.ourdomain
= optarg
;
145 openlog("yppush", LOG_PID
, LOG_DAEMON
);
147 if (strlen(ypi
.map
) > YPMAXMAP
)
148 errx(1, "%s: map name too long (limit %d)", ypi
.map
, YPMAXMAP
);
151 * ensure we have a domain
153 if (ypi
.ourdomain
== NULL
) {
154 c
= yp_get_default_domain(&ypi
.ourdomain
);
155 if (ypi
.ourdomain
== NULL
)
156 errx(1, "unable to get default domain: %s",
160 * verify that the domain and specified database exsists
162 * XXXCDC: this effectively prevents us from pushing from any
163 * host but the master. an alternate plan is to find the master
164 * host for a map, clear it, ask for the order number, and then
165 * send xfr requests. if that was used we would not need local
168 if (chdir(YP_DB_PATH
) < 0)
169 err(1, "%s", YP_DB_PATH
);
170 if (chdir(ypi
.ourdomain
) < 0)
171 err(1, "%s/%s", YP_DB_PATH
, ypi
.ourdomain
);
174 * now open the database so we can extract "order number"
175 * (i.e. timestamp) of the map.
177 ypdb
= ypdb_open(ypi
.map
);
179 err(1, "ypdb_open %s/%s/%s", YP_DB_PATH
, ypi
.ourdomain
,
181 dat
.dptr
= YP_LAST_KEY
;
182 dat
.dsize
= YP_LAST_LEN
;
183 dat
= ypdb_fetch(ypdb
, dat
);
184 if (dat
.dptr
== NULL
)
186 "unable to fetch %s key: check database with 'makedbm -u'",
190 while (cp
< dat
.dptr
+ dat
.dsize
) {
191 if (!isdigit((unsigned char)*cp
))
193 "invalid order number: check database with 'makedbm -u'");
194 ypi
.order
= (ypi
.order
* 10) + *cp
- '0';
200 printf("pushing %s [order=%d] in domain %s\n", ypi
.map
,
201 ypi
.order
, ypi
.ourdomain
);
204 * ok, we are ready to do it. first we send a clear_2 request
205 * to the local server [should be the master] to make sure it has
206 * the correct database open.
208 * XXXCDC: note that yp_bind_local exits on failure so ypserv can't
209 * be null. this makes it difficult to print a useful error message.
210 * [it will print "clntudp_create: no contact with localhost"]
214 ypserv
= yp_bind_local(YPPROG
, YPVERS
);
215 retval
= clnt_call(ypserv
, YPPROC_CLEAR
, xdr_void
, 0, xdr_void
, 0, tv
);
216 if (retval
!= RPC_SUCCESS
)
217 errx(1, "clnt_call CLEAR to local ypserv: %s",
218 clnt_sperrno(retval
));
219 clnt_destroy(ypserv
);
222 * now use normal yplib functions to bind to the domain.
224 rv
= yp_bind(ypi
.ourdomain
);
226 errx(1, "error binding to %s: %s", ypi
.ourdomain
,
230 * find 'owner' of the map (see pushit for usage)
232 rv
= yp_master(ypi
.ourdomain
, ypi
.map
, &ypi
.owner
);
234 errx(1, "error finding master for %s in %s: %s", ypi
.map
,
235 ypi
.ourdomain
, yperr_string(rv
));
238 * inform user of our progress
241 printf("pushing map %s in %s: order=%d, owner=%s\n", ypi
.map
,
242 ypi
.ourdomain
, ypi
.order
, ypi
.owner
);
243 printf("pushing to %s\n",
244 (targhost
) ? targhost
: "<all ypservs>");
251 push(targhost
, strlen(targhost
), &ypi
);
255 * no host specified, do all hosts the master knows about via
258 rv
= yp_master(ypi
.ourdomain
, "ypservers", &master
);
260 errx(1, "error finding master for ypservers in %s: %s",
261 ypi
.ourdomain
, yperr_string(rv
));
265 "contacting ypservers %s master on %s for list of ypservs...\n",
266 ypi
.ourdomain
, master
);
268 ypserv
= yp_bind_host(master
, YPPROG
, YPVERS
, 0, 1);
270 ypallcb
.foreach
= pushit
; /* callback function */
271 ypallcb
.data
= (char *) &ypi
; /* data to pass into callback */
273 rv
= yp_all_host(ypserv
, ypi
.ourdomain
, "ypservers", &ypallcb
);
275 errx(1, "pushing %s in %s failed: %s", ypi
.map
,
276 ypi
.ourdomain
, yperr_string(rv
));
282 * usage: print usage and exit
287 fprintf(stderr
, "usage: %s [-d domain] [-h host] [-v] map\n",
293 * pushit: called from yp_all_host to push a specific host.
294 * the key/value pairs are from the ypservers map.
297 pushit(int instatus
, char *inkey
, int inkeylen
, char *inval
,
298 int invallen
, char *indata
)
300 struct yppush_info
*ypi
= (struct yppush_info
*) indata
;
302 push(inkey
, inkeylen
, ypi
); /* do it! */
307 * push: push a specific map on a specific host
310 push(char *host
, int hostlen
, struct yppush_info
*ypi
)
312 char target
[YPMAXPEER
];
317 struct ypreq_xfr req
;
320 * get our target host in a null terminated string
322 snprintf(target
, sizeof(target
), "%*.*s", hostlen
, hostlen
, host
);
325 * XXXCDC: arg! we would like to use yp_bind_host here, except that
326 * it exits on failure and we don't want to give up just because
327 * one host fails. thus, we have to do it the hard way.
329 ypserv
= clnt_create(target
, YPPROG
, YPVERS
, "tcp");
330 if (ypserv
== NULL
) {
331 clnt_pcreateerror(target
);
336 * our XFR rpc request to the client just starts the transfer.
337 * when the client is done, it wants to call a procedure that
338 * we are serving to tell us that it is done. so we must create
339 * and register a procedure for us for it to call.
341 transp
= svcudp_create(RPC_ANYSOCK
);
342 if (transp
== NULL
) {
343 warnx("callback svcudp_create failed");
347 /* register it with portmap */
348 for (prog
= 0x40000000; prog
< 0x5fffffff; prog
++) {
349 if (svc_register(transp
, prog
, 1, yppush_xfrrespprog_1
,
353 if (prog
>= 0x5fffffff) {
354 warnx("unable to register callback");
359 * now fork off a server to catch our reply
363 svc_unregister(prog
, 1); /* drop our mapping with
370 * child process becomes the server
378 * we are the parent process: send XFR request to server.
379 * the "owner" field isn't used by ypserv (and shouldn't be, since
380 * the ypserv has no idea if we are a legitimate yppush or not).
381 * instead, the owner of the map is determined by the master value
382 * currently cached on the slave server.
384 close(transp
->xp_fd
); /* close child's socket, we don't need it */
385 /* don't wait for anything here, we will wait for child's exit */
388 req
.map_parms
.domain
= ypi
->ourdomain
;
389 req
.map_parms
.map
= ypi
->map
;
390 req
.map_parms
.owner
= ypi
->owner
; /* NOT USED */
391 req
.map_parms
.ordernum
= ypi
->order
;
392 req
.transid
= (u_int
) pid
;
394 req
.port
= transp
->xp_port
;
397 printf("asking host %s to transfer map (xid=%d)\n", target
,
400 rv
= clnt_call(ypserv
, YPPROC_XFR
, xdr_ypreq_xfr
, &req
,
401 xdr_void
, NULL
, tv
); /* do it! */
403 if (rv
!= RPC_SUCCESS
&& rv
!= RPC_TIMEDOUT
) {
404 warnx("unable to xfr to host %s: %s", target
, clnt_sperrno(rv
));
409 * now wait for child to get the reply and exit
411 wait4(pid
, NULL
, 0, NULL
);
412 svc_unregister(prog
, 1);
415 * ... and we are done. fall through
421 clnt_destroy(ypserv
);
426 * _svc_run: this is the main loop for the RPC server that we fork off
427 * to await the reply from ypxfr.
436 nfds
= sysconf(_SC_OPEN_MAX
);
439 readfds
= svc_fdset
; /* structure copy from global var */
443 rv
= select(nfds
, &readfds
, NULL
, NULL
, &tv
);
448 warn("_svc_run: select failed");
452 errx(0, "_svc_run: callback timed out");
457 svc_getreqset(&readfds
);