IPC.c
  1  /**
  2   * IPC.c - System Starter IPC routines
  3   * Wilfredo Sanchez  | wsanchez@opensource.apple.com
  4   * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu
  5   * $Apple$
  6   **
  7   * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
  8   *
  9   * @APPLE_APACHE_LICENSE_HEADER_START@
 10   * 
 11   * Licensed under the Apache License, Version 2.0 (the "License");
 12   * you may not use this file except in compliance with the License.
 13   * You may obtain a copy of the License at
 14   * 
 15   *     http://www.apache.org/licenses/LICENSE-2.0
 16   * 
 17   * Unless required by applicable law or agreed to in writing, software
 18   * distributed under the License is distributed on an "AS IS" BASIS,
 19   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 20   * See the License for the specific language governing permissions and
 21   * limitations under the License.
 22   * 
 23   * @APPLE_APACHE_LICENSE_HEADER_END@
 24   **/
 25  
 26  #include <sys/wait.h>
 27  #include <mach/mach.h>
 28  #include <mach/message.h>
 29  #include <mach/mach_error.h>
 30  #include <CoreFoundation/CoreFoundation.h>
 31  #include <syslog.h>
 32  
 33  #include "bootstrap.h"
 34  
 35  #include "IPC.h"
 36  #include "StartupItems.h"
 37  #include "SystemStarter.h"
 38  #include "SystemStarterIPC.h"
 39  
 40  /* Structure to pass StartupContext and anItem to the termination handler. */
 41  typedef struct TerminationContextStorage {
 42  	StartupContext  aStartupContext;
 43  	CFMutableDictionaryRef anItem;
 44  }              *TerminationContext;
 45  
 46  /**
 47   * A CFMachPort invalidation callback that records the termination of
 48   * a startup item task.  Stops the current run loop to give system_starter
 49   * another go at running items.
 50   **/
 51  static void 
 52  startupItemTerminated(CFMachPortRef aMachPort, void *anInfo)
 53  {
 54  	TerminationContext aTerminationContext = (TerminationContext) anInfo;
 55  
 56  	if (aMachPort) {
 57  		mach_port_deallocate(mach_task_self(), CFMachPortGetPort(aMachPort));
 58  	}
 59  	if (aTerminationContext && aTerminationContext->anItem) {
 60  		pid_t           aPID = 0;
 61  		pid_t           rPID = 0;
 62  		int             aStatus = 0;
 63  		CFMutableDictionaryRef anItem = aTerminationContext->anItem;
 64  		StartupContext  aStartupContext = aTerminationContext->aStartupContext;
 65  
 66  		/* Get the exit status */
 67  		if (anItem) {
 68  			aPID = StartupItemGetPID(anItem);
 69  			if (aPID > 0)
 70  				rPID = waitpid(aPID, &aStatus, 0);
 71  		}
 72  		if (aStartupContext) {
 73  			--aStartupContext->aRunningCount;
 74  
 75  			/* Record the item's status */
 76  			if (aStartupContext->aStatusDict) {
 77  				StartupItemExit(aStartupContext->aStatusDict, anItem, (WIFEXITED(aStatus) && WEXITSTATUS(aStatus) == 0));
 78  				if (aStatus) {
 79  					CF_syslog(LOG_WARNING, CFSTR("%@ (%d) did not complete successfully"), CFDictionaryGetValue(anItem, CFSTR("Description")), aPID);
 80  				} else {
 81  					CF_syslog(LOG_DEBUG, CFSTR("Finished %@ (%d)"), CFDictionaryGetValue(anItem, CFSTR("Description")), aPID);
 82  				}
 83  			}
 84  			/*
 85  			 * If the item failed to start, then add it to the
 86  			 * failed list
 87  			 */
 88  			if (WEXITSTATUS(aStatus) || WTERMSIG(aStatus) || WCOREDUMP(aStatus)) {
 89  				CFDictionarySetValue(anItem, kErrorKey, kErrorReturnNonZero);
 90  				AddItemToFailedList(aStartupContext, anItem);
 91  			}
 92  			/*
 93  			 * Remove the item from the waiting list regardless
 94  			 * if it was successful or it failed.
 95  			 */
 96  			RemoveItemFromWaitingList(aStartupContext, anItem);
 97  		}
 98  	}
 99  	if (aTerminationContext)
100  		free(aTerminationContext);
101  }
102  
103  void 
104  MonitorStartupItem(StartupContext aStartupContext, CFMutableDictionaryRef anItem)
105  {
106  	pid_t           aPID = StartupItemGetPID(anItem);
107  	if (anItem && aPID > 0) {
108  		mach_port_t     aPort;
109  		kern_return_t   aResult;
110  		CFMachPortContext aContext;
111  		CFMachPortRef   aMachPort;
112  		CFRunLoopSourceRef aSource;
113  		TerminationContext aTerminationContext = (TerminationContext) malloc(sizeof(struct TerminationContextStorage));
114  
115  		aTerminationContext->aStartupContext = aStartupContext;
116  		aTerminationContext->anItem = anItem;
117  
118  		aContext.version = 0;
119  		aContext.info = aTerminationContext;
120  		aContext.retain = 0;
121  		aContext.release = 0;
122  
123  		if ((aResult = task_name_for_pid(mach_task_self(), aPID, &aPort)) != KERN_SUCCESS)
124  			goto out_bad;
125  
126  		if (!(aMachPort = CFMachPortCreateWithPort(NULL, aPort, NULL, &aContext, NULL)))
127  			goto out_bad;
128  
129  		if (!(aSource = CFMachPortCreateRunLoopSource(NULL, aMachPort, 0))) {
130  			CFRelease(aMachPort);
131  			goto out_bad;
132  		}
133  		CFMachPortSetInvalidationCallBack(aMachPort, startupItemTerminated);
134  		CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource, kCFRunLoopCommonModes);
135  		CFRelease(aSource);
136  		CFRelease(aMachPort);
137  		return;
138  out_bad:
139  		/*
140  		 * The assumption is something failed, the task already
141  		 * terminated.
142  		 */
143  		startupItemTerminated(NULL, aTerminationContext);
144  	}
145  }