Related
I want to execute another program within C code.
For example, I want to execute a command
./foo 1 2 3
foo is the program which exists in the same folder, and 1 2 3 are arguments.
foo program creates a file which will be used in my code.
How do I do this?
For a simple way, use system():
#include <stdlib.h>
...
int status = system("./foo 1 2 3");
system() will wait for foo to complete execution, then return a status variable which you can use to check e.g. exitcode (the command's exitcode gets multiplied by 256, so divide system()'s return value by that to get the actual exitcode: int exitcode = status / 256).
The manpage for wait() (in section 2, man 2 wait on your Linux system) lists the various macros you can use to examine the status, the most interesting ones would be WIFEXITED and WEXITSTATUS.
Alternatively, if you need to read foo's standard output, use popen(3), which returns a file pointer (FILE *); interacting with the command's standard input/output is then the same as reading from or writing to a file.
The system function invokes a shell to run the command. While this is convenient, it has well known security implications. If you can fully specify the path to the program or script that you want to execute, and you can afford losing the platform independence that system provides, then you can use an execve wrapper as illustrated in the exec_prog function below to more securely execute your program.
Here's how you specify the arguments in the caller:
const char *my_argv[64] = {"/foo/bar/baz" , "-foo" , "-bar" , NULL};
Then call the exec_prog function like this:
int rc = exec_prog(my_argv);
Here's the exec_prog function:
static int exec_prog(const char **argv)
{
pid_t my_pid;
int status, timeout /* unused ifdef WAIT_FOR_COMPLETION */;
if (0 == (my_pid = fork())) {
if (-1 == execve(argv[0], (char **)argv , NULL)) {
perror("child process execve failed [%m]");
return -1;
}
}
#ifdef WAIT_FOR_COMPLETION
timeout = 1000;
while (0 == waitpid(my_pid , &status , WNOHANG)) {
if ( --timeout < 0 ) {
perror("timeout");
return -1;
}
sleep(1);
}
printf("%s WEXITSTATUS %d WIFEXITED %d [status %d]\n",
argv[0], WEXITSTATUS(status), WIFEXITED(status), status);
if (1 != WIFEXITED(status) || 0 != WEXITSTATUS(status)) {
perror("%s failed, halt system");
return -1;
}
#endif
return 0;
}
Remember the includes:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
See related SE post for situations that require communication with the executed program via file descriptors such as stdin and stdout.
You can use fork() and system() so that your program doesn't have to wait until system() returns.
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[]){
int status;
// By calling fork(), a child process will be created as a exact duplicate of the calling process.
// Search for fork() (maybe "man fork" on Linux) for more information.
if(fork() == 0){
// Child process will return 0 from fork()
printf("I'm the child process.\n");
status = system("my_app");
exit(0);
}else{
// Parent process will return a non-zero value from fork()
printf("I'm the parent.\n");
}
printf("This is my main program and it will continue running and doing anything i want to...\n");
return 0;
}
system() executes a shell which is then responsible for parsing the arguments and executing the desired program. To execute the program directly, use fork() and exec() (which is what system() uses to execute the shell as well as what the shell itself uses to execute commands).
#include <unistd.h>
int main() {
if (fork() == 0) {
/*
* fork() returns 0 to the child process
* and the child's PID to the parent.
*/
execl("/path/to/foo", "foo", "arg1", "arg2", "arg3", 0);
/*
* We woundn't still be here if execl() was successful,
* so a non-zero exit value is appropriate.
*/
return 1;
}
return 0;
}
In C
#include <stdlib.h>
system("./foo 1 2 3");
In C++
#include <cstdlib>
std::system("./foo 1 2 3");
Then open and read the file as usual.
How about like this:
char* cmd = "./foo 1 2 3";
system(cmd);
Here's the way to extend to variable args when you don't have the args hard coded (although they are still technically hard coded in this example, but should be easy to figure out how to extend...):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int argcount = 3;
const char* args[] = {"1", "2", "3"};
const char* binary_name = "mybinaryname";
char myoutput_array[5000];
sprintf(myoutput_array, "%s", binary_name);
for(int i = 0; i < argcount; ++i)
{
strcat(myoutput_array, " ");
strcat(myoutput_array, args[i]);
}
system(myoutput_array);
I have been bussy for the last five hours with this problem so I hope someone can help me out. In my C++ program (which I develop in QTcreator on lubuntu) I want to run airodump-ng in the child process of my program. The output of airodump-ng should be directed to the STDOUT of the parent proces. This works with many other programs but strangly enough not with airodump-ng. There is simply no output in the console. This, or my Linux crashes, I get logged out and when I log back in all my programs are closed. Does anybody know why?
#include <QCoreApplication>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//execl("/usr/sbin/airodump-ng", "airodump-ng", (char*)0 );
//dup2(1, 2); //pipe stderr to stdout
pid_t pidAirodump;
pid_t pidAircrack;
int pip[2];
if (pipe(pip) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
pidAirodump = fork();
if(pidAirodump > 0)//parent
{
pidAircrack = fork();
if(pidAircrack == 0)//pidAircrack
{
close(pip[0]);
dup2(pip[1], 2);
cout << "test" << endl;
//execl("/usr/sbin/arp", "arp", (char*)0 );
execl("/usr/sbin/airodump-ng", "airodump-ng ", "mon0", (char*)0 );
exit(0);
}
}
else//pidAirodump
{
exit(0);
}
wait(NULL);
return a.exec();
}
There's a few oddities in your program. But let's start at the question - you should distinguish between execl not working and the program you're trying to execute is misbehaving. You should not be able to crash linux from a user space program without special privilieges. The code snippet you've posted should not do that (what airpodump-ng does is another question).
If execl fails it will return and set errno, I suggest that you examine that after execl instead of just exiting.
Now for the oddities:
The first fork? Why do you do that? You basically forks and exits the child right away. You fork again and let the parent wait - this should trigger on the fact that the first child has terminated rather immediately.
The pipe, why do you do that if you wan't to keep the standard out? Instead you dup standard err to the write end of the pipe, but you doesn't seem to do anything with the read end.
This question already has answers here:
Can popen() make bidirectional pipes like pipe() + fork()?
(6 answers)
Closed 3 years ago.
Is it possible to read and write to a file descriptor returned by popen. I have an interactive process I'd like to control through C. If this isn't possible with popen, is there any way around it?
As already answered, popen works in one direction. If you need to read and write, You can create a pipe with pipe(), span a new process by fork() and exec functions and then redirect its input and outputs with dup2(). Anyway I prefer exec over popen, as it gives you better control over the process (e.g. you know its pid)
EDITED:
As comments suggested, a pipe can be used in one direction only. Therefore you have to create separate pipes for reading and writing. Since the example posted before was wrong, I deleted it and created a new, correct one:
#include<unistd.h>
#include<sys/wait.h>
#include<sys/prctl.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(int argc, char** argv)
{
pid_t pid = 0;
int inpipefd[2];
int outpipefd[2];
char buf[256];
char msg[256];
int status;
pipe(inpipefd);
pipe(outpipefd);
pid = fork();
if (pid == 0)
{
// Child
dup2(outpipefd[0], STDIN_FILENO);
dup2(inpipefd[1], STDOUT_FILENO);
dup2(inpipefd[1], STDERR_FILENO);
//ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
//replace tee with your process
execl("/usr/bin/tee", "tee", (char*) NULL);
// Nothing below this line should be executed by child process. If so,
// it means that the execl function wasn't successfull, so lets exit:
exit(1);
}
// The code below will be executed only by parent. You can write and read
// from the child using pipefd descriptors, and you can send signals to
// the process using its pid by kill() function. If the child process will
// exit unexpectedly, the parent process will obtain SIGCHLD signal that
// can be handled (e.g. you can respawn the child process).
//close unused pipe ends
close(outpipefd[0]);
close(inpipefd[1]);
// Now, you can write to outpipefd[1] and read from inpipefd[0] :
while(1)
{
printf("Enter message to send\n");
scanf("%s", msg);
if(strcmp(msg, "exit") == 0) break;
write(outpipefd[1], msg, strlen(msg));
read(inpipefd[0], buf, 256);
printf("Received answer: %s\n", buf);
}
kill(pid, SIGKILL); //send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
The reason popen() and friends don't offer bidirectional communication is that it would be deadlock-prone, due to buffering in the subprocess. All the makeshift pipework and socketpair() solutions discussed in the answers suffer from the same problem.
Under UNIX, most commands cannot be trusted to read one line and immediately process it and print it, except if their standard output is a tty. The reason is that stdio buffers output in userspace by default, and defers the write() system call until either the buffer is full or the stdio stream is closed (typically because the program or script is about to exit after having seen EOF on input). If you write to such a program's stdin through a pipe, and now wait for an answer from that program's stdout (without closing the ingress pipe), the answer is stuck in the stdio buffers and will never come out - This is a deadlock.
You can trick some line-oriented programs (eg grep) into not buffering by using a pseudo-tty to talk to them; take a look at libexpect(3). But in the general case, you would have to re-run a different subprocess for each message, allowing to use EOF to signal the end of each message and cause whatever buffers in the command (or pipeline of commands) to be flushed. Obviously not a good thing performance-wise.
See more info about this problem in the perlipc man page (it's for bi-directional pipes in Perl but the buffering considerations apply regardless of the language used for the main program).
You want something often called popen2. Here's a basic implementation without error checking (found by a web search, not my code):
// http://media.unpythonic.net/emergent-files/01108826729/popen2.c
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "popen2.h"
int popen2(const char *cmdline, struct popen2 *childinfo) {
pid_t p;
int pipe_stdin[2], pipe_stdout[2];
if(pipe(pipe_stdin)) return -1;
if(pipe(pipe_stdout)) return -1;
//printf("pipe_stdin[0] = %d, pipe_stdin[1] = %d\n", pipe_stdin[0], pipe_stdin[1]);
//printf("pipe_stdout[0] = %d, pipe_stdout[1] = %d\n", pipe_stdout[0], pipe_stdout[1]);
p = fork();
if(p < 0) return p; /* Fork failed */
if(p == 0) { /* child */
close(pipe_stdin[1]);
dup2(pipe_stdin[0], 0);
close(pipe_stdout[0]);
dup2(pipe_stdout[1], 1);
execl("/bin/sh", "sh", "-c", cmdline, NULL);
perror("execl"); exit(99);
}
childinfo->child_pid = p;
childinfo->to_child = pipe_stdin[1];
childinfo->from_child = pipe_stdout[0];
close(pipe_stdin[0]);
close(pipe_stdout[1]);
return 0;
}
//#define TESTING
#ifdef TESTING
int main(void) {
char buf[1000];
struct popen2 kid;
popen2("tr a-z A-Z", &kid);
write(kid.to_child, "testing\n", 8);
close(kid.to_child);
memset(buf, 0, 1000);
read(kid.from_child, buf, 1000);
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
printf("from child: %s", buf);
printf("waitpid() -> %d\n", waitpid(kid.child_pid, NULL, 0));
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
return 0;
}
#endif
popen() can only open the pipe in read or write mode, not both. Take a look at this thread for a workaround.
In one of netresolve backends I'm talking to a script and therefore I need to write to its stdin and read from its stdout. The following function executes a command with stdin and stdout redirected to a pipe. You can use it and adapt it to your liking.
static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
int p1[2], p2[2];
if (!pid || !infd || !outfd)
return false;
if (pipe(p1) == -1)
goto err_pipe1;
if (pipe(p2) == -1)
goto err_pipe2;
if ((*pid = fork()) == -1)
goto err_fork;
if (*pid) {
/* Parent process. */
*infd = p1[1];
*outfd = p2[0];
close(p1[0]);
close(p2[1]);
return true;
} else {
/* Child process. */
dup2(p1[0], 0);
dup2(p2[1], 1);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execvp(*command, command);
/* Error occured. */
fprintf(stderr, "error running %s: %s", *command, strerror(errno));
abort();
}
err_fork:
close(p2[1]);
close(p2[0]);
err_pipe2:
close(p1[1]);
close(p1[0]);
err_pipe1:
return false;
}
https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46
(I used the same code in Can popen() make bidirectional pipes like pipe() + fork()?)
Use forkpty (it's non-standard, but the API is very nice, and you can always drop in your own implementation if you don't have it) and exec the program you want to communicate with in the child process.
Alternatively, if tty semantics aren't to your liking, you could write something like forkpty but using two pipes, one for each direction of communication, or using socketpair to communicate with the external program over a unix socket.
You can't use popen to use two-way pipes.
In fact, some OSs don't support two-way pipes, in which case a socket-pair (socketpair) is the only way to do it.
popen works for me in both directions (read and write)
I have been using a popen() pipe in both directions..
Reading and writing a child process stdin and stdout with the file descriptor returned by popen(command,"w")
It seems to work fine..
I assumed it would work before I knew better, and it does.
According posts above this shouldn't work.. which worries me a little bit.
gcc on raspbian (raspbery pi debian)
I'm trying to use a socketpair to have a parent process provide input to a child process that execs a different program (e.g., grep) and then read the resulting output. The program hangs in the while loop that reads the output from the program that the child execs.. The child dupes stdin and stdout on to its end of the socketpair and the parent and the child both close their unused end of the pair.
Interestingly, if the child execs a program that I wrote (OK, I ripped it off from Stevens Advanced Programming in the Unix Environment) everything works as expected. However, if the child execs grep (or some other standard program) the parent invariably hangs in trying to read the output. I can't tell if the input is not reaching grep or if the grep cannot determine the end of the input or if the output is somehow being lost.
Here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <cstdio>
#include <cerrno>
#include <iostream>
using namespace std;
void
sigpipe_handler(int sig, siginfo_t *siginfo, void * context) {
cout << "caught SIGPIPE\n";
pid_t pid;
if (errno == EPIPE) {
throw "SIGPIPE caught";
}
}
int main(int argc, char** argv) {
struct sigaction sa;
memset(&sa, '\0', sizeof(struct sigaction));
sa.sa_sigaction = sigpipe_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPIPE, &sa, NULL);
int sp[2];
socketpair(PF_UNIX, SOCK_STREAM, AF_UNIX, sp);
pid_t childPid = fork();
if (childPid == 0) {
close(sp[0]);
if (dup2(sp[1], STDIN_FILENO) != STDIN_FILENO) throw "dup2 error to stdin";
if (dup2(sp[1], STDOUT_FILENO) != STDOUT_FILENO) throw "dup2 error to stdout";
execl("/bin/grep", "grep", "-n", "namespace", (char*)NULL);
} else {
close(sp[1]);
char line[80];
int n;
try {
while (fgets(line, 80, stdin) != NULL) {
n = strlen(line);
if (write(sp[0], line, n) != n) {
throw "write error to pipe";
}
if ((n=read(sp[0], line, 80)) < 0) { // hangs here
throw "read error from pipe";
}
if (n ==0) {
throw "child closed pipe";
break;
}
line[n] = 0;
if (fputs(line, stdout) == EOF) {
throw "puts error";
}
if (ferror(stdin)) {
throw "fgets error on stdin";
}
exit(0);
}
} catch (const char* e) {
cout << e << endl;
}
int status;
waitpid(childPid, &status, 0);
}
}
Your code hangs as grep's output may be less than 80 bytes and you are issuing a blocking read on sp[0]. The proper way of doing this is by marking both sockets as non-blocking and selecting() over both of them.
You also forgot to close(sp[0]) before you wait(), which will leave your child process waiting for input.
You cannot achieve deadlock-free bidirectional communication with a subprocess using UNIX pipes or socketpairs, because you don't have control over buffering in the subprocess.
It just so happens that cat can be trusted to read one line and immediately print it, regardless of whether its standard output is a tty, a pipe or a socket. This is not the case with grep (and actually most programs using stdio), which will buffer output in-process (in the stdio buffers) and defer the write() call until either the buffer is full or the stdio stream is closed (typically because grep is about to exit after having seen EOF on input).
You can trick line-oriented programs (including grep) into not buffering by using a pseudo-tty instead; take a look at libexpect(3). But in the general case, you would have to re-run a different subprocess for each message, which allows to use EOF to signal the end of each message and cause whatever buffers in the command (or pipeline of commands) to be flushed.
See more info about this problem in the perlipc man page (it's for bi-directional pipes in Perl but the buffering considerations apply regardless of the language used for the main program).
It works fine with cat, so the problem is with grep. May be grep output behave differently when connected to something else than a terminal. Or it is not detecting the pattern for some reason.
When I execute "python" from the terminal with no arguments it brings up the Python interactive shell.
When I execute "cat | python" from the terminal it doesn't launch the interactive mode. Somehow, without getting any input, it has detected that it is connected to a pipe.
How would I do a similar detection in C or C++ or Qt?
Use isatty:
#include <stdio.h>
#include <io.h>
...
if (isatty(fileno(stdin)))
printf( "stdin is a terminal\n" );
else
printf( "stdin is a file or a pipe\n");
(On windows they're prefixed with underscores: _isatty, _fileno)
Summary
For many use cases the POSIX function isatty() is all what it is needed to detect if stdin is connected to a terminal. A minimal example:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (isatty(fileno(stdin)))
puts("stdin is connected to a terminal");
else
puts("stdin is NOT connected to a terminal");
return 0;
}
The following section compares different methods that can be used if different degrees of interactivity have to be tested.
Methods in Detail
There are several methods to detect if a program is running interactively.
Following table shows an overview:
cmd\method ctermid open isatty fstat
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
./test /dev/tty OK YES S_ISCHR
./test < test.cc /dev/tty OK NO S_ISREG
cat test.cc | ./test /dev/tty OK NO S_ISFIFO
echo ./test | at now /dev/tty FAIL NO S_ISREG
The results are from a Ubuntu Linux 11.04 system using the following program:
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
int main() {
char tty[L_ctermid+1];
ctermid(tty);
printf("ID: %s\n", tty);
int fd = open(tty, O_RDONLY);
if (fd < 0) perror("Could not open terminal");
else {
printf("Opened terminal\n");
struct termios term;
int r = tcgetattr(fd, &term);
if (r < 0) perror("Could not get attributes");
else printf("Got attributes\n");
}
if (isatty(fileno(stdin))) printf("Is a terminal\n");
else printf("Is not a terminal\n");
struct stat stats;
int r = fstat(fileno(stdin), &stats);
if (r < 0) perror("fstat failed");
else {
if (S_ISCHR(stats.st_mode)) printf("S_ISCHR\n");
else if (S_ISFIFO(stats.st_mode)) printf("S_ISFIFO\n");
else if (S_ISREG(stats.st_mode)) printf("S_ISREG\n");
else printf("unknown stat mode\n");
}
return 0;
}
Terminal device
If the interactive session needs certain capabilities, you can open the
terminal device and (temporarily) set terminal attributes you need
via tcsetattr().
Python Example
The Python code that decides whether the interpreter runs interactively uses isatty(). The Function PyRun_AnyFileExFlags()
/* Parse input from a file and execute it */
int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
PyCompilerFlags *flags)
{
if (filename == NULL)
filename = "???";
if (Py_FdIsInteractive(fp, filename)) {
int err = PyRun_InteractiveLoopFlags(fp, filename, flags);
calls Py_FdIsInteractive()
/*
* The file descriptor fd is considered ``interactive'' if either
* a) isatty(fd) is TRUE, or
* b) the -i flag was given, and the filename associated with
* the descriptor is NULL or "<stdin>" or "???".
*/
int
Py_FdIsInteractive(FILE *fp, const char *filename)
{
if (isatty((int)fileno(fp)))
return 1;
which calls isatty().
Conclusion
There are different degrees of interactivity. For checking if stdin is connected to a pipe/file or a real terminal isatty() is a natural method to do that.
Probably they are checking the type of file that "stdin" is with fstat, something like this:
struct stat stats;
fstat(0, &stats);
if (S_ISCHR(stats.st_mode)) {
// Looks like a tty, so we're in interactive mode.
} else if (S_ISFIFO(stats.st_mode)) {
// Looks like a pipe, so we're in non-interactive mode.
}
Of course Python is open source, so you can just look at what they do and know for sure:
http://www.python.org/ftp/python/2.6.2/Python-2.6.2.tar.bz2
On Windows you can use GetFileType.
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD type = GetFileType(hIn);
switch (type) {
case FILE_TYPE_CHAR:
// it's from a character device, almost certainly the console
case FILE_TYPE_DISK:
// redirected from a file
case FILE_TYPE_PIPE:
// piped from another program, a la "echo hello | myprog"
case FILE_TYPE_UNKNOWN:
// this shouldn't be happening...
}
Call stat() or fstat() and see if S_IFIFO is set in st_mode.
You can call stat(0, &result) and check for !S_ISREG( result.st_mode ). That's Posix, not C/C++, though.