popen vs system function in C - c

Is The benefit of using popen is only to read the ouput produced by a command or there are some more benefits or advantages of popen over system.
Consider two programs below:
Program 1:
#include <stdio.h>
int main()
{
FILE *more,*who;
if (!(more = popen("more", "w")))
{
printf("Command `more` not found!");
return -1;
}
if (!(who = popen("who", "r")))
{
printf("Command `who` not found!");
return -1;
}
while (!feof(who))
{
char buffer[100];
if (fgets(buffer, 100, who) != NULL)
{
fputs(buffer, more);
}
}
fclose(more);
fclose(who);
return 0;
}
Program 2:
#include <unistd.h>
int main()
{
system("who|more");
return 0;
}
Why should i use Program1 if i can do the same thing in one line as done in Program2.

The two programs you have provided as examples are not equivalent. popen gives you a pair of file handles you can use to read and write input and output to and from stdin/stdout of the running process in an interactive manner. The system call is merely executing that and redirecting stdin of the current process to that of the invoked child process, and stdout of that process to stdout of the current (host) process.
It depends what you are trying to achieve, in general. If your goal is simply to run a command, system works fine. If you're interested in reading its output in a programmatic manner and processing it (and possibly generating more input), then popen is going to work better.

Related

No output in the parent process without fflush(stdout)

I'm trying to understand what is behind this behaviour in my parent process.
Basically, I create a child process and connect its stdout to my pipe. The parent process continuously reads from the pipe and does some stuff.
I noticed that when inserting the while loop in the parent the stdout seems to be lost, nothing appears on the terminal etc I thought that the output of stdout would somehow go to the pipe (maybe an issue with dup2) but that doesn't seem to be the issue. If I don't continuously fflush(stdout) in the parent process, whatever I'm trying to get to the terminal just won't show. Without a while loop in the parent it works fine, but I'm really not sure why it's happening or if the rest of my implementation is problematic somehow.
Nothing past the read system call seems to be going to the stdout in the parent process. Assuming the output of inotifywait in the pipe is small enough ( 30 > bytes ), what exactly is wrong with this program?
What I expect to happen is the stdout of inotifywait to go to the pipe, then for the parent to read the message, run strtok and print the file name (which only appears in stdout when I fflush)
Running the program with inotify installed and creating any file in the current directory of the program should be enough. Removing the while loop does print the created file's name (as expected).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
int main(void) {
char b[100];
int pipefd;
if (mkfifo("fifo", 0666) == -1) {
if (errno != EEXIST) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if ((pipefd = open("fifo", O_RDWR)) < 0) {
perror("open pipe");
exit(EXIT_FAILURE);
}
if (pid == 0) {
dup2(pipefd, 1);
const char* dir = ".";
const char* args[] = {"inotifywait", dir, "-m", "-e",
"create", "-e", "moved_to", NULL};
execvp("inotifywait", (char**)args);
perror("inotifywait");
} else {
while (1) {
fflush(stdout); // the output only appears in stdout with this here
if (read(pipefd, b, 30) < 0) {
perror("problem # read");
exit(1);
}
char filename[30];
printf("anything");
sscanf(b, "./ CREATE %s", filename);
printf("%s", filename);
}
}
}
The streams used by the C standard library are designed in such a way that they are normally buffered (except for the standard error stream stderr).
The standard output stream is normally line buffered, unless the output device is not an interactive device, in which case it is normally fully buffered. Therefore, in your case, it is probably line buffered.
This means that the buffer will only be flushed
when it is full,
when an \n character is encountered,
when the stream is closed (e.g. during normal program termination),
when reading input from an unbuffered or line-buffered stream (in certain situations), or
when you explicitly call fflush.
This explains why you are not seeing the output, because none of the above are happening in your infinite loop (when you don't call fflush). Although you are reading input, you are not doing this from a C standard library FILE * stream. Instead, you are bypassing the C runtime library (e.g. glibc) by using the read system call directly (i.e. you are using a file descriptor instead of a stream).
The simplest solution to your problem would probably be to replace the line
printf("%s", filename);
with:
printf("%s\n", filename);
If stdout is line-buffered (which should be the case if it is connected to a terminal), then the input should automatically be flushed after every line and an explicit call to fflush should no longer be necessary.

Call a C Program within Another and Get the Child's Output in macOS

I have two C programs and am trying to call some child program "child.c" inside of some parent program "parent.c", and capturing the output to stdout from child.c. How would I go about doing this?
I am using macOS.
Here's an example of what parent.c and child.c might look like
parent.c
while (1)
{
// call the child program
// capture the output from the child
if (child_output == some_condition)
{
break;
}
}
child.c
printf("Hello world!")
Thanks for the help.
Simply use popen() and create a stream object of type FILE * which you can use with fread()/fgets() to get the output from the child program. Reading the manual page should be enough to get you started.
But here is an example
#include <stdio.h>
int
main(void)
{
FILE *pipe;
char line[256];
pipe = popen("ls", "r");
if (pipe != NULL) {
while (fgets(line, sizeof line, pipe) != NULL) {
fprintf(stdout, "%s", line);
}
pclose(pipe);
}
return 0;
}
Also, read the manual to get an idea of how this actually works.

Output of the Linux System Call in C program

I want to manipulate the output of the ls -al command in my C program.
Is there a way to do it ?
int main()
{
int return_value;
return_value = system("ls -al ");
return return_value;
}
You need a pipe to the process.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char output[1024];
fp = popen("/bin/ls -al", "r");
if (fp == NULL)
exit(1);
while (fgets(output, 1023, fp) != NULL)
printf("%s", output);
pclose(fp);
return 0;
}
You could use popen(3) on /bin/ls as answered by Baris Demiray.
But in your particular case (getting the files in a directory), you don't really need to start some external process running ls, you could simply use opendir(3) & readdir(3) to read the directory entries, and use stat(2) on each of them (you'll build the path using snprintf(3)). See also glob(7), nftw(3) and read Advanced Linux Programming
Notice that the system(3) is a very poorly named standard C library function. It is not a direct system call (they are listed in syscalls(2)...), but uses fork(2), execve(2), waitpid(2), etc...

Interacting with a Shell using C

I have a binary called TEST which spawns a bash shell, I was hoping to write a C program that runs TEST and then passes commands to the bash shell that it spawns - I have tried the following - can someone indicate if this is possible. I can run the file using, but don't know how to then pass commands to shell it spawns:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
system("/var/testfolder/TEST"); #run the test file
return 0;
}
The UNIX styled popen() function is what you want to use.
See the man page for specifics.
It runs your command in a subprocess and gives you a pipe to interact with it. It returns a FILE handle like fopen() does, but you close it with pclose() rather than fclose(). Otherwise you can interact with the pipe the same way as for a file stream. Very easy to use and useful.
Here's a link to a use case example
Also check out this example illustrating a way to do what you are trying to do:
#include <stdio.h>
int main(void) {
FILE *in;
extern FILE *popen();
char buf[512];
if (!(in = popen("ls -sail", "r")))
exit(1);
while (fgets(buf, sizeof(buf), in) != NULL)
printf("%s", buf);
pclose(in);
}

Pass a string as standard input to an executable

I have count = read(pipe, buffer, buffsize); and am trying to run what is received (buffer) through another executable to have a differing process done on it.
printf("%s", buffer); prints it out correctly, but running it through execl("/path", "/path", buffer, NULL); or a number of other ways I've tried doesn't seem to run the executable. path is a compiled executable.
The executable does run properly if I use execv("./path", STDIN_FILENO);, but that isn't being taken from the pipe. path is expecting a the string as standard input.
The situation of the program is that I'm typing in input on one program using a while loop and read(), using a pipe to send that text to the program that is running execl (nothing else needs to be done in this program), that is then trying to call the executable with the string as an stdin. Only the intended input is coming in through the pipe, in chunks when the user presses enter.
An example of a string coming through the pipe is this is an example. The executable needs to have this inputted as standard input.
How can I get the string to be used as standard input for /path executable correctly?
It sounds like popen() is what you're looking for, to open a pipe to your desired executable so you can either pass stuff to its standard input, or read stuff from its standard output.
For instance:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE * p = popen("/bin/cat", "w");
if ( !p ) {
perror("error opening pipe");
return EXIT_FAILURE;
}
fputs("Echo me via /bin/cat\n", p);
if ( pclose(p) == -1 ) {
perror("error closing pipe");
return EXIT_FAILURE;
}
return 0;
}
which outputs:
paul#thoth:~/src/sandbox$ ./testpopen
Echo me via /bin/cat
paul#thoth:~/src/sandbox$

Resources