Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
dCache
cta
Commits
9f227007
Commit
9f227007
authored
Oct 29, 2020
by
Cedric Caffy
Browse files
Added JSONC Parser and stdin input passing to a program ran by Subprocess
parent
fbeedb47
Changes
10
Hide whitespace changes
Inline
Side-by-side
common/CMakeLists.txt
View file @
9f227007
...
...
@@ -20,6 +20,7 @@ find_package (binutils REQUIRED)
find_package
(
libattr REQUIRED
)
find_package
(
libcap REQUIRED
)
find_package
(
libuuid REQUIRED
)
find_package
(
json-c REQUIRED
)
add_subdirectory
(
exception
)
...
...
@@ -104,6 +105,7 @@ set (COMMON_LIB_SRC_FILES
exception/TimeOut.cpp
exception/UserError.cpp
exception/XrootCl.cpp
json/parser/JSONCParser.cpp
log/DummyLogger.cpp
log/FileLogger.cpp
log/LogContext.cpp
...
...
@@ -166,6 +168,7 @@ target_link_libraries (ctacommon
z
cap
XrdCl
json-c
)
set
(
COMMON_UNIT_TESTS_LIB_SRC_FILES
...
...
@@ -200,7 +203,9 @@ set (COMMON_UNIT_TESTS_LIB_SRC_FILES
utils/RegexTest.cpp
utils/UtilsTest.cpp
optionalTest.cpp
rangeTest.cpp
)
rangeTest.cpp
json/parser/JSONCParserTest.cpp
)
add_library
(
ctacommonunittests SHARED
${
COMMON_UNIT_TESTS_LIB_SRC_FILES
}
)
...
...
common/json/object/SchedulerHints.hpp
0 → 100644
View file @
9f227007
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2019 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/>.
*/
#pragma once
#include
<string>
namespace
cta
{
namespace
utils
{
namespace
json
{
namespace
object
{
struct
SchedulerHints
{
int
test
;
std
::
string
test2
;
};
}}}}
common/json/object/TestObject.hpp
0 → 100644
View file @
9f227007
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2019 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/>.
*/
#pragma once
#include
<string>
namespace
cta
{
namespace
utils
{
namespace
json
{
namespace
object
{
struct
TestObject
{
uint64_t
integer_number
;
std
::
string
str
;
double
double_number
;
};
}}}}
\ No newline at end of file
common/json/parser/JSONCParser.cpp
0 → 100644
View file @
9f227007
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2019 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
<json-c/json.h>
#include
<json/json_object.h>
#include
"JSONCParser.hpp"
#include
"common/json/object/SchedulerHints.hpp"
#include
"common/json/object/TestObject.hpp"
#include
"common/exception/Exception.hpp"
#include
"JSONParserException.hpp"
namespace
cta
{
namespace
utils
{
namespace
json
{
namespace
parser
{
JSONCParser
::
JSONCParser
()
{
m_jsonObject
=
json_object_new_object
();
}
void
JSONCParser
::
setJSONToBeParsed
(
const
std
::
string
&
json
){
//DO JSON_C deinitialization
if
(
m_jsonObject
!=
nullptr
){
json_object_put
(
m_jsonObject
);
m_jsonObject
=
nullptr
;
}
m_jsonObject
=
json_tokener_parse
(
json
.
c_str
());
}
std
::
string
JSONCParser
::
getJSON
()
const
{
return
std
::
string
(
json_object_to_json_string_ext
(
m_jsonObject
,
JSON_C_TO_STRING_PLAIN
));
}
JSONCParser
::~
JSONCParser
()
{
//Free the JSON object if initialized
if
(
m_jsonObject
!=
nullptr
){
json_object_put
(
m_jsonObject
);
m_jsonObject
=
nullptr
;
}
}
json_object
*
JSONCParser
::
getJSONObject
(
const
std
::
string
&
key
){
json_object
*
objectRet
;
if
(
json_object_object_get_ex
(
m_jsonObject
,
key
.
c_str
(),
&
objectRet
)){
return
objectRet
;
}
std
::
string
errMsg
=
"In JSONCParser::getJSONObject(), the provided json does not contain any key named
\"
"
+
key
+
"
\"
."
;
throw
cta
::
exception
::
JSONParserException
(
errMsg
);
}
////////////////////////////////////////////////////////////////////
// JSONCParser::getValue() implementation START
////////////////////////////////////////////////////////////////////
template
<
>
std
::
string
JSONCParser
::
getValue
(
const
std
::
string
&
key
){
json_object
*
jsonObj
=
getJSONObject
(
key
);
return
std
::
string
(
json_object_get_string
(
jsonObj
));
}
template
<
>
uint64_t
JSONCParser
::
getValue
(
const
std
::
string
&
key
){
json_object
*
jsonObj
=
getJSONObject
(
key
);
return
json_object_get_int64
(
jsonObj
);
}
template
<
>
double
JSONCParser
::
getValue
(
const
std
::
string
&
key
){
json_object
*
jsonObj
=
getJSONObject
(
key
);
return
json_object_get_double
(
jsonObj
);
}
////////////////////////////////////////////////////////////////////
// JSONCParser::getValue() implementation END
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// json::object::TestObject implementation START
////////////////////////////////////////////////////////////////////
template
<
>
void
JSONCParser
::
generateJSONFromObject
(
const
object
::
TestObject
&
value
){
json_object_object_add
(
m_jsonObject
,
"integer_number"
,
json_object_new_int64
(
value
.
integer_number
));
json_object_object_add
(
m_jsonObject
,
"str"
,
json_object_new_string
(
value
.
str
.
c_str
()));
json_object_object_add
(
m_jsonObject
,
"double_number"
,
json_object_new_double
(
value
.
double_number
));
}
template
<
>
object
::
TestObject
JSONCParser
::
getObjectFromJSON
(){
object
::
TestObject
ret
;
ret
.
str
=
getValue
<
std
::
string
>
(
"str"
);
ret
.
integer_number
=
getValue
<
uint64_t
>
(
"integer_number"
);
ret
.
double_number
=
getValue
<
double
>
(
"double_number"
);
return
ret
;
}
////////////////////////////////////////////////////////////////////
// json::object::TestObject implementation END
////////////////////////////////////////////////////////////////////
template
<
>
void
JSONCParser
::
generateJSONFromObject
(
const
object
::
SchedulerHints
&
value
){
}
}}}}
\ No newline at end of file
common/json/parser/JSONCParser.hpp
0 → 100644
View file @
9f227007
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2019 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/>.
*/
#pragma once
#include
<string>
#include
<json-c/json.h>
#include
<memory>
namespace
cta
{
namespace
utils
{
namespace
json
{
namespace
parser
{
class
JSONCParser
{
public:
JSONCParser
();
void
setJSONToBeParsed
(
const
std
::
string
&
json
);
std
::
string
getJSON
()
const
;
template
<
typename
T
>
void
generateJSONFromObject
(
const
T
&
value
);
template
<
typename
T
>
T
getObjectFromJSON
();
virtual
~
JSONCParser
();
private:
json_object
*
m_jsonObject
=
nullptr
;
template
<
typename
T
>
T
getValue
(
const
std
::
string
&
key
);
json_object
*
getJSONObject
(
const
std
::
string
&
key
);
};
}}}}
common/json/parser/JSONCParserTest.cpp
0 → 100644
View file @
9f227007
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2019 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
<gtest/gtest.h>
#include
"common/json/parser/JSONCParser.hpp"
#include
"common/json/object/TestObject.hpp"
#include
"JSONParserException.hpp"
namespace
unitTests
{
using
namespace
cta
::
utils
;
TEST
(
JSONCParserTest
,
testJSONGenerationFromObject
)
{
json
::
object
::
TestObject
to
;
to
.
double_number
=
42.0
;
to
.
integer_number
=
42
;
to
.
str
=
"forty two"
;
json
::
parser
::
JSONCParser
parser
;
parser
.
generateJSONFromObject
(
to
);
ASSERT_EQ
(
"{
\"
integer_number
\"
:42,
\"
str
\"
:
\"
forty two
\"
,
\"
double_number
\"
:42.000000}"
,
parser
.
getJSON
());
}
TEST
(
JSONCParserTest
,
testObjectGenerationFromJSON
){
std
::
string
json
=
"{
\"
integer_number
\"
:42,
\"
str
\"
:
\"
forty two
\"
,
\"
double_number
\"
:42.000000}"
;
json
::
parser
::
JSONCParser
parser
;
parser
.
setJSONToBeParsed
(
json
);
json
::
object
::
TestObject
to
=
parser
.
getObjectFromJSON
<
decltype
(
to
)
>
();
ASSERT_EQ
(
42
,
to
.
integer_number
);
ASSERT_EQ
(
"forty two"
,
to
.
str
);
ASSERT_EQ
(
42.000000
,
to
.
double_number
);
}
TEST
(
JSONCParserTest
,
testJSONCParserGetObjectFromUninitializedJSON
){
json
::
parser
::
JSONCParser
parser
;
ASSERT_THROW
(
parser
.
getObjectFromJSON
<
json
::
object
::
TestObject
>
(),
cta
::
exception
::
JSONParserException
);
}
TEST
(
JSONCParserTest
,
testJSONCParserGetJSONShouldReturnEmptyJSON
){
json
::
parser
::
JSONCParser
parser
;
ASSERT_EQ
(
"{}"
,
parser
.
getJSON
());
}
TEST
(
JSONCParserTest
,
testJSONCParserSetJSONToBeParsedWrongJSONFormat
){
json
::
parser
::
JSONCParser
parser
;
parser
.
setJSONToBeParsed
(
"WRONG_JSON_STRING"
);
ASSERT_EQ
(
"null"
,
parser
.
getJSON
());
parser
.
setJSONToBeParsed
(
"{
\"
test"
);
ASSERT_EQ
(
"null"
,
parser
.
getJSON
());
ASSERT_THROW
(
parser
.
getObjectFromJSON
<
json
::
object
::
TestObject
>
(),
cta
::
exception
::
JSONParserException
);
}
}
\ No newline at end of file
common/json/parser/JSONParserException.hpp
0 → 100644
View file @
9f227007
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2019 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
"common/exception/Exception.hpp"
namespace
cta
{
namespace
exception
{
class
JSONParserException
:
public
Exception
{
using
Exception
::
Exception
;
};
}}
\ No newline at end of file
common/threading/SubProcess.cpp
View file @
9f227007
...
...
@@ -63,7 +63,7 @@ private:
}
namespace
cta
{
namespace
threading
{
SubProcess
::
SubProcess
(
const
std
::
string
&
executable
,
const
std
::
list
<
std
::
string
>&
argv
)
:
SubProcess
::
SubProcess
(
const
std
::
string
&
executable
,
const
std
::
list
<
std
::
string
>&
argv
,
const
std
::
string
&
stdinInput
)
:
m_childComplete
(
false
)
{
// Sanity checks
if
(
argv
.
size
()
<
1
)
...
...
@@ -74,10 +74,13 @@ m_childComplete(false) {
const
size_t
writeSide
=
1
;
int
stdoutPipe
[
2
];
int
stderrPipe
[
2
];
int
stdinPipe
[
2
];
cta
::
exception
::
Errnum
::
throwOnNonZero
(
::
pipe2
(
stdoutPipe
,
O_NONBLOCK
),
"In Subprocess::Subprocess failed to create the stdout pipe"
);
cta
::
exception
::
Errnum
::
throwOnNonZero
(
::
pipe2
(
stderrPipe
,
O_NONBLOCK
),
"In Subprocess::Subprocess failed to create the stderr pipe"
);
cta
::
exception
::
Errnum
::
throwOnNonZero
(
::
pipe2
(
stdinPipe
,
O_NONBLOCK
),
"In Subprocess::Subprocess failed to create the stdin pipe"
);
// Prepare the actions to be taken on file descriptors
ScopedPosixSpawnFileActions
fileActions
;
// We will be the child process. Close the read sides of the pipes.
...
...
@@ -85,16 +88,25 @@ m_childComplete(false) {
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (1)"
);
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_addclose
(
fileActions
,
stderrPipe
[
readSide
]),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (2)"
);
// Close stdin and rewire the stdout and stderr to the pipes.
// We close the write side of the stdinPipe: the child does not write in it
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_addclose
(
fileActions
,
stdinPipe
[
writeSide
]),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (3)"
);
//Rewire the stdout and stderr to the pipes.
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_adddup2
(
fileActions
,
stdoutPipe
[
writeSide
],
STDOUT_FILENO
),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_adddup2() (1)"
);
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_adddup2
(
fileActions
,
stderrPipe
[
writeSide
],
STDERR_FILENO
),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_adddup2() (2)"
);
//Rewiring the read side of the stdin pipe to stdin
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_adddup2
(
fileActions
,
stdinPipe
[
readSide
],
STDIN_FILENO
),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_adddup2() (3)"
);
// Close the now duplicated pipe file descriptors
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_addclose
(
fileActions
,
stdoutPipe
[
writeSide
]),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (3)"
);
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_addclose
(
fileActions
,
stderrPipe
[
writeSide
]),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (4)"
);
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_addclose
(
fileActions
,
stderrPipe
[
writeSide
]),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (5)"
);
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
posix_spawn_file_actions_addclose
(
fileActions
,
stdinPipe
[
readSide
]),
"In Subprocess::Subprocess(): failed to posix_spawn_file_actions_addclose() (6)"
);
// And finally spawn the subprocess
// Prepare the spawn attributes (we need vfork)
ScopedPosixSpawnAttr
attr
;
...
...
@@ -114,7 +126,15 @@ m_childComplete(false) {
int
spawnRc
=::
posix_spawnp
(
&
m_child
,
executable
.
c_str
(),
fileActions
,
attr
,
cargv
.
get
(),
::
environ
);
cta
::
exception
::
Errnum
::
throwOnReturnedErrno
(
spawnRc
,
"In Subprocess::Subprocess failed to posix_spawn()"
);
}
// We are the parent process. Close the write sides of pipes.
// We are the parent process.
// close the readSide of stdin pipe as we are going to write to the subprocess stdin
//Send input to child stdin
//Write data to child stdin
::
write
(
stdinPipe
[
writeSide
],
stdinInput
.
c_str
(),
stdinInput
.
size
());
//Close stdin
::
close
(
stdinPipe
[
writeSide
]);
::
close
(
stdinPipe
[
readSide
]);
//Close the write sides of pipes.
::
close
(
stdoutPipe
[
writeSide
]);
::
close
(
stderrPipe
[
writeSide
]);
m_stdoutFd
=
stdoutPipe
[
readSide
];
...
...
common/threading/SubProcess.hpp
View file @
9f227007
...
...
@@ -33,7 +33,7 @@ namespace cta { namespace threading {
*/
class
SubProcess
{
public:
SubProcess
(
const
std
::
string
&
program
,
const
std
::
list
<
std
::
string
>
&
argv
);
SubProcess
(
const
std
::
string
&
program
,
const
std
::
list
<
std
::
string
>
&
argv
,
const
std
::
string
&
str
=
""
);
~
SubProcess
();
void
wait
(
void
);
std
::
string
stdout
();
...
...
tests/SubprocessSystemTests.cpp
View file @
9f227007
...
...
@@ -38,4 +38,13 @@ TEST(SubProcessHelper, basicTests) {
ASSERT_EQ
(
127
,
sp3
.
exitValue
());
ASSERT_EQ
(
""
,
sp3
.
stderr
());
}
TEST
(
SubProcessHelper
,
testSubprocessWithStdinInput
)
{
std
::
string
stdinInput
=
"{
\"
integer_number
\"
:42,
\"
str
\"
:
\"
forty two
\"
,
\"
double_number
\"
:42.000000}"
;
cta
::
threading
::
SubProcess
sp2
(
"tee"
,
std
::
list
<
std
::
string
>
({
"tee"
}),
stdinInput
);
sp2
.
wait
();
ASSERT_EQ
(
stdinInput
,
sp2
.
stdout
());
ASSERT_EQ
(
0
,
sp2
.
exitValue
());
ASSERT_EQ
(
""
,
sp2
.
stderr
());
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment