void openSeekRead(char *filename, bits64 offset, bits64 len, char *buf) /* Read len bits starting at offset from filename into buf or die. */ { int fd = mustOpenFd(filename, O_RDONLY); mustLseek(fd, offset, SEEK_SET); mustReadFd(fd, buf, len); mustCloseFd(&fd); }
// TODO move this to a generic re-usable location void startBackgroundWork(char *exec, char **pWorkUrl) /* deal with forking off child for background work * and setting up the trash file for communicating * from the child to the browser */ { char *workUrl = NULL; char hgsid[64]; struct tempName tn; safef(hgsid, sizeof(hgsid), "%s", cartSessionId(cart)); trashDirFile(&tn, "backGround", hgsid, ".tmp"); workUrl = cloneString(tn.forCgi); fflush(stdout); fflush(stderr); // seems that we need to use the double-fork trick // to create enough separation between the non-waiting parent // and the grand-child process. otherwise the OS and Apache are waiting on the child. int pid = fork(); if (pid == -1) { errAbort("can't fork, error %d", errno); } if (pid == 0) // child { int pid2 = fork(); if (pid2 == -1) { errAbort("can't fork, error %d", errno); } if (pid2 == 0) // grand child { // we need to close or redup to open stdout, stderr, stdin // in order for apache to break ties with it. // Will the grandchild cgi still be able to function? // redirect stdout of child to the trash file for easier use of // library functions that output html to stdout. int out = mustOpenFd(tn.forCgi, O_WRONLY | O_CREAT); fflush(stdout); dup2(out,STDOUT_FILENO); /* closes STDOUT before setting it back to saved descriptor */ close(out); // Unfortunately we must create our own stderr log file char errName[1024]; safef(errName, sizeof errName, "%s.err", tn.forCgi); int err = mustOpenFd(errName, O_CREAT | O_WRONLY | O_APPEND); dup2(err, STDERR_FILENO); close(err); // stdin input is just empty int in = mustOpenFd("/dev/null", O_RDONLY); dup2(in, STDIN_FILENO); close(in); // execute so that we will be able to use database and other operations normally. char execPath[4096]; safef(execPath, sizeof execPath, "%s hgsid=%s", exec, hgsid); char *args[10]; int numArgs = chopString(execPath, " ", args, 10); args[numArgs] = NULL; // by creating a minimal environment and not inheriting from the parent, // it cause cgiSpoof to run, picking up command-line params as cgi vars. char *newenviron[] = { "HGDB_CONF=hg.conf", NULL }; int sleepSeconds = 1; // was 5 sleep(sleepSeconds); // Give the foreground process time to write the cart. execve(args[0], args+1, newenviron); // SHOULD NOT GET HERE UNLESS EXEC FAILED. verbose(1,"execve failed for %s\n", exec); _exit(0); // exit without the usual cleanup which messes up parent's db connections etc. } else // child { _exit(0); // exit without the usual cleanup which messes up parent's db connections etc. } } else // parent { *pWorkUrl = workUrl; // wait for the exiting child (not grandchild) int w, status; do { w = waitpid(pid, &status, WUNTRACED | WCONTINUED); if (w == -1) { perror("waitpid"); exit(EXIT_FAILURE); } if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) verbose(1, "exited, status=%d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { verbose(1, "killed by signal %d\n", WTERMSIG(status)); } else if (WIFSTOPPED(status)) { verbose(1, "stopped by signal %d\n", WSTOPSIG(status)); } else if (WIFCONTINUED(status)) { verbose(1, "continued\n"); } } while (!WIFEXITED(status) && !WIFSIGNALED(status)); // done waiting for child. } }