static void stopAppleTalk(CFRunLoopTimerRef timer, void *info) { char *argv[] = { "appletalk", "-d", NULL }; SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk shutdown")); execCommand = _SCDPluginExecCommand(stopComplete, // callback info, // context 0, // uid 0, // gid "/usr/sbin/appletalk", // path argv); // argv if (!timer) { execRetry = 5; // initialize retry count } return; }
static void booter(kickeeRef target) { char **argv = NULL; char *cmd = NULL; CFStringRef execCommand = CFDictionaryGetValue(target->dict, CFSTR("execCommand")); int i; CFArrayRef keys = NULL; CFStringRef name = CFDictionaryGetValue(target->dict, CFSTR("name")); int nKeys = 0; Boolean ok = FALSE; CFStringRef postName = CFDictionaryGetValue(target->dict, CFSTR("postName")); SCLog(_verbose, LOG_DEBUG, CFSTR("Kicker callback, target=%@"), name); if (!isA_CFString(postName) && !isA_CFString(execCommand)) { goto error; /* if no notifications to post nor commands to execute */ } if (isA_CFString(postName)) { uint32_t status; /* * post a notification */ cmd = _SC_cfstring_to_cstring(postName, NULL, 0, kCFStringEncodingASCII); if (!cmd) { SCLog(TRUE, LOG_DEBUG, CFSTR(" could not convert post name to C string")); goto error; } SCLog(TRUE, LOG_NOTICE, CFSTR("posting notification %s"), cmd); status = notify_post(cmd); if (status != NOTIFY_STATUS_OK) { SCLog(TRUE, LOG_DEBUG, CFSTR(" notify_post() failed: error=%ld"), status); goto error; } CFAllocatorDeallocate(NULL, cmd); /* clean up */ cmd = NULL; } /* * get the arguments for the kickee */ keys = target->changedKeys; target->changedKeys = NULL; if (isA_CFString(execCommand)) { CFRange bpr; CFNumberRef execGID = CFDictionaryGetValue(target->dict, CFSTR("execGID")); CFNumberRef execUID = CFDictionaryGetValue(target->dict, CFSTR("execUID")); CFBooleanRef passKeys = CFDictionaryGetValue(target->dict, CFSTR("changedKeysAsArguments")); gid_t reqGID = 0; uid_t reqUID = 0; CFMutableStringRef str; /* * build the kickee command */ str = CFStringCreateMutableCopy(NULL, 0, execCommand); bpr = CFStringFind(str, CFSTR("$BUNDLE"), 0); if (bpr.location != kCFNotFound) { CFStringRef bundlePath; bundlePath = CFURLCopyFileSystemPath(myBundleURL, kCFURLPOSIXPathStyle); CFStringReplace(str, bpr, bundlePath); CFRelease(bundlePath); } cmd = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingASCII); CFRelease(str); if (!cmd) { SCLog(TRUE, LOG_DEBUG, CFSTR(" could not convert command to C string")); goto error; } /* * get the UID/GID for the kickee */ if (isA_CFNumber(execUID)) { CFNumberGetValue(execUID, kCFNumberIntType, &reqUID); } if (isA_CFNumber(execGID)) { CFNumberGetValue(execGID, kCFNumberIntType, &reqGID); } nKeys = CFArrayGetCount(keys); argv = CFAllocatorAllocate(NULL, (nKeys + 2) * sizeof(char *), 0); for (i = 0; i < (nKeys + 2); i++) { argv[i] = NULL; } /* create command name argument */ if ((argv[0] = rindex(cmd, '/')) != NULL) { argv[0]++; } else { argv[0] = cmd; } /* create changed key arguments */ if (isA_CFBoolean(passKeys) && CFBooleanGetValue(passKeys)) { for (i = 0; i < nKeys; i++) { CFStringRef key = CFArrayGetValueAtIndex(keys, i); argv[i+1] = _SC_cfstring_to_cstring(key, NULL, 0, kCFStringEncodingASCII); if (!argv[i+1]) { SCLog(TRUE, LOG_DEBUG, CFSTR(" could not convert argument to C string")); goto error; } } } SCLog(TRUE, LOG_NOTICE, CFSTR("executing %s"), cmd); SCLog(_verbose, LOG_DEBUG, CFSTR(" current uid = %d, requested = %d"), geteuid(), reqUID); /* this kicker is now "running" */ target->active = TRUE; (void)_SCDPluginExecCommand(booterExit, target, reqUID, reqGID, cmd, argv); // CFAllocatorDeallocate(NULL, cmd); /* clean up */ // cmd = NULL; } else { target->active = FALSE; } target->needsKick = FALSE; /* allow additional requests to be queued */ ok = TRUE; error : if (keys) CFRelease(keys); if (cmd) CFAllocatorDeallocate(NULL, cmd); if (argv) { for (i = 0; i < nKeys; i++) { if (argv[i+1]) { CFAllocatorDeallocate(NULL, argv[i+1]); } } CFAllocatorDeallocate(NULL, argv); } if (!ok) { /* * If the target action can't be performed this time then * there's not much point in trying again. As such, I close * the session and the kickee target released. */ cleanupKicker(target); } return; }
/* _doPowerEmergencyShutdown() * Performs a semi-complicated proecdure to get machines experiencing power failure to automatically power up when external power is restored. We do this to trigger the onboard "Restart Automatically after a power failure" feature; pulling the power from a mostly-shutdown machine simulates a "power failure." At the time we do the shutdown... The UPS has lost external power (presumably due to a power outage) and our backup power thresholds have been exceeded and we've decided to do a system shutdown. 1. Request that the UPS remove power from the system in kDelayedRemovePowerMinutes 2. Set the StallSystemAtHalt root domain property that will cause the machine to hang at shutdown (after the OS has been shutdown). 3. We run the emergency shutdown script (/usr/libexec/upsshutdown) and shutdown to the point of stalling. 4. OS hangs there at the endpoint, waiting for UPS to remove power to its AC plugs. Later... 1. External power is restored to the UPS 2. The UPS restores power to its outlets, the CPU's "Restart Automatically after a power failure" switch is triggered. System boots up and resumes operation. * */ static void _doPowerEmergencyShutdown(CFNumberRef ups_id) { static int _alreadyShuttingDown = 0; CFDictionaryRef _ESSettings = NULL; char *shutdown_argv[3]; CFNumberRef auto_restart; IOReturn error; bool upsRestart = false; int restart_setting; if(_alreadyShuttingDown) return; _alreadyShuttingDown = 1; syslog(LOG_INFO, "Performing emergency UPS low power shutdown now"); _ESSettings = PMSettings_CopyActivePMSettings(); if(!_ESSettings) goto shutdown; auto_restart = isA_CFNumber(CFDictionaryGetValue(_ESSettings, CFSTR(kIOPMRestartOnPowerLossKey))); if(auto_restart) { CFNumberGetValue(auto_restart, kCFNumberIntType, &restart_setting); } else { restart_setting = 0; } upsRestart = restart_setting ? true:false; if(!upsRestart) { // Automatic Restart On Power Loss checkbox is disabled - // just do a plain old shutdown // and don't attempt to auto-restart. goto shutdown; } // Does the attached UPS support RemovePowerDelayed? if(_upsSupports(ups_id, CFSTR(kIOPSCommandDelayedRemovePowerKey))) { syslog(LOG_INFO, "System will restart when external power is restored to UPS."); error = _upsCommand(ups_id, CFSTR(kIOPSCommandStartupDelayKey), _delayBeforeStartupMinutes); if(kIOReturnSuccess != error) { // Attempt to set "startup when power restored" delay failed syslog(LOG_INFO, "UPS Emergency shutdown: error 0x%08x requesting UPS startup delay of %d minutes\n", error, _delayBeforeStartupMinutes); goto shutdown; } error = _upsCommand(ups_id, CFSTR(kIOPSCommandDelayedRemovePowerKey), _delayedRemovePowerMinutes); if(kIOReturnSuccess != error) { // We tried telling the UPS to auto-restart us, but since that's // failing we're just going to do an old school shutdown that // requires human intervention to power on. syslog(LOG_INFO, "UPS Emergency shutdown: error 0x%08x communicating shutdown time to UPS\n", error); goto shutdown; } } shutdown: if (_ESSettings) { CFRelease(_ESSettings); } shutdown_argv[0] = (char *)"/usr/libexec/upsshutdown"; if(upsRestart) { shutdown_argv[1] = (char *)"WaitForUPS"; shutdown_argv[2] = NULL; } else { shutdown_argv[1] = NULL; } _SCDPluginExecCommand(0, 0, 0, 0, "/usr/libexec/upsshutdown", shutdown_argv); }
static void startAppleTalk(CFRunLoopTimerRef timer, void *info) { int argc = 0; char *argv[8]; char *computerName = NULL; char *interface = NULL; CFStringRef mode = CFDictionaryGetValue(curStartup, CFSTR("APPLETALK")); CFStringRef name = CFDictionaryGetValue(curStartup, CFSTR("APPLETALK_HOSTNAME")); SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk startup")); if (!mode) { // Huh? return; } // set command name argv[argc++] = "appletalk"; // set hostname if (name) { computerName = cfstring_to_cstring(name, NULL, 0); if (computerName) { argv[argc++] = "-C"; argv[argc++] = computerName; } else { // could not convert name goto done; } } // set mode if (CFEqual(mode, CFSTR("-ROUTER-"))) { argv[argc++] = "-r"; } else if (CFEqual(mode, CFSTR("-MULTIHOME-"))) { argv[argc++] = "-x"; } else { interface = cfstring_to_cstring(mode, NULL, 0); if (interface) { argv[argc++] = "-u"; argv[argc++] = interface; } else { // could not convert interface goto done; } } // set non-interactive argv[argc++] = "-q"; // close argument list argv[argc++] = NULL; execCommand = _SCDPluginExecCommand(startComplete, // callback info, // context 0, // uid 0, // gid "/usr/sbin/appletalk", // path argv); // argv if (!timer) { execRetry = 5; // initialize retry count } done : if (computerName) CFAllocatorDeallocate(NULL, computerName); if (interface) CFAllocatorDeallocate(NULL, interface); return; }