Writing struct to child through pipe failing - c

I'm attempting to fork a child and write a struct to it, but for some reason the write is failing. Here is what I've got so far:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
pid_t pid;
int pfd[2];
int rv;
if(pipe(pfd) == -1){
printf("Pipe failed\n");
}
struct t{
int count[26];
char array[4];
};
struct t st;
if((pid = fork()) < 0){
printf("Fork error\n");
}else if(pid == 0){
close(pfd[1]);
rv = read(pfd[0],st,sizeof(struct t));
printf("Read string: %d\n",rv);
}else{
int i = 0;
for(i = 0; i < 26; i++){
st.count[i] = i;
}
for(i = 0; i < 3; i++){
st.array[i] = 'A';
}
st.array[3] = '\0';
close(pfd[0]);
rv = write(pfd[1],st,sizeof(struct t));
printf("wrote: %d",rv);
close(pfd[1]);
}
}
This has been killing me all night. I have a suspicion that it has to do with me not fully understanding pointers to structs. I printed sizeof(struct t) which returned 108 bytes (as expected), and I thought st was a pointer to the beginning of the struct, meaning the write command would start there and write the first 108 bytes, but clearly that's not the case. What am I missing?

Try &st. st is the struct itself. & takes a pointer off of it.
You probably need to enable (or heed) the compiler warnings. Add -Wall or -Wall -Wextra to the compile command.
You need to include the header file for the read function.
#include <unistd.h>
Now you should get error messages.

Related

Pipe: Bad file descriptors

I know this kind of posts have been asked previously, but their level are clearly higher than mind, I still don't get it after reading their post, so I decide to post this question again from here.
I am learning multi-processes communication using pipe, I have confronted to this error called Bad file descriptors, I don't understand why I am having this error in my code.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define SIZE 50
struct record {
int freq;
char word[SIZE];
};
int main(){
int number_process = 3;
int pipes[number_process][2];
struct record r1;
r1.freq = 10;
strcpy(r1.word, "Cat");
struct record r2;
r2.freq = 20;
strcpy(r2.word, "Elephant");
struct record r3;
r3.freq = 30;
strcpy(r3.word, "Dragon");
struct record records_array[3] = {r1, r2, r3};
for (int i = 0; i < number_process; i++){
if (pipe(pipes[i]) == -1){
perror("pipe");
exit(1);
}
// Create children.
pid_t fork_result = fork();
if (fork_result == -1){
perror("Parent fork");
exit(1);
} else if (fork_result == 0){
if (close(pipes[i][0]) == -1){
perror("Child closes reading port");
exit(1);
}
// Later children is going to close all reading port from pipe that parent creates.
for (int child_no = 0; child_no < i; child_no++) {
if (close(pipes[child_no][0]) == -1) {
perror("close reading ends of previously forked children");
exit(1);
}
}
// Now, I am trying to write each strct record member from the above array into the pipe
// when I run the program, it won't allow me to do so because of bad file descriptor exception.
for (int j = 0; j < number_process; i++){
if (write(pipes[i][1], &(records_array[j]), sizeof(struct record)) == -1){
perror("write from child to pipe");
exit(1);
}
}
// Finishing writing, close the writing end in pipe.
if (close(pipes[i][1]) == -1){
perror("Child closes writing port");
exit(1);
}
// Terminate the process.
exit(0);
} else {
// Parent is closing all the writing ends in pipe.
if (close(pipes[i][1]) == -1){
perror("Parent close writing");
exit(1);
}
}
}
return 0;
}
When I finish compiling and run the executable, it just tells me bad file descriptors occurs. I tried to use gdb to take a closer look at where this error might occur, and I notice that gdb reports this error even before I call write().
I feel completely lost in this write and pipe concept, can someone kindly please explain to me what I did wrong somewhere in the process?
Your issue has nothing to do with any of the system calls you're using. It is more mundane. for (int j = 0; j < number_process; i++) is a bug. You are using i to access your array of file descriptors and incrementing it incorrectly. You meant to increment j.

Pipe: How do I ensure if I successfully write into the pipe?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define SIZE 50
struct record {
int freq;
char word[SIZE];
};
int main(){
int number_process = 3;
int pipes[number_process][2];
struct record r1;
r1.freq = 10;
strcpy(r1.word, "Cat");
struct record r2;
r2.freq = 20;
strcpy(r2.word, "Elephant");
struct record r3;
r3.freq = 30;
strcpy(r3.word, "Dragon");
struct record records_array[3] = {r1, r2, r3};
for (int i = 0; i < number_process; i++){
if (pipe(pipes[i]) == -1){
perror("pipe");
exit(1);
}
// Create children.
pid_t fork_result = fork();
if (fork_result == -1){
perror("Parent fork");
exit(1);
} else if (fork_result == 0){
if (close(pipes[i][0]) == -1){
perror("Child closes reading port");
exit(1);
}
// Later children is going to close all reading port from pipe that parent creates.
for (int child_no = 0; child_no < i; child_no++) {
if (close(pipes[child_no][0]) == -1) {
perror("close reading ends of previously forked children");
exit(1);
}
}
// Now, I am trying to write each strct record member from the above array into the pipe
// when I run the program, it won't allow me to do so because of bad file descriptor exception.
for (int j = 0; j < number_process; i++){
if (write(pipes[i][1], &(records_array[j]), sizeof(struct record)) == -1){
perror("write from child to pipe");
exit(1);
}
}
// Finishing writing, close the writing end in pipe.
if (close(pipes[i][1]) == -1){
perror("Child closes writing port");
exit(1);
}
// Terminate the process.
exit(0);
} else {
// Parent is closing all the writing ends in pipe.
if (close(pipes[i][1]) == -1){
perror("Parent close writing");
exit(1);
}
}
}
struct record buffer;
for (int i = 0; i < number_process; i++){
// Parent reads from the pipe.
if (read(pipes[i][0], &buffer, sizeof(struct record)) == -1){
perror("parent read");
exit(1);
}
printf("buffer.freq = %d\n", buffer.freq);
printf("buffer.word = %s\n", buffer.word);
}
return 0;
}
I am new to system programming, the following code is some practice I implement to see the pipe functionality. I have a few questions to my code:
1) Is there any system call or library call that will help me to ensure the content that I want to write into the pipe has actually been successfully written into the pipe? In other words, is there any methods available for me to check the content/data I wrote into the pipe?
2) I feel my parent reading part is not implemented correct, when I run this code, my parent reading part reads consecutive 3 same things, although it should be different things each time it read.
3) I have confronted the bad addresses issue when I try to read something from the pipe in my parent process, what is the reason that this error occurs?
Can someone please help me understand this stuff? Really appreciated.
You've got a simple cut-n-paste error. for (int j = 0; j < number_process; i++){
You need to increment j.

static variables in multiple processes (Signals)

I have 2 processes running test.c. There is a signal handler in test.c which executes an execlp. In test.c, I have a static variable which needs to be only initialized once, and incremented each time before the execlp call. When either process reaches 99, they exit.
Unfortunately, right now, it's not getting incremented, my guess is because there are 2 processes that each have a copy of the static variable. Here is test.c:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static int i = 0;
static int foo(int j)
{
printf("In the foo...\n");
j++;
printf("%d\n", j);
return j;
}
void main(int argc, char *argv[])
{
int pid, pid2, k;
int *h = malloc(sizeof(int));
int g = 0;
h = &g;
static char s[15];
pid = fork();
if (pid > 0)
{
sleep(1);
}
if (pid == 0)
{
k = foo(*h);
sprintf(s, "%d", k);
if (k >= 99)
{
printf("k=99\n");
exit(0);
}
execlp("./a.out", "forktest", s, NULL);
}
pid2 = fork();
if (pid2 == 0)
{
k = foo(*h);
sprintf(s, "%d", k);
if (k >= 99)
{
printf("k=99\n");
exit(0);
}
execlp("./a.out", "forktest", s, NULL);
}
wait(pid2);
wait(pid);
}
Can anyone please explain why there is an infinite loop? Why isn't the static variable get incremented?
Thank you.
Use Interprocess communication concepts (pipe, fifo, shared memory) here, execlp function overwrites memory of current program with new program. So when ever you call execlp gets called your program get refreshed and starts from begining and static int i is always 0.
I recommend to use pipe Refer this.
You need to use memory projection (mmap function) if you want to use the concept of shared memory between process.
In your code, the variable 'h' is the shared variable between the three process.It should defined using mmap function and initialized in the main process and then incremented in the two child process.
The answers to your two questions are related: either of the two child process never exits (exit(0)) because the if(k>=99) is never statisfied. This is due to the non-shared variable h which doesn't get incremented.
I will rather use a while loop and a return type main function.
By the way, you don't need the 'g' varibale, you can initialize directly 'h'. And there is no need of declaring the function foo as static (static functions are only useful when you want them to visible only with the file where they are defined). The buffer 's' can be declared non static (it is only a buffer which contains the value of k)
Here is a modified version of your code, it compiles and works fine.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
int foo(int* j)
{
printf("In the foo...\n");
(*j)++;
printf("%d\n", *j);
return *j;
}
int main(void)
{
int pid, pid2, k;
char s[15];
int * h = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (h == MAP_FAILED) {
printf("map failed\n");
return -1;
}
*h = 0;
pid = fork();
if (pid < 0) {
printf("fork failed pid\n");
return -1;
}
if (pid > 0) {
sleep(1);
}
else {
while(1) {
k = foo(h);
sprintf(s, "%d", k);
if (k>=99) {
printf("k>=99\n");
printf("%s\n", s);
exit(0);
}
execlp("./a.out", "forktest", s, NULL);
}
}
pid2 = fork();
if (pid2 < 0) {
printf("fork failed pid2\n");
}
if (pid2 > 0) {
sleep(1);
}
else {
while(1) {
k = foo(h);
sprintf(s, "%d", k);
if (k>=99) {
printf("k>=99\n");
exit(0);
}
execlp("./a.out", "forktest", s, NULL);
}
}
wait(pid);
wait(pid2);
return 0;
}
Here is the output (only the last strings) click on the link:
output

Segmentation fault 11 printing string from struct

This is the first time I've run into Segmentation fault 11 in C and I can't seem to wrap my head around what is actually going wrong.
What I'm trying to do is write a few int values to a struct plus the file name from the command line (char *) from a child process and then write the struct to the pipe to read from from the parent process. It works fine when it's only the integers and I take out the code working with the string, but once I add in the string and try to print out the file name in the parent process I get the segmentation fault 11 when the program is run.
I've looked at various posts from all over, but have noticed that the common issue for this is when someone attempts to assign a string to a char array and prints, but I made sure to use only char * here. Here's the code where it locks up
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
Here is the read out of what the program does when I run it:
$ ./a testfile
Parent process... should be waiting on child...
In child process! pid = 21993
it worked? testfile
Done with child process!
words = 1
lines = 2
bytes = 3
Segmentation fault: 11
Also here is the full code
EDIT: I swapped out the code using sizeof for string and used strlen
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char *file;
} Vals;
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
pid_t pid;
Vals v, pv;
char *fname = "Not set";
if(argc > 1)
{
fname = malloc(strlen(argv[1]));
strcpy(fname, argv[1]);
}
if((pipe(pd)) == -1)
{
error_exit("pipe creation");
}
if((pid = fork()) == -1)
{
error_exit("the fork forked up!");
}
else if(pid == 0)
{
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
v.file = malloc(strlen(fname));
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
{
error_exit("Write from child");
}
//return; //return from child
printf("Done with child process!\n");
close(pd[1]);
return 0;
}
else
{
printf("Parent process... should be waiting on child...\n");
}
//wait for child
while((pid = wait(NULL)) > 0);
close(pd[1]);
//Vals pv = {0, 0, 0, "pv.file not set"};
//just assign anything to file to see if it fixes
//pv.file = malloc(strlen(fname));
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
I really appreciate any insight on this!
Your main problem is that you don't quite have the right understanding of C strings. You cannot do sizeof(char_pointer). That will just give you the pointer size (4 in a 32 bit system) and not the size of the string it points to. Use strlen to get the length of a string.
The second related problem is that you are writing a pointer address, v.file, and not the full string contents through the pipe. That is not correct because each process has a seperate address space and hence a pointer in one process is not valid in another process.
There are several ways to fix your problem. I will give you the simplest (but not the best).
First declare file inside the struct as a char array rather than a char pointer. This essentially gives you a fixed sized buffer.
#define MAX_FILENAME_LEN 64
typedef struct total {
int words, lines, bytes;
char file[MAX_FILENAME_LEN];
} Vals;
Then remove the malloc call. You don't need it anymore as file is already a buffer that you can copy into.
Finally, make sure you don't overflow the buffer during string copy:
if (strlen(fname) >= MAX_FILENAME_LEN) {
error_exit("File name too long");
}
strcpy(v.file, fname);
You also don't need the +1 in the write as the sizeof gives you the full buffer size.
I'll leave it as an exercise for you to use dynamic memory for the file name in the struct. It's not hard but will require you to change your read and write logic a little as you will need to read/write the file name seperately (because writing the whole struct in that case will just write the pointer not the contents).
There's a few things wrong here. First, you aren't free()ing the space you allocate with malloc().
Second, you should be using strlen() in place of sizeof() in your calculations. This occurs twice in your code.
Third, the declaration char fname = "Not set"; is not safe, since it is actually a const char* to read-only memory (text segment), and it's later pointed to something allocated via malloc(). Don't do this.
Corrected Code Listing
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define MAX_BUF_LEN (1024)
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char file[MAX_BUF_LEN];
} Vals;
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
pid_t pid;
Vals v, pv;
char fname[MAX_BUF_LEN] = "Not set";
if(argc > 1) {
//fname = malloc(sizeof(argv[1]) + 1);
//fname = argv[1];
strcpy(fname, argv[1]);
}
if((pipe(pd)) == -1) {
error_exit("pipe creation");
}
if((pid = fork()) == -1) {
error_exit("the fork forked up!");
} else if(pid == 0) {
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
//v.file = malloc(strlen(fname) + 1);
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
error_exit("Write from child");
}
printf("Done with child process!\n");
close(pd[1]);
return 0; //return from child
}
else
{
printf("Parent process... should be waiting on child...\n");
}
//wait for child
while((pid = wait(NULL)) > 0);
close(pd[1]);
if((read(pd[0], &pv, 2048)) == -1) {
error_exit("read not working");
}
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
Sample Run
Parent process... should be waiting on child...
In child process! pid = 7410
it worked? HelloWorld
Done with child process!
words = 1
lines = 2
bytes = 3
file = HelloWorld
This code has several issues which for some reason were not mentioned. Hence here goes my take.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
Well, gcc -Wall -Wextra tells me:
warning: implicit declaration of function ‘wait’
How are you compiling this? Did you see this error and ignored it? If so, no candy for a week.
void error_exit(char *);
typedef struct total {
int words, lines, bytes;
char *file;
} Vals;
Weird naming. 'total'? 'vals'?
int main(int argc, char *argv[])
{
int pd[2]; //pipe descriptor
Rather useless comment.
pid_t pid;
Vals v, pv;
char *fname = "Not set";
if(argc > 1)
Should test argc == 2 and throw insults if > 2.
{
fname = malloc(strlen(argv[1]));
strcpy(fname, argv[1]);
Incorrect. strlen return the length without the terminating null character. Consider using strdup instead (non-standard). Missing NULL check.
}
if((pipe(pd)) == -1)
{
error_exit("pipe creation");
}
if((pid = fork()) == -1)
{
error_exit("the fork forked up!");
}
else if(pid == 0)
{
printf("In child process! pid = %d\n", getpid());
v.words = 1;
v.lines = 2;
v.bytes = 3;
v.file = malloc(strlen(fname));
strcpy(v.file, fname);
printf("it worked? %s\n", v.file);
close(pd[0]);
You typically close earlier.
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
{
error_exit("Write from child");
}
This code this does not work, but you may be tempted to use 'char file[BIGNUM];' mentioned in other comments, so let's steal a sample which was supposed to work:
if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
error_exit("Write from child");
}
Incorrect. Let's assume this adds up to the size of the structure - then '+1' found here causes reading 1 byte after the structure. But sizes of all struct elements are not guaranteed to add up to the size of the entire structure due to padding. If using 'char file[BIGNUM];' just sizeof(v). If playing with char *file, you have to make sure file is always last and for simplicity just use offsetof to the file pointer.
//return; //return from child
printf("Done with child process!\n");
close(pd[1]);
return 0;
Incorrect. Should use _Exit(2) instead.
}
else
{
printf("Parent process... should be waiting on child...\n");
}
What's up with the else clause which only prints something and passes execution below?
//wait for child
while((pid = wait(NULL)) > 0);
Incorrect. wait can return due to a signal.
close(pd[1]);
Should close before wait.
//Vals pv = {0, 0, 0, "pv.file not set"};
//just assign anything to file to see if it fixes
//pv.file = malloc(strlen(fname));
if((read(pd[0], &pv, 2048)) == -1)
{
error_exit("read not working");
}
pv does not have 2048 bytes, so this can happen to work only by accident.
printf("words = %d\n", pv.words);
printf("lines = %d\n", pv.lines);
printf("bytes = %d\n", pv.bytes);
printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line
close(pd[0]);
//program ended normally
return 0;
}
void error_exit(char *err)
{
printf("exiting because of this section: %s\nerrno = %d", err, errno);
exit(1);
}
Consider using perror or err-family of functions (not portable).
Finally, I recommend finding less atrocious style (from linux or KNF).

Segfault from execvp

I'm trying to exec input that I get from a socket. I take the message buffer and put it into a char *[] and it is null terminated it works for ls but it won't work with paramaters like ls -la.
char *CMD[msg.c+1];
CMD[msg.c] = NULL;
Here is me parsing and using execvp.
//parse
char *tmp = NULL;
tmp = strtok(msg.v,space);
for(i = 0; i < msg.c; i++){
CMD[i] = tmp;
tmp = strtok(NULL,space);
printf("%s\n",CMD[i]);
}
int fd[2];
pipe(fd);
pid_t pid = fork();
if (pid == 0) {
close(fd[0]);
dup2(fd[1], 1);
msg.c = execvp(CMD[0],CMD);
}
This is probably so late answer that no one cares... To summarise there are some issues in your program, but I do not really know why you got segmentation fault or if you already fixed that.
In the code:
use of strsep(3) instead of obsolete strtok(3)
strdup(3) to be sure no other code modifies msg, if not, then you can remove strdup and free
added some error handling with err(3) to find errors early (earlier).
you cannot do msg.c = execvp(CMD[0], CMD), the program is executing CMD and will never return; to be able to retrieve the exit code, use wait(2) or waitpid(2)
variable length of arrays (char *CMD[msg.c+1]) is allowed in C99, but I do not use it, you can if you want. :)
And here is the lengthy code, probably a lot of bugs, but please tell me and I will try to fix:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_ARGS 10
int
main(int argc, char *argv[])
{
char *cmd[MAX_ARGS];
char *s;
char buf[256];
char msg[] = "ls -la /";
int n;
int fd[2];
pid_t pid;
int stat;
if ((s = strdup(msg)) == NULL)
err(1, "strdup");
for (n = 0; n < MAX_ARGS && (cmd[n] = strsep(&s, " ")) != NULL; n++)
;
if (pipe(fd) == -1)
err(1, "pipe");
switch ((pid = fork())) {
case -1:
close(fd[0]);
close(fd[1]);
err(1, "fork");
case 0: /* child */
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execvp(cmd[0], cmd);
err(1, "execvp");
default: /* parent */
free(s);
close(fd[1]);
while ((n = read(fd[0], buf, sizeof(buf))) > 0)
write(STDOUT_FILENO, buf, n);
waitpid(pid, &stat, 0);
fprintf(stderr, "child(%d) exited with %d\n",
pid, WEXITSTATUS(stat));
}
return (0);
}

Resources