I'm working on a graph coloring problem in C++ for my uni. I'm quite new to C, C++ and Linux. I wrote a function that's supposed to create a .svg file using graphviz and display it using eye of gnome. My idea was to fork my program, execute a graphviz command, and after it finishes, execute an eog command. The code is:
int pid, status;
if ((pid = fork()) == -1) {
std::cerr<<"fork error"<<std::endl;
exit(1);
}
if (pid == 0) {
execl("/usr/bin/dot", "dot", "-Tsvg", "temp.dot", "-o", "graph.svg", NULL);
std::cerr<<"exec error"<<std::endl;
exit(1);
}
wait(&status);
if (status != 1) {
execl("/usr/bin/eog", "eog", "graph.svg", NULL);
std::cerr<<"exec error"<<std::endl;
exit(1);
}
If I'm understanding correctly, the first argument of execl is the path to the command, which I got typing which dot and which eog. Then, we're inputing command line arguments, followed by NULL. If I remove this block of code from my program and just type these 2 commands into the terminal, everything works fine.
After running the code, the terminal displayed two "exec error" messages, which means two things:
Both execl() commands failed, and I have no idea why.
If the first exec failed, then the child process should return 1, so the "status" variable should be equal to 1, so the second exec shouldn't even be executed. I think I don't understand something, can you help? :D
int main()
{
int pid, status;
if ((pid = fork()) == -1) {
std::cerr<<"fork error"<<std::endl;
exit(1);
}
if (pid == 0) {
execl("/usr/bin/dot", "dot", "-Tsvg", "temp.dot", "-o", "graph.svg", NULL);
std::cerr<< "dot exec error: " << strerror(errno) << std::endl;
exit(1);
}
wait(&status);
if (status != 1) {
execl("/usr/bin/eog", "eog", "graph.svg", NULL);
std::cerr<< "eog exec error: " << strerror(errno) << std::endl;
exit(1);
}
}
I get this result:
Program stderr
dot exec error: No such file or directory
eog exec error: No such file or directory
Godbolt: https://godbolt.org/z/Kh66G3j3W
Related
To expand on this, by chain piping I am referring to when I have 3 separate processes:
process 1 writes to process 2,
process 2 reads from process 1 and writes to process 3,
process 3 reads from process 2 and then finishes.
I am specifically trying to handle complex commands in a C++ written bash shell. So I would be using this to execute a set of commands like this that all communicate with each other:
ls | sort | grep "exit"
where process 1 is executing ls and its stdout is written to process 2 through a pipe, etc.
I already am writing code to solve this for a project and was just wondering if my approach is correct, as right now when just doing a 2 command call of ls | grep "exit" I am getting the bash error "grep: (standard input): Bad file descriptor"
//Block for when the userInput is a complex command
else{
if (debug)
printf("Complex command: %s\n", userInput.c_str());
vector<char*> commandsVect = splitCString(const_cast<char*>(userInput.c_str()), const_cast<char*>( delimVertPipe.c_str()));
if (debug)
printVect(commandsVect);
if (pipe(fileDescriptor) == -1){
fprintf(stderr, "Pipe failed for command %s\n", userInput.c_str());
return 1;
}
for (int i = 0; i < commandsVect.size(); ++i) {
vector<char*> tokens = splitCString(const_cast<char*>(commandsVect[i]), const_cast<char*>( delimSpace.c_str()));
printf("Commands vect size is %ld\n", commandsVect.size());
printf("Parsing command \'%s\'\n", commandsVect[i]);
if (debug) {
printVect(tokens);
}
procID = fork();
//Block for the first command
if (i == 0){
if (procID < 0){
fprintf(stderr, "Fork number %d in the complex command \'%s\' failed\n", i+1, userInput.c_str());
return 1;
}
//Child process
else if (procID == 0){
//close(fileDescriptor[READ_END]);
close(STDOUT_FILENO);
//Links the write end of the pipe to the STDOUT
dup2(fileDescriptor[WRITE_END], 1);
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
tokens.push_back(nullptr); //execvp() arg array needs a NULL pointer at the end
if ( execvp(tokens[0], tokens.data()) < 0 ) {
fprintf( stderr, "execvp() call failed for the command \'%s\' inside the input string \'%s\'\n", commandsVect[i], userInput.c_str() );
return 1;
}
exit(1);
}
//Parent process
else{
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
wait(NULL);
}
}
//Block for the very last command, which will pipe input from the previous
else if (i == commandsVect.size() - 1){
if (procID < 0){
fprintf(stderr, "Fork number %d in the complex command \'%s\' failed\n", i+1, userInput.c_str());
return 1;
}
//Child process
else if (procID == 0){
//close(fileDescriptor[WRITE_END]);
close(STDIN_FILENO);
//Links the read end of the pipe to the STDIN
dup2(fileDescriptor[READ_END], 0);
close(fileDescriptor[WRITE_END]);
close(fileDescriptor[READ_END]);
tokens.push_back(nullptr); //execvp() arg array needs a NULL pointer at the end
if ( execvp(tokens[0], tokens.data()) < 0 ) {
fprintf( stderr, "execvp() call failed for the command \'%s\' inside the input string \'%s\'\n", commandsVect[i], userInput.c_str() );
return 1;
}
exit(1);
}
//Parent process
else{
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
wait(NULL);
}
}
//To note for StackOverflow, this block of code is never executed since I am only ever calling a 2 chained command like ls|grep "exit"
//Block for the middle commands. (Will pipe input from previous, and output to the next)
else{
printf("GOING THROUGH BAD CODE");
continue;
if (procID < 0){
fprintf(stderr, "Fork number %d in the complex command \'%s\' failed\n", i+1, userInput.c_str());
return 1;
}
//Child process
else if (procID == 0){
exit(1);
}
//Parent process
else{
wait(NULL);
}
}
}
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
}
This might not be possible with your larger application, but you could simplify things by letting the shell manage the pipes. Write P1 (process one), P2, and P3 as three separate executables. In stead of doing IO on pipes, each program could read from stdin and write to stdout. Simple. To execute - let bash or whatever shell you use glue the three together by calling them as...
$P1 | P2 | P3;
Under the hood, your shell is doing pretty much what you're doing in C++ (only successfully 😉). It creates a pipe for P1, which it passes to exec as stdin to launch P1 after forking. It creates an input and output pipe for P2, and binds it stdin and stdout as appropriate in the same way - passed into exec when launching P2 after the fork. P3 gets only a stdin pipe and its stdout stream goes right to the console as normal. It's not quite as sexy as doing it all in C++, but it's very robust - pretty much guaranteed to work.
I'm trying to execute ping command within my program. I'm working on newest Raspian on RaspBerry Pi B Model. It tells me that:
/bin/sh-c: 0: Can't open ping -c 1 192.168.0.12
My code(it's not finished jet, i know that following code can have some issues):
int status;
pid_t pid;
pid = fork();
const char* commandChar = commandName.c_str();
if(pid == 0)
{
execl(SHELL, SHELL "-c", commandChar, NULL);
_exit(1);
}else if(pid < 0)
{
return false;
}
commandChar = NULL;
delete commandChar;
return true;
Ping (/bin/ping) have rwsr-xr-x permissions, so why I can't execute it and how to make it work?
execl expects a file path, you are giving it the file path "/bin/ping -c 192.168.0.12", which of course doesn't exist. Look at the man page for execl, you will see that the last argument is used for the arguments to the program to be run.
When we use system() command, program wait until it complete but I am executing a process using system() and using load balance server due to which program comes to next line just after executing system command. Please note that that process may not be complete.
system("./my_script");
// after this I want to see whether it is complete or not using its pid.
// But how do i Know PID?
IsScriptExecutionComplete();
Simple answer: you can't.
The purpose of system() is to block when command is being executed.
But you can 'cheat' like this:
pid_t system2(const char * command, int * infp, int * outfp)
{
int p_stdin[2];
int p_stdout[2];
pid_t pid;
if (pipe(p_stdin) == -1)
return -1;
if (pipe(p_stdout) == -1) {
close(p_stdin[0]);
close(p_stdin[1]);
return -1;
}
pid = fork();
if (pid < 0) {
close(p_stdin[0]);
close(p_stdin[1]);
close(p_stdout[0]);
close(p_stdout[1]);
return pid;
} else if (pid == 0) {
close(p_stdin[1]);
dup2(p_stdin[0], 0);
close(p_stdout[0]);
dup2(p_stdout[1], 1);
dup2(::open("/dev/null", O_RDONLY), 2);
/// Close all other descriptors for the safety sake.
for (int i = 3; i < 4096; ++i)
::close(i);
setsid();
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1);
}
close(p_stdin[0]);
close(p_stdout[1]);
if (infp == NULL) {
close(p_stdin[1]);
} else {
*infp = p_stdin[1];
}
if (outfp == NULL) {
close(p_stdout[0]);
} else {
*outfp = p_stdout[0];
}
return pid;
}
Here you can have not only PID of the process, but also it's STDIN and STDOUT. Have fun!
Not an expert on this myself, but if you look at the man page for system:
system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed
You can go into the background within the command/script you're executing (and return immediately), but I don't think there's a specific provision in system for that case.
Ideas I can think of are:
Your command might return the pid through the return code.
Your code might want to look up the name of the command in the active processes (e.g. /proc APIs in unix-like environments).
You might want to launch the command yourself (instead of through a SHELL) using fork/exec
As the other answers said, std::system blocks until complete anyway. However, if you want to run the child process async and you are ok with boost you can use boost.process (ref):
#include <boost/process.hpp>
namespace bp = boost::process;
bp::child c(bp::search_path("echo"), "hello world");
std::cout << c.id() << std::endl;
// ... do something with ID ...
c.wait();
You can check exit status of your command by following code :
int ret = system("./my_script");
if (WIFEXITED(ret) && !WEXITSTATUS(ret))
{
printf("Completed successfully\n"); ///successful
}
else
{
printf("execution failed\n"); //error
}
I am forking a process and running a wc command using execl. Now under correct arguments, it runs fine, but when I give a wrong file name, it fails, but in both the cases the return value of
WEXITSTATUS(status)
is always 0.
I believe there is something wrong with what I am doing, but I'm not sure what is. Reading man pages and Google suggests that I should get a correct value as per the status code.
Here is my code:
#include <iostream>
#include <unistd.h>
int main(int argc, const char * argv[])
{
pid_t pid = fork();
if(pid <0){
printf("error condition");
} else if(pid == 0) {
printf("child process");
execl("/usr/bin/wc", "wc", "-l", "/Users/gabbi/learning/test/xyz.st",NULL);
printf("this happened");
} else {
int status;
wait(&status);
if( WIFEXITED( status ) ) {
std::cout << "Child terminated normally" << std::endl;
printf("exit status is %d",WEXITSTATUS(status));
return 0;
} else {
}
}
}
If you supply a name of non existing file to execl() as 1st argument it fails. If this happens the program leaves without returning any specifiy value. So the default of 0 is returned.
You could fix the for example like this:
#include <errno.h>
...
int main(int argc, const char * argv[])
{
pid_t pid = fork();
if(pid <0){
printf("error condition");
} else if(pid == 0) {
printf("child process");
execl(...); /* In case exec succeeds it never returns. */
perror("execl() failed");
return errno; /* In case exec fails return something different then 0. */
}
...
You are not passing the file name from argv to the child process
Instead of
execl("/usr/bin/wc", "wc", "-l", "/Users/gabbi/learning/test/xyz.st",NULL);
Try this,
execl("/usr/bin/wc", "wc", "-l", argv[1],NULL);
The output I got on my machine
xxx#MyUbuntu:~/cpp$ ./a.out test.txt
6 test.txt
Child terminated normally
exit status is 0
xxx#MyUbuntu:~/cpp$ ./a.out /test.txt
wc: /test.txt: No such file or directory
Child terminated normally
exit status is 1
This was an xcode issue, running from console works fine. I am a Java guy, doing some assignments in CPP. Nevertheless, it might come handy to someone getting stuck at similar issue.
I want to write a program A which executes another program B.
It is very important to execute program B from it's directory, cause it turns on program BB who sits in the same directory of B.
I mean:
./B will work
./b/B won't work
I thought about two ways to do so:
do fork(), change the PWD in env, and then call execv()
do fork(), create a temporal variable, envp, and call execve()
Lets say program A sits here: /home/a, and program B and BB sits here: /home/a/b
This is my code of program A who sits in /home/a
#include <iostream>
#include <errno.h>
int main() {
int pid;
char *cmd[20] = {"/home/a/b/B", NULL};
if ((pid = fork()) == 0) {
/*if (putenv("PWD=/home/a/b") < 0) {
fprintf(stderr, "error PWD%s\n", strerror(errno));
}*/
char *envp[20] = {"PWD=/home/a/b", NULL};
execve( cmd[0], cmd, envp);
fprintf(stderr, "error: execv: %s\n", strerror(errno));
exit(0);
} else if (pid < 0) {
fprintf(stderr, "error: fork: %s\n", strerror(errno));
exit(0);
}
fprintf(stderr, "father quits\n");
return 0;
}
I tried both of my solutions, but none of them worked,
I mean, I manage to execute program B, but it can't find program BB.
I also printed program's B's PWD, and it's /home/a/b/ - but still, it cannot execute BB.
Is it possible?
Can someone see what I am I do wrong?
Thanks
You are looking for chdir() instead of the envp manipulation.