1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2016 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "shrpx_exec.h"
26
27 #include <cerrno>
28
29 #include "shrpx_signal.h"
30 #include "shrpx_log.h"
31 #include "util.h"
32 #include "template.h"
33
34 using namespace nghttp2;
35
36 namespace shrpx {
37
38 // inspired by h2o_read_command function from h2o project:
39 // https://github.com/h2o/h2o
exec_read_command(Process & proc,char * const argv[])40 int exec_read_command(Process &proc, char *const argv[]) {
41 int rv;
42 int pfd[2];
43
44 #ifdef O_CLOEXEC
45 if (pipe2(pfd, O_CLOEXEC) == -1) {
46 return -1;
47 }
48 #else // !O_CLOEXEC
49 if (pipe(pfd) == -1) {
50 return -1;
51 }
52 util::make_socket_closeonexec(pfd[0]);
53 util::make_socket_closeonexec(pfd[1]);
54 #endif // !O_CLOEXEC
55
56 auto closer = defer([&pfd]() {
57 if (pfd[0] != -1) {
58 close(pfd[0]);
59 }
60
61 if (pfd[1] != -1) {
62 close(pfd[1]);
63 }
64 });
65
66 sigset_t oldset;
67
68 rv = shrpx_signal_block_all(&oldset);
69 if (rv != 0) {
70 auto error = errno;
71 LOG(ERROR) << "Blocking all signals failed: errno=" << error;
72
73 return -1;
74 }
75
76 auto pid = fork();
77
78 if (pid == 0) {
79 // This is multithreaded program, and we are allowed to use only
80 // async-signal-safe functions here.
81
82 // child process
83 shrpx_signal_unset_worker_proc_ign_handler();
84
85 rv = shrpx_signal_unblock_all();
86 if (rv != 0) {
87 static constexpr char msg[] = "Unblocking all signals failed\n";
88 while (write(STDERR_FILENO, msg, str_size(msg)) == -1 && errno == EINTR)
89 ;
90 nghttp2_Exit(EXIT_FAILURE);
91 }
92
93 dup2(pfd[1], 1);
94 close(pfd[0]);
95
96 rv = execv(argv[0], argv);
97 if (rv == -1) {
98 static constexpr char msg[] = "Could not execute command\n";
99 while (write(STDERR_FILENO, msg, str_size(msg)) == -1 && errno == EINTR)
100 ;
101 nghttp2_Exit(EXIT_FAILURE);
102 }
103 // unreachable
104 }
105
106 // parent process
107 if (pid == -1) {
108 auto error = errno;
109 LOG(ERROR) << "Could not execute command: " << argv[0]
110 << ", fork() failed, errno=" << error;
111 }
112
113 rv = shrpx_signal_set(&oldset);
114 if (rv != 0) {
115 auto error = errno;
116 LOG(FATAL) << "Restoring all signals failed: errno=" << error;
117
118 nghttp2_Exit(EXIT_FAILURE);
119 }
120
121 if (pid == -1) {
122 return -1;
123 }
124
125 close(pfd[1]);
126 pfd[1] = -1;
127
128 util::make_socket_nonblocking(pfd[0]);
129
130 proc.pid = pid;
131 proc.rfd = pfd[0];
132
133 pfd[0] = -1;
134
135 return 0;
136 }
137
138 } // namespace shrpx
139