pid_t vfork_and_run(void (*fn)(void*) /*NORETURN*/, void *arg) { /* GNO's fork2 call will return immediately and allow the parent and * child processes to execute concurrently using the same memory * space. To prevent them stomping on each other, we want to get * behavior like a traditional vfork() implementation, where the * parent blocks until the child terminates or execs. * * Our approach is to check the process tables to make sure the * child has actually finished or exec'd. If not, we loop and try again. * We can't just rely on the fact that the child signaled us, because * it may still be running in libc's implementation of exec*. */ long oldmask; pid_t pid; kvmt *kvm_context; struct pentry *proc_entry; int done = 0; /* Isolate child process's environment from parent */ if (environPush() != 0) return -1; /* Block all signals for now */ oldmask = sigblock(-1); pid = fork2(fork_thunk, CHILD_STACKSIZE, 0, forked_child_name, (sizeof(fn) + sizeof(arg) + sizeof(oldmask) + 1) / 2, fn, arg, oldmask); if (pid < 0) goto ret; while (!done) { /* Wait for ~100 ms. If procsend worked, the child could send a * message with it to end the waiting earlier, but this isn't * possible in GNO 2.0.6 because procsend is broken. This isn't * too big an issue, since 100ms isn't very long to wait anyhow. */ procrecvtim(1); /* Check if the child is really dead or forked by inspecting * the kernel's process entry for it. */ kvm_context = kvm_open(); if (kvm_context == NULL) break; proc_entry = kvmgetproc(kvm_context, pid); if (proc_entry == NULL || (proc_entry->args != NULL && strcmp(forked_child_name, proc_entry->args + 8) != 0)) done = 1; kvm_close(kvm_context); } ret: sigsetmask(oldmask); environPop(); return pid; }
int main (int argc, char **argv) { int i=1; /* the number of the test */ unsigned int result; if (argc > 1) { use_environ = 1; } else { use_environ = 0; printf("NOT "); } printf("using environ global variable\n"); /* * initial Test 1:1 */ result = test_environ(); if (result != NONE_SET) { printf("Please unset the variable \"%s\" for this test.\n", string1); return -1; } printf("Test %d passed.\n",i); i++; #define FAIL(fmt) { printf(fmt, i, string1); exit(1); } /* * environInit(); Test :2 */ if (use_environ) { environInit(); result = test_environ(); switch (result) { case BOTH_SET: FAIL("Test %d failed. %s prematurely set\n"); case ORCA_SET: FAIL("Test %d failed. %s set internally\n"); case UNIX_SET: FAIL("Test %d failed. %s set in environ\n"); case NONE_SET: printf("Test %d passed.\n",i); i++; break; default: printf("Test internal error: test %d returned %ud\n", i, result); return -1; } } /* * setenv(); Test 2:3 */ if (setenv(string1,string2,1) != 0) { printf("Test %d: setenv() failed\n",i); return -1; } result = test_environ(); if (use_environ) { switch (result) { case BOTH_SET: printf("Test %d passed.\n",i); i++; break; case ORCA_SET: FAIL("Test %d failed. %s not set in environ\n"); case UNIX_SET: FAIL("Test %d failed. %s not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: printf("Test internal error: test %d returned %ud\n", i, result); return -1; } } else { switch (result) { case BOTH_SET: FAIL("Test %d failed. %s set in environ\n"); case ORCA_SET: printf("Test %d passed.\n", i); i++; break; case UNIX_SET: FAIL("Test %d failed. %s set in environ, not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: printf("Test internal error: test %d returned %ud\n", i, result); return -1; } } /* * unsetenv() Test 3:4 */ unsetenv(string1); result = test_environ(); if (use_environ) { switch (result) { case BOTH_SET: FAIL("Test %d failed. %s set\n"); case ORCA_SET: FAIL("Test %d failed. %s set internally\n"); case UNIX_SET: FAIL("Test %d failed. %s set in in environ\n"); case NONE_SET: printf("Test %d passed.\n",i); i++; break; default: printf("Test internal error: test %d returned %ud\n", i, result); return -1; } } else { switch (result) { case BOTH_SET: FAIL("Test %d failed. %s set\n"); case ORCA_SET: FAIL("Test %d failed. %s set internally\n"); case UNIX_SET: FAIL("Test %d failed. %s set in environ\n"); case NONE_SET: printf("Test %d passed.\n",i); i++; break; default: printf("Test internal error: test %d returned %ud\n", i, result); return -1; } } /* * environPush() Test 4:5 bork */ if (setenv(string1,string2,1) != 0) { printf("Test %d: setenv() failed\n",i); return -1; } if (environPush() != 0) { printf("Test %d: environPush() failed\n",i); return -1; } result = test_environ(); if (use_environ) { switch (result) { case BOTH_SET: printf("Test %d passed.\n",i); i++; break; case ORCA_SET: FAIL("Test %d failed. %s not set in environ\n"); case UNIX_SET: FAIL("Test %d failed. %s not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: assert(0); } } else { switch (result) { case BOTH_SET: FAIL("Test %d failed. %s set in environ\n"); case ORCA_SET: printf("Test %d passed.\n",i); i++; break; case UNIX_SET: FAIL("Test %d failed. %s set in environ, not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: assert(0); } } /* * environPop() Test 5:6 */ unsetenv(string1); environPop(); result = test_environ(); if (use_environ) { switch (result) { case BOTH_SET: printf("Test %d passed.\n",i); i++; break; case ORCA_SET: FAIL("Test %d failed. %s not set in environ\n"); case UNIX_SET: FAIL("Test %d failed. %s not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: assert(0); } } else { switch (result) { case BOTH_SET: FAIL("Test %d failed. %s set in environ\n"); case ORCA_SET: printf("Test %d passed.\n",i); i++; break; case UNIX_SET: FAIL("Test %d failed. %s set in environ, not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: assert(0); } } /* * putenv() Test 6:7 */ unsetenv(string1); if (putenv(string3) != 0) { printf("Test %d: putenv() failed\n",i); return -1; } result = test_environ(); if (use_environ) { switch (result) { case BOTH_SET: printf("Test %d passed.\n",i); i++; break; case ORCA_SET: FAIL("Test %d failed. %s not set in environ\n"); case UNIX_SET: FAIL("Test %d failed. %s not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: assert(0); } } else { switch (result) { case BOTH_SET: FAIL("Test %d failed. %s set in environ\n"); case ORCA_SET: printf("Test %d passed.\n",i); i++; break; case UNIX_SET: FAIL("Test %d failed. %s set in environ, not set internally\n"); case NONE_SET: FAIL("Test %d failed. %s not set\n"); default: assert(0); } } unsetenv(string1); printf("Tests done.\n"); return 0; }