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