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
|
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "orconfig.h"
#include "lib/process/daemon.h"
#ifndef _WIN32
#include "lib/fs/files.h"
#include "lib/log/torlog.h"
#include "lib/thread/threads.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
/* Based on code contributed by christian grothoff */
/** True iff we've called start_daemon(). */
static int start_daemon_called = 0;
/** True iff we've called finish_daemon(). */
static int finish_daemon_called = 0;
/** Socketpair used to communicate between parent and child process while
* daemonizing. */
static int daemon_filedes[2];
/** Start putting the process into daemon mode: fork and drop all resources
* except standard fds. The parent process never returns, but stays around
* until finish_daemon is called. (Note: it's safe to call this more
* than once: calls after the first are ignored.)
*/
void
start_daemon(void)
{
pid_t pid;
if (start_daemon_called)
return;
start_daemon_called = 1;
if (pipe(daemon_filedes)) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno));
exit(1); // exit ok: during daemonize, pipe failed.
/* LCOV_EXCL_STOP */
}
pid = fork();
if (pid < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"fork failed. Exiting.");
exit(1); // exit ok: during daemonize, fork failed
/* LCOV_EXCL_STOP */
}
if (pid) { /* Parent */
int ok;
char c;
close(daemon_filedes[1]); /* we only read */
ok = -1;
while (0 < read(daemon_filedes[0], &c, sizeof(char))) {
if (c == '.')
ok = 1;
}
fflush(stdout);
if (ok == 1)
exit(0); // exit ok: during daemonize, daemonizing.
else
exit(1); /* child reported error. exit ok: daemonize failed. */
} else { /* Child */
close(daemon_filedes[0]); /* we only write */
(void) setsid(); /* Detach from controlling terminal */
/*
* Fork one more time, so the parent (the session group leader) can exit.
* This means that we, as a non-session group leader, can never regain a
* controlling terminal. This part is recommended by Stevens's
* _Advanced Programming in the Unix Environment_.
*/
if (fork() != 0) {
exit(0); // exit ok: during daemonize, fork failed (2)
}
set_main_thread(); /* We are now the main thread. */
return;
}
}
/** Finish putting the process into daemon mode: drop standard fds, and tell
* the parent process to exit. (Note: it's safe to call this more than once:
* calls after the first are ignored. Calls start_daemon first if it hasn't
* been called already.)
*/
void
finish_daemon(const char *desired_cwd)
{
int nullfd;
char c = '.';
if (finish_daemon_called)
return;
if (!start_daemon_called)
start_daemon();
finish_daemon_called = 1;
if (!desired_cwd)
desired_cwd = "/";
/* Don't hold the wrong FS mounted */
if (chdir(desired_cwd) < 0) {
log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd);
exit(1); // exit ok: during daemonize, chdir failed.
}
nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
if (nullfd < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
exit(1); // exit ok: during daemonize, couldn't open /dev/null
/* LCOV_EXCL_STOP */
}
/* close fds linking to invoking terminal, but
* close usual incoming fds, but redirect them somewhere
* useful so the fds don't get reallocated elsewhere.
*/
if (dup2(nullfd,0) < 0 ||
dup2(nullfd,1) < 0 ||
dup2(nullfd,2) < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"dup2 failed. Exiting.");
exit(1); // exit ok: during daemonize, dup2 failed.
/* LCOV_EXCL_STOP */
}
if (nullfd > 2)
close(nullfd);
/* signal success */
if (write(daemon_filedes[1], &c, sizeof(char)) != sizeof(char)) {
log_err(LD_GENERAL,"write failed. Exiting.");
}
close(daemon_filedes[1]);
}
#else /* !(!defined(_WIN32)) */
/* defined(_WIN32) */
void
start_daemon(void)
{
}
void
finish_daemon(const char *cp)
{
(void)cp;
}
#endif /* !defined(_WIN32) */
|