Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
81a0e597
Commit
81a0e597
authored
Mar 10, 2015
by
Francis Dupont
Browse files
[master] Fixed races in ProcessSpawn util (#3733)
parent
bfce8a98
Changes
3
Hide whitespace changes
Inline
Side-by-side
src/lib/util/process_spawn.cc
View file @
81a0e597
...
...
@@ -19,11 +19,28 @@
#include <map>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
namespace
isc
{
namespace
util
{
/// @brief Type for process state
struct
ProcessState
{
/// @brief Constructor
ProcessState
()
:
running_
(
true
),
status_
(
0
)
{
}
/// @brief true until the exit status is collected
bool
running_
;
/// @brief 0 or the exit status
int
status_
;
};
typedef
std
::
map
<
pid_t
,
ProcessState
>
ProcessStates
;
/// @brief Implementation of the @c ProcessSpawn class.
///
/// This pimpl idiom is used by the @c ProcessSpawn in this case to
...
...
@@ -95,7 +112,7 @@ public:
/// exception is thrown.
///
/// @param pid A process pid.
void
clearStat
us
(
const
pid_t
pid
);
void
clearStat
e
(
const
pid_t
pid
);
private:
...
...
@@ -125,7 +142,7 @@ private:
SignalSetPtr
signals_
;
/// @brief A map holding the status codes of executed processes.
std
::
map
<
pid_t
,
int
>
process_stat
us
_
;
ProcessStates
process_stat
e
_
;
/// @brief Path to an executable.
std
::
string
executable_
;
...
...
@@ -136,7 +153,7 @@ private:
ProcessSpawnImpl
::
ProcessSpawnImpl
(
const
std
::
string
&
executable
,
const
ProcessArgs
&
args
)
:
signals_
(
new
SignalSet
(
SIGCHLD
)),
process_stat
us
_
(),
:
signals_
(
new
SignalSet
(
SIGCHLD
)),
process_stat
e
_
(),
executable_
(
executable
),
args_
(
new
char
*
[
args
.
size
()
+
2
])
{
// Set the handler which is invoked immediately when the signal
// is received.
...
...
@@ -154,7 +171,6 @@ ProcessSpawnImpl::ProcessSpawnImpl(const std::string& executable,
}
}
ProcessSpawnImpl
::~
ProcessSpawnImpl
()
{
int
i
=
0
;
// Deallocate strings in the array of arguments.
...
...
@@ -183,12 +199,20 @@ ProcessSpawnImpl::getCommandLine() const {
pid_t
ProcessSpawnImpl
::
spawn
()
{
// Protect us against SIGCHLD signals
sigset_t
sset
;
sigemptyset
(
&
sset
);
sigaddset
(
&
sset
,
SIGCHLD
);
sigprocmask
(
SIG_BLOCK
,
&
sset
,
0
);
// Create the child
pid_t
pid
=
fork
();
if
(
pid
<
0
)
{
isc_throw
(
ProcessSpawnError
,
"unable to fork current process"
);
}
else
if
(
pid
==
0
)
{
// We're in the child process. Run the executable.
// We're in the child process.
sigprocmask
(
SIG_UNBLOCK
,
&
sset
,
0
);
// Run the executable.
if
(
execvp
(
executable_
.
c_str
(),
args_
)
!=
0
)
{
// We may end up here if the execvp failed, e.g. as a result
// of issue with permissions or invalid executable name.
...
...
@@ -198,25 +222,28 @@ ProcessSpawnImpl::spawn() {
exit
(
EXIT_SUCCESS
);
}
process_status_
[
pid
]
=
0
;
// We're in the parent process.
process_state_
.
insert
(
std
::
pair
<
pid_t
,
ProcessState
>
(
pid
,
ProcessState
()));
sigprocmask
(
SIG_UNBLOCK
,
&
sset
,
0
);
return
(
pid
);
}
bool
ProcessSpawnImpl
::
isRunning
(
const
pid_t
pid
)
const
{
if
(
process_status_
.
find
(
pid
)
==
process_status_
.
end
())
{
ProcessStates
::
const_iterator
proc
=
process_state_
.
find
(
pid
);
if
(
proc
==
process_state_
.
end
())
{
isc_throw
(
BadValue
,
"the process with the pid '"
<<
pid
<<
"' hasn't been spawned and it status cannot be"
" returned"
);
}
return
(
(
pid
!=
0
)
&&
(
kill
(
pid
,
0
)
==
0
)
);
return
(
proc
->
second
.
running_
);
}
bool
ProcessSpawnImpl
::
isAnyRunning
()
const
{
for
(
std
::
map
<
pid_t
,
int
>
::
const_iterator
proc
=
process_stat
us
_
.
begin
();
proc
!=
process_stat
us
_
.
end
();
++
proc
)
{
if
(
isRunning
(
proc
->
first
)
)
{
for
(
ProcessStates
::
const_iterator
proc
=
process_stat
e
_
.
begin
();
proc
!=
process_stat
e
_
.
end
();
++
proc
)
{
if
(
proc
->
second
.
running_
)
{
return
(
true
);
}
}
...
...
@@ -225,13 +252,13 @@ ProcessSpawnImpl::isAnyRunning() const {
int
ProcessSpawnImpl
::
getExitStatus
(
const
pid_t
pid
)
const
{
std
::
map
<
pid_t
,
int
>
::
const_iterator
status
=
process_stat
us
_
.
find
(
pid
);
if
(
status
==
process_stat
us
_
.
end
())
{
ProcessStates
::
const_iterator
proc
=
process_stat
e
_
.
find
(
pid
);
if
(
proc
==
process_stat
e
_
.
end
())
{
isc_throw
(
InvalidOperation
,
"the process with the pid '"
<<
pid
<<
"' hasn't been spawned and it status cannot be"
" returned"
);
}
return
(
WEXITSTATUS
(
status
->
second
));
return
(
WEXITSTATUS
(
proc
->
second
.
status_
));
}
char
*
...
...
@@ -249,27 +276,34 @@ ProcessSpawnImpl::allocateArg(const std::string& src) const {
bool
ProcessSpawnImpl
::
waitForProcess
(
int
signum
)
{
// We're only interested in SIGCHLD.
if
(
signum
==
SIGCHLD
)
{
if
(
signum
!=
SIGCHLD
)
{
return
(
false
);
}
for
(;;)
{
int
status
=
0
;
pid_t
pid
=
waitpid
(
-
1
,
&
status
,
0
);
if
(
pid
>
0
)
{
/// @todo Check that the terminating process was started
/// by our instance of ProcessSpawn and only handle it
/// if it was.
process_status_
[
pid
]
=
status
;
pid_t
pid
=
waitpid
(
-
1
,
&
status
,
WNOHANG
);
if
(
pid
<=
0
)
{
break
;
}
ProcessStates
::
iterator
proc
=
process_state_
.
find
(
pid
);
/// Check that the terminating process was started
/// by our instance of ProcessSpawn
if
(
proc
!=
process_state_
.
end
())
{
// In this order please
proc
->
second
.
status_
=
status
;
proc
->
second
.
running_
=
false
;
}
return
(
true
);
}
return
(
fals
e
);
return
(
tru
e
);
}
void
ProcessSpawnImpl
::
clearStat
us
(
const
pid_t
pid
)
{
ProcessSpawnImpl
::
clearStat
e
(
const
pid_t
pid
)
{
if
(
isRunning
(
pid
))
{
isc_throw
(
InvalidOperation
,
"unable to remove the status for the"
"process (pid: "
<<
pid
<<
") which is still running"
);
}
process_stat
us
_
.
erase
(
pid
);
process_stat
e
_
.
erase
(
pid
);
}
ProcessSpawn
::
ProcessSpawn
(
const
std
::
string
&
executable
,
...
...
@@ -307,8 +341,8 @@ ProcessSpawn::getExitStatus(const pid_t pid) const {
}
void
ProcessSpawn
::
clearStat
us
(
const
pid_t
pid
)
{
return
(
impl_
->
clearStat
us
(
pid
));
ProcessSpawn
::
clearStat
e
(
const
pid_t
pid
)
{
return
(
impl_
->
clearStat
e
(
pid
));
}
}
...
...
src/lib/util/process_spawn.h
View file @
81a0e597
...
...
@@ -96,6 +96,10 @@ public:
/// @brief Checks if the process is still running.
///
/// Note that only a negative (false) result is reliable as the child
/// process can exit between the time its state is checked and this
/// function returns.
///
/// @param pid ID of the child processes for which state should be checked.
///
/// @return true if the child process is running, false otherwise.
...
...
@@ -111,6 +115,9 @@ public:
/// If the process is still running, the previous status is returned
/// or 0, if the process is being ran for the first time.
///
/// @note @c ProcessSpawn::isRunning should be called and have returned
/// false before using @c ProcessSpawn::getExitStatus.
///
/// @param pid ID of the child process for which exit status should be
/// returned.
///
...
...
@@ -123,8 +130,13 @@ public:
/// If the process is still running, the status is not removed and the
/// exception is thrown.
///
/// Note @c ProcessSpawn::isRunning must be called and have returned
/// false before using clearState(). And of course
/// @c ProcessSpawn::getExitStatus should be called first, if there is
/// some interest in the status.
///
/// @param pid A process pid.
void
clearStat
us
(
const
pid_t
pid
);
void
clearStat
e
(
const
pid_t
pid
);
private:
...
...
src/lib/util/tests/process_spawn_unittest.cc
View file @
81a0e597
...
...
@@ -90,11 +90,11 @@ TEST(ProcessSpawn, spawnTwoProcesses) {
// Clear the status of the first process. An attempt to get the status
// for the cleared process should result in exception. But, there should
// be no exception for the second process.
process
.
clearStat
us
(
pid1
);
process
.
clearStat
e
(
pid1
);
EXPECT_THROW
(
process
.
getExitStatus
(
pid1
),
InvalidOperation
);
EXPECT_NO_THROW
(
process
.
getExitStatus
(
pid2
));
process
.
clearStat
us
(
pid2
);
process
.
clearStat
e
(
pid2
);
EXPECT_THROW
(
process
.
getExitStatus
(
pid2
),
InvalidOperation
);
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a 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