/externals/google-toolbox-for-mac/Foundation/GTMServiceManagement.c

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