1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <assert.h> 28 #include <dirent.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <pthread.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <strings.h> 35 #include <string.h> 36 #include <syslog.h> 37 #include <sys/msg.h> 38 #include <sys/stat.h> 39 #include <sys/types.h> 40 #include <unistd.h> 41 42 #include "libnwam_impl.h" 43 #include <libnwam_priv.h> 44 #include <libnwam.h> 45 46 /* 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. 54 */ 55 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_NUM_WLANS 32 60 #define NWAM_EVENT_MAX_SIZE (sizeof (struct nwam_event) + \ 61 (NWAM_EVENT_MAX_NUM_WLANS * sizeof (nwam_wlan_t))) 62 #define NWAM_EVENT_WAIT_TIME 10 63 #define NWAM_EVENT_MAX_NUM_PENDING 25 64 65 /* 66 * This is protecting simultaneous access to the msqid and its configuration. 67 */ 68 static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER; 69 static int event_msqid = -1; 70 71 static nwam_error_t 72 nwam_event_alloc(nwam_event_t *eventp) 73 { 74 assert(eventp != NULL); 75 76 *eventp = calloc(1, NWAM_EVENT_MAX_SIZE); 77 if (*eventp == NULL) 78 return (NWAM_NO_MEMORY); 79 return (NWAM_SUCCESS); 80 } 81 82 void 83 nwam_event_free(nwam_event_t event) 84 { 85 if (event != NULL) 86 free(event); 87 } 88 89 /* 90 * Get next event in queue. 91 */ 92 nwam_error_t 93 nwam_event_wait(nwam_event_t *eventp) 94 { 95 nwam_error_t err; 96 nwam_event_t event; 97 98 assert(eventp != NULL); 99 100 if ((err = nwam_event_alloc(&event)) != NWAM_SUCCESS) 101 return (err); 102 while (msgrcv(event_msqid, (struct msgbuf *)event, NWAM_EVENT_MAX_SIZE, 103 0, 0) == -1) { 104 switch (errno) { 105 case EAGAIN: 106 case EBUSY: 107 /* 108 * We see this errno eventhough it isn't 109 * documented. Try again. If this causes 110 * a busy loop then grab a trace otherwise 111 * it's a brace 'til we can figure out why it 112 * happens. 113 */ 114 continue; 115 116 default: 117 nwam_event_free(event); 118 return (nwam_errno_to_nwam_error(errno)); 119 } 120 } 121 122 /* Resize event down from maximum size */ 123 if ((*eventp = realloc(event, event->nwe_size)) == NULL) 124 return (NWAM_NO_MEMORY); 125 126 return (NWAM_SUCCESS); 127 } 128 129 /* 130 * Register for receipt of events from nwamd. Event delivery is 131 * done via a System V message queue. 132 */ 133 nwam_error_t 134 nwam_events_init(void) 135 { 136 char eventmsgfile[MAXPATHLEN]; 137 nwam_error_t err; 138 nwam_error_t rc = NWAM_SUCCESS; 139 key_t key; 140 141 (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s.%d", 142 NWAM_EVENT_MSG_FILE_PREFIX, getpid()); 143 144 (void) pthread_mutex_lock(&event_mutex); 145 146 if (event_msqid != -1) { 147 rc = NWAM_ENTITY_IN_USE; 148 goto exit; 149 } 150 151 if ((err = nwam_request_register_unregister 152 (NWAM_REQUEST_TYPE_EVENT_REGISTER, eventmsgfile)) != NWAM_SUCCESS) { 153 rc = err; 154 goto exit; 155 } 156 157 if ((key = ftok(eventmsgfile, 0)) == -1) { 158 rc = nwam_errno_to_nwam_error(errno); 159 goto exit; 160 } 161 162 /* Get system-wide message queue ID */ 163 if ((event_msqid = msgget(key, 0444)) == -1) { 164 rc = nwam_errno_to_nwam_error(errno); 165 goto exit; 166 } 167 168 exit: 169 (void) pthread_mutex_unlock(&event_mutex); 170 171 return (rc); 172 } 173 174 /* 175 * Un-register for receipt of events from nwamd. Make a request to nwamd 176 * to destroy the message queue. 177 */ 178 void 179 nwam_events_fini(void) 180 { 181 char eventmsgfile[MAXPATHLEN]; 182 183 (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s.%d", 184 NWAM_EVENT_MSG_FILE_PREFIX, getpid()); 185 186 (void) pthread_mutex_lock(&event_mutex); 187 188 (void) nwam_request_register_unregister 189 (NWAM_REQUEST_TYPE_EVENT_UNREGISTER, eventmsgfile); 190 191 event_msqid = -1; 192 193 (void) pthread_mutex_unlock(&event_mutex); 194 } 195 196 /* 197 * Create an event queue. Called by nwamd to create System V message queues 198 * for clients to listen for events. 199 */ 200 nwam_error_t 201 nwam_event_queue_init(const char *eventmsgfile) 202 { 203 int fd; 204 key_t key; 205 206 if ((fd = open(eventmsgfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) == -1) 207 return (nwam_errno_to_nwam_error(errno)); 208 (void) close(fd); 209 210 if ((key = ftok(eventmsgfile, 0)) == -1) 211 return (nwam_errno_to_nwam_error(errno)); 212 213 if (msgget(key, 0644 | IPC_CREAT) == -1) 214 return (nwam_errno_to_nwam_error(errno)); 215 216 return (NWAM_SUCCESS); 217 } 218 219 /* 220 * Send event to registered listeners via the set of registered System V 221 * message queues. 222 */ 223 nwam_error_t 224 nwam_event_send(nwam_event_t event) 225 { 226 DIR *dirp; 227 struct dirent *dp; 228 struct msqid_ds buf; 229 key_t key; 230 int msqid; 231 char eventmsgfile[MAXPATHLEN]; 232 nwam_error_t err = NWAM_SUCCESS; 233 234 if ((dirp = opendir(NWAM_EVENT_MSG_DIR)) == NULL) { 235 return (nwam_errno_to_nwam_error(errno)); 236 } 237 238 /* 239 * For each file matching our event message queue file prefix, 240 * check the queue is still being read, and if so send the message. 241 */ 242 while ((dp = readdir(dirp)) != NULL) { 243 if (strncmp(dp->d_name, NWAM_EVENT_MSG_FILE, 244 strlen(NWAM_EVENT_MSG_FILE)) != 0) 245 continue; 246 247 (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s/%s", 248 NWAM_EVENT_MSG_DIR, dp->d_name); 249 250 if ((key = ftok(eventmsgfile, 0)) == -1) { 251 int errno_save = errno; 252 syslog(LOG_INFO, "nwam_event_send: ftok: %s", 253 strerror(errno_save)); 254 err = nwam_errno_to_nwam_error(errno_save); 255 continue; 256 } 257 258 if ((msqid = msgget(key, 0644)) == -1) { 259 int errno_save = errno; 260 syslog(LOG_INFO, "nwam_event_send: msgget: %s", 261 strerror(errno_save)); 262 err = nwam_errno_to_nwam_error(errno_save); 263 continue; 264 } 265 266 /* Retrieve stats to analyse queue activity */ 267 if (msgctl(msqid, IPC_STAT, &buf) == -1) { 268 int errno_save = errno; 269 syslog(LOG_INFO, "nwam_event_send: msgctl: %s", 270 strerror(errno_save)); 271 err = nwam_errno_to_nwam_error(errno_save); 272 continue; 273 } 274 /* 275 * If buf.msg_qnum > NWAM_EVENT_MAX_NUM_PENDING 276 * _and_ msg_stime is more than 10s after msg_rtime - 277 * indicating message(s) have been hanging around unclaimed - 278 * we destroy the queue as the client has most likely gone 279 * away. This can happen if a registered client hits Ctrl^C. 280 */ 281 if (buf.msg_qnum > NWAM_EVENT_MAX_NUM_PENDING && 282 ((buf.msg_stime + NWAM_EVENT_WAIT_TIME) > buf.msg_rtime)) { 283 nwam_event_queue_fini(eventmsgfile); 284 continue; 285 } 286 287 /* 288 * This shouldn't ever block. If it does then log an error and 289 * clean up the queue. 290 */ 291 if (msgsnd(msqid, (struct msgbuf *)event, event->nwe_size, 292 IPC_NOWAIT) == -1) { 293 int errno_save = errno; 294 syslog(LOG_ERR, "nwam_event_send: msgsnd: %s, " 295 "destroying message queue %s", strerror(errno_save), 296 eventmsgfile); 297 nwam_event_queue_fini(eventmsgfile); 298 err = nwam_errno_to_nwam_error(errno_save); 299 continue; 300 } 301 302 } 303 (void) closedir(dirp); 304 305 return (err); 306 } 307 308 /* 309 * Destroy an event queue. Called by nwamd to destroy the associated message 310 * queue. 311 */ 312 void 313 nwam_event_queue_fini(const char *eventmsgfile) 314 { 315 key_t key; 316 int msqid; 317 318 if ((key = ftok(eventmsgfile, 0)) != -1 && 319 (msqid = msgget(key, 0644)) != -1 && 320 msgctl(msqid, IPC_RMID, NULL) != -1) 321 (void) unlink(eventmsgfile); 322 } 323 324 /* 325 * Stop sending events. Called by nwamd to destroy each System V message queue 326 * registered. 327 */ 328 void 329 nwam_event_send_fini(void) 330 { 331 DIR *dirp; 332 struct dirent *dp; 333 char eventmsgfile[MAXPATHLEN]; 334 335 (void) pthread_mutex_lock(&event_mutex); 336 337 if ((dirp = opendir(NWAM_EVENT_MSG_DIR)) == NULL) { 338 (void) pthread_mutex_unlock(&event_mutex); 339 return; 340 } 341 342 /* 343 * For each file matching our event message queue file prefix, 344 * destroy the queue and message file. 345 */ 346 while ((dp = readdir(dirp)) != NULL) { 347 if (strncmp(dp->d_name, NWAM_EVENT_MSG_FILE, 348 strlen(NWAM_EVENT_MSG_FILE)) != 0) 349 continue; 350 351 (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s/%s", 352 NWAM_EVENT_MSG_DIR, dp->d_name); 353 354 nwam_event_queue_fini(eventmsgfile); 355 } 356 (void) pthread_mutex_unlock(&event_mutex); 357 } 358