Executing shell command and reading its output on a C program - c

I am trying to create a function which takes a shell command as an argument , uses fork to spawn a new process which executes the command. I also want to redirect the standard output of the command so the caller of the function can read it using a FILE* pointer.
static FILE* runCommand(char* command){
int pfd[2];
if(pipe(pfd)<0)
return NULL;
if(pid=fork()==0){ //child
close(pfd[0]);
dup2(pfd[1],1); //redirect output to pipe for writing
execlp(command,(char*)0);
}
close(pfd[1]);
//return a file pointer/descriptor here?
}
I am not sure how to return a file pointer which can be used to read the output of the command. Also is that the correct way to execute a command on the shell?
ps. I read about popen but there is a good reason I can't use it, thus I have to implement this functionality myself.
Thank you

One bug in that code is that you assign to a variable pid that is not declared anywhere. And pid will always be 1 in the parent, because the code as written is equivalent to pid=(fork()==0) rather than (pid=fork())==0.
You should also close pfd[1] after the dup2 call. And for good measure, check for errors from dup2 and execlp.
The answer to your real question is to use fdopen.

Use fdopen to associate an existing file descriptor with a FILE * object. Everything else looks pretty good.

Related

C - Redirecting IO of Child Process

I am trying to redirect the IO of a child process (after fork()) into a file, and I can't figure out why it isn't working.
Here's what I've done:
if(fork() == 0){
execv(exe, (char*[]){ exe, "> temp.exe" });
...
And the executable runs, but it doesn't redirect to the file. I would appreciate it if anyone could explain what am I doing wrong, and how I should do it. I'm getting a feeling I need to redirect before the execv() but I have no idea how to.
Thanks in advance!
Shell redirections (like > file) are implemented by the shell. By using execve(), you are bypassing the shell; the child process will see "> temp.exe" in argv, and will attempt to process it as an argument.
If you want to redirect output to a file, the easiest approach will be to implement that redirection yourself by opening the file after forking and using dup2() to move its file descriptor to standard output:
if (fork() == 0) {
int fd = open("temp.exe", O_CREAT | O_WRONLY, 0666);
if (fd < 0) { handle error... exit(255); }
dup2(fd, 1);
close(fd);
execv(exe, ...);
}
The execX() family of calls does not have the same flexibility as, say system() or popen(). These latter methods call shell to do the interpretation of the command.
The arguments to the execX call are the exact path of the program you want to run and the arguments you want to give to that program. Any "shell" features such as redirection you have to implement yourself before calling execX.
Alternatively, you can let shell actually do the work, execp("sh","sh",myexe+" >test.txt");, but that is lazy, and then why not just use system anyway?
Two very useful methods are pipe() and dup2(): pipe allows you to create pipes to your host program; dup2 lets you set a scenario where the program being executed thinks that it is writing to stdout (1), or reading from stdin (0), but is actually writing or reading to a file or pipe that you created.
You will get a long way by reading the man pages for pipe and dup2, or in google looking for exec pipe and dup2, so I won't take your enjoyment away by writing a full implementation here.

How to know if a command given to execlp() exists?

I've searched quite a lot, but I still don't have an answer for this. I've got a program that creates other processes by asking the user the desired command, then I use execlp to open this new process. I wanted to know if there's an easy way to the parent process find out if the command was executed, or if the received command doesn't exist.
I have the following code:
if (executarComando(comando) != OK)
fprintf(stderr,"Nao foi possivel executar esse comando. ");
where executarComando is:
int executarComando(char* cmd) {
if ( execlp("xterm", "xterm", "-hold", "-e", cmd, NULL) == ERROR) // error
return ERROR;
return OK;
}
Your problem is that your execlp always succeeds; it's running xterm, not the command you're passing to the shell xterm runs. You will need to add some kind of communication channel between your program and this shell so that you can communicate back success or failure. I would do something like replacing the command with
( command ) 99>&- ; echo $? >&99
Then, open a pipe before forking to call execlp, and in the child, use dup2 to create as file descriptor number 99 corresponding to the write end of the pipe. Now, you can read back the exit status of the command across the pipe.
Just hope xterm doesn't go closing all file descriptors on you; otherwise you're out of luck and you'll have to make a temporary fifo (via mkfifo) somewhere in the filesystem to achieve the same result.
Note that the number 99 was arbitrary; anything other than 0, 1, or 2 should work.
There's no trivial way; a convention often used is that the fork()ed child will report the error and exit(-1) (or exit(255)) in the specific case where the exec() fails, and most commands avoid using that for their own failure modes.

chdir on a file to use execl

i'm programming on the command find in c and i'm blocked when i want to use exec on a file.
if it is a repertory it is simple, i just use chdir(path) but on file i have the error not a directory so i can't use exec on it
this is what i have
if (chdir(resultat[i])==-1){
perror("erreur changement de repertoire\n");
exit(1);
}
execl("/bin/ls","ls",(char *)0);
resultat[i] is the path of my file
thanks for your help
(I am not exactly sure what you are trying to do and I do not know what the rest of your code is doing, so this is only a shot in the dark...)
Have you considered passing resultat[i] as an argument to ls? ls will probably do The Right Thing(TM) on it own. I.e. replace the snippet that you supplied with this:
execl("/bin/ls","ls", resultat[i], (char *)0);
I assume that you have already done all the needed work (e.g. fork()) to avoid your application terminating prematurely at the exec() call...
I assume this is Linux (ls is linux only haha), so try using opendir first and see if you get a valid handle, then try to read one file out of it. If you get a valid filename, you can then check if its a file...
http://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html#Reading_002fClosing-Directory

capturing commandline output directly in a buffer

I want to execute a command using system() command or execl and want to capture the output directly in a buffer in C. Is ther any possibility to capture the output in a buffer using dup() system call or using pipe(). I dont want to use any file in between using mkstemp or any other temporary file. please help me in this.Thanks in advance.
I tried it with fork() creating two process and piping the output and it is working.However I dont want to use fork system call since i am going to run the module infinitely using seperate thread and it is invoking lot of fork() and system is running out of resources sometimes after.
To be clear about what i am doing is capturing an output of a shell script in a buffer processing the ouput and displaying it in a window which i have designed using ncurses.Thankyou.
Here is some code for capturing the output of program; it uses exec() instead of system(), but that is straightforward to accomodate by invoking the shell directly:
How can I implement 'tee' programmatically in C?
void tee(const char* fname) {
int pipe_fd[2];
check(pipe(pipe_fd));
const pid_t pid = fork();
check(pid);
if(!pid) { // our log child
close(pipe_fd[1]); // Close unused write end
FILE* logFile = fname? fopen(fname,"a"): NULL;
if(fname && !logFile)
fprintf(stderr,"cannot open log file \"%s\": %d (%s)\n",fname,errno,strerror(errno));
char ch;
while(read(pipe_fd[0],&ch,1) > 0) {
//### any timestamp logic or whatever here
putchar(ch);
if(logFile)
fputc(ch,logFile);
if('\n'==ch) {
fflush(stdout);
if(logFile)
fflush(logFile);
}
}
putchar('\n');
close(pipe_fd[0]);
if(logFile)
fclose(logFile);
exit(EXIT_SUCCESS);
} else {
close(pipe_fd[0]); // Close unused read end
// redirect stdout and stderr
dup2(pipe_fd[1],STDOUT_FILENO);
dup2(pipe_fd[1],STDERR_FILENO);
close(pipe_fd[1]);
}
}
A simple way is to use popen ( http://www.opengroup.org/onlinepubs/007908799/xsh/popen.html), which returns a FILE*.
You can try popen(), but your fundamental problem is running too many processes. You have to make sure your commands finish, otherwise you will end up with exactly the problems you're having. popen() internally calls fork() anyway (or the effect is as if it did).
So, in the end, you have to make sure that the program you want to run from your threads exits "soon enough".
You want to use a sequence like this:
Call pipe once per stream you want to create (eg. stdin, stdout, stderr)
Call fork
in the child
close the parent end of the handles
close any other handles you have open
set up stdin, stdout, stderr to be the appropriate child side of the pipe
exec your desired command
If that fails, die.
in the parent
close the child side of the handles
Read and write to the pipes as appropriate
When done, call waitpid() (or similar) to clean up the child process.
Beware of blocking and buffering. You don't want your parent process to block on a write while the child is blocked on a read; make sure you use non-blocking I/O or threads to deal with those issues.
If you are have implemented a C program and you want to execute a script, you want to use a fork(). Unless you are willing to consider embedding the script interpreter in your program, you have to use fork() (system() uses fork() internally).
If you are running out of resources, most likely, you are not reaping your children. Until the parent process get the exit code, the OS needs keeps the child around as a 'zombie' process. You need to issue a wait() call to get the OS to free up the final resources associated with the child.

The exec family

I have a project the requires the use of the exec family. My project consist of making an interactive shell. The shell will implement a few basic commands like cd, ls, echo, etc. I have been researching the use of exec, but have not found a useful site. Any suggested links would help.
int ret;
ret = execl ("/bin/ls", "ls", "-1", (char *)0);
How would i get the output of this operation to show on the screen?
doing
int fd = 1;
dup(fd);
close(fd);
gets the output to the screen.
The code you wrote works for me in a simple test program that does nothing else. Remember, when you call execl, the process retains all of the old file handles. So whatever stdout was when you call execl, it will be the same when the new binary is loaded. If you just want the output to go to the terminal, just make sure stdout goes to the terminal.
If you want to do I/O with another program, popen is good for this (as mgb mentioned). It will fork a new process, set up plumbing for you, call some variant of exec, and return a file handle you can use for communication.

Resources