/*
* 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 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <rpcsvc/sm_inter.h>
#include <rpcsvc/nsm_addr.h>
#include <memory.h>
#include <netdb.h>
#include <netdir.h>
#include <synch.h>
#include <thread.h>
#include <ifaddrs.h>
#include <errno.h>
#include <assert.h>
#include "sm_statd.h"
/* client name-to-address translation table */
static void pr_mon(char *);
static char *get_system_id(char *hostname);
static void *thr_send_notice(void *);
static void add_to_host_array(char *host);
static int in_host_array(char *host);
extern int self_check(char *hostname);
extern struct lifconf *getmyaddrs(void);
/* ARGSUSED */
void
{
if (debug)
(void) printf("proc sm_stat: mon_name = %s\n",
}
/* ARGSUSED */
void
{
if (debug) {
(void) printf("proc sm_mon: mon_name = %s, id = %d\n",
}
/* only monitor other hosts */
/* store monitor request into monitor_q */
}
}
/* ARGSUSED */
void
{
if (debug) {
(void) printf(
"proc sm_unmon: mon_name = %s, [%s, %d, %d, %d]\n",
}
}
/* ARGSUSED */
void
{
if (debug)
(void) printf("proc sm_unmon_all: [%s, %d, %d, %d]\n",
}
/*
* Notifies lockd specified by name that state has changed for this server.
*/
void
{
if (debug)
(void) printf("sm_notify: %s state =%d\n",
}
/* ARGSUSED */
void
{
int i;
int found = 0;
if (debug)
(void) printf("proc sm_simu_crash\n");
/* Only one crash should be running at a time. */
if (in_crash != 0) {
return;
}
in_crash = 1;
for (i = 0; i < MAX_HASHSIZE; i++) {
found = 1;
break;
}
}
/*
* If there are entries found in the monitor table,
* initiate a crash, else zero out the in_crash variable.
*/
if (found) {
die = 1;
/* Signal sm_try() thread if sleeping. */
sm_crash();
} else {
in_crash = 0;
}
}
/* ARGSUSED */
void
{
char *tmp_n_bytes;
if (debug) {
int i;
(void) printf("nap1_reg: fam= %d, name= %s, len= %d\n",
(void) printf("address is: ");
(void) printf("%d.",
}
(void) printf("\n");
}
/*
* Locate the entry with the name in the NSM_ADDR_REG request if
* it exists. If it doesn't, create a new entry to hold this name.
* The first time through this code, name_addr starts out as NULL.
*/
if (debug) {
(void) printf("nap1_reg: matched name %s\n",
}
break;
}
}
if (debug) {
(void) printf(
"nsmaddrproc1_reg: no memory for entry\n");
}
goto done;
}
if (debug) {
(void) printf(
"nsmaddrproc1_reg: no memory for name\n");
}
goto done;
}
/*
* Link the new entry onto the *head* of the name_addr
* table.
*
* Note: there is code below in the address maintenance
* section that assumes this behavior.
*/
}
/*
* Try to match the address in the request; if it doesn't match,
* add it to the entry's address list.
*/
if (debug) {
int i;
(void) printf("nap1_reg: matched addr ");
(void) printf("%d.",
}
(void) printf(" family %d for name %s\n",
}
break;
}
}
if (debug) {
(void) printf("nap1_reg: no memory for addr\n");
}
/*
* If this name entry was just newly made in the
* table, back it out now that we can't register
* an address with it anyway.
*
* Note: we are making an assumption about how
* names are added to (the head of) name_addr here.
*/
if (tmp_n_bytes)
if (addr)
goto done;
}
}
/*
* Note: this check for address family assumes that we
* will get something different here someday for
* other supported address types, such as IPv6.
*/
if (debug) {
(void) printf(
"nap1_reg: unknown addr family %d\n",
}
}
}
done:
if (debug) {
}
}
/*
* Insert an entry into the monitor_q. Space for the entry is allocated
* here. It is then filled in from the information passed in.
*/
static void
{
unsigned int hash;
/* Allocate entry for new */
"statd: insert_mon: malloc error on mon %s (id=%d)\n",
return;
}
/* Initialize and copy contents of monp to new */
/* Allocate entry for new mon_name */
"statd: insert_mon: malloc error on mon %s (id=%d)\n",
return;
}
/* Allocate entry for new my_name */
"statd: insert_mon: malloc error on mon %s (id=%d)\n",
return;
}
if (debug)
(void) printf("add_mon(%x) %s (id=%d)\n",
/*
* Record the name, and all addresses which have been registered
* for this name, in the filesystem name space.
*/
if (regfiles_only == 0) {
continue;
}
}
break;
}
}
/* If mon_table hash list is empty. */
if (debug)
return;
} else {
found = 0;
/*
* This list is searched sequentially for the
* tuple (hostname, prog, vers, proc). The tuples
* are inserted in the beginning of the monitor_q,
* if the hostname is not already present in the list.
* If the hostname is found in the list, the incoming
* tuple is inserted just after all the tuples with the
* same hostname. However, if the tuple matches exactly
* with an entry in the list, space allocated for the
* new entry is released and nothing is inserted in the
* list.
*/
/* found */
/*
* already exists an identical one,
* release the space allocated for the
* mon_entry
*/
return;
} else {
/*
* mark the last callback that is
* not matching; new is inserted
* after this
*/
}
} else if (found)
break;
}
if (found) {
/*
* insert just after the entry having matching tuple.
*/
} else {
/*
* not found, insert in front of list.
*/
}
return;
}
}
/*
* Deletes a specific monitor name or deletes all monitors with same id
* in hash table.
*/
static void
{
unsigned int hash;
record_name(mon_name, 0);
} else {
}
}
}
/*
* Deletes a monitor in list.
* IF mon_name is NULL, delete all mon_names that have the same id,
* else delete specific monitor.
*/
void
{
mon_name) == 0)) {
/* found */
if (debug)
(void) printf("delete_mon(%x): %s\n",
mon_name : "<NULL>");
/*
* Remove the monitor name from the
* record_q, if id matches.
*/
/* if nl is not the first entry on list */
else {
}
}
} /* end of if mon */
}
}
/*
* Notify lockd of host specified by mon_name that the specified state
* has changed.
*/
static void
{
unsigned int hash;
/*
* Prepare the minfop structure to pass to
* thr_create(). This structure is a copy of
* mon info and state.
*/
if ((minfop =
/* Allocate entry for mon_name */
"malloc error on mon %s (id=%d)\n",
continue;
}
/* Allocate entry for my_name */
"malloc error on mon %s (id=%d)\n",
continue;
}
/*
* Create detached threads to process each host
* to notify. If error, print out msg, free
* resources and continue.
*/
"create thread to send_notice to "
"%s.\n", mon_name);
continue;
}
}
}
}
}
/*
* Work thread created to do the actual statd_call_lockd
*/
static void *
{
(void) printf("problem with notifying %s failure, "
} else {
if (debug)
(void) printf("send_notice: %s, %d notified.\n",
}
thr_exit((void *) 0);
#ifdef lint
/*NOTREACHED*/
return ((void *)0);
#endif
}
/*
* Contact lockd specified by monp.
*/
static int
{
char *mon_name;
int i;
int rc = 0;
for (i = 0; i < 16; i++) {
}
if (debug)
(void) printf("statd_call_lockd: %s state = %d\n",
tottimeout.tv_usec = 0;
"ticotsord", &tottimeout);
return (-1);
}
if (debug) {
(void) printf("clnt_stat=%s(%d)\n",
}
if (clnt_stat != (int)RPC_SUCCESS) {
"statd: cannot talk to lockd at %s, %s(%d)\n",
rc = -1;
}
return (rc);
}
/*
* Client handle created.
*/
CLIENT *
{
int fd;
"netpath", utimeout);
} else {
return (NULL);
}
utimeout);
}
return (NULL);
}
/*
* Set time outs for connectionless case
*/
(void) CLNT_CONTROL(client,
}
} else
return (NULL);
return (client);
}
/*
* ONLY for debugging.
* Debug messages which prints out the monitor table information.
* If name is specified, just print out the hash list corresponding
* to name, otherwise print out the entire monitor table.
*/
static void
{
int hash;
if (!debug)
return;
/* print all */
(void) printf(
"*****monitor_q = NULL hash %d\n", hash);
continue;
}
(void) printf("*****monitor_q:\n ");
(void) printf("%s:(%x), ",
}
(void) printf("\n");
}
} else { /* print one hash list */
} else {
(void) printf("*****monitor_q:\n ");
(void) printf("%s:(%x), ",
}
(void) printf("\n");
}
}
}
/*
* Only for debugging.
* Dump the host name-to-address translation table passed in `name_addr'.
*/
static void
{
char *ipv6_addr;
(void) printf("name-to-address translation table:\n");
(void) printf("\t%s: ",
case AF_INET:
(void) printf(" %s (fam %d)",
break;
case AF_INET6:
(void) printf(" %s (fam %d)",
break;
default:
return;
}
}
printf("\n");
}
}
/*
* First, try to compare the hostnames as strings. If the hostnames does not
* match we might deal with the hostname aliases. In this case two different
* aliases for the same machine don't match each other when using strcmp. To
* deal with this, the hostnames must be translated into some sort of universal
* identifier. These identifiers can be compared. Universal network addresses
* are currently used for this identifier because it is general and easy to do.
* Other schemes are possible and this routine could be converted if required.
*
* If it can't find an address for some reason, 0 is returned.
*/
static int
{
char *sysid1;
char *sysid2;
int rv;
/* Compare hostnames as strings */
return (1);
/* Try harder if hostnames do not match */
rv = 0;
else
return (rv);
}
/*
* Convert a hostname character string into its network address.
* A network address is found by searching through all the entries
* entry found. The netbuf structure returned is converted into
* a universal address format.
*
* If a NULL hostname is given, then the name of the current host
* is used. If the hostname doesn't map to an address, a NULL
* pointer is returned.
*
* N.B. the character string returned is allocated in taddr2uaddr()
* and should be freed by the caller using free().
*/
static char *
{
void *hp;
char *uaddr;
int rv;
else
hp = setnetconfig();
return (NULL);
}
if (rv != 0) {
continue;
}
if (addrs) {
return (uaddr);
}
}
else
continue;
}
return (NULL);
}
void
merge_hosts(void)
{
int n;
int af;
char *addr;
int errnum;
/*
* This function will enumerate all the interfaces for
* this platform, then get the hostent for each i/f.
* With the hostent structure, we can get all of the
* aliases for the i/f. Then we'll merge all the aliases
* with the existing host_name[] list to come up with
* all of the known names for each interface. This solves
* the problem of a multi-homed host not knowing which
* name to publish when statd is started. All the aliases
* will be stored in the array, host_name.
*
* NOTE: Even though we will use all of the aliases we
* can get from the i/f hostent, the receiving statd
* will still need to handle aliases with hostname_eq.
* This is because the sender's aliases may not match
* those of the receiver.
*/
lifc = getmyaddrs();
goto finish;
}
if (sock == -1) {
goto finish;
}
/* If it's the loopback interface, ignore */
"statd: SIOCGLIFFLAGS failed, error: %m\n");
goto finish;
}
continue;
"statd: SIOCGLIFADDR failed, error: %m\n");
goto finish;
}
} else {
"unexpected address family (%d)",
continue;
}
if (phost)
}
/*
* Now, just in case we didn't get them all byaddr,
* let's look by name.
*/
if (phost)
if (sock != -1)
if (lifc) {
}
}
/*
* add_aliases traverses a hostent alias list, compares
* the aliases to the contents of host_name, and if an
* alias is not already present, adds it to host_name[].
*/
static void
{
char **aliases;
}
return; /* no aliases to register */
if (!in_host_array(*aliases)) {
}
}
}
/*
* in_host_array checks if the given hostname exists in the host_name
* array. Returns 0 if the host doesn't exist, and 1 if it does exist
*/
static int
{
int i;
if (debug)
return (1);
for (i = 0; i < addrix; i++) {
return (1);
}
return (0);
}
/*
* add_to_host_array adds a hostname to the host_name array. But if
* the array is already full, then it first reallocates the array with
* HOST_NAME_INCR extra elements. If the realloc fails, then it does
* nothing and leaves host_name the way it was previous to the call.
*/
static void
/* Make sure we don't overrun host_name. */
if (addrix >= host_name_count) {
host_name_count*sizeof (char *));
else {
return;
}
}
addrix++;
}
/*
* Compares the unqualified hostnames for hosts. Returns 0 if the
* names match, and 1 if the names fail to match.
*/
int
{
char *domain;
if (debug) {
(void) printf("str_cmp_unqual: rawname1= %s, rawname2= %s\n",
}
return (1);
}
return (0);
}
return (1);
}
/*
* Compares <family>.<address-specifier> ASCII names for hosts. Returns
* 0 if the addresses match, and 1 if the addresses fail to match.
* If the args are indeed specifiers, they should look like this:
*
* ipv4.192.9.200.1 or ipv6.::C009:C801
*/
int
{
if (debug) {
(void) printf("str_cmp_addr: specifier1= %s, specifier2= %s\n",
}
/*
* Verify that:
* 1. The family tokens match;
* 2. The IP addresses following the `.' are legal; and
* 3. These addresses match.
*/
len = 4;
len = 16;
}
else
return (1);
else
return (1);
return (1);
++rawaddr1;
++rawaddr2;
return (0);
}
}
return (1);
}
/*
* Add IP address strings to the host_name list.
*/
void
merge_ips(void)
{
int error;
if (error) {
return;
}
case AF_INET: {
/* Skip loopback addresses. */
continue;
}
break;
}
case AF_INET6: {
/* Skip loopback addresses. */
continue;
}
break;
}
default:
continue;
}
== NULL) {
"string representation for interface '%s' "
continue;
}
if (!in_host_array(addr_str)) {
}
}
}