PageRenderTime 69ms CodeModel.GetById 2ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/update-engine/externals/google-toolbox-for-mac/Foundation/GTMServiceManagement.c

http://macfuse.googlecode.com/
C | 776 lines | 663 code | 60 blank | 53 comment | 154 complexity | 5a1b893613dfdb622faa06e48b7c72bb MD5 | raw file
  1//
  2//  GTMServiceManagement.c
  3//
  4//  Copyright 2010 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19// As per http://openp2p.com/pub/a/oreilly/ask_tim/2001/codepolicy.html
 20// The following functions
 21// open_devnull
 22// spc_sanitize_files
 23// spc_drop_privileges
 24// are derived from Chapter 1 of "Secure Programming Cookbook for C and C++" by
 25// John Viega and Matt Messier. Copyright 2003 O'Reilly & Associates.
 26// ISBN 0-596-00394-3
 27
 28// Note: launch_data_t have different ownership semantics than CFType/NSObjects.
 29//       In general if you create one, you are responsible for releasing it.
 30//       However, if you add it to a collection (LAUNCH_DATA_DICTIONARY,
 31//       LAUNCH_DATA_ARRAY), you no longer own it, and are no longer
 32//       responsible for releasing it (you may be responsible for the array
 33//       or dictionary of course). A corrollary of this is that a
 34//       launch_data_t can only be in one collection at any given time.
 35
 36#include "GTMServiceManagement.h"
 37
 38#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
 39
 40#include <paths.h>
 41#include <unistd.h>
 42#include <sys/stat.h>
 43#include <vproc.h>
 44
 45typedef struct {
 46  CFMutableDictionaryRef dict;
 47  bool convert_non_standard_objects;
 48  CFErrorRef *error;
 49} GTMLToCFDictContext;
 50
 51typedef struct {
 52  launch_data_t dict;
 53  CFErrorRef *error;
 54} GTMCFToLDictContext;
 55
 56static CFErrorRef GTMCFLaunchCreateUnlocalizedError(CFIndex code,
 57                                                    CFStringRef format, ...) CF_FORMAT_FUNCTION(2, 3);
 58
 59static CFErrorRef GTMCFLaunchCreateUnlocalizedError(CFIndex code,
 60                                                    CFStringRef format, ...) {
 61  CFDictionaryRef user_info = NULL;
 62  if (format) {
 63    va_list args;
 64    va_start(args, format);
 65    CFStringRef string
 66      = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
 67                                             NULL,
 68                                             format,
 69                                             args);
 70    user_info = CFDictionaryCreate(kCFAllocatorDefault,
 71                                   (const void **)&kCFErrorDescriptionKey,
 72                                   (const void **)&string,
 73                                   1,
 74                                   &kCFTypeDictionaryKeyCallBacks,
 75                                   &kCFTypeDictionaryValueCallBacks);
 76    CFRelease(string);
 77    va_end(args);
 78  }
 79  CFErrorRef error = CFErrorCreate(kCFAllocatorDefault,
 80                                   kCFErrorDomainPOSIX,
 81                                   code,
 82                                   user_info);
 83  if (user_info) {
 84    CFRelease(user_info);
 85  }
 86  return error;
 87}
 88
 89static void GTMConvertCFDictEntryToLaunchDataDictEntry(const void *key,
 90                                                       const void *value,
 91                                                       void *context) {
 92  GTMCFToLDictContext *local_context = (GTMCFToLDictContext *)context;
 93  if (*(local_context->error)) return;
 94
 95  launch_data_t launch_value
 96    = GTMLaunchDataCreateFromCFType(value, local_context->error);
 97  if (launch_value) {
 98    launch_data_t launch_key
 99      = GTMLaunchDataCreateFromCFType(key, local_context->error);
100    if (launch_key) {
101      bool goodInsert
102        = launch_data_dict_insert(local_context->dict,
103                                  launch_value,
104                                  launch_data_get_string(launch_key));
105      if (!goodInsert) {
106        *(local_context->error)
107          = GTMCFLaunchCreateUnlocalizedError(EINVAL,
108                                              CFSTR("launch_data_dict_insert "
109                                                    "failed key: %@ value: %@"),
110                                              key,
111                                              value);
112        launch_data_free(launch_value);
113      }
114      launch_data_free(launch_key);
115    }
116  }
117}
118
119static void GTMConvertLaunchDataDictEntryToCFDictEntry(const launch_data_t value,
120                                                       const char *key,
121                                                       void *context) {
122  GTMLToCFDictContext *local_context = (GTMLToCFDictContext *)context;
123  if (*(local_context->error)) return;
124
125  CFTypeRef cf_value
126    = GTMCFTypeCreateFromLaunchData(value,
127                                    local_context->convert_non_standard_objects,
128                                    local_context->error);
129  if (cf_value) {
130    CFStringRef cf_key = CFStringCreateWithCString(kCFAllocatorDefault,
131                                                   key,
132                                                   kCFStringEncodingUTF8);
133    if (cf_key) {
134      CFDictionarySetValue(local_context->dict, cf_key, cf_value);
135      CFRelease(cf_key);
136    } else {
137      *(local_context->error)
138        = GTMCFLaunchCreateUnlocalizedError(EINVAL,
139                                            CFSTR("Unable to create key %s"),
140                                            key);
141    }
142    CFRelease(cf_value);
143  }
144}
145
146static launch_data_t GTMPerformOnLabel(const char *verb,
147                                       CFStringRef jobLabel,
148                                       CFErrorRef *error) {
149  launch_data_t resp = NULL;
150  launch_data_t label = GTMLaunchDataCreateFromCFType(jobLabel, error);
151  if (*error == NULL) {
152    launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
153    launch_data_dict_insert(msg, label, verb);
154    resp = launch_msg(msg);
155    launch_data_free(msg);
156    if (!resp) {
157      *error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR(""));
158    }
159  }
160  return resp;
161}
162
163launch_data_t GTMLaunchDataCreateFromCFType(CFTypeRef cf_type_ref,
164                                            CFErrorRef *error) {
165  launch_data_t result = NULL;
166  CFErrorRef local_error = NULL;
167  if (cf_type_ref == NULL) {
168    local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL,
169                                                    CFSTR("NULL CFType"));
170    goto exit;
171  }
172
173  CFTypeID cf_type = CFGetTypeID(cf_type_ref);
174  if (cf_type == CFStringGetTypeID()) {
175    CFIndex length = CFStringGetLength(cf_type_ref);
176    CFIndex max_length
177      = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
178    char *buffer = calloc(max_length, sizeof(char));
179    size_t buffer_size = max_length * sizeof(char);
180    if (buffer) {
181      if (CFStringGetCString(cf_type_ref,
182                             buffer,
183                             buffer_size,
184                             kCFStringEncodingUTF8)) {
185        result = launch_data_alloc(LAUNCH_DATA_STRING);
186        launch_data_set_string(result, buffer);
187      } else {
188        local_error
189          = GTMCFLaunchCreateUnlocalizedError(EINVAL,
190                                              CFSTR("CFStringGetCString failed %@"),
191                                              cf_type_ref);
192      }
193      free(buffer);
194    } else {
195      local_error = GTMCFLaunchCreateUnlocalizedError(ENOMEM,
196                                                      CFSTR("calloc of %zu failed"),
197                                                      buffer_size);
198    }
199  } else if (cf_type == CFBooleanGetTypeID()) {
200    result = launch_data_alloc(LAUNCH_DATA_BOOL);
201    launch_data_set_bool(result, CFBooleanGetValue(cf_type_ref));
202  } else if (cf_type == CFArrayGetTypeID()) {
203    CFIndex count = CFArrayGetCount(cf_type_ref);
204    result = launch_data_alloc(LAUNCH_DATA_ARRAY);
205    for (CFIndex i = 0; i < count; i++) {
206      CFTypeRef array_value = CFArrayGetValueAtIndex(cf_type_ref, i);
207      if (array_value) {
208        launch_data_t launch_value
209          = GTMLaunchDataCreateFromCFType(array_value, &local_error);
210        if (local_error) break;
211        launch_data_array_set_index(result, launch_value, i);
212      }
213    }
214  } else if (cf_type == CFDictionaryGetTypeID()) {
215    result = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
216    GTMCFToLDictContext context = { result, &local_error };
217    CFDictionaryApplyFunction(cf_type_ref,
218                              GTMConvertCFDictEntryToLaunchDataDictEntry,
219                              &context);
220  } else if (cf_type == CFDataGetTypeID()) {
221    result = launch_data_alloc(LAUNCH_DATA_OPAQUE);
222    launch_data_set_opaque(result,
223                           CFDataGetBytePtr(cf_type_ref),
224                           CFDataGetLength(cf_type_ref));
225  } else if (cf_type == CFNumberGetTypeID()) {
226    CFNumberType cf_number_type = CFNumberGetType(cf_type_ref);
227    switch (cf_number_type) {
228      case kCFNumberSInt8Type:
229      case kCFNumberSInt16Type:
230      case kCFNumberSInt32Type:
231      case kCFNumberSInt64Type:
232      case kCFNumberCharType:
233      case kCFNumberShortType:
234      case kCFNumberIntType:
235      case kCFNumberLongType:
236      case kCFNumberLongLongType:
237      case kCFNumberCFIndexType:
238      case kCFNumberNSIntegerType:{
239        long long value;
240        if (CFNumberGetValue(cf_type_ref, kCFNumberLongLongType, &value)) {
241          result = launch_data_alloc(LAUNCH_DATA_INTEGER);
242          launch_data_set_integer(result, value);
243        } else {
244          local_error
245            = GTMCFLaunchCreateUnlocalizedError(EINVAL,
246                                                CFSTR("Unknown to convert: %@"),
247                                                cf_type_ref);
248        }
249        break;
250      }
251
252      case kCFNumberFloat32Type:
253      case kCFNumberFloat64Type:
254      case kCFNumberFloatType:
255      case kCFNumberDoubleType:
256      case kCFNumberCGFloatType: {
257        double value;
258        if (CFNumberGetValue(cf_type_ref, kCFNumberDoubleType, &value)) {
259          result = launch_data_alloc(LAUNCH_DATA_REAL);
260          launch_data_set_real(result, value);
261        } else {
262          local_error
263            = GTMCFLaunchCreateUnlocalizedError(EINVAL,
264                                                CFSTR("Unknown to convert: %@"),
265                                                cf_type_ref);
266        }
267        break;
268      }
269
270      default:
271        local_error
272          = GTMCFLaunchCreateUnlocalizedError(EINVAL,
273                                              CFSTR("Unknown CFNumberType %lld"),
274                                              (long long)cf_number_type);
275        break;
276    }
277  } else {
278    local_error
279      = GTMCFLaunchCreateUnlocalizedError(EINVAL,
280                                          CFSTR("Unknown CFTypeID %lu"),
281                                          cf_type);
282  }
283
284exit:
285  if (error) {
286    *error = local_error;
287  } else if (local_error) {
288#ifdef DEBUG
289    CFShow(local_error);
290#endif //  DEBUG
291    CFRelease(local_error);
292  }
293  return result;
294}
295
296CFTypeRef GTMCFTypeCreateFromLaunchData(launch_data_t ldata,
297                                        bool convert_non_standard_objects,
298                                        CFErrorRef *error) {
299  CFTypeRef cf_type_ref = NULL;
300  CFErrorRef local_error = NULL;
301  if (ldata == NULL) {
302    local_error = GTMCFLaunchCreateUnlocalizedError(EINVAL,
303                                                    CFSTR("NULL ldata"));
304    goto exit;
305  }
306
307  launch_data_type_t ldata_type = launch_data_get_type(ldata);
308  switch (ldata_type) {
309    case LAUNCH_DATA_STRING:
310      cf_type_ref
311        = CFStringCreateWithCString(kCFAllocatorDefault,
312                                    launch_data_get_string(ldata),
313                                    kCFStringEncodingUTF8);
314      break;
315
316    case LAUNCH_DATA_INTEGER: {
317      long long value = launch_data_get_integer(ldata);
318      cf_type_ref = CFNumberCreate(kCFAllocatorDefault,
319                                   kCFNumberLongLongType,
320                                   &value);
321      break;
322    }
323
324    case LAUNCH_DATA_REAL: {
325      double value = launch_data_get_real(ldata);
326      cf_type_ref = CFNumberCreate(kCFAllocatorDefault,
327                                   kCFNumberDoubleType,
328                                   &value);
329      break;
330    }
331
332    case LAUNCH_DATA_BOOL: {
333      bool value = launch_data_get_bool(ldata);
334      cf_type_ref = value ? kCFBooleanTrue : kCFBooleanFalse;
335      CFRetain(cf_type_ref);
336      break;
337    }
338
339    case LAUNCH_DATA_OPAQUE: {
340      size_t size = launch_data_get_opaque_size(ldata);
341      void *data = launch_data_get_opaque(ldata);
342      cf_type_ref = CFDataCreate(kCFAllocatorDefault, data, size);
343      break;
344    }
345
346    case LAUNCH_DATA_ARRAY: {
347      size_t count = launch_data_array_get_count(ldata);
348      cf_type_ref = CFArrayCreateMutable(kCFAllocatorDefault,
349                                         count,
350                                         &kCFTypeArrayCallBacks);
351      if (cf_type_ref) {
352        for (size_t i = 0; !local_error && i < count; i++) {
353          launch_data_t l_sub_data = launch_data_array_get_index(ldata, i);
354          CFTypeRef cf_sub_type
355            = GTMCFTypeCreateFromLaunchData(l_sub_data,
356                                            convert_non_standard_objects,
357                                            &local_error);
358          if (cf_sub_type) {
359            CFArrayAppendValue((CFMutableArrayRef)cf_type_ref, cf_sub_type);
360            CFRelease(cf_sub_type);
361          }
362        }
363      }
364      break;
365    }
366
367    case LAUNCH_DATA_DICTIONARY:
368      cf_type_ref = CFDictionaryCreateMutable(kCFAllocatorDefault,
369                                              0,
370                                              &kCFTypeDictionaryKeyCallBacks,
371                                              &kCFTypeDictionaryValueCallBacks);
372      if (cf_type_ref) {
373        GTMLToCFDictContext context = {
374          (CFMutableDictionaryRef)cf_type_ref,
375          convert_non_standard_objects,
376          &local_error
377        };
378        launch_data_dict_iterate(ldata,
379                                 GTMConvertLaunchDataDictEntryToCFDictEntry,
380                                 &context);
381      }
382      break;
383
384    case LAUNCH_DATA_FD:
385      if (convert_non_standard_objects) {
386        int file_descriptor = launch_data_get_fd(ldata);
387        cf_type_ref = CFNumberCreate(kCFAllocatorDefault,
388                                     kCFNumberIntType,
389                                     &file_descriptor);
390      }
391      break;
392
393    case LAUNCH_DATA_MACHPORT:
394      if (convert_non_standard_objects) {
395        mach_port_t port = launch_data_get_machport(ldata);
396        cf_type_ref = CFNumberCreate(kCFAllocatorDefault,
397                                     kCFNumberIntType,
398                                     &port);
399      }
400      break;
401
402    default:
403      local_error =
404        GTMCFLaunchCreateUnlocalizedError(EINVAL,
405                                          CFSTR("Unknown launchd type %d"),
406                                          ldata_type);
407      break;
408  }
409exit:
410  if (error) {
411    *error = local_error;
412  } else if (local_error) {
413#ifdef DEBUG
414    CFShow(local_error);
415#endif //  DEBUG
416    CFRelease(local_error);
417  }
418  return cf_type_ref;
419}
420
421// open the standard file descs to devnull.
422static int open_devnull(int fd) {
423  FILE *f = NULL;
424
425  if (fd == STDIN_FILENO) {
426    f = freopen(_PATH_DEVNULL, "rb", stdin);
427  }
428  else if (fd == STDOUT_FILENO) {
429    f = freopen(_PATH_DEVNULL, "wb", stdout);
430  }
431  else if (fd == STDERR_FILENO) {
432    f = freopen(_PATH_DEVNULL, "wb", stderr);
433  }
434  return (f && fileno(f) == fd);
435}
436
437void spc_sanitize_files(void) {
438  int standard_fds[] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO };
439  int standard_fds_count
440    = (int)(sizeof(standard_fds) / sizeof(standard_fds[0]));
441
442  // Make sure all open descriptors other than the standard ones are closed
443  int fds = getdtablesize();
444  for (int i = standard_fds_count; i < fds; ++i) close(i);
445
446  // Verify that the standard descriptors are open.  If they're not, attempt to
447  // open them using /dev/null.  If any are unsuccessful, abort.
448  for (int i = 0; i < standard_fds_count; ++i) {
449    struct stat st;
450    int fd = standard_fds[i];
451    if (fstat(fd, &st) == -1 && (errno != EBADF || !open_devnull(fd))) {
452      abort();
453    }
454  }
455}
456
457void spc_drop_privileges(void) {
458  gid_t newgid = getgid(), oldgid = getegid();
459  uid_t newuid = getuid(), olduid = geteuid();
460
461  // If root privileges are to be dropped, be sure to pare down the ancillary
462  // groups for the process before doing anything else because the setgroups()
463  // system call requires root privileges.  Drop ancillary groups regardless of
464  // whether privileges are being dropped temporarily or permanently.
465  if (!olduid) setgroups(1, &newgid);
466
467  if (newgid != oldgid) {
468    if (setregid(-1, newgid) == -1) {
469      abort();
470    }
471  }
472
473  if (newuid != olduid) {
474    if (setregid(-1, newuid) == -1) {
475      abort();
476    }
477  }
478
479  // verify that the changes were successful
480  if (newgid != oldgid && (setegid(oldgid) != -1 || getegid() != newgid)) {
481    abort();
482  }
483  if (newuid != olduid && (seteuid(olduid) != -1 || geteuid() != newuid)) {
484    abort();
485  }
486}
487
488Boolean GTMSMJobSubmit(CFDictionaryRef cf_job, CFErrorRef *error) {
489  // We launch our jobs using launchctl instead of doing it by hand
490  // because launchctl does a whole pile of parsing of the job internally
491  // to handle the sockets cases that we don't want to duplicate here.
492  int fd = -1;
493  CFDataRef xmlData = NULL;
494  CFErrorRef local_error = NULL;
495
496  if (!cf_job) {
497    local_error
498      = GTMCFLaunchCreateUnlocalizedError(EINVAL,
499                                          CFSTR("NULL Job."),
500                                          NULL);
501    goto exit;
502  }
503
504  CFStringRef jobLabel = CFDictionaryGetValue(cf_job,
505                                              CFSTR(LAUNCH_JOBKEY_LABEL));
506  if (!jobLabel) {
507    local_error
508      = GTMCFLaunchCreateUnlocalizedError(EINVAL,
509                                          CFSTR("Job missing label."),
510                                          NULL);
511    goto exit;
512  }
513
514  CFDictionaryRef jobDict = GTMSMJobCopyDictionary(jobLabel);
515  if (jobDict) {
516    CFRelease(jobDict);
517    local_error
518      = GTMCFLaunchCreateUnlocalizedError(EEXIST,
519                                          CFSTR("Job already exists %@."),
520                                          jobLabel);
521    goto exit;
522  }
523
524  xmlData = CFPropertyListCreateXMLData(NULL, cf_job);
525  if (!xmlData) {
526    local_error
527      = GTMCFLaunchCreateUnlocalizedError(EINVAL,
528                                          CFSTR("Invalid Job %@."),
529                                          jobLabel);
530    goto exit;
531  }
532
533  char fileName[] = _PATH_TMP "GTMServiceManagement.XXXXXX.plist";
534  fd = mkstemps(fileName, 6);
535  if (fd == -1) {
536    local_error
537      = GTMCFLaunchCreateUnlocalizedError(errno,
538                                          CFSTR("Unable to create %s."),
539                                          fileName);
540    goto exit;
541  }
542  write(fd, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData));
543  close(fd);
544
545  pid_t childpid = fork();
546  if (childpid == -1) {
547    local_error
548      = GTMCFLaunchCreateUnlocalizedError(errno,
549                                          CFSTR("Unable to fork."),
550                                          NULL);
551    goto exit;
552  }
553  if (childpid != 0) {
554    // Parent process
555    int status = 0;
556    pid_t pid = -1;
557    do {
558      pid = waitpid(childpid, &status, 0);
559    } while (pid == -1 && errno == EINTR);
560    if (pid == -1) {
561      local_error
562        = GTMCFLaunchCreateUnlocalizedError(errno,
563                                            CFSTR("waitpid failed."));
564        goto exit;
565    } else if (WEXITSTATUS(status)) {
566      local_error
567        = GTMCFLaunchCreateUnlocalizedError(ECHILD,
568                                            CFSTR("Child exit status: %d "
569                                                  "pid: %d"),
570                                            WEXITSTATUS(status), childpid);
571      goto exit;
572    }
573  } else {
574    // Child Process
575    spc_sanitize_files();
576    spc_drop_privileges();
577    const char *args[] = { "launchctl", "load", fileName, NULL };
578    execve("/bin/launchctl", (char* const*)args, NULL);
579    abort();
580  }
581exit:
582  if (xmlData) {
583    CFRelease(xmlData);
584  }
585  if (fd != -1) {
586    unlink(fileName);
587  }
588  if (error) {
589    *error = local_error;
590  } else if (local_error) {
591#ifdef DEBUG
592    CFShow(local_error);
593#endif //  DEBUG
594    CFRelease(local_error);
595  }
596  return local_error == NULL;
597}
598
599CFDictionaryRef GTMSMCopyJobCheckInDictionary(CFErrorRef *error) {
600  CFErrorRef local_error = NULL;
601  CFDictionaryRef check_in_dict = NULL;
602  launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
603  launch_data_t resp = launch_msg(msg);
604  launch_data_free(msg);
605  if (resp) {
606    launch_data_type_t resp_type = launch_data_get_type(resp);
607    switch (resp_type) {
608      case LAUNCH_DATA_DICTIONARY:
609        check_in_dict = GTMCFTypeCreateFromLaunchData(resp, true, &local_error);
610        break;
611
612      case LAUNCH_DATA_ERRNO: {
613        int e = launch_data_get_errno(resp);
614        if (e) {
615          local_error = GTMCFLaunchCreateUnlocalizedError(e, CFSTR(""));
616        }
617        break;
618      }
619
620      default:
621        local_error
622          = GTMCFLaunchCreateUnlocalizedError(EINVAL,
623                                              CFSTR("unknown response from launchd %d"),
624                                              resp_type);
625        break;
626    }
627    launch_data_free(resp);
628  } else {
629    local_error = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR(""));
630  }
631  if (error) {
632    *error = local_error;
633  } else if (local_error) {
634#ifdef DEBUG
635    CFShow(local_error);
636#endif //  DEBUG
637    CFRelease(local_error);
638  }
639  return check_in_dict;
640}
641
642Boolean GTMSMJobRemove(CFStringRef jobLabel, CFErrorRef *error) {
643  CFErrorRef local_error = NULL;
644  launch_data_t resp = GTMPerformOnLabel(LAUNCH_KEY_REMOVEJOB,
645                                         jobLabel,
646                                         &local_error);
647  if (resp) {
648    launch_data_type_t resp_type = launch_data_get_type(resp);
649    switch (resp_type) {
650      case LAUNCH_DATA_ERRNO: {
651        int e = launch_data_get_errno(resp);
652        if (e) {
653          local_error = GTMCFLaunchCreateUnlocalizedError(e, CFSTR(""));
654        }
655        break;
656      }
657
658      default:
659        local_error
660          = GTMCFLaunchCreateUnlocalizedError(EINVAL,
661                                              CFSTR("unknown response from launchd %d"),
662                                              resp_type);
663        break;
664    }
665    launch_data_free(resp);
666  }
667  if (error) {
668    *error = local_error;
669  } else if (local_error) {
670#ifdef DEBUG
671    CFShow(local_error);
672#endif //  DEBUG
673    CFRelease(local_error);
674  }
675  return local_error == NULL;
676}
677
678CFDictionaryRef GTMSMJobCopyDictionary(CFStringRef jobLabel) {
679  CFDictionaryRef dict = NULL;
680  CFErrorRef error = NULL;
681  launch_data_t resp = GTMPerformOnLabel(LAUNCH_KEY_GETJOB,
682                                         jobLabel,
683                                         &error);
684  if (resp) {
685    launch_data_type_t ldata_Type = launch_data_get_type(resp);
686    if (ldata_Type == LAUNCH_DATA_DICTIONARY) {
687      dict = GTMCFTypeCreateFromLaunchData(resp, true, &error);
688    } else {
689      error = GTMCFLaunchCreateUnlocalizedError(EINVAL,
690                                                CFSTR("Unknown launchd type %d"),
691                                                ldata_Type);
692    }
693    launch_data_free(resp);
694  }
695  if (error) {
696#ifdef DEBUG
697    CFShow(error);
698#endif //  DEBUG
699    CFRelease(error);
700  }
701  return dict;
702}
703
704CFDictionaryRef GTMSMCopyAllJobDictionaries(void) {
705  CFDictionaryRef dict = NULL;
706  launch_data_t msg = launch_data_new_string(LAUNCH_KEY_GETJOBS);
707  launch_data_t resp = launch_msg(msg);
708  launch_data_free(msg);
709  CFErrorRef error = NULL;
710
711  if (resp) {
712    launch_data_type_t ldata_Type = launch_data_get_type(resp);
713    if (ldata_Type == LAUNCH_DATA_DICTIONARY) {
714      dict = GTMCFTypeCreateFromLaunchData(resp, true, &error);
715    } else {
716      error = GTMCFLaunchCreateUnlocalizedError(EINVAL,
717                                                CFSTR("Unknown launchd type %d"),
718                                                ldata_Type);
719    }
720    launch_data_free(resp);
721  } else {
722    error
723      = GTMCFLaunchCreateUnlocalizedError(errno, CFSTR(""));
724  }
725  if (error) {
726#ifdef DEBUG
727    CFShow(error);
728#endif //  DEBUG
729    CFRelease(error);
730  }
731  return dict;
732}
733
734// Some private SPIs defined by apple in the launchd sources
735// http://opensource.apple.com/source/launchd/launchd-258.25/launchd/src/
736// and
737// http://opensource.apple.com/source/launchd/launchd-329.3/launchd/src/
738// It turns out that they renamed the enum that I need to use between 10.5 and
739// 10.6. Luckily if we request the 10_5 value on 10_6 we get an error
740// so we just ask for the 10_5 value first, and then the 10_6 value second.
741
742typedef enum {
743  VPROC_GSK_ENVIRONMENT_10_5 = 10,
744  VPROC_GSK_ENVIRONMENT_10_6 = 11
745} vproc_gsk_t;
746
747extern vproc_err_t vproc_swap_complex(vproc_t vp,
748                                      vproc_gsk_t key,
749                                      launch_data_t inval,
750                                      launch_data_t *outval);
751
752CFDictionaryRef GTMCopyLaunchdExports(void) {
753  launch_data_t resp;
754  CFDictionaryRef dict = NULL;
755  vproc_err_t err = vproc_swap_complex(NULL,
756                                       VPROC_GSK_ENVIRONMENT_10_5,
757                                       NULL,
758                                       &resp);
759  if (err) {
760    err = vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT_10_6, NULL, &resp);
761  }
762  if (err == NULL) {
763    CFErrorRef error = NULL;
764    dict = GTMCFTypeCreateFromLaunchData(resp, false, &error);
765    if (error) {
766#ifdef DEBUG
767      CFShow(error);
768#endif //  DEBUG
769      CFRelease(error);
770    }
771    launch_data_free(resp);
772  }
773  return dict;
774}
775
776#endif //  if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4