/*
* 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
* 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
*/
/*
*/
/*
* protocol.c - protocols between graph engine and restarters
*
* The graph engine uses restarter_protocol_send_event() to send a
* restarter_event_type_t to the restarter. For delegated restarters,
* this is published on the GPEC queue for the restarter, which can
* then be consumed by the librestart interfaces. For services managed
* by svc.startd, the event is stored on the local restarter_queue list,
* where it can be dequeued by the restarter.
*
* The svc.startd restarter uses graph_protocol_send_event() to send
* a graph_event_type_t to the graph engine when an instance's states are
* updated.
*
* The graph engine uses restarter_protocol_init_delegate() to
* register its interest in a particular delegated restarter's instance
* state events. The state_cb() registered on the event channel then
* invokes graph_protocol_send_event() to communicate the update to
* the graph engine.
*/
#include <assert.h>
#include <libintl.h>
#include <libsysevent.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <strings.h>
#include <errno.h>
#include <libuutil.h>
#include <librestart.h>
#include <librestart_priv.h>
#include "protocol.h"
#include "startd.h"
/* Local event queue structures. */
typedef struct graph_protocol_event_queue {
typedef struct restarter_protocol_event_queue {
void
{
"graph_protocol_events", sizeof (graph_protocol_event_t),
}
/*
* "data" will be freed by the consumer
*/
static void
{
e = startd_zalloc(sizeof (graph_protocol_event_t));
e->gpe_inst_sz = size;
}
uu_die("failed to enqueue graph event (%s: %s)\n",
}
void
{
(void) pthread_mutex_destroy(&e->gpe_lock);
startd_free(e, sizeof (graph_protocol_event_t));
}
/*
* graph_protocol_event_t *graph_event_dequeue()
* The caller must hold gu_lock, and is expected to be a single thread.
* It is allowed to utilize graph_event_requeue() and abort processing
* on the event. If graph_event_requeue() is not called, the caller is
* expected to call graph_event_release() when finished.
*/
{
if (e == NULL) {
return (NULL);
}
return (e);
}
/*
* void graph_event_requeue()
* Requeue the event back at the head of the queue.
*/
void
{
uu_die("failed to requeue graph event (%s: %s)\n",
}
void
{
}
void
{
"restarter_protocol_events", sizeof (restarter_protocol_event_t),
sizeof (restarter_protocol_event_queue_t));
}
/*
* void restarter_event_enqueue()
* Enqueue a restarter event.
*/
static void
{
int r;
/* Allocate and populate the event structure. */
e = startd_zalloc(sizeof (restarter_protocol_event_t));
e->rpe_reason = reason;
assert(r == 0);
}
void
{
startd_free(e, sizeof (restarter_protocol_event_t));
}
/*
* restarter_protocol_event_t *restarter_event_dequeue()
* Dequeue a restarter protocol event. The caller is expected to be
* a single thread. It is allowed to utilize restarter_event_requeue()
* and abort processing on the event. The caller is expected to call
* restarter_event_release() when finished.
*/
{
if (e == NULL) {
return (NULL);
}
return (e);
}
static int
{
char *instance_name;
int err;
/*
* Might fail due to a bad event or a lack of memory. Try
* the callback again to see if it goes better the next time.
*/
return (EAGAIN);
&state) != 0) ||
&next_state) != 0) ||
&instance_name) != 0) ||
0))
states);
sizeof (str_next_state));
return (0);
}
evchan_t *
{
int r = 0;
/* master restarter -- nothing to do */
uu_warn("Attempt to initialize restarter protocol delegate "
"with %s\n", fmri);
return (NULL);
}
fmri);
RESTARTER_CHANNEL_DELEGATE)) == NULL ||
RESTARTER_CHANNEL_MASTER)) == NULL ||
if (delegate_channel_name) {
}
if (master_channel_name) {
}
uu_warn("Allocation of channel name failed");
return (NULL);
}
EVCH_CREAT|EVCH_HOLD_PEND)) != 0) {
uu_warn("%s: sysevent_evc_bind failed: %s\n",
goto out;
}
EVCH_CREAT|EVCH_HOLD_PEND)) != 0) {
uu_warn("%s: sysevent_evc_bind failed: %s\n",
goto out;
}
"%s: Bound to channel %s (delegate), %s (master)\n", fmri,
/*
* The following errors can be returned in this
* case :
* EINVAL : inappropriate flags or dump flag
* and the dump failed.
* EEXIST : svc.startd already has a channel
* named as the master channel name
* ENOMEM : too many subscribers to the channel
*/
uu_warn("Failed to subscribe to restarter %s, channel %s with "
switch (r) {
case EEXIST:
uu_warn("Channel name already exists\n");
break;
case ENOMEM:
uu_warn("Too many subscribers for the channel\n");
break;
default:
}
} else {
"%s: Subscribed to channel %s with subscriber id %s\n",
}
out:
if (r == 0)
return (delegate_channel);
return (NULL);
}
void
{
int ret;
/*
* If the service is managed by the master restarter,
* queue the event locally.
*/
return;
}
/*
* Otherwise, send the event to the delegate.
*/
reason) != 0)
uu_die("Allocation failure\n");
switch (ret) {
case ENOSPC:
"Delegate may not be running.\n",
break;
default:
}
}
if (event != RESTARTER_EVENT_TYPE_ADD_INSTANCE) {
/*
* Not relevant for graph loading.
*/
return;
}
/*
* For the purposes of loading state after interruption, this is
* sufficient, as svc.startd(1M) won't receive events on the contracts
* associated with each delegate.
*/
if (--st->st_load_instances == 0)
}