Coverage Report

Created: 2026-08-12 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tor/src/lib/process/daemon.c
Line
Count
Source
1
/* Copyright (c) 2003, Roger Dingledine
2
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3
 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4
/* See LICENSE for licensing information */
5
6
/**
7
 * \file daemon.c
8
 * \brief Run the tor process in the background (unix only)
9
 **/
10
11
#include "orconfig.h"
12
#include "lib/process/daemon.h"
13
14
#ifndef _WIN32
15
16
#include "lib/fdio/fdio.h"
17
#include "lib/fs/files.h"
18
#include "lib/log/log.h"
19
#include "lib/thread/threads.h"
20
21
#ifdef HAVE_SYS_TYPES_H
22
#include <sys/types.h>
23
#endif
24
#ifdef HAVE_UNISTD_H
25
#include <unistd.h>
26
#endif
27
#ifdef HAVE_FCNTL_H
28
#include <fcntl.h>
29
#endif
30
#include <errno.h>
31
#include <stdlib.h>
32
#include <string.h>
33
34
/* Based on code contributed by christian grothoff */
35
/** True iff we've called start_daemon(). */
36
static int start_daemon_called = 0;
37
/** True iff we've called finish_daemon(). */
38
static int finish_daemon_called = 0;
39
/** Socketpair used to communicate between parent and child process while
40
 * daemonizing. */
41
static int daemon_filedes[2];
42
43
/**
44
 * Return true iff we've called start_daemon() at least once.
45
 */
46
bool
47
start_daemon_has_been_called(void)
48
0
{
49
0
  return start_daemon_called != 0;
50
0
}
51
52
/** Start putting the process into daemon mode: fork and drop all resources
53
 * except standard fds.  The parent process never returns, but stays around
54
 * until finish_daemon is called.  (Note: it's safe to call this more
55
 * than once: calls after the first are ignored.)  Return true if we actually
56
 * forked and this is the child; false otherwise.
57
 */
58
int
59
start_daemon(void)
60
0
{
61
0
  pid_t pid;
62
63
0
  if (start_daemon_called)
64
0
    return 0;
65
0
  start_daemon_called = 1;
66
67
0
  if (tor_pipe_cloexec(daemon_filedes)) {
68
    /* LCOV_EXCL_START */
69
0
    log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno));
70
0
    exit(1); // exit ok: during daemonize, pipe failed.
71
    /* LCOV_EXCL_STOP */
72
0
  }
73
0
  pid = fork();
74
0
  if (pid < 0) {
75
    /* LCOV_EXCL_START */
76
0
    log_err(LD_GENERAL,"fork failed. Exiting.");
77
0
    exit(1); // exit ok: during daemonize, fork failed
78
    /* LCOV_EXCL_STOP */
79
0
  }
80
0
  if (pid) {  /* Parent */
81
0
    int ok;
82
0
    char c;
83
84
0
    close(daemon_filedes[1]); /* we only read */
85
0
    ok = -1;
86
0
    while (0 < read(daemon_filedes[0], &c, sizeof(char))) {
87
0
      if (c == '.')
88
0
        ok = 1;
89
0
    }
90
0
    fflush(stdout);
91
0
    if (ok == 1)
92
0
      exit(0); // exit ok: during daemonize, daemonizing.
93
0
    else
94
0
      exit(1); /* child reported error. exit ok: daemonize failed. */
95
0
    return 0; // LCOV_EXCL_LINE unreachable
96
0
  } else { /* Child */
97
0
    close(daemon_filedes[0]); /* we only write */
98
99
0
    (void) setsid(); /* Detach from controlling terminal */
100
    /*
101
     * Fork one more time, so the parent (the session group leader) can exit.
102
     * This means that we, as a non-session group leader, can never regain a
103
     * controlling terminal.   This part is recommended by Stevens's
104
     * _Advanced Programming in the Unix Environment_.
105
     */
106
0
    if (fork() != 0) {
107
0
      exit(0); // exit ok: during daemonize, fork failed (2)
108
0
    }
109
0
    set_main_thread(); /* We are now the main thread. */
110
111
0
    return 1;
112
0
  }
113
0
}
114
115
/** Finish putting the process into daemon mode: drop standard fds, and tell
116
 * the parent process to exit.  (Note: it's safe to call this more than once:
117
 * calls after the first are ignored.  Calls start_daemon first if it hasn't
118
 * been called already.) Return true if we actually did a fork; false if we
119
 * didn't.
120
 */
121
int
122
finish_daemon(const char *desired_cwd)
123
0
{
124
0
  int nullfd;
125
0
  char c = '.';
126
0
  if (finish_daemon_called)
127
0
    return 0;
128
0
  if (!start_daemon_called)
129
0
    start_daemon();
130
0
  finish_daemon_called = 1;
131
132
0
  if (!desired_cwd)
133
0
    desired_cwd = "/";
134
   /* Don't hold the wrong FS mounted */
135
0
  if (chdir(desired_cwd) < 0) {
136
0
    log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd);
137
0
    exit(1); // exit ok: during daemonize, chdir failed.
138
0
  }
139
140
0
  nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
141
0
  if (nullfd < 0) {
142
    /* LCOV_EXCL_START */
143
0
    log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
144
0
    exit(1); // exit ok: during daemonize, couldn't open /dev/null
145
    /* LCOV_EXCL_STOP */
146
0
  }
147
  /* close fds linking to invoking terminal, but
148
   * close usual incoming fds, but redirect them somewhere
149
   * useful so the fds don't get reallocated elsewhere.
150
   */
151
0
  if (dup2(nullfd,0) < 0 ||
152
0
      dup2(nullfd,1) < 0 ||
153
0
      dup2(nullfd,2) < 0) {
154
    /* LCOV_EXCL_START */
155
0
    log_err(LD_GENERAL,"dup2 failed. Exiting.");
156
0
    exit(1); // exit ok: during daemonize, dup2 failed.
157
    /* LCOV_EXCL_STOP */
158
0
  }
159
0
  if (nullfd > 2)
160
0
    close(nullfd);
161
  /* signal success */
162
0
  if (write(daemon_filedes[1], &c, sizeof(char)) != sizeof(char)) {
163
0
    log_err(LD_GENERAL,"write failed. Exiting.");
164
0
  }
165
0
  close(daemon_filedes[1]);
166
167
0
  return 0;
168
0
}
169
#else /* defined(_WIN32) */
170
/* defined(_WIN32) */
171
int
172
start_daemon(void)
173
{
174
  return 0;
175
}
176
int
177
finish_daemon(const char *cp)
178
{
179
  (void)cp;
180
  return 0;
181
}
182
bool
183
start_daemon_has_been_called(void)
184
{
185
  return false;
186
}
187
188
#endif /* !defined(_WIN32) */