/src/h2o/lib/common/serverutil.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Nick Desaulniers |
3 | | * |
4 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | | * of this software and associated documentation files (the "Software"), to |
6 | | * deal in the Software without restriction, including without limitation the |
7 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
8 | | * sell copies of the Software, and to permit persons to whom the Software is |
9 | | * furnished to do so, subject to the following conditions: |
10 | | * |
11 | | * The above copyright notice and this permission notice shall be included in |
12 | | * all copies or substantial portions of the Software. |
13 | | * |
14 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
19 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
20 | | * IN THE SOFTWARE. |
21 | | */ |
22 | | #include <errno.h> |
23 | | #include <fcntl.h> |
24 | | #include <grp.h> |
25 | | #include <pthread.h> |
26 | | #include <pwd.h> |
27 | | #include <signal.h> |
28 | | #ifndef __linux__ |
29 | | #include <spawn.h> |
30 | | #endif |
31 | | #include <stdint.h> |
32 | | #include <stdlib.h> |
33 | | #include <string.h> |
34 | | #include <sys/types.h> |
35 | | #include <sys/wait.h> |
36 | | #include <unistd.h> |
37 | | #if !defined(_SC_NPROCESSORS_ONLN) |
38 | | #include <sys/sysctl.h> |
39 | | #endif |
40 | | #include "cloexec.h" |
41 | | #include "h2o/memory.h" |
42 | | #include "h2o/serverutil.h" |
43 | | #include "h2o/socket.h" |
44 | | #include "h2o/string_.h" |
45 | | |
46 | | void h2o_set_signal_handler(int signo, void (*cb)(int signo)) |
47 | 0 | { |
48 | 0 | struct sigaction action; |
49 | |
|
50 | 0 | memset(&action, 0, sizeof(action)); |
51 | 0 | sigemptyset(&action.sa_mask); |
52 | 0 | action.sa_handler = cb; |
53 | 0 | sigaction(signo, &action, NULL); |
54 | 0 | } |
55 | | |
56 | | int h2o_setuidgid(const char *user) |
57 | 0 | { |
58 | 0 | struct passwd pwbuf, *pw; |
59 | 0 | char buf[65536]; /* should be large enough */ |
60 | |
|
61 | 0 | errno = 0; |
62 | 0 | if (getpwnam_r(user, &pwbuf, buf, sizeof(buf), &pw) != 0) { |
63 | 0 | h2o_perror("getpwnam_r"); |
64 | 0 | return -1; |
65 | 0 | } |
66 | 0 | if (pw == NULL) { |
67 | 0 | h2o_error_printf("unknown user:%s\n", user); |
68 | 0 | return -1; |
69 | 0 | } |
70 | 0 | if (setgid(pw->pw_gid) != 0) { |
71 | 0 | h2o_error_printf("setgid(%d) failed:%s\n", (int)pw->pw_gid, strerror(errno)); |
72 | 0 | return -1; |
73 | 0 | } |
74 | 0 | if (initgroups(pw->pw_name, pw->pw_gid) != 0) { |
75 | 0 | h2o_error_printf("initgroups(%s, %d) failed:%s\n", pw->pw_name, (int)pw->pw_gid, strerror(errno)); |
76 | 0 | return -1; |
77 | 0 | } |
78 | 0 | if (setuid(pw->pw_uid) != 0) { |
79 | 0 | h2o_error_printf("setuid(%d) failed:%s\n", (int)pw->pw_uid, strerror(errno)); |
80 | 0 | return -1; |
81 | 0 | } |
82 | | |
83 | 0 | return 0; |
84 | 0 | } |
85 | | |
86 | | size_t h2o_server_starter_get_fds(int **_fds) |
87 | 0 | { |
88 | 0 | const char *ports_env, *start, *end, *eq; |
89 | 0 | size_t t; |
90 | 0 | H2O_VECTOR(int) fds = {NULL}; |
91 | |
|
92 | 0 | if ((ports_env = getenv(SERVER_STARTER_PORT)) == NULL) |
93 | 0 | return 0; |
94 | 0 | if (ports_env[0] == '\0') { |
95 | 0 | h2o_error_printf("$" SERVER_STARTER_PORT " is empty\n"); |
96 | 0 | return SIZE_MAX; |
97 | 0 | } |
98 | | |
99 | | /* ports_env example: 127.0.0.1:80=3;/tmp/sock=4 */ |
100 | 0 | for (start = ports_env; *start != '\0'; start = *end == ';' ? end + 1 : end) { |
101 | 0 | if ((end = strchr(start, ';')) == NULL) |
102 | 0 | end = start + strlen(start); |
103 | 0 | if ((eq = memchr(start, '=', end - start)) == NULL) { |
104 | 0 | h2o_error_printf("invalid $" SERVER_STARTER_PORT ", an element without `=` in: %s\n", ports_env); |
105 | 0 | goto Error; |
106 | 0 | } |
107 | 0 | if ((t = h2o_strtosize(eq + 1, end - eq - 1)) == SIZE_MAX) { |
108 | 0 | h2o_error_printf("invalid file descriptor number in $" SERVER_STARTER_PORT ": %s\n", ports_env); |
109 | 0 | goto Error; |
110 | 0 | } |
111 | 0 | h2o_vector_reserve(NULL, &fds, fds.size + 1); |
112 | 0 | fds.entries[fds.size++] = (int)t; |
113 | 0 | } |
114 | | |
115 | 0 | *_fds = fds.entries; |
116 | 0 | return fds.size; |
117 | 0 | Error: |
118 | 0 | free(fds.entries); |
119 | 0 | return SIZE_MAX; |
120 | 0 | } |
121 | | |
122 | | static char **build_spawn_env(void) |
123 | 0 | { |
124 | 0 | extern char **environ; |
125 | 0 | size_t num; |
126 | | |
127 | | /* calculate number of envvars, as well as looking for H2O_ROOT= */ |
128 | 0 | for (num = 0; environ[num] != NULL; ++num) |
129 | 0 | if (strncmp(environ[num], "H2O_ROOT=", sizeof("H2O_ROOT=") - 1) == 0) |
130 | 0 | return NULL; |
131 | | |
132 | | /* not found */ |
133 | 0 | char **newenv = h2o_mem_alloc(sizeof(*newenv) * (num + 2) + sizeof("H2O_ROOT=" H2O_TO_STR(H2O_ROOT))); |
134 | 0 | memcpy(newenv, environ, sizeof(*newenv) * num); |
135 | 0 | newenv[num] = (char *)(newenv + num + 2); |
136 | 0 | newenv[num + 1] = NULL; |
137 | 0 | strcpy(newenv[num], "H2O_ROOT=" H2O_TO_STR(H2O_ROOT)); |
138 | |
|
139 | 0 | return newenv; |
140 | 0 | } |
141 | | |
142 | | pid_t h2o_spawnp(const char *cmd, char *const *argv, const int *mapped_fds, int cloexec_mutex_is_locked) |
143 | 0 | { |
144 | 0 | #if defined(__linux__) |
145 | | #ifndef _GNU_SOURCE |
146 | | extern int pipe2(int pipefd[2], int flags); |
147 | | #endif |
148 | | |
149 | | /* Before glibc 2.24, posix_spawnp of Linux does not return error if the executable does not exist, see |
150 | | * https://gist.github.com/kazuho/0c233e6f86d27d6e4f09 |
151 | | */ |
152 | 0 | extern char **environ; |
153 | 0 | int pipefds[2] = {-1, -1}, errnum; |
154 | 0 | pid_t pid; |
155 | | |
156 | | /* create pipe, used for sending error codes */ |
157 | 0 | if (pipe2(pipefds, O_CLOEXEC) != 0) |
158 | 0 | goto Error; |
159 | | |
160 | | /* fork */ |
161 | 0 | if (!cloexec_mutex_is_locked) |
162 | 0 | pthread_mutex_lock(&cloexec_mutex); |
163 | 0 | if ((pid = fork()) == 0) { |
164 | | /* in child process, map the file descriptors and execute; return the errnum through pipe if exec failed */ |
165 | 0 | if (mapped_fds != NULL) { |
166 | 0 | for (; *mapped_fds != -1; mapped_fds += 2) { |
167 | 0 | if (mapped_fds[0] != mapped_fds[1]) { |
168 | 0 | if (mapped_fds[1] != -1) |
169 | 0 | dup2(mapped_fds[0], mapped_fds[1]); |
170 | 0 | close(mapped_fds[0]); |
171 | 0 | } |
172 | 0 | } |
173 | 0 | } |
174 | 0 | char **env = build_spawn_env(); |
175 | 0 | if (env != NULL) |
176 | 0 | environ = env; |
177 | 0 | execvp(cmd, argv); |
178 | 0 | errnum = errno; |
179 | 0 | write(pipefds[1], &errnum, sizeof(errnum)); |
180 | 0 | _exit(EX_SOFTWARE); |
181 | 0 | } |
182 | 0 | if (!cloexec_mutex_is_locked) |
183 | 0 | pthread_mutex_unlock(&cloexec_mutex); |
184 | 0 | if (pid == -1) |
185 | 0 | goto Error; |
186 | | |
187 | | /* parent process */ |
188 | 0 | close(pipefds[1]); |
189 | 0 | pipefds[1] = -1; |
190 | 0 | ssize_t rret; |
191 | 0 | errnum = 0; |
192 | 0 | while ((rret = read(pipefds[0], &errnum, sizeof(errnum))) == -1 && errno == EINTR) |
193 | 0 | ; |
194 | 0 | if (rret != 0) { |
195 | | /* spawn failed */ |
196 | 0 | while (waitpid(pid, NULL, 0) != pid) |
197 | 0 | ; |
198 | 0 | pid = -1; |
199 | 0 | errno = errnum; |
200 | 0 | goto Error; |
201 | 0 | } |
202 | | |
203 | | /* spawn succeeded */ |
204 | 0 | close(pipefds[0]); |
205 | 0 | return pid; |
206 | | |
207 | 0 | Error: |
208 | 0 | errnum = errno; |
209 | 0 | if (pipefds[0] != -1) |
210 | 0 | close(pipefds[0]); |
211 | 0 | if (pipefds[1] != -1) |
212 | 0 | close(pipefds[1]); |
213 | 0 | errno = errnum; |
214 | 0 | return -1; |
215 | |
|
216 | | #else |
217 | | |
218 | | posix_spawn_file_actions_t file_actions; |
219 | | pid_t pid; |
220 | | extern char **environ; |
221 | | char **env = build_spawn_env(); |
222 | | posix_spawn_file_actions_init(&file_actions); |
223 | | if (mapped_fds != NULL) { |
224 | | for (; *mapped_fds != -1; mapped_fds += 2) { |
225 | | if (mapped_fds[1] != -1) |
226 | | posix_spawn_file_actions_adddup2(&file_actions, mapped_fds[0], mapped_fds[1]); |
227 | | posix_spawn_file_actions_addclose(&file_actions, mapped_fds[0]); |
228 | | } |
229 | | } |
230 | | if (!cloexec_mutex_is_locked) |
231 | | pthread_mutex_lock(&cloexec_mutex); |
232 | | errno = posix_spawnp(&pid, cmd, &file_actions, NULL, argv, env != NULL ? env : environ); |
233 | | if (!cloexec_mutex_is_locked) |
234 | | pthread_mutex_unlock(&cloexec_mutex); |
235 | | free(env); |
236 | | posix_spawn_file_actions_destroy(&file_actions); |
237 | | if (errno != 0) |
238 | | return -1; |
239 | | |
240 | | return pid; |
241 | | |
242 | | #endif |
243 | 0 | } |
244 | | |
245 | | int h2o_read_command(const char *cmd, char **argv, h2o_iovec_t std_in, h2o_buffer_t **resp, int *child_status) |
246 | 0 | { |
247 | 0 | int respfds[2] = {-1, -1}, inputfds[2] = {-1, -1}; |
248 | 0 | pid_t pid = -1; |
249 | 0 | int mutex_locked = 0, ret = -1; |
250 | |
|
251 | 0 | h2o_buffer_init(resp, &h2o_socket_buffer_prototype); |
252 | |
|
253 | 0 | pthread_mutex_lock(&cloexec_mutex); |
254 | 0 | mutex_locked = 1; |
255 | | |
256 | | /* create pipes for reading the result and for supplying input */ |
257 | 0 | if (pipe(respfds) != 0) |
258 | 0 | goto Exit; |
259 | 0 | if (fcntl(respfds[0], F_SETFD, FD_CLOEXEC) < 0) |
260 | 0 | goto Exit; |
261 | 0 | if (pipe(inputfds) != 0) |
262 | 0 | goto Exit; |
263 | 0 | if (fcntl(inputfds[1], F_SETFD, FD_CLOEXEC) < 0) |
264 | 0 | goto Exit; |
265 | | |
266 | | /* spawn */ |
267 | 0 | int mapped_fds[] = {inputfds[0], 0, /* stdin of the child process is what is being provide as input */ |
268 | 0 | respfds[1], 1, /* stdout of the child process is read from the pipe */ |
269 | 0 | -1}; |
270 | 0 | if ((pid = h2o_spawnp(cmd, argv, mapped_fds, 1)) == -1) |
271 | 0 | goto Exit; |
272 | 0 | close(respfds[1]); |
273 | 0 | respfds[1] = -1; |
274 | 0 | close(inputfds[0]); |
275 | 0 | inputfds[0] = -1; |
276 | |
|
277 | 0 | pthread_mutex_unlock(&cloexec_mutex); |
278 | 0 | mutex_locked = 0; |
279 | | |
280 | | /* supply input */ |
281 | 0 | for (size_t off = 0; off < std_in.len;) { |
282 | 0 | ssize_t r; |
283 | 0 | while ((r = write(inputfds[1], std_in.base + off, std_in.len - off)) == -1 && errno == EINTR) |
284 | 0 | ; |
285 | 0 | if (r < 0) |
286 | 0 | break; |
287 | 0 | off += r; |
288 | 0 | } |
289 | 0 | close(inputfds[1]); |
290 | 0 | inputfds[1] = -1; |
291 | | |
292 | | /* read the response from pipe */ |
293 | 0 | while (1) { |
294 | 0 | h2o_iovec_t buf = h2o_buffer_reserve(resp, 8192); |
295 | 0 | ssize_t r; |
296 | 0 | while ((r = read(respfds[0], buf.base, buf.len)) == -1 && errno == EINTR) |
297 | 0 | ; |
298 | 0 | if (r <= 0) |
299 | 0 | break; |
300 | 0 | (*resp)->size += r; |
301 | 0 | } |
302 | |
|
303 | 0 | Exit: |
304 | 0 | if (mutex_locked) |
305 | 0 | pthread_mutex_unlock(&cloexec_mutex); |
306 | 0 | if (pid != -1) { |
307 | | /* wait for the child to complete */ |
308 | 0 | pid_t r; |
309 | 0 | while ((r = waitpid(pid, child_status, 0)) == -1 && errno == EINTR) |
310 | 0 | ; |
311 | 0 | if (r == pid) { |
312 | | /* success */ |
313 | 0 | ret = 0; |
314 | 0 | } |
315 | 0 | } |
316 | 0 | #define CLOSE_FD(x) \ |
317 | 0 | do { \ |
318 | 0 | if ((x) != -1) \ |
319 | 0 | close(x); \ |
320 | 0 | } while (0) |
321 | 0 | CLOSE_FD(respfds[0]); |
322 | 0 | CLOSE_FD(respfds[1]); |
323 | 0 | CLOSE_FD(inputfds[0]); |
324 | 0 | CLOSE_FD(inputfds[1]); |
325 | 0 | #undef CLOSE_FD |
326 | 0 | if (ret != 0) |
327 | 0 | h2o_buffer_dispose(resp); |
328 | |
|
329 | 0 | return ret; |
330 | 0 | } |
331 | | |
332 | | size_t h2o_numproc(void) |
333 | 0 | { |
334 | 0 | #if defined(_SC_NPROCESSORS_ONLN) |
335 | 0 | return (size_t)sysconf(_SC_NPROCESSORS_ONLN); |
336 | | #elif defined(CTL_HW) && defined(HW_AVAILCPU) |
337 | | int name[] = {CTL_HW, HW_AVAILCPU}; |
338 | | int ncpu; |
339 | | size_t ncpu_sz = sizeof(ncpu); |
340 | | if (sysctl(name, sizeof(name) / sizeof(name[0]), &ncpu, &ncpu_sz, NULL, 0) != 0 || sizeof(ncpu) != ncpu_sz) { |
341 | | h2o_error_printf("[ERROR] failed to obtain number of CPU cores, assuming as one\n"); |
342 | | ncpu = 1; |
343 | | } |
344 | | return ncpu; |
345 | | #else |
346 | | return 1; |
347 | | #endif |
348 | 0 | } |