]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2007,2011-2013 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | // | |
26 | // machserver - C++ shell for writing Mach 3 servers | |
27 | // | |
28 | #include "machserver.h" | |
29 | #include <servers/bootstrap.h> | |
30 | #include <mach/kern_return.h> | |
31 | #include <mach/message.h> | |
32 | #include <mach/mig_errors.h> | |
b54c578e | 33 | #include "mach_notifyServer.h" |
b1ab9ed8 A |
34 | #include <security_utilities/debugging.h> |
35 | #include <malloc/malloc.h> | |
36 | ||
37 | #if defined(USECFCURRENTTIME) | |
38 | # include <CoreFoundation/CFDate.h> | |
39 | #else | |
40 | # include <sys/time.h> | |
41 | #endif | |
42 | ||
0e1db9d1 A |
43 | #define SEC_MACH_AUDIT_TOKEN_PID (5) |
44 | ||
b1ab9ed8 A |
45 | namespace Security { |
46 | namespace MachPlusPlus { | |
47 | ||
48 | ||
49 | // | |
50 | // Global per-thread information | |
51 | // | |
52 | ModuleNexus< ThreadNexus<MachServer::PerThread> > MachServer::thread; | |
53 | ||
54 | ||
55 | // | |
56 | // Create a server object. | |
57 | // The resulting object is not "active", and any number of server objects | |
58 | // can be in this "prepared" state at the same time. | |
59 | // | |
60 | MachServer::MachServer() | |
61 | { setup("(anonymous)"); } | |
62 | ||
63 | MachServer::MachServer(const char *name) | |
64 | : mServerPort(name, bootstrap) | |
65 | { setup(name); } | |
66 | ||
67 | MachServer::MachServer(const char *name, const Bootstrap &boot) | |
68 | : bootstrap(boot), mServerPort(name, bootstrap) | |
69 | { setup(name); } | |
70 | ||
71 | void MachServer::setup(const char *name) | |
72 | { | |
73 | workerTimeout = 60 * 2; // 2 minutes default timeout | |
74 | maxWorkerCount = 100; // sanity check limit | |
75 | useFloatingThread = false; // tight thread management | |
76 | ||
77 | mPortSet += mServerPort; | |
78 | } | |
79 | ||
80 | MachServer::~MachServer() | |
81 | { | |
82 | // The ReceivePort members will clean themselves up. | |
83 | // The bootstrap server will clear us from its map when our receive port dies. | |
84 | } | |
85 | ||
86 | ||
87 | // | |
88 | // Add and remove extra listening ports. | |
89 | // Messages directed to those ports are dispatched through the main handler. | |
90 | // To get automatic call-out to another handler, use the Handler class. | |
91 | // | |
92 | void MachServer::add(Port receiver) | |
93 | { | |
fa7225c8 | 94 | secinfo("machserver", "port add: %d", receiver.port()); |
b1ab9ed8 A |
95 | mPortSet += receiver; |
96 | } | |
97 | ||
98 | void MachServer::remove(Port receiver) | |
99 | { | |
fa7225c8 | 100 | secinfo("machserver", "port remove: %d", receiver.port()); |
b1ab9ed8 A |
101 | mPortSet -= receiver; |
102 | } | |
103 | ||
104 | ||
105 | // | |
106 | // Register for mach port notifications | |
107 | // | |
108 | void MachServer::notifyIfDead(Port port, bool doNotify) const | |
109 | { | |
110 | if (doNotify) | |
111 | port.requestNotify(mServerPort); | |
112 | else | |
113 | port.cancelNotify(); | |
114 | } | |
115 | ||
116 | void MachServer::notifyIfUnused(Port port, bool doNotify) const | |
117 | { | |
118 | if (doNotify) | |
119 | port.requestNotify(port, MACH_NOTIFY_NO_SENDERS, true); | |
120 | else | |
121 | port.cancelNotify(MACH_NOTIFY_NO_SENDERS); | |
122 | } | |
123 | ||
124 | ||
125 | // | |
126 | // Initiate service. | |
127 | // This call will take control of the current thread and use it to service | |
128 | // incoming requests. The thread will not be released until an error happens, which | |
129 | // will cause an exception to be thrown. In other words, this never returns normally. | |
130 | // We may also be creating additional threads to service concurrent requests | |
131 | // as appropriate. | |
132 | // @@@ Msg-errors in additional threads are not acted upon. | |
133 | // | |
427c49bc | 134 | void MachServer::run(mach_msg_size_t maxSize, mach_msg_options_t options) |
b1ab9ed8 A |
135 | { |
136 | // establish server-global (thread-shared) parameters | |
137 | mMaxSize = maxSize; | |
138 | mMsgOptions = options; | |
139 | ||
140 | // establish the thread pool state | |
141 | // (don't need managerLock since we're the only thread as of yet) | |
142 | idleCount = workerCount = 1; | |
143 | nextCheckTime = Time::now() + workerTimeout; | |
144 | leastIdleWorkers = 1; | |
145 | highestWorkerCount = 1; | |
146 | ||
147 | // run server loop in initial (immortal) thread | |
fa7225c8 | 148 | secinfo("machserver", "start thread"); |
b1ab9ed8 | 149 | runServerThread(false); |
fa7225c8 | 150 | secinfo("machserver", "end thread"); |
b1ab9ed8 A |
151 | |
152 | // primary server thread exited somehow (not currently possible) | |
153 | assert(false); | |
154 | } | |
155 | ||
156 | ||
157 | // | |
158 | // This is the core of a server thread at work. It takes over the thread until | |
159 | // (a) an error occurs, throwing an exception | |
160 | // (b) low-load timeout happens, causing a normal return (doTimeout only) | |
161 | // This code was once based on mach_msg_server.c, but it is getting harder to notice | |
162 | // the lingering resemblance. | |
163 | // | |
164 | extern "C" boolean_t cdsa_notify_server(mach_msg_header_t *in, mach_msg_header_t *out); | |
165 | ||
166 | void MachServer::runServerThread(bool doTimeout) | |
167 | { | |
168 | // allocate request/reply buffers | |
169 | Message bufRequest(mMaxSize); | |
170 | Message bufReply(mMaxSize); | |
171 | ||
172 | // all exits from runServerThread are through exceptions | |
173 | try { | |
174 | // register as a worker thread | |
175 | perThread().server = this; | |
176 | ||
177 | for (;;) { | |
178 | // progress hook | |
179 | eventDone(); | |
180 | ||
181 | // process all pending timers | |
427c49bc | 182 | while (processTimer()) {} |
b1ab9ed8 A |
183 | |
184 | // check for worker idle timeout | |
185 | { StLock<Mutex> _(managerLock); | |
186 | // record idle thread low-water mark in scan interval | |
187 | if (idleCount < leastIdleWorkers) | |
188 | leastIdleWorkers = idleCount; | |
189 | ||
190 | // perform self-timeout processing | |
191 | if (doTimeout) { | |
192 | if (workerCount > maxWorkerCount) // someone reduced maxWorkerCount recently... | |
193 | break; // ... so release this thread immediately | |
194 | Time::Absolute rightNow = Time::now(); | |
195 | if (rightNow >= nextCheckTime) { // reaping period complete; process | |
196 | UInt32 idlers = leastIdleWorkers; | |
fa7225c8 | 197 | secinfo("machserver", "reaping workers: %d %d", (uint32_t) workerCount, (uint32_t) idlers); |
b1ab9ed8 A |
198 | nextCheckTime = rightNow + workerTimeout; |
199 | leastIdleWorkers = INT_MAX; | |
200 | if (idlers > 1) // multiple idle threads throughout measuring interval... | |
201 | break; // ... so release this thread now | |
202 | } | |
203 | } | |
204 | } | |
205 | ||
206 | // determine next timeout (if any) | |
207 | bool indefinite = false; | |
208 | Time::Interval timeout = workerTimeout; | |
209 | { StLock<Mutex> _(managerLock); | |
210 | if (timers.empty()) { | |
211 | indefinite = !doTimeout; | |
212 | } else { | |
213 | timeout = max(Time::Interval(0), timers.next() - Time::now()); | |
214 | if (doTimeout && workerTimeout < timeout) | |
215 | timeout = workerTimeout; | |
216 | } | |
217 | } | |
fa7225c8 | 218 | |
b1ab9ed8 A |
219 | // receive next IPC request (or wait for timeout) |
220 | mach_msg_return_t mr = indefinite ? | |
221 | mach_msg_overwrite(bufRequest, | |
222 | MACH_RCV_MSG | mMsgOptions, | |
223 | 0, mMaxSize, mPortSet, | |
224 | MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, | |
225 | (mach_msg_header_t *) 0, 0) | |
226 | : | |
227 | mach_msg_overwrite(bufRequest, | |
228 | MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_INTERRUPT | mMsgOptions, | |
229 | 0, mMaxSize, mPortSet, | |
230 | mach_msg_timeout_t(timeout.mSeconds()), MACH_PORT_NULL, | |
231 | (mach_msg_header_t *) 0, 0); | |
232 | ||
233 | switch (mr) { | |
234 | case MACH_MSG_SUCCESS: | |
235 | // process received request message below | |
236 | break; | |
237 | default: | |
fa7225c8 | 238 | secinfo("machserver", "received error: %d", mr); |
b1ab9ed8 A |
239 | continue; |
240 | } | |
241 | ||
79b9da22 A |
242 | // reset the buffer each time, handlers don't consistently set out params |
243 | bufReply.clearBuffer(); | |
244 | ||
b1ab9ed8 A |
245 | // process received message |
246 | if (bufRequest.msgId() >= MACH_NOTIFY_FIRST && | |
247 | bufRequest.msgId() <= MACH_NOTIFY_LAST) { | |
248 | // mach kernel notification message | |
249 | // we assume this is quick, so no thread arbitration here | |
0e1db9d1 A |
250 | mach_msg_audit_trailer_t *tlr = bufRequest.auditTrailer(); |
251 | if (tlr == NULL || tlr->msgh_audit.val[SEC_MACH_AUDIT_TOKEN_PID] != 0) { | |
252 | secnotice("machserver", "ignoring invalid notify message"); | |
253 | continue; | |
254 | } | |
b1ab9ed8 A |
255 | cdsa_notify_server(bufRequest, bufReply); |
256 | } else { | |
257 | // normal request message | |
258 | StLock<MachServer, &MachServer::busy, &MachServer::idle> _(*this); | |
fa7225c8 | 259 | secinfo("machserver", "begin request: %d, %d", bufRequest.localPort().port(), bufRequest.msgId()); |
b1ab9ed8 A |
260 | |
261 | // try subsidiary handlers first | |
262 | bool handled = false; | |
263 | for (HandlerSet::const_iterator it = mHandlers.begin(); | |
264 | it != mHandlers.end(); it++) | |
265 | if (bufRequest.localPort() == (*it)->port()) { | |
266 | (*it)->handle(bufRequest, bufReply); | |
267 | handled = true; | |
268 | } | |
269 | if (!handled) { | |
270 | // unclaimed, send to main handler | |
271 | handle(bufRequest, bufReply); | |
272 | } | |
273 | ||
fa7225c8 | 274 | secinfo("machserver", "end request"); |
b1ab9ed8 A |
275 | } |
276 | ||
277 | // process reply generated by handler | |
278 | if (!(bufReply.bits() & MACH_MSGH_BITS_COMPLEX) && | |
279 | bufReply.returnCode() != KERN_SUCCESS) { | |
280 | if (bufReply.returnCode() == MIG_NO_REPLY) | |
281 | continue; | |
282 | // don't destroy the reply port right, so we can send an error message | |
283 | bufRequest.remotePort(MACH_PORT_NULL); | |
284 | mach_msg_destroy(bufRequest); | |
285 | } | |
286 | ||
287 | if (bufReply.remotePort() == MACH_PORT_NULL) { | |
288 | // no reply port, so destroy the reply | |
289 | if (bufReply.bits() & MACH_MSGH_BITS_COMPLEX) | |
290 | bufReply.destroy(); | |
291 | continue; | |
292 | } | |
293 | ||
294 | /* | |
295 | * We don't want to block indefinitely because the client | |
296 | * isn't receiving messages from the reply port. | |
297 | * If we have a send-once right for the reply port, then | |
298 | * this isn't a concern because the send won't block. | |
299 | * If we have a send right, we need to use MACH_SEND_TIMEOUT. | |
300 | * To avoid falling off the kernel's fast RPC path unnecessarily, | |
301 | * we only supply MACH_SEND_TIMEOUT when absolutely necessary. | |
302 | */ | |
b54c578e | 303 | mr = mach_msg_overwrite(bufReply, |
b1ab9ed8 A |
304 | (MACH_MSGH_BITS_REMOTE(bufReply.bits()) == |
305 | MACH_MSG_TYPE_MOVE_SEND_ONCE) ? | |
306 | MACH_SEND_MSG | mMsgOptions : | |
307 | MACH_SEND_MSG | MACH_SEND_TIMEOUT | mMsgOptions, | |
308 | bufReply.length(), 0, MACH_PORT_NULL, | |
309 | 0, MACH_PORT_NULL, NULL, 0); | |
b54c578e A |
310 | switch (mr) { |
311 | case MACH_MSG_SUCCESS: | |
312 | break; | |
313 | case MACH_SEND_INVALID_DEST: | |
314 | case MACH_SEND_TIMED_OUT: | |
fa7225c8 | 315 | secinfo("machserver", "send error: %d %d", mr, bufReply.remotePort().port()); |
b54c578e A |
316 | bufReply.destroy(); |
317 | break; | |
318 | default: | |
319 | secinfo("machserver", "send error: %d %d", mr, bufReply.remotePort().port()); | |
320 | break; | |
321 | } | |
b1ab9ed8 A |
322 | |
323 | ||
324 | // clean up after the transaction | |
325 | releaseDeferredAllocations(); | |
326 | } | |
327 | perThread().server = NULL; | |
328 | ||
329 | } catch (...) { | |
330 | perThread().server = NULL; | |
331 | throw; | |
332 | } | |
333 | } | |
334 | ||
335 | ||
336 | // | |
337 | // Manage subsidiary port handlers | |
338 | // | |
339 | void MachServer::add(Handler &handler) | |
340 | { | |
341 | assert(mHandlers.find(&handler) == mHandlers.end()); | |
342 | assert(handler.port() != MACH_PORT_NULL); | |
343 | mHandlers.insert(&handler); | |
344 | mPortSet += handler.port(); | |
345 | } | |
346 | ||
347 | void MachServer::remove(Handler &handler) | |
348 | { | |
349 | assert(mHandlers.find(&handler) != mHandlers.end()); | |
350 | mHandlers.erase(&handler); | |
351 | mPortSet -= handler.port(); | |
352 | } | |
353 | ||
354 | ||
355 | // | |
356 | // Abstract auxiliary message handlers | |
357 | // | |
358 | MachServer::Handler::~Handler() | |
359 | { /* virtual */ } | |
360 | ||
361 | ||
362 | // | |
363 | // Implement a Handler that sends no reply | |
364 | // | |
365 | boolean_t MachServer::NoReplyHandler::handle(mach_msg_header_t *in, mach_msg_header_t *out) | |
366 | { | |
367 | // set up reply message to be valid (enough) and read "do not send reply" | |
368 | out->msgh_bits = 0; | |
369 | out->msgh_remote_port = MACH_PORT_NULL; | |
370 | out->msgh_size = sizeof(mig_reply_error_t); | |
371 | ((mig_reply_error_t *)out)->RetCode = MIG_NO_REPLY; | |
372 | ||
373 | // call input-only handler | |
374 | return handle(in); | |
375 | } | |
376 | ||
377 | ||
378 | // | |
379 | // Register a memory block for deferred release. | |
380 | // | |
381 | void MachServer::releaseWhenDone(Allocator &alloc, void *memory) | |
382 | { | |
383 | if (memory) { | |
384 | set<Allocation> &releaseSet = perThread().deferredAllocations; | |
385 | assert(releaseSet.find(Allocation(memory, alloc)) == releaseSet.end()); | |
fa7225c8 | 386 | secinfo("machserver", "allocing register %p with alloc %p", memory, &alloc); |
b1ab9ed8 A |
387 | releaseSet.insert(Allocation(memory, alloc)); |
388 | } | |
389 | } | |
390 | ||
391 | ||
392 | // | |
393 | // Run through the accumulated deferred allocations and release them. | |
394 | // This is done automatically on every pass through the server loop; | |
395 | // it must be called by subclasses that implement their loop in some | |
396 | // other way. | |
397 | // @@@X Needs to be thread local | |
398 | // | |
399 | void MachServer::releaseDeferredAllocations() | |
400 | { | |
401 | set<Allocation> &releaseSet = perThread().deferredAllocations; | |
402 | for (set<Allocation>::iterator it = releaseSet.begin(); it != releaseSet.end(); it++) { | |
fa7225c8 A |
403 | secinfo("machserver", "releasing alloc at %p with %p", it->addr, it->allocator); |
404 | ||
b1ab9ed8 A |
405 | // before we release the deferred allocation, zap it so that secrets aren't left in memory |
406 | size_t memSize = malloc_size(it->addr); | |
407 | bzero(it->addr, memSize); | |
408 | it->allocator->free(it->addr); | |
409 | } | |
410 | releaseSet.erase(releaseSet.begin(), releaseSet.end()); | |
411 | } | |
412 | ||
413 | ||
414 | // | |
415 | // The handler function calls this if it realizes that it might be blocked | |
416 | // (or doing something that takes a long time). We respond by ensuring that | |
417 | // at least one more thread is ready to serve requests. | |
418 | // Calls the threadLimitReached callback in the server object if the thread | |
419 | // limit has been exceeded and a needed new thread was not created. | |
420 | // | |
421 | void MachServer::longTermActivity() | |
422 | { | |
423 | if (!useFloatingThread) { | |
424 | StLock<Mutex> _(managerLock); | |
425 | ensureReadyThread(); | |
426 | } | |
427 | } | |
428 | ||
429 | void MachServer::busy() | |
430 | { | |
431 | StLock<Mutex> _(managerLock); | |
432 | idleCount--; | |
433 | if (useFloatingThread) | |
434 | ensureReadyThread(); | |
435 | } | |
436 | ||
437 | void MachServer::idle() | |
438 | { | |
439 | StLock<Mutex> _(managerLock); | |
440 | idleCount++; | |
441 | } | |
442 | ||
443 | ||
444 | void MachServer::ensureReadyThread() | |
445 | { | |
446 | if (idleCount == 0) { | |
447 | if (workerCount >= maxWorkerCount) { | |
448 | this->threadLimitReached(workerCount); // call remedial handler | |
449 | } | |
450 | if (workerCount < maxWorkerCount) { // threadLimit() may have raised maxWorkerCount | |
451 | (new LoadThread(*this))->run(); | |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | ||
457 | // | |
458 | // The callback hook for our subclasses. | |
459 | // The default does nothing, thereby denying further thread creation. | |
460 | // You could do something like maxThreads(limit+1) here to grant an variance; | |
461 | // or throw an exception to avoid possible deadlocks (this would abort the current | |
462 | // request but not otherwise impact the server's operation). | |
463 | // | |
464 | void MachServer::threadLimitReached(UInt32 limit) | |
465 | { | |
466 | } | |
467 | ||
468 | ||
469 | // | |
470 | // What our (non-primary) load threads do | |
471 | // | |
472 | void MachServer::LoadThread::action() | |
473 | { | |
474 | //@@@ race condition?! can server exit before helpers thread gets here? | |
475 | ||
476 | // register the worker thread and go | |
477 | server.addThread(this); | |
478 | try { | |
fa7225c8 | 479 | secinfo("machserver", "start thread"); |
b1ab9ed8 | 480 | server.runServerThread(true); |
fa7225c8 | 481 | secinfo("machserver", "end thread"); |
b1ab9ed8 A |
482 | } catch (...) { |
483 | // fell out of server loop by error. Let the thread go quietly | |
fa7225c8 | 484 | secinfo("machserver", "end thread (due to error)"); |
b1ab9ed8 A |
485 | } |
486 | server.removeThread(this); | |
487 | } | |
488 | ||
489 | ||
490 | // | |
491 | // Thread accounting | |
492 | // | |
493 | void MachServer::addThread(Thread *thread) | |
494 | { | |
495 | StLock<Mutex> _(managerLock); | |
496 | workerCount++; | |
497 | idleCount++; | |
498 | workers.insert(thread); | |
499 | } | |
500 | ||
501 | void MachServer::removeThread(Thread *thread) | |
502 | { | |
503 | StLock<Mutex> _(managerLock); | |
504 | workerCount--; | |
505 | idleCount--; | |
506 | workers.erase(thread); | |
507 | } | |
508 | ||
509 | ||
510 | // | |
511 | // Timer management | |
512 | // | |
513 | MachServer::Timer::~Timer() | |
514 | { } | |
515 | ||
516 | void MachServer::Timer::select() | |
517 | { } | |
518 | ||
519 | void MachServer::Timer::unselect() | |
520 | { } | |
521 | ||
522 | bool MachServer::processTimer() | |
523 | { | |
524 | Timer *top; | |
525 | { StLock<Mutex> _(managerLock); // could have multiple threads trying this | |
526 | if (!(top = static_cast<Timer *>(timers.pop(Time::now())))) | |
527 | return false; // nothing (more) to be done now | |
528 | } // drop lock; work has been retrieved | |
529 | try { | |
fa7225c8 | 530 | secinfo("machserver", "timer start: %p, %d, %f", top, top->longTerm(), Time::now().internalForm()); |
b1ab9ed8 A |
531 | StLock<MachServer::Timer, |
532 | &MachServer::Timer::select, &MachServer::Timer::unselect> _t(*top); | |
533 | if (top->longTerm()) { | |
534 | StLock<MachServer, &MachServer::busy, &MachServer::idle> _(*this); | |
535 | top->action(); | |
536 | } else { | |
537 | top->action(); | |
538 | } | |
fa7225c8 | 539 | secinfo("machserver", "timer end (false)"); |
b1ab9ed8 | 540 | } catch (...) { |
fa7225c8 | 541 | secinfo("machserver", "timer end (true)"); |
b1ab9ed8 A |
542 | } |
543 | return true; | |
544 | } | |
545 | ||
546 | void MachServer::setTimer(Timer *timer, Time::Absolute when) | |
547 | { | |
548 | StLock<Mutex> _(managerLock); | |
549 | timers.schedule(timer, when); | |
550 | } | |
551 | ||
552 | void MachServer::clearTimer(Timer *timer) | |
553 | { | |
554 | StLock<Mutex> _(managerLock); | |
555 | if (timer->scheduled()) | |
556 | timers.unschedule(timer); | |
557 | } | |
558 | ||
559 | ||
560 | // | |
561 | // Notification hooks and shims. Defaults do nothing. | |
562 | // | |
79b9da22 | 563 | kern_return_t cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port) |
b1ab9ed8 A |
564 | { |
565 | try { | |
566 | MachServer::active().notifyDeadName(port); | |
567 | } catch (...) { | |
568 | } | |
dbe77505 A |
569 | // the act of receiving a dead name notification allocates a dead-name |
570 | // right that must be deallocated | |
571 | mach_port_deallocate(mach_task_self(), port); | |
79b9da22 | 572 | return KERN_SUCCESS; |
b1ab9ed8 A |
573 | } |
574 | ||
575 | void MachServer::notifyDeadName(Port) { } | |
576 | ||
79b9da22 | 577 | kern_return_t cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port) |
b1ab9ed8 A |
578 | { |
579 | try { | |
580 | MachServer::active().notifyPortDeleted(port); | |
581 | } catch (...) { | |
582 | } | |
79b9da22 | 583 | return KERN_SUCCESS; |
b1ab9ed8 A |
584 | } |
585 | ||
586 | void MachServer::notifyPortDeleted(Port) { } | |
587 | ||
79b9da22 | 588 | kern_return_t cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port) |
b1ab9ed8 A |
589 | { |
590 | try { | |
591 | MachServer::active().notifyPortDestroyed(port); | |
592 | } catch (...) { | |
593 | } | |
79b9da22 | 594 | return KERN_SUCCESS; |
b1ab9ed8 A |
595 | } |
596 | ||
597 | void MachServer::notifyPortDestroyed(Port) { } | |
598 | ||
79b9da22 | 599 | kern_return_t cdsa_mach_notify_send_once(mach_port_t port) |
b1ab9ed8 A |
600 | { |
601 | try { | |
602 | MachServer::active().notifySendOnce(port); | |
603 | } catch (...) { | |
604 | } | |
79b9da22 | 605 | return KERN_SUCCESS; |
b1ab9ed8 A |
606 | } |
607 | ||
608 | void MachServer::notifySendOnce(Port) { } | |
609 | ||
79b9da22 | 610 | kern_return_t cdsa_mach_notify_no_senders(mach_port_t port, mach_port_mscount_t count) |
b1ab9ed8 A |
611 | { |
612 | try { | |
613 | MachServer::active().notifyNoSenders(port, count); | |
614 | } catch (...) { | |
615 | } | |
79b9da22 | 616 | return KERN_SUCCESS; |
b1ab9ed8 A |
617 | } |
618 | ||
619 | void MachServer::notifyNoSenders(Port, mach_port_mscount_t) { } | |
620 | ||
621 | void MachServer::eventDone() { } | |
622 | ||
623 | ||
624 | } // end namespace MachPlusPlus | |
625 | ||
626 | } // end namespace Security |