Reputation: 105
I have a Linux service (daemon) that has multiple-threads and uses boost io_service listening on a TCP socket. When I receive a certain message on that socket I want to start another service with e.g. /etc/init.d/corosync start
.
The problem is that after starting the service, when I quit my own service, the other service has inherited the sockets from my own service and it remains in a strange status where I cannot stop it the usual way.
Before exiting my process "MonitorSipServer" the open socket shows like this:
netstat -anop |grep 144
tcp 0 0 0.0.0.0:20144 0.0.0.0:* LISTEN 4480/MonitorSipServ off (0.00/0/0)
tcp 0 0 140.0.24.181:20144 140.0.101.75:47036 ESTABLISHED 4480/MonitorSipServ off (0.00/0/0)
After exiting my process "MonitorSipServer" the open socket shows like this:
netstat -anop |grep 144
tcp 0 0 0.0.0.0:20144 0.0.0.0:* LISTEN 4502/corosync off (0.00/0/0)
tcp 0 0 140.0.24.181:20144 140.0.101.75:47036 ESTABLISHED 4502/corosync off (0.00/0/0)
I have already tried with system
, popen
and with fork
+ execv
or execve
with null
environment. It's always the same result or worse.
My last hope was the Linux setsid
command, but it has not worked either.
Any help would be appreciated. Regards, Jan
Upvotes: 6
Views: 2805
Reputation: 51931
Boost.Asio supports the fork()
system call:
It requires the program to prepare and notify the io_service
of the fork with io_service::notify_fork()
:
io_service_.notify_fork(boost::asio::io_service::fork_prepare);
if (fork() == 0)
{
io_service_.notify_fork(boost::asio::io_service::fork_child);
...
}
else
{
io_service_.notify_fork(boost::asio::io_service::fork_parent);
...
}
Failing to use this pattern results in unspecified behavior. For some configurations, the parent will fail to receive event notifications, as they are consumed by the child.
It is the program's responsibility to handle any file descriptors accessible through Boost.Asio's public API. For example, if the parent has an open acceptor
, then the child would need to explicitly invoke acceptor::close()
before spawning its own children processes or replacing the process image. The fork support documentation states:
Note that any file descriptors accessible via Boost.Asio's public API (e.g. the descriptors underlying
basic_socket<>
,posix::stream_descriptor
, etc.) are not altered during a fork. It is the program's responsibility to manage these as required.
fork()
states that the child may only invoke async-signal-safe operations between fork()
and one of the exec()
functions. io_service::notify_fork()
does not make this guarantee, and the current implementation invokes non async-signal-safe operations.With that said, I have seen applications not observe undesirable behavior when using Boost.Asio's fork()
support in a multi-threaded process. However, if one wishes to meet the requirements of both fork()
and Boost.Asio, then one solution is to fork the daemon process when it is still single threaded. The child process will remain single threaded and perform fork()
and exec()
when it is told to do so by the parent process via inter-process communication. This solution is suggested and demonstrated in this answer.
Upvotes: 3
Reputation: 4752
If you're referring to the socket descriptors themselves being inherited by exec'd child processes, and this being undesirable, then you could pass SOCK_CLOEXEC
when creating the sockets with socket(2)
to make sure that they are closed when you execute other programs. (This won't close the connection by the way, since your program still has a reference to the socket.)
If you are using some higher-level library, then check if there is some way to get it to pass this flag, or do fcntl(sock_fd, F_SETFD, fcntl(sock_fd, F_GETFD) | FD_CLOEXEC)
to set the close-on-exec flag on the descriptor after it is created if you can get to it. (The fcntl(2)
approach can be racy in a multi-threaded environment though, since some thread could exec(3)
a program between the point where the socket is created and when FD_CLOEXEC
is set on it.)
If the above won't work, then you could manually fork(2)
and then close(2)
the socket descriptors before executing a service. The advantage of SOCK_CLOEXEC
is that the sockets will only be closed if the exec*()
is actually successful, which sometimes makes it easier to recover from errors. In addition, SOCK_CLOEXEC
can avoid some races and makes it harder to forget to close descriptors.
Upvotes: 7
Reputation: 136385
I have a Linux service (daemon) that has multiple-threads and uses boost io_service listening on a TCP socket. When I receive a certain message on that socket I want to start another service with e.g. /etc/init.d/corosync start
For modern Linux'es with systemd you may like to look into socket activation. That is, your main service daemon will just send a datagram to a unix socket (that datagram may contain file descriptors you would like to explicitly pass to the other service). systemd would start that other service for you if has not started yet.
The benefit of using systemd is that your service does not need to run as root in order to be able to start another service and the service processes are completely isolated from each other (no inheritance of anything as when fork
ing).
Upvotes: 0