+ free(argv);
+ }
+ os_log_error(AUTH_LOG, "comm pipe failure");
+ return errAuthorizationToolExecuteFailure;
+ }
+
+ OSStatus status = errSecSuccess;
+
+ // do the standard forking tango...
+ int delay = 1;
+ for (int n = 5;; n--, delay *= 2) {
+ switch (fork()) {
+ case -1: // error
+ if (errno == EAGAIN) {
+ // potentially recoverable resource shortage
+ if (n > 0) {
+ os_log(AUTH_LOG, "resource shortage (EAGAIN), delaying %d seconds", delay);
+ sleep(delay);
+ continue;
+ }
+ }
+ os_log_error(AUTH_LOG, "fork failed (errno=%d)", errno);
+ close(notify[READ]); close(notify[WRITE]);
+ status = errAuthorizationToolExecuteFailure;
+ goto exit_point;
+
+ default: { // parent
+ // close foreign side of pipes
+ close(notify[WRITE]);
+ if (communicationsPipe)
+ close(comm[WRITE]);
+
+ close(dataPipe[READ]);
+ if (write(dataPipe[WRITE], extForm, sizeof(*extForm)) != sizeof(*extForm)) {
+ os_log_error(AUTH_LOG, "fwrite data failed (errno=%d)", errno);
+ status = errAuthorizationInternal;
+ close(notify[READ]);
+ close(dataPipe[WRITE]);
+ if (communicationsPipe) {
+ close(comm[READ]);
+ }
+ goto exit_point;
+ }
+ // get status notification from child
+ os_log_debug(AUTH_LOG, "parent waiting for status");
+ ssize_t rc = read(notify[READ], &status, sizeof(status));
+ status = n2h(status);
+ switch (rc) {
+ default: // weird result of read: post error
+ os_log_error(AUTH_LOG, "unexpected read return value %ld", long(rc));
+ status = errAuthorizationToolEnvironmentError;
+ // fall through
+ case sizeof(status): // read succeeded: child reported an error
+ os_log_error(AUTH_LOG, "parent received status=%d", (int)status);
+ close(notify[READ]);
+ close(dataPipe[WRITE]);
+ if (communicationsPipe) {
+ close(comm[READ]);
+ close(comm[WRITE]);
+ }
+ goto exit_point;
+ case 0: // end of file: exec succeeded
+ close(notify[READ]);
+ close(dataPipe[WRITE]);
+ if (communicationsPipe)
+ *communicationsPipe = fdopen(comm[READ], "r+");
+ os_log_debug(AUTH_LOG, "parent resumes (no error)");
+ status = errSecSuccess;
+ goto exit_point;
+ }
+ }
+
+ case 0: // child
+ // close foreign side of pipes
+ close(notify[READ]);
+ if (communicationsPipe)
+ close(comm[READ]);
+
+ // close write end of the data PIPE
+ close(dataPipe[WRITE]);
+
+ // fd 1 (stdout) holds the notify write end
+ dup2(notify[WRITE], 1);
+ close(notify[WRITE]);
+
+ // fd 0 (stdin) holds either the comm-link write-end or /dev/null
+ if (communicationsPipe) {
+ dup2(comm[WRITE], 0);
+ close(comm[WRITE]);
+ } else {
+ close(0);
+ open("/dev/null", O_RDWR);
+ }
+
+ // okay, execute the trampoline
+ if (argv)
+ execv(trampoline, (char *const*)argv);
+
+ // execute failed - tell the parent
+ {
+ // in case of failure, close read end of the data pipe as well
+ close(dataPipe[WRITE]);
+ close(dataPipe[READ]);
+ OSStatus error = errAuthorizationToolExecuteFailure;
+ error = h2n(error);
+ write(1, &error, sizeof(error));
+ _exit(1);
+ }
+ }
+ }
+
+exit_point:
+ free(argv);
+ return status;
+}