File: wvfork.cc

package info (click to toggle)
wvstreams 4.0.2-4
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 6,420 kB
  • ctags: 6,518
  • sloc: cpp: 52,544; sh: 5,770; ansic: 810; makefile: 461; tcl: 114; perl: 18
file content (141 lines) | stat: -rw-r--r-- 3,273 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
 * Worldvisions Weaver Software:
 *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
 *
 * wvfork() just runs fork(), but it closes all file descriptors that
 * are flagged close-on-exec, since we don't necessarily always run
 * exec() after we fork()...
 *
 * This fixes the year-old mystery bug where WvTapeBackup caused
 * watchdog reboots because the CHILD process wasn't touching it, and
 * it was already open before the fork()...
 *
 * If you want to explicitly leave a file descriptor OPEN, even if
 * it's marked close-on-exec, then add the fd number to dontclose, and
 * pass that to wvfork().  This is mainly useful for WvLoopbacks --
 * you may want certain ones open or closed depending on which call to
 * wvfork() you're making.  (for WvTapeBackup, you want the three
 * backup loopbacks open, and, say, any WvResolver loopbacks closed.)
 */
#include <fcntl.h>

#include "wvfork.h"
#include "wvlinklist.h"

#define MAX_FD sysconf(_SC_OPEN_MAX) + 1

DeclareWvList(WvForkCallback);
static WvForkCallbackList *callbacks;

class StupidWvForkDeallocator
{
public:
    ~StupidWvForkDeallocator()
        { if (callbacks) delete callbacks; }
};

static StupidWvForkDeallocator sfd;


// note: this shouldn't really be needed (it would be better to use a simple
// static list), but might be needed if your dynamic linker (ld.so) runs
// global constructors in the wrong order.
static WvForkCallbackList &get_callbacks()
{
    if (!callbacks)
	callbacks = new WvForkCallbackList;
    return *callbacks;		
}


void add_wvfork_callback(WvForkCallback cb)
{
#if 0
    // be sure we don't add this twice
    WvForkCallbackList::Iter i(get_callbacks());
    for (i.rewind(); i.next(); )
        if (*i == cb) return;
#endif
    get_callbacks().append(new WvForkCallback(cb), true);
}

#if 0
void remove_wvfork_callback(WvForkCallback cb)
{
    WvForkCallbackList::Iter i(get_callbacks());
    for (i.rewind(); i.next(); )
        if (*i == cb) i.xunlink();
}
#endif

pid_t wvfork(int dontclose1, int dontclose2)
{
    intTable t(1);
    if (dontclose1 >= 0)
	t.add(&dontclose1, false);
    if (dontclose2 >= 0)
	t.add(&dontclose2, false);
    return (wvfork(t));
}

pid_t wvfork_start(int *waitfd)
{
    int waitpipe[2];

    if (pipe(waitpipe) < 0)
	return -1;

    pid_t pid = fork();

    WvForkCallbackList::Iter i(get_callbacks());
    for (i.rewind(); i.next(); )
    {
        WvForkCallback *cb = i.ptr();
        (*cb)(pid);
    }

    if (pid < 0)
	return pid;
    else if (pid > 0)
    {
	// parent process. close its writing end of the pipe and wait
	// for its reading end to close.
	char buf;
	close(waitpipe[1]);
	read(waitpipe[0], &buf, 1);
	close(waitpipe[0]);
    }
    else
    {
	// child process. close its reading end of the pipe.
	close(waitpipe[0]);
	*waitfd = waitpipe[1];
    }

    return pid;
}

pid_t wvfork(intTable &dontclose)
{
    int waitfd = -1;
    pid_t pid = wvfork_start(&waitfd);

    if (pid != 0)
    {
	// parent or error
	return pid;
    }

    // child process
    // check the close-on-exec flag of all file descriptors
    for (int fd = 0; fd < MAX_FD; fd++)
    {
	if (!dontclose[fd] && fd != waitfd &&
	    (fcntl(fd, F_GETFD) & FD_CLOEXEC) > 0)
	    close(fd);
    }

    close(waitfd);

    return pid;
}