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 
h2o_set_signal_handler(int signo,void (* cb)(int signo))46 void h2o_set_signal_handler(int signo, void (*cb)(int signo))
47 {
48     struct sigaction action;
49 
50     memset(&action, 0, sizeof(action));
51     sigemptyset(&action.sa_mask);
52     action.sa_handler = cb;
53     sigaction(signo, &action, NULL);
54 }
55 
h2o_setuidgid(const char * user)56 int h2o_setuidgid(const char *user)
57 {
58     struct passwd pwbuf, *pw;
59     char buf[65536]; /* should be large enough */
60 
61     errno = 0;
62     if (getpwnam_r(user, &pwbuf, buf, sizeof(buf), &pw) != 0) {
63         h2o_perror("getpwnam_r");
64         return -1;
65     }
66     if (pw == NULL) {
67         h2o_error_printf("unknown user:%s\n", user);
68         return -1;
69     }
70     if (setgid(pw->pw_gid) != 0) {
71         h2o_error_printf("setgid(%d) failed:%s\n", (int)pw->pw_gid, strerror(errno));
72         return -1;
73     }
74     if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
75         h2o_error_printf("initgroups(%s, %d) failed:%s\n", pw->pw_name, (int)pw->pw_gid, strerror(errno));
76         return -1;
77     }
78     if (setuid(pw->pw_uid) != 0) {
79         h2o_error_printf("setuid(%d) failed:%s\n", (int)pw->pw_uid, strerror(errno));
80         return -1;
81     }
82 
83     return 0;
84 }
85 
h2o_server_starter_get_fds(int ** _fds)86 size_t h2o_server_starter_get_fds(int **_fds)
87 {
88     const char *ports_env, *start, *end, *eq;
89     size_t t;
90     H2O_VECTOR(int) fds = {NULL};
91 
92     if ((ports_env = getenv(SERVER_STARTER_PORT)) == NULL)
93         return 0;
94     if (ports_env[0] == '\0') {
95         h2o_error_printf("$" SERVER_STARTER_PORT " is empty\n");
96         return SIZE_MAX;
97     }
98 
99     /* ports_env example: 127.0.0.1:80=3;/tmp/sock=4 */
100     for (start = ports_env; *start != '\0'; start = *end == ';' ? end + 1 : end) {
101         if ((end = strchr(start, ';')) == NULL)
102             end = start + strlen(start);
103         if ((eq = memchr(start, '=', end - start)) == NULL) {
104             h2o_error_printf("invalid $" SERVER_STARTER_PORT ", an element without `=` in: %s\n", ports_env);
105             goto Error;
106         }
107         if ((t = h2o_strtosize(eq + 1, end - eq - 1)) == SIZE_MAX) {
108             h2o_error_printf("invalid file descriptor number in $" SERVER_STARTER_PORT ": %s\n", ports_env);
109             goto Error;
110         }
111         h2o_vector_reserve(NULL, &fds, fds.size + 1);
112         fds.entries[fds.size++] = (int)t;
113     }
114 
115     *_fds = fds.entries;
116     return fds.size;
117 Error:
118     free(fds.entries);
119     return SIZE_MAX;
120 }
121 
build_spawn_env(void)122 static char **build_spawn_env(void)
123 {
124     extern char **environ;
125     size_t num;
126 
127     /* calculate number of envvars, as well as looking for H2O_ROOT= */
128     for (num = 0; environ[num] != NULL; ++num)
129         if (strncmp(environ[num], "H2O_ROOT=", sizeof("H2O_ROOT=") - 1) == 0)
130             return NULL;
131 
132     /* not found */
133     char **newenv = h2o_mem_alloc(sizeof(*newenv) * (num + 2) + sizeof("H2O_ROOT=" H2O_TO_STR(H2O_ROOT)));
134     memcpy(newenv, environ, sizeof(*newenv) * num);
135     newenv[num] = (char *)(newenv + num + 2);
136     newenv[num + 1] = NULL;
137     strcpy(newenv[num], "H2O_ROOT=" H2O_TO_STR(H2O_ROOT));
138 
139     return newenv;
140 }
141 
h2o_spawnp(const char * cmd,char * const * argv,const int * mapped_fds,int cloexec_mutex_is_locked)142 pid_t h2o_spawnp(const char *cmd, char *const *argv, const int *mapped_fds, int cloexec_mutex_is_locked)
143 {
144 #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     extern char **environ;
153     int pipefds[2] = {-1, -1}, errnum;
154     pid_t pid;
155 
156     /* create pipe, used for sending error codes */
157     if (pipe2(pipefds, O_CLOEXEC) != 0)
158         goto Error;
159 
160     /* fork */
161     if (!cloexec_mutex_is_locked)
162         pthread_mutex_lock(&cloexec_mutex);
163     if ((pid = fork()) == 0) {
164         /* in child process, map the file descriptors and execute; return the errnum through pipe if exec failed */
165         if (mapped_fds != NULL) {
166             for (; *mapped_fds != -1; mapped_fds += 2) {
167                 if (mapped_fds[0] != mapped_fds[1]) {
168                     if (mapped_fds[1] != -1)
169                         dup2(mapped_fds[0], mapped_fds[1]);
170                     close(mapped_fds[0]);
171                 }
172             }
173         }
174         char **env = build_spawn_env();
175         if (env != NULL)
176             environ = env;
177         execvp(cmd, argv);
178         errnum = errno;
179         write(pipefds[1], &errnum, sizeof(errnum));
180         _exit(EX_SOFTWARE);
181     }
182     if (!cloexec_mutex_is_locked)
183         pthread_mutex_unlock(&cloexec_mutex);
184     if (pid == -1)
185         goto Error;
186 
187     /* parent process */
188     close(pipefds[1]);
189     pipefds[1] = -1;
190     ssize_t rret;
191     errnum = 0;
192     while ((rret = read(pipefds[0], &errnum, sizeof(errnum))) == -1 && errno == EINTR)
193         ;
194     if (rret != 0) {
195         /* spawn failed */
196         while (waitpid(pid, NULL, 0) != pid)
197             ;
198         pid = -1;
199         errno = errnum;
200         goto Error;
201     }
202 
203     /* spawn succeeded */
204     close(pipefds[0]);
205     return pid;
206 
207 Error:
208     errnum = errno;
209     if (pipefds[0] != -1)
210         close(pipefds[0]);
211     if (pipefds[1] != -1)
212         close(pipefds[1]);
213     errno = errnum;
214     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 }
244 
h2o_read_command(const char * cmd,char ** argv,h2o_iovec_t std_in,h2o_buffer_t ** resp,int * child_status)245 int h2o_read_command(const char *cmd, char **argv, h2o_iovec_t std_in, h2o_buffer_t **resp, int *child_status)
246 {
247     int respfds[2] = {-1, -1}, inputfds[2] = {-1, -1};
248     pid_t pid = -1;
249     int mutex_locked = 0, ret = -1;
250 
251     h2o_buffer_init(resp, &h2o_socket_buffer_prototype);
252 
253     pthread_mutex_lock(&cloexec_mutex);
254     mutex_locked = 1;
255 
256     /* create pipes for reading the result and for supplying input */
257     if (pipe(respfds) != 0)
258         goto Exit;
259     if (fcntl(respfds[0], F_SETFD, FD_CLOEXEC) < 0)
260         goto Exit;
261     if (pipe(inputfds) != 0)
262         goto Exit;
263     if (fcntl(inputfds[1], F_SETFD, FD_CLOEXEC) < 0)
264         goto Exit;
265 
266     /* spawn */
267     int mapped_fds[] = {inputfds[0], 0, /* stdin of the child process is what is being provide as input */
268                         respfds[1], 1,  /* stdout of the child process is read from the pipe */
269                         -1};
270     if ((pid = h2o_spawnp(cmd, argv, mapped_fds, 1)) == -1)
271         goto Exit;
272     close(respfds[1]);
273     respfds[1] = -1;
274     close(inputfds[0]);
275     inputfds[0] = -1;
276 
277     pthread_mutex_unlock(&cloexec_mutex);
278     mutex_locked = 0;
279 
280     /* supply input */
281     for (size_t off = 0; off < std_in.len;) {
282         ssize_t r;
283         while ((r = write(inputfds[1], std_in.base + off, std_in.len - off)) == -1 && errno == EINTR)
284             ;
285         if (r < 0)
286             break;
287         off += r;
288     }
289     close(inputfds[1]);
290     inputfds[1] = -1;
291 
292     /* read the response from pipe */
293     while (1) {
294         h2o_iovec_t buf = h2o_buffer_reserve(resp, 8192);
295         ssize_t r;
296         while ((r = read(respfds[0], buf.base, buf.len)) == -1 && errno == EINTR)
297             ;
298         if (r <= 0)
299             break;
300         (*resp)->size += r;
301     }
302 
303 Exit:
304     if (mutex_locked)
305         pthread_mutex_unlock(&cloexec_mutex);
306     if (pid != -1) {
307         /* wait for the child to complete */
308         pid_t r;
309         while ((r = waitpid(pid, child_status, 0)) == -1 && errno == EINTR)
310             ;
311         if (r == pid) {
312             /* success */
313             ret = 0;
314         }
315     }
316 #define CLOSE_FD(x)                                                                                                                \
317     do {                                                                                                                           \
318         if ((x) != -1)                                                                                                             \
319             close(x);                                                                                                              \
320     } while (0)
321     CLOSE_FD(respfds[0]);
322     CLOSE_FD(respfds[1]);
323     CLOSE_FD(inputfds[0]);
324     CLOSE_FD(inputfds[1]);
325 #undef CLOSE_FD
326     if (ret != 0)
327         h2o_buffer_dispose(resp);
328 
329     return ret;
330 }
331 
h2o_numproc(void)332 size_t h2o_numproc(void)
333 {
334 #if defined(_SC_NPROCESSORS_ONLN)
335     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 }
349