1 /*******************************************************************************
2 
3     Global configuration variable to define whether the close-on-exec option
4     should be set on system calls that create a file descriptor in ocean
5     modules.
6 
7     On a POSIX system, if a program calls `exec(2)` (or an equivalent function
8     from the `exec` family) to execute another program, then the devices
9     referred to by the file descriptors held by the original program stay open,
10     and the executed program inherits all of them.
11     This has the consequence that, if the executed program is unaware of the
12     original program's file descriptors so it doesn't close those it doesn't
13     need, then all devices will stay open until the process exits.
14 
15     Most of the time an `exec` call is preceded by a `fork` call to execute a
16     program in a new process. As with `exec` the child process created by `fork`
17     inherits all file descriptors from its parent process that called `fork`.
18     However, each device referred to by one of these file descriptors stays open
19     until _both_ the parent and child process close them or exit. This means
20     that the parent process has no way of closing its devices any more unless
21     the child process cooperates and does so as well, which is in practice very
22     unlikely.
23 
24     This can cause the following situation:
25      - A program opens a device (file, socket, timer or event fd).
26      - The program registers the device with `epoll`.
27      - The program uses a third-party library which starts a task in a separate
28        process using `fork` + `exec`.
29      - The original program (parent process) closes the device.
30      - The executed program (child process) is, as libraries are, unaware of
31        its parent's business so it doesn't close any of the inherited file
32        descriptors; it doesn't even know which it inherited. So the device stays
33        opened.
34      - Because the device is opened it stays registered with `epoll`. The parent
35        process cannot unregister it any more because it has closed the file
36        descriptor.
37      - Until the child process exits `epoll_wait(2)` keeps reporting events for
38        the device in the parent process.
39 
40     This is a problem because it can cause sporadic erratic behaviour in a
41     program. The error may be hard to reproduce and track down.
42 
43     To prevent this from happening POSIX provides a close-on-exec option for
44     each file descriptor. By default it is disabled. If enabled, `exec` will
45     close the file descriptor so that the executed program won't inherit it. But
46     it works only if the parent process explicitly enables this option for every
47     single file descriptor it obtains from the system.
48 
49     The global `open_with_close_on_exec` variable in this module is read by all
50     functions and class constructors in ocean that obtain a file descriptor from
51     the system. If it is `true` then they set the close-on-exec option.
52 
53     This does not affect `stdin`, `stdout` and `stderr`, which are opened before
54     the start of the program.
55 
56     The flag is `true` by default, which is in general recommended to avoid the
57     aforementioned problem unless file descriptor inheriting is needed, which
58     is not a feature that is currently relevant to Sociomantic's use cases.
59 
60     IF YOU HAVE A USE CASE FOR INHERITING FILE DESCRIPTORS, PLEASE CONTACT THE
61     OCEAN MAINTAINERS.
62 
63     The following ocean functions obtain a file descriptor and use this flag:
64 
65     - `ocean.sys.Epoll`: `Epoll.create`
66     - `ocean.sys.EventFD`: `EventFD` constructor
67     - `ocean.sys.Inotify`: `Inotify` constructor
68     - `ocean.sys.SignalFD`: `SignalFD.register`
69     - `ocean.sys.TimerFD`: `TimerFD` constructor
70     - `ocean.sys.socket`: The `socket` and `accept` methods in all `*Socket`
71         classes
72     - `ocean.io.device.File`: `File.open`
73 
74     Copyright:
75         Copyright (c) 2017 dunnhumby Germany GmbH.
76         All rights reserved.
77 
78     License:
79         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
80         Alternatively, this file may be distributed under the terms of the Tango
81         3-Clause BSD License (see LICENSE_BSD.txt for details).
82 
83 *******************************************************************************/
84 
85 module ocean.sys.CloseOnExec;
86 
87 import ocean.meta.types.Qualifiers;
88 
89 /*******************************************************************************
90 
91     If true then all ocean functions obtaining a file descriptor from the system
92     set the close-on-exec option; if false they don't. Changing the value of
93     this variable does not change the state of previously obtained file
94     descriptors.
95 
96 *******************************************************************************/
97 
98 __gshared bool open_with_close_on_exec = true;
99 
100 /*******************************************************************************
101 
102     Helper function to set the close-on-exec bit in a bit mask which specifies
103     option flags for a system call that obtains a new file descriptor, such as
104     `open(2)`. On recent Linux all such system/library functions support
105     enabling the close-on-exec option; this is a Linux extension to POSIX. Some
106     of these flag accepting functions were added more recently with a name
107     extension, for example `accept4(2)` or `inotify_init1(2)`.
108 
109     Params:
110         flags = the flags where the close-on-exec bit should be set if
111                 `open_with_close_on_exec` is `true`
112         close_on_exec_flag = a bitmask with only the close-on-exec bit set
113 
114     Returns:
115         `flags | close_on_exec_flag` if `open_with_close_on_exec` is `true`,
116         otherwise `flags`.
117 
118 *******************************************************************************/
119 
120 T setCloExec ( T, U ) ( T flags, U close_on_exec_flag )
121 {
122     return open_with_close_on_exec? (flags | close_on_exec_flag) : flags;
123 }