XrdCtaFile.cpp 118 KB
Newer Older
1
/*
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 * The CERN Tape Archive (CTA) project
 * Copyright (C) 2015  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 3 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, see <http://www.gnu.org/licenses/>.
 */

19
#include "scheduler/SchedulerDatabase.hpp"
20
#include "xroot_plugins/XrdCtaFile.hpp"
Daniele Kruse's avatar
Daniele Kruse committed
21

22
#include "XrdSec/XrdSecEntity.hh"
23

24
#include "catalogue/TapeFileSearchCriteria.hpp"
25
#include "common/Configuration.hpp"
26
#include "common/utils/utils.hpp"
27
#include "common/utils/GetOptThreadSafe.hpp"
28
29
30
#include "common/exception/UserError.hpp"
#include "common/exception/NonRetryableError.hpp"
#include "common/exception/RetryableError.hpp"
31

32
33
#include <cryptopp/base64.h>
#include <cryptopp/osrng.h>
34
#include <iomanip>
35
#include <iostream>
36
#include <memory>
37
#include <pwd.h>
38
39
#include <sstream>
#include <string>
40
#include <time.h>
41

42
43
namespace cta { namespace xrootPlugins {

44
45
46
//------------------------------------------------------------------------------
// checkClient
//------------------------------------------------------------------------------
47
void XrdCtaFile::checkClient(const XrdSecEntity *client) {
48
49
  if(client==nullptr || client->name==nullptr || client->host==nullptr || client->prot==nullptr) {
    throw cta::exception::Exception(std::string(__FUNCTION__)+": [ERROR] XrdSecEntity from xroot contains invalid information (nullptr pointer detected!)");
50
  }
51
  std::cerr << "Client request-> Username: " << client->name << " Host: " << client->host << " Prot: " << client->prot << std::endl;
52
53
54
55
56
57
58
59
60
61
62
63
64
  /**
   * The username here has two meanings depending on the protocol used:
   * 
   * 1) Kerberos 5: in this case it is the actual username of the administrator issuing the command
   * 2) Simple Shared Secret (SSS): in this case it is the name of the instance
   * 
   * It is important to remember that we are using kerberos authentication only for admin commands, while for user (EOS)
   * commands we use SSS. Each EOS instance will receive a separate SSS key which will be named (this means that a
   * username will be associated to it), for example all the cms EOS instances (1 or more) will receive the same SSS key
   * with the username "cms" associated to it. Having named keys is important to identify instances and limit crosstalk
   * as much as possible: a client having a "cms" SSS key will only be authorized to archive/retrieve/update/delete
   * cms files, or list cms storageclasses.
   */
65
66
  m_cliIdentity.username=client->name;
  m_cliIdentity.host=client->host;
67
  m_protocol=client->prot;
68
69
}

70
//------------------------------------------------------------------------------
71
// checkOptions
72
//------------------------------------------------------------------------------
73
74
75
76
void XrdCtaFile::checkOptions(const std::string &helpString) {
  if(!m_missingRequiredOptions.empty()||(!m_missingOptionalOptions.empty() && m_optionalOptions.empty())) {
    throw cta::exception::UserError(helpString);
  }
77
78
}

79
80
81
82
//------------------------------------------------------------------------------
// logRequestAndSetCmdlineResult
//------------------------------------------------------------------------------
int XrdCtaFile::logRequestAndSetCmdlineResult(const cta::common::dataStructures::FrontendReturnCode rc, const std::string &returnString) {
83
84
85
86
87
88
89
90
  if(!m_missingRequiredOptions.empty()) {
    m_cmdlineOutput += "The following required options are missing:\n";
    for(auto it=m_missingRequiredOptions.cbegin(); it!=m_missingRequiredOptions.cend(); it++) {
      m_cmdlineOutput += "Missing option: ";
      m_cmdlineOutput += *it;
      m_cmdlineOutput += "\n";
    }
  }
91
  if(!m_missingOptionalOptions.empty() && m_optionalOptions.empty() && !m_suppressOptionalOptionsWarning) {
92
93
94
95
96
97
98
99
    m_cmdlineOutput += "At least one of the following options is required:\n";
    for(auto it=m_missingOptionalOptions.cbegin(); it!=m_missingOptionalOptions.cend(); it++) {
      m_cmdlineOutput += "Missing option: ";
      m_cmdlineOutput += *it;
      m_cmdlineOutput += "\n";
    }
  }
  m_cmdlineOutput += returnString;
100
  m_cmdlineReturnCode = rc;
101
  
Victor Kotlyar's avatar
Victor Kotlyar committed
102
  std::list<cta::log::Param> params;
103
104
105
106
  params.push_back({"Username", m_cliIdentity.username});
  params.push_back({"Host", m_cliIdentity.host});
  params.push_back({"ReturnCode", toString(m_cmdlineReturnCode)});
  params.push_back({"CommandOutput", m_cmdlineOutput});
107
  std::stringstream originalRequest;
108
  for(auto it=m_requestTokens.begin(); it!=m_requestTokens.end(); it++) {
109
110
    originalRequest << *it << " ";
  }
Victor Kotlyar's avatar
Victor Kotlyar committed
111
  params.push_back(cta::log::Param("REQUEST", originalRequest.str()));
112
  
113
  switch(m_cmdlineReturnCode) {
114
115
116
117
    case cta::common::dataStructures::FrontendReturnCode::ok:
      m_log(log::INFO, "Successful Request", params);
      break;
    case cta::common::dataStructures::FrontendReturnCode::userErrorNoRetry:
118
      m_log(log::USERERR, "Syntax error or missing argument(s) in request", params);
119
120
      break;
    default:
Victor Kotlyar's avatar
Victor Kotlyar committed
121
      params.push_back(cta::log::Param("ERROR", returnString));
122
123
124
125
126
      m_log(log::ERR, "Unsuccessful Request", params);
      break;
  }

  return SFS_OK;
127
128
}

129
130
131
132
//------------------------------------------------------------------------------
// authorizeAdmin
//------------------------------------------------------------------------------
void XrdCtaFile::authorizeAdmin(){
133
134
135
  if(m_protocol!="krb5") {
    throw cta::exception::Exception(std::string("[ERROR] Admin commands are possible only through Kerberos 5 protocol authentication. Protocol used for this connection: ")+m_protocol);
  }
136
137
138
  m_scheduler->authorizeAdmin(m_cliIdentity);
}

139
140
141
//------------------------------------------------------------------------------
// commandDispatcher
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
142
void XrdCtaFile::dispatchCommand() {
143
  std::string command(m_requestTokens.at(1));
144
  
145
  if     ("ad"   == command || "admin"                  == command) {authorizeAdmin(); xCom_admin();}
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  else if("ah"   == command || "adminhost"              == command) {authorizeAdmin(); xCom_adminhost();}
  else if("tp"   == command || "tapepool"               == command) {authorizeAdmin(); xCom_tapepool();}
  else if("ar"   == command || "archiveroute"           == command) {authorizeAdmin(); xCom_archiveroute();}
  else if("ll"   == command || "logicallibrary"         == command) {authorizeAdmin(); xCom_logicallibrary();}
  else if("ta"   == command || "tape"                   == command) {authorizeAdmin(); xCom_tape();}
  else if("sc"   == command || "storageclass"           == command) {authorizeAdmin(); xCom_storageclass();}
  else if("rmr"  == command || "requestermountrule"     == command) {authorizeAdmin(); xCom_requestermountrule();}
  else if("gmr"  == command || "groupmountrule"         == command) {authorizeAdmin(); xCom_groupmountrule();}
  else if("mp"   == command || "mountpolicy"            == command) {authorizeAdmin(); xCom_mountpolicy();}
  else if("re"   == command || "repack"                 == command) {authorizeAdmin(); xCom_repack();}
  else if("sh"   == command || "shrink"                 == command) {authorizeAdmin(); xCom_shrink();}
  else if("ve"   == command || "verify"                 == command) {authorizeAdmin(); xCom_verify();}
  else if("af"   == command || "archivefile"            == command) {authorizeAdmin(); xCom_archivefile();}
  else if("te"   == command || "test"                   == command) {authorizeAdmin(); xCom_test();}
  else if("dr"   == command || "drive"                  == command) {authorizeAdmin(); xCom_drive();}
  else if("lpa"  == command || "listpendingarchives"    == command) {authorizeAdmin(); xCom_listpendingarchives();}
  else if("lpr"  == command || "listpendingretrieves"   == command) {authorizeAdmin(); xCom_listpendingretrieves();}
  else if("lds"  == command || "listdrivestates"        == command) {authorizeAdmin(); xCom_listdrivestates();}
Daniele Kruse's avatar
Daniele Kruse committed
164
  
165
166
167
168
169
170
171
  else if("a"    == command || "archive"                == command) {xCom_archive();}
  else if("r"    == command || "retrieve"               == command) {xCom_retrieve();}
  else if("da"   == command || "deletearchive"          == command) {xCom_deletearchive();}
  else if("cr"   == command || "cancelretrieve"         == command) {xCom_cancelretrieve();}
  else if("ufi"  == command || "updatefileinfo"         == command) {xCom_updatefileinfo();}
  else if("ufsc" == command || "updatefilestorageclass" == command) {xCom_updatefilestorageclass();}
  else if("lsc"  == command || "liststorageclass"       == command) {xCom_liststorageclass();}
172
  
173
  else {
174
    throw cta::exception::UserError(getGenericHelp(m_requestTokens.at(0)));
175
  }
176
}
Daniele Kruse's avatar
Daniele Kruse committed
177

178
179
180
//------------------------------------------------------------------------------
// decode
//------------------------------------------------------------------------------
181
std::string XrdCtaFile::decode(const std::string msg) const {
182
183
184
185
186
  std::string ret;
  CryptoPP::StringSource ss1(msg, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(ret)));
  return ret;
}

Daniele Kruse's avatar
Daniele Kruse committed
187
188
189
//------------------------------------------------------------------------------
// open
//------------------------------------------------------------------------------
190
int XrdCtaFile::open(const char *fileName, XrdSfsFileOpenMode openMode, mode_t createMode, const XrdSecEntity *client, const char *opaque) {
191
  try {
192
    checkClient(client);
193
    if(!strlen(fileName)) { //this should never happen
194
      throw cta::exception::UserError(getGenericHelp(""));
195
    }
196
    
197
    std::stringstream ss(fileName+1); //let's skip the first slash which is always prepended since we are asking for an absolute path
198
199
200
201
202
203
204
205
    std::string item;
    while (std::getline(ss, item, '&')) {
      replaceAll(item, "_", "/"); 
      //need to add this because xroot removes consecutive slashes, and the 
      //cryptopp base64 algorithm may produce consecutive slashes. This is solved 
      //in cryptopp-5.6.3 (using Base64URLEncoder instead of Base64Encoder) but we 
      //currently have cryptopp-5.6.2. To be changed in the future...
      item = decode(item);
206
      m_requestTokens.push_back(item);
207
208
    }

209
    if(m_requestTokens.size() == 0) { //this should never happen
210
      throw cta::exception::UserError(getGenericHelp(""));
211
    }
212
    if(m_requestTokens.size() < 2) {
213
      throw cta::exception::UserError(getGenericHelp(m_requestTokens.at(0)));
214
    }    
Daniele Kruse's avatar
Daniele Kruse committed
215
    dispatchCommand();
216
  } catch (cta::exception::UserError &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
217
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::userErrorNoRetry, ex.getMessageValue()+"\n");
218
  } catch (cta::exception::NonRetryableError &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
219
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, ex.getMessageValue()+"\n");
220
  } catch (cta::exception::RetryableError &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
221
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorRetry, ex.getMessageValue()+"\n");
222
  } catch (cta::exception::Exception &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
223
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, ex.getMessageValue()+"\n");
224
  } catch (std::exception &ex) {
Daniele Kruse's avatar
Daniele Kruse committed
225
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, std::string(ex.what())+"\n");
226
  } catch (...) {
Daniele Kruse's avatar
Daniele Kruse committed
227
    logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ctaErrorNoRetry, "Unknown exception caught!\n");
228
  }
Daniele Kruse's avatar
Daniele Kruse committed
229
  return SFS_OK;
Daniele Kruse's avatar
Daniele Kruse committed
230
231
232
233
234
}

//------------------------------------------------------------------------------
// close
//------------------------------------------------------------------------------
235
int XrdCtaFile::close() {
Daniele Kruse's avatar
Daniele Kruse committed
236
  return SFS_OK;
Daniele Kruse's avatar
Daniele Kruse committed
237
238
239
240
241
}

//------------------------------------------------------------------------------
// fctl
//------------------------------------------------------------------------------
242
int XrdCtaFile::fctl(const int cmd, const char *args, XrdOucErrInfo &eInfo) {  
Daniele Kruse's avatar
Daniele Kruse committed
243
244
245
246
247
248
249
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// FName
//------------------------------------------------------------------------------
250
const char* XrdCtaFile::FName() {
Daniele Kruse's avatar
Daniele Kruse committed
251
  error.setErrInfo(ENOTSUP, "Not supported.");
252
  return nullptr;
Daniele Kruse's avatar
Daniele Kruse committed
253
254
255
256
257
}

//------------------------------------------------------------------------------
// getMmap
//------------------------------------------------------------------------------
258
int XrdCtaFile::getMmap(void **Addr, off_t &Size) {
259
260
261
  m_cmdlineOutput = std::to_string(m_cmdlineReturnCode) + m_cmdlineOutput;
  *Addr = const_cast<char *>(m_cmdlineOutput.c_str());
  Size = m_cmdlineOutput.length();
262
  return SFS_OK; //change to "return SFS_ERROR;" in case the read function below is wanted, in that case uncomment the lines in that function.
Daniele Kruse's avatar
Daniele Kruse committed
263
264
265
266
267
}

//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
268
XrdSfsXferSize XrdCtaFile::read(XrdSfsFileOffset offset, char *buffer, XrdSfsXferSize size) {
269
270
271
//  if((unsigned long)offset<m_cmdlineOutput.length()) {
//    strncpy(buffer, m_cmdlineOutput.c_str()+offset, size);
//    return m_cmdlineOutput.length()-offset;
272
273
//  }
//  else {
274
//    return SFS_OK;
275
276
//  }
  error.setErrInfo(ENOTSUP, "Not supported.");
277
  return SFS_ERROR;
Daniele Kruse's avatar
Daniele Kruse committed
278
279
}

280
281
282
//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
283
XrdSfsXferSize XrdCtaFile::read(XrdSfsFileOffset offset, XrdSfsXferSize size) {
284
  error.setErrInfo(ENOTSUP, "Not supported.");
285
  return SFS_ERROR;
286
287
}

Daniele Kruse's avatar
Daniele Kruse committed
288
289
290
//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
291
XrdSfsXferSize XrdCtaFile::read(XrdSfsAio *aioparm) {
Daniele Kruse's avatar
Daniele Kruse committed
292
  error.setErrInfo(ENOTSUP, "Not supported.");
293
  return SFS_ERROR;
Daniele Kruse's avatar
Daniele Kruse committed
294
295
296
297
298
}

//------------------------------------------------------------------------------
// write
//------------------------------------------------------------------------------
299
XrdSfsXferSize XrdCtaFile::write(XrdSfsFileOffset offset, const char *buffer, XrdSfsXferSize size) {
Daniele Kruse's avatar
Daniele Kruse committed
300
  error.setErrInfo(ENOTSUP, "Not supported.");
301
  return SFS_ERROR;
Daniele Kruse's avatar
Daniele Kruse committed
302
303
304
305
306
}

//------------------------------------------------------------------------------
// write
//------------------------------------------------------------------------------
307
int XrdCtaFile::write(XrdSfsAio *aioparm) {
Daniele Kruse's avatar
Daniele Kruse committed
308
309
310
311
312
313
314
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// stat
//------------------------------------------------------------------------------
315
int XrdCtaFile::stat(struct stat *buf) {
316
  buf->st_size=m_cmdlineOutput.length();
Daniele Kruse's avatar
Daniele Kruse committed
317
  return SFS_OK;
Daniele Kruse's avatar
Daniele Kruse committed
318
319
320
321
322
}

//------------------------------------------------------------------------------
// sync
//------------------------------------------------------------------------------
323
int XrdCtaFile::sync() {
Daniele Kruse's avatar
Daniele Kruse committed
324
325
326
327
328
329
330
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// sync
//------------------------------------------------------------------------------
331
int XrdCtaFile::sync(XrdSfsAio *aiop) {
Daniele Kruse's avatar
Daniele Kruse committed
332
333
334
335
336
337
338
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// truncate
//------------------------------------------------------------------------------
339
int XrdCtaFile::truncate(XrdSfsFileOffset fsize) {
Daniele Kruse's avatar
Daniele Kruse committed
340
341
342
343
344
345
346
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// getCXinfo
//------------------------------------------------------------------------------
347
int XrdCtaFile::getCXinfo(char cxtype[4], int &cxrsz) {
Daniele Kruse's avatar
Daniele Kruse committed
348
349
350
351
352
353
354
  error.setErrInfo(ENOTSUP, "Not supported.");
  return SFS_ERROR;
}

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
355
XrdCtaFile::XrdCtaFile(
356
357
  cta::catalogue::Catalogue *catalogue,
  cta::Scheduler *scheduler,
358
  cta::log::Logger *log,
359
360
361
362
363
  const char *user,
  int MonID):
  error(user, MonID),
  m_catalogue(catalogue),
  m_scheduler(scheduler),
364
  m_log(*log),
365
  m_cmdlineOutput(""),
366
367
  m_cmdlineReturnCode(cta::common::dataStructures::FrontendReturnCode::ok),
  m_suppressOptionalOptionsWarning(false){  
Daniele Kruse's avatar
Daniele Kruse committed
368
369
370
371
372
}

//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
373
XrdCtaFile::~XrdCtaFile() {  
374
375
376
377
378
}

//------------------------------------------------------------------------------
// replaceAll
//------------------------------------------------------------------------------
379
void XrdCtaFile::replaceAll(std::string& str, const std::string& from, const std::string& to) const {
380
  if(from.empty() || str.empty())
381
    return;
382
383
  size_t start_pos = 0;
  while((start_pos = str.find(from, start_pos)) != std::string::npos) {
384
385
    str.replace(start_pos, from.length(), to);
    start_pos += to.length();
386
  }
387
388
}

389
//------------------------------------------------------------------------------
390
// getOption
391
//------------------------------------------------------------------------------
392
393
std::string XrdCtaFile::getOption(const std::string& optionShortName, const std::string& optionLongName) {
  bool encoded = false;
394
  for(auto it=m_requestTokens.cbegin(); it!=m_requestTokens.cend(); it++) {
395
396
397
398
    if(optionShortName == *it || optionLongName == *it || optionLongName+":base64" == *it) {
      if(optionLongName+":base64" == *it) {
        encoded = true;
      }
399
      auto it_next=it+1;
400
      if(it_next!=m_requestTokens.cend()) {
401
402
403
        std::string value = *it_next;
        if(!encoded) return value;
        else return decode(value.erase(0,7)); //erasing the first 7 characters "base64:" and decoding the rest
404
405
406
407
      }
      else {
        return "";
      }
408
409
410
411
412
    }
  }
  return "";
}

413
414
415
//------------------------------------------------------------------------------
// getOptionStringValue
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
416
optional<std::string> XrdCtaFile::getOptionStringValue(const std::string& optionShortName, 
417
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
418
419
420
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
421
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
422
423
424
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
425
  if(option.empty()) {
426
427
428
429
430
431
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
432
433
    return optional<std::string>();
  }
434
435
436
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
437
438
439
440
441
442
  return optional<std::string>(option);
}

//------------------------------------------------------------------------------
// getOptionUint64Value
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
443
optional<uint64_t> XrdCtaFile::getOptionUint64Value(const std::string& optionShortName, 
444
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
445
446
447
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
448
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
449
450
451
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
452
  if(option.empty()) {
453
454
455
456
457
458
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
459
460
    return optional<uint64_t>();
  }
461
462
463
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
464
465
466
467
468
469
  return optional<uint64_t>(stringParameterToUint64(optionLongName, option));
}

//------------------------------------------------------------------------------
// getOptionBoolValue
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
470
optional<bool> XrdCtaFile::getOptionBoolValue(const std::string& optionShortName, 
471
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
472
473
474
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
475
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
476
477
478
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
479
  if(option.empty()) {
480
481
482
483
484
485
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
486
487
    return optional<bool>();
  }
488
489
490
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
491
492
493
494
495
496
  return optional<bool>(stringParameterToBool(optionLongName, option));
}

//------------------------------------------------------------------------------
// getOptionTimeValue
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
497
optional<time_t> XrdCtaFile::getOptionTimeValue(const std::string& optionShortName, 
498
        const std::string& optionLongName,
Daniele Kruse's avatar
Daniele Kruse committed
499
500
501
        const bool required, 
        const bool useDefaultIfMissing, 
        const std::string& defaultValue) {
502
  std::string option = getOption(optionShortName, optionLongName);
Daniele Kruse's avatar
Daniele Kruse committed
503
504
505
  if(option.empty()&&useDefaultIfMissing) {
    option = defaultValue;
  }
506
  if(option.empty()) {
507
508
509
510
511
512
    if(required) {
      m_missingRequiredOptions.push_back(optionLongName);
    }
    else {
      m_missingOptionalOptions.push_back(optionLongName);
    }
513
514
    return optional<time_t>();
  }
515
516
517
  if(!required) {
    m_optionalOptions.push_back(optionLongName);
  }
518
519
520
  return optional<time_t>(stringParameterToTime(optionLongName, option));
}

521
522
523
//------------------------------------------------------------------------------
// hasOption
//------------------------------------------------------------------------------
524
bool XrdCtaFile::hasOption(const std::string& optionShortName, const std::string& optionLongName) {
525
  for(auto it=m_requestTokens.cbegin(); it!=m_requestTokens.cend(); it++) {
526
527
528
529
530
531
532
    if(optionShortName == *it || optionLongName == *it) {
      return true;
    }
  }
  return false;
}

533
534
535
//------------------------------------------------------------------------------
// timeToString
//------------------------------------------------------------------------------
536
std::string XrdCtaFile::timeToString(const time_t &time) {
537
538
539
540
541
  std::string timeString(ctime(&time));
  timeString=timeString.substr(0,timeString.size()-1); //remove newline
  return timeString;
}

542
//------------------------------------------------------------------------------
543
// formatResponse
544
//------------------------------------------------------------------------------
545
std::string XrdCtaFile::formatResponse(const std::vector<std::vector<std::string>> &responseTable, const bool withHeader) {
546
547
  if(responseTable.empty()||responseTable.at(0).empty()) {
    return "";
548
  }
549
  std::vector<int> columnSizes;
550
  for(uint j=0; j<responseTable.at(0).size(); j++) { //for each column j
551
    uint columnSize=0;
552
    for(uint i=0; i<responseTable.size(); i++) { //for each row i
553
554
555
      if(responseTable.at(i).at(j).size()>columnSize) {
        columnSize=responseTable.at(i).at(j).size();
      }
556
    }
557
    columnSize++; //add one space
558
    columnSizes.push_back(columnSize);//loops here
559
  }
560
561
  std::stringstream responseSS;
  for(auto row=responseTable.cbegin(); row!=responseTable.cend(); row++) {
562
563
564
    if(withHeader && row==responseTable.cbegin()) responseSS << "\x1b[31;1m";
    for(uint i=0; i<row->size(); i++) {
      responseSS << " " << std::setw(columnSizes.at(i)) << row->at(i);
565
    }
566
567
    if(withHeader && row==responseTable.cbegin()) responseSS << "\x1b[0m" << std::endl;
    else responseSS << std::endl;
568
  }
569
570
571
572
573
574
  return responseSS.str();
}

//------------------------------------------------------------------------------
// addLogInfoToResponseRow
//------------------------------------------------------------------------------
575
void XrdCtaFile::addLogInfoToResponseRow(std::vector<std::string> &responseRow, const cta::common::dataStructures::EntryLog &creationLog, const cta::common::dataStructures::EntryLog &lastModificationLog) {
576
  responseRow.push_back(creationLog.username);
577
578
  responseRow.push_back(creationLog.host);
  responseRow.push_back(timeToString(creationLog.time));
579
  responseRow.push_back(lastModificationLog.username);
580
581
  responseRow.push_back(lastModificationLog.host);
  responseRow.push_back(timeToString(lastModificationLog.time));
582
583
}

584
585
586
587
588
//------------------------------------------------------------------------------
// stringParameterToUint64
//------------------------------------------------------------------------------
uint64_t XrdCtaFile::stringParameterToUint64(const std::string &parameterName, const std::string &parameterValue) const {
  try {
589
590
591
592
    return cta::utils::toUint64(parameterValue);
  } catch(cta::exception::Exception &ex) {
    throw cta::exception::Exception(std::string(__FUNCTION__)+" - Parameter: "+parameterName+" ("+parameterValue+
            ") could not be converted to uint64_t because: " + ex.getMessageValue());
593
594
595
596
597
598
599
600
601
602
603
604
  }
}

//------------------------------------------------------------------------------
// stringParameterToBool
//------------------------------------------------------------------------------
bool XrdCtaFile::stringParameterToBool(const std::string &parameterName, const std::string &parameterValue) const {
  if(parameterValue == "true") {
    return true;
  } 
  else if(parameterValue == "false") {
    return false;
605
  } 
606
607
608
609
610
611
612
613
614
615
616
  else {
    throw cta::exception::Exception(std::string(__FUNCTION__)+" - Parameter: "+parameterName+" ("+parameterValue+
            ") could not be converted to bool: the only values allowed are \"true\" and \"false\"");
  }
}

//------------------------------------------------------------------------------
// stringParameterToTime
//------------------------------------------------------------------------------
time_t XrdCtaFile::stringParameterToTime(const std::string &parameterName, const std::string &parameterValue) const {
  struct tm time;
617
  if(nullptr==strptime(parameterValue.c_str(), "%d/%m/%Y", &time)) {
618
619
620
621
    throw cta::exception::Exception(std::string(__FUNCTION__)+" - Parameter: "+parameterName+" ("+parameterValue+
            ") could not be interpreted as a time, the allowed format is: <DD/MM/YYYY>");
  }
  return mktime(&time);
622
623
}

624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
//------------------------------------------------------------------------------
// EOS2CTAChecksumType
//------------------------------------------------------------------------------
optional<std::string> XrdCtaFile::EOS2CTAChecksumType(const optional<std::string> &EOSChecksumType) {
  if(EOSChecksumType && EOSChecksumType.value()=="adler") return optional<std::string>("ADLER32");
  return nullopt;
}

//------------------------------------------------------------------------------
// EOS2CTAChecksumValue
//------------------------------------------------------------------------------
optional<std::string> XrdCtaFile::EOS2CTAChecksumValue(const optional<std::string> &EOSChecksumValue) {
  if(EOSChecksumValue) {
    std::string CTAChecksumValue("0X");  
    CTAChecksumValue += EOSChecksumValue.value();
    cta::utils::toUpper(CTAChecksumValue);
    return optional<std::string>(CTAChecksumValue);
  }
  return nullopt;
}

645
646
647
//------------------------------------------------------------------------------
// xCom_admin
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
648
void XrdCtaFile::xCom_admin() {
649
  std::stringstream cmdlineOutput;
650
  std::stringstream help;
651
  help << m_requestTokens.at(0) << " ad/admin add/ch/rm/ls:" << std::endl
652
653
654
       << "\tadd --username/-u <user_name> --comment/-m <\"comment\">" << std::endl
       << "\tch  --username/-u <user_name> --comment/-m <\"comment\">" << std::endl
       << "\trm  --username/-u <user_name>" << std::endl
655
       << "\tls  [--header/-h]" << std::endl;
656
  if(m_requestTokens.size() < 3) {
657
    throw cta::exception::UserError(help.str());
658
  }
659
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
660
    optional<std::string> username = getOptionStringValue("-u", "--username", true, false);
661
    if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2)) {
662
      optional<std::string> comment = getOptionStringValue("-m", "--comment", true, false);
663
      if("add" == m_requestTokens.at(2)) { //add
664
        checkOptions(help.str());
665
        m_catalogue->createAdminUser(m_cliIdentity, username.value(), comment.value());
666
667
      }
      else { //ch
668
        checkOptions(help.str());
669
        m_catalogue->modifyAdminUserComment(m_cliIdentity, username.value(), comment.value());
670
671
672
      }
    }
    else { //rm
673
      checkOptions(help.str());
674
      m_catalogue->deleteAdminUser(username.value());
675
    }
676
  }
677
  else if("ls" == m_requestTokens.at(2)) { //ls
678
    std::list<cta::common::dataStructures::AdminUser> list= m_catalogue->getAdminUsers();
679
    if(list.size()>0) {
680
      std::vector<std::vector<std::string>> responseTable;
681
      std::vector<std::string> header = {"user","c.user","c.host","c.time","m.user","m.host","m.time","comment"};
682
      if(hasOption("-h", "--header")) responseTable.push_back(header);    
683
      for(auto it = list.cbegin(); it != list.cend(); it++) {
684
        std::vector<std::string> currentRow;
685
        currentRow.push_back(it->name);
686
687
        addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
        currentRow.push_back(it->comment);
688
689
        responseTable.push_back(currentRow);
      }
690
      cmdlineOutput << formatResponse(responseTable, hasOption("-h", "--header"));
691
692
693
    }
  }
  else {
694
    throw cta::exception::UserError(help.str());
695
  }
Daniele Kruse's avatar
Daniele Kruse committed
696
  logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ok, cmdlineOutput.str());
697
}
698

699
//------------------------------------------------------------------------------
700
// xCom_adminhost
701
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
702
void XrdCtaFile::xCom_adminhost() {
703
  std::stringstream cmdlineOutput;
704
  std::stringstream help;
705
  help << m_requestTokens.at(0) << " ah/adminhost add/ch/rm/ls:" << std::endl
706
       << "\tadd --name/-n <host_name> --comment/-m <\"comment\">" << std::endl
Daniele Kruse's avatar
Daniele Kruse committed
707
       << "\tch  --name/-n <host_name> --comment/-m <\"comment\">" << std::endl
708
       << "\trm  --name/-n <host_name>" << std::endl
709
       << "\tls  [--header/-h]" << std::endl;  
710
  if(m_requestTokens.size() < 3) {
711
    throw cta::exception::UserError(help.str());
712
  }
713
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
714
    optional<std::string> hostname = getOptionStringValue("-n", "--name", true, false);
715
    if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2)) {
716
      optional<std::string> comment = getOptionStringValue("-m", "--comment", true, false);
717
      if("add" == m_requestTokens.at(2)) { //add
718
        checkOptions(help.str());
719
        m_catalogue->createAdminHost(m_cliIdentity, hostname.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
720
721
      }
      else { //ch
722
        checkOptions(help.str());
723
        m_catalogue->modifyAdminHostComment(m_cliIdentity, hostname.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
724
725
726
      }
    }
    else { //rm
727
      checkOptions(help.str());
728
      m_catalogue->deleteAdminHost(hostname.value());
Daniele Kruse's avatar
Daniele Kruse committed
729
730
    }
  }
731
  else if("ls" == m_requestTokens.at(2)) { //ls
732
    std::list<cta::common::dataStructures::AdminHost> list= m_catalogue->getAdminHosts();
Daniele Kruse's avatar
Daniele Kruse committed
733
734
    if(list.size()>0) {
      std::vector<std::vector<std::string>> responseTable;
735
      std::vector<std::string> header = {"hostname","c.user","c.host","c.time","m.user","m.host","m.time","comment"};
736
      if(hasOption("-h", "--header")) responseTable.push_back(header);
737
      for(auto it = list.cbegin(); it != list.cend(); it++) {
Daniele Kruse's avatar
Daniele Kruse committed
738
        std::vector<std::string> currentRow;
739
740
741
        currentRow.push_back(it->name);
        addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
        currentRow.push_back(it->comment);
Daniele Kruse's avatar
Daniele Kruse committed
742
743
        responseTable.push_back(currentRow);
      }
744
      cmdlineOutput << formatResponse(responseTable, hasOption("-h", "--header"));
Daniele Kruse's avatar
Daniele Kruse committed
745
746
747
    }
  }
  else {
748
    throw cta::exception::UserError(help.str());
Daniele Kruse's avatar
Daniele Kruse committed
749
  }
Daniele Kruse's avatar
Daniele Kruse committed
750
  logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ok, cmdlineOutput.str());
751
}
752

753
754
755
//------------------------------------------------------------------------------
// xCom_tapepool
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
756
void XrdCtaFile::xCom_tapepool() {
757
  std::stringstream cmdlineOutput;
758
  std::stringstream help;
759
  help << m_requestTokens.at(0) << " tp/tapepool add/ch/rm/ls:" << std::endl
Daniele Kruse's avatar
Daniele Kruse committed
760
761
       << "\tadd --name/-n <tapepool_name> --partialtapesnumber/-p <number_of_partial_tapes> --encrypted/-e <\"true\" or \"false\"> --comment/-m <\"comment\">" << std::endl
       << "\tch  --name/-n <tapepool_name> [--partialtapesnumber/-p <number_of_partial_tapes>] [--encrypted/-e <\"true\" or \"false\">] [--comment/-m <\"comment\">]" << std::endl
762
       << "\trm  --name/-n <tapepool_name>" << std::endl
763
       << "\tls  [--header/-h]" << std::endl;  
764
  if(m_requestTokens.size() < 3) {
765
    throw cta::exception::UserError(help.str());
766
  }
767
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
768
    optional<std::string> name = getOptionStringValue("-n", "--name", true, false);
769
    if("add" == m_requestTokens.at(2)) { //add
770
771
772
      optional<uint64_t> ptn = getOptionUint64Value("-p", "--partialtapesnumber", true, false);
      optional<std::string> comment = getOptionStringValue("-m", "--comment", true, false);
      optional<bool> encrypted = getOptionBoolValue("-e", "--encrypted", true, false);
773
      checkOptions(help.str());
Daniele Kruse's avatar
Daniele Kruse committed
774
      m_catalogue->createTapePool(m_cliIdentity, name.value(), ptn.value(), encrypted.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
775
    }
776
    else if("ch" == m_requestTokens.at(2)) { //ch
777
778
779
      optional<uint64_t> ptn = getOptionUint64Value("-p", "--partialtapesnumber", false, false);
      optional<std::string> comment = getOptionStringValue("-m", "--comment", false, false);
      optional<bool> encrypted = getOptionBoolValue("-e", "--encrypted", false, false);
780
      checkOptions(help.str());
781
      if(comment) {
782
        m_catalogue->modifyTapePoolComment(m_cliIdentity, name.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
783
      }
784
      if(ptn) {
785
        m_catalogue->modifyTapePoolNbPartialTapes(m_cliIdentity, name.value(), ptn.value());
Daniele Kruse's avatar
Daniele Kruse committed
786
      }
Daniele Kruse's avatar
Daniele Kruse committed
787
788
      if(encrypted) {
        m_catalogue->setTapePoolEncryption(m_cliIdentity, name.value(), encrypted.value());
789
      }
Daniele Kruse's avatar
Daniele Kruse committed
790
791
    }
    else { //rm
792
      checkOptions(help.str());
793
      m_catalogue->deleteTapePool(name.value());
Daniele Kruse's avatar
Daniele Kruse committed
794
795
    }
  }
796
  else if("ls" == m_requestTokens.at(2)) { //ls
797
    std::list<cta::common::dataStructures::TapePool> list= m_catalogue->getTapePools();
Daniele Kruse's avatar
Daniele Kruse committed
798
799
    if(list.size()>0) {
      std::vector<std::vector<std::string>> responseTable;
800
      std::vector<std::string> header = {"name","# partial tapes","encrypt","c.user","c.host","c.time","m.user","m.host","m.time","comment"};
801
      if(hasOption("-h", "--header")) responseTable.push_back(header);    
802
      for(auto it = list.cbegin(); it != list.cend(); it++) {
Daniele Kruse's avatar
Daniele Kruse committed
803
        std::vector<std::string> currentRow;
804
805
        currentRow.push_back(it->name);
        currentRow.push_back(std::to_string((unsigned long long)it->nbPartialTapes));
806
        if(it->encryption) currentRow.push_back("true"); else currentRow.push_back("false");
807
808
        addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
        currentRow.push_back(it->comment);
Daniele Kruse's avatar
Daniele Kruse committed
809
810
        responseTable.push_back(currentRow);
      }
811
      cmdlineOutput << formatResponse(responseTable, hasOption("-h", "--header"));
Daniele Kruse's avatar
Daniele Kruse committed
812
813
814
    }
  }
  else {
815
    throw cta::exception::UserError(help.str());
Daniele Kruse's avatar
Daniele Kruse committed
816
  }
Daniele Kruse's avatar
Daniele Kruse committed
817
  logRequestAndSetCmdlineResult(cta::common::dataStructures::FrontendReturnCode::ok, cmdlineOutput.str());
818
}
819

820
821
822
//------------------------------------------------------------------------------
// xCom_archiveroute
//------------------------------------------------------------------------------
Daniele Kruse's avatar
Daniele Kruse committed
823
void XrdCtaFile::xCom_archiveroute() {
824
  std::stringstream cmdlineOutput;
825
  std::stringstream help;
826
  help << m_requestTokens.at(0) << " ar/archiveroute add/ch/rm/ls:" << std::endl
827
828
829
       << "\tadd --instance/-i <instance_name> --storageclass/-s <storage_class_name> --copynb/-c <copy_number> --tapepool/-t <tapepool_name> --comment/-m <\"comment\">" << std::endl
       << "\tch  --instance/-i <instance_name> --storageclass/-s <storage_class_name> --copynb/-c <copy_number> [--tapepool/-t <tapepool_name>] [--comment/-m <\"comment\">]" << std::endl
       << "\trm  --instance/-i <instance_name> --storageclass/-s <storage_class_name> --copynb/-c <copy_number>" << std::endl
830
       << "\tls  [--header/-h]" << std::endl;  
831
  if(m_requestTokens.size() < 3) {
832
    throw cta::exception::UserError(help.str());
833
  }
834
  if("add" == m_requestTokens.at(2) || "ch" == m_requestTokens.at(2) || "rm" == m_requestTokens.at(2)) {
835
836
837
    optional<std::string> scn = getOptionStringValue("-s", "--storageclass", true, false);
    optional<uint64_t> cn = getOptionUint64Value("-c", "--copynb", true, false);
    optional<std::string> in = getOptionStringValue("-i", "--instance", true, false);
838
    if("add" == m_requestTokens.at(2)) { //add
839
840
      optional<std::string> tapepool = getOptionStringValue("-t", "--tapepool", true, false);
      optional<std::string> comment = getOptionStringValue("-m", "--comment", true, false);
841
      checkOptions(help.str());
842
      m_catalogue->createArchiveRoute(m_cliIdentity, in.value(), scn.value(), cn.value(), tapepool.value(), comment.value());
Daniele Kruse's avatar
Daniele Kruse committed
843
    }
844
    else if("ch" == m_requestTokens.at(2)) { //ch
845
846
      optional<std::string> tapepool = getOptionStringValue("-t", "--tapepool", false, false);
      optional<std::string> comment = getOptionStringValue("-m", "--comment", false, false);
847
      checkOptions(help.str());