Modern C++ way of starting and terminating a linux program - c++

I have a C++ program that, based on user input, needs to start and stop a given Linux program.
I've simplified the logic I'm currently using in the following code:
int pid = fork();
if (pid == -1)
{
//Handle error
}
else if (pid == 0)
{
execlp("my_program", nullptr);
exit(0);
}
else
{
//Main program stuff
if(/* user selects close "my_program"*/)
{
kill(pid, SIGTERM);
}
//Other main program stuff
}
Everything is working, but I was wondering if there were any other approaches, maybe more in the modern C++ style, that could be used in this situation.
Thanks in advance.

Take a look at boost::process.
There are several ways a process can be spawned. For example, in your example, you fork the process, but you don't close the cloned file descriptors in the child process. That is an often forgotten practice by those that invoke fork and can lead to binding to port problems (port/address already in use) or other hard-to-debug issues.
Even boost didn't do that quite correctly for some time.

The modern way would be to use a library like Boost.Process https://www.boost.org/doc/libs/1_78_0/doc/html/process.html or Qt https://doc.qt.io/qt-5/qprocess.html.

Related

how a daemonize a c/c++ program on Linux

I wrote a daemon program on Linux according to the guide at http://linux.die.net/man/1/daemonize, but the process crashed several times and I cannot find the reason. It has troubled me for a few days.
Today I happened to have read 'UNIX network Programming
volume 1, Third Edition' by W.Richard Stevens. And in this book, it shows an example to write daemon program. After reading the example, I realized 'Disassociate from the control terminal' is missing from my code.
Now my question is to daemonize a process, why we need disassociate from the control terminal? And does it related to the crash of the process? Is there any other place is missing in my code for daemonize?
Appreciate your replies.
Here is my code:
bool daemonize()
{
// http://linux.die.net/man/1/daemonize
// change working dir to root
(void) uchdir("/");
// close stdin, stderr, stdout
if (int fdnull = open("/dev/null", O_RDWR))
{
dup2 (fdnull, STDIN_FILENO);
dup2 (fdnull, STDOUT_FILENO);
dup2 (fdnull, STDERR_FILENO);
close(fdnull);
}
else
{
Log (ERR, "Failed to open /dev/null");
return false;
}
// detach from previous process group
if (setsid () == -1) /* request a new session (job control) */
{
Log (ERR, "Failed to detach from previous process group");
return false;
}
// inhibit others completely and group write
umask(027);
// it's dameonized!
return true;
}
The basic steps to deamonize a C or C++ program have already been mentioned in this question: Creating a daemon in Linux
Yes, the question there was for C and not for C++, but since the system calls you need to daemonize a program are C functions in both cases, that really does not make a difference.
I found this github repository useful, it has what you need to build a daemon:
Simple example of daemon for Linux
And here is a stack overflow thread why double fork is nessesary.
Not addressing your actual question but…
I wrote a daemon program on Linux according to the guide at [http://linux.die.net/man/1/daemonize][1],
please don't!
Programs that daemon-ize are the bane of any sensible init system and service tracker. The problem is, that after a program detaches from its controlling terminal and parent process it gets difficult to keep track about its state and regain control over it. The usual way of using PID files is prone to race conditions, which is especially bad if the intention is to automatically respawn such a process.
The use of daemon led to the development of several hacks, some outright ugly some kind of okay, but none of them being beautiful. Do yourself and the rest of the world a favour and don't daemonize your program.

Better way to monitor and kill other program's stalled process in linux?

I need my program to run some other program, but if the other program won't return within some time limit, I need to kill it. I came up with the following solution that seems to be working.
int main()
{
int retval, timeout=10;
pid_t proc1=fork();
if(proc1>0)
{
while(timeout)
{
waitpid(proc1, &retval, WNOHANG);
if(WIFEXITED(retval)) break; //normal termination
sleep(1);
--timeout;
if(timeout==0)
{
printf("attempt to kill process\n");
kill(proc1, SIGTERM);
break;
}
}
}
else if(proc1==0)
{
execlp("./someprogram", "./someprogram", "-a", "-b", NULL);
}
//else if fork failed etc.
return 0;
}
I need my program to be as robust as possible but I am new to programming under linux so I may not be aware of possible problems with it. My questions are:
1. Is this a proper solution to this particular problem or are there better methods?
2. Does anyone see possible problems or bugs that can lead to an unexpected behavior or a leak of system resources?
(WIFEXITED(retval)) won't return true if the program is killed by a signal (including say a crash due to segmentation violation).
Probably best to just check for a successful return from waitpid. That will only happen if the program is terminated (whether voluntarily or not).
Depending on how important it is to make sure the process is gone...
After killing the process with SIGTERM, you could sleep another second or so and if it's still not gone, use SIGKILL to be sure.

Is there a way for my win32 program to tell that the child process it launched has crashed (and not just exited)?

I wrote a multi-platform C++ class that launches a user-specified child process, and lets the user communicate with the child process's stdin/stdout, wait for the child process to exit, and so on.
In the Unix/POSIX implementation of this class, I've just added a feature that lets the caller find out whether the child process's exit was due to an unhandled signal (i.e. a crash):
bool ChildProcessDataIO :: WaitForChildProcessToExit(bool & retDidChildProcessCrash)
{
int status = 0;
int pid = waitpid(_childPID, &status, 0);
if (pid == _childPID)
{
retDidChildProcessCrash = WIFSIGNALED(status);
return true;
}
else return false; // error, couldn't get child process's status
}
... and now I'd like to add similar functionality to the Windows implementation, which currently looks like this:
bool ChildProcessDataIO :: WaitForChildProcessToExit(bool & retDidChildProcessCrash)
{
bool ret = (WaitForSingleObject(_childProcess, INFINITE) == WAIT_OBJECT_0);
if (ret)
{
/* TODO: somehow set (retDidChildProcessCrash) here */
}
return ret;
}
... but I haven't figured out how to set (retDidChildProcessCrash) to the appropriate value using the Win32 API.
Is there some way to do this, or do I just need to put a note in my documentation that this feature isn't currently implemented under Windows?
Arrange for the child to communicate with the parent to indicate completion. A shared event would be one way. If the process terminates and the parent has not received notification of success then it can conclude that the child failed.
Another option might be to use the process exit code. Will be zero on success, assuming the child follows the usual conventions. And a crash will lead to an error code indicating the form of the crash, according to this question: Predictable exit code of crashed process in Windows?
This is less reliable though. A process might terminate because it had TerminateProcess called on it, with a zero exit code. So if you control both process the first approach is safer. If you don't control the child process then the exit code might be your best shot. There's not much else you can get from a process handle of a terminated process.

Keep forked process alive if parent/child exits abnormally (C++)

I am trying to execute another command line process in parallel with the current process. However, I realize that the command line program sometimes abnormally exits, and that kills my main program as well.
// MAIN PROGRAM
pid = fork();
char *argv[] = { stuff.. };
if (pid == 0) {
int rc = execv("command line program...", argv);
}
// DO OTHER STUFF HERE.
if (pid > 0) {
waitpid(pid, 0, 0);
}
Is there any way to keep my main program running after the command line program dies abnormally? Thanks!
[UPDATE]:Yes, the main process is writing to a file where the command line is reading from, but it is a normal file, not a pipe. I receive a segfault.
It is extremely hard for me to reproduce the bug, since the child process does not crash very often. But it does happen. Randomly crashing is a known bug in the command line program, which is why I want to keep my main program alive even if the command line dies.
In your real code do you have an else here:
if (pid == 0) {
int rc = execv("command line program...", argv);
// possibly more child stuff
}
else {
// parent stuff
}
It's always a good idea to post real code when asking questions here.
Use vfork rather than fork to avoid unnecessary process cloning.
Make sure you don't crash when SIGCHLD is received by parent process.
Use proper if-then-else statement to make it clear what code executes in parent process and what happens in a child process. For example it is very likely that both child and process will execute code where // DO OTHER STUFF HERE. comment is in case execv fails.
After all, use gdb. It will tell you where the crash occurs.

Do other tasks while a system() command is being executed

I have this c++ program that is doing a simple ping on a specified ip address. I am not into networking so i'm just using the system() command in c++ to execute the ping from a shell and store the results in a file, that's easy.
The problem is that i want some dots to be printed on the screen while the system() command is being executed. i tried with:
while(system(cmd))
{
//execute the task here
}
but no success. I think that i should create threads or something.
Can you help me ? What exactly i am supposed to do in order to get this done as i want to ?
The problem with system is that it suspends execution until completion. On unix systems you will need to use a sequence of fork, execv, dup2 and pipe. This will allow you to do something more useful. See http://docs.linux.cz/programming/c/unix_examples/execill.html for an example.
You need to create a forked process using fork, like this, and using popen to read the input from the output of the command ping google.com and process it accordingly. There is an interesting guide by Beej on understanding the IPC mechanisms which is included in the code sample below...
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
int rv;
FILE *ping;
char buf[2000];
switch(pid = fork()) {
case -1:
perror("fork"); /* something went wrong */
exit(1); /* parent exits */
case 0:
// We're the child
ping = popen("ping google.com", "r");
if (ping != NULL){
fgets(buf, sizeof(buf), ping);
pclose(ping);
rv = 0;
}else{
perror("popen failed");
rv = -1;
}
exit(rv);
default:
// We're the parent...
wait(&rv);
}
// Now process the buffer
return 0;
}
Hope this helps,
Best regards,
Tom.
Edit On consideration, I believe that popen is the way to go with or without output from cmd.
Older
You are probably looking for something in the wait (2) family of commands plus a fork and exec.
From the manual page
The wait() function suspends execution of its calling process until
stat_loc information is available for a terminated child process, or a
signal is received. On return from a successful wait() call, the
stat_loc area contains termination information about the process that
exited as defined below.
Or if cmd returns some progress information you want popen (3) which is discussed in a number of existing SO questions; for instance How can I run an external program from C and parse its output?.
If you are on a unix system, you can start a command with an & to indicate that it runs in the background like this: myscript &
This will start a new process separate from the current program. You need to pick up the process number (hopefully from system, my c posix api knowledge is rusty) and then check up on it probably with a call to something like wait or waitpid with non-blocking turned on.
This is complicated for a beginner -- I'll let someone else post details if still interested.