Pass a string as standard input to an executable - c

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$

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.

what will happen calling printf after close stdout?

I tried below code, and screen showed nothing.
close(STDOUT_FILENO);
printf("Child output something\n");
is it just can not find the stdout,then abort the data?
I want to find wether printf write some data, since I can not print the return value so I output it to some file.
close(STDOUT_FILENO);
int res = printf("output something\n");
open("./log.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d", res); // return 17
So printf work, but I don't know where it write to.
The reason you're seeing this result has to do with buffering. In general, a file which is attached to a terminal is line buffered and all other files are block buffered. stderr is unbuffered.
When you close stdout, it's no longer attached to a terminal, so it's block buffered, not line buffered. You've attempted to write fewer bytes than the buffer size (which is usually some multiple of 512), so printf happily copied it to the buffer and did nothing else. If you wrote a suitable amount of data using printf, you'd find that it did indeed fail at that point.
You can verify a similar behavior by calling fflush(stdout):
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
close(STDOUT_FILENO);
int res = printf("output something\n");
fprintf(stderr, "%d\n", res);
res = fflush(stdout);
fprintf(stderr, "%d %s\n", res, strerror(errno));
}
The last line will output -1 Bad file descriptor, which shows that the attempt to write out to stdout failed with EBADF, as expected. If you need to verify that data has been written, you must call fflush or fsync as appropriate.
Note that in general, you don't want to close any of the three default file descriptors, because any time you open a new file descriptor, it will use the lowest unused number and take the place of one of the standard streams. If a separate part of your program attempts to write to one of those streams without checking, it can write into an unexpected file, corrupting it. The safe thing to do is redirect those streams to /dev/null instead.
Your open call for log.output does exactly the thing I just mentioned in that it opens file descriptor 1 (stdout) again.

Not able to read input from stdin/STDIN_FILENO in C?

I have this command line argument -
cat file_name | ./a.out
The problem is not reading from the cat command inside the C program as we can do that with read(), fgets(), fgetc() but the actual problem I am facing is after reading the data from cat I am not able to take input from user using fgets.
Here is my sample code
while(fgets(buffer, BUFSIZ, stdin ) != NULL )
puts( buffer ); // Here I have tried strtok( buffer, "\n" ) too.
memset( buffer, 0, BUFSIZ );`
The problem is after this line, it is not asking for the input like the below is not working-
puts("Name: ");
fgets( buffer, BUFSIZ, stdin );
Help me with what's wrong happening here?
When you do cat file_name | ./a.out the standard input of your program is tied to a pipe linking it to the output of cat. Your program will never get to see the user input - the very stream from where it would arrive has been replaced by the aforementioned pipe.
Mind you, I suspect that with some horrible POSIX-specific trickery you may be able to reopen it going straight for the tty device, but it's just bad design. If you need to both read from a file and accept interactive user input just accept the file as a command line argument and use stdin to interact with the user.
Edit
This is an example of the Unix-specific kludges that one can attempt, assuming that the process still has a controlling terminal. After reading all the original stdin, I'm opening /dev/tty (which is the controlling terminal of the process) and re-linking stdin to it.
Disclaimer: this is for entertainment purposes only, don't do this for real.
#include <stdio.h>
#include <stdlib.h>
void die(const char *msg) {
fprintf(stderr, "%s\n", msg);
fputs(msg, stderr);
exit(1);
}
int main() {
/* Read all of stdin and count the bytes read (just to do something with it) */
int ch;
unsigned long count = 0;
while((ch = getchar())!=EOF) {
count++;
}
printf("Read %lu bytes from stdin\n", count);
/* Open the controlling terminal and re-link it to the relevant C library FILE *
* Notice that the UNIX fd for stdin is still the old one (it's
* surprisingly complex to "reset" stdio stdin to a new UNIX fd) */
if(freopen("/dev/tty", "r", stdin) == NULL) {
die("Failed freopen");
}
/* Do something with this newly gained console */
puts("How old are you?");
fflush(stdout);
int age = -1;
if(scanf("%d", &age)!=1) {
die("Bad input");
}
printf("You are %d years old\n", age);
return 0;
}
(previously I had a solution that checked if stderr or stdout were still consoles, which was even more of a kludge; thanks #rici for reminding me of the fact that POSIX has the concept of "controlling terminal", which is accessible through /dev/tty)
If you need to use stdin for user interaction, then you need to use a different file descriptor for reading the input stream.
You could use a specific pre-opened file descriptor and document that (e.g. "the input stream should be connected to fd 3"), but the usual approach is to accept a file name as a command-line argument. You can then provide a named pipe as the argument; shells such as Bash provide process substitution to make that easy:
./a.out <(cat file_name)
When that is run interactively like that, stdin is still connected to the terminal, and can be used at the same time as the stream from the connected command.
(Obviously, if the command actually is cat with a single argument, then you could just provide the filename itself as the argument, but I'm assuming that's a placeholder for a more involved pipeline).

How can i save all output to the screen while still displaying it?

I am currently writing a custom shell for a CS course.
It is a basic shell that reads in user input, and checks for shell functions, and then forks itself and passes the commands that were parsed to execvp().
I need to save the output from the screen into a text file while the shell is currently being run. I looked into freopen but that cut off all printing to the screen.
Can anyone give me a hint in which direction to look to perform this functionality?
You need something which will read from standard input and write same thing into both standard output as well standard error, that way you can print one, while save other in file.
Here is a simple implementation:
#include <stdio.h>
#include <unistd.h>
#define BUF_SIZE 1000
int main ()
{
char buffer[BUF_SIZE];
ssize_t read_size;
while ((read_size = read (STDIN_FILENO, buffer, BUF_SIZE)) > 0)
{
if (write (STDOUT_FILENO, buffer, read_size) <= 0 ||
write (STDERR_FILENO, buffer, read_size) <= 0)
return 1;
}
return 0;
}
If program is called dupl then you can do something like this
ls | ./dupl 2> file
This will display and as well save standard output of ls into file

How to write redirected output to file inside loop in C?

I am writing a C program on unix which should redirect it's output to the file, and write to it some text every second in infinite loop:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
int outDes = open("./output.txt", O_APPEND | O_WRONLY);
dup2(outDes, 1);
while(1) {
printf("output text\n");
sleep(1);
}
}
But it writes nothing to the output file. I tried to change the 'while' loop for 'for' with 10 loops, and I found that it writes all 10 lines to the file at once after the series ends. It is not very good for me, while I need to have an infinite loop.
When I'm not redirecting output, it is all ok, and new line appears every second on terminal.
I also tried to put one
printf("text\n");
before redirecting output to the file. Then the program wrote the lines to the file in real time, which is good, but wrote there the first (non redirected) line too. I don't want this first line in my output file, I don't understand how it could be written into file when output was not redirected yet (maybe redirect remained there since last run?), and how it could cause that the lines are suddenly written in real time.
Can anyone explain me how does it work?
You are not checking the return value of open() and dup2(). If either open() or dup2() failed, it won't write anything in output.txt.
if (outDes < -1) {
perror("open");
return 1;
}
if (dup2(outDes, 1) == -1) {
perror("dup2");
return 1;
}
stdio streams are buffered, and the writes happen in memory before being done on the real file description.
Try adding a fflush(stdout) after printf().
You're running afoul of a poorly documented DWIMmy feature in many Unix C libraries. The first time you write to stdout or stderr, the library probes the underlying file descriptor (with isatty(3)). If it's a (pseudo-)terminal, the library puts the FILE in "line buffered" mode, meaning that it'll buffer input until a newline is written and then flush it all to the OS. But if the file descriptor is not a terminal, it puts the FILE in "fully buffered" mode, where it'll buffer something like BUFSIZ bytes of output before flushing them, and pays no attention to line breaks.
This is normally the behavior you want, but if you don't want it (as in this case), you can change it with setvbuf(3). This function (although not the behavior I described above) is ISO standard C. Here's how to use it in your case.
#include <stdio.h>
#include <unistd.h>
int
main(void)
{
if (freopen("output.txt", "a", stdout)) {
perror("freopen");
return 1;
}
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
for (;;) {
puts("output text");
sleep(1);
}
/* not reached */
}

Resources