]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/lists.c
launchd-106.10.tar.gz
[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 serverp = servicep->server;
282
283 if (servicep->isActive && serverp)
284 serverp->active_services--;
285
286 delete_service(servicep);
287
288 if (!serverp)
289 continue;
290 if (!active_server(serverp))
291 delete_server(serverp);
292 }
293 }
294
295 service_t *
296 lookup_service_by_port(mach_port_t port)
297 {
298 service_t *servicep;
299
300 for ( servicep = FIRST(services)
301 ; !IS_END(servicep, services)
302 ; servicep = NEXT(servicep))
303 {
304 if (port == servicep->port)
305 return servicep;
306 }
307 return NULL;
308 }
309
310 service_t *
311 lookup_service_by_server(server_t *serverp)
312 {
313 service_t *servicep;
314
315 for ( servicep = FIRST(services)
316 ; !IS_END(servicep, services)
317 ; servicep = NEXT(servicep))
318 {
319 if (serverp == servicep->server)
320 return servicep;
321 }
322 return NULL;
323 }
324
325 server_t *
326 lookup_server_by_task_port(mach_port_t port)
327 {
328 server_t *serverp;
329
330 for ( serverp = FIRST(servers)
331 ; !IS_END(serverp, servers)
332 ; serverp = NEXT(serverp))
333 {
334 if (port == serverp->task_port)
335 return serverp;
336 }
337 return NULL;
338 }
339
340 server_t *
341 lookup_server_by_port(mach_port_t port)
342 {
343 server_t *serverp;
344
345 for ( serverp = FIRST(servers)
346 ; !IS_END(serverp, servers)
347 ; serverp = NEXT(serverp))
348 {
349 if (port == serverp->port)
350 return serverp;
351 }
352 return NULL;
353 }
354
355 void
356 delete_server(server_t *serverp)
357 {
358 service_t *servicep;
359 service_t *next;
360
361 syslog(LOG_INFO, "Deleting server %s", serverp->cmd);
362 ASSERT(serverp->prev->next == serverp);
363 ASSERT(serverp->next->prev == serverp);
364 serverp->prev->next = serverp->next;
365 serverp->next->prev = serverp->prev;
366
367 for ( servicep = FIRST(services)
368 ; !IS_END(servicep, services)
369 ; servicep = next)
370 {
371 next = NEXT(servicep);
372 if (serverp == servicep->server)
373 delete_service(servicep);
374 }
375
376 deallocate_bootstrap(serverp->bootstrap);
377
378 if (serverp->port)
379 mach_port_mod_refs(mach_task_self(), serverp->port,
380 MACH_PORT_RIGHT_RECEIVE, -1);
381
382 free(serverp);
383 }
384
385 void
386 deactivate_bootstrap(bootstrap_info_t *bootstrap)
387 {
388 bootstrap_info_t *deactivating_bootstraps;
389 bootstrap_info_t *query_bootstrap;
390 bootstrap_info_t *next_limit;
391 bootstrap_info_t *limit;
392
393 /*
394 * we need to recursively deactivate the whole subset tree below
395 * this point. But we don't want to do real recursion because
396 * we don't have a limit on the depth. So, build up a chain of
397 * active bootstraps anywhere underneath this one.
398 */
399 deactivating_bootstraps = bootstrap;
400 bootstrap->deactivate = NULL;
401 for (next_limit = deactivating_bootstraps, limit = NULL
402 ; deactivating_bootstraps != limit
403 ; limit = next_limit, next_limit = deactivating_bootstraps)
404 {
405 for (bootstrap = deactivating_bootstraps
406 ; bootstrap != limit
407 ; bootstrap = bootstrap->deactivate)
408 {
409 for ( query_bootstrap = FIRST(bootstraps)
410 ; !IS_END(query_bootstrap, bootstraps)
411 ; query_bootstrap = NEXT(query_bootstrap))
412 {
413 if (query_bootstrap->parent == bootstrap &&
414 query_bootstrap->requestor_port != MACH_PORT_NULL) {
415 mach_port_deallocate(
416 mach_task_self(),
417 query_bootstrap->requestor_port);
418 query_bootstrap->requestor_port = MACH_PORT_NULL;
419 query_bootstrap->deactivate = deactivating_bootstraps;
420 deactivating_bootstraps = query_bootstrap;
421 }
422 }
423 }
424 }
425
426 /*
427 * The list is ordered with the furthest away progeny being
428 * at the front, and concluding with the one we started with.
429 * This allows us to safely deactivate and remove the reference
430 * each holds on their parent without fear of the chain getting
431 * corrupted (because each active parent holds a reference on
432 * itself and that doesn't get removed until we reach its spot
433 * in the list).
434 */
435 do {
436 bootstrap = deactivating_bootstraps;
437 deactivating_bootstraps = bootstrap->deactivate;
438
439 syslog(LOG_INFO, "deactivating bootstrap %x", bootstrap->bootstrap_port);
440
441 delete_bootstrap_services(bootstrap);
442
443 mach_port_deallocate(mach_task_self(), bootstrap->bootstrap_port);
444
445 {
446 mach_port_t previous;
447 mach_port_request_notification(
448 mach_task_self(),
449 bootstrap->bootstrap_port,
450 MACH_NOTIFY_NO_SENDERS,
451 1,
452 bootstrap->bootstrap_port,
453 MACH_MSG_TYPE_MAKE_SEND_ONCE,
454 &previous);
455 }
456 } while (deactivating_bootstraps != NULL);
457 }
458
459 void
460 deallocate_bootstrap(bootstrap_info_t *bootstrap)
461 {
462 ASSERT(bootstrap->prev->next == bootstrap);
463 ASSERT(bootstrap->next->prev == bootstrap);
464 if (--bootstrap->ref_count > 0)
465 return;
466
467 bootstrap->prev->next = bootstrap->next;
468 bootstrap->next->prev = bootstrap->prev;
469 deallocate_bootstrap(bootstrap->parent);
470 free(bootstrap);
471 }
472
473 void *
474 ckmalloc(unsigned nbytes)
475 {
476 void *cp;
477
478 if ((cp = malloc(nbytes)) == NULL) {
479 syslog(LOG_EMERG, "malloc(): %m");
480 exit(EXIT_FAILURE);
481 }
482 return cp;
483 }
484
485