File: EventsSourceSpawner.cpp

package info (click to toggle)
drbd-utils 9.22.0-1.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,768 kB
  • sloc: ansic: 48,975; xml: 11,553; cpp: 9,843; sh: 4,568; makefile: 1,029; perl: 353; ruby: 43
file content (197 lines) | stat: -rw-r--r-- 5,324 bytes parent folder | download | duplicates (4)
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include <EventsSourceSpawner.h>
#include <memory>
#include <cstring>
#include <utils.h>

extern "C"
{
    #include <unistd.h>
    #include <signal.h>
    #include <spawn.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <errno.h>
}

const char* EventsSourceSpawner::EVENTS_PROGRAM = "drbdsetup";
const char* EventsSourceSpawner::EVENTS_PROGRAM_ARGS[] =
{
    "drbdsetup",
    "events2",
    "all",
    nullptr
};

EventsSourceSpawner::EventsSourceSpawner(MessageLog& logRef):
    log(logRef)
{
}

EventsSourceSpawner::~EventsSourceSpawner()
{
    try
    {
        cleanup_child_processes_impl();
    }
    catch (EventsSourceException&)
    {
        // Triggered by cleanup_child_processes_impl() if the events source
        // process tracked by this instance exited
    }

    terminate_child_process();

    int io_flags = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, io_flags & ~O_NONBLOCK);
}

pid_t EventsSourceSpawner::get_process_id()
{
    return spawned_pid;
}

int EventsSourceSpawner::get_events_out_fd()
{
    return out_pipe_fd[posix::PIPE_READ_SIDE];
}

int EventsSourceSpawner::get_events_err_fd()
{
    return err_pipe_fd[posix::PIPE_READ_SIDE];
}

// @throws std::bad_alloc, EventSourceException
void EventsSourceSpawner::spawn_source()
{
    // Initialize the pipes
    posix::Pipe out_pipe_mgr(&out_pipe_fd);
    posix::Pipe err_pipe_mgr(&err_pipe_fd);

    // Make the pipes' read end nonblocking
    set_nonblocking(out_pipe_fd[posix::PIPE_READ_SIDE]);
    set_nonblocking(err_pipe_fd[posix::PIPE_READ_SIDE]);

    // Make stdin nonblocking
    set_nonblocking(STDIN_FILENO);

    // Initialize the datastructures for posix_spawn())
    posix::SpawnFileActions pipe_init;
    posix::SpawnAttr spawn_attr;
    posix::SpawnArgs spawn_args(EVENTS_PROGRAM_ARGS);

    // Redirect stdout to the pipes write side
    checked_int_rc(
        posix_spawn_file_actions_adddup2(
            pipe_init.actions,
            out_pipe_fd[posix::PIPE_WRITE_SIDE],
            STDOUT_FILENO
        )
    );
    checked_int_rc(
        posix_spawn_file_actions_adddup2(
            pipe_init.actions,
            err_pipe_fd[posix::PIPE_WRITE_SIDE],
            STDERR_FILENO
        )
    );
    // Close the read end of the pipes
    checked_int_rc(
        posix_spawn_file_actions_addclose(
            pipe_init.actions,
            out_pipe_fd[posix::PIPE_READ_SIDE]
        )
    );
    checked_int_rc(
        posix_spawn_file_actions_addclose(
            pipe_init.actions,
            err_pipe_fd[posix::PIPE_READ_SIDE]
        )
    );

    // Reset ignored signals to the default action
    sigset_t mask;
    checked_int_rc(sigemptyset(&mask));
    checked_int_rc(sigaddset(&mask, SIGTERM));
    checked_int_rc(sigaddset(&mask, SIGHUP));
    checked_int_rc(sigaddset(&mask, SIGINT));
    checked_int_rc(sigaddset(&mask, SIGWINCH));
    checked_int_rc(sigaddset(&mask, SIGCHLD));
    checked_int_rc(posix_spawnattr_setsigdefault(spawn_attr.attr, &mask));
    checked_int_rc(posix_spawnattr_setflags(spawn_attr.attr, POSIX_SPAWN_SETSIGDEF));

    // Attempt to spawn the events source program
    int spawn_rc = posix_spawnp(&spawned_pid, EVENTS_PROGRAM, pipe_init.actions, spawn_attr.attr,
                                spawn_args.args, environ);
    if (spawn_rc != 0)
    {
        spawned_pid = -1;
        log.add_entry(
            MessageLog::log_level::ALERT,
            "Spawning the events source process failed"
        );
        throw EventsSourceException();
    }

    // Close the local write side of the pipes
    out_pipe_mgr.close_write_side();
    err_pipe_mgr.close_write_side();

    // Keep the pipes open when leaving scope
    out_pipe_mgr.release();
    err_pipe_mgr.release();
}

// @throws EventsSourceException
void EventsSourceSpawner::cleanup_child_processes()
{
    cleanup_child_processes_impl();
}

// @throws EventsSourceException
void EventsSourceSpawner::cleanup_child_processes_impl()
{
    // Cleanup any child processes that have exited (zombie processes))
    pid_t child_pid {0};
    do
    {
        int exit_status;
        child_pid = waitpid(-1, &exit_status, WNOHANG);
        if (spawned_pid != -1 && child_pid == spawned_pid &&
            (WIFEXITED(exit_status) || WIFSIGNALED(exit_status)))
        {
            // Events source process exited, throw an EventsSourceException
            // to trigger reinitialization of the application and
            // thereby a respawn of the events source process
            spawned_pid = -1;
            throw EventsSourceException();
        }
    }
    while (child_pid > 0);
}

void EventsSourceSpawner::terminate_child_process() noexcept
{
    // Unless the child process exited already or was not ever spawned,
    // tell the child process to terminate
    if (spawned_pid != -1)
    {
        static_cast<void> (kill(spawned_pid, SIGTERM));
    }
}

// Sets O_NONBLOCK on the specified filedescriptor
void EventsSourceSpawner::set_nonblocking(int fd)
{
    int io_flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, io_flags | O_NONBLOCK);
}

// Throws EventsSourceException if rc is not equal to 0
// @throws EventsSourceException
void EventsSourceSpawner::checked_int_rc(int rc) const
{
    if (rc != 0)
    {
        throw EventsSourceException();
    }
}