aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/daemon.cpp
blob: 130e6c8f45ea52d99f9b8bbe07b00f7db0e7aab2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <util/generic/yexception.h>

#include <cerrno>
#include <cstdlib>
#include <util/system/info.h>

#if defined(_win_)
    #include <io.h>
#else
    #include <sys/wait.h>
    #include <unistd.h>
    #include <fcntl.h>
#endif

#include "daemon.h"

#ifdef _unix_
using namespace NDaemonMaker;

static bool Fork(EParent parent) {
    pid_t pid = fork();

    if (pid > 0) {
        int status = 0;
        while (waitpid(pid, &status, 0) < 0 && errno == EINTR) {
        }
        if (parent == callExitFromParent) {
            _exit(0);
        } else {
            return true;
        }
    } else if (pid < 0) {
        ythrow TSystemError() << "Cannot fork";
    }

    if (setsid() < 0) {
        ythrow TSystemError() << "Cannot setsid";
    }

    pid = fork();

    if (pid > 0) {
        _exit(0);
    } else if (pid < 0) {
        ythrow TSystemError() << "Cannot second fork";
    }
    return false;
}

#endif

static void CloseFromToExcept(int from, int to, const int* except) {
    (void)from;
    (void)to;
    (void)except;

#ifdef _unix_
    int mfd = NSystemInfo::MaxOpenFiles();
    for (int s = from; s < mfd && (to == -1 || s < to); s++) {
        for (const int* ex = except; *ex >= 0; ++ex) {
            if (s == *ex) {
                goto dontclose;
            }
        }
        while (close(s) == -1) {
            if (errno == EBADF) {
                break;
            }
            if (errno != EINTR) {
                ythrow TSystemError() << "close(" << s << ") failed";
            }
        }
    dontclose:;
    }
#endif /* _unix_ */
}

bool NDaemonMaker::MakeMeDaemon(ECloseDescriptors cd, EStdIoDescriptors iod, EChDir chd, EParent parent) {
    (void)cd;
    (void)iod;
    (void)chd;

#ifdef _unix_
    if (Fork(parent)) {
        return true;
    }

    if (chd == chdirRoot) {
        if (chdir("/")) {
            ythrow TSystemError() << "chdir(\"/\") failed";
        }
    }

    int fd[4] = {-1, -1, -1, -1};
    switch (iod) {
        case openYandexStd:
            fd[0] = open("yandex.stdin", O_RDONLY);
            if (fd[0] < 0) {
                ythrow TSystemError() << "Cannot open 'yandex.stdin'";
            }
            fd[1] = open("yandex.stdout", O_WRONLY | O_APPEND | O_CREAT, 660);
            if (fd[1] < 0) {
                ythrow TSystemError() << "Cannot open 'yandex.stdout'";
            }
            fd[2] = open("yandex.stderr", O_WRONLY | O_APPEND | O_CREAT, 660);
            if (fd[2] < 0) {
                ythrow TSystemError() << "Cannot open 'yandex.stderr'";
            }
            break;
        case openDevNull:
            fd[0] = open("/dev/null", O_RDWR, 0);
            break;
        case openNone:
            break;
        default:
            ythrow yexception() << "Unknown open descriptors mode: " << (int)iod;
    }

    const int except[4] = {
        fd[0],
        fd[1],
        fd[2],
        -1};
    if (closeAll == cd) {
        CloseFromToExcept(0, -1, except);
    } else if (closeStdIoOnly == cd) {
        CloseFromToExcept(0, 3, except);
    } else {
        ythrow yexception() << "Unknown close descriptors mode: " << (int)cd;
    }

    switch (iod) {
        case openYandexStd:
            /* Assuming that open(2) acquires fds in order. */
            dup2(fd[0], STDIN_FILENO);
            if (fd[0] > 2) {
                close(fd[0]);
            }
            dup2(fd[1], STDOUT_FILENO);
            if (fd[1] > 2) {
                close(fd[1]);
            }
            dup2(fd[2], STDERR_FILENO);
            if (fd[2] > 2) {
                close(fd[2]);
            }
            break;
        case openDevNull:
            dup2(fd[0], STDIN_FILENO);
            dup2(fd[0], STDOUT_FILENO);
            dup2(fd[0], STDERR_FILENO);
            if (fd[0] > 2) {
                close(fd[0]);
            }
            break;
        default:
            break;
    }
    return false;
#else
    return true;
#endif
}

void NDaemonMaker::CloseFrom(int fd) {
    static const int except[1] = {-1};
    CloseFromToExcept(fd, -1, except);
}