WvStreams
wvfork.cc
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 }