CatalogueDrive.cpp 35.1 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/exception/Exception.hpp"
25
#include "castor/tape/tapeserver/daemon/CatalogueDrive.hpp"
26
#include "castor/tape/tapeserver/daemon/Constants.hpp"
27
#include "castor/tape/tapeserver/daemon/EmptyDriveProbe.hpp"
28
#include "castor/utils/utils.hpp"
29
30
31
#include "Ctape_constants.h"
#include "rmc_constants.h"
#include "serrno.h"
Steven Murray's avatar
Steven Murray committed
32

33
34
#include <errno.h>
#include <signal.h>
Steven Murray's avatar
Steven Murray committed
35
#include <string.h>
36
#include <sys/types.h>
Steven Murray's avatar
Steven Murray committed
37
38
39
40

//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
41
castor::tape::tapeserver::daemon::CatalogueDrive::CatalogueDrive(
42
  const int netTimeout,
43
44
45
  log::Logger &log,
  ProcessForkerProxy &processForker,
  const std::string &hostName,
46
  const DriveConfig &config,
47
  const CatalogueDriveState state,
48
49
  const CatalogueConfig &catalogueConfig,
  System::virtualWrapper &sysWrapper)
Steven Murray's avatar
Steven Murray committed
50
  throw():
51
  m_netTimeout(netTimeout),
52
53
  m_log(log),
  m_processForker(processForker),
54
  m_hostName(hostName),
55
  m_config(config),
56
  m_sysWrapper(sysWrapper),
57
  m_state(state),
58
  m_catalogueConfig(catalogueConfig),
59
  m_session(NULL) {
60
61
62
}

//------------------------------------------------------------------------------
63
// destructor
64
//------------------------------------------------------------------------------
65
castor::tape::tapeserver::daemon::CatalogueDrive::~CatalogueDrive()
66
  throw() {
67
68
69
  deleteSession();
}

70
//------------------------------------------------------------------------------
71
// handleTick
72
//------------------------------------------------------------------------------
73
bool castor::tape::tapeserver::daemon::CatalogueDrive::handleTick() {
74
75
76
77
  // If there is a tape session and it does not want to continue the main event
  // loop
  if(NULL != m_session && !m_session->handleTick()) {
    return false; // Do no continue the main event loop
78
79
  }

80
81
  launchTransferSessionIfNecessary();

82
83
84
  return true; // Continue the main event loop
}

85
86
87
//------------------------------------------------------------------------------
// deleteSession
//------------------------------------------------------------------------------
88
void castor::tape::tapeserver::daemon::CatalogueDrive::deleteSession() {
89
  delete m_session;
90
  m_session = NULL;
Steven Murray's avatar
Steven Murray committed
91
92
}

93
94
95
//------------------------------------------------------------------------------
// getConfig
//------------------------------------------------------------------------------
96
const castor::tape::tapeserver::daemon::DriveConfig
97
  &castor::tape::tapeserver::daemon::CatalogueDrive::getConfig() const {
98
99
100
101
  return m_config;
}

//------------------------------------------------------------------------------
102
103
// getState
//------------------------------------------------------------------------------
104
105
castor::tape::tapeserver::daemon::CatalogueDriveState castor::tape::tapeserver::
  daemon::CatalogueDrive::getState() const throw() {
106
107
108
109
110
  return m_state;
} 

//------------------------------------------------------------------------------
// getSession
111
//------------------------------------------------------------------------------
112
const castor::tape::tapeserver::daemon::CatalogueSession &
113
  castor::tape::tapeserver::daemon::CatalogueDrive::getSession() const {
114
115
116
117
118
119
  try {
    checkForSession();
    return *m_session;
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to get tape session for drive " <<
120
121
      m_config.getUnitName() << " from drive catalogue: " <<
      ne.getMessage().str();
122
123
    throw ex;
  }
124
125
126
}

//------------------------------------------------------------------------------
127
// checkForSession
128
//------------------------------------------------------------------------------
129
void castor::tape::tapeserver::daemon::CatalogueDrive::checkForSession()
130
  const {
131
132
  if(NULL == m_session) {
    castor::exception::Exception ex;
133
    ex.getMessage() << "Drive is currently not running a session: state=" <<
134
      catalogueDriveStateToStr(m_state);
135
136
    throw ex;
  }
137
138
139
}

//------------------------------------------------------------------------------
140
// getCleanerSession
141
//------------------------------------------------------------------------------
142
143
const castor::tape::tapeserver::daemon::CatalogueCleanerSession &
  castor::tape::tapeserver::daemon::CatalogueDrive::getCleanerSession()
144
145
146
  const {
  try {
    checkForCleanerSession();
147
148
    const CatalogueCleanerSession *const cleanerSession =
      dynamic_cast<const CatalogueCleanerSession*>(m_session);
149
150
151
152
    if(NULL == cleanerSession) {
      // Should never get here
      castor::exception::Exception ex;
      ex.getMessage() <<
153
        "Failed to cast session to CatalogueCleanerSession";
154
155
156
157
158
159
160
      throw ex;
    }

    return *cleanerSession;
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to get cleaner session for drive " <<
161
162
      m_config.getUnitName() << " from drive catalogue: " <<
      ne.getMessage().str();
163
164
165
    throw ex;
  }
}
166

Daniele Kruse's avatar
Daniele Kruse committed
167
//------------------------------------------------------------------------------
168
// getCleanerSession
Daniele Kruse's avatar
Daniele Kruse committed
169
//------------------------------------------------------------------------------
170
171
castor::tape::tapeserver::daemon::CatalogueCleanerSession &
  castor::tape::tapeserver::daemon::CatalogueDrive::getCleanerSession() {
172
173
  try {
    checkForCleanerSession();
174
175
    CatalogueCleanerSession *const cleanerSession =
      dynamic_cast<CatalogueCleanerSession*>(m_session);
176
177
178
179
    if(NULL == cleanerSession) {
      // Should never get here
      castor::exception::Exception ex;
      ex.getMessage() << 
180
        "Failed to cast session to CatalogueCleanerSession";
181
182
183
184
185
186
187
      throw ex;
    }

    return *cleanerSession;
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to get cleaner session for drive " <<
188
189
      m_config.getUnitName() << " from drive catalogue: " <<
      ne.getMessage().str();
190
191
    throw ex;
  }
Daniele Kruse's avatar
Daniele Kruse committed
192
193
194
}

//------------------------------------------------------------------------------
195
// checkForCleanerSession
Daniele Kruse's avatar
Daniele Kruse committed
196
//------------------------------------------------------------------------------
197
void castor::tape::tapeserver::daemon::CatalogueDrive::
198
  checkForCleanerSession() const {
199
  checkForSession();
200
201
  const CatalogueSession::Type sessionType = m_session->getType();
  if(CatalogueSession::SESSION_TYPE_CLEANER != sessionType) {
202
203
204
    castor::exception::Exception ex;
    ex.getMessage() <<
      "Session associated with drive is not a cleaner session"
205
      ": actual=" << CatalogueSession::sessionTypeToStr(sessionType);
206
207
208
209
210
211
212
    throw ex;
  }
}

//------------------------------------------------------------------------------
// getLabelSession
//------------------------------------------------------------------------------
213
214
const castor::tape::tapeserver::daemon::CatalogueLabelSession &
  castor::tape::tapeserver::daemon::CatalogueDrive::getLabelSession()
Daniele Kruse's avatar
Daniele Kruse committed
215
  const {
216
217
  try {
    checkForLabelSession();
218
219
    const CatalogueLabelSession *const labelSession =
      dynamic_cast<const CatalogueLabelSession*>(m_session);
220
221
222
    if(NULL == labelSession) {
      // Should never get here
      castor::exception::Exception ex;
223
      ex.getMessage() << "Failed to cast session to CatalogueLabelSession";
224
225
226
227
228
      throw ex;
    }

    return *labelSession;
  } catch(castor::exception::Exception &ne) {
Daniele Kruse's avatar
Daniele Kruse committed
229
    castor::exception::Exception ex;
230
    ex.getMessage() << "Failed to get label session for drive " <<
231
232
      m_config.getUnitName() << " from drive catalogue: " <<
      ne.getMessage().str();
233
234
235
236
237
238
239
    throw ex;
  }
}

//------------------------------------------------------------------------------
// getLabelSession
//------------------------------------------------------------------------------
240
241
castor::tape::tapeserver::daemon::CatalogueLabelSession &
  castor::tape::tapeserver::daemon::CatalogueDrive::getLabelSession() {
242
243
  try {
    checkForLabelSession();
244
245
    CatalogueLabelSession *const labelSession =
      dynamic_cast<CatalogueLabelSession*>(m_session);
246
247
248
    if(NULL == labelSession) {
      // Should never get here
      castor::exception::Exception ex;
249
      ex.getMessage() << "Failed to cast session to CatalogueLabelSession";
250
251
252
253
254
255
256
      throw ex;
    }

    return *labelSession;
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to get label session for drive " <<
257
258
      m_config.getUnitName() << " from drive catalogue: " <<
      ne.getMessage().str();
259
260
261
262
263
264
265
    throw ex;
  }
}

//------------------------------------------------------------------------------
// checkForLabelSession
//------------------------------------------------------------------------------
266
void castor::tape::tapeserver::daemon::CatalogueDrive::
267
  checkForLabelSession() const {
268
  checkForSession();
269
270
  const CatalogueSession::Type sessionType = m_session->getType();
  if(CatalogueSession::SESSION_TYPE_LABEL != sessionType) {
271
272
    castor::exception::Exception ex;
    ex.getMessage() << "Session associated with drive is not a label session"
273
      ": actual=" << CatalogueSession::sessionTypeToStr(sessionType);
274
275
276
277
278
279
280
    throw ex;
  }
}

//------------------------------------------------------------------------------
// getTransferSession
//------------------------------------------------------------------------------
281
282
const castor::tape::tapeserver::daemon::CatalogueTransferSession &
  castor::tape::tapeserver::daemon::CatalogueDrive::getTransferSession()
283
284
285
  const {
  try {
    checkForTransferSession();
286
287
    CatalogueTransferSession *const transferSession =
      dynamic_cast<CatalogueTransferSession*>(m_session);
288
289
290
291
    if(NULL == transferSession) {
      // Should never get here
      castor::exception::Exception ex;
      ex.getMessage() <<
292
        "Failed to cast session to CatalogueTransferSession";
293
294
295
296
297
298
299
      throw ex;
    }

    return *transferSession;
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to get transfer session for drive " <<
300
301
      m_config.getUnitName() << " from drive catalogue: " <<
      ne.getMessage().str();
302
303
304
305
306
307
308
    throw ex;
  }
}

//------------------------------------------------------------------------------
// getTransferSession
//------------------------------------------------------------------------------
309
310
castor::tape::tapeserver::daemon::CatalogueTransferSession &
  castor::tape::tapeserver::daemon::CatalogueDrive::getTransferSession() {
311
312
  try {
    checkForTransferSession();
313
314
    CatalogueTransferSession *const transferSession =
      dynamic_cast<CatalogueTransferSession*>(m_session);
315
316
317
318
    if(NULL == transferSession) {
      // Should never get here
      castor::exception::Exception ex;
      ex.getMessage() <<
319
        "Failed to cast session to CatalogueTransferSession";
320
321
322
323
324
325
326
      throw ex;
    }

    return *transferSession;
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to get transfer session for drive " <<
327
328
      m_config.getUnitName() << " from drive catalogue: " <<
      ne.getMessage().str();
329
330
331
332
333
334
335
    throw ex;
  }
}

//------------------------------------------------------------------------------
// checkForTransferSession
//------------------------------------------------------------------------------
336
void castor::tape::tapeserver::daemon::CatalogueDrive::
337
  checkForTransferSession() const {
338
  checkForSession();
339
340
  const CatalogueSession::Type sessionType = m_session->getType();
  if(CatalogueSession::SESSION_TYPE_TRANSFER != sessionType) {
341
342
343
    castor::exception::Exception ex;
    ex.getMessage() <<
      "Session associated with drive is not a transfer session"
344
      ": actual=" << CatalogueSession::sessionTypeToStr(sessionType);
345
346
    throw ex;
  }
347
348
349
350
351
}

//------------------------------------------------------------------------------
// configureUp
//------------------------------------------------------------------------------
352
void castor::tape::tapeserver::daemon::CatalogueDrive::configureUp() {
353
354
  switch(m_state) {
  case DRIVE_STATE_UP:
355
  case DRIVE_STATE_RUNNING:
356
    // This state transition is idempotent
357
358
    break;
  case DRIVE_STATE_DOWN:
359
    transitionFromDownToUp();
360
361
    break;
  case DRIVE_STATE_WAITDOWN:
362
363
    // Leave state in vdqm as RUNNING, just update internally state to reflect
    // the  future intention of transitioning to DOWN
364
    changeState(DRIVE_STATE_RUNNING);
365
366
367
368
    break;
  default:
    {
      castor::exception::Exception ex;
369
370
      ex.getMessage() << "Failed to configure tape-drive " <<
        m_config.getUnitName() << " up: Incompatible drive state: state=" <<
371
        catalogueDriveStateToStr(m_state);
372
373
374
      throw ex;
    }
  }
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
}

//-----------------------------------------------------------------------------
// transitionFromDownToUp
//-----------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
  transitionFromDownToUp() {
  checkDriveIsEmpty();
  changeState(DRIVE_STATE_UP);
}

//-----------------------------------------------------------------------------
// checkDriveIsEmpty
//-----------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::checkDriveIsEmpty() {
390
  EmptyDriveProbe probe(m_log, m_config, m_sysWrapper);
391

392
  if(!probe.driveIsEmpty()) {
393
    castor::exception::Exception ex(ETDRVNOTREADYFORMNT);
394
395
396
    ex.getMessage() << "Drive " << m_config.getUnitName() << " is not empty";
    throw ex;
  }
397
398
399
400
401
}

//-----------------------------------------------------------------------------
// configureDown
//-----------------------------------------------------------------------------
402
void castor::tape::tapeserver::daemon::CatalogueDrive::configureDown() {
403
404
  switch(m_state) {
  case DRIVE_STATE_DOWN:
405
  case DRIVE_STATE_WAITDOWN:
406
407
    break;
  case DRIVE_STATE_UP:
408
    changeState(DRIVE_STATE_DOWN);
409
    break;
410
  case DRIVE_STATE_RUNNING:
411
    changeState(DRIVE_STATE_WAITDOWN);
412
413
414
415
    break;
  default:
    {
      castor::exception::Exception ex;
416
417
      ex.getMessage() << "Failed to configure tape drive " <<
        m_config.getUnitName() << " down: Incompatible drive state: state=" <<
418
        catalogueDriveStateToStr(m_state);
419
420
421
422
423
424
      throw ex;
    }
  }
}

//-----------------------------------------------------------------------------
425
426
427
428
429
430
// launchTransferSessionIfNecessary
//-----------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
  launchTransferSessionIfNecessary() {

  // Simply return if it's too soon to launch a transfer session
431
432
  if((double)m_catalogueConfig.transferSessionTimerSecs >
    m_launchTransferSessionTimer.secs()) {
433
434
435
436
437
438
439
440
441
442
443
444
445
446
    return;
  }

  m_launchTransferSessionTimer.reset();

  if(DRIVE_STATE_UP == m_state) {
    changeState(DRIVE_STATE_RUNNING);
    {
      CatalogueTransferSession *const transferSession =
        CatalogueTransferSession::create(
          m_log,
          m_netTimeout,
          m_config,
          m_hostName,
447
448
449
          m_catalogueConfig.waitJobTimeoutSecs,
          m_catalogueConfig.mountTimeoutSecs,
          m_catalogueConfig.blockMoveTimeoutSecs,
450
451
452
453
454
455
456
          m_processForker);
      m_session = dynamic_cast<CatalogueSession *>(transferSession);
    }
  }
}

//-----------------------------------------------------------------------------
457
458
// receivedLabelJob
//-----------------------------------------------------------------------------
459
void castor::tape::tapeserver::daemon::CatalogueDrive::receivedLabelJob(
460
461
462
463
  const legacymsg::TapeLabelRqstMsgBody &job, const int labelCmdConnection) {
  const std::string unitName(job.drive);

  std::ostringstream task;
464
  task << "handle label job for tape drive " << m_config.getUnitName();
465
466

  // Sanity check
467
  if(job.drive != m_config.getUnitName()) {
468
469
470
471
    // Should never happen
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to " << task.str() <<
      ": unit name mismatch: job.drive=" << job.drive << 
472
      " m_config.getUnitName()=" << m_config.getUnitName();
473
474
475
476
477
    throw ex;
  }

  switch(m_state) {
  case DRIVE_STATE_UP:
478
    if(std::string(job.logicalLibrary) != m_config.getLogicalLibrary()) {
479
480
      castor::exception::Exception ex;
      ex.getMessage() << "Failed to " << task.str() <<
481
482
        ": logicalLibrary mismatch: catalogueLogicalLibrary=" << m_config.getLogicalLibrary() << " labelJobDgn="
        << job.logicalLibrary;
483
484
      throw ex;
    }
485
486
487
488
489
490
491
    m_session = CatalogueLabelSession::create(
      m_log,
      m_netTimeout,
      m_config,
      job,
      labelCmdConnection,
      m_processForker);
492
    changeState(DRIVE_STATE_RUNNING);
493
494
495
496
497
    break;
  default:
    {
      castor::exception::Exception ex;
      ex.getMessage() << "Failed to " << task.str() <<
498
499
        ": Incompatible drive state: state=" <<
        catalogueDriveStateToStr(m_state);
500
501
502
503
504
      throw ex;
    }
  }
}

505
//-----------------------------------------------------------------------------
506
// createCleaner
507
//-----------------------------------------------------------------------------
508
509
castor::tape::tapeserver::daemon::CatalogueCleanerSession
  *castor::tape::tapeserver::daemon::CatalogueDrive::createCleaner(
510
  const std::string &vid, const time_t assignmentTime,
511
512
  const bool waitMediaInDrive,
  const uint32_t waitMediaInDriveTimeout) const {
513
  try {
514
    return CatalogueCleanerSession::create(
515
      m_log,
516
      m_netTimeout,
517
      m_config,
518
      m_processForker,
519
      vid,
520
      assignmentTime,
521
522
      waitMediaInDrive,
      waitMediaInDriveTimeout);
523
524
525
526
527
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to create cleaner session: " <<
      ne.getMessage().str();
    throw ex;
528
  } 
529
530
}

531
532
533
//-----------------------------------------------------------------------------
// sessionSucceeded
//-----------------------------------------------------------------------------
534
void castor::tape::tapeserver::daemon::CatalogueDrive::
535
  sessionSucceeded() {
536
  std::unique_ptr<CatalogueSession> session(m_session);
537
538
  m_session = NULL;

539
  switch(m_state) {
540
  case DRIVE_STATE_RUNNING:
541
    changeState(DRIVE_STATE_UP);
542
    session->sessionSucceeded();
543
544
    break;
  case DRIVE_STATE_WAITDOWN:
545
    changeState(DRIVE_STATE_DOWN);
546
    session->sessionSucceeded();
547
    break;
548
  case DRIVE_STATE_WAITSHUTDOWNKILL:
549
550
551
    changeState(DRIVE_STATE_SHUTDOWN);
    session->sessionSucceeded();
    break;
552
553
  case DRIVE_STATE_WAITSHUTDOWNCLEANER:
    changeState(DRIVE_STATE_SHUTDOWN);
554
    session->sessionSucceeded();
555
556
557
558
559
560
    break;
  default:
    {
      castor::exception::Exception ex;
      ex.getMessage() <<
        "Failed to record tape session succeeded for session with pid " <<
561
        getSession().getPid() << ": Incompatible drive state: state=" <<
562
        catalogueDriveStateToStr(m_state);
563
564
565
566
567
568
      throw ex;
    }
  }
}

//-----------------------------------------------------------------------------
569
// sessionFailedAndRequestedDriveDown
570
//-----------------------------------------------------------------------------
571
572
void castor::tape::tapeserver::daemon::CatalogueDrive::
  sessionFailedAndRequestedDriveDown() {
573
  switch(m_state) {
574
  case DRIVE_STATE_RUNNING:
575
  case DRIVE_STATE_WAITDOWN:
576
  case DRIVE_STATE_WAITSHUTDOWNKILL:
577
    return runningSessionFailedAndRequestedDriveDown();
578
579
580
581
582
583
584
585
  case DRIVE_STATE_WAITSHUTDOWNCLEANER:
    return cleanerOfShutdownFailed();
  default:
    {
      castor::exception::Exception ex;
      ex.getMessage() <<
        "Failed to record tape session failed for session with pid " <<
        getSession().getPid() << ": Incompatible drive state: state=" <<
586
        catalogueDriveStateToStr(m_state);
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
      delete m_session;
      m_session = NULL;
      throw ex;
    }
  }
}

//-----------------------------------------------------------------------------
// sessionKilled
//-----------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
sessionKilled(uint32_t signal) {
  switch(m_state) {
  case DRIVE_STATE_RUNNING:
  case DRIVE_STATE_WAITDOWN:
    return runningSessionKilled(signal);
603
  case DRIVE_STATE_WAITSHUTDOWNKILL:
604
    return sessionKilledByShutdown();
605
  case DRIVE_STATE_WAITSHUTDOWNCLEANER:
606
    return cleanerOfShutdownFailed();
607
608
609
610
611
  default:
    {
      castor::exception::Exception ex;
      ex.getMessage() <<
        "Failed to record tape session failed for session with pid " <<
612
        getSession().getPid() << ": Incompatible drive state: state=" <<
613
        catalogueDriveStateToStr(m_state);
Daniele Kruse's avatar
Daniele Kruse committed
614
615
      delete m_session;
      m_session = NULL;
616
617
618
619
620
      throw ex;
    }
  }
}

621
//------------------------------------------------------------------------------
622
// runningSessionFailedAndRequestedDriveDown
623
//------------------------------------------------------------------------------
624
625
void castor::tape::tapeserver::daemon::CatalogueDrive::
  runningSessionFailedAndRequestedDriveDown() {
626
  std::unique_ptr<CatalogueSession> session(m_session);
627
628
  m_session = NULL;
  session->sessionFailed();
629
630
631
632
633
634
635
636
  changeState(DRIVE_STATE_DOWN);
}

//------------------------------------------------------------------------------
// runningSessionKilled
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
runningSessionKilled(uint32_t signal) {
637
  std::unique_ptr<CatalogueSession> session(m_session);
638
639
  m_session = NULL;
  session->sessionKilled(signal);
640
641

  if(CatalogueSession::SESSION_TYPE_CLEANER != session->getType()) {
642
643
    const uint32_t waitMediaInDriveTimeout = 60;
    const bool waitMediaInDrive = true;
644
    m_session = createCleaner(session->getVid(), session->getAssignmentTime(),
645
      waitMediaInDrive, waitMediaInDriveTimeout);
646
647
648
649
650
  } else {
    changeState(DRIVE_STATE_DOWN);
  }
}

651

652
653
654
655
656
//------------------------------------------------------------------------------
// sessionKilledByShutdown
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
  sessionKilledByShutdown() {
657
  std::unique_ptr<CatalogueSession> session(m_session);
658
659
660
661
  m_session = NULL;
  session->sessionFailed();

  changeState(DRIVE_STATE_WAITSHUTDOWNCLEANER);
662
663
  const uint32_t waitMediaInDriveTimeout = 60;
  const bool waitMediaInDrive = true;
664
  m_session = createCleaner(session->getVid(), session->getAssignmentTime(),
665
    waitMediaInDrive, waitMediaInDriveTimeout);
666
667
668
669
670
671
672
}

//------------------------------------------------------------------------------
// cleanerOfShutdownFailed
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
  cleanerOfShutdownFailed() {
673
  std::unique_ptr<CatalogueSession> session(m_session);
674
675
676
677
678
679
680
  m_session = NULL;
  session->sessionFailed();

  // Cleaner failed, no more can be done, mark drive as shutdown
  changeState(DRIVE_STATE_SHUTDOWN);
}

681
//------------------------------------------------------------------------------
682
// sessionFailedAndRequestedCleaner
683
684
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
685
  sessionFailedAndRequestedCleaner() {
686
687
688
689
690
691
692
693
694
695
  switch(m_state) {
  case DRIVE_STATE_RUNNING:
  case DRIVE_STATE_WAITDOWN:
    return runningSessionFailedAndRequestedCleaner();
  default:
    {
      castor::exception::Exception ex;
      ex.getMessage() <<
        "Failed to record tape session failed for session with pid " <<
        getSession().getPid() << ": Incompatible drive state: state=" <<
696
        catalogueDriveStateToStr(m_state);
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
      delete m_session;
      m_session = NULL;
      throw ex;
    }
  }
}

//------------------------------------------------------------------------------
// runningSessionFailedAndRequestedCleaner
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::
  runningSessionFailedAndRequestedCleaner() {
  {
    const pid_t sessionPid = m_session->getPid();
    const CatalogueSession::Type sessionType = m_session->getType();
    const char *sessionTypeStr =
      CatalogueSession::sessionTypeToStr(sessionType);

    std::list<log::Param> params;
716
    params.push_back(log::Param("unitName", m_config.getUnitName()));
717
718
719
720
721
722
    params.push_back(log::Param("sessionType", sessionTypeStr));
    params.push_back(log::Param("sessionPid", sessionPid));

    m_log(LOG_INFO, "Failed session has requested cleaner", params);
  }

723
  std::unique_ptr<CatalogueSession> session(m_session);
724
725
726
727
  m_session = NULL;
  session->sessionFailed();

  if(CatalogueSession::SESSION_TYPE_CLEANER != session->getType()) {
728
729
    const uint32_t waitMediaInDriveTimeout = 60;
    const bool waitMediaInDrive = true;
730
    m_session = createCleaner(session->getVid(), session->getAssignmentTime(),
731
      waitMediaInDrive, waitMediaInDriveTimeout);
732
733
734
735
736
  } else {
    changeState(DRIVE_STATE_DOWN);
  }
}

737
738
739
740
//------------------------------------------------------------------------------
// getTapeStatDriveEntry
//------------------------------------------------------------------------------
castor::legacymsg::TapeStatDriveEntry
741
  castor::tape::tapeserver::daemon::CatalogueDrive::getTapeStatDriveEntry()
742
743
744
745
  const {
  legacymsg::TapeStatDriveEntry entry;

  try {
746
747
    entry.uid = getUidForTapeStatDriveEntry();
    entry.jid = getJidForTapeStatDriveEntry();
748
    castor::utils::copyString(entry.logicalLibrary, m_config.getLogicalLibrary());
749
750
751
    entry.up = getUpForTapeStatDriveEntry();
    entry.asn = getAsnForTapeStatDriveEntry();
    entry.asn_time = getAsnTimeForTapeStatDriveEntry();
752
    castor::utils::copyString(entry.drive, m_config.getUnitName());
753
754
755
756
    entry.mode = getModeForTapeStatDriveEntry();
    castor::utils::copyString(entry.lblcode,
      getLblCodeForTapeStatDriveEntry().c_str());
    entry.tobemounted = getToBeMountedForTapeStatDriveEntry();
757
758
    castor::utils::copyString(entry.vid, getVidForTapeStatDriveEntry());
    castor::utils::copyString(entry.vsn, getVsnForTapeStatDriveEntry());
759
760
761
762
763
764
765
766
767
768
769
770
771
772
    entry.cfseq = 0; // the fseq is ignored by tpstat, so we leave it set to 0
  } catch(castor::exception::Exception &ne) {
    castor::exception::Exception ex;
    ex.getMessage() << "Failed to get TapeStatDriveEntry: " <<
      ne.getMessage().str();
    throw ex;
  }

  return entry;
}

//------------------------------------------------------------------------------
// getUidForTapeStatDriveEntry
//------------------------------------------------------------------------------
773
uint32_t castor::tape::tapeserver::daemon::CatalogueDrive::
774
775
  getUidForTapeStatDriveEntry() const throw() {
  switch(m_state) {
776
  case DRIVE_STATE_RUNNING:
777
778
779
780
781
782
783
784
785
786
  case DRIVE_STATE_WAITDOWN:
    return geteuid();
  default:
    return 0;
  }
}

//------------------------------------------------------------------------------
// getJidForTapeStatDriveEntry
//------------------------------------------------------------------------------
787
uint32_t castor::tape::tapeserver::daemon::CatalogueDrive::
788
  getJidForTapeStatDriveEntry() const throw() {
789
  try {
790
    return getSession().getPid();
791
  } catch(...) {
792
793
794
795
796
797
798
    return 0;
  }
}

//------------------------------------------------------------------------------
// getUpForTapeStatDriveEntry
//------------------------------------------------------------------------------
799
uint16_t castor::tape::tapeserver::daemon::CatalogueDrive::
800
801
802
  getUpForTapeStatDriveEntry() const throw() {
  switch(m_state) {
  case DRIVE_STATE_UP:
803
  case DRIVE_STATE_RUNNING:
804
805
806
807
808
809
810
811
812
    return 1;
  default:
    return 0;
  }
}

//------------------------------------------------------------------------------
// getAsnForTapeStatDriveEntry
//------------------------------------------------------------------------------
813
uint16_t castor::tape::tapeserver::daemon::CatalogueDrive::
814
815
  getAsnForTapeStatDriveEntry() const throw() {
  switch(m_state) {
816
  case DRIVE_STATE_RUNNING:
817
818
819
820
821
822
823
824
825
826
  case DRIVE_STATE_WAITDOWN:
    return 1;
  default:
    return 0;
  }
}

//------------------------------------------------------------------------------
// getAsnTimeForTapeStatDriveEntry
//------------------------------------------------------------------------------
827
uint32_t castor::tape::tapeserver::daemon::CatalogueDrive::
828
  getAsnTimeForTapeStatDriveEntry() const throw() {
829
830
831
  try {
    return getSession().getAssignmentTime();
  } catch(...) {
832
833
    return 0;
  }
834
835
836
837
838
}

//------------------------------------------------------------------------------
// getModeForTapeStatDriveEntry
//------------------------------------------------------------------------------
839
uint16_t castor::tape::tapeserver::daemon::CatalogueDrive::
840
  getModeForTapeStatDriveEntry() const throw() {
841
842
843
  try {
    return getSession().getMode();
  } catch(...) {
844
845
846
847
848
849
850
    return WRITE_DISABLE;
  }
}

//------------------------------------------------------------------------------
// getLblCodeForTapeStatDriveEntry
//------------------------------------------------------------------------------
851
std::string castor::tape::tapeserver::daemon::CatalogueDrive::
852
853
854
855
856
857
858
  getLblCodeForTapeStatDriveEntry() const throw() {
  return "aul";
}

//------------------------------------------------------------------------------
// getToBeMountedForTapeStatDriveEntry
//------------------------------------------------------------------------------
859
uint16_t castor::tape::tapeserver::daemon::CatalogueDrive::
860
  getToBeMountedForTapeStatDriveEntry() const throw() {
861
862
863
  try {
    return getSession().tapeIsBeingMounted();
  } catch(...) {
864
865
    return 0;
  }
866
867
868
869
870
}

//------------------------------------------------------------------------------
// getVidForTapeStatDriveEntry
//------------------------------------------------------------------------------
871
std::string castor::tape::tapeserver::daemon::CatalogueDrive::
872
  getVidForTapeStatDriveEntry() const throw() {
873
874
875
  try {
    return getSession().getVid();
  } catch(...) {
876
877
    return "";
  }
878
879
}

880
881
882
//------------------------------------------------------------------------------
// getAssignmentTimeForCleaner
//------------------------------------------------------------------------------
883
time_t castor::tape::tapeserver::daemon::CatalogueDrive::
884
885
886
887
888
889
890
891
  getAssignmentTimeForCleaner() const throw() {
  try {
    return getSession().getAssignmentTime();
  } catch(...) {
    return 0;
  }
}

892
//------------------------------------------------------------------------------
893
// shutdown
894
//------------------------------------------------------------------------------
895
896
897
898
void castor::tape::tapeserver::daemon::CatalogueDrive::shutdown() {
  // If there is no running session
  if(NULL == m_session) {

899
    changeState(DRIVE_STATE_WAITSHUTDOWNCLEANER);
900

901
902
903
    // Create a cleaner process to make 100% sure the tape drive is empty
    const std::string vid = ""; // Empty string means VID is not known
    const time_t assignmentTime = time(NULL);
904
905
906
907
908
    
    const uint32_t waitMediaInDriveTimeout = 0;
    const bool waitMediaInDrive = false;
    m_session = createCleaner(vid, assignmentTime,
      waitMediaInDrive, waitMediaInDriveTimeout);
909

910
911
  // Else there is a running session
  } else {
912

913
914
    // If the running session is a cleaner
    if(CatalogueSession::SESSION_TYPE_CLEANER == m_session->getType()) {
915

916
917
918
919
920
921
922
923
924
925
926
927
928
      changeState(DRIVE_STATE_WAITSHUTDOWNCLEANER);

    // Else the running session is not a cleaner
    } else {

      changeState(DRIVE_STATE_WAITSHUTDOWNKILL);

      const pid_t sessionPid = m_session->getPid();
      const CatalogueSession::Type sessionType = m_session->getType();
      const char *sessionTypeStr =
        CatalogueSession::sessionTypeToStr(sessionType);

      std::list<log::Param> params;
929
      params.push_back(log::Param("unitName", m_config.getUnitName()));
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
      params.push_back(log::Param("sessionType", sessionTypeStr));
      params.push_back(log::Param("sessionPid", sessionPid));

      // Kill the non-cleaner session
      if(kill(sessionPid, SIGKILL)) {
        const std::string message = castor::utils::errnoToString(errno);
        params.push_back(log::Param("message", message));
        m_log(LOG_ERR, "Failed to kill non-cleaner session whilst shutting"
          " down", params);

        // Nothing else can be done, mark drive as shutdown
        changeState(DRIVE_STATE_SHUTDOWN);

      } else {
        m_log(LOG_WARNING, "Sent SIGKILL to tape-session child-process",
          params);
      }

    } // Else the running session is not a cleaner

  } // Else there is a running session
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
}

//------------------------------------------------------------------------------
// killSession
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::killSession() {
  // Do nothing if there is no running session
  if(NULL == m_session) {
    return;
  }

  const pid_t sessionPid = m_session->getPid();
  const CatalogueSession::Type sessionType = m_session->getType();
  const char *sessionTypeStr =
    CatalogueSession::sessionTypeToStr(sessionType);

  std::list<log::Param> params;
968
  params.push_back(log::Param("unitName", m_config.getUnitName()));
969
970
  params.push_back(log::Param("sessionType", sessionTypeStr));
  params.push_back(log::Param("sessionPid", sessionPid));
971
  params.push_back(log::Param("TPVID", m_session->getVid()));
972
973
974
975
976
977
978

  if(kill(sessionPid, SIGKILL)) {
    const std::string errorStr = castor::utils::errnoToString(errno);
    castor::exception::Exception ex;
    ex.getMessage() << "CatalogueDrive failed to kill session: sessionPid=" <<
     sessionPid << " sessionType=" << sessionTypeStr << ": " << errorStr;
    throw ex;
979
  }
980
981
982
983
  m_log(LOG_WARNING, "Killed tape-session child-process", params);
  delete m_session;
  m_session = NULL;
  changeState(DRIVE_STATE_DOWN);
984
985
}

986
987
988
//------------------------------------------------------------------------------
// getVsnForTapeStatDriveEntry
//------------------------------------------------------------------------------
989
std::string castor::tape::tapeserver::daemon::CatalogueDrive::
990
991
992
  getVsnForTapeStatDriveEntry() const throw() {
  return getVidForTapeStatDriveEntry();
}
993
994
995
996
997

//------------------------------------------------------------------------------
// changeState
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CatalogueDrive::changeState(
998
  const CatalogueDriveState newState) throw() {
999
  std::list<log::Param> params;
1000
  params.push_back(log::Param("unitName", m_config.getUnitName()));