WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * wvfork() just runs fork(), but it closes all file descriptors that 00006 * are flagged close-on-exec, since we don't necessarily always run 00007 * exec() after we fork()... 00008 * 00009 * This fixes the year-old mystery bug where WvTapeBackup caused 00010 * watchdog reboots because the CHILD process wasn't touching it, and 00011 * it was already open before the fork()... 00012 * 00013 * If you want to explicitly leave a file descriptor OPEN, even if 00014 * it's marked close-on-exec, then add the fd number to dontclose, and 00015 * pass that to wvfork(). This is mainly useful for WvLoopbacks -- 00016 * you may want certain ones open or closed depending on which call to 00017 * wvfork() you're making. (for WvTapeBackup, you want the three 00018 * backup loopbacks open, and, say, any WvResolver loopbacks closed.) 00019 */ 00020 #include <fcntl.h> 00021 00022 #include "wvfork.h" 00023 #include "wvlinklist.h" 00024 00025 #define MAX_FD sysconf(_SC_OPEN_MAX) + 1 00026 00027 DeclareWvList(WvForkCallback); 00028 static WvForkCallbackList *callbacks; 00029 00030 class StupidWvForkDeallocator 00031 { 00032 public: 00033 ~StupidWvForkDeallocator() 00034 { if (callbacks) delete callbacks; } 00035 }; 00036 00037 static StupidWvForkDeallocator sfd; 00038 00039 00040 // note: this shouldn't really be needed (it would be better to use a simple 00041 // static list), but might be needed if your dynamic linker (ld.so) runs 00042 // global constructors in the wrong order. 00043 static WvForkCallbackList &get_callbacks() 00044 { 00045 if (!callbacks) 00046 callbacks = new WvForkCallbackList; 00047 return *callbacks; 00048 } 00049 00050 00051 void add_wvfork_callback(WvForkCallback cb) 00052 { 00053 #if 0 00054 // be sure we don't add this twice 00055 WvForkCallbackList::Iter i(get_callbacks()); 00056 for (i.rewind(); i.next(); ) 00057 if (*i == cb) return; 00058 #endif 00059 get_callbacks().append(new WvForkCallback(cb), true); 00060 } 00061 00062 #if 0 00063 void remove_wvfork_callback(WvForkCallback cb) 00064 { 00065 WvForkCallbackList::Iter i(get_callbacks()); 00066 for (i.rewind(); i.next(); ) 00067 if (*i == cb) i.xunlink(); 00068 } 00069 #endif 00070 00071 pid_t wvfork(int dontclose1, int dontclose2) 00072 { 00073 intTable t(1); 00074 if (dontclose1 >= 0) 00075 t.add(&dontclose1, false); 00076 if (dontclose2 >= 0) 00077 t.add(&dontclose2, false); 00078 return (wvfork(t)); 00079 } 00080 00081 pid_t wvfork_start(int *waitfd) 00082 { 00083 int waitpipe[2]; 00084 00085 if (pipe(waitpipe) < 0) 00086 return -1; 00087 00088 pid_t pid = fork(); 00089 00090 WvForkCallbackList::Iter i(get_callbacks()); 00091 for (i.rewind(); i.next(); ) 00092 { 00093 WvForkCallback *cb = i.ptr(); 00094 (*cb)(pid); 00095 } 00096 00097 if (pid < 0) 00098 return pid; 00099 else if (pid > 0) 00100 { 00101 // parent process. close its writing end of the pipe and wait 00102 // for its reading end to close. 00103 char buf; 00104 close(waitpipe[1]); 00105 read(waitpipe[0], &buf, 1); 00106 close(waitpipe[0]); 00107 } 00108 else 00109 { 00110 // child process. close its reading end of the pipe. 00111 close(waitpipe[0]); 00112 *waitfd = waitpipe[1]; 00113 } 00114 00115 return pid; 00116 } 00117 00118 pid_t wvfork(intTable &dontclose) 00119 { 00120 int waitfd = -1; 00121 pid_t pid = wvfork_start(&waitfd); 00122 00123 if (pid != 0) 00124 { 00125 // parent or error 00126 return pid; 00127 } 00128 00129 // child process 00130 // check the close-on-exec flag of all file descriptors 00131 for (int fd = 0; fd < MAX_FD; fd++) 00132 { 00133 if (!dontclose[fd] && fd != waitfd && 00134 (fcntl(fd, F_GETFD) & FD_CLOEXEC) > 0) 00135 close(fd); 00136 } 00137 00138 close(waitfd); 00139 00140 return pid; 00141 }