#include <sys/wait.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <mach/mach_error.h>
#include <CoreFoundation/CoreFoundation.h>
#include <syslog.h>
#include "libbootstrap_public.h"
#include "IPC.h"
#include "StartupItems.h"
#include "SystemStarter.h"
#include "SystemStarterIPC.h"
typedef struct TerminationContextStorage {
StartupContext aStartupContext;
CFMutableDictionaryRef anItem;
} *TerminationContext;
static void
startupItemTerminated(CFMachPortRef aMachPort, void *anInfo)
{
TerminationContext aTerminationContext = (TerminationContext) anInfo;
if (aMachPort) {
mach_port_deallocate(mach_task_self(), CFMachPortGetPort(aMachPort));
}
if (aTerminationContext && aTerminationContext->anItem) {
pid_t aPID = 0;
pid_t rPID = 0;
int aStatus = 0;
CFMutableDictionaryRef anItem = aTerminationContext->anItem;
StartupContext aStartupContext = aTerminationContext->aStartupContext;
if (anItem) {
aPID = StartupItemGetPID(anItem);
if (aPID > 0)
rPID = waitpid(aPID, &aStatus, 0);
}
if (aStartupContext) {
--aStartupContext->aRunningCount;
if (aStartupContext->aStatusDict) {
StartupItemExit(aStartupContext->aStatusDict, anItem, (WIFEXITED(aStatus) && WEXITSTATUS(aStatus) == 0));
if (aStatus) {
CF_syslog(LOG_WARNING, CFSTR("%@ (%d) did not complete successfully"), CFDictionaryGetValue(anItem, CFSTR("Description")), aPID);
} else {
CF_syslog(LOG_DEBUG, CFSTR("Finished %@ (%d)"), CFDictionaryGetValue(anItem, CFSTR("Description")), aPID);
}
}
if (WEXITSTATUS(aStatus) || WTERMSIG(aStatus) || WCOREDUMP(aStatus)) {
CFDictionarySetValue(anItem, kErrorKey, kErrorReturnNonZero);
AddItemToFailedList(aStartupContext, anItem);
}
RemoveItemFromWaitingList(aStartupContext, anItem);
}
}
if (aTerminationContext)
free(aTerminationContext);
}
void
MonitorStartupItem(StartupContext aStartupContext, CFMutableDictionaryRef anItem)
{
pid_t aPID = StartupItemGetPID(anItem);
if (anItem && aPID > 0) {
mach_port_t aPort;
kern_return_t aResult;
CFMachPortContext aContext;
CFMachPortRef aMachPort;
CFRunLoopSourceRef aSource;
TerminationContext aTerminationContext = (TerminationContext) malloc(sizeof(struct TerminationContextStorage));
aTerminationContext->aStartupContext = aStartupContext;
aTerminationContext->anItem = anItem;
aContext.version = 0;
aContext.info = aTerminationContext;
aContext.retain = 0;
aContext.release = 0;
if ((aResult = task_name_for_pid(mach_task_self(), aPID, &aPort)) != KERN_SUCCESS)
goto out_bad;
if (!(aMachPort = CFMachPortCreateWithPort(NULL, aPort, NULL, &aContext, NULL)))
goto out_bad;
if (!(aSource = CFMachPortCreateRunLoopSource(NULL, aMachPort, 0))) {
CFRelease(aMachPort);
goto out_bad;
}
CFMachPortSetInvalidationCallBack(aMachPort, startupItemTerminated);
CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource, kCFRunLoopCommonModes);
CFRelease(aSource);
CFRelease(aMachPort);
return;
out_bad:
startupItemTerminated(NULL, aTerminationContext);
}
}