Daemon.cpp 6.81 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*******************************************************************************
 *
 * This file is part of the Castor project.
 * See http://castor.web.cern.ch/castor
 *
 * Copyright (C) 2003  CERN
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * @author Castor Dev team, castor-dev@cern.ch
 ******************************************************************************/

22
#include "common/exception/Errnum.hpp"
23
24
25
26
27
28
29
30
#include "castor/server/Daemon.hpp"
#include "castor/server/ThreadNotification.hpp"
#include "castor/System.hpp"
#include <getopt.h>

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
31
#include <sys/stat.h>
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

//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
castor::server::Daemon::Daemon(std::ostream &stdOut, std::ostream &stdErr,
  log::Logger &log) throw():
  m_stdOut(stdOut),
  m_stdErr(stdErr),
  m_log(log),
  m_foreground(false),
  m_commandLineHasBeenParsed(false) {
}

//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
castor::server::Daemon::~Daemon() throw() {
}

//------------------------------------------------------------------------------
// parseCommandLine
//------------------------------------------------------------------------------
void castor::server::Daemon::parseCommandLine(int argc,
  char *argv[])  {
  struct ::option longopts[4];

  longopts[0].name = "foreground";
  longopts[0].has_arg = no_argument;
  longopts[0].flag = NULL;
  longopts[0].val = 'f';

  longopts[1].name = "config";
  longopts[1].has_arg = required_argument;
  longopts[1].flag = NULL;
  longopts[1].val = 'c';

  longopts[2].name = "help";
  longopts[2].has_arg = no_argument;
  longopts[2].flag = NULL;
  longopts[2].val = 'h';

73
  memset(&longopts[3], 0, sizeof(struct ::option));
74
75

  char c;
76
77
78
79
80
81
  // Reset getopt's global variables to make sure we start fresh
  optind=0;
  // Prevent getopt from printing out errors on stdout
  opterr=0;
  // We ask getopt to not reshuffle argv ('+')
  while ((c = getopt_long(argc, argv, "+fc:h", longopts, NULL)) != -1) {
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
    switch (c) {
    case 'f':
      m_foreground = true;
      break;
    case 'c':
      setenv("PATH_CONFIG", optarg, 1);
      m_stdOut << "Using configuration file " << optarg << std::endl;
      break;
    case 'h':
      help(argv[0]);
      exit(0);
      break;
    default:
      break;
    }
  }

  m_commandLineHasBeenParsed = true;
}

//------------------------------------------------------------------------------
// help
//------------------------------------------------------------------------------
void castor::server::Daemon::help(const std::string &programName)
  throw() {
  m_stdOut << "Usage: " << programName << " [options]\n"
    "\n"
    "where options can be:\n"
    "\n"
    "\t--foreground            or -f         \tRemain in the Foreground\n"
    "\t--config <config-file>  or -c         \tConfiguration file\n"
    "\t--help                  or -h         \tPrint this help and exit\n"
    "\n"
    "Comments to: Castor.Support@cern.ch\n";
}

//------------------------------------------------------------------------------
// getServerName
//------------------------------------------------------------------------------
const std::string &castor::server::Daemon::getServerName() const throw() {
  return m_log.getProgramName();
}

//------------------------------------------------------------------------------
// getForeground
//------------------------------------------------------------------------------
bool castor::server::Daemon::getForeground() const
   {
  if(!m_commandLineHasBeenParsed) {
131
    cta::exception::CommandLineNotParsed ex;
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
    ex.getMessage() <<
      "Failed to determine whether or not the daemon should run in the"
      " foreground because the command-line has not yet been parsed";
    throw ex;
  }

  return m_foreground;
}

//-----------------------------------------------------------------------------
// setCommandLineParsed
//-----------------------------------------------------------------------------
void castor::server::Daemon::setCommandLineHasBeenParsed(const bool foreground)
  throw() {
  m_foreground = foreground;
  m_commandLineHasBeenParsed = true;
}

//------------------------------------------------------------------------------
// daemonizeIfNotRunInForeground
//------------------------------------------------------------------------------
void castor::server::Daemon::daemonizeIfNotRunInForeground(
  const bool runAsStagerSuperuser) {
  // Do nothing if already a daemon
  if (1 == getppid())  {
    return;
  }

  // If the daemon is to be run in the background
  if (!m_foreground) {
    m_log.prepareForFork();

    {
      pid_t pid = 0;
166
      cta::exception::Errnum::throwOnNegative(pid = fork(),
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
        "Failed to daemonize: Failed to fork");
      // If we got a good PID, then we can exit the parent process
      if (0 < pid) {
        exit(EXIT_SUCCESS);
      }
    }

    // We could set our working directory to '/' here with a call to chdir(2).
    // For the time being we don't and leave it to the initd script to change
    // to a suitable directory for us!

    // Change the file mode mask
    umask(0);

    // Run the daemon in a new session
182
    cta::exception::Errnum::throwOnNegative(setsid(),
183
184
185
      "Failed to daemonize: Failed to run daemon is a new session");

    // Redirect standard files to /dev/null
186
    cta::exception::Errnum::throwOnNull(
187
188
      freopen("/dev/null", "r", stdin),
      "Failed to daemonize: Falied to freopen stdin");
189
    cta::exception::Errnum::throwOnNull(
190
191
      freopen("/dev/null", "w", stdout),
      "Failed to daemonize: Failed to freopen stdout");
192
    cta::exception::Errnum::throwOnNull(
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
      freopen("/dev/null", "w", stderr),
      "Failed to daemonize: Failed to freopen stderr");
  } // if (!m_foreground)

  // Change the user of the daemon process to the Castor superuser if requested
  if (runAsStagerSuperuser) {
    castor::System::switchToCastorSuperuser();
  }

  // Ignore SIGPIPE (connection lost with client)
  // and SIGXFSZ (a file is too big)
  signal(SIGPIPE, SIG_IGN);
  signal(SIGXFSZ, SIG_IGN);
}