ypxfr.c revision a506a34ceb0e9dcc6c61bf0560202f8538928650
/*
* 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
* 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 2005 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
* under license from the Regents of the University of
* California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This is a user command which gets a NIS data base from some running
* server, and gets it to the local site by using the normal NIS 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> ]
* [ -s <domainname> ] [-f] [-c] [-C tid prot name] map
*
* If the host is ommitted, ypxfr will attempt to discover the master by
* using normal NIS 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 NIS 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 host "name".
*
* The -s option is used to specify a source domain which may be
* different from the destination domain, for transfer of maps
* that are identical in different domains (e.g. services.byname)
*
*/
#include <ndbm.h>
#define DATUM
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include <netdb.h>
#include <netconfig.h>
#include <netdir.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include "ypdefs.h"
#include "yp_b.h"
#include "shim.h"
#include "yptol.h"
/* ypxfr never uses N2L mode */
#define TREEPUSH 1
/*
* The name of the xfer peer as specified as a
* -h option, -C name option or from querying the NIS
*/
unsigned int master_prog_vers; /* YPVERS (barfs at YPOLDVERS !) */
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
*/
/*
* TRUE iff -C flag set. tid, proto and name
* will be set to point to the command line args.
*/
bool interdomain_map = FALSE;
/*
* TRUE if there is yp_interdomain in either
* the local or the master version of the map
*/
int interdomain_sz = 0; /* Size of the interdomain value */
char *interdomain_value; /* place to store the interdomain value */
char *tid;
char *proto;
int entry_count; /* counts entries in the map */
static char err_usage[] =
"Usage:\n\
ypxfr [-f] [ -h host ] [ -d domainname ]\n\
[ -s domainname ] [-c] [-C tid prot servname ] map\n\n\
where\n\
-f forces transfer even if the master's copy is not newer.\n\
host is the server from where the map should be transfered\n\
-d domainname is specified if other than the default domain\n\
-s domainname is a source for the map that is same across domains\n\
-c inhibits sending a \"Clear map\" message to the local ypserv.\n\
-C is for use only 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 yptempname_prefix[] = "ypxfr_map.";
char ypbkupname_prefix[] = "ypxfr_bkup.";
void get_command_line_args();
bool bind_to_server();
bool ping_server();
bool get_private_recs();
bool get_order();
bool get_v1order();
bool get_v2order();
bool get_misc_recs();
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 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 CLIENT *__yp_clnt_create_rsvdport();
extern int errno;
/*
* This is the mainline for the ypxfr process.
*/
int
int argc;
char **argv;
{
static char default_domain_name[YPMAXDOMAIN];
static unsigned big = 0xffffffff;
int status;
set_output();
/*
* Inter-process lock synchronization structure. Since slave servers
* get their maps from another NIS server rather than LDAP they can
* never run in N2L mode. We thus do not have to init the update
* locking mechanism.
*/
if (init_lock_map() == FALSE) {
exit(1);
}
if (!domain) {
} else {
}
}
}
if (!source)
if (!master) {
}
/*
* if we were unable to get the master name, either from
* the -h option or from -C "name" option or from NIS,
* we are doomed !
*/
if (!master) {
}
&master_prog_vers, &status)) {
}
if (!get_private_recs(&status)) {
}
if (!master_version) {
if (force) {
master_version = &big;
} else {
"Can't get order number for map %s from server at %s: use the -f flag.\n",
}
}
}
}
if (logging) {
logprintf("Transferred map %s from %s (%d entries).\n",
}
return (0);
/* NOTREACHED */
}
/*
* 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
{
if (!isatty(1)) {
} else {
}
}
}
/*
* This constructs a logging record.
*/
void
/*VARARGS*/
{
struct timeval t;
if (logging) {
(void) gettimeofday(&t, NULL);
}
}
/*
* This does the command line argument processing.
*/
void
int argc;
char **argv;
{
argv++;
if (argc < 2) {
}
while (--argc) {
if ((*argv)[0] == '-') {
switch ((*argv)[1]) {
case 'f': {
argv++;
break;
}
case 'D': {
argv++;
break;
}
case 'T': {
argv++;
break;
}
case 'P': {
check_count = TRUE;
argv++;
break;
}
case 'W': {
argv++;
break;
}
case 'c': {
send_clear = FALSE;
argv++;
break;
}
case 'h': {
if (argc > 1) {
argv++;
argc--;
argv++;
}
} else {
}
break;
}
case 'd':
if (argc > 1) {
argv++;
argc--;
argv++;
}
} else {
}
break;
case 's':
if (argc > 1) {
argv++;
argc--;
argv++;
}
} else {
}
break;
case 'C':
if (argc > 3) {
}
argc -= 3;
argv++;
} else {
}
break;
case 'b': {
interdomain_value = "";
interdomain_sz = 0;
argv++;
break;
}
default: {
}
}
} else {
if (!map) {
argv++;
}
} else {
}
}
}
if (!map) {
}
}
/*
* This tries to get the master name for the named map, from any
* server's version, using the vanilla NIS client interface. If we get a
* name back, the global "master" gets pointed to it.
*/
void
{
int err;
yperr_string(err));
}
}
#ifdef TREEPUSH
int
char *name;
{
char inmap[256];
char inkey[256];
int inkeylen;
char *outval;
int outvallen;
int err;
inkey[0] = 0;
return (err);
}
#endif
/*
* This sets up a udp connection to speak the correct program and version
* to a NIS server. vers is set to YPVERS, doesn't give a damn about
* YPOLDVERS.
*/
bool
char *host;
struct dom_binding *pdomb;
unsigned int *vers;
int *status;
{
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
char *host;
struct dom_binding *pdomb;
unsigned int vers;
int *status;
{
0, 0, 0)) != 0) {
/*
* if we are on a c2 system, we should only accept data
* from a server which is on a reserved port.
*/
/*
* NUKE this for 5.0DR.
*
* if (issecure() &&
* (pdomb->dom_server_addr.sin_family != AF_INET ||
* pdomb->dom_server_addr.sin_port >= IPPORT_RESERVED)) {
* clnt_destroy(pdomb->dom_client);
* close(pdomb->dom_socket);
* (void) logprintf("bind_to_server: \
* server is not using a privileged port\n");
* *status = YPPUSH_YPERR;
* return (FALSE);
* }
*/
if (rpc_stat == RPC_SUCCESS) {
return (TRUE);
} else {
if (rpc_stat != RPC_PROGVERSMISMATCH) {
"ypxfr: bind_to_server clnt_call error");
}
*status = YPPUSH_RPC;
return (FALSE);
}
} else {
logprintf("bind_to_server __clnt_create_rsvd error");
(void) clnt_pcreateerror("");
*status = YPPUSH_RPC;
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
int *pushstat;
{
static char anumber[20];
static unsigned number;
int status;
status = 0;
master_version = &number;
"ypxfr: Master Version is %s\n", master_ascii_version);
} else {
if (status != 0) {
"ypxfr: Couldn't get map's master version number, \
status was %d\n", status);
return (FALSE);
}
}
master_name = name;
"ypxfr: Maps master is '%s'\n", master_name);
} else {
if (status != 0) {
"ypxfr: Couldn't get map's master name, status was %d\n",
status);
return (FALSE);
}
}
if (debug)
"ypxfr: Getting private records from master.\n");
if (get_misc_recs(&status)) {
if (debug)
"ypxfr: Masters map %s secure and %s an interdomain map.\n",
} else {
if (status != 0) {
if (debug)
"ypxfr: Couldn't get state of secure and interdomain flags in map.\n");
return (FALSE);
}
}
return (TRUE);
}
/*
* This gets the map's order number from the master server
*/
bool
char *an;
unsigned *n;
int *pushstat;
{
if (master_prog_vers == YPVERS) {
} else
return (FALSE);
}
bool
char *an;
unsigned *n;
int *pushstat;
{
struct ypreq_nokey req;
struct ypresp_order resp;
int retval;
/*
* Get the map''s order number, null-terminate it and store it,
* and convert it to binary and store it again.
*/
udp_timeout) == RPC_SUCCESS) {
if (!logging) {
"(info) Can't get order number from ypserv at %s. Reason: %s.\n",
}
}
(char *)&resp);
} else {
*pushstat = YPPUSH_RPC;
}
return (retval);
}
/*
* Pick up the state of the YP_SECURE and YP_INTERDOMAIN records from the
* master. Only works on 4.0 V2 masters that will match a YP_ private key
* when asked to explicitly.
*/
bool
int *pushstat;
{
struct ypresp_val resp;
int retval;
/*
* Get the value of the IS_SECURE key in the map.
*/
if (debug)
udp_timeout) == RPC_SUCCESS) {
if (debug)
secure_map = TRUE;
if (!logging) {
"(info) Can't get secure flag from ypserv at %s. Reason: %s.\n",
}
}
(char *)&resp);
} else {
*pushstat = YPPUSH_RPC;
}
if (debug)
/*
* Get the value of the INTERDOMAIN key in the map.
*/
udp_timeout) == RPC_SUCCESS) {
if (debug)
if (!logging) {
"(info) Can't get interdomain flag from ypserv at %s. Reason: %s.\n",
}
}
(char *)&resp);
} else {
*pushstat = YPPUSH_RPC;
}
return (retval);
}
/*
* This gets the map's master name from the master server
*/
bool
char *name;
int *pushstat;
{
if (master_prog_vers == YPVERS) {
} else
return (FALSE);
}
bool
char *name;
int *pushstat;
{
struct ypreq_nokey req;
struct ypresp_master resp;
int retval;
udp_timeout) == RPC_SUCCESS) {
if (!logging) {
"(info) Can't get master name from ypserv at %s. Reason: %s.\n",
}
}
(char *)&resp);
} else {
*pushstat = YPPUSH_RPC;
"ypxfr(get_v2master_name) RPC call to %s failed", master);
}
return (retval);
}
/*
* This does the work of transferring the map.
*/
bool
int *pushstat;
{
unsigned local_version;
char an[11];
unsigned n;
int hgstatus;
if (!force) {
"ypxfr: Local version of map '%s' is %d\n",
if (local_version >= *master_version) {
"Map %s at %s is not more recent than local.\n",
*pushstat = YPPUSH_AGE;
return (FALSE);
}
}
if (!new_mapfiles(tmp_name)) {
"Can't create temp map %s.\n", tmp_name);
*pushstat = YPPUSH_FILE;
return (FALSE);
}
{
"(info) %s %s %s ypxfrd getdbm failed (reason = %d) -- using ypxfr\n",
"Can't dbm init temp map %s.\n", tmp_name);
*pushstat = YPPUSH_DBM;
return (FALSE);
}
return (FALSE);
}
if (!add_private_entries(tmp_name)) {
*pushstat = YPPUSH_DBM;
return (FALSE);
}
/*
* Decide whether the map just transferred is a secure map.
* If we already know the local version was secure, we do not
* need to check this version.
*/
if (!secure_map) {
secure_map = TRUE;
}
}
if (dbm_close_status(db) < 0) {
"Can't do dbm close operation on temp map %s.\n",
tmp_name);
*pushstat = YPPUSH_DBM;
return (FALSE);
}
return (FALSE);
}
if (n != *master_version) {
"Version skew at %s while transferring map %s.\n",
*pushstat = YPPUSH_SKEW;
return (FALSE);
}
if (check_count)
*pushstat = YPPUSH_DBM;
return (FALSE);
}
} else {
/* touch up the map */
"Can't dbm init temp map %s.\n", tmp_name);
*pushstat = YPPUSH_DBM;
return (FALSE);
}
if (!add_private_entries(tmp_name)) {
*pushstat = YPPUSH_DBM;
return (FALSE);
}
/*
* Decide whether the map just transferred is a secure map.
* If we already know the local version was secure, we do not
* need to check this version.
*/
if (!secure_map) {
secure_map = TRUE;
}
}
if (dbm_close_status(db) < 0) {
"Can't do dbm close operation on temp map %s.\n",
tmp_name);
*pushstat = YPPUSH_DBM;
return (FALSE);
}
}
*pushstat = YPPUSH_FILE;
return (FALSE);
}
if (!check_map_existence(map_name)) {
"Rename error: couldn't mv %s to %s.\n",
*pushstat = YPPUSH_FILE;
return (FALSE);
}
} else {
"Rename error: check that old %s is still intact.\n",
map_name);
*pushstat = YPPUSH_FILE;
return (FALSE);
}
} else {
"Rename error: check that old %s is still intact.\n",
map_name);
*pushstat = YPPUSH_FILE;
return (FALSE);
}
}
if (unlock_map(map_name) == 0)
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
char *name;
{
char number[11];
if (!check_map_existence(name)) {
return (0);
}
"ypxfr: Map does exist, checking version now.\n");
return (0);
}
return (0);
}
return (0);
}
/* Now save this value while we have it available */
/*
* Now check to see if it is 'secure'. If we haven't already
* determined that it is secure in get_private_recs() then we check
* the local map here.
*/
if (!secure_map) {
}
/*
* Now check to see if interdomain requests are made of the local
* map. Keep the value around if they are.
*/
if (!interdomain_map) {
}
}
/* finish up */
(void) dbm_close_status(db);
}
/*
* This constructs a file name for a map, minus its dbm_dir
* or dbm_pag extensions
*/
void
char *ppath;
{
int len;
/* Work out if we are in yptol mode */
yptol_mode = is_yptol_mode();
if (yptol_mode)
logprintf("Map name string too long.\n");
}
if (yptol_mode)
}
/*
* This returns a temporary name for a map transfer minus its dbm_dir or
* dbm_pag extensions.
*/
void
char *prefix;
char *xfr_name;
{
char xfr_anumber[10];
long xfr_number;
if (!xfr_name) {
return;
}
xfr_number = getpid();
}
/*
* 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
char *basename;
{
if (!basename) {
return;
}
}
/*
* This creates <pname>.dir and <pname>.pag
*/
bool
char *pname;
{
int f;
int len;
return (FALSE);
}
errno = 0;
(void) close(f);
0600)) >= 0) {
(void) close(f);
return (TRUE);
} else {
return (FALSE);
}
} else {
return (FALSE);
}
}
int
int status;
{
"Error from ypserv on %s (ypall_callback) = %s.\n",
}
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.
*/
int
char *pname;
int oldcount;
{
#ifdef REALLY_PARANOID
struct ypall_callback cbinfo;
struct ypreq_nokey allreq;
enum clnt_stat s;
struct dom_binding domb;
#endif /* REALLY_PARANOID */
entry_count = 0;
if (db) {
entry_count++;
}
if (oldcount != entry_count) {
"*** Count mismatch in dbm file %s: old=%d, new=%d ***\n",
return (FALSE);
}
#ifdef REALLY_PARANOID
"tcp6", 0, 0)) == 0 &&
"tcp", 0, 0)) == 0) {
clnt_pcreateerror("ypxfr (mismatch) - TCP channel "
"create failure");
return (FALSE);
}
if (master_prog_vers == YPVERS) {
int tmpstat;
tmpstat = 0;
entry_count = 0;
if (tmpstat == 0) {
if (s == RPC_SUCCESS) {
} else {
return (FALSE);
}
} else {
return (FALSE);
}
} else {
logprintf("Wrong version number!\n");
return (FALSE);
}
if (oldcount != entry_count) {
"*** Count mismatch after enumerate %s: old=%d, new=%d ***\n",
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
char *pname;
int *pushstat;
{
struct dom_binding domb;
enum clnt_stat s;
struct ypreq_nokey allreq;
struct ypall_callback cbinfo;
int tmpstat;
int fd;
if (! svcaddr)
return (FALSE);
return (FALSE);
}
for (i = 0; i <= lastnetid; i++) {
if (i != lastnetid)
continue;
logprintf("ypxfr: tcp transport not supported\n");
return (FALSE);
}
if (i != lastnetid)
continue;
return (FALSE);
}
if (i != lastnetid)
continue;
"ypxfr (get_map) - TCP channel create failure");
*pushstat = YPPUSH_RPC;
return (FALSE);
}
break;
}
entry_count = 0;
if (master_prog_vers == YPVERS) {
tmpstat = 0;
if (tmpstat == 0) {
if (s == RPC_SUCCESS) {
} else {
*pushstat = YPPUSH_RPC;
}
} else {
}
} else
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
int status;
char *key;
int kl;
char *val;
int vl;
int *pushstat;
{
"Error from ypserv on %s (ypall_callback) = %s.\n",
}
return (TRUE);
}
entry_count++;
/* way too many fetches */
#ifdef PARANOID
*pushstat = YPPUSH_DBM;
return (TRUE);
}
#endif /* PARANOID */
"Can't do dbm store into temp map %s.\n", map);
*pushstat = YPPUSH_DBM;
return (TRUE);
}
#ifdef PARANOID
logprintf("Key %s was not inserted into dbm file %s\n",
*pushstat = YPPUSH_DBM;
return (TRUE);
}
#endif /* PARANOID */
logprintf("Key %s dbm_error raised in file %s\n",
*pushstat = YPPUSH_DBM;
return (TRUE);
}
return (FALSE);
}
/*
* This maps a YP_xxxx error code into a YPPUSH_xxxx error code
*/
int
int yperr;
{
int reason;
switch (yperr) {
case YP_NOMORE:
break;
case YP_NOMAP:
break;
case YP_NODOM:
break;
case YP_NOKEY:
break;
case YP_BADARGS:
break;
case YP_BADDB:
break;
default:
break;
}
return (reason);
}
/*
* This writes the last-modified and master entries into the new dbm file
*/
bool
char *pname;
{
if (!fake_master_version) {
"Can't do dbm store into temp map %s.\n",
pname);
return (FALSE);
}
entry_count++;
}
if (master_name) {
"Can't do dbm store into temp map %s.\n",
pname);
return (FALSE);
}
entry_count++;
}
if (interdomain_map) {
"Can't do dbm store into temp map %s.\n",
pname);
return (FALSE);
}
entry_count++;
}
if (secure_map) {
"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.
*/
bool
int *pushstat;
{
struct dom_binding domb;
char local_host_name[256];
unsigned int progvers;
int status;
logprintf("Can't get local machine name.\n");
*pushstat = YPPUSH_RSRC;
return (FALSE);
}
*pushstat = YPPUSH_CLEAR;
return (FALSE);
}
udp_timeout) != RPC_SUCCESS) {
"Can't send ypclear message to ypserv on the local machine.\n");
}
return (TRUE);
}
/*
* This decides if send_callback has to get called, and does the process exit.
*/
void
int status;
{
if (callback) {
}
if (status == YPPUSH_SUCC) {
#ifdef TREEPUSH
if (treepush) {
if (debug)
perror("yppush");
}
#endif
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
int *status;
{
struct yppushresp_xfr resp;
struct dom_binding domb;
0, 0, 0)) == NULL) {
*status = YPPUSH_RPC;
return;
}
udp_timeout) != RPC_SUCCESS) {
*status = YPPUSH_RPC;
return;
}
}
/*
* 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.
*/
{
return (TRUE);
return (FALSE);
}