Commit 09fa7174 authored by Eric Cano's avatar Eric Cano
Browse files

Create a serialized wrapper for getopt

This is needed as getopt is not thread safe. This will allow usage of getopt to parse CTA command lines.
parent eda443c2
......@@ -113,6 +113,7 @@ set (COMMON_LIB_SRC_FILES
threading/System.cpp
threading/Threading.cpp
threading/Semaphores.cpp
utils/GetOptThreadSafe.cpp
utils/utils.cpp
utils/strerror_r_wrapper.cpp
CreationLog.cpp
......@@ -154,6 +155,7 @@ set (COMMON_UNIT_TESTS_LIB_SRC_FILES
threading/ThreadingMTTests.cpp
threading/ThreadingTests.cpp
threading/AtomicCounterTest.cpp
utils/GetOptThreadSafeTest.cpp
utils/UtilsTest.cpp
UserIdentityTest.cpp
optionalTest.cpp)
......
/*
* 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/>.
*/
#include "GetOptThreadSafe.hpp"
#include "common/exception/Exception.hpp"
#include <memory>
namespace cta { namespace utils {
GetOpThreadSafe::Reply GetOpThreadSafe::getOpt(const Request& request) {
std::lock_guard<std::mutex> lock(gMutex);
// Prepare the classic styled argv.
std::unique_ptr<char * []>argv(new char *[request.argv.size()]);
char ** p=argv.get();
for (auto & a: request.argv) {
// This is ugly, but getopt's interface takes NON-const char ** for argv.
*(p++) = const_cast<char *>(a.c_str());
}
// Prepare the global variables
::optind=0; // optind = 0 allows using GNU extentions on the option string.
::opterr=0; // Prevent getopt from printing to stderr.
// Prepare the return value
Reply ret;
// Find the present options
int longIndex, c;
while (-1 != (c = ::getopt_long(request.argv.size(), argv.get(),
request.optstring.c_str(),
request.longopts, &longIndex))) {
if (!c) {
// We received a long option.
ret.options.push_back(FoundOption());
ret.options.back().option = request.longopts[longIndex].name;
if (::optarg)
ret.options.back().parameter = ::optarg;
} else if (1 == c ) {
// We received a non option in
} else if ('?' == c || ':' == c) {
// getopt is unhappy
cta::exception::Exception e("Unexpected option: ");
e.getMessage() << argv[::optind];
throw e;
} else {
// We received a character option.
ret.options.push_back(FoundOption());
ret.options.back().option = " ";
ret.options.back().option[0] = c;
if (::optarg)
ret.options.back().parameter = ::optarg;
}
}
size_t pos = ::optind;
while (pos < request.argv.size()) {
ret.remainder.push_back(argv.get()[pos++]);
}
return ret;
}
std::mutex GetOpThreadSafe::gMutex;
}} // namespace cta::utils
\ No newline at end of file
/*
* 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/>.
*/
#include <vector>
#include <string>
#include <unistd.h>
#include <getopt.h>
#include <mutex>
namespace cta { namespace utils {
/**
* A thread safe (but serialized) wrapper to getopt.
*/
class GetOpThreadSafe {
public:
struct Request {
std::vector<std::string> argv;
std::string optstring;
const struct ::option *longopts;
};
struct FoundOption {
std::string option;
std::string parameter;
};
struct Reply {
std::vector<FoundOption> options;
std::vector<std::string> remainder;
};
static Reply getOpt (const Request & request);
private:
static std::mutex gMutex;
};
}} // namespace cta::utils
\ No newline at end of file
/*
* 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/>.
*/
#include "GetOptThreadSafe.hpp"
#include <gtest/gtest.h>
namespace unitTests {
TEST (GetOptThreadSafe, BasicTest) {
cta::utils::GetOpThreadSafe::Request request;
request.argv = { "down", "-f" };
request.optstring = { "f" };
struct ::option longOptions[] = { { "force", no_argument, 0 , 'f' }, { 0, 0, 0, 0 } };
request.longopts = longOptions;
auto reply = cta::utils::GetOpThreadSafe::getOpt(request);
ASSERT_EQ(0, reply.remainder.size());
ASSERT_EQ(1, reply.options.size());
ASSERT_EQ("f", reply.options.at(0).option);
ASSERT_EQ("", reply.options.at(0).parameter);
request.argv = { "down", "--force", "myDrive" };
reply = cta::utils::GetOpThreadSafe::getOpt(request);
ASSERT_EQ(1, reply.remainder.size());
ASSERT_EQ("myDrive", reply.remainder.at(0));
ASSERT_EQ(1, reply.options.size());
ASSERT_EQ("f", reply.options.at(0).option);
ASSERT_EQ("", reply.options.at(0).parameter);
request.argv = { "down", "myDrive" , "--force"};
reply = cta::utils::GetOpThreadSafe::getOpt(request);
ASSERT_EQ(1, reply.remainder.size());
ASSERT_EQ("myDrive", reply.remainder.at(0));
ASSERT_EQ(1, reply.options.size());
ASSERT_EQ("f", reply.options.at(0).option);
ASSERT_EQ("", reply.options.at(0).parameter);
}
} // namespace
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment