summaryrefslogtreecommitdiff
path: root/usr/src/cmd/ypcmd/yppush.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/ypcmd/yppush.c')
-rw-r--r--usr/src/cmd/ypcmd/yppush.c1102
1 files changed, 1102 insertions, 0 deletions
diff --git a/usr/src/cmd/ypcmd/yppush.c b/usr/src/cmd/ypcmd/yppush.c
new file mode 100644
index 0000000000..441e546763
--- /dev/null
+++ b/usr/src/cmd/ypcmd/yppush.c
@@ -0,0 +1,1102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ *
+ * Portions of this source code were derived from Berkeley
+ * 4.3 BSD under license from the Regents of the University of
+ * California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define _SVID_GETTOD
+#include <sys/time.h>
+extern int gettimeofday(struct timeval *);
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <rpc/rpc.h>
+#include <rpc/nettype.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/rpcb_clnt.h>
+#include <sys/systeminfo.h>
+#include <sys/select.h>
+#include "ypsym.h"
+#include "ypdefs.h"
+#include "yp_b.h"
+#include "shim.h"
+#include "yptol.h"
+
+
+#ifdef DEBUG
+#undef YPPROG
+#define YPPROG ((ulong_t)109999)
+#undef YPBINDPROG
+#define YPBINDPROG ((ulong_t)109998)
+#endif
+
+#define INTER_TRY 12 /* Seconds between tries */
+#define PORTMAP_TIME 30 /* Seconds before decide its down */
+#define TIMEOUT INTER_TRY*4 /* Total time for timeout */
+#define CUR_PAR 4 /* Total parallal yppushes */
+#define MIN_GRACE 25 /* select timeout and minimum grace */
+#define GRACE_PERIOD 800 /* Total seconds we'll wait for */
+ /* responses from ypxfrs, yes */
+ /* virginia yp map transfers */
+ /* can take a long time, we */
+ /* only worry if the slave */
+ /* crashes ... */
+
+USE_YPDBPATH
+static char *pusage;
+static char *domain = NULL;
+static char *host = NULL;
+static char my_name[YPMAXPEER +1];
+static char default_domain_name[YPMAXDOMAIN];
+static char domain_alias[MAXNAMLEN]; /* nickname for domain - */
+ /* used in sysv filesystems */
+static char map_alias[MAXNAMLEN]; /* nickname for map - */
+ /* used in sysv filesystems */
+static char *map = NULL;
+static bool verbose = FALSE;
+static bool onehost = FALSE;
+static bool oldxfr = FALSE;
+static bool callback_timeout = FALSE; /* set when a callback times out */
+int grace_period = GRACE_PERIOD;
+int curpar = CUR_PAR; /* should be set by other stuff */
+static char ypmapname[1024]; /* Used to check for map's existence */
+
+static struct timeval intertry = {
+ INTER_TRY, /* Seconds */
+ 0 /* Microseconds */
+};
+static struct timeval timeout = {
+ TIMEOUT, /* Seconds */
+ 0 /* Microseconds */
+};
+static SVCXPRT *transport4;
+static SVCXPRT *transport6;
+struct server {
+ struct server *pnext;
+ struct dom_binding domb;
+ char svc_name[YPMAXPEER+1];
+ unsigned long xactid;
+ unsigned short state;
+ unsigned long status;
+ bool oldvers;
+ int start_time;
+};
+#define n_conf dom_binding->ypbind_nconf
+#define svc_addr dom_binding->ypbind_svcaddr
+static struct server *server_list = (struct server *)NULL;
+static struct server *active_list = (struct server *)NULL;
+
+/* State values for server.state field */
+
+#define SSTAT_INIT 0
+#define SSTAT_CALLED 1
+#define SSTAT_RESPONDED 2
+#define SSTAT_PROGNOTREG 3
+#define SSTAT_RPC 4
+#define SSTAT_RSCRC 5
+#define SSTAT_SYSTEM 6
+
+static char err_usage[] =
+"Usage:\n\typpush [-p <par>] [-d <domainname>] [-h <hostname>] [-v] map\n";
+static char err_bad_args[] =
+ "The %s argument is bad.\n";
+static char err_cant_get_kname[] =
+ "Can't get %s from system call.\n";
+static char err_null_kname[] =
+ "The %s hasn't been set on this machine.\n";
+static char err_bad_domainname[] = "domainname";
+static char err_cant_bind[] =
+ "Can't find a yp server for domain %s. Reason: %s.\n";
+static char err_cant_build_serverlist[] =
+ "Can't build server list from map \"ypservers\". Reason: %s.\n";
+static char err_cant_find_host[] =
+ "Can't find host %s in map \"ypservers\".\n";
+/*
+ * State_duple table. All messages should take 1 arg - the node name.
+ */
+struct state_duple {
+ int state;
+ char *state_msg;
+};
+static struct state_duple state_duples[] = {
+ {SSTAT_INIT, "Internal error trying to talk to %s."},
+ {SSTAT_CALLED, "%s has been called."},
+ {SSTAT_RESPONDED, "%s (v1 ypserv) sent an old-style request."},
+ {SSTAT_PROGNOTREG, "nis server not registered at %s."},
+ {SSTAT_RPC, "RPC error to %s: "},
+ {SSTAT_RSCRC, "Local resource allocation failure - can't talk to %s."},
+ {SSTAT_SYSTEM, "System error talking to %s: "},
+ {0, (char *)NULL}
+};
+/*
+ * Status_duple table. No messages should require any args.
+ */
+static struct status_duple {
+ long status;
+ char *status_msg;
+};
+static struct status_duple status_duples[] = {
+ {YPPUSH_SUCC, "Map successfully transferred."},
+ {YPPUSH_AGE,
+ "Transfer not done: master's version isn't newer."},
+ {YPPUSH_NOMAP, "Failed - ypxfr there can't find a server for map."},
+ {YPPUSH_NODOM, "Failed - domain isn't supported."},
+ {YPPUSH_RSRC, "Failed - local resource allocation failure."},
+ {YPPUSH_RPC, "Failed - ypxfr had an RPC failure"},
+ {YPPUSH_MADDR, "Failed - ypxfr couldn't get the map master's address."},
+ {YPPUSH_YPERR, "Failed - nis server or map format error."},
+ {YPPUSH_BADARGS, "Failed - args to ypxfr were bad."},
+ {YPPUSH_DBM, "Failed - dbm operation on map failed."},
+ {YPPUSH_FILE, "Failed - file I/O operation on map failed"},
+ {YPPUSH_SKEW, "Failed - map version skew during transfer."},
+ {YPPUSH_CLEAR,
+ "Map successfully transferred, but ypxfr \
+ couldn't send \"Clear map\" to ypserv "},
+ {YPPUSH_FORCE,
+ "Failed - no local order number in map - use -f flag to ypxfr."},
+ {YPPUSH_XFRERR, "Failed - ypxfr internal error."},
+ {YPPUSH_REFUSED, "Failed - Transfer request refused."},
+ {YPPUSH_NOALIAS,
+ "Failed - System V domain/map alias not in alias file."},
+ {0, (char *)NULL}
+};
+/*
+ * rpcerr_duple table
+ */
+static struct rpcerr_duple {
+ enum clnt_stat rpc_stat;
+ char *rpc_msg;
+};
+static struct rpcerr_duple rpcerr_duples[] = {
+ {RPC_SUCCESS, "RPC success"},
+ {RPC_CANTENCODEARGS, "RPC Can't encode args"},
+ {RPC_CANTDECODERES, "RPC Can't decode results"},
+ {RPC_CANTSEND, "RPC Can't send"},
+ {RPC_CANTRECV, "RPC Can't recv"},
+ {RPC_TIMEDOUT, "NIS server registered, but does not respond"},
+ {RPC_VERSMISMATCH, "RPC version mismatch"},
+ {RPC_AUTHERROR, "RPC auth error"},
+ {RPC_PROGUNAVAIL, "RPC remote program unavailable"},
+ {RPC_PROGVERSMISMATCH, "RPC program mismatch"},
+ {RPC_PROCUNAVAIL, "RPC unknown procedure"},
+ {RPC_CANTDECODEARGS, "RPC Can't decode args"},
+ {RPC_UNKNOWNHOST, "unknown host"},
+ {RPC_RPCBFAILURE, "rpcbind failure (host is down?)"},
+ {RPC_PROGNOTREGISTERED, "RPC prog not registered"},
+ {RPC_SYSTEMERROR, "RPC system error"},
+ {RPC_SUCCESS, (char *)NULL} /* Duplicate rpc_stat */
+ /* unused in list-end */
+ /* entry */
+};
+
+static void get_default_domain_name(void);
+static void get_command_line_args(int argc, char **argv);
+static unsigned short send_message(struct server *ps,
+ unsigned long program, long *err);
+static void make_server_list(void);
+static void one_host_list(void);
+static void add_server(char *sname, int namelen);
+static int generate_callback(unsigned long *program);
+static void xactid_seed(unsigned long *xactid);
+static void main_loop(unsigned long program);
+static void listener_exit(unsigned long program, int stat);
+static void listener_dispatch(struct svc_req *rqstp, SVCXPRT *transp);
+static void print_state_msg(struct server *s, long e);
+static void print_callback_msg(struct server *s);
+static void rpcerr_msg(enum clnt_stat e);
+static void get_xfr_response(SVCXPRT *transp);
+
+#ifdef SYSVCONFIG
+extern void sysvconfig(void);
+#endif
+extern int yp_getalias(char *key, char *key_alias, int maxlen);
+extern int getdomainname(char *, int);
+
+extern struct rpc_createerr rpc_createerr;
+extern char *sys_errlist[];
+extern int sys_nerr;
+extern CLIENT *__yp_clnt_create_rsvdport();
+
+int
+main(int argc, char **argv)
+{
+ unsigned long program;
+ struct stat sbuf;
+
+ get_command_line_args(argc, argv);
+
+ if (!domain) {
+ get_default_domain_name();
+ }
+
+#ifdef SYSVCONFIG
+ sysvconfig();
+#endif
+
+ if (yp_getalias(domain, domain_alias, NAME_MAX) != 0)
+ fprintf(stderr, "domain alias for %s not found\n", domain);
+ if (yp_getalias(map, map_alias, MAXALIASLEN) != 0)
+ fprintf(stderr, "map alias for %s not found\n", map);
+
+ /* check to see if the map exists in this domain */
+ if (is_yptol_mode())
+ sprintf(ypmapname, "%s/%s/%s%s.dir", ypdbpath, domain_alias,
+ NTOL_PREFIX, map_alias);
+ else
+ sprintf(ypmapname, "%s/%s/%s.dir", ypdbpath, domain_alias,
+ map_alias);
+ if (stat(ypmapname, &sbuf) < 0) {
+ fprintf(stderr, "yppush: Map does not exist.\n");
+ exit(1);
+ }
+
+ if (onehost) {
+ one_host_list();
+ } else {
+ make_server_list();
+ }
+
+ /*
+ * All process exits after the call to generate_callback should be
+ * through listener_exit(program, status), not exit(status), so the
+ * transient server can get unregistered with the portmapper.
+ */
+
+ if (!generate_callback(&program)) {
+ fprintf(stderr, "Can't set up transient callback server.\n");
+ }
+
+ main_loop(program);
+
+ listener_exit(program, 0);
+
+ /* NOTREACHED */
+ return (0);
+}
+
+/*
+ * This does the command line parsing.
+ */
+static void
+get_command_line_args(int argc, char **argv)
+{
+ pusage = err_usage;
+ argv++;
+
+ if (argc < 2) {
+ fprintf(stderr, pusage);
+ exit(1);
+ }
+
+ while (--argc) {
+ if ((*argv)[0] == '-') {
+ switch ((*argv)[1]) {
+ case 'v':
+ verbose = TRUE;
+ argv++;
+ break;
+ case 'd':
+ if (argc > 1) {
+ argv++;
+ argc--;
+ domain = *argv;
+ argv++;
+ if (((int)strlen(domain)) >
+ YPMAXDOMAIN) {
+ fprintf(stderr,
+ err_bad_args,
+ err_bad_domainname);
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, pusage);
+ exit(1);
+ }
+ break;
+ case 'h':
+ if (argc > 1) {
+ onehost = TRUE;
+ argv++;
+ argc--;
+ host = *argv;
+ argv++;
+ } else {
+ fprintf(stderr, pusage);
+ exit(1);
+ }
+ break;
+
+ case 'p':
+
+ if (argc > 1) {
+ argv++;
+ argc--;
+ if (sscanf(*argv, "%d", &curpar) != 1) {
+ (void) fprintf(stderr, pusage);
+ exit(1);
+ }
+ argv++;
+ if (curpar < 1) {
+ (void) fprintf(stderr, pusage);
+ exit(1);
+ }
+ } else {
+ (void) fprintf(stderr, pusage);
+ exit(1);
+ }
+ break;
+
+ default:
+ fprintf(stderr, pusage);
+ exit(1);
+ }
+ } else {
+ if (!map) {
+ map = *argv;
+ } else {
+ fprintf(stderr, pusage);
+ exit(1);
+ }
+ argv++;
+ }
+ }
+
+ if (!map) {
+ fprintf(stderr, pusage);
+ exit(1);
+ }
+}
+
+/*
+ * This gets the local kernel domainname, and sets the global domain to it.
+ */
+static void
+get_default_domain_name(void)
+{
+ if (!getdomainname(default_domain_name, YPMAXDOMAIN)) {
+ domain = default_domain_name;
+ } else {
+ fprintf(stderr, err_cant_get_kname, err_bad_domainname);
+ exit(1);
+ }
+
+ if ((int)strlen(domain) == 0) {
+ fprintf(stderr, err_null_kname, err_bad_domainname);
+ exit(1);
+ }
+}
+
+/*
+ * This verifies that the hostname supplied by the user is in the map
+ * "ypservers" then calls add_server to make it the only entry on the
+ * list of servers.
+ */
+static void
+one_host_list(void)
+{
+ char *key;
+ int keylen;
+ char *val;
+ int vallen;
+ int err;
+ char *ypservers = "ypservers";
+
+ if (verbose) {
+ printf("Verifying YP server: %s\n", host);
+ fflush(stdout);
+ }
+
+ if (err = yp_bind(domain_alias)) {
+ fprintf(stderr, err_cant_bind, domain, yperr_string(err));
+ exit(1);
+ }
+
+ keylen = strlen(host);
+
+ if (yp_match(domain_alias, ypservers, host, keylen,
+ &val, &vallen)) {
+ fprintf(stderr, err_cant_find_host, host);
+ exit(1);
+ }
+
+ add_server(host, keylen);
+}
+
+/*
+ * This uses yp operations to retrieve each server name in the map
+ * "ypservers". add_server is called for each one to add it to the list of
+ * servers.
+ */
+static void
+make_server_list(void)
+{
+ char *key;
+ int keylen;
+ char *outkey;
+ int outkeylen;
+ char *val;
+ int vallen;
+ int err;
+ char *ypservers = "ypservers";
+ int count;
+
+ if (verbose) {
+ printf("Finding YP servers: ");
+ fflush(stdout);
+ count = 4;
+ }
+
+ if (err = yp_bind(domain_alias)) {
+ fprintf(stderr, err_cant_bind, domain, yperr_string(err));
+ exit(1);
+ }
+
+ if (err = yp_first(domain_alias, ypservers, &outkey, &outkeylen,
+ &val, &vallen)) {
+ fprintf(stderr, err_cant_build_serverlist, yperr_string(err));
+ exit(1);
+ }
+
+ for (;;) {
+ add_server(outkey, outkeylen);
+ if (verbose) {
+ printf(" %s", outkey);
+ fflush(stdout);
+ if (count++ == 8) {
+ printf("\n");
+ count = 0;
+ }
+ }
+ free(val);
+ key = outkey;
+ keylen = outkeylen;
+
+ if (err = yp_next(domain_alias, ypservers, key, keylen,
+ &outkey, &outkeylen, &val, &vallen)) {
+
+ if (err == YPERR_NOMORE) {
+ break;
+ } else {
+ fprintf(stderr, err_cant_build_serverlist,
+ yperr_string(err));
+ exit(1);
+ }
+ }
+
+ free(key);
+ }
+ if (count != 0) {
+ if (verbose)
+ printf("\n");
+ }
+}
+
+/*
+ * This adds a single server to the server list.
+ */
+static void
+add_server(char *sname, int namelen)
+{
+ struct server *ps;
+ static unsigned long seq;
+ static unsigned long xactid = 0;
+
+ if (strcmp(sname, my_name) == 0)
+ return;
+
+ if (xactid == 0) {
+ xactid_seed(&xactid);
+ }
+
+ if ((ps = (struct server *)malloc((unsigned)sizeof (struct server)))
+ == (struct server *)NULL) {
+ perror("yppush: malloc failure");
+ exit(1);
+ }
+
+ sname[namelen] = '\0';
+ strcpy(ps->svc_name, sname);
+ ps->state = SSTAT_INIT;
+ ps->status = 0;
+ ps->oldvers = FALSE;
+ ps->xactid = xactid + seq++;
+ ps->pnext = server_list;
+ server_list = ps;
+}
+
+/*
+ * This sets the base range for the transaction ids used in speaking the the
+ * server ypxfr processes.
+ */
+static void
+xactid_seed(unsigned long *xactid)
+{
+ struct timeval t;
+
+ if (gettimeofday(&t) == -1) {
+ perror("yppush gettimeofday failure");
+ *xactid = 1234567;
+ } else {
+ *xactid = t.tv_sec;
+ }
+}
+
+/*
+ * This generates the channel which will be used as the listener process'
+ * service rendezvous point, and comes up with a transient program number
+ * for the use of the RPC messages from the ypxfr processes.
+ */
+static int
+generate_callback(unsigned long *program)
+{
+ unsigned long prognum = 0x40000000, maxprognum;
+ union {
+ unsigned long p;
+ unsigned char b[sizeof (unsigned long)];
+ } u;
+ int ret, i;
+ struct netconfig *nc4, *nc6, *nc;
+ SVCXPRT *trans;
+
+ nc4 = getnetconfigent("udp");
+ nc6 = getnetconfigent("udp6");
+ if (nc4 == 0 && nc6 == 0) {
+ fprintf(stderr,
+ "yppush: Could not get udp or udp6 netconfig entry\n");
+ exit(1);
+ }
+
+ transport4 = (nc4 == 0) ? 0 : svc_tli_create(RPC_ANYFD, nc4, 0, 0, 0);
+ transport6 = (nc6 == 0) ? 0 : svc_tli_create(RPC_ANYFD, nc6, 0, 0, 0);
+ if (transport4 == 0 && transport6 == 0) {
+ fprintf(stderr, "yppush: Could not create server handle(s)\n");
+ exit(1);
+ }
+
+ /* Find the maximum possible program number using an unsigned long */
+ for (i = 0; i < sizeof (u.b); i++)
+ u.b[i] = 0xff;
+ maxprognum = u.p;
+
+ if (transport4 != 0) {
+ trans = transport4;
+ nc = nc4;
+ } else {
+ trans = transport6;
+ nc = nc6;
+ }
+ while (prognum < maxprognum && (ret =
+ rpcb_set(prognum, YPPUSHVERS, nc, &trans->xp_ltaddr)) == 0)
+ prognum++;
+
+ if (ret == 0) {
+ fprintf(stderr, "yppush: Could not create callback service\n");
+ exit(1);
+ } else {
+ if (trans == transport4 && transport6 != 0) {
+ ret = rpcb_set(prognum, YPPUSHVERS, nc6,
+ &transport6->xp_ltaddr);
+ if (ret == 0) {
+ fprintf(stderr,
+ "yppush: Could not create udp6 callback service\n");
+ exit(1);
+ }
+ }
+ *program = prognum;
+ }
+
+ return (ret);
+}
+
+/*
+ * This is the main loop. Send messages to each server,
+ * and then wait for a response.
+ */
+
+
+add_to_active()
+{
+ struct server *ps;
+ ps = server_list;
+ if (ps == NULL)
+ return (0);
+ server_list = server_list->pnext; /* delete from server_list */
+ ps->pnext = active_list;
+ active_list = ps;
+ return (1);
+}
+
+delete_active(in)
+ struct server *in;
+{
+ struct server *p;
+ struct server *n;
+ if (in == active_list) {
+ active_list = active_list->pnext;
+ return (1);
+ }
+ p = active_list;
+ for (n = active_list; n; n = n->pnext) {
+ if (in == n) {
+ p->pnext = n->pnext;
+ return (0);
+
+ }
+ p = n;
+ }
+ return (-1);
+}
+
+void
+main_loop(program)
+ unsigned long program;
+{
+ pollfd_t *pollset = NULL;
+ int npollfds = 0;
+ int pollret;
+ struct server *ps;
+ long error;
+ int hpar; /* this times par count */
+ int i;
+ int j;
+ int time_now;
+ int docb;
+ int actives = 0;
+ int dead = 0;
+
+ if (grace_period < MIN_GRACE)
+ grace_period = MIN_GRACE;
+ if (transport4 != 0) {
+ if (!svc_reg(transport4, program, YPPUSHVERS,
+ listener_dispatch, 0)) {
+ fprintf(stderr,
+ "Can't set up transient udp callback server.\n");
+ }
+ }
+ if (transport6 != 0) {
+ if (!svc_reg(transport6, program, YPPUSHVERS,
+ listener_dispatch, 0)) {
+ fprintf(stderr,
+ "Can't set up transient udp6 callback server.\n");
+ }
+ }
+ for (;;) {
+ time_now = time(0);
+ if (server_list == NULL) {
+ actives = 0;
+ dead = 0;
+ for (ps = active_list; ps; ps = ps->pnext)
+ if (ps->state == SSTAT_CALLED) {
+ if ((time_now - ps->start_time) <
+ grace_period)
+ actives++;
+ else
+ dead++;
+ }
+ if (actives == 0) {
+ if (verbose) {
+ printf("terminating %d dead\n", dead);
+ fflush(stdout);
+ }
+
+ for (ps = active_list; ps; ps = ps->pnext)
+ if (ps->state == SSTAT_CALLED) {
+ if ((time_now - ps->start_time)
+ >= grace_period) {
+ if (verbose) {
+ printf(
+ "no response from %s -- grace of %d seconds expired.\n",
+ ps->svc_name, grace_period);
+ fflush(stdout);
+ }
+ fprintf(stderr,
+ "No response from ypxfr on %s\n", ps->svc_name);
+ }
+ }
+ break;
+ }
+ }
+ actives = 0;
+ for (ps = active_list; ps; ps = ps->pnext) {
+ if (ps->state == SSTAT_CALLED) {
+ if ((time_now - ps->start_time)
+ < grace_period) {
+ actives++;
+
+ if (verbose) {
+ printf(
+ "No response yet from ypxfr on %s\n", ps->svc_name);
+ fflush(stdout);
+ }
+ }
+ } else {
+ if (verbose) {
+ printf("Deactivating %s\n",
+ ps->svc_name);
+ fflush(stdout);
+ }
+ delete_active(ps);
+ }
+ }
+
+ /* add someone to the active list keep up with curpar */
+ for (i = 0; i < (curpar - actives); i++) {
+ if (add_to_active()) {
+ ps = active_list;
+ ps->state = send_message(ps, program, &error);
+ print_state_msg(ps, error);
+ if (ps->state != SSTAT_CALLED)
+ delete_active(ps); /* zorch it */
+ else
+ ps->start_time = time(0); /* set time */
+ }
+ }
+ docb = 0;
+ for (ps = active_list; ps; ps = ps->pnext)
+ if (ps->state == SSTAT_CALLED) {
+ docb = 1;
+ break;
+ }
+ if (docb == 0) {
+ if (verbose) {
+ printf("No one to wait for this pass.\n");
+ fflush(stdout);
+ }
+ continue; /* try curpar more */
+ }
+
+ if (npollfds != svc_max_pollfd) {
+ pollset = realloc(pollset,
+ sizeof (pollfd_t) * svc_max_pollfd);
+ npollfds = svc_max_pollfd;
+ }
+
+ /*
+ * Get existing array of pollfd's, should really compress
+ * this but it shouldn't get very large (or sparse).
+ */
+ (void) memcpy(pollset, svc_pollfd,
+ sizeof (pollfd_t) * svc_max_pollfd);
+
+ errno = 0;
+ switch (pollret = poll(pollset, npollfds, MIN_GRACE * 1000)) {
+ case -1:
+ if (errno != EINTR) {
+ (void) perror("main loop select");
+ }
+ break;
+
+ case 0:
+ if (verbose) {
+ (void) printf("timeout in main loop select.\n");
+ fflush(stdout);
+ }
+ break;
+
+ default:
+ svc_getreq_poll(pollset, pollret);
+ break;
+ } /* switch */
+ } /* for */
+}
+
+/*
+ * This does the listener process cleanup and process exit.
+ */
+static void
+listener_exit(unsigned long program, int stat)
+{
+ svc_unreg(program, YPPUSHVERS);
+ exit(stat);
+}
+
+/*
+ * This is the listener process' RPC service dispatcher.
+ */
+static void
+listener_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ switch (rqstp->rq_proc) {
+
+ case YPPUSHPROC_NULL:
+ if (!svc_sendreply(transp, xdr_void, 0)) {
+ fprintf(stderr, "Can't reply to rpc call.\n");
+ }
+ break;
+
+ case YPPUSHPROC_XFRRESP:
+ get_xfr_response(transp);
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ break;
+ }
+}
+
+
+/*
+ * This dumps a server state message to stdout. It is called in cases where
+ * we have no expectation of receiving a callback from the remote ypxfr.
+ */
+static void
+print_state_msg(struct server *s, long e)
+{
+ struct state_duple *sd;
+
+ if (s->state == SSTAT_SYSTEM)
+ return; /* already printed */
+
+ if (!verbose && (s->state == SSTAT_RESPONDED ||
+ s->state == SSTAT_CALLED))
+ return;
+
+ for (sd = state_duples; sd->state_msg; sd++) {
+ if (sd->state == s->state) {
+ printf(sd->state_msg, s->svc_name);
+
+ if (s->state == SSTAT_RPC) {
+ rpcerr_msg((enum clnt_stat) e);
+ }
+
+ printf("\n");
+ fflush(stdout);
+ return;
+ }
+ }
+
+ fprintf(stderr, "yppush: Bad server state value %d.\n", s->state);
+}
+
+/*
+ * This dumps a transfer status message to stdout. It is called in
+ * response to a received RPC message from the called ypxfr.
+ */
+static void
+print_callback_msg(struct server *s)
+{
+ register struct status_duple *sd;
+
+ if (!verbose &&
+ (s->status == YPPUSH_AGE) ||
+ (s->status == YPPUSH_SUCC))
+
+ return;
+
+ for (sd = status_duples; sd->status_msg; sd++) {
+
+ if (sd->status == s->status) {
+ printf("Status received from ypxfr on %s:\n\t%s\n",
+ s->svc_name, sd->status_msg);
+ fflush(stdout);
+ return;
+ }
+ }
+
+ fprintf(stderr, "yppush listener: Garbage transaction "
+ "status (value %d) from ypxfr on %s.\n",
+ (int)s->status, s->svc_name);
+}
+
+/*
+ * This dumps an RPC error message to stdout. This is basically a rewrite
+ * of clnt_perrno, but writes to stdout instead of stderr.
+ */
+static void
+rpcerr_msg(enum clnt_stat e)
+{
+ struct rpcerr_duple *rd;
+
+ for (rd = rpcerr_duples; rd->rpc_msg; rd++) {
+
+ if (rd->rpc_stat == e) {
+ printf(rd->rpc_msg);
+ return;
+ }
+ }
+
+ fprintf(stderr, "Bad error code passed to rpcerr_msg: %d.\n", e);
+}
+
+/*
+ * This picks up the response from the ypxfr process which has been started
+ * up on the remote node. The response status must be non-zero, otherwise
+ * the status will be set to "ypxfr error".
+ */
+static void
+get_xfr_response(SVCXPRT *transp)
+{
+ struct yppushresp_xfr resp;
+ register struct server *s;
+
+ if (!svc_getargs(transp, (xdrproc_t)xdr_yppushresp_xfr,
+ (caddr_t)&resp)) {
+ svcerr_decode(transp);
+ return;
+ }
+
+ if (!svc_sendreply(transp, xdr_void, 0)) {
+ (void) fprintf(stderr, "Can't reply to rpc call.\n");
+ }
+
+ for (s = active_list; s; s = s->pnext) {
+
+ if (s->xactid == resp.transid) {
+ s->status = resp.status ? resp.status: YPPUSH_XFRERR;
+ print_callback_msg(s);
+ s->state = SSTAT_RESPONDED;
+ return;
+ }
+ }
+}
+
+/*
+ * This sends a message to a single ypserv process. The return value is
+ * a state value. If the RPC call fails because of a version
+ * mismatch, we'll assume that we're talking to a version 1 ypserv process,
+ * and will send him an old "YPPROC_GET" request, as was defined in the
+ * earlier version of yp_prot.h
+ */
+static unsigned short
+send_message(struct server *ps, unsigned long program, long *err)
+{
+ struct ypreq_newxfr req;
+ struct ypreq_xfr oldreq;
+ enum clnt_stat s;
+ struct rpc_err rpcerr;
+
+ if ((ps->domb.dom_client = __yp_clnt_create_rsvdport(ps->svc_name,
+ YPPROG, YPVERS,
+ (char *)NULL,
+ 0, 0)) == NULL) {
+
+ if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) {
+ return (SSTAT_PROGNOTREG);
+ } else {
+ printf("Error talking to %s: ", ps->svc_name);
+ rpcerr_msg(rpc_createerr.cf_stat);
+ printf("\n");
+ fflush(stdout);
+ return (SSTAT_SYSTEM);
+ }
+ }
+
+ if (sysinfo(SI_HOSTNAME, my_name, sizeof (my_name)) == -1) {
+ return (SSTAT_RSCRC);
+ }
+
+ if (!oldxfr) {
+ req.ypxfr_domain = domain;
+ req.ypxfr_map = map;
+ req.ypxfr_ordernum = 0;
+ req.ypxfr_owner = my_name;
+ req.name = ps->svc_name;
+ /*
+ * the creation of field req.name, instead of ypreq_xfr (old)
+ * req.port, does not make any sense. it doesn't give any
+ * information to receiving ypserv except its own name !!
+ * new ypserv duplicates work for YPPROC_XFR and YPPROC_NEWXFR
+ */
+ req.transid = ps->xactid;
+ req.proto = program;
+ s = (enum clnt_stat) clnt_call(ps->domb.dom_client,
+ YPPROC_NEWXFR,
+ (xdrproc_t)xdr_ypreq_newxfr,
+ (caddr_t)&req,
+ xdr_void, 0, timeout);
+ }
+
+ clnt_geterr(ps->domb.dom_client, &rpcerr);
+
+ if (s == RPC_PROCUNAVAIL) {
+ oldreq.ypxfr_domain = domain;
+ oldreq.ypxfr_map = map;
+ oldreq.ypxfr_ordernum = 0;
+ oldreq.ypxfr_owner = my_name;
+ oldreq.transid = ps->xactid;
+ oldreq.proto = program;
+ oldreq.port = 0;
+ s = (enum clnt_stat) clnt_call(ps->domb.dom_client,
+ YPPROC_XFR,
+ (xdrproc_t)xdr_ypreq_xfr,
+ (caddr_t)&oldreq,
+ xdr_void, 0, timeout);
+ clnt_geterr(ps->domb.dom_client, &rpcerr);
+ }
+
+ clnt_destroy(ps->domb.dom_client);
+
+ if (s == RPC_SUCCESS) {
+ return (SSTAT_CALLED);
+ } else {
+ *err = (long)rpcerr.re_status;
+ return (SSTAT_RPC);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * FUNCTION: is_yptol_mode();
+ *
+ * DESCRIPTION: Determines if we should run in N2L or traditional mode based
+ * on the presence of the N2L mapping file.
+ *
+ * This is a copy of a function from libnisdb. If more than this
+ * one function become required it may be worth linking the
+ * entire lib.
+ *
+ * INPUTS: Nothing
+ *
+ * OUTPUTS: TRUE = Run in N2L mode
+ * FALSE = Run in traditional mode.
+ */
+bool_t
+is_yptol_mode()
+{
+ struct stat filestat;
+
+ if (stat(NTOL_MAP_FILE, &filestat) != -1)
+ return (TRUE);
+
+ return (FALSE);
+}