CFDictionaryRef GTMSMCopyAllJobDictionaries(void) { CFDictionaryRef dict = NULL; launch_data_t msg = launch_data_new_string(LAUNCH_KEY_GETJOBS); launch_data_t resp = launch_msg(msg); launch_data_free(msg); CFErrorRef error = NULL; if (resp) { launch_data_type_t ldata_Type = launch_data_get_type(resp); if (ldata_Type == LAUNCH_DATA_DICTIONARY) { dict = GTMCFTypeCreateFromLaunchData(resp, true, &error); } else { error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unknown launchd type %d"), ldata_Type); } launch_data_free(resp); } else { error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR("")); } if (error) { #ifdef DEBUG CFShow(error); #endif // DEBUG CFRelease(error); } return dict; }
static void GTMConvertCFDictEntryToLaunchDataDictEntry(const void *key, const void *value, void *context) { GTMCFToLDictContext *local_context = (GTMCFToLDictContext *)context; if (*(local_context->error)) return; launch_data_t launch_value = GTMLaunchDataCreateFromCFType(value, local_context->error); if (launch_value) { launch_data_t launch_key = GTMLaunchDataCreateFromCFType(key, local_context->error); if (launch_key) { bool goodInsert = launch_data_dict_insert(local_context->dict, launch_value, launch_data_get_string(launch_key)); if (!goodInsert) { *(local_context->error) = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("launch_data_dict_insert " "failed key: %@ value: %@"), key, value); launch_data_free(launch_value); } launch_data_free(launch_key); } } }
static void GTMConvertLaunchDataDictEntryToCFDictEntry(const launch_data_t value, const char *key, void *context) { GTMLToCFDictContext *local_context = (GTMLToCFDictContext *)context; if (*(local_context->error)) return; CFTypeRef cf_value = GTMCFTypeCreateFromLaunchData(value, local_context->convert_non_standard_objects, local_context->error); if (cf_value) { CFStringRef cf_key = CFStringCreateWithCString(kCFAllocatorDefault, key, kCFStringEncodingUTF8); if (cf_key) { CFDictionarySetValue(local_context->dict, cf_key, cf_value); CFRelease(cf_key); } else { *(local_context->error) = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unable to create key %s"), key); } CFRelease(cf_value); } }
CFDictionaryRef GTMSMJobCopyDictionary(CFStringRef jobLabel) { CFDictionaryRef dict = NULL; CFErrorRef error = NULL; launch_data_t resp = GTMPerformOnLabel(LAUNCH_KEY_GETJOB, jobLabel, &error); if (resp) { launch_data_type_t ldata_Type = launch_data_get_type(resp); if (ldata_Type == LAUNCH_DATA_DICTIONARY) { dict = GTMCFTypeCreateFromLaunchData(resp, true, &error); } else { error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unknown launchd type %d"), ldata_Type); } launch_data_free(resp); } if (error) { #ifdef DEBUG CFShow(error); #endif // DEBUG CFRelease(error); } return dict; }
Boolean GTMSMJobRemove(CFStringRef jobLabel, CFErrorRef *error) { CFErrorRef local_error = NULL; launch_data_t resp = GTMPerformOnLabel(LAUNCH_KEY_REMOVEJOB, jobLabel, &local_error); if (resp) { launch_data_type_t resp_type = launch_data_get_type(resp); switch (resp_type) { case LAUNCH_DATA_ERRNO: { int e = launch_data_get_errno(resp); // In OSX 10.10+, launch_msg(LAUNCH_KEY_REMOVEJOB, ...) returns the // error EINPROGRESS if the job was running at the time it was removed. // This should be considered a success as it was on earlier OS versions. if (e == EINPROGRESS && IsOsYosemiteOrGreater()) { break; } if (e) { local_error = GTMCFLaunchCreateUnlocalizedError(e, CFSTR("")); } break; } default: local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("unknown response from launchd %d"), resp_type); break; } launch_data_free(resp); } if (error) { *error = local_error; } else if (local_error) { #ifdef DEBUG CFShow(local_error); #endif // DEBUG CFRelease(local_error); } return local_error == NULL; }
CFDictionaryRef GTMSMJobCheckIn(CFErrorRef *error) { CFErrorRef local_error = NULL; CFDictionaryRef check_in_dict = NULL; launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); launch_data_t resp = launch_msg(msg); launch_data_free(msg); if (resp) { launch_data_type_t resp_type = launch_data_get_type(resp); switch (resp_type) { case LAUNCH_DATA_DICTIONARY: check_in_dict = GTMCFTypeCreateFromLaunchData(resp, true, &local_error); break; case LAUNCH_DATA_ERRNO: { int e = launch_data_get_errno(resp); if (e) { local_error = GTMCFLaunchCreateUnlocalizedError(e, CFSTR("")); } break; } default: local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("unknown response from launchd %d"), resp_type); break; } launch_data_free(resp); } else { local_error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR("")); } if (error) { *error = local_error; } else if (local_error) { #ifdef DEBUG CFShow(local_error); #endif // DEBUG CFRelease(local_error); } return check_in_dict; }
Boolean GTMSMJobRemove(CFStringRef jobLabel, CFErrorRef *error) { CFErrorRef local_error = NULL; launch_data_t resp = GTMPerformOnLabel(LAUNCH_KEY_REMOVEJOB, jobLabel, &local_error); if (resp) { launch_data_type_t resp_type = launch_data_get_type(resp); switch (resp_type) { case LAUNCH_DATA_ERRNO: { int e = launch_data_get_errno(resp); if (e) { local_error = GTMCFLaunchCreateUnlocalizedError(e, CFSTR("")); } break; } default: local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("unknown response from launchd %d"), resp_type); break; } launch_data_free(resp); } else { local_error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR("")); } if (error) { *error = local_error; } else if (local_error) { #ifdef DEBUG CFShow(local_error); #endif // DEBUG CFRelease(local_error); } return local_error == NULL; }
static launch_data_t GTMPerformOnLabel(const char *verb, CFStringRef jobLabel, CFErrorRef *error) { launch_data_t resp = NULL; launch_data_t label = GTMLaunchDataCreateFromCFType(jobLabel, error); if (*error == NULL) { launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); launch_data_dict_insert(msg, label, verb); resp = launch_msg(msg); launch_data_free(msg); if (!resp) { *error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR("")); } } return resp; }
Boolean GTMSMJobSubmit(CFDictionaryRef cf_job, CFErrorRef *error) { // We launch our jobs using launchctl instead of doing it by hand // because launchctl does a whole pile of parsing of the job internally // to handle the sockets cases that we don't want to duplicate here. int fd = -1; CFDataRef xmlData = NULL; char fileName[] = _PATH_TMP "GTMServiceManagement.XXXXXX"; CFErrorRef local_error = NULL; if (!cf_job) { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("NULL Job."), NULL); goto exit; } CFStringRef jobLabel = CFDictionaryGetValue(cf_job, CFSTR(LAUNCH_JOBKEY_LABEL)); if (!jobLabel) { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Job missing label."), NULL); goto exit; } CFDictionaryRef jobDict = GTMSMJobCopyDictionary(jobLabel); if (jobDict) { CFRelease(jobDict); local_error = GTMCFLaunchCreateUnlocalizedError(EEXIST, CFSTR("Job already exists %@."), jobLabel); goto exit; } xmlData = CFPropertyListCreateXMLData(NULL, cf_job); if (!xmlData) { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Invalid Job %@."), jobLabel); goto exit; } fd = mkstemp(fileName); if (fd == -1) { local_error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR("Unable to create %s."), fileName); goto exit; } write(fd, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData)); close(fd); pid_t childpid = fork(); if (childpid == -1) { local_error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR("Unable to fork."), NULL); goto exit; } if (childpid != 0) { // Parent process int status = 0; pid_t pid = -1; do { pid = waitpid(childpid, &status, 0); } while (pid == -1 && errno == EINTR); if (pid == -1) { local_error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR("waitpid failed.")); goto exit; } else if (WEXITSTATUS(status)) { local_error = GTMCFLaunchCreateUnlocalizedError(ECHILD, CFSTR("Child exit status: %d " "pid: %d"), WEXITSTATUS(status), childpid); goto exit; } } else { // Child Process spc_sanitize_files(); spc_drop_privileges(); const char *args[] = { "launchctl", "load", fileName, NULL }; execve("/bin/launchctl", (char* const*)args, NULL); abort(); } exit: if (xmlData) { CFRelease(xmlData); } if (fd != -1) { unlink(fileName); } if (error) { *error = local_error; } else if (local_error) { #ifdef DEBUG CFShow(local_error); #endif // DEBUG CFRelease(local_error); } return local_error == NULL; }
CFTypeRef GTMCFTypeCreateFromLaunchData(launch_data_t ldata, bool convert_non_standard_objects, CFErrorRef *error) { CFTypeRef cf_type_ref = NULL; CFErrorRef local_error = NULL; if (ldata == NULL) { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("NULL ldata")); goto exit; } launch_data_type_t ldata_type = launch_data_get_type(ldata); switch (ldata_type) { case LAUNCH_DATA_STRING: cf_type_ref = CFStringCreateWithCString(kCFAllocatorDefault, launch_data_get_string(ldata), kCFStringEncodingUTF8); break; case LAUNCH_DATA_INTEGER: { long long value = launch_data_get_integer(ldata); cf_type_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &value); break; } case LAUNCH_DATA_REAL: { double value = launch_data_get_real(ldata); cf_type_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value); break; } case LAUNCH_DATA_BOOL: { bool value = launch_data_get_bool(ldata); cf_type_ref = value ? kCFBooleanTrue : kCFBooleanFalse; CFRetain(cf_type_ref); break; } case LAUNCH_DATA_OPAQUE: { size_t size = launch_data_get_opaque_size(ldata); void *data = launch_data_get_opaque(ldata); cf_type_ref = CFDataCreate(kCFAllocatorDefault, data, size); break; } case LAUNCH_DATA_ARRAY: { size_t count = launch_data_array_get_count(ldata); cf_type_ref = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks); if (cf_type_ref) { for (size_t i = 0; !local_error && i < count; i++) { launch_data_t l_sub_data = launch_data_array_get_index(ldata, i); CFTypeRef cf_sub_type = GTMCFTypeCreateFromLaunchData(l_sub_data, convert_non_standard_objects, &local_error); if (cf_sub_type) { CFArrayAppendValue((CFMutableArrayRef)cf_type_ref, cf_sub_type); CFRelease(cf_sub_type); } } } break; } case LAUNCH_DATA_DICTIONARY: cf_type_ref = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (cf_type_ref) { GTMLToCFDictContext context = { (CFMutableDictionaryRef)cf_type_ref, convert_non_standard_objects, &local_error }; launch_data_dict_iterate(ldata, GTMConvertLaunchDataDictEntryToCFDictEntry, &context); } break; case LAUNCH_DATA_FD: if (convert_non_standard_objects) { int file_descriptor = launch_data_get_fd(ldata); cf_type_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &file_descriptor); } break; case LAUNCH_DATA_MACHPORT: if (convert_non_standard_objects) { mach_port_t port = launch_data_get_machport(ldata); cf_type_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &port); } break; default: local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unknown launchd type %d"), ldata_type); break; } exit: if (error) { *error = local_error; } else if (local_error) { #ifdef DEBUG CFShow(local_error); #endif // DEBUG CFRelease(local_error); } return cf_type_ref; }
launch_data_t GTMLaunchDataCreateFromCFType(CFTypeRef cf_type_ref, CFErrorRef *error) { launch_data_t result = NULL; CFErrorRef local_error = NULL; if (cf_type_ref == NULL) { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("NULL CFType")); goto exit; } CFTypeID cf_type = CFGetTypeID(cf_type_ref); if (cf_type == CFStringGetTypeID()) { CFIndex length = CFStringGetLength(cf_type_ref); CFIndex max_length = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; char *buffer = calloc(max_length, sizeof(char)); size_t buffer_size = max_length * sizeof(char); if (buffer) { if (CFStringGetCString(cf_type_ref, buffer, buffer_size, kCFStringEncodingUTF8)) { result = launch_data_alloc(LAUNCH_DATA_STRING); launch_data_set_string(result, buffer); } else { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("CFStringGetCString failed %@"), cf_type_ref); } free(buffer); } else { local_error = GTMCFLaunchCreateUnlocalizedError(ENOMEM, CFSTR("calloc of %zu failed"), buffer_size); } } else if (cf_type == CFBooleanGetTypeID()) { result = launch_data_alloc(LAUNCH_DATA_BOOL); launch_data_set_bool(result, CFBooleanGetValue(cf_type_ref)); } else if (cf_type == CFArrayGetTypeID()) { CFIndex count = CFArrayGetCount(cf_type_ref); result = launch_data_alloc(LAUNCH_DATA_ARRAY); for (CFIndex i = 0; i < count; i++) { CFTypeRef array_value = CFArrayGetValueAtIndex(cf_type_ref, i); if (array_value) { launch_data_t launch_value = GTMLaunchDataCreateFromCFType(array_value, &local_error); if (local_error) break; launch_data_array_set_index(result, launch_value, i); } } } else if (cf_type == CFDictionaryGetTypeID()) { result = launch_data_alloc(LAUNCH_DATA_DICTIONARY); GTMCFToLDictContext context = { result, &local_error }; CFDictionaryApplyFunction(cf_type_ref, GTMConvertCFDictEntryToLaunchDataDictEntry, &context); } else if (cf_type == CFDataGetTypeID()) { result = launch_data_alloc(LAUNCH_DATA_OPAQUE); launch_data_set_opaque(result, CFDataGetBytePtr(cf_type_ref), CFDataGetLength(cf_type_ref)); } else if (cf_type == CFNumberGetTypeID()) { CFNumberType cf_number_type = CFNumberGetType(cf_type_ref); switch (cf_number_type) { case kCFNumberSInt8Type: case kCFNumberSInt16Type: case kCFNumberSInt32Type: case kCFNumberSInt64Type: case kCFNumberCharType: case kCFNumberShortType: case kCFNumberIntType: case kCFNumberLongType: case kCFNumberLongLongType: case kCFNumberCFIndexType: case kCFNumberNSIntegerType:{ long long value; if (CFNumberGetValue(cf_type_ref, kCFNumberLongLongType, &value)) { result = launch_data_alloc(LAUNCH_DATA_INTEGER); launch_data_set_integer(result, value); } else { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unknown to convert: %@"), cf_type_ref); } break; } case kCFNumberFloat32Type: case kCFNumberFloat64Type: case kCFNumberFloatType: case kCFNumberDoubleType: case kCFNumberCGFloatType: { double value; if (CFNumberGetValue(cf_type_ref, kCFNumberDoubleType, &value)) { result = launch_data_alloc(LAUNCH_DATA_REAL); launch_data_set_real(result, value); } else { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unknown to convert: %@"), cf_type_ref); } break; } default: local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unknown CFNumberType %lld"), (long long)cf_number_type); break; } } else { local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL, CFSTR("Unknown CFTypeID %lu"), cf_type); } exit: if (error) { *error = local_error; } else if (local_error) { #ifdef DEBUG CFShow(local_error); #endif // DEBUG CFRelease(local_error); } return result; }