1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 Intel Corporation.
4 **
5 ** Permission is hereby granted, free of charge, to any person obtaining a copy
6 ** of this software and associated documentation files (the "Software"), to deal
7 ** in the Software without restriction, including without limitation the rights
8 ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 ** copies of the Software, and to permit persons to whom the Software is
10 ** furnished to do so, subject to the following conditions:
11 **
12 ** The above copyright notice and this permission notice shall be included in
13 ** all copies or substantial portions of the Software.
14 **
15 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 ** THE SOFTWARE.
22 **
23 ****************************************************************************/
24 
25 #include "forkfd.h"
26 
27 #include <sys/types.h>
28 #include <sys/procdesc.h>
29 
30 #include "forkfd_atomic.h"
31 
32 // in forkfd.c
33 static int convertForkfdWaitFlagsToWaitFlags(int ffdoptions);
34 static void convertStatusToForkfdInfo(int status, struct forkfd_info *info);
35 
36 #if __FreeBSD__ >= 10
37 /* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option
38  * anymore and can't be disabled. */
39 static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(1);
40 #else
41 static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(0);
42 #endif
43 
system_has_forkfd()44 int system_has_forkfd()
45 {
46     return ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED) > 0;
47 }
48 
system_forkfd(int flags,pid_t * ppid,int * system)49 int system_forkfd(int flags, pid_t *ppid, int *system)
50 {
51     int ret;
52     pid_t pid;
53 
54     int state = ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED);
55     *system = 0;
56     if (state < 0)
57         return -1;
58 
59     pid = pdfork(&ret, PD_DAEMON);
60 #  if __FreeBSD__ == 9
61     if (state == 0 && pid != 0) {
62         /* Parent process: remember whether PROCDESC was compiled into the kernel */
63         state = (pid == -1 && errno == ENOSYS) ? -1 : 1;
64         ffd_atomic_store(&system_forkfd_state, state, FFD_ATOMIC_RELAXED);
65     }
66     if (state < 0)
67         return -1;
68 #  endif
69     *system = 1;
70     if (__builtin_expect(pid == -1, 0))
71         return -1;
72 
73     if (pid == 0) {
74         /* child process */
75         return FFD_CHILD_PROCESS;
76     }
77 
78     /* parent process */
79     if (flags & FFD_CLOEXEC)
80         fcntl(ret, F_SETFD, FD_CLOEXEC);
81     if (flags & FFD_NONBLOCK)
82         fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK);
83     if (ppid)
84         *ppid = pid;
85     return ret;
86 }
87 
system_forkfd_wait(int ffd,struct forkfd_info * info,int ffdoptions,struct rusage * rusage)88 int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdoptions, struct rusage *rusage)
89 {
90     pid_t pid;
91     int status;
92     int options = convertForkfdWaitFlagsToWaitFlags(ffdoptions);
93 
94     int ret = pdgetpid(ffd, &pid);
95     if (ret == -1)
96         return ret;
97 
98     if ((options & WNOHANG) == 0) {
99         /* check if the file descriptor is non-blocking */
100         ret = fcntl(ffd, F_GETFL);
101         if (ret == -1)
102             return ret;
103         options |= (ret & O_NONBLOCK) ? WNOHANG : 0;
104     }
105     ret = wait4(pid, &status, options, rusage);
106     if (ret != -1 && info)
107         convertStatusToForkfdInfo(status, info);
108     return ret == -1 ? -1 : 0;
109 }
110