#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <syslog.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "libnwam_impl.h"
#include <libnwam_priv.h>
#include <libnwam.h>
#define NWAM_EVENT_MSG_DIR "/etc/svc/volatile/nwam/"
#define NWAM_EVENT_MSG_FILE "nwam_event_msgs"
#define NWAM_EVENT_MSG_FILE_PREFIX NWAM_EVENT_MSG_DIR NWAM_EVENT_MSG_FILE
#define NWAM_EVENT_MAX_SIZE (sizeof (struct nwam_event) + \
(NWAMD_MAX_NUM_WLANS * sizeof (nwam_wlan_t)))
#define NWAM_EVENT_WAIT_TIME 10
#define NWAM_EVENT_MAX_NUM_PENDING 25
static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER;
static int event_msqid = -1;
static nwam_error_t
nwam_event_alloc(nwam_event_t *eventp)
{
assert(eventp != NULL);
*eventp = calloc(1, NWAM_EVENT_MAX_SIZE);
if (*eventp == NULL)
return (NWAM_NO_MEMORY);
return (NWAM_SUCCESS);
}
void
nwam_event_free(nwam_event_t event)
{
if (event != NULL)
free(event);
}
nwam_error_t
nwam_event_wait(nwam_event_t *eventp)
{
nwam_error_t err;
nwam_event_t event;
assert(eventp != NULL);
if ((err = nwam_event_alloc(&event)) != NWAM_SUCCESS)
return (err);
while (msgrcv(event_msqid, (struct msgbuf *)event, NWAM_EVENT_MAX_SIZE,
0, 0) == -1) {
switch (errno) {
case EAGAIN:
case EBUSY:
continue;
default:
nwam_event_free(event);
return (nwam_errno_to_nwam_error(errno));
}
}
if ((*eventp = realloc(event, event->nwe_size)) == NULL)
return (NWAM_NO_MEMORY);
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_events_init(void)
{
char eventmsgfile[MAXPATHLEN];
nwam_error_t err;
nwam_error_t rc = NWAM_SUCCESS;
key_t key;
(void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s.%d",
NWAM_EVENT_MSG_FILE_PREFIX, getpid());
(void) pthread_mutex_lock(&event_mutex);
if (event_msqid != -1) {
rc = NWAM_ENTITY_IN_USE;
goto exit;
}
if ((err = nwam_request_register_unregister
(NWAM_REQUEST_TYPE_EVENT_REGISTER, eventmsgfile)) != NWAM_SUCCESS) {
rc = err;
goto exit;
}
if ((key = ftok(eventmsgfile, 0)) == -1) {
rc = nwam_errno_to_nwam_error(errno);
goto exit;
}
if ((event_msqid = msgget(key, 0444)) == -1) {
rc = nwam_errno_to_nwam_error(errno);
goto exit;
}
exit:
(void) pthread_mutex_unlock(&event_mutex);
return (rc);
}
void
nwam_events_fini(void)
{
char eventmsgfile[MAXPATHLEN];
(void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s.%d",
NWAM_EVENT_MSG_FILE_PREFIX, getpid());
(void) pthread_mutex_lock(&event_mutex);
(void) nwam_request_register_unregister
(NWAM_REQUEST_TYPE_EVENT_UNREGISTER, eventmsgfile);
event_msqid = -1;
(void) pthread_mutex_unlock(&event_mutex);
}
nwam_error_t
nwam_event_queue_init(const char *eventmsgfile)
{
int fd;
key_t key;
if ((fd = open(eventmsgfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) == -1)
return (nwam_errno_to_nwam_error(errno));
(void) close(fd);
if ((key = ftok(eventmsgfile, 0)) == -1)
return (nwam_errno_to_nwam_error(errno));
if (msgget(key, 0644 | IPC_CREAT) == -1)
return (nwam_errno_to_nwam_error(errno));
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_event_send(nwam_event_t event)
{
DIR *dirp;
struct dirent *dp;
struct msqid_ds buf;
key_t key;
int msqid;
char eventmsgfile[MAXPATHLEN];
nwam_error_t err = NWAM_SUCCESS;
if ((dirp = opendir(NWAM_EVENT_MSG_DIR)) == NULL) {
return (nwam_errno_to_nwam_error(errno));
}
while ((dp = readdir(dirp)) != NULL) {
if (strncmp(dp->d_name, NWAM_EVENT_MSG_FILE,
strlen(NWAM_EVENT_MSG_FILE)) != 0)
continue;
(void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s/%s",
NWAM_EVENT_MSG_DIR, dp->d_name);
if ((key = ftok(eventmsgfile, 0)) == -1) {
int errno_save = errno;
syslog(LOG_INFO, "nwam_event_send: ftok: %s",
strerror(errno_save));
err = nwam_errno_to_nwam_error(errno_save);
continue;
}
if ((msqid = msgget(key, 0644)) == -1) {
int errno_save = errno;
syslog(LOG_INFO, "nwam_event_send: msgget: %s",
strerror(errno_save));
err = nwam_errno_to_nwam_error(errno_save);
continue;
}
if (msgctl(msqid, IPC_STAT, &buf) == -1) {
int errno_save = errno;
syslog(LOG_INFO, "nwam_event_send: msgctl: %s",
strerror(errno_save));
err = nwam_errno_to_nwam_error(errno_save);
continue;
}
if (buf.msg_qnum > NWAM_EVENT_MAX_NUM_PENDING &&
((buf.msg_stime + NWAM_EVENT_WAIT_TIME) > buf.msg_rtime)) {
nwam_event_queue_fini(eventmsgfile);
continue;
}
if (msgsnd(msqid, (struct msgbuf *)event, event->nwe_size,
IPC_NOWAIT) == -1) {
int errno_save = errno;
syslog(LOG_ERR, "nwam_event_send: msgsnd: %s, "
"destroying message queue %s", strerror(errno_save),
eventmsgfile);
nwam_event_queue_fini(eventmsgfile);
err = nwam_errno_to_nwam_error(errno_save);
continue;
}
}
(void) closedir(dirp);
return (err);
}
void
nwam_event_queue_fini(const char *eventmsgfile)
{
key_t key;
int msqid;
if ((key = ftok(eventmsgfile, 0)) != -1 &&
(msqid = msgget(key, 0644)) != -1 &&
msgctl(msqid, IPC_RMID, NULL) != -1)
(void) unlink(eventmsgfile);
}
void
nwam_event_send_fini(void)
{
DIR *dirp;
struct dirent *dp;
char eventmsgfile[MAXPATHLEN];
(void) pthread_mutex_lock(&event_mutex);
if ((dirp = opendir(NWAM_EVENT_MSG_DIR)) == NULL) {
(void) pthread_mutex_unlock(&event_mutex);
return;
}
while ((dp = readdir(dirp)) != NULL) {
if (strncmp(dp->d_name, NWAM_EVENT_MSG_FILE,
strlen(NWAM_EVENT_MSG_FILE)) != 0)
continue;
(void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s/%s",
NWAM_EVENT_MSG_DIR, dp->d_name);
nwam_event_queue_fini(eventmsgfile);
}
(void) pthread_mutex_unlock(&event_mutex);
}