#include <NetInfo/config.h>
#include <NetInfo/project_version.h>
#include "ni_server.h"
#include <NetInfo/mm.h>
#include <NetInfo/system.h>
#include <NetInfo/system_log.h>
#include <NetInfo/network.h>
#include <NetInfo/systhread.h>
#include "ni_globals.h"
#include "ni_notify.h"
#include "ni_dir.h"
#include <NetInfo/socket_lock.h>
#include "alert.h"
#include "getstuff.h"
#include "proxy_pids.h"
#include <sys/socket.h>
#include "sanitycheck.h"
#include "multi_call.h"
#include "psauth.h"
#include "shauth.h"
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/dirent.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#ifdef _OS_NEXT_
#define _ni_ping_2_svc _ni_ping_2
#define _ni_statistics_2_svc _ni_statistics_2
#define _ni_root_2_svc _ni_root_2
#define _ni_self_2_svc _ni_self_2
#define _ni_parent_2_svc _ni_parent_2
#define _ni_children_2_svc _ni_children_2
#define _ni_create_2_svc _ni_create_2
#define _ni_destroy_2_svc _ni_destroy_2
#define _ni_read_2_svc _ni_read_2
#define _ni_write_2_svc _ni_write_2
#define _ni_lookup_2_svc _ni_lookup_2
#define _ni_lookupread_2_svc _ni_lookupread_2
#define _ni_list_2_svc _ni_list_2
#define _ni_listall_2_svc _ni_listall_2
#define _ni_readprop_2_svc _ni_readprop_2
#define _ni_writeprop_2_svc _ni_writeprop_2
#define _ni_listprops_2_svc _ni_listprops_2
#define _ni_createprop_2_svc _ni_createprop_2
#define _ni_destroyprop_2_svc _ni_destroyprop_2
#define _ni_renameprop_2_svc _ni_renameprop_2
#define _ni_createname_2_svc _ni_createname_2
#define _ni_writename_2_svc _ni_writename_2
#define _ni_readname_2_svc _ni_readname_2
#define _ni_destroyname_2_svc _ni_destroyname_2
#define _ni_bind_2_svc _ni_bind_2
#define _ni_rparent_2_svc _ni_rparent_2
#define _ni_crashed_2_svc _ni_crashed_2
#define _ni_readall_2_svc _ni_readall_2
#define _ni_resync_2_svc _ni_resync_2
#endif _OS_NEXT_
CLIENT *svctcp_getclnt(SVCXPRT *xprt);
#define MAXINTSTRSIZE sizeof("4294967296")
#define NI_SEPARATOR '/'
#ifdef _OS_APPLE_
#define PARENT_NINTERVALS 0
#else
#define PARENT_NINTERVALS 10
#endif
#define PARENT_SLEEPTIME 1
#define RPARENT_CATCH_SLEEP_SECONDS 2
#define PING_TIMEOUT_SEC 0
#define PING_TIMEOUT_USEC 500000
#define PING_CALL_SEC 5
#define PING_CALL_USEC 0
#define NETROOT_TIMEOUT_MINS 10
#define MAX_REBIND_ATTEMPTS 2
extern bool_t readall();
static int ni_ping(u_long, ni_name);
extern void setproctitle(char *fmt, ...);
extern int have_notifications_pending(void);
extern int ni_svc_connections;
extern int ni_svc_topconnections;
extern int ni_svc_maxconnections;
extern int ni_svc_lruclosed;
#define NAME_LOCALCONFIG "localconfig"
#define NAME_LANGUAGE "language"
#define KEY_PARENT_SERVER_ADDR "ServerAddresses"
#define KEY_PARENT_SERVER_TAG "ServerTags"
#define forever for(;;)
typedef enum
{
TokenNULL = 0,
TokenWord = 1,
TokenXMLArrayStart = 2,
TokenXMLArrayEnd = 3,
TokenXMLDictStart = 4,
TokenXMLDictEnd = 5,
TokenXMLKeyStart = 6,
TokenXMLKeyEnd = 7
} token_type_t;
typedef struct
{
token_type_t type;
char *value;
} token_t;
static int
isupdate(struct svc_req *req)
{
struct sockaddr_in *sin = svc_getcaller(req->rq_xprt);
if (req->rq_cred.oa_flavor == AUTH_UNIX) {
return (FALSE);
}
return (sin->sin_addr.s_addr == master_addr &&
ntohs(sin->sin_port) < IPPORT_RESERVED);
}
static bool_t
is_admin(void *ni, char *user)
{
ni_namelist nl;
ni_status status;
ni_id id;
ni_idlist idl;
int i;
if (ni == NULL) return FALSE;
if (user == NULL) return FALSE;
status = ni_root(ni, &id);
if (status != NI_OK) return FALSE;
NI_INIT(&idl);
status = ni_lookup(ni, &id, NAME_NAME, NAME_GROUPS, &idl);
if (status != NI_OK) return FALSE;
id.nii_object = idl.ni_idlist_val[0];
ni_idlist_free(&idl);
NI_INIT(&idl);
status = ni_lookup(ni, &id, NAME_NAME, NAME_ADMIN, &idl);
if (status != NI_OK) return FALSE;
id.nii_object = idl.ni_idlist_val[0];
ni_idlist_free(&idl);
NI_INIT(&nl);
status = ni_lookupprop(ni, &id, NAME_USERS, &nl);
if (status != NI_OK) return FALSE;
for (i = 0; i < nl.ni_namelist_len; i++)
{
if (!strcmp(user, nl.ni_namelist_val[i]))
{
ni_namelist_free(&nl);
return TRUE;
}
}
ni_namelist_free(&nl);
return FALSE;
}
static ni_status
authenticate(void *ni, struct svc_req *req)
{
struct sockaddr_in *sin = svc_getcaller(req->rq_xprt);
struct authunix_parms *aup;
char *p;
ni_namelist nl, name_nl, nl2;
ni_status status;
ni_id id;
ni_idlist idl;
char uidstr[MAXINTSTRSIZE];
int u, found, try_crypt, hash_check;
if (sys_is_my_address(&(sin->sin_addr)) && ntohs(sin->sin_port) < IPPORT_RESERVED)
{
ni_setuser(ni, ACCESS_USER_SUPER);
return NI_OK;
}
if (req->rq_cred.oa_flavor != AUTH_UNIX)
{
ni_setuser(ni, NULL);
return NI_OK;
}
aup = (struct authunix_parms *)req->rq_clntcred;
for (p = aup->aup_machname; *p; p++) *p = ~(*p);
status = ni_root(ni, &id);
if (status != NI_OK) return status;
NI_INIT(&idl);
status = ni_lookup(ni, &id, NAME_NAME, NAME_USERS, &idl);
if (status != NI_OK)
{
system_log(LOG_ERR,
"Cannot authenticate user %d from %s:%hu - no /%s "
"directory: %s", aup->aup_uid,
inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
NAME_USERS,
ni_error(status));
auth_count[BAD]++;
return ((status == NI_NODIR) ? NI_NOUSER : status);
}
id.nii_object = idl.ni_idlist_val[0];
ni_idlist_free(&idl);
sprintf(uidstr, "%d", aup->aup_uid);
NI_INIT(&idl);
status = ni_lookup(ni, &id, NAME_UID, uidstr, &idl);
if (status != NI_OK)
{
system_log(LOG_ERR, "Cannot find user %d from %s:%hu: %s",
aup->aup_uid,
inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
ni_error(status));
auth_count[BAD]++;
return ((status == NI_NODIR) ? NI_NOUSER : status);
}
found = 0;
for (u = 0; u < idl.ni_idlist_len; u++)
{
id.nii_object = idl.ni_idlist_val[u];
try_crypt = 0;
NI_INIT(&nl);
status = ni_lookupprop(ni, &id, NAME_AUTHENTICATION_AUTHORITY, &nl);
if ((status == NI_OK) && (nl.ni_namelist_len > 0))
{
if (CheckAuthType(nl.ni_namelist_val[0], BASIC_AUTH_TYPE))
{
try_crypt = 1;
}
else
{
NI_INIT(&name_nl);
status = ni_lookupprop(ni, &id, NAME_NAME, &name_nl);
if ((status == NI_OK) && (name_nl.ni_namelist_len > 0))
{
if (CheckAuthType(nl.ni_namelist_val[0], SHADOWHASH_AUTH_TYPE))
{
NI_INIT(&nl2);
status = ni_lookupprop(ni, &id, NAME_GUID, &nl2);
if ((status == NI_OK) && (nl2.ni_namelist_len > 0))
{
hash_check = DoSHAuth(name_nl.ni_namelist_val[0], aup->aup_machname, nl2.ni_namelist_val[0]);
ni_namelist_free(&nl2);
}
else
{
hash_check = DoSHAuth(name_nl.ni_namelist_val[0], aup->aup_machname, NULL);
}
if (hash_check == 0)
{
found = 1;
ni_namelist_free(&name_nl);
ni_namelist_free(&nl);
break;
}
ni_namelist_free(&name_nl);
}
else
{
if (DoPSAuth(name_nl.ni_namelist_val[0], aup->aup_machname, nl.ni_namelist_val[0]) == 0)
{
found = 1;
ni_namelist_free(&name_nl);
ni_namelist_free(&nl);
break;
}
ni_namelist_free(&name_nl);
}
}
}
ni_namelist_free(&nl);
}
else
{
try_crypt = 1;
}
if (try_crypt == 1)
{
NI_INIT(&nl);
status = ni_lookupprop(ni, &id, NAME_PASSWD, &nl);
if (status == NI_OK)
{
if ((nl.ni_namelist_len == 0) || (nl.ni_namelist_val[0][0] == '\0'))
{
found = 1;
ni_namelist_free(&nl);
break;
}
if (!strcmp(nl.ni_namelist_val[0], crypt(aup->aup_machname, nl.ni_namelist_val[0])) != 0)
{
found = 1;
ni_namelist_free(&nl);
break;
}
}
ni_namelist_free(&nl);
}
}
ni_idlist_free(&idl);
if (!found)
{
system_log(LOG_ERR, "Authentication error for user "
"%d from %s:%hu",
aup->aup_uid,
inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
ni_error(status));
auth_count[BAD]++;
return NI_AUTHERROR;
}
NI_INIT(&nl);
status = ni_lookupprop(ni, &id, NAME_NAME, &nl);
if (status != NI_OK)
{
system_log(LOG_ERR,
"User %d from %s:%hu - name prop not found during "
"authentication",
aup->aup_uid,
inet_ntoa(sin->sin_addr), ntohs(sin->sin_port),
ni_error(status));
auth_count[BAD]++;
return ((status == NI_NOPROP) ? NI_NOUSER : status);
}
if (nl.ni_namelist_len == 0)
{
system_log(LOG_ERR,
"User %d from %s:%hu - name value not found during "
"authentication",
aup->aup_uid,
inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
auth_count[BAD]++;
return NI_NOUSER;
}
promote_admins = get_promote_admins(ni);
if (aup->aup_uid == 0)
{
ni_setuser(ni, ACCESS_USER_SUPER);
}
else if (promote_admins && is_admin(ni, nl.ni_namelist_val[0]))
{
ni_setuser(ni, ACCESS_USER_SUPER);
}
else
{
ni_setuser(ni, nl.ni_namelist_val[0]);
}
auth_count[GOOD]++;
system_log(LOG_NOTICE,
"Authenticated user %s [%d] from %s:%hu",
nl.ni_namelist_val[0], aup->aup_uid,
inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
ni_namelist_free(&nl);
return NI_OK;
}
static ni_status
validate_write(struct svc_req *req)
{
ni_status status;
status = NI_OK;
ni_setuser(db_ni, NULL);
if (i_am_clone)
{
if (!isupdate(req)) status = NI_RDONLY;
else ni_setuser(db_ni, ACCESS_USER_SUPER);
}
else
{
status = authenticate(db_ni, req);
}
if ((sending_all > 0) || db_lockup) {
status = NI_MASTERBUSY;
}
return (status);
}
static void
add_peer_servers(ni_entry entry, ni_namelist *sl)
{
ni_name sep;
ni_namelist nl;
ni_index i, j, taglen;
ni_id id;
char *s;
if (entry.names == NULL) return;
if (sl == NULL) return;
id.nii_object = entry.id;
for (i = 0; i < entry.names->ni_namelist_len; i++)
{
if (strncmp(entry.names->ni_namelist_val[i], "./", 2)) continue;
sep = entry.names->ni_namelist_val[i] + 1;
taglen = strlen(sep);
if (taglen == 0) continue;
NI_INIT(&nl);
if (ni_lookupprop(db_ni, &id, NAME_IP_ADDRESS, &nl) != NI_OK) continue;
for (j = 0; j < nl.ni_namelist_len; j++)
{
s = malloc(strlen(nl.ni_namelist_val[j]) + taglen + 1);
sprintf(s, "%s%s", nl.ni_namelist_val[j], sep);
ni_namelist_insert(sl, s, NI_INDEX_NULL);
free(s);
}
ni_namelist_free(&nl);
}
}
static ni_status
get_servers_from_db(void *ni, ni_namelist *sl)
{
ni_id id;
ni_idlist ids;
ni_entrylist entries;
int i;
ni_status status;
status = ni_root(ni, &id);
if (status != NI_OK) return status;
NI_INIT(&ids);
status = ni_lookup(ni, &id, NAME_NAME, NAME_MACHINES, &ids);
if (status != NI_OK) return status;
id.nii_object = ids.ni_idlist_val[0];
ni_idlist_free(&ids);
NI_INIT(&entries);
status = ni_list_const(ni, &id, NAME_SERVES, &entries);
if (status != NI_OK) return status;
for (i = 0; i < entries.ni_entrylist_len; i++)
{
add_peer_servers(entries.ni_entrylist_val[i], sl);
}
ni_list_const_free(ni);
return NI_OK;
}
void *
_ni_ping_2_svc(
void *arg,
struct svc_req *req
)
{
if (req == NULL) return ((void *)~0);
return ((void *)~0);
}
ni_proplist *
_ni_statistics_2_svc(
void *arg,
struct svc_req *req
)
{
extern struct ni_stats
{
unsigned long ncalls;
unsigned long time;
} netinfod_stats[];
#define STATS_PROCNUM 0
#define STATS_NCALLS 1
#define STATS_TIME 2
#define N_STATS_VALS (STATS_TIME+1)
int total_calls = 0;
interface_list_t *ifaces;
struct timeval now;
extern char *procname(int);
static int props_sizes[] =
{
MAXINTSTRSIZE,
128,
NI_NAME_MAXLEN+1,
16,
MAXHOSTNAMELEN+1,
16+1+MAXNAMLEN+1,
MAXINTSTRSIZE+17,
MAXINTSTRSIZE,
MAXINTSTRSIZE,
MAXINTSTRSIZE,
MAXINTSTRSIZE,
MAXINTSTRSIZE,
MAXINTSTRSIZE,
16+1+MAXNAMLEN+1,
MAXINTSTRSIZE
};
#define wProps(i, fmt, val) wPropsN(i,0, fmt, val)
#define wPropsN(i, j, fmt, val) sprintf(props[i].nip_val.ni_namelist_val[j], fmt, val)
static ni_property props[] =
{
{"checksum", {1, NULL}},
#define P_CHECKSUM 0
{"server_version", {1, NULL}},
#define P_VERSION 1
{"tag", {3, NULL}},
#define P_TAG 2
#define STATS_TAG 0
#define STATS_MASTER 1
#define STATS_NCLONES 2
{"ip_address", {16, NULL}},
#define P_ADDR 3
{"hostname", {1, NULL}},
#define P_HOST 4
{"domain_servers", {0, NULL}},
#define P_SERVERS 5
{"write_locked", {1, NULL}},
#define P_LOCKED 6
#define SENDING_ALL1_STG "Yes (a readall)"
#define SENDING_ALLn_STG "Yes (%u readalls)"
#define READING_ALL_STG "clone%s"
{"notify_threads", {3, NULL}},
#define P_THREADS 7
#define STATS_THREADS_USED 0
#define STATS_THREADS_MAX 1
#define STATS_THREADS_LATENCY 2
{"notifications_pending", {1, NULL}},
#define P_PENDING 8
{"authentications", {4, NULL}},
#define P_AUTHS 9
{"readall_proxies", {2, NULL}},
#define P_PROXIES 10
#define STATS_PROXIES 0
#define STATS_PROXIES_STRICT 1
{"cleanup_wait", {2, NULL}},
#define P_CLEANUP 11
#define STATS_CLEANUPTIME 0
#define STATS_CLEANUP_TOGO 1
{"total_calls", {1, NULL}},
#define P_CALLS 12
{"binding", {1, NULL}},
#define P_BINDING 13
{"connections", {4, NULL}},
#define P_CONNECTIONS 14
#define STATS_CONNECTIONS_USED 0
#define STATS_CONNECTIONS_TOP 1
#define STATS_CONNECTIONS_MAX 2
#define STATS_CONNECTIONS_CLOSED 3
#define PROC_STATS_START 15
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}},
{NULL, {N_STATS_VALS, NULL}}
};
int i, j;
unsigned current_checksum;
static unsigned char first_time = TRUE;
static unsigned last_checksum = 0;
static ni_proplist res;
if (req == NULL) return NULL;
if (first_time)
{
first_time = FALSE;
for (i = 0; i < PROC_STATS_START; i++)
{
if (i == P_SERVERS) continue;
props[i].nip_val.ni_namelist_val = (ni_name *)malloc(props[i].nip_val.ni_namelist_len * sizeof(ni_name));
if (props[i].nip_val.ni_namelist_val == NULL)
{
system_log(LOG_ALERT, "Couldn't allocate memory for statistics");
system_log(LOG_ALERT, "Aborting!");
abort();
}
for (j = 0; j < props[i].nip_val.ni_namelist_len; j++)
{
props[i].nip_val.ni_namelist_val[j] = (ni_name)malloc(props_sizes[i]);
if (props[i].nip_val.ni_namelist_val[j] == NULL)
{
system_log(LOG_ALERT, "Couldn't allocate memory for statistics");
system_log(LOG_ALERT, "Aborting!");
abort();
}
}
}
for (i = PROC_STATS_START; i <= PROC_STATS_START+_NI_LOOKUPREAD; i++)
{
props[i].nip_name = procname(i - PROC_STATS_START);
props[i].nip_val.ni_namelist_val = (ni_name *)malloc(sizeof(ni_name) * N_STATS_VALS);
if (props[i].nip_val.ni_namelist_val == NULL)
{
system_log(LOG_ALERT, "Couldn't allocate memory for statistics");
system_log(LOG_ALERT, "Aborting!");
abort();
}
for (j = 0; j < N_STATS_VALS; j++)
{
props[i].nip_val.ni_namelist_val[j] = (ni_name)malloc(MAXINTSTRSIZE+1);
if (props[i].nip_val.ni_namelist_val[j] == NULL)
{
system_log(LOG_ALERT, "Couldn't allocate memory for statistics");
system_log(LOG_ALERT, "Aborting!");
abort();
}
}
wPropsN(i, STATS_PROCNUM, "%u", i - PROC_STATS_START);
}
wProps(P_VERSION, "%s", _PROJECT_VERSION_);
wPropsN(P_TAG, STATS_TAG, "%s", db_tag);
if (!i_am_clone)
{
wPropsN(P_TAG, STATS_MASTER, "%s", "master");
props[P_TAG].nip_val.ni_namelist_len = 3;
}
else
{
wPropsN(P_TAG, STATS_MASTER, "%s", "clone");
props[P_TAG].nip_val.ni_namelist_len = 2;
}
wProps(P_HOST, "%s", sys_hostname());
}
current_checksum = ni_getchecksum(db_ni);
wProps(P_CHECKSUM, "%u", current_checksum);
if (req->rq_cred.oa_flavor == AUTH_UNIX)
{
struct authunix_parms *aup;
char *p;
aup = (struct authunix_parms *)req->rq_clntcred;
for (p = aup->aup_machname; *p; p++) *p = ~(*p);
if (strcmp(aup->aup_machname, "checksum") == 0)
{
res.ni_proplist_len = 1;
res.ni_proplist_val = props;
return (&res);
}
}
if (!i_am_clone)
{
wPropsN(P_TAG, STATS_NCLONES, "%d", count_clones());
}
ifaces = sys_interfaces();
for (i = 0, j = 0; ifaces && (i < ifaces->count) && (j < 16); i++)
{
if ((ifaces->interface[i].flags & IFF_UP) == 0) continue;
if (ifaces->interface[i].flags & IFF_LOOPBACK) continue;
wPropsN(P_ADDR, j++, "%s", inet_ntoa(ifaces->interface[i].addr));
}
sys_interfaces_release(ifaces);
if (j == 0)
{
props[P_ADDR].nip_val.ni_namelist_len = 1;
wPropsN(P_ADDR, j++, "%s", "0.0.0.0");
}
props[P_ADDR].nip_val.ni_namelist_len = j;
if (current_checksum != last_checksum)
{
ni_namelist_free(&(props[P_SERVERS].nip_val));
get_servers_from_db(db_ni, &(props[P_SERVERS].nip_val));
last_checksum = current_checksum;
}
if (i_am_clone)
{
wProps(P_LOCKED, READING_ALL_STG, reading_all ? " (reading all)" : "");
}
else if (sending_all > 0)
{
wProps(P_LOCKED, 1 == sending_all ? SENDING_ALL1_STG : SENDING_ALLn_STG, sending_all);
}
else if (db_lockup)
{
wProps(P_LOCKED, "%s", "Yes (due to SIGINT)");
}
else
{
wProps(P_LOCKED, "%s", "No");
}
wPropsN(P_THREADS, STATS_THREADS_USED, "%u", count_notify_subthreads());
wPropsN(P_THREADS, STATS_THREADS_MAX, "%u", max_subthreads);
wPropsN(P_THREADS, STATS_THREADS_LATENCY, "%u", update_latency_secs);
wProps(P_PENDING, "%u", notifications_pending());
wPropsN(P_AUTHS, GOOD, "%u", auth_count[GOOD]);
wPropsN(P_AUTHS, BAD, "%u", auth_count[BAD]);
wPropsN(P_AUTHS, WGOOD, "%u", auth_count[WGOOD]);
wPropsN(P_AUTHS, WBAD, "%u", auth_count[WBAD]);
wPropsN(P_PROXIES, STATS_PROXIES, "%d", max_readall_proxies);
wPropsN(P_PROXIES, STATS_PROXIES_STRICT, "%s", strict_proxies ? "strict" : "loose");
gettimeofday(&now, NULL);
wPropsN(P_CLEANUP, STATS_CLEANUPTIME, "%d", cleanupwait/60);
#ifdef _OS_NEXT_
wPropsN(P_CLEANUP, STATS_CLEANUP_TOGO, "%ld", cleanupwait < 0 ? -1 : (cleanuptime - now.tv_sec)/60);
#else
wPropsN(P_CLEANUP, STATS_CLEANUP_TOGO, "%d", cleanupwait < 0 ? -1 : (cleanuptime - now.tv_sec)/60);
#endif
if (forcedIsRoot)
{
wProps(P_BINDING, "%s", "forcedRoot");
}
else
{
switch (get_binding_status())
{
case NI_NORESPONSE:
wProps(P_BINDING, "%s", "notResponding");
break;
case NI_NETROOT:
wProps(P_BINDING, "%s", "root");
break;
case NI_OK:
wProps(P_BINDING, "%s", latestParentInfo);
break;
case NI_NOTMASTER:
default:
wProps(P_BINDING, "%s", "unknown");
break;
}
}
wPropsN(P_CONNECTIONS, STATS_CONNECTIONS_USED, "%d", ni_svc_connections);
wPropsN(P_CONNECTIONS, STATS_CONNECTIONS_TOP, "%d", ni_svc_topconnections);
wPropsN(P_CONNECTIONS, STATS_CONNECTIONS_MAX, "%d", ni_svc_maxconnections);
wPropsN(P_CONNECTIONS, STATS_CONNECTIONS_CLOSED, "%d", ni_svc_lruclosed);
for (i = PROC_STATS_START; i <= PROC_STATS_START + _NI_LOOKUPREAD; i++)
{
wPropsN(i, STATS_NCALLS, "%lu", netinfod_stats[i - PROC_STATS_START].ncalls);
wPropsN(i, STATS_TIME, "%lu", netinfod_stats[i - PROC_STATS_START].time);
total_calls += netinfod_stats[i - PROC_STATS_START].ncalls;
}
wProps(P_CALLS, "%u", total_calls);
res.ni_proplist_len = PROC_STATS_START+(_NI_LOOKUPREAD+1);
res.ni_proplist_val = props;
return (&res);
}
ni_id_res *
_ni_root_2_svc(
void *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = ni_root(db_ni, &res.ni_id_res_u.id);
return (&res);
}
ni_id_res *
_ni_self_2_svc(
ni_id *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.ni_id_res_u.id = *arg;
res.status = ni_self(db_ni, &res.ni_id_res_u.id);
return (&res);
}
ni_parent_res *
_ni_parent_2_svc(
ni_id *arg,
struct svc_req *req
)
{
static ni_parent_res res;
if (req == NULL) {
return (NULL);
}
res.ni_parent_res_u.stuff.self_id = *arg;
res.status = ni_parent(db_ni, &res.ni_parent_res_u.stuff.self_id,
&res.ni_parent_res_u.stuff.object_id);
return (&res);
}
ni_children_res *
_ni_children_2_svc(
ni_id *arg,
struct svc_req *req
)
{
static ni_children_res res;
ni_idlist_free(&res.ni_children_res_u.stuff.children);
if (req == NULL) {
return (NULL);
}
res.ni_children_res_u.stuff.self_id = *arg;
res.status = ni_children(db_ni, &res.ni_children_res_u.stuff.self_id,
&res.ni_children_res_u.stuff.children);
return (&res);
}
ni_create_res *
_ni_create_2_svc(
ni_create_args *arg,
struct svc_req *req
)
{
static ni_create_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
if (arg->target_id != NULL) {
res.ni_create_res_u.stuff.id = *arg->target_id;
} else {
res.ni_create_res_u.stuff.id.nii_object = NI_INDEX_NULL;
}
res.ni_create_res_u.stuff.self_id = arg->id;
res.status = ni_create(db_ni, &res.ni_create_res_u.stuff.self_id,
arg->props, &res.ni_create_res_u.stuff.id,
arg->where);
if (res.status == NI_OK) {
if (!i_am_clone) {
if (arg->target_id == NULL) {
MM_ALLOC(arg->target_id);
*arg->target_id = res.ni_create_res_u.stuff.id;
}
notify_clients(_NI_CREATE, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_id_res *
_ni_destroy_2_svc(
ni_destroy_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->parent_id;
res.status = ni_destroy(db_ni, &res.ni_id_res_u.id, arg->self_id);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_DESTROY, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_proplist_res *
_ni_read_2_svc(
ni_id *arg,
struct svc_req *req
)
{
static ni_proplist_res res;
ni_proplist_free(&res.ni_proplist_res_u.stuff.props);
if (req == NULL) {
return (NULL);
}
res.ni_proplist_res_u.stuff.id = *arg;
res.status = ni_read(db_ni, &res.ni_proplist_res_u.stuff.id,
&res.ni_proplist_res_u.stuff.props);
return (&res);
}
ni_id_res *
_ni_write_2_svc(
ni_proplist_stuff *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_write(db_ni, &res.ni_id_res_u.id, arg->props);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_WRITE, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_lookup_res *
_ni_lookup_2_svc(
ni_lookup_args *arg,
struct svc_req *req
)
{
static ni_lookup_res res;
ni_idlist_free(&res.ni_lookup_res_u.stuff.idlist);
if (req == NULL) {
return (NULL);
}
res.ni_lookup_res_u.stuff.self_id = arg->id;
NI_INIT(&res.ni_lookup_res_u.stuff.idlist);
res.status = ni_lookup(db_ni, &res.ni_lookup_res_u.stuff.self_id,
arg->key, arg->value,
&res.ni_lookup_res_u.stuff.idlist);
return (&res);
}
ni_proplist_res *
_ni_lookupread_2_svc(
ni_lookup_args *arg,
struct svc_req *req
)
{
static ni_proplist_res res;
ni_proplist_free(&res.ni_proplist_res_u.stuff.props);
if (req == NULL) {
return (NULL);
}
res.ni_proplist_res_u.stuff.id = arg->id;
res.status = ni_lookupread(db_ni,
&res.ni_proplist_res_u.stuff.id,
arg->key, arg->value,
&res.ni_proplist_res_u.stuff.props);
return (&res);
}
ni_list_res *
_ni_list_2_svc(
ni_name_args *arg,
struct svc_req *req
)
{
static ni_list_res res;
if (req == NULL) {
ni_list_const_free(db_ni);
return (NULL);
}
res.ni_list_res_u.stuff.self_id = arg->id;
res.status = ni_list_const(db_ni, &res.ni_list_res_u.stuff.self_id,
arg->name,
&res.ni_list_res_u.stuff.entries);
return (&res);
}
ni_listall_res *
_ni_listall_2_svc(
ni_id *id,
struct svc_req *req
)
{
static ni_listall_res res;
ni_proplist_list_free(&res.ni_listall_res_u.stuff.entries);
if (req == NULL) {
return (NULL);
}
res.ni_listall_res_u.stuff.self_id = *id;
res.status = ni_listall(db_ni, &res.ni_listall_res_u.stuff.self_id,
&res.ni_listall_res_u.stuff.entries);
return (&res);
}
ni_namelist_res *
_ni_readprop_2_svc(
ni_prop_args *arg,
struct svc_req *req
)
{
static ni_namelist_res res;
ni_namelist_free(&res.ni_namelist_res_u.stuff.values);
if (req == NULL) {
return (NULL);
}
res.ni_namelist_res_u.stuff.self_id = arg->id;
res.status = ni_readprop(db_ni, &res.ni_namelist_res_u.stuff.self_id,
arg->prop_index,
&res.ni_namelist_res_u.stuff.values);
return (&res);
}
ni_id_res *
_ni_writeprop_2_svc(
ni_writeprop_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_writeprop(db_ni, &res.ni_id_res_u.id,
arg->prop_index,
arg->values);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_WRITEPROP, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_namelist_res *
_ni_listprops_2_svc(
ni_id *arg,
struct svc_req *req
)
{
static ni_namelist_res res;
ni_namelist_free(&res.ni_namelist_res_u.stuff.values);
if (req == NULL) {
return (NULL);
}
res.ni_namelist_res_u.stuff.self_id = *arg;
res.status = ni_listprops(db_ni, &res.ni_namelist_res_u.stuff.self_id,
&res.ni_namelist_res_u.stuff.values);
return (&res);
}
ni_id_res *
_ni_createprop_2_svc(
ni_createprop_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_createprop(db_ni, &res.ni_id_res_u.id, arg->prop, arg->where);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_CREATEPROP, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_id_res *
_ni_destroyprop_2_svc(
ni_prop_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_destroyprop(db_ni, &res.ni_id_res_u.id, arg->prop_index);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_DESTROYPROP, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_id_res *
_ni_renameprop_2_svc(
ni_propname_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_renameprop(db_ni, &res.ni_id_res_u.id,
arg->prop_index, arg->name);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_RENAMEPROP, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_id_res *
_ni_createname_2_svc(
ni_createname_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_createname(db_ni, &res.ni_id_res_u.id, arg->prop_index, arg->name,
arg->where);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_CREATENAME, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_id_res *
_ni_writename_2_svc(
ni_writename_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_writename(db_ni, &res.ni_id_res_u.id,
arg->prop_index, arg->name_index,
arg->name);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_WRITENAME, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
ni_readname_res *
_ni_readname_2_svc(
ni_nameindex_args *arg,
struct svc_req *req
)
{
static ni_readname_res res;
ni_name_free(&res.ni_readname_res_u.stuff.name);
if (req == NULL) {
return (NULL);
}
res.ni_readname_res_u.stuff.id = arg->id;
res.status = ni_readname(db_ni, &res.ni_readname_res_u.stuff.id,
arg->prop_index, arg->name_index,
&res.ni_readname_res_u.stuff.name);
return (&res);
}
ni_id_res *
_ni_destroyname_2_svc(
ni_nameindex_args *arg,
struct svc_req *req
)
{
static ni_id_res res;
if (req == NULL) {
return (NULL);
}
res.status = validate_write(req);
if (res.status != NI_OK) {
return (&res);
}
res.ni_id_res_u.id = arg->id;
res.status = ni_destroyname(db_ni, &res.ni_id_res_u.id,
arg->prop_index, arg->name_index);
if (res.status == NI_OK) {
if (!i_am_clone) {
notify_clients(_NI_DESTROYNAME, arg);
}
} else if (i_am_clone) {
dir_clonecheck();
}
return (&res);
}
static int
tag_match(
ni_name slashtag,
ni_name tag
)
{
ni_name sep;
int len;
sep = index(slashtag, NI_SEPARATOR);
if (sep == NULL) {
return (0);
}
if (!ni_name_match(sep + 1, tag)) {
return (0);
}
len = sep - slashtag;
if (ni_name_match_seg(NAME_DOT, slashtag, len) ||
ni_name_match_seg(NAME_DOTDOT, slashtag, len)) {
return (0);
}
return (1);
}
void *
_ni_bind_2_svc(
ni_binding *binding,
struct svc_req *req
)
{
ni_id id;
ni_idlist ids;
ni_namelist nl;
ni_index i;
ni_name address;
struct in_addr inaddr;
ni_status status;
if (req == NULL) return (NULL);
if (db_ni == NULL)
{
svcerr_systemerr(req->rq_xprt);
return (NULL);
}
if (ni_root(db_ni, &id) != NI_OK)
{
svcerr_systemerr(req->rq_xprt);
return (NULL);
}
NI_INIT(&ids);
status = ni_lookup(db_ni, &id, NAME_NAME, NAME_MACHINES, &ids);
if (status!= NI_OK)
{
svcerr_systemerr(req->rq_xprt);
return (NULL);
}
id.nii_object = ids.ni_idlist_val[0];
ni_idlist_free(&ids);
inaddr.s_addr = binding->addr;
address = inet_ntoa(inaddr);
NI_INIT(&ids);
status = ni_lookup(db_ni, &id, NAME_IP_ADDRESS, address, &ids);
if (status != NI_OK)
{
svcerr_systemerr(req->rq_xprt);
return (NULL);
}
id.nii_object = ids.ni_idlist_val[0];
ni_idlist_free(&ids);
NI_INIT(&nl);
status = ni_lookupprop(db_ni, &id, NAME_SERVES, &nl);
if (status != NI_OK)
{
svcerr_systemerr(req->rq_xprt);
return (NULL);
}
for (i = 0; i < nl.ni_namelist_len; i++)
{
if (tag_match(nl.ni_namelist_val[i], binding->tag))
{
ni_namelist_free(&nl);
return ((void *)~0);
}
}
ni_namelist_free(&nl);
svcerr_systemerr(req->rq_xprt);
return (NULL);
}
typedef struct ni_rparent_stuff {
nibind_bind_args *bindings;
ni_rparent_res *res;
} ni_rparent_stuff;
static bool_t
catch(void *vstuff, struct sockaddr_in *raddr, int which)
{
ni_rparent_stuff *stuff = (ni_rparent_stuff *)vstuff;
ni_name_free(&stuff->res->ni_rparent_res_u.binding.tag);
stuff->res->ni_rparent_res_u.binding.tag = ni_name_dup(stuff->bindings[which].server_tag);
stuff->res->ni_rparent_res_u.binding.addr = raddr->sin_addr.s_addr;
stuff->res->status = NI_OK;
sleep(RPARENT_CATCH_SLEEP_SECONDS);
return (TRUE);
}
static unsigned
servesdotdot(ni_entry entry, ni_name *tag)
{
ni_name name;
ni_name sep;
unsigned addr;
ni_namelist nl;
ni_index i;
ni_id id;
if (entry.names == NULL || forcedIsRoot) return (0);
id.nii_object = entry.id;
for (i = 0; i < entry.names->ni_namelist_len; i++)
{
name = entry.names->ni_namelist_val[i];
sep = index(name, NI_SEPARATOR);
if (sep == NULL) continue;
if (!ni_name_match_seg(NAME_DOTDOT, name, sep - name)) continue;
NI_INIT(&nl);
if (ni_lookupprop(db_ni, &id, NAME_IP_ADDRESS, &nl) != NI_OK) continue;
if (nl.ni_namelist_len == 0) continue;
addr = inet_addr(nl.ni_namelist_val[0]);
ni_namelist_free(&nl);
*tag = ni_name_dup(sep + 1);
return (addr);
}
return (0);
}
static void
add_binding_entry(struct in_addr sa, ni_name st, struct in_addr ca, ni_name ct,
struct in_addr **addrs, ni_rparent_stuff *stuff, unsigned int *naddrs)
{
char astr[32], cstr[32];
sprintf(astr, "%s", inet_ntoa(sa));
sprintf(cstr, "%s", inet_ntoa(ca));
system_log(LOG_DEBUG, "binding list added %s/%s (%s/%s)", astr, st, cstr, ct);
MM_GROW_ARRAY(*addrs, *naddrs);
(*addrs)[*naddrs].s_addr = sa.s_addr;
MM_GROW_ARRAY(stuff->bindings, *naddrs);
stuff->bindings[*naddrs].client_tag = ni_name_dup(ct);
stuff->bindings[*naddrs].client_addr = ntohl(ca.s_addr);
stuff->bindings[*naddrs].server_tag = ni_name_dup(st);
*naddrs = *naddrs + 1;
}
static void
add_broadcast_binding(ni_name server_tag, ni_name client_tag,
struct in_addr **addrs, ni_rparent_stuff *stuff, unsigned int *naddrs)
{
interface_list_t *l;
int i;
l = sys_interfaces();
if (l == NULL) return;
for (i = 0; i < l->count; i++)
{
if ((l->interface[i].flags & IFF_UP) == 0) continue;
if (l->interface[i].flags & IFF_LOOPBACK)
{
if (l->count == 1)
{
add_binding_entry(l->interface[i].addr, server_tag, l->interface[i].addr, client_tag, addrs, stuff, naddrs);
}
}
else
{
add_binding_entry(l->interface[i].bcast, server_tag, l->interface[i].addr, client_tag, addrs, stuff, naddrs);
}
}
sys_interfaces_release(l);
}
static void
add_hardwired_binding(struct in_addr server_addr, ni_name server_tag, ni_name client_tag, struct in_addr **addrs, ni_rparent_stuff *stuff, unsigned int *naddrs)
{
interface_list_t *l;
int i;
l = sys_interfaces();
if (l == NULL) return;
for (i = 0; i < l->count; i++)
{
if ((l->interface[i].flags & IFF_UP) == 0) continue;
if ((l->interface[i].flags & IFF_LOOPBACK) && (l->count > 1)) continue;
add_binding_entry(server_addr, server_tag, l->interface[i].addr, client_tag, addrs, stuff, naddrs);
}
sys_interfaces_release(l);
}
static void
freeToken(token_t *t)
{
if (t == NULL) return;
if (t->value != NULL) free(t->value);
t->value = NULL;
t->type = TokenNULL;
free(t);
}
static token_t *
get_token_1(FILE *fp, int xword)
{
token_t *t, *s;
char c;
static char x = EOF;
int i, run, len, xml;
t = (token_t *)malloc(sizeof(token_t));
t->type = TokenNULL;
t->value = NULL;
len = 0;
if (xword == 1)
{
run = 1;
while (run == 1)
{
c = x;
x = EOF;
if (c == EOF) c = getc(fp);
if (c == EOF) return t;
if (c == '<')
{
x = c;
break;
}
if (len == 0) t->value = malloc(1);
t->value[len++] = c;
t->value = realloc(t->value, len + 1);
t->value[len] = '\0';
}
if (t->value == NULL) return t;
s = (token_t *)malloc(sizeof(token_t));
s->type = TokenWord;
s->value = NULL;
len = 0;
for (i = 0; t->value[i] != '\0'; i++)
{
c = t->value[i];
if (c == '&')
{
if (!strncmp(t->value + i, "&", 5))
{
c = '&';
i += 4;
}
else if (!strncmp(t->value + i, "<", 4))
{
c = '<';
i += 3;
}
else if (!strncmp(t->value + i, ">", 4))
{
c = '>';
i += 3;
}
}
if (len == 0) s->value = malloc(1);
s->value[len++] = c;
s->value = realloc(s->value, len + 1);
s->value[len] = '\0';
}
freeToken(t);
return s;
}
run = 1;
while (run == 1)
{
c = x;
x = EOF;
if (c == EOF) c = getc(fp);
if (c == EOF) return t;
if (c == ' ') continue;
if (c == '\t') continue;
if (c == '\n') continue;
run = 0;
}
xml = 0;
if (c == '<') xml = 1;
len = 1;
t->value = malloc(len + 1);
t->value[0] = c;
t->value[len] = '\0';
t->type = TokenWord;
run = 1;
while (run == 1)
{
c = getc(fp);
if (c == EOF) return t;
if (c == '<')
{
x = c;
return t;
}
if ((xml == 1) && (c == '>')) run = 0;
t->value[len++] = c;
t->value = realloc(t->value, len + 1);
t->value[len] = '\0';
}
return t;
}
static token_t *
pop_token(FILE *fp)
{
token_t *t, *s;
t = get_token_1(fp, 0);
if (t->type != TokenWord) return t;
if (!strcmp(t->value, "<string>"))
{
freeToken(t);
t = get_token_1(fp, 1);
s = get_token_1(fp, 0);
if (strcmp(s->value, "</string>"))
{
freeToken(t);
freeToken(s);
return NULL;
}
freeToken(s);
return t;
}
if (t->value[0] == '<')
{
if (!strcmp(t->value, "<array>")) t->type = TokenXMLArrayStart;
if (!strcmp(t->value, "</array>")) t->type = TokenXMLArrayEnd;
if (!strcmp(t->value, "<dict>")) t->type = TokenXMLDictStart;
if (!strcmp(t->value, "</dict>")) t->type = TokenXMLDictEnd;
if (!strcmp(t->value, "<key>")) t->type = TokenXMLKeyStart;
if (!strcmp(t->value, "</key>")) t->type = TokenXMLKeyEnd;
return t;
}
return t;
}
static ni_property *
pl_createprop(ni_proplist *l, char *n)
{
ni_property *p;
ni_index where;
p = (ni_property *)malloc(sizeof(ni_property));
NI_INIT(p);
p->nip_name = ni_name_dup(n);
p->nip_val.ni_namelist_len = 0;
p->nip_val.ni_namelist_val = NULL;
ni_proplist_insert(l, *p, NI_INDEX_NULL);
ni_prop_free(p);
free(p);
where = ni_proplist_match(*l, n, NULL);
return &(l->nipl_val[where]);
}
static ni_proplist *
fread_plist(FILE *fp)
{
ni_proplist *pl;
ni_property *p;
token_t *t;
t = pop_token(fp);
while (t->type != TokenXMLDictStart)
{
if (t->type == TokenNULL)
{
freeToken(t);
return NULL;
}
freeToken(t);
t = pop_token(fp);
}
freeToken(t);
pl = (ni_proplist *)malloc(sizeof(ni_proplist));
NI_INIT(pl);
forever
{
t = pop_token(fp);
if (t->type == TokenXMLDictEnd)
{
freeToken(t);
return pl;
}
if (t->type != TokenXMLKeyStart)
{
freeToken(t);
ni_proplist_free(pl);
return NULL;
}
freeToken(t);
t = pop_token(fp);
if (t->type != TokenWord)
{
freeToken(t);
ni_proplist_free(pl);
return NULL;
}
p = pl_createprop(pl, t->value);
freeToken(t);
t = pop_token(fp);
if (t->type != TokenXMLKeyEnd)
{
freeToken(t);
ni_proplist_free(pl);
return NULL;
}
freeToken(t);
t = pop_token(fp);
if (t->type != TokenXMLArrayStart)
{
freeToken(t);
ni_proplist_free(pl);
return NULL;
}
freeToken(t);
forever
{
t = pop_token(fp);
if (t->type == TokenXMLArrayEnd)
{
freeToken(t);
break;
}
if (t->type != TokenWord)
{
freeToken(t);
ni_proplist_free(pl);
return NULL;
}
ni_namelist_insert(&(p->nip_val), t->value, NI_INDEX_NULL);
freeToken(t);
}
}
}
static void
add_parent_server(struct in_addr server_addr, ni_name server_tag, ni_name client_tag, struct in_addr **addrs, ni_rparent_stuff *stuff, unsigned int *naddrs)
{
if (server_addr.s_addr == 0) return;
if ((server_tag == NULL) || (client_tag == NULL)) return;
if ((!strcmp(server_tag, client_tag)) && (sys_is_my_address(&server_addr))) return;
if (sys_is_general_broadcast(&server_addr))
{
add_broadcast_binding(server_tag, client_tag, addrs, stuff, naddrs);
}
else
{
add_hardwired_binding(server_addr, server_tag, client_tag, addrs, stuff, naddrs);
}
}
static ni_status
get_parents_from_file(void *ni, struct in_addr **addrs, ni_rparent_stuff *stuff, unsigned int *naddrs)
{
FILE *fp;
char str[MAXPATHLEN + 1], tag[MAXPATHLEN + 1];
ni_name client_tag = NULL;
ni_proplist *pl;
ni_index where_addr, where_tag;
struct in_addr server_addr;
ni_name server_tag;
int i, i_tag;
client_tag = ni_tagname(ni);
strcpy(tag, client_tag);
ni_name_free(&client_tag);
sprintf(str, CONFIG_FILE_NAME, tag);
fp = fopen(str, "r");
if (fp == NULL) return NI_FAILED;
pl = fread_plist(fp);
fclose(fp);
if (pl == NULL) return NI_OK;
where_addr = ni_proplist_match(*pl, KEY_PARENT_SERVER_ADDR, NULL);
where_tag = ni_proplist_match(*pl, KEY_PARENT_SERVER_TAG, NULL);
if ((where_addr == NI_INDEX_NULL)
|| (where_tag == NI_INDEX_NULL)
|| (pl->ni_proplist_val[where_addr].nip_val.ni_namelist_len == 0)
|| (pl->ni_proplist_val[where_tag].nip_val.ni_namelist_len == 0))
{
ni_proplist_free(pl);
free(pl);
return NI_OK;
}
i_tag = 0;
for (i = 0; i < pl->ni_proplist_val[where_addr].nip_val.ni_namelist_len; i++)
{
server_addr.s_addr = inet_addr(pl->ni_proplist_val[where_addr].nip_val.ni_namelist_val[i]);
server_tag = pl->ni_proplist_val[where_tag].nip_val.ni_namelist_val[i_tag];
add_parent_server(server_addr, server_tag, tag, addrs, stuff, naddrs);
i_tag++;
if (i_tag == pl->ni_proplist_val[where_tag].nip_val.ni_namelist_len) i_tag--;
}
ni_proplist_free(pl);
free(pl);
return NI_OK;
}
static ni_status
get_parents_from_db(void *ni, struct in_addr **addrs, ni_rparent_stuff *stuff, unsigned int *naddrs)
{
ni_name client_tag = NULL;
ni_name server_tag = NULL;
struct in_addr server_addr;
ni_id id;
ni_idlist ids;
ni_entrylist entries;
int i;
ni_status status;
status = ni_root(ni, &id);
if (status != NI_OK) return status;
NI_INIT(&ids);
status = ni_lookup(ni, &id, NAME_NAME, NAME_MACHINES, &ids);
if (status != NI_OK) return status;
id.nii_object = ids.ni_idlist_val[0];
ni_idlist_free(&ids);
NI_INIT(&entries);
status = ni_list_const(ni, &id, NAME_SERVES, &entries);
if (status != NI_OK) return status;
client_tag = ni_tagname(ni);
for (i = 0; i < entries.ni_entrylist_len; i++)
{
server_addr.s_addr = servesdotdot(entries.ni_entrylist_val[i], &server_tag);
add_parent_server(server_addr, server_tag, client_tag, addrs, stuff, naddrs);
ni_name_free(&server_tag);
}
ni_name_free(&client_tag);
ni_list_const_free(ni);
return NI_OK;
}
static void
swap_addrs(struct in_addr *addrs, ni_rparent_stuff *stuff, int a, int b)
{
unsigned long tempaddr;
ni_name temptag;
if (a == b) return;
tempaddr = addrs[a].s_addr;
addrs[a].s_addr = addrs[b].s_addr;
addrs[b].s_addr = tempaddr;
temptag = stuff->bindings[a].server_tag;
stuff->bindings[a].server_tag = stuff->bindings[b].server_tag;
stuff->bindings[b].server_tag = temptag;
tempaddr = stuff->bindings[a].client_addr;
stuff->bindings[a].client_addr = stuff->bindings[b].client_addr;
stuff->bindings[b].client_addr = tempaddr;
temptag = stuff->bindings[a].client_tag;
stuff->bindings[a].client_tag = stuff->bindings[b].client_tag;
stuff->bindings[b].client_tag = temptag;
return;
}
static ni_status
bind_to_parent(struct in_addr *addrs, ni_rparent_stuff *stuff, unsigned int naddrs, ni_rparent_res *res)
{
int i, j;
unsigned int nlocal, nnetwork;
unsigned int nbcast;
enum clnt_stat stat;
static ni_rparent_res old_res = { NI_NOTMASTER };
static struct in_addr old_binding;
static struct in_addr new_binding;
int *shuffle, temp;
static int fix3408166 = NI_OK;
unsigned int rentry;
if (naddrs == 0) return NI_NETROOT;
shuffle = (int *)malloc(naddrs * sizeof(int));
for (i = 0; i < naddrs; i++) shuffle[i] = i;
for (i = 0, j = naddrs; j > 0; i++, j--)
{
rentry = random() % j;
temp = shuffle[rentry];
shuffle[rentry] = shuffle[j-1];
shuffle[j-1] = temp;
swap_addrs(addrs, stuff, rentry, j-1);
}
free(shuffle);
if (get_binding_status() == NI_FAILED)
{
system_log(LOG_DEBUG, "bind_to_parent aborted");
return NI_NORESPONSE;
}
nlocal = 0;
for (i = nlocal; i < naddrs; i++)
{
if (sys_is_my_address(&(addrs[i])))
{
swap_addrs(addrs, stuff, nlocal, i);
nlocal++;
}
}
nnetwork = nlocal;
nbcast = 0;
for (i = nnetwork; i < naddrs; i++)
{
if (sys_is_on_attached_network(&(addrs[i])))
{
if (sys_is_my_broadcast(&(addrs[i]))) nbcast++;
swap_addrs(addrs, stuff, nnetwork, i);
nnetwork++;
}
}
for (i = 0; i < naddrs; i++)
{
if (get_binding_status() == NI_FAILED)
{
system_log(LOG_DEBUG, "bind_to_parent aborted");
return NI_NORESPONSE;
}
if (sys_is_my_broadcast(&(addrs[i]))) continue;
if (ni_ping(addrs[i].s_addr, stuff->bindings[i].server_tag))
{
ni_name_free(&(res->ni_rparent_res_u.binding.tag));
res->ni_rparent_res_u.binding.tag = ni_name_dup(stuff->bindings[i].server_tag);
res->ni_rparent_res_u.binding.addr = addrs[i].s_addr;
system_log(LOG_INFO, "Anonymous binding to %s/%s",
inet_ntoa(addrs[i]), stuff->bindings[i].server_tag);
res->status = NI_OK;
stat = RPC_SUCCESS;
goto binding_done;
}
}
stuff->res = res;
stat = RPC_TIMEDOUT;
if ((stat != RPC_SUCCESS) && (nlocal > 0))
{
if (get_binding_status() == NI_FAILED)
{
system_log(LOG_DEBUG, "bind_to_parent aborted");
return NI_NORESPONSE;
}
for (i = 0; i < nlocal; i++)
{
system_log(LOG_INFO, "local bind try %d: %s/%s",
i + 1, inet_ntoa(addrs[i]),
stuff->bindings[i].server_tag);
}
stat = ni_multi_call(nlocal, addrs,
NIBIND_PROG, NIBIND_VERS, NIBIND_BIND,
xdr_nibind_bind_args, stuff->bindings,
sizeof(nibind_bind_args),
xdr_void, stuff,
catch, -1);
}
if (stat != RPC_SUCCESS && nnetwork > 0)
{
if (get_binding_status() == NI_FAILED)
{
system_log(LOG_DEBUG, "bind_to_parent aborted");
return NI_NORESPONSE;
}
for (i = 0; i < nnetwork; i++)
{
system_log(LOG_INFO, "network bind try %d: %s/%s",
i + 1, inet_ntoa(addrs[i]),
stuff->bindings[i].server_tag);
}
stat = ni_multi_call(nnetwork, addrs,
NIBIND_PROG, NIBIND_VERS, NIBIND_BIND,
xdr_nibind_bind_args, stuff->bindings,
sizeof(nibind_bind_args),
xdr_void, stuff,
catch, -1);
}
if (stat != RPC_SUCCESS && ((naddrs - nbcast) > 0))
{
if (get_binding_status() == NI_FAILED)
{
system_log(LOG_DEBUG, "bind_to_parent aborted");
return NI_NORESPONSE;
}
for (i = 0; i < naddrs; i++)
{
system_log(LOG_INFO, "world bind try %d: %s/%s",
i + 1, inet_ntoa(addrs[i]),
stuff->bindings[i].server_tag);
}
stat = ni_multi_call(naddrs, addrs,
NIBIND_PROG, NIBIND_VERS, NIBIND_BIND,
xdr_nibind_bind_args, stuff->bindings,
sizeof(nibind_bind_args),
xdr_void, stuff,
catch, -1);
}
binding_done:
if (get_binding_status() == NI_FAILED)
{
system_log(LOG_DEBUG, "bind_to_parent aborted");
return NI_NORESPONSE;
}
if (stat != RPC_SUCCESS)
{
alert_open("English");
res->status = NI_NORESPONSE;
}
else
{
alert_close();
res->status = NI_OK;
}
old_binding.s_addr = old_res.ni_rparent_res_u.binding.addr;
if (NI_OK == res->status)
{
fix3408166 = NI_OK;
new_binding.s_addr = res->ni_rparent_res_u.binding.addr;
switch (old_res.status)
{
case NI_NOTMASTER:
system_log(LOG_NOTICE, "bound to %s/%s",
inet_ntoa(new_binding), res->ni_rparent_res_u.binding.tag);
old_res.ni_rparent_res_u.binding.tag =
(ni_name)malloc(NI_NAME_MAXLEN+1);
break;
case NI_NETROOT:
system_log(LOG_NOTICE, "bound to (new parent) %s/%s",
inet_ntoa(new_binding), res->ni_rparent_res_u.binding.tag);
old_res.ni_rparent_res_u.binding.tag =
(ni_name)malloc(NI_NAME_MAXLEN+1);
break;
default:
system_log(LOG_WARNING, "rebound to %s/%s (was to %s/%s)",
inet_ntoa(new_binding), res->ni_rparent_res_u.binding.tag,
inet_ntoa(old_binding), old_res.ni_rparent_res_u.binding.tag);
break;
}
old_res.status = res->status;
old_res.ni_rparent_res_u.binding.addr =
res->ni_rparent_res_u.binding.addr;
(void)strcpy(old_res.ni_rparent_res_u.binding.tag,
res->ni_rparent_res_u.binding.tag);
if (latestParentInfo == NULL)
latestParentInfo = malloc(sizeof("xxx.xxx.xxx.xxx") + 1 + NI_NAME_MAXLEN + 1);
sprintf(latestParentInfo, "%s/%s",
inet_ntoa(new_binding), res->ni_rparent_res_u.binding.tag);
}
else
{
if (fix3408166 == NI_OK)
{
switch (old_res.status)
{
case NI_NOTMASTER:
system_log(LOG_ERR, "unable to bind to parent - %s",
clnt_sperrno(stat));
break;
default:
system_log(LOG_ERR, "unable to rebind from %s/%s - %s",
inet_ntoa(old_binding), old_res.ni_rparent_res_u.binding.tag,
clnt_sperrno(stat));
break;
}
}
fix3408166 = old_res.status;
}
return res->status;
}
ni_rparent_res *
_ni_rparent_2_svc(void *arg, struct svc_req *req)
{
static ni_rparent_res res;
static long root_time = -1;
static unsigned long paddr = -1;
long now;
struct in_addr *addrs;
unsigned int i, naddrs, j;
ni_rparent_stuff stuff;
ni_status status, binding_status;
if (req == NULL) return NULL;
res.status = get_binding_status();
if (alert_aborted())
{
set_binding_status(NI_NETROOT);
return &res;
}
if (sys_is_standalone())
{
set_binding_status(NI_NETROOT);
return &res;
}
if (res.status == NI_OK)
{
if (ni_ping(paddr, res.ni_rparent_res_u.binding.tag))
{
res.ni_rparent_res_u.binding.addr = ntohl(paddr);
return (&res);
}
}
if ((res.status == NI_NETROOT) && (root_time != -1))
{
time(&now);
if ((now - root_time) < (NETROOT_TIMEOUT_MINS * 60)) return (&res);
}
binding_status = NI_NORESPONSE;
for (j = 0; (j < MAX_REBIND_ATTEMPTS) && (binding_status == NI_NORESPONSE); j++)
{
addrs = NULL;
naddrs = 0;
stuff.bindings = NULL;
set_binding_status(NI_NORESPONSE);
status = get_parents_from_file(db_ni, &addrs, &stuff, &naddrs);
if (status != NI_OK)
{
status = get_parents_from_db(db_ni, &addrs, &stuff, &naddrs);
}
if (status != NI_OK)
{
res.status = NI_NETROOT;
set_binding_status(NI_NETROOT);
return &res;
}
system_log(LOG_DEBUG, "Rebinding");
binding_status = bind_to_parent(addrs, &stuff, naddrs, &res);
MM_FREE_ARRAY(addrs, naddrs);
for (i = 0; i < naddrs; i++)
{
ni_name_free(&stuff.bindings[i].client_tag);
ni_name_free(&stuff.bindings[i].server_tag);
}
MM_FREE_ARRAY(stuff.bindings, naddrs);
}
if (binding_status == NI_OK)
{
paddr = res.ni_rparent_res_u.binding.addr;
res.ni_rparent_res_u.binding.addr = ntohl(paddr);
}
else
{
res.status = NI_NETROOT;
time(&root_time);
}
set_binding_status(res.status);
return (&res);
}
void
waitforparent(void)
{
ni_rparent_res *res;
int i;
unsigned int a, n;
n = get_localbindattempts(db_ni);
if (n == 0) return;
a = 0;
alert_enable(1);
for (;;)
{
a++;
system_log(LOG_DEBUG, "waitforparent attempt %d", a);
res = _ni_rparent_2_svc(NULL, (struct svc_req *)(~0));
if (res->status != NI_NORESPONSE)
{
system_log(LOG_DEBUG, "waitforparent exiting for status %d", res->status);
break;
}
if (a >= n)
{
system_log(LOG_DEBUG, "waitforparent aborted after %d attempt%s", a, (a == 1) ? "" : "s");
break;
}
for (i = 0; i < PARENT_NINTERVALS; i++)
{
if (alert_aborted())
{
system_log(LOG_DEBUG, "waitforparent aborted (alert_aborted)");
alert_enable(0);
return;
}
sleep(PARENT_SLEEPTIME);
}
}
alert_enable(0);
return;
}
void *
_ni_crashed_2_svc(unsigned *checksum, struct svc_req *req)
{
if (req == NULL) return NULL;
if (i_am_clone)
{
if (*checksum != ni_getchecksum(db_ni))
{
have_transferred = 0;
dir_clonecheck();
}
}
return ((void *)~0);
}
void proxy_term(void);
ni_readall_res *
_ni_readall_2_svc(
unsigned *checksum,
struct svc_req *req
)
{
static ni_readall_res res;
unsigned db_checksum;
int didit;
struct stat stat_buf;
int i;
static int kpid;
if (req == NULL) return (NULL);
if (i_am_clone && !cloneReadallResponseOK)
{
res.status = NI_NOTMASTER;
return (&res);
}
db_checksum = ni_getchecksum(db_ni);
if (*checksum == db_checksum)
{
res.status = NI_OK;
res.ni_readall_res_u.stuff.checksum = 0;
res.ni_readall_res_u.stuff.list = NULL;
return (&res);
}
if (have_notifications_pending())
{
res.status = NI_MASTERBUSY;
return (&res);
}
system_log(LOG_WARNING, "readall %s {%u} to %s:%hu {%u}",
db_tag, db_checksum,
inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr),
ntohs(svc_getcaller(req->rq_xprt)->sin_port), *checksum);
syslock_lock(readall_syslock);
sending_all++;
if (max_readall_proxies != 0)
{
if ((max_readall_proxies == -1) ||
(max_readall_proxies > readall_proxies))
{
readall_proxies++;
kpid = fork();
if (kpid == 0)
{
if (readall_proxies == 1)
process_group = getpid();
if (setpgrp(0, process_group) == -1) {
system_log(LOG_ERR, "proxy can't setpgrp - %m");
}
syslock_unlock(readall_syslock);
signal(SIGUSR1, (void *)proxy_term);
sleep(1);
i_am_proxy = TRUE;
setproctitle("netinfod %s->%s", db_tag,
inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr));
for (i = getdtablesize() - 1; i >= 0; i--)
{
if (i != req->rq_xprt->xp_sock)
{
if (fstat(i, &stat_buf) == 0)
{
if ((S_IFSOCK == (S_IFMT & stat_buf.st_mode)) ||
!stat_buf.st_mode)
close(i);
}
else
{
switch(errno)
{
case 0:
system_log(LOG_WARNING,
"proxy: fstat(%d) return of 0, and errno of 0");
break;
case EBADF:
break;
default:
system_log(LOG_ERR, "proxy: fstat(%d) - %m", i);
break;
}
}
}
}
didit = svc_sendreply(req->rq_xprt, readall, db_ni);
if (!didit)
{
system_log(LOG_ERR, "readall %s to %s:%hu failed",
db_tag, inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr),
ntohs(svc_getcaller(req->rq_xprt)->sin_port));
exit(NI_SYSTEMERR);
}
else
{
system_log(LOG_NOTICE, "readall %s to %s:%hu complete",
db_tag, inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr),
ntohs(svc_getcaller(req->rq_xprt)->sin_port));
}
exit(0);
}
else if (kpid == -1)
{
readall_proxies--;
syslock_unlock(readall_syslock);
system_log(LOG_ERR, "Can't fork for readall [%p], retaining");
}
else
{
system_log(LOG_DEBUG, "readall proxy pid %d", kpid);
if (readall_proxies == 1)
process_group = kpid;
if (setpgrp(kpid, process_group) == -1) {
system_log(LOG_ERR, "proxy can't setpgrp - %m");
}
add_proxy(kpid, svc_getcaller(req->rq_xprt)-> sin_addr.s_addr);
syslock_unlock(readall_syslock);
return(NULL);
}
}
else
{
system_log(LOG_WARNING, "readall proxy limit (%d) reached; %s "
"request from %s:%hu", max_readall_proxies,
strict_proxies ? "proroguing" : "retaining",
inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr),
ntohs(svc_getcaller(req->rq_xprt)->sin_port));
if (strict_proxies)
{
sending_all--;
syslock_unlock(readall_syslock);
res.status = NI_MASTERBUSY;
return (&res);
}
syslock_unlock(readall_syslock);
}
} else {
syslock_unlock(readall_syslock);
}
socket_lock();
didit = svc_sendreply(req->rq_xprt, readall, db_ni);
socket_unlock();
syslock_lock(readall_syslock);
sending_all--;
syslock_unlock(readall_syslock);
if (!didit)
{
system_log(LOG_ERR, "readall %s to %s:%hu failed", db_tag,
inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr),
ntohs(svc_getcaller(req->rq_xprt)->sin_port));
}
else
{
system_log(LOG_NOTICE, "readall %s to %s:%hu complete", db_tag,
inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr),
ntohs(svc_getcaller(req->rq_xprt)->sin_port));
}
return (NULL);
}
void
readall_catcher(void)
{
readall_done = TRUE;
}
void
readall_cleanup(void)
{
int p;
union wait wait_stat;
unsigned int addr;
syslock_lock(readall_syslock);
while (TRUE)
{
p = wait4(-1, (_WAIT_TYPE_ *)&wait_stat, WNOHANG, NULL);
switch (p)
{
case 0:
syslock_unlock(readall_syslock);
return;
break;
case -1:
syslock_unlock(readall_syslock);
if (ECHILD != errno)
system_log(LOG_WARNING, "problem reaping proxy: %m");
return;
break;
default:
addr = get_proxy(p);
if (addr == 0)
{
system_log(LOG_WARNING,
"child process %d not a readall proxy", p);
}
else
{
if ((wait_stat.w_retcode == 0) && (wait_stat.w_termsig == 0))
{
notify_mark_clone(addr);
remove_proxy(p);
}
else
{
system_log(LOG_ERR, "readall proxy terminated with status %u, "
"signal %u", wait_stat.w_retcode, wait_stat.w_termsig);
}
if (sending_all > 0) sending_all--;
if ((readall_proxies > 0) && (0 == --readall_proxies))
readall_done = FALSE;
}
break;
}
}
syslock_unlock(readall_syslock);
return;
}
void
dblock_catcher(void)
{
if (i_am_clone)
{
system_log(LOG_ERR, "SIGINT to clone ignored");
return;
}
else if (i_am_proxy)
{
system_log(LOG_ERR, "SIGINT to readall proxy ignored");
return;
}
syslock_lock(lockup_syslock);
db_lockup = ! db_lockup;
system_log(LOG_WARNING, "Master database is now %s",
db_lockup ? "locked" : "unlocked");
syslock_unlock(lockup_syslock);
return;
}
ni_status *
_ni_resync_2_svc(
void *arg,
struct svc_req *req
)
{
static ni_status status;
if (req == NULL) return (NULL);
system_log(LOG_NOTICE, "got a resync from %s:%hu",
inet_ntoa(svc_getcaller(req->rq_xprt)->sin_addr),
ntohs(svc_getcaller(req->rq_xprt)->sin_port));
cleanupwait = get_cleanupwait(db_ni);
forcedIsRoot = get_forced_root(db_ni);
cloneReadallResponseOK = get_clone_readall(db_ni);
if (i_am_clone)
{
if (get_sanitycheck(db_ni)) sanitycheck(db_tag);
dir_clonecheck();
}
else
{
notify_resync();
}
status = NI_OK;
return (&status);
}
static int
ni_ping(u_long address, ni_name tag)
{
struct sockaddr_in sin;
struct timeval tv, total;
enum clnt_stat stat;
int sock;
nibind_getregister_res res;
CLIENT *cl;
sin.sin_family = AF_INET;
sin.sin_port = 0;
sin.sin_addr.s_addr = address;
bzero(sin.sin_zero, sizeof(sin.sin_zero));
sock = socket_open(&sin, NIBIND_PROG, NIBIND_VERS);
if (sock < 0) return 0;
tv.tv_sec = PING_TIMEOUT_SEC;
tv.tv_usec = PING_TIMEOUT_USEC;
total.tv_sec = PING_CALL_SEC;
total.tv_usec = PING_CALL_USEC;
cl = clntudp_create(&sin, NIBIND_PROG, NIBIND_VERS, tv, &sock);
if (cl == NULL)
{
socket_close(sock);
return 0;
}
stat = clnt_call(cl, NIBIND_GETREGISTER, xdr_ni_name, &tag, xdr_nibind_getregister_res, &res, total);
clnt_destroy(cl);
socket_close(sock);
if (stat != RPC_SUCCESS) return 0;
if (res.status != NI_OK) return 0;
sin.sin_port = htons(res.nibind_getregister_res_u.addrs.udp_port);
sock = socket_open(&sin, NI_PROG, NI_VERS);
if (sock < 0) return 0;
cl = clntudp_create(&sin, NI_PROG, NI_VERS, tv, &sock);
if (cl == NULL)
{
socket_close(sock);
return 0;
}
stat = clnt_call(cl, _NI_PING, xdr_void, (void *)NULL, xdr_void, (void *)NULL, total);
clnt_destroy(cl);
socket_close(sock);
return (stat == RPC_SUCCESS);
}