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