Related
I am trying to share a file between parent and child processes. Parent sends the file via a pipe and child write that lines into shared memory so that parent can read and print out the file via shared memory. However, I am getting segmentation fault: 11. Besides, I did something similar like the code below, but that time I could not get the correct content and even I was getting different results at each call.
I am not sure about increasing the pointer part. But, it is better to have an extra eye on the code.
Edit: I corrected char* to char[] and segmentation fault is now gone. However, I get different results at each run, some extra characters are seen in output.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#define SHM_NAME_1 "Child 1"
int main(){
pid_t pid;
FILE *file;
char *infile = "in.txt";
pid = fork();
if(pid < 0){
fprintf(stderr, "Fork failed\n");
return 1;
}
if(pid > 0){ // parent
file = fopen(infile, "r");
if(file == 0){
fprintf(stderr, "File failed\n");
return 1;
}
// close read end of pipe
mknod("FIFO", S_IFIFO | 0666, 0);
int fd = open("FIFO", O_WRONLY);
char str[300];
while(fgets(str, sizeof(str), file) > 0)
{
// write all lines of file
write(fd, str, strlen(str));
}
// close file and pipe
close(fd);
fclose(file);
// wait for child to write to shared memory
wait(NULL);
// open shared segment
int shm_first = shm_open(SHM_NAME_1, O_RDONLY, 0666);
if (shm_first == -1) {
fprintf(stderr, "Failed: Shared Memory 1");
exit(-1);
}
// create memory pointer
void *ptr = mmap(0,4096, PROT_READ, MAP_SHARED, shm_first, 0);
if (ptr == MAP_FAILED) {
printf("Map failed 1\n");
return -1;
}
// print out result and unlibk shared segment
fprintf(stdout, "Normal input: \n%s\n", ptr);
shm_unlink(SHM_NAME_1);
} else { // child
// create the shared segment for the first time
int shm_child_1 = shm_open(SHM_NAME_1, O_CREAT | O_RDWR, 0666);
// configure the size of the shared memory segment
ftruncate(shm_child_1,4096);
// map the pointer to the segment
void *ptr_child_1 = mmap(0,4096, PROT_READ | PROT_WRITE, MAP_SHARED, shm_child_1, 0);
if (ptr_child_1 == MAP_FAILED)
{
printf("Map failed in first child\n");
return -1;
}
mknod("FIFO", S_IFIFO | 0666, 0);
int fd = open("FIFO", O_RDONLY);
int num;
char s[300];
while((num = read(fd, s, sizeof(s)))> 0)
{
sprintf(ptr_child_1, "%s", s);
ptr_child_1 += num;
}
close(fd);
exit(0);
}
return 0;
}
One quick observation.
In the following code, you have a char pointer that is not initialized to point to anything. Which causes fgets to copy what it reads from file to some arbitrary place in memory.
char *str;
while(fgets(str, 100, file) > 0)
Now that the buffer issues are resolved, there is also an issue with the braces in the following expression
while((num = read(fd, s, sizeof(s)) > 0))
num is going to be 1 or 0 rather than the number of bytes read or 0 for eof. This should be
while((num = read(fd, s, sizeof(s))) > 0)
Once you have the number of bytes read, you need to zero terminate the buffer. Because you are using sprintf which expects the argument for %s to be a zero terminated string.
while((num = read(fd, s, sizeof(s)))> 0)
{
s[num] = '\0'; // Terminate the string to the number of bytes read
sprintf(ptr_child_1, "%s", s);
ptr_child_1 += num;
}
So I am working on a simple program in C but have been stuck on the copying portion. The program takes two filenames on the command line as arguments and copies the first to the second by using system calls. If the second file exists it asks the user if they want to overwrite, if not it creates it. However, my program when the user choices overwrite goes on infinitely.
Here is my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd1, fd2;
char buffer[1024];
long int n;
char c;
int num;
if (argc != 3) {
printf("%d\n",argc);
printf("Error, you need to give 2 arguments. Such that [File to copy] [File to create].\n");
exit(1);
}
if (access(argv[1], F_OK) < 0) {
printf("File %s either does not exist or cannot be accessed.\n", argv[1]);
exit(1);
} else {
printf("file %s exists\n", argv[1]);
}
if (access(argv[2], F_OK) < 0) {
printf("File %s does not exist, but one will be created.\n", argv[1]);
fd2=open(argv[2],O_CREAT|O_WRONLY|O_TRUNC, 0700);
} else {
printf("file %s exists\n", argv[2]);
printf("Would you like to overwrite %s? (Type 1 for yes or 0 for no)\n", argv[2]);
scanf("%d%c", &num, &c); // use c to capture \n
if (num == 1) {
fd2=open(argv[2],O_CREAT|O_WRONLY|O_TRUNC, 0700);
} else {
if (num == 0) {
printf("Ok, the file will not be copied and the program will now exit.\n");
exit(1);
} else {
printf("I do not recognize this response, program will now be terminated.\n");
}
}
}
printf("step\n");
while ((n1 = read(fd1, buffer, 1024)) > 0) {
printf("step\n");
if(write(fd2, buffer, n1) != n1){
printf("step\n");
perror("Error writing file.");
printf("step\n");
exit(3);
}
printf("stepss\n");
}
close(fd1);
close(fd2);
}
The printf("step") is for debugging, but it only prints one. Meaning the program freezes up by the while loop. I can use stat(), open(), read(), write(), close(), and access(). Any ideas on what is wrong or how it can be done better would be appreciated!
Any ideas on what is wrong
Your fd1 never been assigned, so read(fd1, ...) returns an error.
Check return value of read and printf("%m\n") will print the details.
$ ./a.out a b
file a exists
file b exists
Would you like to overwrite b? (Type 1 for yes or 0 for no)
1
step
Bad file descriptor
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 was trying to run two programs.
Case 1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int n;
int k = 10;
int ret_val = 0;
ret_val = write (0, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write");
exit (EXIT_FAILURE);
}
scanf ("%d", &n);
printf ("Integer read is %d \n", n);
return 0;
}
Then I tried the next one.
Case 2
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int n;
int k = 10;
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
int stdin_fd;
/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf ("Failed to dup");
exit (EXIT_FAILURE);
}
/* write to stdin_buffer (content will be taken from file_in.txt) */
ret_val = write (stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}
scanf ("%d", &n);
printf ("Integer read is %d \n", n);
close(source_fd);
return 0;
}
Now in the first case, I was not able to write to stdin. In the second case, I was able to take the input from a file, "file_in.txt", and send the content to the standard input buffer.
I couldn't get a good explanation for why my first case didn't work out. Can someone explain?
stdin should be like any other file right? If it is write protected, fine. But then when I redirected the input (in the second case), there was no "permission denied" problem. This code seems to be non-portable. Is there a portable and safe way to redirect stdin from a file?
After going through the comments, I have come up with a better working code. I would like some feedback on this code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
int main()
{
int n;
char buffer[LEN];
memset (buffer, '\0', LEN);
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
{
perror ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
/* Temp stdin_buffer */
int temp_fd = open ("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
{
perror ("Failed to open temp stdin");
exit (EXIT_FAILURE);
}
int stdin_fd;
/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (temp_fd);
if (-1 == stdin_fd)
{
perror ("Failed to dup");
exit (EXIT_FAILURE);
}
ret_val = read (source_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to read from source");
exit (EXIT_FAILURE);
}
else
{
printf ("%s read from Source file\n", buffer);
}
/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write (stdin_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}
ret_val = lseek (stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
{
perror ("Failed lseek");
exit (EXIT_FAILURE);
}
ret_val = scanf ("%d", &n);
if (-1 == ret_val)
{
perror ("Failed to read stdin_buffer");
exit (EXIT_FAILURE);
}
printf ("Integer read is %d \n", n);
close(source_fd);
return 0;
}
Before the updates
In the first program, 3 null bytes and a newline were (probably) written to the screen (not necessarily in that order); the program then tries to read from the keyboard (assuming that there's no I/O redirection on the command line). Writing to standard input does not load the input buffer. You very often can write to standard input (and read from standard output and standard error) because the classic technique opens a file descriptor with O_RDWR and then connects that to the standard I/O channels. However, there is no guarantee that you can do so. (The first program needs <unistd.h>, incidentally.)
The second program has so much undefined behaviour it is difficult to analyze. The open() call needs three arguments because it includes O_CREAT; the third argument is the mode for the file (e.g. 0644). You don't check that the open() succeeds. You don't check that the write succeeds; it won't, because the file descriptor is opened O_RDONLY (or, rather, the source_fd is opened O_RDONLY, and the dup() will copy that mode to file descriptor 0), which means the write() will fail. The input operation is not checked (you don't ensure that scanf() succeeds). (The second program doesn't really need <sys/types.h> or <sys/stat.h>.)
Basically, you don't know anything about what is going on because you've not checked any of the critical function calls.
After update 1
Note that error messages should be written to standard error and should be terminated with newlines.
I get the first program working as stated (Mac OS X 10.10 Yosemite, GCC 4.8.1), though it is hard to prove that the null bytes got written to standard input (but a newline was written there). I could then type 10 (or 20, or 100, or …) plus Return and that integer would then be printed.
The second program fails on the scanf() because the file pointer is at the end of the file when you try to read. You can see with this variant of your program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading\n");
exit (EXIT_FAILURE);
}
close(0);
int stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf("Failed to dup\n");
exit(EXIT_FAILURE);
}
int k = 10;
int ret_val = write(stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf("Failed to write to stdin_buffer\n");
exit(EXIT_FAILURE);
}
int rc;
int n;
if ((rc = scanf("%d", &n)) != 1)
printf("Failed to read from standard input: rc = %d\n", rc);
else
printf("Integer read is %d (0x%08x)\n", n, n);
close(source_fd);
return 0;
}
It produces:
Failed to read from standard input: rc = -1
If you rewind the file before reading, you will get 0 returned; the binary data written to the file is not a valid string representation of an integer.
After update 2
I've written a small function err_exit() because it allows the code to be smaller on the page. I've modified your code in a couple of places to report on the return value from a previous function. The absence of input is not an error. When you get 0 bytes read, that isn't an error; it is EOF. When there is data to be read but it isn't the text format for an integer value, no conversions take place, but that isn't an error.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int n = 99;
char buffer[LEN];
memset(buffer, '\0', LEN);
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin_buffer */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");
/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");
ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);
/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write(stdin_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to write to stdin_buffer");
ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");
ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin_buffer");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);
close(source_fd);
return 0;
}
Output:
(0 bytes) <<>> read from Source file
Integer read is 99 (ret_val = 0)
When scanf() fails to read a value, it (usually) doesn't write anything into the corresponding variable. That's why the 99 survives. If you want data that can be read by scanf() as an integer, you need:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int n = 99;
char buffer[LEN] = "";
int ret_val = 0;
/* Open file from which content shall be inserted to stdin */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");
/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");
ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);
/* write to stdin (content taken from file_in.txt) */
ret_val = write(stdin_fd, "10\n", sizeof("10\n")-1);
if (-1 == ret_val)
err_exit("Failed to write to stdin");
ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");
ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);
close(source_fd);
return 0;
}
Output:
(0 bytes) <<>> read from Source file
Integer read is 10 (ret_val = 1)
Let's see what your three programs are doing...
Program 1
The first program writes to the filedescriptor 0. By default, this is stdout, hence the macro STDOUT_FILENO has that value. Since stdout is a unidirectional stream that runs into the process, writing fails.
Program 2
In this program, you open a file, which probably gets FD (filedescriptor) 3 (after 0-2 for the standard streams). Then, you close stdout with FD 0. Then, you dup() FD 3 and since the first open spot is at index 0, that's the new FD to the file. Since scanf() just uses the (unchanged) macro STDIN_FILENO, it will pick up the content of that file there.
Program 3
In program 3, you do pretty much the same as in program 2, only that you open the file twice. The explanation of what is going on pretty much follows the two above.
Question now is what you mean when you say "a better working code". The point is that "better" is impossible to say unless you specify what you actually want. Guessing from your topic and your comment, you want to remote-control a second process, just like using the pipe symbol in bash.
In order to do that, you will have to resort to OS-specific means, just search for "input output redirection" and you should be able to find some info. Since you already mentioned execl(), portability beyond POSIX (e.g. to win32) seems not be an issue though, and for that you should find lots of example code out there, much better that what I can write up here.
In any case, it boils down to roughly these steps:
pipe() to create a pair of FDs
fork() to create a new process
child: dup2() to reassign the input FD from pipe() to stdin
child: execl() the new process
parent: write to output FD to generate input for the child process
In addition, you should close unused streams or configure them with fcntl(FD_CLOEXEC) to have them closed automatically for you. The important point is that the two FDs remain connected to the same (!) channel, although you have both end in both processes. Closing one end in each leaves a unidirectional channel between the two processes.
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.