4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
39 #include <sys/types.h>
42 #include "libnwam_impl.h"
43 #include <libnwam_priv.h>
47 * Implementation of event notification mechanism used by the GUI and
48 * nwamadm. Clients register for events via nwam_events_init() and
49 * unregister via nwam_events_fini(). nwamd sends events via nwam_event_send()
50 * and applications block waiting for a new event to be delivered in
51 * nwam_event_wait(). Events are implemented as System V message queues,
52 * one per event client. The event mechanism has to be resilient to
53 * nwamd restarts so that clients do not lose the event connection.
56 #define NWAM_EVENT_MSG_DIR "/etc/svc/volatile/nwam/"
57 #define NWAM_EVENT_MSG_FILE "nwam_event_msgs"
58 #define NWAM_EVENT_MSG_FILE_PREFIX NWAM_EVENT_MSG_DIR NWAM_EVENT_MSG_FILE
59 #define NWAM_EVENT_MAX_SIZE (sizeof (struct nwam_event) + \
60 (NWAMD_MAX_NUM_WLANS * sizeof (nwam_wlan_t)))
61 #define NWAM_EVENT_WAIT_TIME 10
62 #define NWAM_EVENT_MAX_NUM_PENDING 25
65 * This is protecting simultaneous access to the msqid and its configuration.
67 static pthread_mutex_t event_mutex
= PTHREAD_MUTEX_INITIALIZER
;
68 static int event_msqid
= -1;
71 nwam_event_alloc(nwam_event_t
*eventp
)
73 assert(eventp
!= NULL
);
75 *eventp
= calloc(1, NWAM_EVENT_MAX_SIZE
);
77 return (NWAM_NO_MEMORY
);
78 return (NWAM_SUCCESS
);
82 nwam_event_free(nwam_event_t event
)
88 * Get next event in queue.
91 nwam_event_wait(nwam_event_t
*eventp
)
96 assert(eventp
!= NULL
);
98 if ((err
= nwam_event_alloc(&event
)) != NWAM_SUCCESS
)
100 while (msgrcv(event_msqid
, (struct msgbuf
*)event
, NWAM_EVENT_MAX_SIZE
,
106 * We see this errno eventhough it isn't
107 * documented. Try again. If this causes
108 * a busy loop then grab a trace otherwise
109 * it's a brace 'til we can figure out why it
115 nwam_event_free(event
);
116 return (nwam_errno_to_nwam_error(errno
));
120 /* Resize event down from maximum size */
121 if ((*eventp
= realloc(event
, event
->nwe_size
)) == NULL
)
122 return (NWAM_NO_MEMORY
);
124 return (NWAM_SUCCESS
);
128 * Register for receipt of events from nwamd. Event delivery is
129 * done via a System V message queue.
132 nwam_events_init(void)
134 char eventmsgfile
[MAXPATHLEN
];
136 nwam_error_t rc
= NWAM_SUCCESS
;
139 (void) snprintf(eventmsgfile
, sizeof (eventmsgfile
), "%s.%d",
140 NWAM_EVENT_MSG_FILE_PREFIX
, getpid());
142 (void) pthread_mutex_lock(&event_mutex
);
144 if (event_msqid
!= -1) {
145 rc
= NWAM_ENTITY_IN_USE
;
149 if ((err
= nwam_request_register_unregister
150 (NWAM_REQUEST_TYPE_EVENT_REGISTER
, eventmsgfile
)) != NWAM_SUCCESS
) {
155 if ((key
= ftok(eventmsgfile
, 0)) == -1) {
156 rc
= nwam_errno_to_nwam_error(errno
);
160 /* Get system-wide message queue ID */
161 if ((event_msqid
= msgget(key
, 0444)) == -1) {
162 rc
= nwam_errno_to_nwam_error(errno
);
167 (void) pthread_mutex_unlock(&event_mutex
);
173 * Un-register for receipt of events from nwamd. Make a request to nwamd
174 * to destroy the message queue.
177 nwam_events_fini(void)
179 char eventmsgfile
[MAXPATHLEN
];
181 (void) snprintf(eventmsgfile
, sizeof (eventmsgfile
), "%s.%d",
182 NWAM_EVENT_MSG_FILE_PREFIX
, getpid());
184 (void) pthread_mutex_lock(&event_mutex
);
186 (void) nwam_request_register_unregister
187 (NWAM_REQUEST_TYPE_EVENT_UNREGISTER
, eventmsgfile
);
191 (void) pthread_mutex_unlock(&event_mutex
);
195 * Create an event queue. Called by nwamd to create System V message queues
196 * for clients to listen for events.
199 nwam_event_queue_init(const char *eventmsgfile
)
204 if ((fd
= open(eventmsgfile
, O_RDWR
| O_CREAT
| O_TRUNC
, 0644)) == -1)
205 return (nwam_errno_to_nwam_error(errno
));
208 if ((key
= ftok(eventmsgfile
, 0)) == -1)
209 return (nwam_errno_to_nwam_error(errno
));
211 if (msgget(key
, 0644 | IPC_CREAT
) == -1)
212 return (nwam_errno_to_nwam_error(errno
));
214 return (NWAM_SUCCESS
);
218 * Send event to registered listeners via the set of registered System V
222 nwam_event_send(nwam_event_t event
)
229 char eventmsgfile
[MAXPATHLEN
];
230 nwam_error_t err
= NWAM_SUCCESS
;
232 if ((dirp
= opendir(NWAM_EVENT_MSG_DIR
)) == NULL
) {
233 return (nwam_errno_to_nwam_error(errno
));
237 * For each file matching our event message queue file prefix,
238 * check the queue is still being read, and if so send the message.
240 while ((dp
= readdir(dirp
)) != NULL
) {
241 if (strncmp(dp
->d_name
, NWAM_EVENT_MSG_FILE
,
242 strlen(NWAM_EVENT_MSG_FILE
)) != 0)
245 (void) snprintf(eventmsgfile
, sizeof (eventmsgfile
), "%s/%s",
246 NWAM_EVENT_MSG_DIR
, dp
->d_name
);
248 if ((key
= ftok(eventmsgfile
, 0)) == -1) {
249 int errno_save
= errno
;
250 syslog(LOG_INFO
, "nwam_event_send: ftok: %s",
251 strerror(errno_save
));
252 err
= nwam_errno_to_nwam_error(errno_save
);
256 if ((msqid
= msgget(key
, 0644)) == -1) {
257 int errno_save
= errno
;
258 syslog(LOG_INFO
, "nwam_event_send: msgget: %s",
259 strerror(errno_save
));
260 err
= nwam_errno_to_nwam_error(errno_save
);
264 /* Retrieve stats to analyse queue activity */
265 if (msgctl(msqid
, IPC_STAT
, &buf
) == -1) {
266 int errno_save
= errno
;
267 syslog(LOG_INFO
, "nwam_event_send: msgctl: %s",
268 strerror(errno_save
));
269 err
= nwam_errno_to_nwam_error(errno_save
);
273 * If buf.msg_qnum > NWAM_EVENT_MAX_NUM_PENDING
274 * _and_ msg_stime is more than 10s after msg_rtime -
275 * indicating message(s) have been hanging around unclaimed -
276 * we destroy the queue as the client has most likely gone
277 * away. This can happen if a registered client hits Ctrl^C.
279 if (buf
.msg_qnum
> NWAM_EVENT_MAX_NUM_PENDING
&&
280 ((buf
.msg_stime
+ NWAM_EVENT_WAIT_TIME
) > buf
.msg_rtime
)) {
281 nwam_event_queue_fini(eventmsgfile
);
286 * This shouldn't ever block. If it does then log an error and
287 * clean up the queue.
289 if (msgsnd(msqid
, (struct msgbuf
*)event
, event
->nwe_size
,
291 int errno_save
= errno
;
292 syslog(LOG_ERR
, "nwam_event_send: msgsnd: %s, "
293 "destroying message queue %s", strerror(errno_save
),
295 nwam_event_queue_fini(eventmsgfile
);
296 err
= nwam_errno_to_nwam_error(errno_save
);
301 (void) closedir(dirp
);
307 * Destroy an event queue. Called by nwamd to destroy the associated message
311 nwam_event_queue_fini(const char *eventmsgfile
)
316 if ((key
= ftok(eventmsgfile
, 0)) != -1 &&
317 (msqid
= msgget(key
, 0644)) != -1 &&
318 msgctl(msqid
, IPC_RMID
, NULL
) != -1)
319 (void) unlink(eventmsgfile
);
323 * Stop sending events. Called by nwamd to destroy each System V message queue
327 nwam_event_send_fini(void)
331 char eventmsgfile
[MAXPATHLEN
];
333 (void) pthread_mutex_lock(&event_mutex
);
335 if ((dirp
= opendir(NWAM_EVENT_MSG_DIR
)) == NULL
) {
336 (void) pthread_mutex_unlock(&event_mutex
);
341 * For each file matching our event message queue file prefix,
342 * destroy the queue and message file.
344 while ((dp
= readdir(dirp
)) != NULL
) {
345 if (strncmp(dp
->d_name
, NWAM_EVENT_MSG_FILE
,
346 strlen(NWAM_EVENT_MSG_FILE
)) != 0)
349 (void) snprintf(eventmsgfile
, sizeof (eventmsgfile
), "%s/%s",
350 NWAM_EVENT_MSG_DIR
, dp
->d_name
);
352 nwam_event_queue_fini(eventmsgfile
);
354 (void) pthread_mutex_unlock(&event_mutex
);