libsysevent.c revision 004388ebfdfe2ed7dfd2d153a876dfcc22d2c006
/*
* 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
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <door.h>
#include <unistd.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <synch.h>
#include <pthread.h>
#include <thread.h>
#include <libnvpair.h>
#include <assert.h>
#include <sys/sysevent.h>
#include <sys/sysevent_impl.h>
#include "libsysevent.h"
#include "libsysevent_impl.h"
/*
* libsysevent - The system event framework library
*
* This library provides routines to help with marshalling
* and unmarshalling of data contained in a sysevent event
* buffer.
*/
#define SE_ENCODE_METHOD NV_ENCODE_NATIVE
static int libsysevent_debug = 0;
/*
* The following routines allow system event publication to the sysevent
* framework.
*/
/*
* sysevent_alloc - allocate a sysevent buffer
*/
static sysevent_t *
{
int payload_sz;
char *attr;
sysevent_t *ev;
!= 0) {
return (NULL);
}
}
/*
* Calculate and reserve space for the class, subclass and
* publisher strings in the event buffer
*/
/* String sizes must be 64-bit aligned in the event buffer */
(aligned_subclass_sz - sizeof (uint64_t)) +
/*
* Allocate event buffer plus additional payload overhead.
*/
return (NULL);
}
/* Initialize the event buffer data */
/* Check for attribute list */
return (ev);
}
/* Copy attribute data to contiguous memory */
0) != 0) {
return (NULL);
}
return (ev);
}
/*
* sysevent_post_event - generate a system event via the sysevent framework
*/
int
{
int error;
sysevent_t *ev;
return (-1);
}
if (error) {
return (-1);
}
return (0);
}
/*
* The following routines are used to free or duplicate a
* sysevent event buffer.
*/
/*
* sysevent_dup - Allocate and copy an event buffer
* Copies both packed and unpacked to unpacked sysevent.
*/
{
/* Copy event header information */
return (NULL);
return (NULL);
}
return (copy);
}
/*
* sysevent_free - Free memory allocated for an event buffer
*/
void
{
if (attr_list)
}
/*
* The following routines are used to extract attribute data from a sysevent
* handle.
*/
/*
* sysevent_get_attr_list - allocate and return an attribute associated with
* the given sysevent buffer.
*/
int
{
int error;
/* Duplicate attribute for an unpacked sysevent buffer */
return (0);
}
} else {
}
return (-1);
}
return (0);
}
return (0);
}
/* unpack nvlist */
} else {
}
return (-1);
}
return (0);
}
/*
* sysevent_attr_name - Get name of attribute
*/
char *
{
return (NULL);
}
}
/*
* sysevent_attr_value - Get attribute value data and type
*/
int
{
return (EINVAL);
/* Convert DATA_TYPE_* to SE_DATA_TYPE_* */
switch (nvpair_type(nvp)) {
case DATA_TYPE_BYTE:
break;
case DATA_TYPE_INT16:
break;
case DATA_TYPE_UINT16:
break;
case DATA_TYPE_INT32:
break;
case DATA_TYPE_UINT32:
break;
case DATA_TYPE_INT64:
break;
case DATA_TYPE_UINT64:
break;
case DATA_TYPE_STRING:
break;
case DATA_TYPE_BYTE_ARRAY:
(void) nvpair_value_byte_array(nvp,
break;
case DATA_TYPE_HRTIME:
break;
default:
return (ENOTSUP);
}
return (0);
}
/*
* sysevent_attr_next - Get next attribute in event attribute list
*/
{
/* all user visible sysevent_t's are unpacked */
return (NULL);
}
}
/*
* sysevent_lookup_attr - Lookup attribute by name and datatype.
*/
int
{
return (ENOENT);
}
/*
* sysevent matches on both name and datatype
* nvlist_look mataches name only. So we walk
* nvlist manually here.
*/
while (nvp) {
return (0);
}
return (ENOENT);
}
/* Routines to extract event header information */
/*
* sysevent_get_class - Get class id
*/
int
{
}
/*
* sysevent_get_subclass - Get subclass id
*/
int
{
return (SE_SUBCLASS(ev));
}
/*
* sysevent_get_class_name - Get class name string
*/
char *
{
return (SE_CLASS_NAME(ev));
}
typedef enum {
} se_pub_id_t;
/*
* sysevent_get_pub - Get publisher name string
*/
char *
{
return (SE_PUB_NAME(ev));
}
/*
* Get the requested string pointed by the token.
*
* Return NULL if not found or for insufficient memory.
*/
static char *
{
int i;
for (i = 0; i <= token; ++i) {
return (NULL);
}
}
return (pub_element);
}
/*
* Return a pointer to the string following the token
*
* Note: This is a dedicated function for parsing
* publisher strings and not for general purpose.
*/
static const char *
{
int i;
for (i = 1; i <= token; i++) {
return (NULL);
pstr++;
}
/* String might be empty */
if (pstr) {
return (NULL);
}
return (pstr);
}
char *
{
}
char *
{
}
/*
* Provide the pid encoded in the publisher string
* w/o allocating any resouces.
*/
void
{
const char *part_str;
return;
return;
}
/*
* sysevent_get_subclass_name - Get subclass name string
*/
char *
{
return (SE_SUBCLASS_NAME(ev));
}
/*
* sysevent_get_seq - Get event sequence id
*/
{
}
/*
* sysevent_get_time - Get event timestamp
*/
void
{
}
/*
* sysevent_get_size - Get event buffer size
*/
{
}
/*
* The following routines are used by devfsadm_mod.c to propagate event
* buffers to devfsadmd. These routines will serve as the basis for
* event channel publication and subscription.
*/
/*
* sysevent_alloc_event -
* allocate a sysevent buffer for sending through an established event
* channel.
*/
{
char *pub_id;
sysevent_t *ev;
return (NULL);
}
if ((class_sz > MAX_CLASS_LEN) ||
(subclass_sz > MAX_SUBCLASS_LEN)) {
return (NULL);
}
/*
* Calculate the publisher size plus string seperators and maximum
* pid characters
*/
if (pub_sz > MAX_PUB_LEN) {
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (ev);
}
/*
* se_unpack - unpack nvlist to a searchable list.
* If already unpacked, will do a dup.
*/
static sysevent_t *
{
/* Copy event header information */
return (NULL);
/* unpack nvlist */
if (attr_len == 0) {
return (copy);
}
return (NULL);
}
return (copy);
}
/*
* se_print - Prints elements in an event buffer
*/
void
{
}
}
}
}
/*
* The following routines are provided to support establishment and use
* of sysevent channels. A sysevent channel is established between
* publishers and subscribers of sysevents for an agreed upon channel name.
* These routines currently support sysevent channels between user-level
* applications running on the same system.
*
* Sysevent channels may be created by a single publisher or subscriber process.
* Once established, up to MAX_SUBSRCIBERS subscribers may subscribe interest in
* receiving sysevent notifications on the named channel. At present, only
* one publisher is allowed per sysevent channel.
*
* The registration information for each channel is kept in the kernel. A
* kernel-based registration was chosen for persistence and reliability reasons.
* If either a publisher or a subscriber exits for any reason, the channel
* properties are maintained until all publishers and subscribers have exited.
* Additionally, an in-kernel registration allows the API to be extended to
* include kernel subscribers as well as userland subscribers in the future.
*
* To insure fast lookup of subscriptions, a cached copy of the registration
* is kept and maintained for the publisher process. Updates are made
* everytime a change is made in the kernel. Changes to the registration are
* expected to be infrequent.
*
* Channel communication between publisher and subscriber processes is
* implemented primarily via doors. Each publisher creates a door for
* registration notifications and each subscriber creates a door for event
* delivery.
*
* Most of these routines are used by syseventd(1M), the sysevent publisher
* for the syseventd channel. Processes wishing to receive sysevent
* notifications from syseventd may use a set of public
* APIs designed to subscribe to syseventd sysevents. The subscription
* APIs are implemented in accordance with PSARC/2001/076.
*
*/
/*
* Door handlers for the channel subscribers
*/
/*
* subscriber_event_handler - generic event handling wrapper for subscribers
* This handler is used to process incoming sysevent
* notifications from channel publishers.
* It is created as a seperate thread in each subscriber
* process per subscription.
*/
static void
{
for (;;) {
}
while (evqp) {
}
return;
}
}
/* NOTREACHED */
}
/*
* Data structure used to communicate event subscription cache updates
* to publishers via a registration door
*/
struct reg_args {
};
/*
* event_deliver_service - generic event delivery service routine. This routine
* is called in response to a door call to post an event.
*
*/
static void
{
int ret = 0;
goto return_from_door;
}
/* Publisher checking on subscriber */
ret = 0;
goto return_from_door;
}
goto return_from_door;
}
/*
* Mustn't block if we are trying to update the registration with
* the publisher
*/
goto return_from_door;
}
goto return_from_door;
}
goto return_from_door;
}
sizeof (sysevent_queue_t));
goto return_from_door;
}
/*
* Allocate and copy the event buffer into the subscriber's
* address space
*/
goto return_from_door;
}
} else {
}
}
/*
* Sysevent subscription information is maintained in the kernel. Updates
* to the in-kernel registration database is expected to be infrequent and
* offers consistency for publishers and subscribers that may come and go
* for a given channel.
*
* To expedite registration lookups by publishers, a cached copy of the
* kernel registration database is kept per-channel. Caches are invalidated
* and refreshed upon state changes to the in-kernel registration database.
*
* To prevent stale subscriber data, publishers may remove subsriber
* registrations from the in-kernel registration database in the event
* that a particular subscribing process is unresponsive.
*
* The following routines provide a mechanism to update publisher and subscriber
* information for a specified channel.
*/
/*
* clnt_deliver_event - Deliver an event through the consumer's event
* delivery door
*
* Returns -1 if message not delivered. With errno set to cause of error.
* Returns 0 for success with the results returned in posting buffer.
*/
static int
{
int error = 0;
/*
* Make door call
*/
continue;
} else {
break;
}
}
return (error);
}
static int
{
int pub_fd;
datasz);
return (-1);
}
if (result != 0) {
return (-1);
}
return (0);
}
/*
* update_kernel_registration - update the in-kernel registration for the
* given channel.
*/
static int
{
int error;
!= 0) {
return (error);
}
return (error);
}
/*
* get_kernel_registration - get the current subscriber registration for
* the given channel
*/
static nvlist_t *
{
char *nvlbuf;
return (NULL);
}
!= 0) {
/* Need a bigger buffer to hold channel registration */
return (NULL);
/* Try again */
return (NULL);
}
} else {
return (NULL);
}
}
return (NULL);
}
return (nvl);
}
/*
* The following routines provide a mechanism for publishers to maintain
* subscriber information.
*/
static void
{
int i;
for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
}
}
}
static int
{
char door_name[MAXPATHLEN];
return (0);
}
/* Allocate and initialize the subscriber data */
sizeof (subscriber_data_t));
return (-1);
}
return (-1);
}
return (-1);
}
return (0);
}
/*
* The following routines are used to update and maintain the registration cache
* for a particular sysevent channel.
*/
static uint32_t
hash_func(const char *s)
{
uint_t g;
while (*s != '\0') {
result <<= 4;
g = result & 0xf0000000;
if (g != 0) {
result ^= g >> 24;
result ^= g;
}
}
return (result);
}
{
return (NULL);
return (sc_list);
}
}
return (NULL);
}
static class_lst_t *
{
int index;
return (class_hash[0]);
}
break;
}
}
return (c_list);
}
static int
{
int i;
for (i = 0; i < subclass_num; ++i) {
!= NULL) {
} else {
sizeof (subclass_lst_t));
return (-1);
return (-1);
}
}
}
return (0);
}
static int
{
char *subclass_all = EC_SUB_ALL;
return (0);
}
/* New class, add to the registration cache */
return (1);
}
return (1);
}
return (1);
}
return (1);
}
}
/* Update the subclass list */
sub_id) != 0)
return (1);
return (0);
}
static void
{
int i;
for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
}
}
}
}
static void
{
return;
}
return;
}
}
}
static void
{
int i;
for (i = 0; i < CLASS_HASH_SZ + 1; i++) {
}
clist = next_clist;
}
}
}
static int
{
int i, j, new_class;
char *class_name;
for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
== NULL) {
class_hash[i] = NULL;
continue;
} else {
goto create_failed;
}
}
goto create_failed;
}
new_class = 1;
while (new_class) {
/* Extract the class name from the nvpair */
goto create_failed;
}
clist = (class_lst_t *)
goto create_failed;
}
goto create_failed;
}
/*
* Extract the subclass name and registration
* from the nvpair
*/
== NULL) {
goto create_failed;
}
class_hash[i] = clist;
for (;;) {
sizeof (subclass_lst_t));
goto create_failed;
}
goto create_failed;
}
&subscribers, &num_elem) != 0) {
goto create_failed;
}
MAX_SUBSCRIBERS + 1);
for (j = 1; j <= MAX_SUBSCRIBERS; ++j) {
continue;
goto create_failed;
}
}
/*
* Check next nvpair - either subclass or
* class
*/
== NULL) {
new_class = 0;
break;
CLASS_NAME) == 0) {
break;
}
}
}
}
return (0);
if (nvl)
return (-1);
}
/*
* cache_update_service - generic event publisher service routine. This routine
* is called in response to a registration cache update.
*
*/
static void
{
int ret = 0;
char *class, **event_list;
goto return_from_door;
}
case SE_UNREGISTER:
sub_id);
break;
case SE_UNBIND_REGISTRATION:
break;
break;
case SE_BIND_REGISTRATION:
/* New subscriber */
break;
}
break;
case SE_REGISTER:
break;
}
/* Get new registration data */
&nvl, 0) != 0) {
break;
}
break;
}
!= 0) {
break;
}
if (ret != 0) {
break;
}
break;
case SE_CLEANUP:
/* Cleanup stale subscribers */
break;
default:
}
}
/*
* sysevent_send_event -
* Send an event via the communication channel associated with the sysevent
* handle. Event notifications are broadcast to all subscribers based upon
* the event class and subclass. The handle must have been previously
* allocated and bound by
* sysevent_open_channel() and sysevent_bind_publisher()
*/
int
{
int deliver_error = 0;
int subscribers_sent = 0;
int want_resend, resend_cnt = 0;
char *event_class, *event_subclass;
/* Check for proper registration */
want_resend = 0;
return (-1);
}
if (sc_lst)
else
if (sc_lst)
else
/* Send event buffer to all valid subscribers */
for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
if ((all_class_subscribers[i] |
(subclass_subscribers && subclass_subscribers[i])) == 0)
continue;
/* Check for active subscriber */
dprint("sysevent_send_event: subscriber %d inactive\n",
i);
continue;
}
/* Process only resend requests */
continue;
}
dprint("sysevent_send_event: Failed to open "
continue;
}
result = 0;
/* Successful door call */
if (error == 0) {
switch (result) {
/* Subscriber requested EAGAIN */
case EAGAIN:
if (resend_cnt > SE_MAX_RETRY_LIMIT) {
deliver_error = 1;
} else {
want_resend = 1;
dprint("sysevent_send_event: resend "
"requested for %d\n", i);
}
break;
/* Bad sysevent handle for subscriber */
case EBADF:
case EINVAL:
dprint("sysevent_send_event: Bad sysevent "
deliver_error = 1;
break;
/* Successful delivery */
default:
}
} else {
dprint("sysevent_send_event: Failed door call "
deliver_error = 1;
}
}
if (want_resend) {
resend_cnt++;
goto send_event;
}
if (deliver_error) {
return (-1);
}
if (subscribers_sent == 0) {
dprint("sysevent_send_event: No subscribers for %s:%s\n",
return (-1);
}
return (0);
}
/*
* Common routine to establish an event channel through which an event
* publisher or subscriber may post or receive events.
*/
static sysevent_handle_t *
sysevent_open_channel_common(const char *channel_path)
{
char *begin_path;
return (NULL);
}
return (NULL);
}
}
/* Check channel file permissions */
dprint("sysevent_open_channel: Invalid permissions for channel "
"%s\n", channel_path);
return (NULL);
dprint("sysevent_open_channel: Invalid "
"permissions for channel %s\n: %d:%d:%d", channel_path,
return (NULL);
}
return (NULL);
}
return (NULL);
}
/* Extract the channel name */
while (*begin_path != '\0' &&
++begin_path;
}
if (update_kernel_registration(shp, 0,
dprint("sysevent_open_channel: Failed for channel %s\n",
return (NULL);
}
return (shp);
}
/*
* Establish a sysevent channel for publication and subscription
*/
sysevent_open_channel(const char *channel)
{
int var_run_mounted = 0;
struct extmnttab m;
return (NULL);
}
/*
* to be opened.
*/
return (NULL);
}
var_run_mounted = 1;
break;
}
}
if (!var_run_mounted) {
return (NULL);
}
dprint("sysevent_open_channel: Unable "
"to create channel directory %s:%s\n", CHAN_PATH,
return (NULL);
}
}
}
MAXPATHLEN) {
return (NULL);
}
return (sysevent_open_channel_common(full_channel));
}
/*
* Establish a sysevent channel for publication and subscription
* Full path to the channel determined by the caller
*/
sysevent_open_channel_alt(const char *channel_path)
{
return (sysevent_open_channel_common(channel_path));
}
/*
* sysevent_close_channel - Clean up resources associated with a previously
* opened sysevent channel
*/
void
{
return;
}
}
(void) update_kernel_registration(shp, 0,
}
/*
* sysevent_bind_publisher - Bind an event publisher to an event channel
*/
int
{
int error = 0;
int fd = -1;
char door_name[MAXPATHLEN];
return (-1);
}
return (-1);
}
NULL) {
return (-1);
}
return (-1);
}
return (-1);
}
/* Only one publisher allowed per channel */
goto fail;
}
}
/*
* Remove door file for robustness.
*/
dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
SH_DOOR_NAME(shp));
/* Open channel registration door */
if (fd == -1) {
goto fail;
}
/*
* Create the registration service for this publisher.
*/
dprint("sysevent_bind_publisher: door create failed: "
goto fail;
}
dprint("sysevent_bind_publisher: unable to "
"bind event channel: fattach: %s\n",
SH_DOOR_NAME(shp));
goto fail;
}
/* Bind this publisher in the kernel registration database */
goto fail;
}
/* Create the subscription registration cache */
(void) update_kernel_registration(shp,
goto fail;
}
return (0);
fail:
return (-1);
}
/*
* sysevent_bind_subscriber - Bind an event receiver to an event channel
*/
int
{
int fd = -1;
int error = 0;
char door_name[MAXPATHLEN];
return (-1);
}
return (-1);
}
sizeof (subscriber_priv_t))) == NULL) {
return (-1);
}
return (-1);
}
return (-1);
}
/* Update the in-kernel registration */
goto fail;
}
goto fail;
}
goto fail;
}
/*
* Remove door file for robustness.
*/
dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
SH_DOOR_NAME(shp));
if (fd == -1) {
goto fail;
}
/*
* Create the sysevent door service for this client.
* syseventd will use this door service to propagate
* events to the client.
*/
dprint("sysevent_bind_subscriber: door create failed: "
goto fail;
}
goto fail;
}
goto fail;
}
/* Create an event handler thread */
goto fail;
}
return (0);
fail:
}
}
return (-1);
}
/*
* sysevent_register_event - register an event class and associated subclasses
* for an event subscriber
*/
int
const char *ev_class, const char **ev_subclass,
int subclass_num)
{
int error;
char *event_class = (char *)ev_class;
char **event_subclass_list = (char **)ev_subclass;
subclass_num <= 0) {
return (-1);
}
return (-1);
}
subclass_num) != 0) {
return (-1);
}
return (-1);
}
/* Store new subscriber in in-kernel registration */
!= 0) {
return (-1);
}
/* Update the publisher's cached registration */
return (-1);
}
return (0);
}
/*
* sysevent_unregister_event - Unregister an event class and associated
* subclasses for an event subscriber
*/
void
{
return;
}
/* Remove subscriber from in-kernel registration */
/* Update the publisher's cached registration */
(void) update_publisher_cache(
}
static int
{
/* Remove registration from the kernel */
0, NULL) != 0) {
dprint("cleanup_id: Unable to clean "
return (-1);
}
return (0);
}
/*
* sysevent_cleanup_subscribers: Allows the caller to cleanup resources
* allocated to unresponsive subscribers.
*/
void
{
return;
}
for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
continue;
}
continue;
}
/* Check for valid and responsive subscriber */
/* Only cleanup on EBADF (Invalid door descriptor) */
continue;
continue;
}
}
/*
* sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
* as needed.
*/
void
{
}
/*
* sysevent_unbind_subscriber: Unbind the subscriber from the sysevent channel.
*/
void
{
return;
return;
}
/* Update the in-kernel registration */
/* Update the sysevent channel publisher */
/* Close down event delivery facilities */
/*
* Release resources and wait for pending event delivery to
* complete.
*/
/* Signal event handler and drain the subscriber's event queue */
}
/*
* sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
*/
void
{
return;
return;
}
/* Close down the registration facilities */
/* Update the in-kernel registration */
/* Free resources associated with bind */
}
/*
* Evolving APIs to subscribe to syseventd(1M) system events.
*/
/*
* sysevent_bind_handle - Bind application event handler for syseventd
* subscription.
*/
{
if (getuid() != 0) {
return (NULL);
}
if (event_handler == NULL) {
return (NULL);
}
return (NULL);
}
/*
* Ask syseventd to clean-up any stale subcribers and try to
* to bind again
*/
int pub_fd;
char door_name[MAXPATHLEN];
return (NULL);
}
/* Try to bind again */
return (NULL);
}
} else {
return (NULL);
}
}
return (shp);
}
/*
* sysevent_unbind_handle - Unbind caller from syseventd subscriptions
*/
void
{
}
/*
* sysevent_subscribe_event - Subscribe to system event notification from
* syseventd(1M) for the class and subclasses specified.
*/
int
const char **event_subclass_list, int num_subclasses)
{
}
void
{
}