#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#include "ypdb.h"
#include "ypdef.h"
#include "yplib_host.h"
#include "yppush.h"
struct yppush_info {
char *ourdomain;
char *map;
char *owner;
int order;
};
int verbo = 0;
static int pushit(int, char *, int, char *, int, char *);
void push(char *, int, struct yppush_info *);
void _svc_run(void);
__dead static void usage(void);
int
main(int argc, char *argv[])
{
char *targhost = NULL;
struct yppush_info ypi = {NULL, NULL, NULL, 0};
int c, rv;
const char *cp;
char *master;
DBM *ypdb;
datum dat;
CLIENT *ypserv;
struct timeval tv;
enum clnt_stat retval;
struct ypall_callback ypallcb;
while ((c = getopt(argc, argv, "d:h:v")) != -1) {
switch (c) {
case 'd':
ypi.ourdomain = optarg;
break;
case 'h':
targhost = optarg;
break;
case 'v':
verbo = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 1)
usage();
openlog("yppush", LOG_PID, LOG_DAEMON);
ypi.map = argv[0];
if (strlen(ypi.map) > YPMAXMAP)
errx(1, "%s: map name too long (limit %d)", ypi.map, YPMAXMAP);
if (ypi.ourdomain == NULL) {
c = yp_get_default_domain(&ypi.ourdomain);
if (ypi.ourdomain == NULL)
errx(1, "unable to get default domain: %s",
yperr_string(c));
}
if (chdir(YP_DB_PATH) < 0)
err(1, "%s", YP_DB_PATH);
if (chdir(ypi.ourdomain) < 0)
err(1, "%s/%s", YP_DB_PATH, ypi.ourdomain);
ypdb = ypdb_open(ypi.map);
if (ypdb == NULL)
err(1, "ypdb_open %s/%s/%s", YP_DB_PATH, ypi.ourdomain,
ypi.map);
dat.dptr = YP_LAST_KEY;
dat.dsize = YP_LAST_LEN;
dat = ypdb_fetch(ypdb, dat);
if (dat.dptr == NULL)
errx(1,
"unable to fetch %s key: check database with 'makedbm -u'",
YP_LAST_KEY);
ypi.order = 0;
cp = dat.dptr;
while (cp < dat.dptr + dat.dsize) {
if (!isdigit((unsigned char)*cp))
errx(1,
"invalid order number: check database with 'makedbm -u'");
ypi.order = (ypi.order * 10) + *cp - '0';
cp++;
}
ypdb_close(ypdb);
if (verbo)
printf("pushing %s [order=%d] in domain %s\n", ypi.map,
ypi.order, ypi.ourdomain);
tv.tv_sec = 10;
tv.tv_usec = 0;
ypserv = yp_bind_local(YPPROG, YPVERS);
retval = clnt_call(ypserv, YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, tv);
if (retval != RPC_SUCCESS)
errx(1, "clnt_call CLEAR to local ypserv: %s",
clnt_sperrno(retval));
clnt_destroy(ypserv);
rv = yp_bind(ypi.ourdomain);
if (rv)
errx(1, "error binding to %s: %s", ypi.ourdomain,
yperr_string(rv));
rv = yp_master(ypi.ourdomain, ypi.map, &ypi.owner);
if (rv)
errx(1, "error finding master for %s in %s: %s", ypi.map,
ypi.ourdomain, yperr_string(rv));
if (verbo) {
printf("pushing map %s in %s: order=%d, owner=%s\n", ypi.map,
ypi.ourdomain, ypi.order, ypi.owner);
printf("pushing to %s\n",
(targhost) ? targhost : "<all ypservs>");
}
if (targhost) {
push(targhost, strlen(targhost), &ypi);
} else {
rv = yp_master(ypi.ourdomain, "ypservers", &master);
if (rv)
errx(1, "error finding master for ypservers in %s: %s",
ypi.ourdomain, yperr_string(rv));
if (verbo)
printf(
"contacting ypservers %s master on %s for list of ypservs...\n",
ypi.ourdomain, master);
ypserv = yp_bind_host(master, YPPROG, YPVERS, 0, 1);
ypallcb.foreach = pushit;
ypallcb.data = (char *) &ypi;
rv = yp_all_host(ypserv, ypi.ourdomain, "ypservers", &ypallcb);
if (rv)
errx(1, "pushing %s in %s failed: %s", ypi.map,
ypi.ourdomain, yperr_string(rv));
}
exit(0);
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-d domain] [-h host] [-v] map\n",
getprogname());
exit(1);
}
static int
pushit(int instatus, char *inkey, int inkeylen, char *inval,
int invallen, char *indata)
{
struct yppush_info *ypi = (struct yppush_info *) indata;
push(inkey, inkeylen, ypi);
return (0);
}
void
push(char *host, int hostlen, struct yppush_info *ypi)
{
char target[YPMAXPEER];
CLIENT *ypserv;
SVCXPRT *transp;
int prog, pid, rv;
struct timeval tv;
struct ypreq_xfr req;
snprintf(target, sizeof(target), "%*.*s", hostlen, hostlen, host);
ypserv = clnt_create(target, YPPROG, YPVERS, "tcp");
if (ypserv == NULL) {
clnt_pcreateerror(target);
return;
}
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL) {
warnx("callback svcudp_create failed");
goto error;
}
for (prog = 0x40000000; prog < 0x5fffffff; prog++) {
if (svc_register(transp, prog, 1, yppush_xfrrespprog_1,
IPPROTO_UDP))
break;
}
if (prog >= 0x5fffffff) {
warnx("unable to register callback");
goto error;
}
pid = fork();
if (pid == -1) {
svc_unregister(prog, 1);
warn("fork failed");
goto error;
}
if (pid == 0) {
_svc_run();
exit(0);
}
close(transp->xp_fd);
tv.tv_sec = 0;
tv.tv_usec = 0;
req.map_parms.domain = ypi->ourdomain;
req.map_parms.map = ypi->map;
req.map_parms.owner = ypi->owner;
req.map_parms.ordernum = ypi->order;
req.transid = (u_int) pid;
req.proto = prog;
req.port = transp->xp_port;
if (verbo)
printf("asking host %s to transfer map (xid=%d)\n", target,
req.transid);
rv = clnt_call(ypserv, YPPROC_XFR, xdr_ypreq_xfr, &req,
xdr_void, NULL, tv);
if (rv != RPC_SUCCESS && rv != RPC_TIMEDOUT) {
warnx("unable to xfr to host %s: %s", target, clnt_sperrno(rv));
kill(pid, SIGTERM);
}
wait4(pid, NULL, 0, NULL);
svc_unregister(prog, 1);
error:
if (transp)
svc_destroy(transp);
clnt_destroy(ypserv);
return;
}
void
_svc_run(void)
{
fd_set readfds;
struct timeval tv;
int rv, nfds;
nfds = svc_maxfd + 1;
while (1) {
readfds = svc_fdset;
tv.tv_sec = 60;
tv.tv_usec = 0;
rv = select(nfds, &readfds, NULL, NULL, &tv);
if (rv < 0) {
if (errno == EINTR)
continue;
warn("_svc_run: select failed");
return;
}
if (rv == 0)
errx(0, "_svc_run: callback timed out");
svc_getreqset(&readfds);
}
}