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
27DeclareWvList(WvForkCallback);
28static WvForkCallbackList *callbacks;
29
31{
32public:
34 { if (callbacks) delete callbacks; }
35};
36
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.
43static WvForkCallbackList &get_callbacks()
44{
45 if (!callbacks)
46 callbacks = new WvForkCallbackList;
47 return *callbacks;
48}
49
50
51void 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
63void 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
71pid_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
81pid_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
118pid_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}
Provides support for forking processes.
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
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
void add_wvfork_callback(WvForkCallback cb)
Register a callback to be called during wvfork.
Definition wvfork.cc:51