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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * protocol.c - protocols between graph engine and restarters 31 * 32 * The graph engine uses restarter_protocol_send_event() to send a 33 * restarter_event_type_t to the restarter. For delegated restarters, 34 * this is published on the GPEC queue for the restarter, which can 35 * then be consumed by the librestart interfaces. For services managed 36 * by svc.startd, the event is stored on the local restarter_queue list, 37 * where it can be dequeued by the restarter. 38 * 39 * The svc.startd restarter uses graph_protocol_send_event() to send 40 * a graph_event_type_t to the graph engine when an instance's states are 41 * updated. 42 * 43 * The graph engine uses restarter_protocol_init_delegate() to 44 * register its interest in a particular delegated restarter's instance 45 * state events. The state_cb() registered on the event channel then 46 * invokes graph_protocol_send_event() to communicate the update to 47 * the graph engine. 48 */ 49 50 #include <assert.h> 51 #include <libintl.h> 52 #include <libsysevent.h> 53 #include <pthread.h> 54 #include <stdarg.h> 55 #include <stdio.h> 56 #include <strings.h> 57 #include <sys/time.h> 58 #include <errno.h> 59 #include <libuutil.h> 60 61 #include <librestart.h> 62 #include <librestart_priv.h> 63 64 #include "protocol.h" 65 #include "startd.h" 66 67 /* Local event queue structures. */ 68 typedef struct graph_protocol_event_queue { 69 uu_list_t *gpeq_event_list; 70 pthread_mutex_t gpeq_lock; 71 } graph_protocol_event_queue_t; 72 73 typedef struct restarter_protocol_event_queue { 74 uu_list_t *rpeq_event_list; 75 pthread_mutex_t rpeq_lock; 76 } restarter_protocol_event_queue_t; 77 78 static uu_list_pool_t *restarter_protocol_event_queue_pool; 79 static restarter_protocol_event_queue_t *restarter_queue; 80 81 static uu_list_pool_t *graph_protocol_event_queue_pool; 82 static graph_protocol_event_queue_t *graph_queue; 83 84 void 85 graph_protocol_init() 86 { 87 graph_protocol_event_queue_pool = startd_list_pool_create( 88 "graph_protocol_events", sizeof (graph_protocol_event_t), 89 offsetof(graph_protocol_event_t, gpe_link), NULL, 90 UU_LIST_POOL_DEBUG); 91 92 graph_queue = startd_zalloc(sizeof (graph_protocol_event_queue_t)); 93 94 (void) pthread_mutex_init(&graph_queue->gpeq_lock, &mutex_attrs); 95 graph_queue->gpeq_event_list = startd_list_create( 96 graph_protocol_event_queue_pool, graph_queue, NULL); 97 } 98 99 /* 100 * "data" will be freed by the consumer 101 */ 102 static void 103 graph_event_enqueue(const char *inst, graph_event_type_t event, 104 protocol_states_t *data) 105 { 106 graph_protocol_event_t *e; 107 108 e = startd_zalloc(sizeof (graph_protocol_event_t)); 109 110 if (inst != NULL) { 111 int size = strlen(inst) + 1; 112 e->gpe_inst = startd_alloc(size); 113 e->gpe_inst_sz = size; 114 (void) strlcpy(e->gpe_inst, inst, size); 115 } 116 e->gpe_type = event; 117 e->gpe_data = data; 118 119 (void) pthread_mutex_init(&e->gpe_lock, &mutex_attrs); 120 121 MUTEX_LOCK(&graph_queue->gpeq_lock); 122 uu_list_node_init(e, &e->gpe_link, graph_protocol_event_queue_pool); 123 if (uu_list_insert_before(graph_queue->gpeq_event_list, NULL, e) == -1) 124 uu_die("failed to enqueue graph event (%s: %s)\n", 125 e->gpe_inst, uu_strerror(uu_error())); 126 127 MUTEX_UNLOCK(&graph_queue->gpeq_lock); 128 } 129 130 void 131 graph_event_release(graph_protocol_event_t *e) 132 { 133 uu_list_node_fini(e, &e->gpe_link, graph_protocol_event_queue_pool); 134 (void) pthread_mutex_destroy(&e->gpe_lock); 135 if (e->gpe_inst != NULL) 136 startd_free(e->gpe_inst, e->gpe_inst_sz); 137 startd_free(e, sizeof (graph_protocol_event_t)); 138 } 139 140 /* 141 * graph_protocol_event_t *graph_event_dequeue() 142 * The caller must hold gu_lock, and is expected to be a single thread. 143 * It is allowed to utilize graph_event_requeue() and abort processing 144 * on the event. If graph_event_requeue() is not called, the caller is 145 * expected to call graph_event_release() when finished. 146 */ 147 graph_protocol_event_t * 148 graph_event_dequeue() 149 { 150 graph_protocol_event_t *e; 151 152 MUTEX_LOCK(&graph_queue->gpeq_lock); 153 154 e = uu_list_first(graph_queue->gpeq_event_list); 155 if (e == NULL) { 156 MUTEX_UNLOCK(&graph_queue->gpeq_lock); 157 return (NULL); 158 } 159 160 if (uu_list_next(graph_queue->gpeq_event_list, e) != NULL) 161 gu->gu_wakeup = 1; 162 uu_list_remove(graph_queue->gpeq_event_list, e); 163 MUTEX_UNLOCK(&graph_queue->gpeq_lock); 164 165 return (e); 166 } 167 168 /* 169 * void graph_event_requeue() 170 * Requeue the event back at the head of the queue. 171 */ 172 void 173 graph_event_requeue(graph_protocol_event_t *e) 174 { 175 assert(e != NULL); 176 177 log_framework(LOG_DEBUG, "Requeing event\n"); 178 179 MUTEX_LOCK(&graph_queue->gpeq_lock); 180 if (uu_list_insert_after(graph_queue->gpeq_event_list, NULL, e) == -1) 181 uu_die("failed to requeue graph event (%s: %s)\n", 182 e->gpe_inst, uu_strerror(uu_error())); 183 184 MUTEX_UNLOCK(&graph_queue->gpeq_lock); 185 } 186 187 void 188 graph_protocol_send_event(const char *inst, graph_event_type_t event, 189 protocol_states_t *data) 190 { 191 graph_event_enqueue(inst, event, data); 192 MUTEX_LOCK(&gu->gu_lock); 193 gu->gu_wakeup = 1; 194 (void) pthread_cond_broadcast(&gu->gu_cv); 195 MUTEX_UNLOCK(&gu->gu_lock); 196 } 197 198 void 199 restarter_protocol_init() 200 { 201 restarter_protocol_event_queue_pool = startd_list_pool_create( 202 "restarter_protocol_events", sizeof (restarter_protocol_event_t), 203 offsetof(restarter_protocol_event_t, rpe_link), NULL, 204 UU_LIST_POOL_DEBUG); 205 206 restarter_queue = startd_zalloc( 207 sizeof (restarter_protocol_event_queue_t)); 208 209 (void) pthread_mutex_init(&restarter_queue->rpeq_lock, &mutex_attrs); 210 restarter_queue->rpeq_event_list = startd_list_create( 211 restarter_protocol_event_queue_pool, restarter_queue, NULL); 212 213 log_framework(LOG_DEBUG, "Initialized restarter protocol\n"); 214 } 215 216 /* 217 * void restarter_event_enqueue() 218 * Enqueue a restarter event. 219 */ 220 static void 221 restarter_event_enqueue(const char *inst, restarter_event_type_t event) 222 { 223 restarter_protocol_event_t *e; 224 int r; 225 226 /* Allocate and populate the event structure. */ 227 e = startd_zalloc(sizeof (restarter_protocol_event_t)); 228 229 e->rpe_inst = startd_alloc(strlen(inst) + 1); 230 (void) strlcpy(e->rpe_inst, inst, strlen(inst)+1); 231 e->rpe_type = event; 232 233 MUTEX_LOCK(&restarter_queue->rpeq_lock); 234 uu_list_node_init(e, &e->rpe_link, restarter_protocol_event_queue_pool); 235 r = uu_list_insert_before(restarter_queue->rpeq_event_list, NULL, e); 236 assert(r == 0); 237 238 MUTEX_UNLOCK(&restarter_queue->rpeq_lock); 239 240 } 241 242 void 243 restarter_event_release(restarter_protocol_event_t *e) 244 { 245 uu_list_node_fini(e, &e->rpe_link, restarter_protocol_event_queue_pool); 246 startd_free(e->rpe_inst, strlen(e->rpe_inst) + 1); 247 startd_free(e, sizeof (restarter_protocol_event_t)); 248 } 249 250 /* 251 * restarter_protocol_event_t *restarter_event_dequeue() 252 * Dequeue a restarter protocol event. The caller is expected to be 253 * a single thread. It is allowed to utilize restarter_event_requeue() 254 * and abort processing on the event. The caller is expected to call 255 * restarter_event_release() when finished. 256 */ 257 restarter_protocol_event_t * 258 restarter_event_dequeue() 259 { 260 restarter_protocol_event_t *e = NULL; 261 262 MUTEX_LOCK(&restarter_queue->rpeq_lock); 263 264 e = uu_list_first(restarter_queue->rpeq_event_list); 265 if (e == NULL) { 266 MUTEX_UNLOCK(&restarter_queue->rpeq_lock); 267 return (NULL); 268 } 269 270 if (uu_list_next(restarter_queue->rpeq_event_list, e) != NULL) 271 ru->restarter_update_wakeup = 1; 272 uu_list_remove(restarter_queue->rpeq_event_list, e); 273 MUTEX_UNLOCK(&restarter_queue->rpeq_lock); 274 275 return (e); 276 } 277 278 static int 279 state_cb(sysevent_t *syse, void *cookie) 280 { 281 char *fmri = (char *)cookie; 282 char *instance_name; 283 nvlist_t *attr_list = NULL; 284 int state, next_state; 285 protocol_states_t *states; 286 int err; 287 288 /* 289 * Might fail due to a bad event or a lack of memory. Try 290 * the callback again to see if it goes better the next time. 291 */ 292 if (sysevent_get_attr_list(syse, &attr_list) != 0) 293 return (EAGAIN); 294 295 if ((nvlist_lookup_int32(attr_list, RESTARTER_NAME_STATE, 296 &state) != 0) || 297 (nvlist_lookup_int32(attr_list, RESTARTER_NAME_NEXT_STATE, 298 &next_state) != 0) || 299 (nvlist_lookup_int32(attr_list, RESTARTER_NAME_ERROR, &err) != 0) || 300 (nvlist_lookup_string(attr_list, RESTARTER_NAME_INSTANCE, 301 &instance_name) != 0)) 302 uu_die("%s: can't decode nvlist\n", fmri); 303 304 states = startd_alloc(sizeof (protocol_states_t)); 305 states->ps_state = state; 306 states->ps_state_next = next_state; 307 states->ps_err = err; 308 309 graph_protocol_send_event(instance_name, GRAPH_UPDATE_STATE_CHANGE, 310 states); 311 312 log_framework(LOG_DEBUG, "%s: state updates for %s (%d, %d)\n", fmri, 313 instance_name, state, next_state); 314 nvlist_free(attr_list); 315 return (0); 316 } 317 318 evchan_t * 319 restarter_protocol_init_delegate(char *fmri) 320 { 321 char *delegate_channel_name, *master_channel_name, *sid; 322 evchan_t *delegate_channel, *master_channel; 323 324 /* master restarter -- nothing to do */ 325 if (strcmp(fmri, SCF_SERVICE_STARTD) == 0) 326 return (NULL); 327 328 log_framework(LOG_DEBUG, "%s: Intializing protocol for delegate\n", 329 fmri); 330 331 if ((delegate_channel_name = _restarter_get_channel_name(fmri, 332 RESTARTER_CHANNEL_DELEGATE)) == NULL || 333 (master_channel_name = _restarter_get_channel_name(fmri, 334 RESTARTER_CHANNEL_MASTER)) == NULL || 335 (sid = strdup("svc.startd")) == NULL) 336 uu_die("Allocation failure\n"); 337 338 if (sysevent_evc_bind(delegate_channel_name, &delegate_channel, 339 EVCH_CREAT|EVCH_HOLD_PEND) != 0) 340 uu_die("%s: sysevent_evc_bind failed: %s\n", 341 delegate_channel_name, strerror(errno)); 342 if (sysevent_evc_bind(master_channel_name, &master_channel, 343 EVCH_CREAT|EVCH_HOLD_PEND) != 0) 344 uu_die("%s: sysevent_evc_bind failed: %s\n", 345 master_channel_name, strerror(errno)); 346 log_framework(LOG_DEBUG, 347 "%s: Bound to channel %s (delegate), %s (master)\n", fmri, 348 delegate_channel_name, master_channel_name); 349 350 if (sysevent_evc_subscribe(master_channel, sid, EC_ALL, 351 state_cb, fmri, EVCH_SUB_KEEP) != 0) 352 uu_die("%s: Failed to subscribe to channel %s with " 353 "subscriber id %s: %s\n", fmri, 354 master_channel_name, sid, strerror(errno)); 355 log_framework(LOG_DEBUG, 356 "%s: Subscribed to channel %s with subscriber id %s\n", fmri, 357 master_channel_name, "svc.startd"); 358 359 free(delegate_channel_name); 360 free(master_channel_name); 361 free(sid); 362 363 return (delegate_channel); 364 } 365 366 void 367 restarter_protocol_send_event(const char *inst, evchan_t *chan, 368 restarter_event_type_t event) 369 { 370 nvlist_t *attr; 371 372 /* 373 * If the service is managed by the master restarter, 374 * queue the event locally. 375 */ 376 if (chan == NULL) { 377 restarter_event_enqueue(inst, event); 378 MUTEX_LOCK(&ru->restarter_update_lock); 379 ru->restarter_update_wakeup = 1; 380 (void) pthread_cond_broadcast(&ru->restarter_update_cv); 381 MUTEX_UNLOCK(&ru->restarter_update_lock); 382 return; 383 } 384 385 /* 386 * Otherwise, send the event to the delegate. 387 */ 388 log_framework(LOG_DEBUG, "Sending %s to channel 0x%p for %s.\n", 389 event_names[event], chan, inst); 390 if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 || 391 nvlist_add_uint32(attr, RESTARTER_NAME_TYPE, event) != 0 || 392 nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, (char *)inst) != 0) 393 uu_die("Allocation failure\n"); 394 395 if (sysevent_evc_publish(chan, "protocol", "restarter", "com.sun", 396 "svc.startd", attr, EVCH_NOSLEEP) != 0) { 397 if (errno == EAGAIN) 398 uu_die("%s: queue is full\n", inst); 399 uu_die("%s: can't publish event: %s\n", inst, strerror(errno)); 400 } 401 nvlist_free(attr); 402 403 if (event != RESTARTER_EVENT_TYPE_ADD_INSTANCE) { 404 /* 405 * Not relevant for graph loading. 406 */ 407 return; 408 } 409 410 /* 411 * For the purposes of loading state after interruption, this is 412 * sufficient, as svc.startd(1M) won't receive events on the contracts 413 * associated with each delegate. 414 */ 415 MUTEX_LOCK(&st->st_load_lock); 416 if (--st->st_load_instances == 0) 417 (void) pthread_cond_broadcast(&st->st_load_cv); 418 MUTEX_UNLOCK(&st->st_load_lock); 419 420 } 421