aboutsummaryrefslogtreecommitdiff
path: root/src/lib/process/daemon.c
blob: b3b98a297e49b0dea848d30ded35be99055b0e4c (plain)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* Copyright (c) 2003, Roger Dingledine
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
 * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
 * \file daemon.c
 * \brief Run the tor process in the background (unix only)
 **/

#include "orconfig.h"
#include "lib/process/daemon.h"

#ifndef _WIN32

#include "lib/fs/files.h"
#include "lib/log/log.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];

/**
 * Return true iff we've called start_daemon() at least once.
 */
bool
start_daemon_has_been_called(void)
{
  return start_daemon_called != 0;
}

/** 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.)  Return true if we actually
 * forked and this is the child; false otherwise.
 */
int
start_daemon(void)
{
  pid_t pid;

  if (start_daemon_called)
    return 0;
  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. */
    return 0; // LCOV_EXCL_LINE unreachable
  } 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 1;
  }
}

/** 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.) Return true if we actually did a fork; false if we
 * didn't.
 */
int
finish_daemon(const char *desired_cwd)
{
  int nullfd;
  char c = '.';
  if (finish_daemon_called)
    return 0;
  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]);

  return 0;
}
#else /* defined(_WIN32) */
/* defined(_WIN32) */
int
start_daemon(void)
{
  return 0;
}
int
finish_daemon(const char *cp)
{
  (void)cp;
  return 0;
}
bool
start_daemon_has_been_called(void)
{
  return false;
}

#endif /* !defined(_WIN32) */