I have to capture the stdout in a program and write that into a file...so I created a pipe. In the parent process, I captured the stdout in the pipe using dup() and I need to get this into a file...so I did a dup() in the child to get the captured file descriptor into the stdin. Now, how do I write this stdin into a file using fwrite()?
Isn't that doing things the hard way? All you need to do in the parent is use freopen() to connect stdout to the file of your choosing.
FILE *fp = freopen("/tmp/mylogfile", "w", stdout);
if (fp == 0)
error("...something went wrong opening the log file...\n");
The direct answer to your question is:
char buffer[32768];
ssize_t nbytes;
FILE *fp = fopen("/tmp/mylogfile", "w");
if (fp == 0)
error("....something went wrong opening my log file...\n");
while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), stdin)) > 0)
if (fwrite(buffer, sizeof(char), nbytes, fp) != nbytes)
error("...something went wrong writing to standard output...\n");
However, this is hardly necessary. You can improve the error handling in all sorts of ways; I'm simply assuming that 'error()' reports a message and does not return.
The easiest way is just to open the file and provide that as the child's stdout:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
switch (pid) {
case -1:
perror("fork");
return 1;
case 0:;
int new_out = open("output.txt", O_WRONLY | O_CREAT, 0666);
if (new_out == -1) {
perror("open");
return 1;
}
if (dup2(new_out, 1) == -1) {
perror("dup2");
return 1;
}
char* args[] = {"/bin/echo", "test output", 0};
execv(args[0], args);
perror("exec");
return 1;
default:;
int s;
if (waitpid(pid, &s, 0) == -1) {
perror("waitpid");
return 1;
}
if (WIFEXITED(s)) {
return WEXITSTATUS(s);
}
return 1;
}
}
You should capture into a byte or char buffer and the send that ot the fwrite.
When I say a buffer I mean an array or dynamically allocated block of bytes/chars.
Related
I need to create a program that has a child process and a parent process. The child process has to convert lines sent by the parent proccess in to upper case, and the parent proccess has to send lines to the child proccess to convert, and show this converted lines by stdin. I already have this, but at the time when i execute on my terminal, the upper case lines doesn't show.
Any sugestion
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
int main(void) {
int p1[2];
int p2[2];
pid_t pid;
char buffer[1024];
FILE *fp1;
FILE *fp2;
pipe(p1);
pipe(p2);
pid = fork();
if (pid < 0) {
fprintf(stderr, "Error fork\n");
exit(1);
}
if (pid == 0) {
// Close pipes entrances that aren't used
close(p1[1]);
close(p2[0]);
// Open file descriptors in used entrances
fp1 = fdopen(p1[0], "r");
fp2 = fdopen(p2[1], "w");
// Read from the corresponding file descriptor (pipe extreme)
while (fgets(buffer, 1024, fp1) != NULL) {
for (int i = 0; i < strlen(buffer); i++) {
buffer[i] = toupper(buffer[i]);
}
fputs(buffer, fp2);
}
// Once finished, close the reaming pipes entrances
fclose(fp1);
fclose(fp2);
exit(1);
}
// Close unused pipes entrances
close(p1[0]);
close(p2[1]);
// Open dile descriptors
fp1 = fdopen(p1[1], "w");
fp2 = fdopen(p2[0], "r");
while (fgets(buffer, 1024, stdin) != NULL) {
fputs(buffer, fp1); // Send buffer to write line pipe
fgets(buffer, 1024, fp2); // Get buffer readed from read line pipe
printf("%s", buffer); // Print in stdout the buffer
}
// Once finished, close the reaming pipes entrances
fclose(fp1);
fclose(fp2);
// Wait fork
wait(NULL);
return 0;
}
When using a FILE * stream and the C library stream API, it's important to keep in mind that I/O operations can be "buffered". In most cases, by default, when performing writes via fputs(...) the bytes will not actually be sent to the underlying file object (a pipe end in this case) until the buffer is flushed. In your code above, you could add calls to fflush(fpN) (where N matches the numbers in your code) after both calls to fputs(...). This should help fix your problem.
Note that alternatively there are ways of manually changing the buffering mode of a given file stream. This information can be found in man setbuf.
I want a program which reads a file in the child process and send the string/content to the parent process using simple pipe.
Here is what I have tried, I have read a file and tried to write into the pipe line by line in the while loop. But It doesn't seem to work that way. Your help will be highly appreciated.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
FILE * fptr;
fptr = fopen(argv[1],"r");
char str1;
int pipefds[2];
char buffer[5];
if(pipe(pipefds) == -1)
{
perror("PIPE ERROR");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if(pid == 0)
{
char singleLine[150];
close(pipefds[0]);
while(!feof(fptr))
{
fgets(singleLine, 150, fptr);
//puts(singleLine);
write(pipefds[1], singleLine, sizeof(singleLine));
}
fclose(fptr);
//Close read file descriptor
exit(EXIT_SUCCESS);
}
else if(pid > 0)
{
//wait(NULL); //Wait for child process to finish
close(pipefds[1]); //Close write file descriptor
read(pipefds[0], buffer, 100); //Read pin from pipe
close(pipefds[0]); //Close read file descriptor
printf("%s",buffer);
}
return 0;
}
Check for Program Argument
To ensure that the filename parameter is provided, you could check as follows:
if (argc < 2) {
fprintf(stderr, "usage: progname <filename>");
exit(EXIT_FAILURE);
}
Opening the File
Presumably it makes sense to check whether fopen is successful or not.
You could do a check e.g. like this:
if ((fptr = fopen(argv[1], "r")) == NULL) {
perror("error opening file");
exit(EXIT_FAILURE);
}
Also like mentioned by #pat in the comments this code can be moved to the child.
Reading the file
Reading the file could be done like this:
while (fgets(singleLine, sizeof(singleLine), fptr)) {
write(pipefds[1], singleLine, strlen(singleLine));
}
Like mentioned by Jonathan Leffler in the comments one should not check feof upfront. Also important to note that you don't want to write sizeof(singleLine) bytes, because lines can have variable size and be shorter then the buffer size, so better to use strlen(singleLine) here.
Reading the Data
Reading the data must happen in a loop - as long as data is available. The call of read returns the number of bytes read.
ssize_t n;
while ((n = read(pipefds[0], buffer, sizeof(buffer) - 1)) > 0) {
buffer[n] = '\0';
printf("%s", buffer);
}
To make sure that you don't save data beyond the end of the buffer, you can use sizeof(buffer) - 1 as the third argument to the read call.
Programm
So your program, slightly modified regarding the above points, could look like this:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
int pipefds[2];
char buffer[100];
if (argc < 2) {
fprintf(stderr, "usage: progname <filename>");
exit(EXIT_FAILURE);
}
if (pipe(pipefds) == -1) {
perror("PIPE ERROR");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == 0) {
FILE *fptr;
if ((fptr = fopen(argv[1], "r")) == NULL) {
perror("error opening file");
exit(EXIT_FAILURE);
}
char singleLine[150];
close(pipefds[0]);
while (fgets(singleLine, sizeof(singleLine), fptr)) {
write(pipefds[1], singleLine, strlen(singleLine));
}
int error = ferror(fptr);
fclose(fptr);
if (error) {
perror("error reading file");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} else if (pid > 0) {
close(pipefds[1]);
ssize_t n;
while ((n = read(pipefds[0], buffer, sizeof(buffer) - 1)) > 0) {
buffer[n] = '\0';
printf("%s", buffer);
}
close(pipefds[0]);
}
return 0;
}
I am trying to finish a program that can fork function a child process, and parent can get the input file (under same directory) , reverse the content of this file, then using pipe function pass to the child process. Child will read the message from pipe and generate an output file. I have finished fork, create pipe and reverse function. However I got stucked on write it to the pipe. I know there must some type confusion when i try to pass the parameter into the write function, Any hits would be appreciated.
Here is the Code I have so far:
#include <stdio.h>
#include <stdlib.h> //exit
#include <string.h>
#include <sys/types.h> //pid_t
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[]){
long loc;
FILE *in, *out;
char ch;
if (argc != 3)
{
printf("Usage %s message\n", argv[0]);
exit(EXIT_FAILURE);
}
int pipefd[2];
int pipe_return = pipe(pipefd);
if((in = fopen(argv[1], "rb")) == NULL) {
printf("Cannot open input file.\n");
exit(1);
}
if((out = fopen(argv[2], "wb"))==NULL) {
printf("Cannot open output file.\n");
exit(1);
}
if(pipe_return == -1)
{
printf("Unable to create pipe\n");
exit(EXIT_FAILURE);
}
pid_t return_from_fork = fork();
if (return_from_fork == -1)
{
printf("Unable to fork\n");
exit(EXIT_FAILURE);
}
else if (return_from_fork == 0) //this is a child
{
char msg;
close(pipefd[WRITE_END]);
int read_return = read(pipefd[READ_END], &msg, 1);
printf("read return:%d\n", read_return);
while(read_return > 0){
fputc(ch, out);
printf("%c",msg);
read_return = read(pipefd[READ_END], &msg, 1);
}
printf("child ends\n");
close(pipefd[READ_END]);
exit(EXIT_SUCCESS);
}
else if (return_from_fork > 0)
{
close(pipefd[READ_END]);
printf("this is parent\n");
fseek(in, 0L, SEEK_END);
loc = ftell(in);
while(loc >= 0L){
fseek(in, loc, SEEK_SET);
ch = fgetc(in);
printf("%c",ch);
int write_r = write(pipefd[WRITE_END], ch, 1);//here is the problem the printf() return -1
printf("%d",write_r);
loc--;
}
printf("\n");
close(pipefd[WRITE_END]);
wait(NULL);
printf("file successful generated.\n");
fcloseall();
exit(EXIT_SUCCESS);
}
}
And Here is the compile result:
zzz#ubuntu:~/Desktop/test$ gcc filereversecopy.c -o run
zzz#ubuntu:~/Desktop/test$ ./run src.txt out.txt
this is parent
�-1
-1e-1c-1n-1e-1t-1n-1e-1s-1 -1a-1 -1s-1i-1 -1s-1i-1h-1T-1
read return:0
child ends
file successful generated.
zzz#ubuntu:~/Desktop/test$
On the line you say is problem you are passing ch to write, and ch is type char. I'm sure you mean &ch instead. I bet if you change that write will return 1 instead of -1.
Also, you seek to the end to start reading, but when you seek to the end you are pointing at EOF. You need to start reading at the position before EOF. So after "fseek(in, 0L, SEEK_END); loc = ftell(in);" adding "loc--; fseek(in, loc, SEEK_SET);" makes it work.
I am writing a simple code to implement the indirect input function for a unix/linux shell.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
extern void error(char* message);
void
cisshRedirectedInput(char* command[], char* inputFile)
{
//Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
//For the child process
if ((pid=fork())==0)
{
//Try to input files, failing on an error
fd=open(inputFile,O_RDONLY);//To read input file
if(fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
//use dup() to copy file
close(1);
if(dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
//Close file and exec()
close(fd);
execvp(command[0], command);
//If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if(wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if(status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
However, after compilation (no error reported), but when I try (fileList created already)
sort -r <fileList
The shell just stuck there without giving me answer, what is the problem please?
The standard input file descriptor is 0 (or STDIN_FILENO), not 1 (or STDOUT_FILENO).
Either use:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
close(0);
if (dup(fd) < 0) …
close(fd);
Or:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
if (dup2(fd, 0) < 0) …
close(fd);
It is good that your code does the close(fd) after duplicating to a standard I/O descriptor — that is almost always correct. It's also good that you are checking that the key system calls succeed. (There isn't much you can do if close() fails.)
This simple modification of your code (key change: use close(0); instead of close(1);) works for me. Did you null terminate your argument list?
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static inline void error(char *message)
{
fprintf(stderr, "%s\n", message);
}
void
cisshRedirectedInput(char *command[], char *inputFile);
void
cisshRedirectedInput(char *command[], char *inputFile)
{
// Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
// For the child process
if ((pid = fork()) == 0)
{
// Try to input files, failing on an error
fd = open(inputFile, O_RDONLY); // To read input file
if (fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
// use dup() to copy file
close(0);
if (dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
// Close file and exec()
close(fd);
execvp(command[0], command);
// If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if (wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if (status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
int main(void)
{
char *args[] = { "sort", "-r", 0 };
cisshRedirectedInput(args, "fileList");
return 0;
}
Input file:
bash-assoc-arrays.sh
cissh.c
fileList
kwargs.py
makefile
posixver.h
rangeinc.c
select.c
spc.py
testcsv.py
uncrustify.bug
yield.py
Output:
yield.py
uncrustify.bug
testcsv.py
spc.py
select.c
rangeinc.c
posixver.h
makefile
kwargs.py
fileList
cissh.c
bash-assoc-arrays.sh
We are trying to communicate between two processes via named pipes that are redirected to stdin and stdout in the child process. The parent process opens the named pipe and calls fdopen on them. The problem we see is that fwrite on this works, however even reading one byte from redirected stdout pipe blocks eternally.
The code works when using the file descriptors instead of FILE *. What is wrong? Code is a little long, sorry.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int create_fifo()
{
int file1;
unlink("fifo-0.0");
file1 = mkfifo("fifo-0.0",0666);
if(file1<0) {
printf("Unable to create a fifo");
return 0;
}
unlink("fifo-0.1");
file1 = mkfifo("fifo-0.1",0666);
if(file1<0) {
printf("Unable to create a fifo");
return 0;
}
return 1;
}
int main()
{
int fd, fd0, fd1;
pid_t pid;
char read_buf_p[50],write_buf_p[50];
char * args[3];
FILE *fd_stdin_p, *fd_stdout_p;
create_fifo();
args[0] = "/bin/cat";
args[1] = "-n";
args[2] = NULL;
pid = fork();
if(pid == 0)
{
fd = open("fifo-0.0", O_RDONLY);
dup2(fd, 0);
close(fd);
fd = open("fifo-0.1", O_WRONLY);
dup2(fd, 1);
close(fd);
execv(args[0],args);
}
else
{
fd0 = open("fifo-0.0", O_WRONLY);
fd_stdin_p = fdopen(fd0,"w");
fd1 = open("fifo-0.1", O_RDONLY);
fd_stdout_p = fdopen(fd1,"r");
int sz = fwrite("Hello World", sizeof(char), strlen("Hello World"), fd_stdin_p);
fread(read_buf_p,1,1,fd_stdout_p);
printf("%s\n", read_buf_p);
}
return 0;
}
I think its because of buffering of Standard output.
Just use '\n' at the end of fflush()
This is the result of FILE-based I/O being "line-buffered", by default. This means that it reads and writes whole lines, and since your data isn't a whole line, it's stuck in the buffer and not passed through to the underlying file descriptor.
Try adding \n at the end of the output data, or call fflush().