#ifndef lint /* @(#)ypxfr.c 2.1 86/04/16 NFSSRC */ static char sccsid[] = "@(#)ypxfr.c 1.1 86/02/05 Copyr 1984 Sun Micro"; #endif /* * This is a user command which gets a yp data base from some running * server, and gets it to the local site by using the normal yp client * enumeration functions. The map is copied to a temp name, then the real * map is removed and the temp map is moved to the real name. ypxfr then * sends a "YPPROC_CLEAR" message to the local server to insure that he will * not hold a removed map open, so serving an obsolete version. * * ypxfr [-h <host>] [-d <domainname>] [-f] [-c] [-C tid prot ipadd port] map * * where host may be either a name or an internet address of form ww.xx.yy.zz * * If the host is ommitted, ypxfr will attempt to discover the master by * using normal yp services. If it can't get the record, it will use * the address of the callback, if specified. If the host is specified * as an internet address, no yp services need to be locally available. * * If the domain is not specified, the default domain of the local machine * is used. * * If the -f flag is used, the transfer will be done even if the master's * copy is not newer than the local copy. * * The -c flag suppresses the YPPROC_CLEAR request to the local ypserv. It * may be used if ypserv isn't currently running to suppress the error message. * * The -C flag is used to pass callback information to ypxfr when it is * activated by ypserv. The callback information is used to send a * yppushresp_xfr message with transaction id "tid" to a yppush process * speaking a transient protocol number "prot". The yppush program is * running on the node with IP address "ipadd", and is listening (UDP) on * "port". * */ #include <dbm.h> #undef NULL #define DATUM #include <stdio.h> #include <errno.h> #include <sys/time.h> #include <ctype.h> #include <netdb.h> #include <rpc/rpc.h> #include <sys/socket.h> #include <sys/dir.h> #include <sys/file.h> #include <sys/stat.h> #include <rpcsvc/ypclnt.h> #include <rpcsvc/yp_prot.h> #include <rpcsvc/ypv1_prot.h> # define PARANOID 1 /* make sure maps have the right # entries */ #define UDPINTER_TRY 10 /* Seconds between tries for udp */ #define UDPTIMEOUT UDPINTER_TRY*4 /* Total timeout for udp */ #define CALLINTER_TRY 10 /* Seconds between callback tries */ #define CALLTIMEOUT CALLINTER_TRY*6 /* Total timeout for callback */ struct timeval udp_intertry = { UDPINTER_TRY, 0 }; struct timeval udp_timeout = { UDPTIMEOUT, 0 }; struct timeval tcp_timeout = { 180, /* timeout for map enumeration (could be long) */ 0 }; char *domain = NULL; char *map = NULL; char *master = NULL; /* The name of the xfer peer as specified as a * -h option, or from querying the yp */ struct in_addr master_addr; /* Addr of above */ struct dom_binding master_server;/* To talk to above */ unsigned int master_prog_vers; /* YPVERS or YPOLDVERS */ char *master_name = NULL; /* Map's master as contained in the map */ unsigned *master_version = NULL; /* Order number as contained in the map */ char *master_ascii_version; /* ASCII order number as contained in the map */ bool fake_master_version = FALSE; /* TRUE only if there's no order number in * the map, and the user specified -f */ char yp_last_modified[] = "YP_LAST_MODIFIED"; char yp_master_name[] = "YP_MASTER_NAME"; bool force = FALSE; /* TRUE iff user specified -f flag */ bool logging = FALSE; /* TRUE iff no tty, but log file exists */ bool send_clear = TRUE; /* FALSE iff user specified -c flag */ bool callback = FALSE; /* TRUE iff -C flag set. tid, proto, ipadd, * and port will be set to point to the * command line args. */ char *tid; char *proto; char *ipadd; char *port; int entry_count; /* counts entries in the map */ char logfile[] = "/etc/yp/ypxfr.log"; char err_usage[] = "Usage:\n\n\ ypxfr [-f] [-h <host>] [-d <domainname>] [-c] [-C tid prot ipadd port] map\n\n\ where\n\ -f forces transfer even if the master's copy is not newer.\n\ host may be either a name or an internet \n\ address of form ww.xx.yy.zz\n\ -c inhibits sending a \"Clear map\" message to the local ypserv.\n\ -C is used by ypserv to pass callback information.\n"; char err_bad_args[] = "%s argument is bad.\n"; char err_cant_get_kname[] = "Can't get %s back from system call.\n"; char err_null_kname[] = "%s hasn't been set on this machine.\n"; char err_bad_hostname[] = "hostname"; char err_bad_mapname[] = "mapname"; char err_bad_domainname[] = "domainname"; char err_udp_failure[] = "Can't set up a udp connection to ypserv on host %s.\n"; char ypdbpath[] = "/etc/yp"; char yptempname_prefix[] = "ypxfr_map."; char ypbkupname_prefix[] = "ypxfr_bkup."; void get_command_line_args(); bool get_master_addr(); bool bind_to_server(); bool ping_server(); bool get_private_recs(); bool get_order(); bool get_v1order(); bool get_v2order(); bool get_master_name(); bool get_v1master_name(); bool get_v2master_name(); void find_map_master(); bool move_map(); unsigned get_local_version(); void mkfilename(); void mk_tmpname(); bool rename_map(); bool check_map_existence(); bool get_map(); bool add_private_entries(); bool new_mapfiles(); void del_mapfiles(); void set_output(); void logprintf(); bool send_ypclear(); void xfr_exit(); void send_callback(); int ypall_callback(); int map_yperr_to_pusherr(); extern u_long inet_addr(); extern struct hostent *gethostbyname(); extern int errno; /* * This is the mainline for the ypxfr process. */ void main(argc, argv) int argc; char **argv; { int i; static char default_domain_name[YPMAXDOMAIN]; static unsigned big = 0xffffffff; int status; set_output(); get_command_line_args(argc, argv); if (!domain) { if (!getdomainname(default_domain_name, YPMAXDOMAIN) ) { domain = default_domain_name; } else { logprintf( err_cant_get_kname, err_bad_domainname); xfr_exit(YPPUSH_RSRC); } if (strlen(domain) == 0) { logprintf( err_null_kname, err_bad_domainname); xfr_exit(YPPUSH_RSRC); } } if (!master) { find_map_master(); } if (!get_master_addr() ) { xfr_exit(YPPUSH_MADDR); } if (!bind_to_server(master, master_addr, &master_server, &master_prog_vers) ) { xfr_exit(YPPUSH_RPC); } if (!get_private_recs(&status) ) { xfr_exit(status); } if (!master_version) { if (force) { master_version = &big; fake_master_version = TRUE; } else { logprintf( "Can't get order number for map %s from server at %s: use the -f flag.\n", map, master); xfr_exit(YPPUSH_FORCE); } } if (!move_map(&status) ) { xfr_exit(status); } if (send_clear && !send_ypclear(&status) ) { xfr_exit(status); } if (logging) { logprintf("Transferred map %s from %s (%d entries).\n", map, master, entry_count); } xfr_exit(YPPUSH_SUCC); } /* * This decides whether we're being run interactively or not, and, if not, * whether we're supposed to be logging or not. If we are logging, it sets * up stderr to point to the log file, and sets the "logging" * variable. If there's no logging, the output goes in the bit bucket. * Logging output differs from interactive output in the presence of a * timestamp, present only in the log file. stderr is reset, too, because it * it's used by various library functions, including clnt_perror. */ void set_output() { if (!isatty(1)) { if (access(logfile, W_OK)) { (void) freopen("/dev/null", "w", stderr); } else { (void) freopen(logfile, "a", stderr); logging = TRUE; } } } /* * This constructs a logging record. */ void logprintf(arg1,arg2,arg3,arg4,arg5,arg6,arg7) { struct timeval t; fseek(stderr,0,2); if (logging) { (void) gettimeofday(&t, NULL); (void) fprintf(stderr, "%19.19s: ", ctime(&t.tv_sec)); } (void) fprintf(stderr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); fflush(stderr); } /* * This does the command line argument processing. */ void get_command_line_args(argc, argv) int argc; char **argv; { argv++; if (argc < 2) { logprintf( err_usage); xfr_exit(YPPUSH_BADARGS); } while (--argc) { if ( (*argv)[0] == '-') { switch ((*argv)[1]) { case 'f': { force = TRUE; argv++; break; } case 'c': { send_clear = FALSE; argv++; break; } case 'h': { if (argc > 1) { argv++; argc--; master = *argv; argv++; if (strlen(master) > 256) { logprintf( err_bad_args, err_bad_hostname); xfr_exit(YPPUSH_BADARGS); } } else { logprintf( err_usage); xfr_exit(YPPUSH_BADARGS); } break; } case 'd': { if (argc > 1) { argv++; argc--; domain = *argv; argv++; if (strlen(domain) > YPMAXDOMAIN) { logprintf( err_bad_args, err_bad_domainname); xfr_exit(YPPUSH_BADARGS); } } else { logprintf( err_usage); xfr_exit(YPPUSH_BADARGS); } break; } case 'C': { if (argc > 5) { callback = TRUE; argv++; tid = *argv++; proto = *argv++; ipadd = *argv++; port = *argv++; argc -= 4; } else { logprintf( err_usage); xfr_exit(YPPUSH_BADARGS); } break; } default: { logprintf( err_usage); xfr_exit(YPPUSH_BADARGS); } } } else { if (!map) { map = *argv; argv++; if (strlen(map) > YPMAXMAP) { logprintf( err_bad_args, err_bad_mapname); xfr_exit(YPPUSH_BADARGS); } } else { logprintf( err_usage); xfr_exit(YPPUSH_BADARGS); } } } if (!map) { logprintf( err_usage); xfr_exit(YPPUSH_BADARGS); } } /* * This checks to see if the master name is an ASCII internet address, in * which case it's translated to an internet address here, or is a host * name. In the second case, the standard library routine gethostbyname(3n) * (which uses the yp services) is called to do the translation. */ bool get_master_addr() { struct in_addr tempaddr; struct hostent *h; bool error = FALSE; if (master==NULL) { /* * if we were unable to get the master name, use the * address of the person who called us. */ if (callback) { master_addr.s_addr = inet_addr(ipadd); master = ipadd; return (TRUE); } return (FALSE); } if (isdigit(*master) ) { tempaddr.s_addr = inet_addr(master); if ((int) tempaddr.s_addr != -1) { master_addr = tempaddr; } else { error = TRUE; } } else { if (h = gethostbyname(master) ) { (void) bcopy(h->h_addr, (char *) &master_addr, h->h_length); } else { error = TRUE; } } if (error) { logprintf( "Can't translate master name %s to an address.\n", master); return (FALSE); } else { return (TRUE); } } /* * This sets up a udp connection to speak the correct program and version * to a yp server. vers is set to one of YPVERS or YPOLDVERS to reflect which * language the server speaks. */ bool bind_to_server(host, host_addr, pdomb, vers) char *host; struct in_addr host_addr; struct dom_binding *pdomb; unsigned int *vers; { if (ping_server(host, host_addr, pdomb, YPVERS)) { *vers = YPVERS; return (TRUE); } else { if (ping_server(host, host_addr, pdomb, YPOLDVERS)) { *vers = YPOLDVERS; return (TRUE); } else { return (FALSE); } } } /* * This sets up a UDP channel to a server which is assumed to speak an input * version of YPPROG. The channel is tested by pinging the server. In all * error cases except "Program Version Number Mismatch", the error is * reported, and in all error cases, the client handle is destroyed and the * socket associated with the channel is closed. */ bool ping_server(host, host_addr, pdomb, vers) char *host; struct in_addr host_addr; struct dom_binding *pdomb; unsigned int vers; { enum clnt_stat rpc_stat; pdomb->dom_server_addr.sin_addr = host_addr; pdomb->dom_server_addr.sin_family = AF_INET; pdomb->dom_server_addr.sin_port = htons((u_short) 0); pdomb->dom_server_port = htons((u_short) 0); pdomb->dom_socket = RPC_ANYSOCK; if (pdomb->dom_client = clntudp_create(&(pdomb->dom_server_addr), YPPROG, vers, udp_intertry, &(pdomb->dom_socket )) ) { rpc_stat = clnt_call(pdomb->dom_client, YPBINDPROC_NULL, xdr_void, 0, xdr_void, 0, udp_timeout); if (rpc_stat == RPC_SUCCESS) { return (TRUE); } else { clnt_destroy(pdomb->dom_client); close(pdomb->dom_socket); if (rpc_stat != RPC_PROGVERSMISMATCH) { (void) clnt_perror(pdomb->dom_client, "ypxfr: bind_to_server clnt_call error"); } return (FALSE); } } else { logprintf("bind_to_server clntudp_create error"); (void) clnt_pcreateerror(""); fflush(stderr); return (FALSE); } } /* * This gets values for the YP_LAST_MODIFIED and YP_MASTER_NAME keys from the * master server's version of the map. Values are held in static variables * here. In the success cases, global pointer variables are set to point at * the local statics. */ bool get_private_recs(pushstat) int *pushstat; { static char anumber[20]; static unsigned number; static char name[YPMAXPEER + 1]; int status; status = 0; if (get_order(anumber, &number, &status) ) { master_version = &number; master_ascii_version = anumber; } else { if (status != 0) { *pushstat = status; return (FALSE); } } if (get_master_name(name, &status) ) { master_name = name; } else { if (status != 0) { *pushstat = status; return (FALSE); } master_name = master; } return (TRUE); } /* * This gets the map's order number from the master server */ bool get_order(an, n, pushstat) char *an; unsigned *n; int *pushstat; { if (master_prog_vers == YPVERS) { return (get_v2order(an, n, pushstat) ); } else { return (get_v1order(an, n, pushstat) ); } } bool get_v1order(an, n, pushstat) char *an; unsigned *n; int *pushstat; { struct yprequest req; struct ypresponse resp; bool retval; char errmsg[256]; retval = FALSE; req.yp_reqtype = YPMATCH_REQTYPE; req.ypmatch_req_domain = domain; req.ypmatch_req_map = map; req.ypmatch_req_keyptr = yp_last_modified; req.ypmatch_req_keysize = (sizeof (yp_last_modified)) - 1; resp.ypmatch_resp_valptr = NULL; resp.ypmatch_resp_valsize = 0; if(clnt_call(master_server.dom_client, YPOLDPROC_MATCH, _xdr_yprequest, &req, _xdr_ypresponse, &resp, udp_timeout) == RPC_SUCCESS) { if (resp.ypmatch_resp_status == YP_TRUE) { bcopy(resp.ypmatch_resp_valptr, an, resp.ypmatch_resp_valsize); an[resp.ypmatch_resp_valsize] = '\0'; *n = atoi(an); retval = TRUE; } else if (resp.ypmatch_resp_status != YP_NOKEY) { *pushstat = map_yperr_to_pusherr( resp.ypmatch_resp_status); if (!logging) { logprintf( "(info) Can't get order number from ypserv at %s. Reason: %s.\n", master, yperr_string(ypprot_err( (unsigned) resp.ypmatch_resp_status)) ); } } CLNT_FREERES(master_server.dom_client, _xdr_ypresponse, &resp); } else { *pushstat = YPPUSH_RPC; (void) sprintf(errmsg, "ypxfr(get_v1order) RPC call to %s failed", master); clnt_perror(master_server.dom_client, errmsg); } return(retval); } bool get_v2order(an, n, pushstat) char *an; unsigned *n; int *pushstat; { struct ypreq_nokey req; struct ypresp_order resp; int retval; char errmsg[256]; req.domain = domain; req.map = map; /* * Get the map''s order number, null-terminate it and store it, * and convert it to binary and store it again. */ retval = FALSE; if((enum clnt_stat) clnt_call(master_server.dom_client, YPPROC_ORDER, xdr_ypreq_nokey, &req, xdr_ypresp_order, &resp, udp_timeout) == RPC_SUCCESS) { if (resp.status == YP_TRUE) { sprintf(an, "%d", resp.ordernum); *n = resp.ordernum; retval = TRUE; } else if (resp.status != YP_BADDB) { *pushstat = ypprot_err(resp.status); if (!logging) { logprintf( "(info) Can't get order number from ypserv at %s. Reason: %s.\n", master, yperr_string( ypprot_err(resp.status)) ); } } CLNT_FREERES(master_server.dom_client, xdr_ypresp_order, &resp); } else { *pushstat = YPPUSH_RPC; logprintf("ypxfr(get_v2order) RPC call to %s failed", master); clnt_perror(master_server.dom_client, ""); } return (retval); } /* * This gets the map's master name from the master server */ bool get_master_name(name, pushstat) char *name; int *pushstat; { if (master_prog_vers == YPVERS) { return (get_v2master_name(name, pushstat)); } else { return (get_v1master_name(name, pushstat)); } } bool get_v1master_name(name, pushstat) char *name; int *pushstat; { struct yprequest req; struct ypresponse resp; bool retval; char errmsg[256]; retval = FALSE; req.yp_reqtype = YPMATCH_REQTYPE; req.ypmatch_req_domain = domain; req.ypmatch_req_map = map; req.ypmatch_req_keyptr = yp_master_name; req.ypmatch_req_keysize = (sizeof (yp_master_name)) -1; resp.ypmatch_resp_valptr = NULL; resp.ypmatch_resp_valsize = 0; if(clnt_call(master_server.dom_client, YPOLDPROC_MATCH, _xdr_yprequest, &req, _xdr_ypresponse, &resp, udp_timeout) == RPC_SUCCESS) { if (resp.ypmatch_resp_status == YP_TRUE) { bcopy(resp.ypmatch_resp_valptr, name, resp.ypmatch_resp_valsize); name[resp.ypmatch_resp_valsize] = '\0'; retval = TRUE; } else if (resp.ypmatch_resp_status != YP_NOKEY) { *pushstat = map_yperr_to_pusherr( resp.ypmatch_resp_status); logprintf( "(info) Can't get master name from ypserv at %s. Reason: %s.\n", master, yperr_string(ypprot_err((unsigned) resp.ypmatch_resp_status)) ); } CLNT_FREERES(master_server.dom_client, _xdr_ypresponse, &resp); } else { *pushstat = YPPUSH_RPC; (void) sprintf(errmsg, "ypxfr(get_v1master_name) RPC call to %s failed", master); clnt_perror(master_server.dom_client, errmsg); } return(retval); } bool get_v2master_name(name, pushstat) char *name; int *pushstat; { struct ypreq_nokey req; struct ypresp_master resp; int retval; char errmsg[256]; req.domain = domain; req.map = map; resp.master = NULL; retval = FALSE; if((enum clnt_stat) clnt_call(master_server.dom_client, YPPROC_MASTER, xdr_ypreq_nokey, &req, xdr_ypresp_master, &resp, udp_timeout) == RPC_SUCCESS) { if (resp.status == YP_TRUE) { strcpy(name, resp.master); retval = TRUE; } else if (resp.status != YP_BADDB) { *pushstat = ypprot_err(resp.status); if (!logging) { logprintf( "(info) Can't get master name from ypserv at %s. Reason: %s.\n", master, yperr_string( ypprot_err(resp.status)) ); } } CLNT_FREERES(master_server.dom_client, xdr_ypresp_master, &resp); } else { *pushstat = YPPUSH_RPC; logprintf( "ypxfr(get_v2master_name) RPC call to %s failed", master); clnt_perror(master_server.dom_client, ""); } return (retval); } /* * This tries to get the master name for the named map, from any * server's version, using the vanilla yp client interface. If we get a * name back, the global "master" gets pointed to it. */ void find_map_master() { int err; if (err = yp_master(domain, map, &master)) { logprintf("Can't get master of %s. Reason: %s.\n", map, yperr_string(err)); } yp_unbind(domain); } /* * This does the work of transferrring the map. */ bool move_map(pushstat) int *pushstat; { unsigned local_version; char map_name[MAXNAMLEN + 1]; char tmp_name[MAXNAMLEN + 1]; char bkup_name[MAXNAMLEN + 1]; char an[11]; unsigned n; mkfilename(map_name); if (!force) { local_version = get_local_version(map_name); if (local_version >= *master_version) { logprintf( "Map %s at %s is not more recent than local.\n", map, master); *pushstat = YPPUSH_AGE; return (FALSE); } } mk_tmpname(yptempname_prefix, tmp_name); if (!new_mapfiles(tmp_name) ) { logprintf( "Can't create temp map %s.\n", tmp_name); *pushstat = YPPUSH_FILE; return (FALSE); } if (dbminit(tmp_name) < 0) { logprintf( "Can't dbm init temp map %s.\n", tmp_name); del_mapfiles(tmp_name); *pushstat = YPPUSH_DBM; return (FALSE); } if (!get_map(tmp_name, pushstat) ) { del_mapfiles(tmp_name); return (FALSE); } if (!add_private_entries(tmp_name) ) { del_mapfiles(tmp_name); *pushstat = YPPUSH_DBM; return (FALSE); } if (dbmclose(tmp_name) < 0) { logprintf( "Can't do dbm close operation on temp map %s.\n", tmp_name); del_mapfiles(tmp_name); *pushstat = YPPUSH_DBM; return (FALSE); } if (!get_order(an, &n, pushstat)) { return(FALSE); } if (n != *master_version) { logprintf( "Version skew at %s while transferring map %s.\n", master, map); del_mapfiles(tmp_name); *pushstat = YPPUSH_SKEW; return (FALSE); } # ifdef PARANOID if (!count_mismatch(tmp_name,entry_count)) { del_mapfiles(tmp_name); *pushstat = YPPUSH_DBM; return(FALSE); } # endif PARANOID if (!check_map_existence(map_name) ) { if (!rename_map(tmp_name, map_name) ) { del_mapfiles(tmp_name); logprintf( "Rename error: couldn't mv %s to %s.\n", tmp_name, map_name); *pushstat = YPPUSH_FILE; return (FALSE); } } else { mk_tmpname(ypbkupname_prefix, bkup_name); if (!rename_map(map_name, bkup_name) ) { (void) rename_map(bkup_name, map_name); logprintf( "Rename error: check that old %s is still intact.\n", map_name); del_mapfiles(tmp_name); *pushstat = YPPUSH_FILE; return (FALSE); } if (rename_map(tmp_name, map_name) ) { del_mapfiles(bkup_name); } else { del_mapfiles(tmp_name); (void) rename_map(bkup_name, map_name); logprintf( "Rename error: check that old %s is still intact.\n", map_name); *pushstat = YPPUSH_FILE; return (FALSE); } } return (TRUE); } /* * This tries to get the order number out of the local version of the map. * If the attempt fails for any version, the function will return "0" */ unsigned get_local_version(name) char *name; { datum key; datum val; char number[11]; if (!check_map_existence(name) ) { return (0); } if (dbminit(name) < 0) { return (0); } key.dptr = yp_last_modified; key.dsize = (sizeof (yp_last_modified) - 1); val = fetch(key); (void) dbmclose(name); if (!val.dptr) { return (0); } if (val.dsize == 0 || val.dsize > 10) { return (0); } (void) bcopy(val.dptr, number, val.dsize); number[val.dsize] = '\0'; return ((unsigned) atoi(number) ); } /* * This constructs a file name for a map, minus its ".dir" or ".pag" extensions */ void mkfilename(ppath) char *ppath; { if ( (strlen(domain) + strlen(map) + strlen(ypdbpath) + 3) > (MAXNAMLEN + 1) ) { logprintf( "Map name string too long.\n"); } (void) strcpy(ppath, ypdbpath); (void) strcat(ppath, "/"); (void) strcat(ppath, domain); (void) strcat(ppath, "/"); (void) strcat(ppath, map); } /* * This returns a temporary name for a map transfer minus its ".dir" or * ".pag" extensions. */ void mk_tmpname(prefix, xfr_name) char *prefix; char *xfr_name; { char xfr_anumber[10]; long xfr_number; if (!xfr_name) { return; } xfr_number = getpid(); (void) sprintf(xfr_anumber, "%d", xfr_number); (void) strcpy(xfr_name, ypdbpath); (void) strcat(xfr_name, "/"); (void) strcat(xfr_name, domain); (void) strcat(xfr_name, "/"); (void) strcat(xfr_name, prefix); (void) strcat(xfr_name, map); (void) strcat(xfr_name, "."); (void) strcat(xfr_name, xfr_anumber); } /* * This deletes the .pag and .dir files which implement a map. * * Note: No error checking is done here for a garbage input file name or for * failed unlink operations. */ void del_mapfiles(basename) char *basename; { char dbfilename[MAXNAMLEN + 1]; if (!basename) { return; } strcpy(dbfilename, basename); strcat(dbfilename, ".pag"); unlink(dbfilename); strcpy(dbfilename, basename); strcat(dbfilename, ".dir"); unlink(dbfilename); } /* * This checks to see if the source map files exist, then renames them to the * target names. This is a boolean function. The file names from.pag and * from.dir will be changed to to.pag and to.dir in the success case. * * Note: If the second of the two renames fails, yprename_map will try to * un-rename the first pair, and leave the world in the state it was on entry. * This might fail, too, though... */ bool rename_map(from, to) char *from; char *to; { char fromfile[MAXNAMLEN + 1]; char tofile[MAXNAMLEN + 1]; if (!from || !to) { return (FALSE); } if (!check_map_existence(from) ) { return (FALSE); } (void) strcpy(fromfile, from); (void) strcat(fromfile, ".pag"); (void) strcpy(tofile, to); (void) strcat(tofile, ".pag"); if (rename(fromfile, tofile) ) { logprintf( "Can't mv %s to %s.\n", fromfile, tofile); return (FALSE); } (void) strcpy(fromfile, from); (void) strcat(fromfile, ".dir"); (void) strcpy(tofile, to); (void) strcat(tofile, ".dir"); if (rename(fromfile, tofile) ) { logprintf( "Can't mv %s to %s.\n", fromfile, tofile); (void) strcpy(fromfile, from); (void) strcat(fromfile, ".pag"); (void) strcpy(tofile, to); (void) strcat(tofile, ".pag"); if (rename(tofile, fromfile) ) { logprintf( "Can't recover from rename failure.\n"); return (FALSE); } return (FALSE); } return (TRUE); } /* * This performs an existence check on the dbm data base files <pname>.pag and * <pname>.dir. */ bool check_map_existence(pname) char *pname; { char dbfile[MAXNAMLEN + 1]; struct stat filestat; int len; if (!pname || ((len = strlen(pname)) == 0) || (len + 5) > (MAXNAMLEN + 1) ) { return (FALSE); } errno = 0; (void) strcpy(dbfile, pname); (void) strcat(dbfile, ".dir"); if (stat(dbfile, &filestat) != -1) { (void) strcpy(dbfile, pname); (void) strcat(dbfile, ".pag"); if (stat(dbfile, &filestat) != -1) { return (TRUE); } else { if (errno != ENOENT) { logprintf( "Stat error on map file %s.\n", dbfile); } return (FALSE); } } else { if (errno != ENOENT) { logprintf( "Stat error on map file %s.\n", dbfile); } return (FALSE); } } /* * This creates <pname>.dir and <pname>.pag */ bool new_mapfiles(pname) char *pname; { char dbfile[MAXNAMLEN + 1]; int f; int len; if (!pname || ((len = strlen(pname)) == 0) || (len + 5) > (MAXNAMLEN + 1) ) { return (FALSE); } errno = 0; (void) strcpy(dbfile, pname); (void) strcat(dbfile, ".dir"); if ((f = open(dbfile, (O_WRONLY | O_CREAT | O_TRUNC), 0644)) >= 0) { (void) close(f); (void) strcpy(dbfile, pname); (void) strcat(dbfile, ".pag"); if ((f = open(dbfile, (O_WRONLY | O_CREAT | O_TRUNC), 0644)) >= 0) { (void) close(f); return (TRUE); } else { return (FALSE); } } else { return (FALSE); } } count_callback(status) int status; { if (status != YP_TRUE) { if (status != YP_NOMORE) { logprintf( "Error from ypserv on %s (ypall_callback) = %s.\n", master, yperr_string(ypprot_err(status))); } return(TRUE); } entry_count++; return(FALSE); } /* * This counts the entries in the dbm file after the transfer to * make sure that the dbm file was built correctly. * Returns TRUE if everything is OK, FALSE if they mismatch. */ count_mismatch(pname,oldcount) char *pname; int oldcount; { datum key, value; struct dom_binding domb; enum clnt_stat s; struct ypreq_nokey allreq; struct ypall_callback cbinfo; entry_count = 0; dbminit(pname); for (key = firstkey(); key.dptr != NULL; key = nextkey(key)) entry_count++; dbmclose(pname); if (oldcount != entry_count) { logprintf( "*** Count mismatch in dbm file %s: old=%d, new=%d ***\n", map, oldcount, entry_count); return(FALSE); } # ifdef REALLY_PARANOID domb.dom_server_addr.sin_addr = master_addr; domb.dom_server_addr.sin_family = AF_INET; domb.dom_server_addr.sin_port = htons((u_short) 0); domb.dom_server_port = htons((u_short) 0); domb.dom_socket = RPC_ANYSOCK; if ((domb.dom_client = clnttcp_create(&(domb.dom_server_addr), YPPROG, master_prog_vers, &(domb.dom_socket), 0, 0)) == (CLIENT *) NULL) { clnt_pcreateerror( "ypxfr (mismatch) - TCP channel create failure"); return(FALSE); } if (master_prog_vers == YPVERS) { int tmpstat; allreq.domain = domain; allreq.map = map; cbinfo.foreach = count_callback; tmpstat = 0; cbinfo.data = (char *) &tmpstat; entry_count = 0; s = clnt_call(domb.dom_client, YPPROC_ALL, xdr_ypreq_nokey, &allreq, xdr_ypall, &cbinfo, tcp_timeout); if (tmpstat == 0) { if (s == RPC_SUCCESS) { } else { clnt_perror(domb.dom_client, "ypxfr (get_map/all) - RPC clnt_call (TCP) failure"); return(FALSE); } } else { return(FALSE); } } else { logprintf("Wrong version number!\n"); return(FALSE); } clnt_destroy(domb.dom_client); close(domb.dom_socket); entry_count += 2; /* add in YP_entries */ if (oldcount != entry_count) { logprintf( "*** Count mismatch after enumerate %s: old=%d, new=%d ***\n", map, oldcount, entry_count); return(FALSE); } # endif REALLY_PARANOID return(TRUE); } /* * This sets up a TCP connection to the master server, and either gets * ypall_callback to do all the work of writing it to the local dbm file * (if the ypserv is current version), or does it itself for an old ypserv. */ bool get_map(pname, pushstat) char *pname; int *pushstat; { struct dom_binding domb; enum clnt_stat s; struct ypreq_nokey allreq; struct ypall_callback cbinfo; struct yprequest oldreq; struct ypresponse resp; bool retval = FALSE; int tmpstat; domb.dom_server_addr.sin_addr = master_addr; domb.dom_server_addr.sin_family = AF_INET; domb.dom_server_addr.sin_port = htons((u_short) 0); domb.dom_server_port = htons((u_short) 0); domb.dom_socket = RPC_ANYSOCK; if ((domb.dom_client = clnttcp_create(&(domb.dom_server_addr), YPPROG, master_prog_vers, &(domb.dom_socket), 0, 0)) == (CLIENT *) NULL) { clnt_pcreateerror( "ypxfr (get_map) - TCP channel create failure"); *pushstat = YPPUSH_RPC; return(FALSE); } entry_count = 0; if (master_prog_vers == YPVERS) { allreq.domain = domain; allreq.map = map; cbinfo.foreach = ypall_callback; tmpstat = 0; cbinfo.data = (char *) &tmpstat; s = clnt_call(domb.dom_client, YPPROC_ALL, xdr_ypreq_nokey, &allreq, xdr_ypall, &cbinfo, tcp_timeout); if (tmpstat == 0) { if (s == RPC_SUCCESS) { retval = TRUE; } else { clnt_perror(domb.dom_client, "ypxfr (get_map/all) - RPC clnt_call (TCP) failure"); *pushstat = YPPUSH_RPC; } } else { *pushstat = tmpstat; } } else { datum inkey, inval; oldreq.yp_reqtype = YPFIRST_REQTYPE; oldreq.ypfirst_req_domain = domain; oldreq.ypfirst_req_map = map; resp.ypfirst_resp_keyptr = NULL; resp.ypfirst_resp_keysize = 0; resp.ypfirst_resp_valptr = NULL; resp.ypfirst_resp_valsize = 0; if((s = clnt_call(domb.dom_client, YPOLDPROC_FIRST, _xdr_yprequest, &oldreq, _xdr_ypresponse, &resp, tcp_timeout)) != RPC_SUCCESS) { clnt_perror(domb.dom_client, "ypxfr (get_map/first) - RPC clnt_call (TCP) failure"); *pushstat = YPPUSH_RPC; goto cleanup; } if (resp.ypfirst_resp_status != YP_TRUE) { logprintf( "Error from ypserv on %s (get first) = %s.\n", master, yperr_string(ypprot_err( resp.ypfirst_resp_status))); *pushstat = YPPUSH_RPC; goto cleanup; } inkey = resp.ypfirst_resp_keydat; inval = resp.ypfirst_resp_valdat; /* * Continue to get the next entries in the map as long as * there are no errors, and there are entries remaining. */ oldreq.yp_reqtype = YPNEXT_REQTYPE; oldreq.ypnext_req_domain = domain; oldreq.ypnext_req_map = map; while (TRUE) { if (strncmp(inkey.dptr,"YP_",3)) { if (store(inkey, inval) < 0) { logprintf( "Can't do dbm store into temp map %s.\n", pname); *pushstat = YPPUSH_DBM; goto cleanup; } entry_count++; } CLNT_FREERES(domb.dom_client, _xdr_ypresponse, &resp); oldreq.ypnext_req_keydat = inkey; resp.ypnext_resp_keydat.dptr = NULL; resp.ypnext_resp_valdat.dptr = NULL; resp.ypnext_resp_keydat.dsize = 0; resp.ypnext_resp_valdat.dsize = 0; if ((s = clnt_call(domb.dom_client, YPOLDPROC_NEXT, _xdr_yprequest, &oldreq, _xdr_ypresponse, &resp, tcp_timeout)) != RPC_SUCCESS) { clnt_perror(domb.dom_client, "ypxfr (get_map/next) - RPC clnt_call (TCP) failure"); *pushstat = YPPUSH_RPC; break; } if (resp.ypnext_resp_status != YP_TRUE) { if (resp.ypnext_resp_status == YP_NOMORE) { retval = TRUE; } else { logprintf( "Error from ypserv on %s (get next) = %d.\n", master, yperr_string(ypprot_err( resp.ypnext_resp_status))); *pushstat = YPPUSH_RPC; } break; } inkey = resp.ypnext_resp_keydat; inval = resp.ypnext_resp_valdat; } } cleanup: clnt_destroy(domb.dom_client); close(domb.dom_socket); return (retval); } /* * This sticks each key-value pair into the current map. It returns FALSE as * long as it wants to keep getting called back, and TRUE on error conditions * and "No more k-v pairs". */ int ypall_callback(status, key, kl, val, vl, pushstat) int status; char *key; int kl; char *val; int vl; int *pushstat; { datum keydat; datum valdat; datum test; if (status != YP_TRUE) { if (status != YP_NOMORE) { logprintf( "Error from ypserv on %s (ypall_callback) = %s.\n", master, yperr_string(ypprot_err(status))); *pushstat = map_yperr_to_pusherr(status); } return(TRUE); } keydat.dptr = key; keydat.dsize = kl; valdat.dptr = val; valdat.dsize = vl; entry_count++; # ifdef PARANOID test = fetch(keydat); if (test.dptr!=NULL) { logprintf("Duplicate key %s in map %s\n",key,map); *pushstat = YPPUSH_DBM; return(TRUE); } # endif PARANOID if (store(keydat, valdat) < 0) { logprintf( "Can't do dbm store into temp map %s.\n",map); *pushstat = YPPUSH_DBM; return(TRUE); } # ifdef PARANOID test = fetch(keydat); if (test.dptr==NULL) { logprintf("Key %s was not inserted into dbm file %s\n", key,map); *pushstat = YPPUSH_DBM; return(TRUE); } # endif PARANOID return(FALSE); } /* * This maps a YP_xxxx error code into a YPPUSH_xxxx error code */ int map_yperr_to_pusherr(yperr) int yperr; { int reason; switch (yperr) { case YP_NOMORE: reason = YPPUSH_SUCC; break; case YP_NOMAP: reason = YPPUSH_NOMAP; break; case YP_NODOM: reason = YPPUSH_NODOM; break; case YP_NOKEY: reason = YPPUSH_YPERR; break; case YP_BADARGS: reason = YPPUSH_BADARGS; break; case YP_BADDB: reason = YPPUSH_YPERR; break; default: reason = YPPUSH_XFRERR; break; } return(reason); } /* * This writes the last-modified and master entries into the new dbm file */ bool add_private_entries(pname) char *pname; { datum key; datum val; if (!fake_master_version) { key.dptr = yp_last_modified; key.dsize = sizeof (yp_last_modified) - 1; val.dptr = master_ascii_version; val.dsize = strlen(master_ascii_version); if (store(key, val) < 0) { logprintf( "Can't do dbm store into temp map %s.\n", pname); return (FALSE); } entry_count++; } if (master_name) { key.dptr = yp_master_name; key.dsize = sizeof (yp_master_name) - 1; val.dptr = master_name; val.dsize = strlen(master_name); if (store(key, val) < 0) { logprintf( "Can't do dbm store into temp map %s.\n", pname); return (FALSE); } entry_count++; } return (TRUE); } /* * This sends a YPPROC_CLEAR message to the local ypserv process. If the * local ypserv is a v.1 protocol guy, we'll just say we succeeded. Such a * situation is outlandish - why are they running the old ypserv at the same * place they are running ypxfr? And who are they, anyway? */ bool send_ypclear(pushstat) int *pushstat; { struct sockaddr_in myaddr; struct dom_binding domb; char local_host_name[256]; unsigned int progvers; get_myaddress(&myaddr); if (gethostname(local_host_name, 256)) { logprintf( "Can't get local machine name.\n"); *pushstat = YPPUSH_RSRC; return (FALSE); } if (!bind_to_server(local_host_name, myaddr.sin_addr, &domb, &progvers) ) { *pushstat = YPPUSH_CLEAR; return (FALSE); } if (progvers == YPOLDVERS) return (TRUE); if((enum clnt_stat) clnt_call(domb.dom_client, YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, udp_timeout) != RPC_SUCCESS) { logprintf( "Can't send ypclear message to ypserv on the local machine.\n"); xfr_exit(YPPUSH_CLEAR); } return (TRUE); } /* * This decides if send_callback has to get called, and does the process exit. */ void xfr_exit(status) int status; { if (callback) { send_callback(&status); } if (status == YPPUSH_SUCC) { exit(0); } else { exit(1); } } /* * This sets up a UDP connection to the yppush process which contacted our * parent ypserv, and sends him a status on the requested transfer. */ void send_callback(status) int *status; { struct yppushresp_xfr resp; struct dom_binding domb; resp.transid = (unsigned long) htonl(atoi(tid)); resp.status = (unsigned long) htonl(*status); domb.dom_server_addr.sin_addr.s_addr = inet_addr(ipadd); domb.dom_server_addr.sin_family = AF_INET; domb.dom_server_addr.sin_port = (unsigned short) htons(atoi(port)); domb.dom_server_port = domb.dom_server_addr.sin_port; domb.dom_socket = RPC_ANYSOCK; udp_intertry.tv_sec = CALLINTER_TRY; udp_timeout.tv_sec = CALLTIMEOUT; if ((domb.dom_client = clntudp_create(&(domb.dom_server_addr), (unsigned long) htons(atoi(proto)), YPPUSHVERS, udp_intertry, &(domb.dom_socket) ) ) == NULL) { *status = YPPUSH_RPC; return; } if((enum clnt_stat) clnt_call(domb.dom_client, YPPUSHPROC_XFRRESP, xdr_yppushresp_xfr, &resp, xdr_void, 0, udp_timeout) != RPC_SUCCESS) { *status = YPPUSH_RPC; return; } }