/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/time.h>
#include <strings.h>
#include <meta.h>
#include <syslog.h>
extern md_mn_msg_tbl_entry_t msg_table[];
/*
* When contacting the local rpc.mdcommd we always want to do that using
* the IPv4 version of localhost.
*/
#define LOCALHOST_IPv4 "127.0.0.1"
md_mn_msgclass_t
mdmn_get_message_class(md_mn_msgtype_t msgtype)
{
return (msg_table[msgtype].mte_class);
}
void (*
mdmn_get_handler(md_mn_msgtype_t msgtype))
(md_mn_msg_t *msg, uint_t flags, md_mn_result_t *res)
{
return (msg_table[msgtype].mte_handler);
}
int (*
mdmn_get_submessage_generator(md_mn_msgtype_t msgtype))
(md_mn_msg_t *msg, md_mn_msg_t **msglist)
{
return (msg_table[msgtype].mte_smgen);
}
time_t
mdmn_get_timeout(md_mn_msgtype_t msgtype)
{
return (msg_table[msgtype].mte_timeout);
}
void
ldump_msg(char *prefix, md_mn_msg_t *msg)
{
(void) fprintf(stderr, "%s &msg = 0x%x\n", prefix, (uint_t)msg);
(void) fprintf(stderr, "%s ID = (%d, 0x%llx-%d)\n", prefix,
MSGID_ELEMS(msg->msg_msgid));
(void) fprintf(stderr, "%s sender = %d\n", prefix, msg->msg_sender);
(void) fprintf(stderr, "%s flags = 0x%x\n",
prefix, msg->msg_flags);
(void) fprintf(stderr, "%s setno = %d\n", prefix, msg->msg_setno);
(void) fprintf(stderr, "%s recipient = %d\n",
prefix, msg->msg_recipient);
(void) fprintf(stderr, "%s type = %d\n", prefix, msg->msg_type);
(void) fprintf(stderr, "%s size = %d\n",
prefix, msg->msg_event_size);
}
#define COMMD_PROGNAME "rpc.mdcommd"
extern uint_t meta_rpc_err_mask(void);
/*
* If a clnt_call gets an RPC error, force the message out here with details.
* This would be nice to send to commd_debug(), but we can't call rpc.mdcommd
* code from libmeta.
*/
static void
mdmn_handle_RPC_error(CLIENT *clnt, char *ident, md_mn_nodeid_t nid)
{
/*
* This is sized for a max message which would look like this:
* "mdmn_wakeup_initiator: rpc.mdcommd node 4294967295"
*/
char errstr[51];
struct rpc_err e;
CLNT_GETERR((CLIENT *) clnt, &e);
if (meta_rpc_err_mask() & (1 << e.re_status)) {
if (nid == 0) {
(void) snprintf(errstr, sizeof (errstr),
"%s: %s node (local)", ident, COMMD_PROGNAME);
} else {
(void) snprintf(errstr, sizeof (errstr),
"%s: %s node %d", ident, COMMD_PROGNAME, nid);
}
syslog(LOG_WARNING, "mdmn_handle_RPC_error: %s",
clnt_sperror(clnt, errstr));
}
}
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };
md_mn_result_t *
mdmn_send_2(argp, clnt, nid)
md_mn_msg_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
md_mn_result_t *clnt_res = Zalloc(sizeof (md_mn_result_t));
res = clnt_call(clnt, mdmn_send,
(xdrproc_t)xdr_md_mn_msg_t, (caddr_t)argp,
(xdrproc_t)xdr_md_mn_result_t, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_send", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_work_2(argp, clnt, nid)
md_mn_msg_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_work,
(xdrproc_t)xdr_md_mn_msg_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_work", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_wakeup_initiator_2(argp, clnt, nid)
md_mn_result_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_wakeup_initiator,
(xdrproc_t)xdr_md_mn_result_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_wakeup_initiator", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_wakeup_master_2(argp, clnt, nid)
md_mn_result_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_wakeup_master,
(xdrproc_t)xdr_md_mn_result_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_wakeup_master", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_comm_lock_2(argp, clnt, nid)
md_mn_set_and_class_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_comm_lock,
(xdrproc_t)xdr_md_mn_set_and_class_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_comm_lock", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_comm_unlock_2(argp, clnt, nid)
md_mn_set_and_class_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_comm_unlock,
(xdrproc_t)xdr_md_mn_set_and_class_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_comm_unlock", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_comm_suspend_2(argp, clnt, nid)
md_mn_set_and_class_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_comm_suspend,
(xdrproc_t)xdr_md_mn_set_and_class_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_comm_suspend", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_comm_resume_2(argp, clnt, nid)
md_mn_set_and_class_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_comm_resume,
(xdrproc_t)xdr_md_mn_set_and_class_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_comm_resume", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_comm_reinit_set_2(argp, clnt, nid)
set_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_comm_reinit_set,
(xdrproc_t)xdr_set_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_comm_reinit_set", nid);
Free(clnt_res);
return (NULL);
}
int *
mdmn_comm_msglock_2(argp, clnt, nid)
md_mn_type_and_lock_t *argp;
CLIENT *clnt;
md_mn_nodeid_t nid;
{
enum clnt_stat res;
int *clnt_res = Zalloc(sizeof (int));
res = clnt_call(clnt, mdmn_comm_msglock,
(xdrproc_t)xdr_md_mn_type_and_lock_t, (caddr_t)argp,
(xdrproc_t)xdr_int, (caddr_t)clnt_res, TIMEOUT);
if (res == RPC_SUCCESS) {
return (clnt_res);
}
mdmn_handle_RPC_error(clnt, "mdmn_comm_msglock", nid);
Free(clnt_res);
return (NULL);
}
#define USECS_PER_TICK 10000
/*
* Let the kernel create a clusterwide unique message ID
*
* returns 0 on success
* 1 on failure
*/
int
mdmn_create_msgid(md_mn_msgid_t *msgid)
{
md_error_t mde = mdnullerror;
if (msgid == NULL) {
return (1); /* failure */
}
if (metaioctl(MD_IOCGUNIQMSGID, msgid, &mde, NULL) != 0) {
msgid->mid_nid = ~0u;
msgid->mid_time = 0LL;
return (1); /* failure */
}
/*
* mid_smid and mid_oclass are only used for submessages.
* mdmn_create_msgid is never called for submessages, as they inherit
* the message ID from their parent.
* Thus we can safely null out the following fields.
*/
msgid->mid_smid = 0;
msgid->mid_oclass = 0;
/* if the node_id is not set yet, somethings seems to be wrong */
if (msgid->mid_nid == ~0u) {
return (1); /* failure */
}
return (0); /* success */
}
md_mn_result_t *
copy_result(md_mn_result_t *res)
{
md_mn_result_t *nres;
nres = Zalloc(sizeof (md_mn_result_t));
/* It's MSGID_COPY(from, to); */
MSGID_COPY(&(res->mmr_msgid), &(nres->mmr_msgid));
nres->mmr_msgtype = res->mmr_msgtype;
nres->mmr_setno = res->mmr_setno;
nres->mmr_flags = res->mmr_flags;
nres->mmr_sender = res->mmr_sender;
nres->mmr_failing_node = res->mmr_failing_node;
nres->mmr_comm_state = res->mmr_comm_state;
nres->mmr_exitval = res->mmr_exitval;
nres->mmr_out_size = res->mmr_out_size;
nres->mmr_err_size = res->mmr_err_size;
if (res->mmr_out_size > 0) {
nres->mmr_out = Zalloc(res->mmr_out_size);
bcopy(res->mmr_out, nres->mmr_out, res->mmr_out_size);
}
if (res->mmr_err_size > 0) {
nres->mmr_err = Zalloc(res->mmr_err_size);
bcopy(res->mmr_err, nres->mmr_err, res->mmr_err_size);
}
if (res->mmr_ep.host != '\0') {
nres->mmr_ep.host = strdup(res->mmr_ep.host);
}
if (res->mmr_ep.extra != '\0') {
nres->mmr_ep.extra = strdup(res->mmr_ep.extra);
}
if (res->mmr_ep.name != '\0') {
nres->mmr_ep.name = strdup(res->mmr_ep.name);
}
return (nres);
}
void
free_result(md_mn_result_t *res)
{
if (res->mmr_out_size > 0) {
Free(res->mmr_out);
}
if (res->mmr_err_size > 0) {
Free(res->mmr_err);
}
if (res->mmr_ep.host != '\0') {
Free(res->mmr_ep.host);
}
if (res->mmr_ep.extra != '\0') {
Free(res->mmr_ep.extra);
}
if (res->mmr_ep.name != '\0') {
Free(res->mmr_ep.name);
}
Free(res);
}
/* allocate a new message and copy a given message into it */
md_mn_msg_t *
copy_msg(md_mn_msg_t *msg, md_mn_msg_t *dest)
{
md_mn_msg_t *nmsg;
nmsg = dest;
if (nmsg == NULL) {
nmsg = Zalloc(sizeof (md_mn_msg_t));
}
if (nmsg->msg_event_data == NULL) {
nmsg->msg_event_data = Zalloc(msg->msg_event_size);
}
/* It's MSGID_COPY(from, to); */
MSGID_COPY(&(msg->msg_msgid), &(nmsg->msg_msgid));
nmsg->msg_sender = msg->msg_sender;
nmsg->msg_flags = msg->msg_flags;
nmsg->msg_setno = msg->msg_setno;
nmsg->msg_type = msg->msg_type;
nmsg->msg_recipient = msg->msg_recipient;
nmsg->msg_event_size = msg->msg_event_size;
if (msg->msg_event_size > 0) {
bcopy(msg->msg_event_data, nmsg->msg_event_data,
msg->msg_event_size);
}
return (nmsg);
}
void
copy_msg_2(md_mn_msg_t *msg, md_mn_msg_od_t *msgod, int direction)
{
assert((direction == MD_MN_COPY_TO_ONDISK) ||
(direction == MD_MN_COPY_TO_INCORE));
if (direction == MD_MN_COPY_TO_ONDISK) {
MSGID_COPY(&(msg->msg_msgid), &(msgod->msg_msgid));
msgod->msg_sender = msg->msg_sender;
msgod->msg_flags = msg->msg_flags;
msgod->msg_setno = msg->msg_setno;
msgod->msg_type = msg->msg_type;
msgod->msg_recipient = msg->msg_recipient;
msgod->msg_od_event_size = msg->msg_event_size;
/* paranoid checks */
if (msg->msg_event_size != 0 && msg->msg_event_data != NULL)
bcopy(msg->msg_event_data,
&msgod->msg_od_event_data[0], msg->msg_event_size);
} else {
MSGID_COPY(&(msgod->msg_msgid), &(msg->msg_msgid));
msg->msg_sender = msgod->msg_sender;
msg->msg_flags = msgod->msg_flags;
msg->msg_setno = msgod->msg_setno;
msg->msg_type = msgod->msg_type;
msg->msg_recipient = msgod->msg_recipient;
msg->msg_event_size = msgod->msg_od_event_size;
if (msg->msg_event_data == NULL)
msg->msg_event_data = Zalloc(msg->msg_event_size);
bcopy(&msgod->msg_od_event_data[0],
msg->msg_event_data, msgod->msg_od_event_size);
}
}
/* Free a message */
void
free_msg(md_mn_msg_t *msg)
{
if (msg->msg_event_size > 0) {
Free(msg->msg_event_data);
}
Free(msg);
}
/* The following declarations are only for the next two routines */
md_mn_client_list_t *mdmn_clients;
mutex_t mcl_mutex;
#define MNGLC_INIT_ONLY 0x0001
#define MNGLC_FOR_REAL 0x0002
/*
* mdmn_get_local_clnt(flag)
* If there is a client in the free pool, get one,
* If no client is available, create one.
* Every multithreaded application that uses mdmn_send_message must call it
* single threaded first with special flags so we do the initialization
* stuff in a safe environment.
*
* Input: MNGLC_INIT_ONLY: just initializes the mutex
* MNGLC_FOR_REAL : do real work
* Output:
* An rpc client for sending rpc requests to the local commd
* NULL in case of an error
*
*/
static CLIENT *
mdmn_get_local_clnt(uint_t flag)
{
CLIENT *local_daemon;
static int inited = 0;
md_mn_client_list_t *tmp;
if (inited == 0) {
(void) mutex_init(&mcl_mutex, USYNC_THREAD, NULL);
inited = 1;
}
if (flag == MNGLC_INIT_ONLY)
return ((CLIENT *)NULL);
(void) mutex_lock(&mcl_mutex);
if (mdmn_clients == (md_mn_client_list_t *)NULL) {
/* if there is no entry, create a client and return a it */
local_daemon = meta_client_create(LOCALHOST_IPv4, MDMN_COMMD,
TWO, "tcp");
} else {
/*
* If there is an entry from a previous put operation,
* remove it from the head of the list and free the list stuff
* around it. Then return the client
*/
local_daemon = mdmn_clients->mcl_clnt;
tmp = mdmn_clients;
mdmn_clients = mdmn_clients->mcl_next;
Free(tmp);
}
(void) mutex_unlock(&mcl_mutex);
if (local_daemon == (CLIENT *)NULL) {
clnt_pcreateerror("local_daemon");
}
return (local_daemon);
}
/*
* mdmn_put_local_clnt()
* returns a no longer used client to the pool
*
* Input: an RPC client
* Output: void
*/
static void
mdmn_put_local_clnt(CLIENT *local_daemon)
{
md_mn_client_list_t *tmp;
(void) mutex_lock(&mcl_mutex);
tmp = mdmn_clients;
mdmn_clients = (md_mn_client_list_t *)
malloc(sizeof (md_mn_client_list_t));
mdmn_clients->mcl_clnt = local_daemon;
mdmn_clients->mcl_next = tmp;
(void) mutex_unlock(&mcl_mutex);
}
/*
* This is the regular interface for sending a message.
* This function only passes through all arguments to
* mdmn_send_message_with_msgid() and adds a NULL for the message ID.
*
* Normally, you don't have already a message ID for the message you want
* to send. Only in case of replaying a previously logged message,
* a msgid is already attached to it.
* In that case mdmn_send_message_with_msgid() has to be called directly.
*
* The recipient argument is almost always unused, and is therefore typically
* set to zero, as zero is an invalid cluster nodeid. The exceptions are the
* marking and clearing of the DRL from a node that is not currently the
* owner. In these cases, the recipient argument will be the nodeid of the
* mirror owner, and MD_MSGF_DIRECTED will be set in the flags. Non-owner
* nodes will not receive these messages.
*
* Return values / CAVEAT EMPTOR: see mdmn_send_message_with_msgid()
*/
int
mdmn_send_message(
set_t setno,
md_mn_msgtype_t type,
uint_t flags,
md_mn_nodeid_t recipient,
char *data,
int size,
md_mn_result_t **result,
md_error_t *ep)
{
return (mdmn_send_message_with_msgid(setno, type, flags,
recipient, data, size, result, MD_NULL_MSGID, ep));
}
/*
* mdmn_send_message_with_msgid()
* Create a message from the given pieces of data and hand it over
* to the local commd.
* This may fail for various reasons (rpc error / class busy / class locked ...)
* Some error types are immediately deadly, others will cause retries
* until the request is fulfilled or until the retries are ecxceeded.
*
* In case an error is returned it is up to the user to decide what to do.
*
* Returns:
* 0 on success
* 1 if retries1 exceeded
* 2 if retries2 exceeded
* -1 if connecting to the local daemon failed
* -2 if the RPC call to the local daemon failed
* -3 if this node hasn't yet joined the set
* -4 if any other problem occured
*
* CAVEAT EMPTOR:
* The caller is responsible for calling free_result() when finished with
* the results!
*/
int
mdmn_send_message_with_msgid(
set_t setno,
md_mn_msgtype_t type,
uint_t flags,
md_mn_nodeid_t recipient,
char *data,
int size,
md_mn_result_t **result,
md_mn_msgid_t *msgid,
md_error_t *ep)
{
uint_t retry1, ticks1, retry2, ticks2;
int retval;
CLIENT *local_daemon;
struct timeval timeout;
md_mn_msg_t msg;
md_mn_result_t *resp;
/*
* Special case for multithreaded applications:
* When starting up, the application should call mdmn_send_message
* single threaded with all parameters set to NULL.
* When we detect this we know, we safely can do initialization
* stuff here.
* We only check for set and type being zero
*/
if ((setno == 0) && (type == 0)) {
/* do all needed initializations here */
(void) mdmn_get_local_clnt(MNGLC_INIT_ONLY);
return (0); /* success */
}
/* did the caller specify space to store the result pointer? */
if (result == (md_mn_result_t **)NULL) {
syslog(LOG_INFO, dgettext(TEXT_DOMAIN,
"FATAL, can not allocate result structure\n"));
return (-4);
}
*result = NULL;
/* Replay messages already have their msgID */
if ((flags & MD_MSGF_REPLAY_MSG) == 0) {
if (mdmn_create_msgid(&msg.msg_msgid) != 0) {
syslog(LOG_INFO, dgettext(TEXT_DOMAIN,
"FATAL, can not create message ID\n"));
return (-4);
}
} else {
/* in this case a message ID must be specified */
assert(msgid != MD_NULL_MSGID);
MSGID_COPY(msgid, &msg.msg_msgid);
}
/*
* When setting the flags, additionally apply the
* default flags for this message type.
*/
msg.msg_flags = flags;
msg.msg_setno = setno;
msg.msg_recipient = recipient;
msg.msg_type = type;
msg.msg_event_size = size;
msg.msg_event_data = data;
/*
* For the timeout pick the specific timeout for the message times the
* the maximum number of nodes.
* This is a better estimate than 1 hour or 3 days or never.
*/
timeout.tv_sec = mdmn_get_timeout(type) * NNODES;
timeout.tv_usec = 0;
if (flags & MD_MSGF_VERBOSE) {
syslog(LOG_INFO, "send_message: ID=(%d, 0x%llx-%d)\n",
MSGID_ELEMS(msg.msg_msgid));
}
/* get an RPC client to the local commd */
local_daemon = mdmn_get_local_clnt(MNGLC_FOR_REAL);
if (local_daemon == (CLIENT *)NULL) {
return (-1);
}
clnt_control(local_daemon, CLSET_TIMEOUT, (char *)&timeout);
retry1 = msg_table[type].mte_retry1;
ticks1 = msg_table[type].mte_ticks1;
retry2 = msg_table[type].mte_retry2;
ticks2 = msg_table[type].mte_ticks2;
/*
* run that loop until:
* - commstate is Ok
* - deadly commstate occured
* - retries1 or retries2 exceeded
*/
for (; ; ) {
*result = mdmn_send_2(&msg, local_daemon, 0);
resp = *result;
if (resp != (md_mn_result_t *)NULL) {
/* Bingo! */
if (resp->mmr_comm_state == MDMNE_ACK) {
retval = 0;
goto out;
}
/* Hmm... what if there's no handler? */
if (resp->mmr_comm_state == MDMNE_NO_HANDLER) {
retval = 0;
goto out;
}
/*
* This node didn't yet join the disk set. It is not
* supposed to send any messages then.
* This is deadly (no retries)
*/
if (resp->mmr_comm_state == MDMNE_NOT_JOINED) {
retval = -3;
goto out;
}
/* these two are deadly too (no retries) */
if ((resp->mmr_comm_state == MDMNE_NO_WAKEUP_ENTRY) ||
(resp->mmr_comm_state == MDMNE_LOG_FAIL)) {
retval = -4;
goto out;
}
/* Class busy? Use retry1 */
if (resp->mmr_comm_state == MDMNE_CLASS_BUSY) {
if (retry1-- == 0) {
retval = 1; /* retry1 exceeded */
goto out;
}
(void) usleep(ticks1 * USECS_PER_TICK);
free_result(resp);
if (flags & MD_MSGF_VERBOSE)
(void) printf("#Resend1 ID=(%d, "
"0x%llx-%d)\n",
MSGID_ELEMS(msg.msg_msgid));
continue;
}
if ((resp->mmr_comm_state == MDMNE_CLASS_LOCKED) ||
(resp->mmr_comm_state == MDMNE_ABORT)) {
/*
* Be patient, wait for 1 secs and try again.
* It's not likely that the ABORT condition ever
* goes away, but it won't hurt to retry
*/
free_result(resp);
(void) sleep(1);
continue;
}
if (resp->mmr_comm_state == MDMNE_SUSPENDED) {
if (flags & MD_MSGF_FAIL_ON_SUSPEND) {
/* caller wants us to fail here */
(void) mddserror(ep,
MDE_DS_NOTNOW_RECONFIG, setno,
mynode(), mynode(), NULL);
retval = -4;
goto out;
} else {
/* wait for 1 secs and try again. */
free_result(resp);
(void) sleep(1);
continue;
}
}
} else {
/*
* If we get a NULL back from the rpc call, try to
* reinitialize the client.
* Depending on retries2 we try again, or not.
*/
syslog(LOG_INFO,
"send_message: ID=(%d, 0x%llx-%d) resp = NULL\n",
MSGID_ELEMS(msg.msg_msgid));
clnt_destroy(local_daemon);
local_daemon = mdmn_get_local_clnt(MNGLC_FOR_REAL);
if (local_daemon == (CLIENT *)NULL) {
return (-1);
}
clnt_control(local_daemon, CLSET_TIMEOUT,
(char *)&timeout);
}
/*
* If we are here, either resp is zero or resp is non-zero
* but some commstate not mentioned above occured.
* In either case we use retry2
*/
if (retry2-- == 0) {
syslog(LOG_INFO, dgettext(TEXT_DOMAIN,
"send_message: (%d, 0x%llx-%d) retry2 exceeded\n"),
MSGID_ELEMS(msg.msg_msgid));
retval = 2; /* retry2 exceeded */
goto out;
}
if (flags & MD_MSGF_VERBOSE) {
syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN,
"send_message: (%d, 0x%llx-%d) resend on retry2\n"),
MSGID_ELEMS(msg.msg_msgid));
}
(void) usleep(ticks2 * USECS_PER_TICK);
if (resp != (md_mn_result_t *)NULL) {
free_result(resp);
}
}
out:
mdmn_put_local_clnt(local_daemon);
return (retval);
}
/*
* suspend the commd for a given set/class combination.
*
* Parameter:
* set number or 0 (meaning all sets)
* class number or 0 (meaning all classes)
*
* Returns:
* 0 on success (set is suspended and all messages drained)
* MDE_DS_COMMDCTL_SUSPEND_NYD if set is not yet drained
* MDE_DS_COMMDCTL_SUSPEND_FAIL if any failure occurred
*/
int
mdmn_suspend(set_t setno, md_mn_msgclass_t class, long timeout)
{
int *resp;
CLIENT *local_daemon;
md_mn_set_and_class_t msc;
md_error_t xep = mdnullerror;
if ((setno >= MD_MAXSETS) || (class >= MD_MN_NCLASSES)) {
return (MDE_DS_COMMDCTL_SUSPEND_FAIL);
}
local_daemon = meta_client_create(LOCALHOST_IPv4, MDMN_COMMD, TWO,
"tcp");
if (local_daemon == (CLIENT *)NULL) {
clnt_pcreateerror("local_daemon");
return (MDE_DS_COMMDCTL_SUSPEND_FAIL);
}
if (timeout != 0) {
if (cl_sto(local_daemon, LOCALHOST_IPv4, timeout, &xep) != 0) {
clnt_destroy(local_daemon);
return (1);
}
}
msc.msc_set = setno;
msc.msc_class = class;
msc.msc_flags = 0;
resp = mdmn_comm_suspend_2(&msc, local_daemon, 0);
clnt_destroy(local_daemon);
if (resp == NULL) {
return (MDE_DS_COMMDCTL_SUSPEND_FAIL);
}
if (*resp == MDMNE_ACK) {
/* set successfully drained, no outstanding messages */
return (0);
}
if (*resp != MDMNE_SET_NOT_DRAINED) {
/* some error occurred */
return (MDE_DS_COMMDCTL_SUSPEND_FAIL);
}
/* still outstanding messages, return not yet drained failure */
return (MDE_DS_COMMDCTL_SUSPEND_NYD);
}
/*
* resume the commd for a given set/class combination.
*
* Parameter:
* set number or 0 (meaning all sets)
* class number or 0 (meaning all classes)
*
* Returns:
* 0 on success
* MDE_DS_COMMDCTL_RESUME_FAIL on failure
*/
int
mdmn_resume(set_t setno, md_mn_msgclass_t class, uint_t flags, long timeout)
{
md_mn_set_and_class_t msc;
int ret = MDE_DS_COMMDCTL_RESUME_FAIL;
int *resp;
CLIENT *local_daemon;
md_error_t xep = mdnullerror;
if ((setno >= MD_MAXSETS) || (class >= MD_MN_NCLASSES)) {
return (MDE_DS_COMMDCTL_RESUME_FAIL);
}
local_daemon = meta_client_create(LOCALHOST_IPv4, MDMN_COMMD, TWO,
"tcp");
if (local_daemon == (CLIENT *)NULL) {
clnt_pcreateerror("local_daemon");
return (MDE_DS_COMMDCTL_RESUME_FAIL);
}
if (timeout != 0) {
if (cl_sto(local_daemon, LOCALHOST_IPv4, timeout, &xep) != 0) {
clnt_destroy(local_daemon);
return (1);
}
}
msc.msc_set = setno;
msc.msc_class = class;
msc.msc_flags = flags;
resp = mdmn_comm_resume_2(&msc, local_daemon, 0);
if (resp != NULL) {
if (*resp == MDMNE_ACK) {
ret = 0;
}
Free(resp);
}
clnt_destroy(local_daemon);
return (ret);
}
/*
* abort all communication
*
* returns void, because: if *this* get's an error what do you want to do?
*/
void
mdmn_abort(void)
{
char *dummy = "abort";
md_mn_result_t *resultp = NULL;
md_error_t mdne = mdnullerror;
(void) mdmn_send_message(0, /* No set is needed for this message */
MD_MN_MSG_ABORT, MD_MSGF_LOCAL_ONLY, 0,
dummy, sizeof (dummy), &resultp, &mdne);
if (resultp != NULL) {
Free(resultp);
}
}
/*
* trigger the reinitialization for a given set.
*
* Parameter: set number
*
* Returns:
* 0 on success
* 1 on failure
*/
int
mdmn_reinit_set(set_t setno, long timeout)
{
int ret = 1;
int *resp;
CLIENT *local_daemon;
md_error_t xep = mdnullerror;
if ((setno == 0) || (setno >= MD_MAXSETS)) {
return (1);
}
local_daemon = meta_client_create(LOCALHOST_IPv4, MDMN_COMMD, TWO,
"tcp");
if (local_daemon == (CLIENT *)NULL) {
clnt_pcreateerror("local_daemon");
return (1);
}
if (timeout != 0) {
if (cl_sto(local_daemon, LOCALHOST_IPv4, timeout, &xep) != 0) {
clnt_destroy(local_daemon);
return (1);
}
}
resp = mdmn_comm_reinit_set_2(&setno, local_daemon, 0);
if (resp != NULL) {
if (*resp == MDMNE_ACK) {
ret = 0;
}
Free(resp);
}
clnt_destroy(local_daemon);
return (ret);
}
/*
* Lock a single message type from being processed on this node
*
* Parameter: md_mn_msgtype_t msgtype, uint_t locktype
*
* Returns:
* 0 on success
* 1 on failure
*/
int
mdmn_msgtype_lock(md_mn_msgtype_t msgtype, uint_t locktype)
{
int ret = 1;
int *resp;
CLIENT *local_daemon;
md_mn_type_and_lock_t mmtl;
if ((msgtype == 0) || (msgtype >= MD_MN_NMESSAGES)) {
return (1);
}
local_daemon = meta_client_create(LOCALHOST_IPv4, MDMN_COMMD, TWO,
"tcp");
if (local_daemon == (CLIENT *)NULL) {
clnt_pcreateerror("local_daemon");
return (1);
}
mmtl.mmtl_type = msgtype;
mmtl.mmtl_lock = locktype;
resp = mdmn_comm_msglock_2(&mmtl, local_daemon, 0);
if (resp != NULL) {
if (*resp == MDMNE_ACK) {
ret = 0;
}
Free(resp);
}
clnt_destroy(local_daemon);
return (ret);
}