WvStreams
wvfork.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  *
5  * wvfork() just runs fork(), but it closes all file descriptors that
6  * are flagged close-on-exec, since we don't necessarily always run
7  * exec() after we fork()...
8  *
9  * This fixes the year-old mystery bug where WvTapeBackup caused
10  * watchdog reboots because the CHILD process wasn't touching it, and
11  * it was already open before the fork()...
12  *
13  * If you want to explicitly leave a file descriptor OPEN, even if
14  * it's marked close-on-exec, then add the fd number to dontclose, and
15  * pass that to wvfork(). This is mainly useful for WvLoopbacks --
16  * you may want certain ones open or closed depending on which call to
17  * wvfork() you're making. (for WvTapeBackup, you want the three
18  * backup loopbacks open, and, say, any WvResolver loopbacks closed.)
19  */
20 #include <fcntl.h>
21 
22 #include "wvfork.h"
23 #include "wvlinklist.h"
24 
25 #define MAX_FD sysconf(_SC_OPEN_MAX) + 1
26 
27 DeclareWvList(WvForkCallback);
28 static WvForkCallbackList *callbacks;
29 
31 {
32 public:
34  { if (callbacks) delete callbacks; }
35 };
36 
37 static StupidWvForkDeallocator sfd;
38 
39 
40 // note: this shouldn't really be needed (it would be better to use a simple
41 // static list), but might be needed if your dynamic linker (ld.so) runs
42 // global constructors in the wrong order.
43 static WvForkCallbackList &get_callbacks()
44 {
45  if (!callbacks)
46  callbacks = new WvForkCallbackList;
47  return *callbacks;
48 }
49 
50 
51 void add_wvfork_callback(WvForkCallback cb)
52 {
53 #if 0
54  // be sure we don't add this twice
55  WvForkCallbackList::Iter i(get_callbacks());
56  for (i.rewind(); i.next(); )
57  if (*i == cb) return;
58 #endif
59  get_callbacks().append(new WvForkCallback(cb), true);
60 }
61 
62 #if 0
63 void remove_wvfork_callback(WvForkCallback cb)
64 {
65  WvForkCallbackList::Iter i(get_callbacks());
66  for (i.rewind(); i.next(); )
67  if (*i == cb) i.xunlink();
68 }
69 #endif
70 
71 pid_t wvfork(int dontclose1, int dontclose2)
72 {
73  intTable t(1);
74  if (dontclose1 >= 0)
75  t.add(&dontclose1, false);
76  if (dontclose2 >= 0)
77  t.add(&dontclose2, false);
78  return (wvfork(t));
79 }
80 
81 pid_t wvfork_start(int *waitfd)
82 {
83  int waitpipe[2];
84 
85  if (pipe(waitpipe) < 0)
86  return -1;
87 
88  pid_t pid = fork();
89 
90  WvForkCallbackList::Iter i(get_callbacks());
91  for (i.rewind(); i.next(); )
92  {
93  WvForkCallback *cb = i.ptr();
94  (*cb)(pid);
95  }
96 
97  if (pid < 0)
98  return pid;
99  else if (pid > 0)
100  {
101  // parent process. close its writing end of the pipe and wait
102  // for its reading end to close.
103  char buf;
104  close(waitpipe[1]);
105  read(waitpipe[0], &buf, 1);
106  close(waitpipe[0]);
107  }
108  else
109  {
110  // child process. close its reading end of the pipe.
111  close(waitpipe[0]);
112  *waitfd = waitpipe[1];
113  }
114 
115  return pid;
116 }
117 
118 pid_t wvfork(intTable &dontclose)
119 {
120  int waitfd = -1;
121  pid_t pid = wvfork_start(&waitfd);
122 
123  if (pid != 0)
124  {
125  // parent or error
126  return pid;
127  }
128 
129  // child process
130  // check the close-on-exec flag of all file descriptors
131  for (int fd = 0; fd < MAX_FD; fd++)
132  {
133  if (!dontclose[fd] && fd != waitfd &&
134  (fcntl(fd, F_GETFD) & FD_CLOEXEC) > 0)
135  close(fd);
136  }
137 
138  close(waitfd);
139 
140  return pid;
141 }
add_wvfork_callback
void add_wvfork_callback(WvForkCallback cb)
Register a callback to be called during wvfork.
Definition: wvfork.cc:51
wvfork
pid_t wvfork(int dontclose1=-1, int dontclose2=-1)
wvfork() just runs fork(), but it closes all file descriptors that are flagged close-on-exec,...
Definition: wvfork.cc:71
StupidWvForkDeallocator
Definition: wvfork.cc:30
wvfork.h
wvfork_start
pid_t wvfork_start(int *waitfd)
wvfork_start is just like fork, except that it will block the parent until the child process closes t...
Definition: wvfork.cc:81