]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/lists.c
8e3eed79f8471533f50077e9ebedd95c0ea33bac
[apple/launchd.git] / launchd / src / lists.c
1 /*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * bootstrap -- fundamental service initiator and port server
26 * Mike DeMoney, NeXT, Inc.
27 * Copyright, 1990. All rights reserved.
28 *
29 * lists.c -- implementation of list handling routines
30 */
31
32 #include <mach/boolean.h>
33 #include <mach/mach_error.h>
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <syslog.h>
38
39 #include <bsm/audit.h>
40
41 #include "bootstrap_internal.h"
42 #include "lists.h"
43
44 /*
45 * Exports
46 */
47 bootstrap_info_t bootstraps; /* head of list of all bootstrap ports */
48 server_t servers; /* head of list of all servers */
49 service_t services; /* head of list of all services */
50 unsigned nservices; /* number of services in list */
51
52 #ifndef ASSERT
53 #define ASSERT(p)
54 #endif
55
56 /*
57 * Private macros
58 */
59 #define NEW(type, num) ((type *)ckmalloc(sizeof(type) * num))
60 #define STREQ(a, b) (strcmp(a, b) == 0)
61 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
62 #define LAST_ELEMENT(x) ((x)[NELEM(x)-1])
63
64 void
65 init_lists(void)
66 {
67 bootstraps.ref_count = 2; /* make sure we never deallocate this one */
68 bootstraps.next = bootstraps.prev = &bootstraps;
69 bootstraps.parent = &bootstraps;
70 servers.next = servers.prev = &servers;
71 services.next = services.prev = &services;
72 nservices = 0;
73 }
74
75 server_t *
76 new_server(
77 bootstrap_info_t *bootstrap,
78 const char *cmd,
79 uid_t uid,
80 servertype_t servertype,
81 auditinfo_t auinfo)
82 {
83 server_t *serverp;
84
85 syslog(LOG_DEBUG, "adding new server \"%s\" with uid %d", cmd, uid);
86 serverp = NEW(server_t, 1);
87 if (serverp != NULL) {
88 /* Doubly linked list */
89 servers.prev->next = serverp;
90 serverp->prev = servers.prev;
91 serverp->next = &servers;
92 servers.prev = serverp;
93
94 bootstrap->ref_count++;
95 serverp->bootstrap = bootstrap;
96
97 serverp->pid = NO_PID;
98 serverp->task_port = MACH_PORT_NULL;
99
100 serverp->uid = uid;
101 serverp->auinfo = auinfo;
102
103 serverp->port = MACH_PORT_NULL;
104 serverp->servertype = servertype;
105 serverp->activity = 0;
106 serverp->active_services = 0;
107 strncpy(serverp->cmd, cmd, sizeof serverp->cmd);
108 LAST_ELEMENT(serverp->cmd) = '\0';
109 }
110 return serverp;
111 }
112
113 service_t *
114 new_service(
115 bootstrap_info_t *bootstrap,
116 const char *name,
117 mach_port_t serviceport,
118 boolean_t isActive,
119 servicetype_t servicetype,
120 server_t *serverp)
121 {
122 service_t *servicep;
123
124 servicep = NEW(service_t, 1);
125 if (servicep != NULL) {
126 /* Doubly linked list */
127 services.prev->next = servicep;
128 servicep->prev = services.prev;
129 servicep->next = &services;
130 services.prev = servicep;
131
132 nservices += 1;
133
134 strncpy(servicep->name, name, sizeof servicep->name);
135 LAST_ELEMENT(servicep->name) = '\0';
136 servicep->servicetype = servicetype;
137 servicep->bootstrap = bootstrap;
138 servicep->port = serviceport;
139 servicep->server = serverp;
140 servicep->isActive = isActive;
141 }
142 return servicep;
143 }
144
145 bootstrap_info_t *
146 new_bootstrap(
147 bootstrap_info_t *parent,
148 mach_port_t bootstrapport,
149 mach_port_t requestorport)
150 {
151 bootstrap_info_t *bootstrap;
152
153 bootstrap = NEW(bootstrap_info_t, 1);
154 if (bootstrap != NULL) {
155 /* Doubly linked list */
156 bootstraps.prev->next = bootstrap;
157 bootstrap->prev = bootstraps.prev;
158 bootstrap->next = &bootstraps;
159 bootstraps.prev = bootstrap;
160
161 bootstrap->bootstrap_port = bootstrapport;
162 bootstrap->requestor_port = requestorport;
163
164 bootstrap->ref_count = 1;
165 bootstrap->parent = parent;
166 parent->ref_count++;
167 }
168 return bootstrap;
169 }
170
171 bootstrap_info_t *
172 lookup_bootstrap_by_port(mach_port_t port)
173 {
174 bootstrap_info_t *bootstrap;
175 bootstrap_info_t *first;
176 server_t *serverp;
177
178 bootstrap = first = FIRST(bootstraps);
179 do {
180 if (bootstrap->bootstrap_port == port)
181 return bootstrap;
182 bootstrap = NEXT(bootstrap);
183 } while (bootstrap != first);
184
185 for ( serverp = FIRST(servers)
186 ; !IS_END(serverp, servers)
187 ; serverp = NEXT(serverp))
188 {
189 if (port == serverp->port)
190 return serverp->bootstrap;
191 }
192 return NULL;
193 }
194
195 bootstrap_info_t *
196 lookup_bootstrap_by_req_port(mach_port_t port)
197 {
198 bootstrap_info_t *bootstrap;
199
200 for ( bootstrap = FIRST(bootstraps)
201 ; !IS_END(bootstrap, bootstraps)
202 ; bootstrap = NEXT(bootstrap))
203 {
204 if (bootstrap->requestor_port == port)
205 return bootstrap;
206 }
207
208 return NULL;
209 }
210
211 service_t *
212 lookup_service_by_name(bootstrap_info_t *bootstrap, name_t name)
213 {
214 service_t *servicep;
215
216 if (bootstrap)
217 do {
218 for ( servicep = FIRST(services)
219 ; !IS_END(servicep, services)
220 ; servicep = NEXT(servicep))
221 {
222 if (!STREQ(name, servicep->name))
223 continue;
224 if (bootstrap && servicep->bootstrap != bootstrap)
225 continue;
226 return servicep;
227 }
228 } while (bootstrap != &bootstraps &&
229 (bootstrap = bootstrap->parent));
230 return NULL;
231 }
232
233 void
234 unlink_service(service_t *servicep)
235 {
236 ASSERT(servicep->prev->next == servicep);
237 ASSERT(servicep->next->prev == servicep);
238 servicep->prev->next = servicep->next;
239 servicep->next->prev = servicep->prev;
240 servicep->prev = servicep->next = servicep; // idempotent
241 }
242
243 void
244 delete_service(service_t *servicep)
245 {
246 unlink_service(servicep);
247 switch (servicep->servicetype) {
248 case REGISTERED:
249 syslog(LOG_INFO, "Registered service %s deleted", servicep->name);
250 mach_port_deallocate(mach_task_self(), servicep->port);
251 break;
252 case DECLARED:
253 syslog(LOG_INFO, "Declared service %s now unavailable", servicep->name);
254 mach_port_deallocate(mach_task_self(), servicep->port);
255 mach_port_mod_refs(mach_task_self(), servicep->port,
256 MACH_PORT_RIGHT_RECEIVE, -1);
257 break;
258 default:
259 syslog(LOG_ERR, "unknown service type %d", servicep->servicetype);
260 break;
261 }
262 free(servicep);
263 nservices -= 1;
264 }
265
266 void
267 delete_bootstrap_services(bootstrap_info_t *bootstrap)
268 {
269 server_t *serverp;
270 service_t *servicep;
271 service_t *next;
272
273 for ( servicep = FIRST(services)
274 ; !IS_END(servicep, services)
275 ; servicep = next)
276 {
277 next = NEXT(servicep);
278 if (bootstrap != servicep->bootstrap)
279 continue;
280
281 if (!servicep->isActive || !servicep->server) {
282 delete_service(servicep);
283 continue;
284 }
285
286 serverp = servicep->server;
287 delete_service(servicep);
288 serverp->active_services--;
289 if (!active_server(serverp))
290 delete_server(serverp);
291 }
292 }
293
294 service_t *
295 lookup_service_by_port(mach_port_t port)
296 {
297 service_t *servicep;
298
299 for ( servicep = FIRST(services)
300 ; !IS_END(servicep, services)
301 ; servicep = NEXT(servicep))
302 {
303 if (port == servicep->port)
304 return servicep;
305 }
306 return NULL;
307 }
308
309 service_t *
310 lookup_service_by_server(server_t *serverp)
311 {
312 service_t *servicep;
313
314 for ( servicep = FIRST(services)
315 ; !IS_END(servicep, services)
316 ; servicep = NEXT(servicep))
317 {
318 if (serverp == servicep->server)
319 return servicep;
320 }
321 return NULL;
322 }
323
324 server_t *
325 lookup_server_by_task_port(mach_port_t port)
326 {
327 server_t *serverp;
328
329 for ( serverp = FIRST(servers)
330 ; !IS_END(serverp, servers)
331 ; serverp = NEXT(serverp))
332 {
333 if (port == serverp->task_port)
334 return serverp;
335 }
336 return NULL;
337 }
338
339 server_t *
340 lookup_server_by_port(mach_port_t port)
341 {
342 server_t *serverp;
343
344 for ( serverp = FIRST(servers)
345 ; !IS_END(serverp, servers)
346 ; serverp = NEXT(serverp))
347 {
348 if (port == serverp->port)
349 return serverp;
350 }
351 return NULL;
352 }
353
354 void
355 delete_server(server_t *serverp)
356 {
357 service_t *servicep;
358 service_t *next;
359
360 syslog(LOG_INFO, "Deleting server %s", serverp->cmd);
361 ASSERT(serverp->prev->next == serverp);
362 ASSERT(serverp->next->prev == serverp);
363 serverp->prev->next = serverp->next;
364 serverp->next->prev = serverp->prev;
365
366 for ( servicep = FIRST(services)
367 ; !IS_END(servicep, services)
368 ; servicep = next)
369 {
370 next = NEXT(servicep);
371 if (serverp == servicep->server)
372 delete_service(servicep);
373 }
374
375 deallocate_bootstrap(serverp->bootstrap);
376
377 if (serverp->port)
378 mach_port_mod_refs(mach_task_self(), serverp->port,
379 MACH_PORT_RIGHT_RECEIVE, -1);
380
381 free(serverp);
382 }
383
384 void
385 deactivate_bootstrap(bootstrap_info_t *bootstrap)
386 {
387 bootstrap_info_t *deactivating_bootstraps;
388 bootstrap_info_t *query_bootstrap;
389 bootstrap_info_t *next_limit;
390 bootstrap_info_t *limit;
391
392 /*
393 * we need to recursively deactivate the whole subset tree below
394 * this point. But we don't want to do real recursion because
395 * we don't have a limit on the depth. So, build up a chain of
396 * active bootstraps anywhere underneath this one.
397 */
398 deactivating_bootstraps = bootstrap;
399 bootstrap->deactivate = NULL;
400 for (next_limit = deactivating_bootstraps, limit = NULL
401 ; deactivating_bootstraps != limit
402 ; limit = next_limit, next_limit = deactivating_bootstraps)
403 {
404 for (bootstrap = deactivating_bootstraps
405 ; bootstrap != limit
406 ; bootstrap = bootstrap->deactivate)
407 {
408 for ( query_bootstrap = FIRST(bootstraps)
409 ; !IS_END(query_bootstrap, bootstraps)
410 ; query_bootstrap = NEXT(query_bootstrap))
411 {
412 if (query_bootstrap->parent == bootstrap &&
413 query_bootstrap->requestor_port != MACH_PORT_NULL) {
414 mach_port_deallocate(
415 mach_task_self(),
416 query_bootstrap->requestor_port);
417 query_bootstrap->requestor_port = MACH_PORT_NULL;
418 query_bootstrap->deactivate = deactivating_bootstraps;
419 deactivating_bootstraps = query_bootstrap;
420 }
421 }
422 }
423 }
424
425 /*
426 * The list is ordered with the furthest away progeny being
427 * at the front, and concluding with the one we started with.
428 * This allows us to safely deactivate and remove the reference
429 * each holds on their parent without fear of the chain getting
430 * corrupted (because each active parent holds a reference on
431 * itself and that doesn't get removed until we reach its spot
432 * in the list).
433 */
434 do {
435 bootstrap = deactivating_bootstraps;
436 deactivating_bootstraps = bootstrap->deactivate;
437
438 syslog(LOG_INFO, "deactivating bootstrap %x", bootstrap->bootstrap_port);
439
440 delete_bootstrap_services(bootstrap);
441
442 mach_port_deallocate(mach_task_self(), bootstrap->bootstrap_port);
443
444 {
445 mach_port_t previous;
446 mach_port_request_notification(
447 mach_task_self(),
448 bootstrap->bootstrap_port,
449 MACH_NOTIFY_NO_SENDERS,
450 1,
451 bootstrap->bootstrap_port,
452 MACH_MSG_TYPE_MAKE_SEND_ONCE,
453 &previous);
454 }
455 } while (deactivating_bootstraps != NULL);
456 }
457
458 void
459 deallocate_bootstrap(bootstrap_info_t *bootstrap)
460 {
461 ASSERT(bootstrap->prev->next == bootstrap);
462 ASSERT(bootstrap->next->prev == bootstrap);
463 if (--bootstrap->ref_count > 0)
464 return;
465
466 bootstrap->prev->next = bootstrap->next;
467 bootstrap->next->prev = bootstrap->prev;
468 deallocate_bootstrap(bootstrap->parent);
469 free(bootstrap);
470 }
471
472 void *
473 ckmalloc(unsigned nbytes)
474 {
475 void *cp;
476
477 if ((cp = malloc(nbytes)) == NULL) {
478 syslog(LOG_EMERG, "malloc(): %m");
479 exit(EXIT_FAILURE);
480 }
481 return cp;
482 }
483
484