RmcDaemon.cpp 14.4 KB
Newer Older
1
/******************************************************************************
2
 *         castor/tape/tapeserver/daemon/RmcDaemon.cpp
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.
 *
 *
 *
22
 * @author Steven.Murray@cern.ch
23
24
 *****************************************************************************/
 
25
26
27
28
29
#include "castor/common/CastorConfiguration.hpp"
#include "castor/exception/Errnum.hpp"
#include "castor/exception/BadAlloc.hpp"
#include "castor/io/io.hpp"
#include "castor/tape/rmc/AcceptHandler.hpp"
30
#include "castor/tape/rmc/RmcDaemon.hpp"
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "castor/tape/utils/utils.hpp"
#include "castor/utils/SmartFd.hpp"
#include "castor/utils/utils.hpp"
#include "h/rmc_constants.h"

#include <algorithm>
#include <limits.h>
#include <memory>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
44

45
46
47
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
48
49
50
51
castor::tape::rmc::RmcDaemon::RmcDaemon::RmcDaemon(
  std::ostream &stdOut,
  std::ostream &stdErr,
  log::Logger &log,
52
  reactor::ZMQReactor &reactor,
53
  legacymsg::CupvProxy &cupv) :
54
55
  castor::server::Daemon(stdOut, stdErr, log),
  m_reactor(reactor),
Steven Murray's avatar
Steven Murray committed
56
  m_cupv(cupv),
57
58
59
60
61
62
63
64
65
  m_programName("rmcd"),
  m_hostName(getHostName()),
  m_rmcPort(getRmcPort()) {
}

//------------------------------------------------------------------------------
// getHostName
//------------------------------------------------------------------------------
std::string castor::tape::rmc::RmcDaemon::RmcDaemon::getHostName()
66
  const  {
67
68
69
70
  char nameBuf[81];
  if(gethostname(nameBuf, sizeof(nameBuf))) {
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
71
    castor::exception::Exception ex;
72
73
74
75
76
77
78
79
80
81
82
    ex.getMessage() << "Failed to get host name: " << errBuf;
    throw ex;
  }

  return nameBuf;
}

//------------------------------------------------------------------------------
// getRmcPort
//------------------------------------------------------------------------------
unsigned short castor::tape::rmc::RmcDaemon::getRmcPort()
83
   {
84
85
86
87
88
89
90
91
92
93
94
95
96
  std::string configParamValue;

  // If RMC PORT is not in /etc/castor.conf then use the compile time default
  try {
    const std::string category = "RMC";
    const std::string name = "PORT";
    configParamValue = getConfigParam(category, name);
  } catch(castor::exception::Exception &ex) {
    return RMC_PORT;
  }

  // If RMC PORT is in /etc/castor.conf then it must be a valid unsigned integer
  if(!castor::utils::isValidUInt(configParamValue.c_str())) {
97
    castor::exception::Exception ex;
98
99
100
101
102
103
104
105
106
107
108
109
110
    ex.getMessage() << "RMC PORT is not a valid unsigned integer";
    throw ex;
  }

  return atoi(configParamValue.c_str());
}

//------------------------------------------------------------------------------
// getConfigParam
//------------------------------------------------------------------------------
std::string castor::tape::rmc::RmcDaemon::getConfigParam(
  const std::string &category,
  const std::string &name)
111
   {
112
113
114
115
116
117
118
119
120
  std::ostringstream task;
  task << "get " << category << ":" << name << " from castor.conf";

  common::CastorConfiguration config;
  std::string value;

  try {
    config = common::CastorConfiguration::getConfig();
  } catch(castor::exception::Exception &ne) {
121
    castor::exception::Exception ex;
122
123
124
125
126
127
    ex.getMessage() << "Failed to " << task.str() <<
      ": Failed to get castor configuration: " << ne.getMessage().str();
    throw ex;
  }

  try {
128
    value = config.getConfEntString(category.c_str(), name.c_str());
129
  } catch(castor::exception::Exception &ne) {
130
    castor::exception::Exception ex;
131
132
133
134
135
136
    ex.getMessage() << "Failed to " << task.str() <<
      ": Failed to get castor configuration entry: " << ne.getMessage().str();
    throw ex;
  }

  return value;
137
138
139
140
141
142
143
144
145
146
147
}

//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
castor::tape::rmc::RmcDaemon::~RmcDaemon() throw() {
}

//------------------------------------------------------------------------------
// main
//------------------------------------------------------------------------------
148
int castor::tape::rmc::RmcDaemon::main(const int argc, char **const argv) throw() {
149
150
151
152
153
  try {

    exceptionThrowingMain(argc, argv);

  } catch (castor::exception::Exception &ex) {
154
155
156
157
    std::ostringstream msg;
    msg << "Aborting: Caught an unexpected exception: " <<
      ex.getMessage().str();
    m_stdErr << std::endl << msg.str() << std::endl << std::endl;
158

159
160
161
162
    log::Param params[] = {
      log::Param("Message", msg.str()),
      log::Param("Code"   , ex.code())};
    m_log(LOG_ERR, msg.str(), params);
163
164
165
166
167
168
169
170
171
172

    return 1;
  }

  return 0;
}

//------------------------------------------------------------------------------
// exceptionThrowingMain
//------------------------------------------------------------------------------
173
void  castor::tape::rmc::RmcDaemon::exceptionThrowingMain(
174
  const int argc, char **const argv)  {
175
  logStartOfDaemon(argc, argv);
Steven Murray's avatar
Steven Murray committed
176
  parseCommandLine(argc, argv);
177
178
  const bool runAsStagerSuperuser = false;
  daemonizeIfNotRunInForeground(runAsStagerSuperuser);
179
180
181
  blockSignals();
  setUpReactor();
  mainEventLoop();
182
183
184
185
186
187
188
189
}

//------------------------------------------------------------------------------
// logStartOfDaemon
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::logStartOfDaemon(
  const int argc, const char *const *const argv) throw() {
  const std::string concatenatedArgs = argvToString(argc, argv);
190
191
  std::ostringstream msg;
  msg << m_programName << " started";
192

Steven Murray's avatar
Steven Murray committed
193
194
  log::Param params[] = {
    log::Param("argv", concatenatedArgs)};
195
  m_log(LOG_INFO, msg.str(), params);
196
197
198
199
200
}

//------------------------------------------------------------------------------
// argvToString
//------------------------------------------------------------------------------
201
202
std::string castor::tape::rmc::RmcDaemon::argvToString(
  const int argc, const char *const *const argv) throw() {
203
204
205
206
207
208
209
210
211
212
213
214
215
  std::string str;

  for(int i=0; i < argc; i++) {
    if(i != 0) {
      str += " ";
    }

    str += argv[i];
  }
  return str;
}

//------------------------------------------------------------------------------
216
// blockSignals
217
//------------------------------------------------------------------------------
218
void castor::tape::rmc::RmcDaemon::blockSignals() const
219
   {
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
  sigset_t sigs;
  sigemptyset(&sigs);
  // The signals that should not asynchronously disturb the daemon
  sigaddset(&sigs, SIGHUP);
  sigaddset(&sigs, SIGINT);
  sigaddset(&sigs, SIGQUIT);
  sigaddset(&sigs, SIGPIPE);
  sigaddset(&sigs, SIGTERM);
  sigaddset(&sigs, SIGUSR1);
  sigaddset(&sigs, SIGUSR2);
  sigaddset(&sigs, SIGCHLD);
  sigaddset(&sigs, SIGTSTP);
  sigaddset(&sigs, SIGTTIN);
  sigaddset(&sigs, SIGTTOU);
  sigaddset(&sigs, SIGPOLL);
  sigaddset(&sigs, SIGURG);
  sigaddset(&sigs, SIGVTALRM);
  castor::exception::Errnum::throwOnNonZero(
    sigprocmask(SIG_BLOCK, &sigs, NULL),
    "Failed to block signals: sigprocmask() failed");
}

//------------------------------------------------------------------------------
// setUpReactor
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::setUpReactor()
246
   {
247
248
249
250
251
252
  createAndRegisterAcceptHandler();
}

//------------------------------------------------------------------------------
// createAndRegisterAcceptHandler
//------------------------------------------------------------------------------
253
void castor::tape::rmc::RmcDaemon::createAndRegisterAcceptHandler()  {
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  castor::utils::SmartFd listenSock;
  try {
    listenSock.reset(io::createListenerSock(m_rmcPort));
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex(ne.code());
    ex.getMessage() << "Failed to create socket to listen for client connections"
      ": " << ne.getMessage().str();
    throw ex;
  }
  {
    log::Param params[] = {
      log::Param("listeningPort", m_rmcPort)};
    m_log(LOG_INFO, "Listening for client connections", params);
  }

  std::auto_ptr<AcceptHandler> acceptHandler;
  try {
    acceptHandler.reset(new AcceptHandler(listenSock.get(), m_reactor, m_log));
    listenSock.release();
  } catch(std::bad_alloc &ba) {
    castor::exception::BadAlloc ex;
    ex.getMessage() <<
      "Failed to create the event handler for accepting client connections"
      ": " << ba.what();
    throw ex;
  }
  m_reactor.registerHandler(acceptHandler.get());
  acceptHandler.release();
}

//------------------------------------------------------------------------------
// mainEventLoop
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::mainEventLoop()
288
   {
289
290
291
292
293
294
295
296
297
  while(handleEvents()) {
    forkChildProcesses();
  }
}

//------------------------------------------------------------------------------
// handleEvents
//------------------------------------------------------------------------------
bool castor::tape::rmc::RmcDaemon::handleEvents()
298
   {
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
  const int timeout = 100; // 100 milliseconds
  m_reactor.handleEvents(timeout);
  return handlePendingSignals();
}

//------------------------------------------------------------------------------
// handlePendingSignals
//------------------------------------------------------------------------------
bool castor::tape::rmc::RmcDaemon::handlePendingSignals()
  throw() {
  bool continueMainEventLoop = true;
  int sig = 0;
  sigset_t allSignals;
  siginfo_t sigInfo;
  sigfillset(&allSignals);
  struct timespec immedTimeout = {0, 0};

  // While there is a pending signal to be handled
  while (0 < (sig = sigtimedwait(&allSignals, &sigInfo, &immedTimeout))) {
    switch(sig) {
    case SIGINT: // Signal number 2
      m_log(LOG_INFO, "Stopping gracefully because SIGINT was received");
      continueMainEventLoop = false;
      break;
    case SIGTERM: // Signal number 15
      m_log(LOG_INFO, "Stopping gracefully because SIGTERM was received");
      continueMainEventLoop = false;
      break;
    case SIGCHLD: // Signal number 17
      reapZombies();
      break;
    default:
      {
        log::Param params[] = {log::Param("signal", sig)};
        m_log(LOG_INFO, "Ignoring signal", params);
      }
      break;
    }
  }

  return continueMainEventLoop;
}

//------------------------------------------------------------------------------
// reapZombies
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::reapZombies() throw() {
  pid_t childPid = 0;
  int waitpidStat = 0;

  while (0 < (childPid = waitpid(-1, &waitpidStat, WNOHANG))) {
    reapZombie(childPid, waitpidStat);
  }
}

//------------------------------------------------------------------------------
// reapZombie
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::reapZombie(const pid_t childPid, const int waitpidStat) throw() {
  logChildProcessTerminated(childPid, waitpidStat);
}
360

361
362
363
364
365
366
//------------------------------------------------------------------------------
// logChildProcessTerminated
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::logChildProcessTerminated(const pid_t childPid, const int waitpidStat) throw() {
  std::list<log::Param> params;
  params.push_back(log::Param("childPid", childPid));
367

368
369
370
  if(WIFEXITED(waitpidStat)) {
    params.push_back(log::Param("WEXITSTATUS", WEXITSTATUS(waitpidStat)));
  }
371

372
373
374
  if(WIFSIGNALED(waitpidStat)) {
    params.push_back(log::Param("WTERMSIG", WTERMSIG(waitpidStat)));
  }
375

376
377
378
379
380
  if(WCOREDUMP(waitpidStat)) {
    params.push_back(log::Param("WCOREDUMP", "true"));
  } else {
    params.push_back(log::Param("WCOREDUMP", "false"));
  }
381

382
383
384
  if(WIFSTOPPED(waitpidStat)) {
    params.push_back(log::Param("WSTOPSIG", WSTOPSIG(waitpidStat)));
  }
385

386
387
388
389
  if(WIFCONTINUED(waitpidStat)) {
    params.push_back(log::Param("WIFCONTINUED", "true"));
  } else {
    params.push_back(log::Param("WIFCONTINUED", "false"));
390
391
  }

392
  m_log(LOG_INFO, "Child-process terminated", params);
393
}
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427

//------------------------------------------------------------------------------
// forkChildProcesses
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::forkChildProcesses() throw() {
/*
  const std::list<std::string> unitNames =
    m_driveCatalogue.getUnitNames(DriveCatalogue::DRIVE_STATE_WAITFORK);

  for(std::list<std::string>::const_iterator itor = unitNames.begin();
    itor != unitNames.end(); itor++) {
    forkChildProcess(*itor);
  }
*/
}

//------------------------------------------------------------------------------
// forkChildProcess
//------------------------------------------------------------------------------
void castor::tape::rmc::RmcDaemon::forkChildProcess() throw() {
/*
  m_log.prepareForFork();
  const pid_t childPid = fork();

  // If fork failed
  if(0 > childPid) {
    // Log an error message and return
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
    log::Param params[] = {log::Param("message", errBuf)};
    m_log(LOG_ERR, "Failed to fork mount session for tape drive", params);

  // Else if this is the parent process
  } else if(0 < childPid) {
428
    m_driveCatalogue.forkedDataTransferSession(unitName, childPid);
429
430
431
432
433
434
435

  // Else this is the child process
  } else {
    // Clear the reactor which in turn will close all of the open
    // file-descriptors owned by the event handlers
    m_reactor.clear();

436
    runDataTransferSession(unitName);
437

438
    // The runDataTransferSession() should call exit() and should therefore never
439
    // return
440
    m_log(LOG_ERR, "runDataTransferSession() returned unexpectedly");
441
  }
442
*/
443
}