TapeDaemon.cpp 32 KB
Newer Older
Steven Murray's avatar
Steven Murray committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/******************************************************************************
 *
 * 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.
 *
 *
 *
21
 * @author Castor Dev team, castor-dev@cern.ch
Steven Murray's avatar
Steven Murray committed
22
23
 *****************************************************************************/
 
24
#include "castor/common/CastorConfiguration.hpp"
25
#include "common/exception/Errnum.hpp"
26
#include "common/exception/BadAlloc.hpp"
27
#include "castor/io/io.hpp"
Daniele Kruse's avatar
Daniele Kruse committed
28
29
#include "castor/legacymsg/CommonMarshal.hpp"
#include "castor/legacymsg/TapeMarshal.hpp"
30
#include "castor/tape/tapeserver/daemon/CleanerSession.hpp"
31
#include "castor/tape/tapeserver/daemon/Constants.hpp"
32
#include "castor/tape/tapeserver/daemon/DataTransferSession.hpp"
33
#include "castor/tape/tapeserver/daemon/LabelSession.hpp"
34
#include "castor/tape/tapeserver/daemon/ProcessForker.hpp"
35
#include "castor/tape/tapeserver/daemon/ProcessForkerConnectionHandler.hpp"
36
#include "castor/tape/tapeserver/daemon/ProcessForkerProxySocket.hpp"
Steven Murray's avatar
Steven Murray committed
37
#include "castor/tape/tapeserver/daemon/TapeDaemon.hpp"
Steven Murray's avatar
Steven Murray committed
38
#include "castor/tape/tapeserver/daemon/TapeDaemonConfig.hpp"
39
#include "castor/tape/tapeserver/daemon/TapeMessageHandler.hpp"
Daniele Kruse's avatar
Daniele Kruse committed
40
#include "castor/tape/tapeserver/file/File.hpp"
41
#include "castor/tape/tapeserver/TapeBridgeConstants.hpp"
42
#include "castor/utils/utils.hpp"
Steven Murray's avatar
Steven Murray committed
43
#include "common/SmartFd.hpp"
44
#include "rmc_constants.h"
45

Steven Murray's avatar
Steven Murray committed
46
#include <algorithm>
47
#include <errno.h>
48
#include <limits.h>
Steven Murray's avatar
Steven Murray committed
49
50
#include <memory>
#include <signal.h>
51
52
#include <sys/prctl.h>
#include <sys/socket.h>
53
54
55
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
Steven Murray's avatar
Steven Murray committed
56
57
58
59

//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
60
castor::tape::tapeserver::daemon::TapeDaemon::TapeDaemon(
61
62
63
64
  const int argc,
  char **const argv,
  std::ostream &stdOut,
  std::ostream &stdErr,
Victor Kotlyar's avatar
Victor Kotlyar committed
65
  cta::log::Logger &log,
66
  const int netTimeout,
67
  const DriveConfigMap &driveConfigs,
68
  reactor::ZMQReactor &reactor,
69
  cta::server::ProcessCap &capUtils,
70
  const TapeDaemonConfig &tapeDaemonConfig):
71
  cta::server::Daemon(log),
72
73
  m_state(TAPEDAEMON_STATE_RUNNING),
  m_startOfShutdown(0),
74
75
  m_argc(argc),
  m_argv(argv),
76
  m_netTimeout(netTimeout),
77
  m_driveConfigs(driveConfigs),
78
  m_reactor(reactor),
79
  m_capUtils(capUtils),
80
  m_tapeDaemonConfig(tapeDaemonConfig),
81
  m_programName("cta-tapeserverd"),
82
  m_hostName(getHostName()),
83
  m_processForker(NULL),
84
  m_processForkerPid(0),
85
  m_catalogue(NULL),
86
  m_zmqContext(NULL) {
87
88
}

89
90
91
92
93
94
95
96
97
98
99
100
//------------------------------------------------------------------------------
// stateToStr
//------------------------------------------------------------------------------
const char *castor::tape::tapeserver::daemon::TapeDaemon::stateToStr(
  const State state) throw () {
  switch(state) {
  case TAPEDAEMON_STATE_RUNNING     : return "RUNNING";
  case TAPEDAEMON_STATE_SHUTTINGDOWN: return "SHUTTINGDOWN";
  default                           : return "UNKNOWN";
  }
}

101
102
103
//------------------------------------------------------------------------------
// getHostName
//------------------------------------------------------------------------------
104
std::string castor::tape::tapeserver::daemon::TapeDaemon::getHostName() const {
105
106
  char nameBuf[81];
  if(gethostname(nameBuf, sizeof(nameBuf))) {
107
    const std::string message = castor::utils::errnoToString(errno);
108
    cta::exception::Exception ex;
109
    ex.getMessage() << "Failed to get host name: " << message;
110
111
112
113
    throw ex;
  }

  return nameBuf;
Steven Murray's avatar
Steven Murray committed
114
115
116
117
118
119
}

//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::TapeDaemon::~TapeDaemon() throw() {
120
121
122
  if(NULL != m_processForker) {
    m_processForker->stopProcessForker("TapeDaemon destructor called");
    delete m_processForker;
123
  }
124
  if(NULL != m_catalogue) {
Victor Kotlyar's avatar
Victor Kotlyar committed
125
    m_log(cta::log::WARNING,
126
127
128
      "Tape-server parent-process killing any running tape-sessions");
    m_catalogue->killSessions();
  }
129
  delete m_catalogue;
130
  //destroyZmqContext();
131
  google::protobuf::ShutdownProtobufLibrary();
Steven Murray's avatar
Steven Murray committed
132
}
133

Steven Murray's avatar
Steven Murray committed
134
135
136
137
//------------------------------------------------------------------------------
// destroyZmqContext
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::destroyZmqContext() throw() {
138
139
  if(NULL != m_zmqContext) {
    if(zmq_term(m_zmqContext)) {
140
      const std::string message = castor::utils::errnoToString(errno);
Victor Kotlyar's avatar
Victor Kotlyar committed
141
142
      std::list<cta::log::Param> params = {cta::log::Param("message", message)};
      m_log(cta::log::ERR, "Failed to destroy ZMQ context", params);
Steven Murray's avatar
Steven Murray committed
143
144
    } else {
      m_zmqContext = NULL;
Victor Kotlyar's avatar
Victor Kotlyar committed
145
      m_log(cta::log::INFO, "Successfully destroyed ZMQ context");
146
147
    }
  }
Steven Murray's avatar
Steven Murray committed
148
149
150
151
152
}

//------------------------------------------------------------------------------
// main
//------------------------------------------------------------------------------
153
int castor::tape::tapeserver::daemon::TapeDaemon::main() throw() {
Steven Murray's avatar
Steven Murray committed
154
  try {
Steven Murray's avatar
Steven Murray committed
155
    exceptionThrowingMain(m_argc, m_argv);
156
  } catch (cta::exception::Exception &ex) {
157
    // Log the error
Victor Kotlyar's avatar
Victor Kotlyar committed
158
159
160
    std::list<cta::log::Param> params = {
      cta::log::Param("Message", ex.getMessage().str())};
    m_log(cta::log::ERR, "Aborting", params);
Steven Murray's avatar
Steven Murray committed
161

162
    return EXIT_FAILURE;
Steven Murray's avatar
Steven Murray committed
163
  }
164
  return EXIT_SUCCESS;
Steven Murray's avatar
Steven Murray committed
165
166
167
168
169
}

//------------------------------------------------------------------------------
// exceptionThrowingMain
//------------------------------------------------------------------------------
170
void  castor::tape::tapeserver::daemon::TapeDaemon::exceptionThrowingMain(
171
  const int argc, char **const argv)  {
172

173
  if(m_driveConfigs.empty()) {
174
    cta::exception::Exception ex;
175
176
177
178
    ex.getMessage() << "/etc/castor/TPCONFIG is empty";
    throw ex;
  }

179
180
  // Process must be able to change user now and should be permitted to perform
  // raw IO in the future
181
  setProcessCapabilities("cap_setgid,cap_setuid+ep cap_sys_rawio+p");
182

183
  const bool runAsStagerSuperuser = true;
184
  daemonizeIfNotRunInForeground(runAsStagerSuperuser);
185
  setDumpable();
186
187
188
189
190
191
192
193

  // Create two socket pairs for ProcessForker communications
  const ForkerCmdPair cmdPair = createForkerCmdPair();
  const ForkerReaperPair reaperPair = createForkerReaperPair();

  m_processForkerPid = forkProcessForker(cmdPair, reaperPair);

  m_processForker = new ProcessForkerProxySocket(m_log, cmdPair.tapeDaemon);
194
  castor::tape::System::realWrapper sysWrapper;
Steven Murray's avatar
Steven Murray committed
195
196
197
198
199
  m_catalogue = new Catalogue(
    m_netTimeout,
    m_log,
    *m_processForker,
    m_hostName,
200
201
202
    m_tapeDaemonConfig.catalogueConfig,
    sysWrapper
  );
203

204
  m_catalogue->populate(m_driveConfigs);
205

206
  // There is no longer any need for the process to be able to change user,
207
208
  // however the process should still be permitted to perform raw IO in the
  // future
209
210
  setProcessCapabilities("cap_sys_rawio+p");

Steven Murray's avatar
Steven Murray committed
211
  blockSignals();
212
  initZmqContext();
213
  setUpReactor(reaperPair.tapeDaemon);
214
  mainEventLoop();
Steven Murray's avatar
Steven Murray committed
215
216
}

217
218
219
220
221
222
//------------------------------------------------------------------------------
// setDumpable
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::setDumpable() {
  castor::utils::setDumpableProcessAttribute(true);
  const bool dumpable = castor::utils::getDumpableProcessAttribute();
Victor Kotlyar's avatar
Victor Kotlyar committed
223
224
225
  std::list<cta::log::Param> params = {
    cta::log::Param("dumpable", dumpable ? "true" : "false")};
  m_log(cta::log::INFO, "Got dumpable attribute of process", params);
226
  if(!dumpable) {
227
    cta::exception::Exception ex;
228
229
230
231
232
233
234
235
236
237
238
239
    ex.getMessage() << "Failed to set dumpable attribute of process to true";
    throw ex;
  }
}

//------------------------------------------------------------------------------
// setProcessCapabilities
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::setProcessCapabilities(
  const std::string &text) {
  try {
    m_capUtils.setProcText(text);
Victor Kotlyar's avatar
Victor Kotlyar committed
240
241
242
    std::list<cta::log::Param> params =
      {cta::log::Param("capabilities", m_capUtils.getProcText())};
    m_log(cta::log::INFO, "Set process capabilities", params);
243
244
  } catch(cta::exception::Exception &ne) {
    cta::exception::Exception ex;
245
246
247
248
249
250
251
252
253
    ex.getMessage() << "Failed to set process capabilities to '" << text <<
      "': " << ne.getMessage().str();
    throw ex;
  }
}

//------------------------------------------------------------------------------
// forkProcessForker
//------------------------------------------------------------------------------
254
255
pid_t castor::tape::tapeserver::daemon::TapeDaemon::forkProcessForker(
  const ForkerCmdPair &cmdPair, const ForkerReaperPair &reaperPair) {
256
257
258
259
260
261
  m_log.prepareForFork();

  const pid_t forkRc = fork();

  // If fork failed
  if(0 > forkRc) {
262
    const std::string message = castor::utils::errnoToString(errno);
263
264
265
266

    closeForkerCmdPair(cmdPair);
    closeForkerReaperPair(reaperPair);

267
    cta::exception::Exception ex;
268
269
270
271
272
273
    ex.getMessage() << "Failed to fork ProcessForker: " << message;
    throw ex;

  // Else if this is the parent process
  } else if(0 < forkRc) {
    {
Victor Kotlyar's avatar
Victor Kotlyar committed
274
275
276
      std::list<cta::log::Param> params = {
        cta::log::Param("processForkerPid", forkRc)};
      m_log(cta::log::INFO, "Successfully forked the ProcessForker", params);
277
278
    }

279
280
    closeProcessForkerSideOfCmdPair(cmdPair);
    closeProcessForkerSideOfReaperPair(reaperPair);
281

282
    return forkRc;
283
284
285

  // Else this is the child process
  } else {
286
287
    closeTapeDaemonSideOfCmdPair(cmdPair);
    closeTapeDaemonSideOfReaperPair(reaperPair);
288

289
290
    castor::utils::setProcessNameAndCmdLine(m_argv[0], "tpforker");

291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
    exit(runProcessForker(cmdPair.processForker, reaperPair.processForker));
  }
}

//------------------------------------------------------------------------------
// createForkerCmdPair
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::TapeDaemon::ForkerCmdPair
  castor::tape::tapeserver::daemon::TapeDaemon::createForkerCmdPair() {
  ForkerCmdPair cmdPair;

  try {
    const std::pair<int, int> socketPair = createSocketPair();
    cmdPair.tapeDaemon = socketPair.first;
    cmdPair.processForker = socketPair.second;
306
307
  } catch(cta::exception::Exception &ne) {
    cta::exception::Exception ex;
308
309
310
311
312
313
    ex.getMessage() << "Failed to create socket pair to control the"
      " ProcessForker: " << ne.getMessage().str();
    throw ex; 
  }

  {
Victor Kotlyar's avatar
Victor Kotlyar committed
314
315
316
317
    std::list<cta::log::Param> params = {
      cta::log::Param("cmdPair.tapeDaemon", cmdPair.tapeDaemon),
      cta::log::Param("cmdPair.processForker", cmdPair.processForker)};
    m_log(cta::log::INFO, "TapeDaemon parent process succesfully created socket"
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
      " pair to control the ProcessForker", params);
  }

  return cmdPair;
}

//------------------------------------------------------------------------------
// createForkerReaperPair
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::TapeDaemon::ForkerReaperPair
  castor::tape::tapeserver::daemon::TapeDaemon::createForkerReaperPair() {
  ForkerReaperPair reaperPair;

  try {
    const std::pair<int, int> socketPair = createSocketPair();
    reaperPair.tapeDaemon = socketPair.first;
    reaperPair.processForker = socketPair.second;
335
336
  } catch(cta::exception::Exception &ne) {
    cta::exception::Exception ex;
337
338
339
340
341
342
    ex.getMessage() << "Failed to create socket pair for the ProcessForker"
      " to report terminated processes: " << ne.getMessage().str();
    throw ex;
  }

  {
Victor Kotlyar's avatar
Victor Kotlyar committed
343
344
345
346
    std::list<cta::log::Param> params = {
      cta::log::Param("reaperPair.tapeDaemon", reaperPair.tapeDaemon),
      cta::log::Param("reaperPair.processForker", reaperPair.processForker)};
    m_log(cta::log::INFO, "TapeDaemon parent process succesfully created socket"
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
      " pair for ProcessForker to report terminated processes", params);
  }

  return reaperPair;
}

//------------------------------------------------------------------------------
// createSocketPair
//------------------------------------------------------------------------------
std::pair<int, int>
  castor::tape::tapeserver::daemon::TapeDaemon::createSocketPair() {
  int sv[2] = {-1, -1};
  if(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
    char message[100];
    strerror_r(errno, message, sizeof(message));
362
    cta::exception::Exception ex;
363
364
365
366
367
368
369
370
371
372
373
    ex.getMessage() << "Failed to create socket pair: " << message;
    throw ex;
  }

  return std::pair<int, int> (sv[0], sv[1]);
}

//------------------------------------------------------------------------------
// closeForkerCmdPair
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::closeForkerCmdPair(
374
  const ForkerCmdPair &cmdPair) const {
375
  if(close(cmdPair.tapeDaemon)) {
376
    const std::string message = castor::utils::errnoToString(errno);
377
    cta::exception::Exception ex;
378
379
380
381
382
383
    ex.getMessage() << "Failed to close TapeDaemon side of cmdPair"
      ": cmdPair.tapeDaemon=" << cmdPair.tapeDaemon << ": " << message;
    throw ex;
  }

  if(close(cmdPair.processForker)) {
384
    const std::string message = castor::utils::errnoToString(errno);
385
    cta::exception::Exception ex;
386
387
388
389
390
391
392
393
394
395
    ex.getMessage() << "Failed to close ProcessForker side of cmdPair"
      ": cmdPair.processForker=" << cmdPair.processForker << ": " << message;
    throw ex;
  }
}

//------------------------------------------------------------------------------
// closeForkerReaperPair
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::closeForkerReaperPair(
396
  const ForkerReaperPair &reaperPair) const {
397
  if(close(reaperPair.tapeDaemon)) {
398
    const std::string message = castor::utils::errnoToString(errno);
399
    cta::exception::Exception ex;
400
401
402
403
404
405
    ex.getMessage() << "Failed to close TapeDaemon side of reaperPair"
      ": reaperPair.tapeDaemon=" << reaperPair.tapeDaemon << ": " << message;
    throw ex;
  }

  if(close(reaperPair.processForker)) {
406
    const std::string message = castor::utils::errnoToString(errno);
407
    cta::exception::Exception ex;
408
409
410
411
412
413
414
415
416
417
418
    ex.getMessage() << "Failed to close ProcessForker side of reaperPair"
      ": reaperPair.processForker=" << reaperPair.processForker << ": " <<
      message;
    throw ex;
  }
}

//------------------------------------------------------------------------------
// closeProcessForkerSideOfCmdPair
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::
419
  closeProcessForkerSideOfCmdPair(const ForkerCmdPair &cmdPair) const {
420
  if(close(cmdPair.processForker)) {
421
    const std::string message = castor::utils::errnoToString(errno);
422
    cta::exception::Exception ex;
423
424
425
426
427
428
429
430
431
432
433
    ex.getMessage() << "TapeDaemon parent process failed to close"
      " ProcessForker side of cmdPair: cmdPair.processForker=" <<
      cmdPair.processForker << ": " << message;
    throw ex;
  }
}

//------------------------------------------------------------------------------
// closeProcessForkerSideOfReaperPair
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::
434
435
  closeProcessForkerSideOfReaperPair(const ForkerReaperPair &reaperPair)
  const {
436
  if(close(reaperPair.processForker)) {
437
    const std::string message = castor::utils::errnoToString(errno);
438
    cta::exception::Exception ex;
439
440
441
442
443
444
    ex.getMessage() << "TapeDaemon parent process failed to close"
      " ProcessForker side of reaperPair: reaperPair.processForker=" <<
      reaperPair.processForker << ": " << message;
    throw ex;
  }
}
445

446
447
448
449
//------------------------------------------------------------------------------
// closeTapeDaemonSideOfCmdPair
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::
450
  closeTapeDaemonSideOfCmdPair(const ForkerCmdPair &cmdPair) const {
451
  if(close(cmdPair.tapeDaemon)) {
452
    const std::string message = castor::utils::errnoToString(errno);
453
    cta::exception::Exception ex;
454
455
456
457
458
459
460
461
462
463
464
    ex.getMessage() << "ProcessForker process failed to close"
      " TapeDaemon side of cmdPair: cmdPair.tapeDaemon=" << cmdPair.tapeDaemon
      << ": " << message;
    throw ex;
  }
}

//------------------------------------------------------------------------------
// closeTapeDaemonSideOfReaperPair
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::
465
  closeTapeDaemonSideOfReaperPair(const ForkerReaperPair &reaperPair) const {
466
  if(close(reaperPair.tapeDaemon)) {
467
    const std::string message = castor::utils::errnoToString(errno);
468
    cta::exception::Exception ex;
469
470
471
472
    ex.getMessage() << "ProcessForker process failed to close"
      " TapeDaemon side of reaperPair: reaperPair.tapeDaemon=" <<
      reaperPair.tapeDaemon << ": " << message;
    throw ex;
473
474
475
476
477
478
  }
}

//------------------------------------------------------------------------------
// runProcessForker
//------------------------------------------------------------------------------
479
int castor::tape::tapeserver::daemon::TapeDaemon::runProcessForker(
480
  const int cmdReceiverSocket, const int reaperSenderSocket) throw() {
481
  try {
482
    ProcessForker processForker(m_log, cmdReceiverSocket, reaperSenderSocket,
483
      m_hostName, m_argv[0], m_tapeDaemonConfig);
484
    processForker.execute();
485
    return 0;
486
  } catch(cta::exception::Exception &ex) {
Victor Kotlyar's avatar
Victor Kotlyar committed
487
488
    std::list<cta::log::Param> params = {cta::log::Param("message", ex.getMessage().str())};
    m_log(cta::log::ERR, "ProcessForker threw an unexpected exception", params);
489
  } catch(std::exception &se) {
Victor Kotlyar's avatar
Victor Kotlyar committed
490
491
    std::list<cta::log::Param> params = {cta::log::Param("message", se.what())};
    m_log(cta::log::ERR, "ProcessForker threw an unexpected exception", params);
492
  } catch(...) {
Victor Kotlyar's avatar
Victor Kotlyar committed
493
    m_log(cta::log::ERR, "ProcessForker threw an unknown and unexpected exception");
494
  }
495
  return 1;
496
497
}

Steven Murray's avatar
Steven Murray committed
498
499
500
//------------------------------------------------------------------------------
// blockSignals
//------------------------------------------------------------------------------
501
void castor::tape::tapeserver::daemon::TapeDaemon::blockSignals() const {
Steven Murray's avatar
Steven Murray committed
502
503
  sigset_t sigs;
  sigemptyset(&sigs);
504
  // The signals that should not asynchronously disturb the daemon
Steven Murray's avatar
Steven Murray committed
505
506
507
508
509
510
511
512
513
514
515
516
517
518
  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);
519
  cta::exception::Errnum::throwOnNonZero(
Steven Murray's avatar
Steven Murray committed
520
    sigprocmask(SIG_BLOCK, &sigs, NULL),
521
522
523
    "Failed to block signals: sigprocmask() failed");
}

524
525
526
527
528
529
530
//------------------------------------------------------------------------------
// initZmqContext
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::initZmqContext() {
  const int sizeOfIOThreadPoolForZMQ = 1;
  m_zmqContext = zmq_init(sizeOfIOThreadPoolForZMQ);
  if(NULL == m_zmqContext) {
531
    const std::string message = castor::utils::errnoToString(errno);
532
    cta::exception::Exception ex;
533
534
535
536
537
    ex.getMessage() << "Failed to instantiate ZMQ context: " << message;
    throw ex;
  }
}

538
//------------------------------------------------------------------------------
539
// setUpReactor
540
//------------------------------------------------------------------------------
541
542
543
void castor::tape::tapeserver::daemon::TapeDaemon::setUpReactor(
  const int reaperSocket) {
  createAndRegisterProcessForkerConnectionHandler(reaperSocket);
544
  createAndRegisterTapeMessageHandler();
545
546
547
}

//------------------------------------------------------------------------------
548
// createAndRegisterProcessForkerConnectionHandler
549
//------------------------------------------------------------------------------
550
void castor::tape::tapeserver::daemon::TapeDaemon::
551
  createAndRegisterProcessForkerConnectionHandler(const int reaperSocket)  {
552
  try {
553
    std::unique_ptr<ProcessForkerConnectionHandler> handler;
554
555
    try {
      handler.reset(new ProcessForkerConnectionHandler(reaperSocket, m_reactor,
556
        m_log, *m_catalogue));
557
    } catch(std::bad_alloc &ba) {
558
      cta::exception::BadAlloc ex;
559
      ex.getMessage() <<
560
561
        "Failed to create event handler for communicating with the"
        " ProcessForker: " << ba.what();
562
563
564
565
      throw ex;
    }
    m_reactor.registerHandler(handler.get());
    handler.release();
566
567
  } catch(cta::exception::Exception &ne) {
    cta::exception::Exception ex;
568
    ex.getMessage() <<
569
570
      "Failed to create and register ProcessForkerConnectionHandler: " <<
      ne.getMessage().str();
571
    throw ex;
572
  }
573
574
}

575
//------------------------------------------------------------------------------
576
// createAndRegisterTapeMessageHandler
577
578
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::TapeDaemon::
579
  createAndRegisterTapeMessageHandler()  {
580
  try {
581
    std::unique_ptr<TapeMessageHandler> handler;
582
    try {
583
      handler.reset(new TapeMessageHandler(m_tapeDaemonConfig.internalPort,
584
        m_reactor, m_log, *m_catalogue, m_hostName, m_zmqContext));
585
    } catch(std::bad_alloc &ba) {
586
      cta::exception::BadAlloc ex;
587
588
589
590
591
592
593
      ex.getMessage() <<
        "Failed to create event handler for communicating with forked sessions"
        ": " << ba.what();
      throw ex;
    }
    m_reactor.registerHandler(handler.get());
    handler.release();
594
595
  } catch(cta::exception::Exception &ne) {
    cta::exception::Exception ex;
596
    ex.getMessage() <<
597
598
      "Failed to create and register TapeMessageHandler: " <<
      ne.getMessage().str();
599
600
    throw ex;
  }
601
602
}

603
//------------------------------------------------------------------------------
604
// mainEventLoop
605
//------------------------------------------------------------------------------
606
void castor::tape::tapeserver::daemon::TapeDaemon::mainEventLoop() {
607
  while (handleIOEvents() && handleTick() && handlePendingSignals()) {
608
609
610
  }
}

611
//------------------------------------------------------------------------------
612
// handleIOEvents
613
//------------------------------------------------------------------------------
614
bool castor::tape::tapeserver::daemon::TapeDaemon::handleIOEvents() throw() {
615
616
617
  try {
    const int timeout = 100; // 100 milliseconds
    m_reactor.handleEvents(timeout);
618
  } catch(cta::exception::Exception &ex) {
619
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
620
621
622
    std::list<cta::log::Param> params = {
      cta::log::Param("message", ex.getMessage().str()),
      cta::log::Param("backtrace", ex.backtrace())
623
    };
Victor Kotlyar's avatar
Victor Kotlyar committed
624
    m_log(cta::log::ERR, "Unexpected castor exception thrown when handling an I/O"
625
      " event", params);
626
627
  } catch(std::exception &se) {
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
628
629
    std::list<cta::log::Param> params = {cta::log::Param("message", se.what())};
    m_log(cta::log::ERR, "Unexpected exception thrown when handling an I/O event",
630
631
632
      params);
  } catch(...) {
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
633
    m_log(cta::log::ERR,
634
635
636
      "Unexpected and unknown exception thrown when handling an I/O event");
  }

637
  return true; // Continue the main event loop
638
639
640
641
642
643
}

//------------------------------------------------------------------------------
// handleTick
//------------------------------------------------------------------------------
bool castor::tape::tapeserver::daemon::TapeDaemon::handleTick() throw() {
644
  if(m_catalogue->allDrivesAreShutdown()) {
Victor Kotlyar's avatar
Victor Kotlyar committed
645
    m_log(cta::log::WARNING, "Tape-server parent-process ending main loop because"
646
647
648
649
650
651
652
653
654
      " all tape drives are shutdown");
    return false; // Do not continue the main event loop
  }

  if(TAPEDAEMON_STATE_SHUTTINGDOWN == m_state) {
    const time_t now = time(NULL);
    const time_t timeSpentShuttingDown = now - m_startOfShutdown;
    const time_t shutdownTimeout = 9*60; // 9 minutes
    if(shutdownTimeout <= timeSpentShuttingDown) {
Victor Kotlyar's avatar
Victor Kotlyar committed
655
656
      std::list<cta::log::Param> params = {cta::log::Param("shutdownTimeout", shutdownTimeout)};
      m_log(cta::log::WARNING, "Tape-server parent-process ending main loop because"
657
658
659
660
661
        " shutdown timeout has been reached", params);
      return false; // Do not continue the main event loop
    }
  }

662
663
  try {
    return m_catalogue->handleTick();
664
  } catch(cta::exception::Exception &ex) {
665
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
666
667
668
    std::list<cta::log::Param> params = {
      cta::log::Param("message", ex.getMessage().str()),
      cta::log::Param("backtrace", ex.backtrace())
669
    };
Victor Kotlyar's avatar
Victor Kotlyar committed
670
    m_log(cta::log::ERR, "Unexpected castor exception thrown when handling a tick"
671
672
673
      " in time", params);
  } catch(std::exception &se) {
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
674
675
    std::list<cta::log::Param> params = {cta::log::Param("message", se.what())};
    m_log(cta::log::ERR, "Unexpected exception thrown when handling a tick in time",
676
677
678
      params);
  } catch(...) {
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
679
    m_log(cta::log::ERR,
680
681
      "Unexpected and unknown exception thrown when handling a tick in time");
  }
682

683
  return true; // Continue the main event loop
684
685
686
687
688
689
690
}

//------------------------------------------------------------------------------
// handlePendingSignals
//------------------------------------------------------------------------------
bool castor::tape::tapeserver::daemon::TapeDaemon::handlePendingSignals()
  throw() {
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  try {
    int sig = 0;
    sigset_t allSignals;
    siginfo_t sigInfo;
    sigfillset(&allSignals);
    const struct timespec immediateTimeout = {0, 0};

    // While there is a pending signal to be handled
    while (0 < (sig = sigtimedwait(&allSignals, &sigInfo, &immediateTimeout))) {
      const bool continueMainEventLoop = handleSignal(sig, sigInfo);

      if(!continueMainEventLoop) {
        return false;
      }
705
    }
706
  } catch(cta::exception::Exception &ex) {
707
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
708
709
710
    std::list<cta::log::Param> params = {
      cta::log::Param("message", ex.getMessage().str()),
      cta::log::Param("backtrace", ex.backtrace())
711
    };
Victor Kotlyar's avatar
Victor Kotlyar committed
712
    m_log(cta::log::ERR, "Unexpected castor exception thrown when handling a"
713
714
715
      " pending signal", params);
  } catch(std::exception &se) {
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
716
717
    std::list<cta::log::Param> params = {cta::log::Param("message", se.what())};
    m_log(cta::log::ERR, "Unexpected exception thrown when handling a pending signal",
718
719
720
      params);
  } catch(...) {
    // Log exception and continue
Victor Kotlyar's avatar
Victor Kotlyar committed
721
    m_log(cta::log::ERR,
722
      "Unexpected and unknown exception thrown when handling a pending signal");
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
  }

  return true; // Continue the main event loop
}

//------------------------------------------------------------------------------
// handleSignal
//------------------------------------------------------------------------------
bool castor::tape::tapeserver::daemon::TapeDaemon::handleSignal(const int sig,
  const siginfo_t &sigInfo) {
  switch(sig) {
  case SIGINT : return handleSIGINT(sigInfo);
  case SIGTERM: return handleSIGTERM(sigInfo);
  case SIGCHLD: return handleSIGCHLD(sigInfo);
  default:
    {
Victor Kotlyar's avatar
Victor Kotlyar committed
739
740
      std::list<cta::log::Param> params = {cta::log::Param("signal", sig)};
      m_log(cta::log::INFO, "Ignoring signal", params);
741
      return true; // Continue the main event loop
742
743
    }
  }
744
745
746
747
748
749
750
}

//------------------------------------------------------------------------------
// handleSIGINT
//------------------------------------------------------------------------------
bool castor::tape::tapeserver::daemon::TapeDaemon::handleSIGINT(
  const siginfo_t &sigInfo) {
751
  if(TAPEDAEMON_STATE_RUNNING == m_state) {
Victor Kotlyar's avatar
Victor Kotlyar committed
752
    m_log(cta::log::WARNING, "Tape-server parent-process starting shutdown sequence"
753
754
755
756
757
758
      " because SIGINT was received");

    m_state = TAPEDAEMON_STATE_SHUTTINGDOWN;
    m_startOfShutdown = time(NULL);
    m_catalogue->shutdown();
  } else {
Victor Kotlyar's avatar
Victor Kotlyar committed
759
    m_log(cta::log::WARNING, "Tape-server parent-process ignoring SIGINT because the"
760
761
762
763
      " shutdown sequence has already been started");
  }

  return true; // Continue the main event loop
764
}
765

766
767
768
769
770
//------------------------------------------------------------------------------
// handleSIGTERM
//------------------------------------------------------------------------------
bool castor::tape::tapeserver::daemon::TapeDaemon::handleSIGTERM(
  const siginfo_t &sigInfo) {
771
  if(TAPEDAEMON_STATE_RUNNING == m_state) {
Victor Kotlyar's avatar
Victor Kotlyar committed
772
    m_log(cta::log::WARNING, "Tape-server parent-process starting shutdown sequence"
773
774
775
776
777
778
      " because SIGTERM was received");

    m_state = TAPEDAEMON_STATE_SHUTTINGDOWN;
    m_startOfShutdown = time(NULL);
    m_catalogue->shutdown();
  } else {
Victor Kotlyar's avatar
Victor Kotlyar committed
779
    m_log(cta::log::WARNING, "Tape-server parent-process ignoring SIGTERM because the"
780
781
782
783
      " shutdown sequence has already been started");
  }

  return true; // Continue the main event loop
784
785
786
}

//------------------------------------------------------------------------------
787
// handleSIGCHLD
788
//------------------------------------------------------------------------------
789
790
791
bool castor::tape::tapeserver::daemon::TapeDaemon::handleSIGCHLD(
  const siginfo_t &sigInfo) {
  // Reap zombie processes
792
  pid_t pid = 0;
793
  int waitpidStat = 0;
794

795
  while (0 < (pid = waitpid(-1, &waitpidStat, WNOHANG))) {
796
797
798
799
    const bool continueMainEventLoop = handleReapedProcess(pid, waitpidStat);
    if(!continueMainEventLoop) {
      return false;
    }
800
  }
801
802

  return true; // Continue the main event loop
803
804
805
}

//------------------------------------------------------------------------------
806
// handleReapedProcess
807
//------------------------------------------------------------------------------
808
bool castor::tape::tapeserver::daemon::TapeDaemon::handleReapedProcess(
809
810
  const pid_t pid, const int waitpidStat) throw() {
  logChildProcessTerminated(pid, waitpidStat);
811

812
  if(pid == m_processForkerPid) {
813
    return handleReapedProcessForker(pid, waitpidStat);
814
  } else {
Victor Kotlyar's avatar
Victor Kotlyar committed
815
816
    std::list<cta::log::Param> params = {cta::log::Param("pid", pid)};
    m_log(cta::log::ERR, "Reaped process was unknown", params);
817
    return true; // Continue the main event loop
818
819
  }
}
820

821
822
823
//------------------------------------------------------------------------------
// handleReapedProcessForker
//------------------------------------------------------------------------------
824
bool castor::tape::tapeserver::daemon::TapeDaemon::handleReapedProcessForker(
825
  const pid_t pid, const int waitpidStat) throw() {
Victor Kotlyar's avatar
Victor Kotlyar committed
826
827
828
  std::list<cta::log::Param> params = {
    cta::log::Param("processForkerPid", pid)};
  m_log(cta::log::WARNING, "Tape-server parent-process stopping gracefully because"
829
830
    " ProcessForker has terminated", params);
  return false; // Do not continue the main event loop
831
}
832

833
//------------------------------------------------------------------------------
834
// logChildProcessTerminated
835
//------------------------------------------------------------------------------
836
837
void castor::tape::tapeserver::daemon::TapeDaemon::logChildProcessTerminated(
  const pid_t pid, const int waitpidStat) throw() {
Victor Kotlyar's avatar
Victor Kotlyar committed
838
839
  std::list<cta::log::Param> params;
  params.push_back(cta::log::Param("terminatedPid", pid));
840
841

  if(WIFEXITED(waitpidStat)) {
Victor Kotlyar's avatar
Victor Kotlyar committed
842
    params.push_back(cta::log::Param("WEXITSTATUS", WEXITSTATUS(waitpidStat)));
843
844
845
  }

  if(WIFSIGNALED(waitpidStat)) {
Victor Kotlyar's avatar
Victor Kotlyar committed
846
    params.push_back(cta::log::Param("WTERMSIG", WTERMSIG(waitpidStat)));
847
848
849
  }

  if(WCOREDUMP(waitpidStat)) {
Victor Kotlyar's avatar
Victor Kotlyar committed
850
    params.push_back(cta::log::Param("WCOREDUMP", "true"));
851
  } else {
Victor Kotlyar's avatar
Victor Kotlyar committed
852
    params.push_back(cta::log::Param("WCOREDUMP", "false"));
853
854
855
  }

  if(WIFSTOPPED(waitpidStat)) {
Victor Kotlyar's avatar
Victor Kotlyar committed
856
    params.push_back(cta::log::Param("WSTOPSIG", WSTOPSIG(waitpidStat)));
857
858
859
  }

  if(WIFCONTINUED(waitpidStat)) {
Victor Kotlyar's avatar
Victor Kotlyar committed
860
    params.push_back(cta::log::Param("WIFCONTINUED", "true"));
861
  } else {
Victor Kotlyar's avatar
Victor Kotlyar committed
862
    params.push_back(cta::log::Param("WIFCONTINUED", "false"));
863
864
  }

Victor Kotlyar's avatar
Victor Kotlyar committed
865
  m_log(cta::log::INFO, "Child process terminated", params);
866
}