From 03967f70f2706b7981a12173e14d3160ab6f8d05 Mon Sep 17 00:00:00 2001 From: "gtm.daemon" Date: Fri, 27 Aug 2010 19:35:47 +0000 Subject: [Author: dmaclach] Initial look at GTMServiceManagement. Added tests and project changes. R=thomasvl DELTA=844 (840 added, 0 deleted, 4 changed) --- Foundation/GTMServiceManagement.c | 602 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 602 insertions(+) create mode 100644 Foundation/GTMServiceManagement.c (limited to 'Foundation/GTMServiceManagement.c') diff --git a/Foundation/GTMServiceManagement.c b/Foundation/GTMServiceManagement.c new file mode 100644 index 0000000..49d110c --- /dev/null +++ b/Foundation/GTMServiceManagement.c @@ -0,0 +1,602 @@ +// +// GTMServiceManagement.c +// +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Note: launch_data_t have different ownership semantics than CFType/NSObjects. +// In general if you create one, you are responsible for releasing it. +// However, if you add it to a collection (LAUNCH_DATA_DICTIONARY, +// LAUNCH_DATA_ARRAY), you no longer own it, and are no longer +// responsible for releasing it (you may be responsible for the array +// or dictionary of course). A corrollary of this is that a +// launch_data_t can only be in one collection at any given time. + +#include "GTMServiceManagement.h" + +typedef struct { + CFMutableDictionaryRef dict; + bool convert_non_standard_objects; + CFErrorRef *error; +} GTMLToCFDictContext; + +typedef struct { + launch_data_t dict; + CFErrorRef *error; +} GTMCFToLDictContext; + +static CFErrorRef GTMCFLaunchCreateUnlocalizedError(CFIndex code, + CFStringRef format, ...) CF_FORMAT_FUNCTION(2, 3) { + CFDictionaryRef user_info = NULL; + if (format) { + va_list args; + va_start(args, format); + CFStringRef string + = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, + NULL, + format, + args); + user_info = CFDictionaryCreate(kCFAllocatorDefault, + (const void **)&kCFErrorDescriptionKey, + (const void **)&string, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(string); + va_end(args); + } + CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, + kCFErrorDomainPOSIX, + code, + user_info); + if (user_info) { + CFRelease(user_info); + } + return error; +} + +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); + } +} + +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, NULL); + } + } + return resp; +} + +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) { + CFShow(local_error); + CFRelease(local_error); + } + return result; +} + +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) { + CFShow(local_error); + CFRelease(local_error); + } + return cf_type_ref; +} + +Boolean GTMSMJobSubmit(CFDictionaryRef cf_job, CFErrorRef *error) { + CFErrorRef local_error = NULL; + launch_data_t launch_job = GTMLaunchDataCreateFromCFType(cf_job, + &local_error); + if (!local_error) { + launch_data_t jobs = launch_data_alloc(LAUNCH_DATA_ARRAY); + launch_data_array_set_index(jobs, launch_job, 0); + launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB); + launch_data_t resp = launch_msg(msg); + if (resp) { + launch_data_type_t resp_type = launch_data_get_type(resp); + switch (resp_type) { + case LAUNCH_DATA_ARRAY: + for (size_t i = 0; i < launch_data_array_get_count(jobs); i++) { + launch_data_t job_response = launch_data_array_get_index(resp, i); + launch_data_t job = launch_data_array_get_index(jobs, i); + launch_data_t job_label + = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL); + const char *job_string + = job_label ? launch_data_get_string(job_label) : "Unlabeled job"; + if (LAUNCH_DATA_ERRNO == launch_data_get_type(job_response)) { + int job_err = launch_data_get_errno(job_response); + if (job_err != 0) { + CFStringRef format_string = NULL; + switch (job_err) { + case EEXIST: + format_string = CFSTR("%s already loaded"); + break; + case ESRCH: + format_string = CFSTR("%s not loaded"); + break; + default: + format_string = CFSTR("%s failed to load"); + break; + } + local_error = GTMCFLaunchCreateUnlocalizedError(job_err, + format_string, + job_string); + } + } + } + break; + + case LAUNCH_DATA_ERRNO: { + int e = launch_data_get_errno(resp); + if (e) { + local_error = GTMCFLaunchCreateUnlocalizedError(e, NULL); + } + break; + } + + default: + local_error + = GTMCFLaunchCreateUnlocalizedError(EINVAL, + CFSTR("unknown response from launchd %d"), + resp_type); + break; + } + launch_data_free(resp); + launch_data_free(msg); + } else { + local_error = GTMCFLaunchCreateUnlocalizedError(errno, NULL); + } + + } + if (error) { + *error = local_error; + } else if (local_error) { + CFShow(local_error); + 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, NULL); + } + break; + } + + default: + local_error + = GTMCFLaunchCreateUnlocalizedError(EINVAL, + CFSTR("unknown response from launchd %d"), + resp_type); + break; + } + launch_data_free(resp); + } else { + local_error = GTMCFLaunchCreateUnlocalizedError(errno, NULL); + } + if (error) { + *error = local_error; + } else if (local_error) { + CFShow(local_error); + 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, NULL); + } + break; + } + + default: + local_error + = GTMCFLaunchCreateUnlocalizedError(EINVAL, + CFSTR("unknown response from launchd %d"), + resp_type); + break; + } + launch_data_free(resp); + } else { + local_error = GTMCFLaunchCreateUnlocalizedError(errno, NULL); + } + if (error) { + *error = local_error; + } else if (local_error) { + CFShow(local_error); + CFRelease(local_error); + } + return local_error == NULL; +} + +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) { + CFShow(error); + CFRelease(error); + } + return dict; +} + +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, NULL); + } + if (error) { + CFShow(error); + CFRelease(error); + } + return dict; +} + + -- cgit v1.2.3