Strange output using pipes for thread communication - c

I have three threads - the first one reads a string, the second counts characters, and the third displays it. I'm using pipes for communication.
However, after running it nothing happens, and when I type in something, let's say "asd", I get:
asd
asd
Enter the message: Enter the message:
or
asd
asd
Enter the message:
What's wrong?
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#include <pthread.h>
#include <string.h>
int first[2];
int second[2];
void *input(void *ptr)
{
char str[100];
int length;
while(1)
{
printf("Enter the message: ");
length = read(STDIN_FILENO, str, sizeof(str));
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[1]);
exit(2);
}
if(write(first[1], str, length) != length)
{
perror("write");
exit(2);
}
}
}
void *countChars(void *ptr)
{
char str[100];
int length, count = 0;
while(1)
{
length = read(first[0], str, sizeof(str));
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[0]);
close(second[1]);
exit(2);
}
if(write(STDOUT_FILENO, str, length) != length)
{
perror("write");
exit(2);
}
while(str[count] != '\n') count++;
write(second[1], &count, sizeof(count));
count = 0;
}
}
void *output(void *ptr)
{
int length, count = 0;
while(1)
{
length = read(second[0], &count, sizeof(count));
if(length <= sizeof(count))
{
close(second[0]);
exit(2);
}
printf("Number of characters: %d\n", count);
}
}
int main()
{
pthread_t t1, t2, t3;
if(pipe(first) == -1)
{
printf("First pipe error");
exit(1);
}
if(pipe(second) == -1)
{
printf("Second pipe error");
exit(1);
}
pthread_create(&t1, NULL, input, NULL);
pthread_create(&t2, NULL, countChars, NULL);
pthread_create(&t3, NULL, output, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}

You have a logic problem in your code. In output:
if (length < sizeof (count)) { // not <=
length will always equal sizeof (count) on successful write of an integer.
Also, wrapping everything function in while (1) {...} is not the safest. Remove the while (1) loops and replace them with a return at the end of the function. i.e. return ptr; For example:
void *
output (void *ptr) {
int length, count = 0;
printf ("\noutput:\n\n");
// while (1) {
length = read (second[0], &count, sizeof (count));
printf ("count: %d\n", count);
if (length < sizeof (count)) { // not <=
printf ("closing second[0] and exiting\n");
close (second[0]);
exit (2);
}
printf ("Number of characters: %d\n", count);
// }
return ptr;
}

That's because printf only writes content to buffer. The content will be actually sent to stdout once it receives a line feed ('\n') or fflush(stdout) is called.
You can try to add flush(stdout); next to the printf("Enter the message: ");, and you should see what you expect.

Related

clearing an array in preparation for new array values

The function gets an input from the user using read() and breaks it down using strtok and places it into an array. The program loops until it reaches an error (which i won't get into because that isn't the problem here) or if the program is terminated by the user. However, when it loops back around and reads the input from the user, it seems to be hanging onto the previous input from the user.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#define BUFFERSIZE 1024
int main(int argc, char* argv[])
{
argc++;
char buf[BUFFERSIZE];
int n;
printf("Please enter commands: \n");
while ((n = read(STDIN_FILENO, buf, BUFFERSIZE)) > 0)
{
printf("original string: %s:\n", buf);
buf[strlen(buf)-1] = '\0';
printf("after change: %s:\n", buf);
int i = 0;
char* array[100];
char* token1 = strtok(buf, " ");
while ((token1 != NULL))
{
array[i++] = token1;
token1 = strtok(NULL, " ");
}//while
for (int j = 0; j < i; j++)
{
printf("Array value %d: %s:\n", j, array[j]);
}//for
if (buf == "exit")
{
printf("found it\n");
}//if
for (int i = 1; i < argc; i++)
{
pid_t pid;
if (argc >= 1)
{
if ((pid = fork()) < 0)
{
perror("fork");
}//if
else if (pid == 0)
{ // child process
if (execvp(array[0], array) == -1)
{
perror("execvp");
return EXIT_FAILURE;
} // if
}//else if
else
{ // parent process
int status;
wait(&status);
printf("Please enter commands again: \n");
}//else
}//if
else
{
fprintf(stderr, "Please specify the name of the program to exec as a command line argument\n");
return EXIT_FAILURE;
}//if
}//for
}//while
if (n == -1) perror("read");
}//main
I've tried to clear the array and clear "buf" but no luck. i have a feeling it has to do with the read() and the fact that "buf" is hanging onto its old value.
Prefaced by my top comments ...
read does not add 0x00 the way fgets does, so we have to do it manually.
execvp needs the array to be terminated with a NULL entry.
child should use exit instead of return.
Here's the refactored code. It is annotated. Note that this code does not do the split/join of the buffer to guarantee the buffer ending in newline as suggested by my top comments:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#define BUFFERSIZE 1024
int
main(int argc, char *argv[])
{
argc++;
char buf[BUFFERSIZE];
int n;
printf("Please enter commands: \n");
// NOTE/BUG: read does _not_ add 0x00 the way fgets does
#if 0
while ((n = read(STDIN_FILENO, buf, BUFFERSIZE)) > 0) {
#else
while ((n = read(STDIN_FILENO, buf, BUFFERSIZE - 1)) > 0) {
buf[n] = 0;
#endif
printf("original string: %s:\n", buf);
buf[strlen(buf) - 1] = '\0';
printf("after change: %s:\n", buf);
int i = 0;
char *array[100];
char *token1 = strtok(buf, " ");
while ((token1 != NULL)) {
array[i++] = token1;
token1 = strtok(NULL, " ");
}
// NOTE/BUG: execvp needs a NULL terminator
#if 1
array[i] = NULL;
#endif
for (int j = 0; j < i; j++) {
printf("Array value %d: %s:\n", j, array[j]);
}
// NOTE/BUG: wrong way to compare strings
#if 0
if (buf == "exit")
#else
if (strcmp(buf, "exit") == 0) {
printf("found it\n");
break;
}
#endif
for (int i = 1; i < argc; i++) {
pid_t pid;
if (argc >= 1) {
if ((pid = fork()) < 0) {
perror("fork");
}
// child process
else if (pid == 0) {
if (execvp(array[0], array) == -1) {
perror("execvp");
#if 0
return EXIT_FAILURE;
#else
exit(EXIT_FAILURE);
#endif
}
}
// parent process
else {
int status;
wait(&status);
printf("Please enter commands again: \n");
}
}
else {
fprintf(stderr, "Please specify the name of the program to exec as a command line argument\n");
return EXIT_FAILURE;
}
}
}
if (n == -1)
perror("read");
}
In the above code, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k

Segmentation fault (core dumped) with FIFO pipes and fork communication between server and client

I'm writing a project where you start the server in one terminal, and in other terminals that are clients you can send messages from one user to another using FIFO pipes. Server creates FIFO pipe that reads messages from clients. Client creates FIFO pipe to read messages from server.
In one terminal to start the server I type ./projectname --start and in client i type ./projectname --login nickname. When I close the client terminal my server receives segmentation fault(core dumped) error message. I tried to get rid of this in every possible way I know for x hours. How can it be fixed?
I have also tried to register users using the void verifyloginclient(char *login) function but parent proccess is unable to receive information from child and is stuck in infinite while(1) sending some message to server that also crashes server with segmentation fault so it's commented for now.
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
char userlist[10][30];
int succesfulllogin = 1;
void handler(int signum){
pid_t pid;
pid = wait(NULL);
succesfulllogin = 0;
printf("Parent knows child %d finished\n", (int)pid);
printf("Parent ending...\n");
}
void sig_handler_parent(int signum){
printf("signal response from child!\n");
succesfulllogin = 0;
exit(0);
}
void sig_handler_child(int signum){
printf("signal from parent\n");
succesfulllogin = 0;
exit(0);
}
void verifyloginclient(char *login)
{
int fd;
char buf[256];
char buf2[256];
snprintf(buf, sizeof buf, "%s%s", "fifopipes/","serwer.fifo");
if((fd = open(buf, O_WRONLY)) == -1){
perror("openFdloginclient");
exit(EXIT_FAILURE);
}
snprintf(buf2, sizeof buf2, "%s%s%s", login, " /login ",login);
if((write(fd, buf2, strlen(buf2))) == -1){
perror("writeloginclient");
}
close(fd);
}
int verifyloginserwer(char *login)
{
// check if login is on the userlist
int flagcmp = -1;
int x;
int indeks=0;
for(int i=0;i<10;i++){
x = strcmp(&userlist[i][0],login);
if(x==0){
flagcmp = i;
}
}
if(flagcmp == -1){ //if it doesnt exist we add him to first free slot
for(int i=0;i<10;i++){
if(userlist[i][0]=='\0'){
strcpy(userlist[i],login);
printf("userlist: %s\n", userlist[i]);
succesfulllogin = 1;
break;
}
else{
indeks++;
}
}
}
else if(flagcmp != -1 || indeks==10){
fprintf(stderr, "loggin error!\n");
succesfulllogin = 0;
return 2;
}
return 0;
}
int splitstring(char *source, int desiredlength, char **s1,char **s2)
{
int len;
len = strlen(source);
if (desiredlength > len)
return(0);
*s1 = (char *) malloc(sizeof(char) * desiredlength);
*s2 = (char *) malloc(sizeof(char) * len-desiredlength+1);
if(s1 ==NULL || s2 == NULL)
return 0;
strncpy(*s1,source,desiredlength);
strncpy(*s2,source+desiredlength, len-desiredlength);
return(1);
}
void startserwer()
{
int fdserwer;
int fdanuluj;
int fd;
char readbuf[80];
int read_bytes;
char buf[256]; //pipe server
char buf2[256]; //pipe client
char buf3[256]; // message to client
for(int i=0;i<10;i++){ // declare userlist
userlist[i][0] = '\0';
}
snprintf(buf, sizeof buf, "%s%s", "fifopipes/","serwer.fifo");
umask(0);
if(mkfifo(buf, 0777) == -1){
perror("mkfifoserwer");
exit(EXIT_FAILURE);
}
while(1){
if((fdserwer = open(buf, O_CREAT | O_RDONLY))== -1){
perror("openFdserwer");
exit(EXIT_FAILURE);
}
if((read_bytes = read(fdserwer, &readbuf, sizeof(readbuf)))== -1){
perror("readbytes");
}
else{
readbuf[read_bytes] = '\0';
//SENDER LOGIN
char senderlogin[80];
strcpy(senderlogin, readbuf);
char *token = strtok(senderlogin, " ");
printf("%s\n", token);
strcpy(senderlogin, token);
int b;
char *loginnadawcy;
char *resztakomendy;
b = splitstring(readbuf, (int) strlen(token)+1,&loginnadawcy,&resztakomendy);
if(b!=1){
printf("error in command\n");
}
else{
loginnadawcy[strlen(token)]='\0';
//COMMAND NAME AFTER SLASH /
char command[80];
strcpy(command, readbuf);
token = strtok(NULL, " ");
printf("%s\n",token);
strcpy(command, token);
int a;
char *komenda;
char *reszta;
a = splitstring(resztakomendy, (int)strlen(token)+1,&komenda,&reszta);
if(a!=1){
printf("error\n");
}
else{
komenda[strlen(token)]='\0';
if(strcmp(komenda,"/login")==0){
if(verifyloginserwer(senderlogin)==2){
//open client pipe
snprintf(buf2, sizeof buf2, "%s%s%s", "fifopipes/",senderlogin,".fifo");
if((fdanuluj = open(buf2, O_WRONLY)) == -1){
perror("openFdanuluj");
exit(EXIT_FAILURE);
}
//SEND MESSAGE TO CLIENT
snprintf(buf3, sizeof buf3, "%s%s", "serwer ", "end");
if((write(fdanuluj, buf3, strlen(buf3))) == -1){
perror("writeFdanuluj");
}
close(fdanuluj);
}
}
else if(strcmp(komenda,"/w")==0){
//RECEIVER LOGIN
char receiverlogin[80];
token = strtok(NULL, " ");
printf("%s\n",token);
strcpy(receiverlogin, token);
int r;
char *login;
char *message;
r = splitstring(reszta,(int)strlen(token)+1,&login,&message);
if(r!=1){
printf("error\n");
}
else{
//RECEIVE INFORMATION
login[strlen(receiverlogin)]='\0';
printf("from %s to %s: %s and length is %d \n", senderlogin, login, message, (int)strlen(message));
//OPEN CLIENT PIPE
snprintf(buf2, sizeof buf2, "%s%s%s", "fifopipes/",receiverlogin,".fifo");
if((fd = open(buf2, O_WRONLY))== -1){
perror("openFd");
exit(EXIT_FAILURE);
}
//SEND MESSAGE TO CLIENT
snprintf(buf3, sizeof buf3, "%s%s%s", senderlogin," ",message);
if( (write(fd, buf3, strlen(buf3) )) == -1){
perror("writeopenfd");
}
close(fd);
}
}
}
}
}
close(fdserwer);
}
unlink(buf);
}
void clientchild(char *login)
{
int fd;
char readbuf[80];
int read_bytes;
char buf[256];
snprintf(buf, sizeof buf, "%s%s%s", "fifopipes/",login,".fifo");
umask(0);
if(mkfifo(buf, 0777) == -1){
perror("mkfifoclient");
exit(EXIT_FAILURE);
}
while(1){
if((fd = open(buf, O_CREAT | O_RDONLY)) == -1){
perror("openFdchild");
exit(EXIT_FAILURE);
}
if((read_bytes = read(fd, &readbuf, sizeof(readbuf))) == -1){
perror("readfdchild");
}
else{
readbuf[read_bytes] = '\0';
//SENDER LOGIN
char nickname[80];
strcpy(nickname, readbuf);
char * token = strtok(nickname, " ");
//printf("%s\n",token);
int r;
char *login;
char *message;
r = splitstring(readbuf,(int)strlen(token)+1,&login,&message);
if(r!=1){
printf("blad\n");
}
else{
login[strlen(token)]='\0';
if(strcmp(login,"serwer")==0 && strcmp(message,"end")==0){
printf("Login error. Closing...\n");
close(fd);
break;
}
printf("%s: %s and length is %d \n", login, message, (int)strlen(message));
close(fd);
//printf("Received string: \"%s\" and length is %d \n", readbuf, (int)strlen(readbuf));
}
//free(login);
//free(message);
}
}
unlink(buf);
}
void clientparent(char *login)
{
signal(SIGQUIT,sig_handler_parent);
int fd;
int stringlen;
char readbuf[80];
char buf[256];
char buf2[256];
snprintf(buf, sizeof buf, "%s%s", "fifopipes/","serwer.fifo");
printf("FIFO_CLIENT: Send messages infinitely\n");
while(1){
if((fd = open(buf, O_WRONLY)) == -1){
perror("openFdparent");
exit(EXIT_FAILURE);
}
//printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
snprintf(buf2, sizeof buf2, "%s%s%s", login," ",readbuf);
if(strlen(login)+1 == strlen(buf2)){
printf("Error\n");
break;
}
else{
if((write(fd, buf2, strlen(buf2))) == -1){
perror("writeparent");
}
printf("Sent string: \"%s\" and string length is %d \n", buf2, (int)strlen(buf2));
}
}
close(fd);
}
void splitclient(char *login)
{
printf("login: %s\n", login);
pid_t pid = fork();
printf("fork returned: %d\n", (int) pid);
//signal(SIGCHLD, handler);
if (pid < 0){
perror("Fork failed");
}
else if (pid == 0){
signal(SIGQUIT,sig_handler_child);
printf("I am the child with pid %d\n", (int) getpid());
char buf[256];
snprintf(buf, sizeof buf, "%s%d", "Child ",(int) getpid());
//kod dziecka
clientchild(login);
//kill(getppid(),SIGQUIT);
exit(0);
}
else{
// We must be the parent
printf("I am the parent, waiting for child to end \n");
//kod rodzica
clientparent(login);
pid_t childpid = wait(NULL);
printf("Parent knows child %d finished\n", (int)childpid);
printf("Parent ending...\n");
}
}
int main(int argc, char **argv)
{
while(1)
{
int c;
int option_index = 0;
static struct option long_options[] =
{
{"start", no_argument, NULL, 's'},
{"login", required_argument, NULL, 'l'},
{0, 0, 0, 0}
};
c = getopt_long (argc, argv, "sl:", long_options, &option_index);
if (c == -1)
break;
switch (c)
{
case 's':
printf("start server\n");
startserwer();
break;
case 'l':
printf("login with option %s \n", optarg);
//verifyloginclient(optarg);
splitclient(optarg);
break;
case '?':
break;
default:
abort();
}
}
if (optind < argc)
{
printf("non-option ARGV-elements: ");
while (optind < argc)
printf("%s ", argv[optind++]);
putchar ('\n');
}
return 0;
}
From this piece in the code in startServer():
if((read_bytes = read(fdserwer, &readbuf, sizeof(readbuf)))== -1){
perror("readbytes");
}
else{
:
char *token = strtok(senderlogin, " ");
:
strcpy(senderlogin, token);
b = splitstring(readbuf, (int)
strlen(token)+1,&loginnadawcy,&resztakomendy);
You are not validating if token is a non-NULL pointer post tokenization. In the case the child exits, read() would return a 0 (and not -1 as a failure), indicating EOF as the man tells:
RETURN VALUE
On success, the number of bytes read is returned (zero indicates
end of file),
Thus the senderlogin buffer would be empty and wouldn't yield any tokens. You need to factor in this case and have appropriate null checks in place. Adding a null chek will lead to a graceful exit of the server program.

Implementing unlimited piping in shell using C

I'm trying to implement a C shell that allows for unlimited unidirectional pipes using the character '>'
So it can handle ls -A > tail > grep '.zip'
I understand that pipes are supposed to talk between processes, but I thought I came up with an idea that could use one pipe and multiple children.
This is what I have so far
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
/*#include <wait.h>*/
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int runCommand(char **arguments, int *fd, int pipeHasSomeData, int baseCase) {
pid_t pid;
int x = 0;
int status;
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);
if(baseCase) {
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal");
}
printf("\n \n");
}
return 1;
} else {
if(pipeHasSomeData == 1) {// read from the pipe
dup2(fd[0], 0);//read from pipe
}
if(baseCase == 0) {// not the base case
dup2(fd[1], 1);//write to pipe
} else {
close(fd[1]);//close write
}
exit(execvp(arguments[0], arguments));
return 0;
}
}
int execute_commands(char *arguments[1000][1000], int pd[2] = NULL) {
int current_count = args_count;
int iterator = 0;
int fd[2];
int useAPipeInCommand = 0;
pipe(fd);
while(iterator <= args_count) {//go through and execute all the commands
if(current_count == 0) {//base case
return runCommand(arguments[iterator], fd, useAPipeInCommand, 1);
} else {
runCommand(arguments[iterator], fd, useAPipeInCommand, 0);
useAPipeInCommand = 1;
}
iterator++;
current_count--;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}
It is running the single base case no problem but the shell freezes when I do a pipe. Any ideas on the issue?
Correct answer from Comments by #beau-bouchard and #rici:
Pipes have a (small) finite buffer; you cannot write more than a little bit to the pipe without blocking unless the other end of the pipe is being read.
For a correct implementation, check out "multiple pipes in C" Coding multiple pipe in C
--UPDATE:
Here is my final working code for anyone that is having a similar issue:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
int READ = 0;
int WRITE = 1;
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int execute_commands(char *arguments[1000][1000]) {
int pd[2];
int iterator = 0;
int fd[2];
int f_in = 0;
while(iterator <= args_count) {//go through and execute all the commands
pid_t pid;
int status;
pipe(fd);
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);//wait for child to exit
close(fd[WRITE]);//close the writing end
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[iterator][0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal %d", status);
}
f_in = fd[READ];//set the pipe to the in
if(iterator == args_count) {
printf("\n \n");
}
//return 1;
} else {
dup2(f_in, 0);
if(iterator != args_count) {//its not the main value
dup2(fd[WRITE], 1);//write to pipe
}
close(fd[READ]);
exit(execvp(arguments[iterator][0], arguments[iterator]));
return 0;
}
iterator++;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}

How to send arrays between threads using pipes?

I have three threads - the first one reads sentences until ";" is given, the second counts the characters in those sentences, and the third shows the result.
Well, I've done this for only one sentence, but sending arrays through pipes seems to generate some problems (as well as reading multiple strings from a thread).
For reading, I can put the string only once, and no more. Even mutex on the whole function doesn't work. Why is that so?
Also, after writing the string I get "write: Success" message.
What's wrong here?
This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#include <pthread.h>
#include <string.h>
int first[2];
int second[2];
void *input(void *ptr)
{
char str[100], ch = '0';
int length, i = 0;
while(1)
{
while(ch != ';')
{
printf("Enter the %d message: ", i + 1);
fflush(stdout);
length = read(STDIN_FILENO, str, sizeof(str));
if(write(first[1], str, sizeof(str)) != length)
{
perror("write");
exit(2);
}
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[1]);
exit(2);
}
i++;
}
}
}
void *countChars(void *ptr)
{
char str[100];
int length, count = 0, i = 0;
while(1)
{
length = read(first[0], str, sizeof(str));
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[0]);
close(second[1]);
exit(2);
}
if(write(STDOUT_FILENO, str, length) != length)
{
perror("write");
exit(2);
}
while(str[count] != '\n') count++;
write(second[1], &count, sizeof(count));
count = 0;
}
}
void *output(void *ptr)
{
int length, count = 0, i = 0;
while(1)
{
length = read(second[0], &count, sizeof(count));
if(length < sizeof(count))
{
close(second[0]);
exit(2);
}
printf("Number of characters: %d\n", count);
}
}
int main()
{
pthread_t t1, t2, t3;
if(pipe(first) == -1)
{
printf("First pipe error");
exit(1);
}
if(pipe(second) == -1)
{
printf("Second pipe error");
exit(1);
}
pthread_create(&t1, NULL, input, NULL);
pthread_create(&t2, NULL, countChars, NULL);
pthread_create(&t3, NULL, output, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}
EDIT.
I think the question would be - how to logically solve this? I see it that way:
Thread1 -> (string) -> Thread2 -> (number of chars) -> Thread3 - save elements somewhere
...
Thread1 -> (ending string) -> Thread2 -> (number of chars removed later) -> Thread3 - display all elements
BUT if so then - how to make threads run one by one like this? How to stop the application on ending string? Where to save those integer values in thread 3?
Pipes are used to communicate data between processes, not threads. Threads run in the same process and have access to the same memory so it is pointless to use pipes in that case.
An example of a pipeline with three processes. Parent sends "hello world" to child, who prepends the string length and sends that new string to the grandchild who prints it to stdout.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void parent(int fd_write) {
char *msg = "hello world";
ssize_t len = strlen(msg);
if (write(fd_write, msg, len) != len) {perror("parent write"); exit(1);}
}
void child(int fd_read, int fd_write) {
char msg_in[100], msg_out[150];
ssize_t len = read(fd_read, msg_in, sizeof msg_in);
if (len == -1) {perror("child read"); exit(1);}
msg_in[len] = '\0';
len = sprintf(msg_out, "%d: %s", (int)len, msg_in);
if (write(fd_write, msg_out, len) != len) {perror("child write"); exit(1);}
}
void grandchild(int fd_read) {
char msg[256];
ssize_t len = read(fd_read, msg, sizeof msg);
if (len == -1) {perror("grandchild read"); exit(1);}
msg[len] = '\0';
printf("Msg: %s\n", msg);
}
int main() {
enum {READ, WRITE};
pid_t pid;
int fd[2];
if (pipe(fd) == -1) {perror("first pipe"); exit(1);}
pid = fork();
if (pid == -1) {perror("first fork"); exit(1);}
if (pid == 0) {
int fd2[2];
if (pipe(fd2) == -1) {perror("second pipe"); exit(1);}
pid = fork();
if (pid == -1) {perror("second fork"); exit(1);}
if (pid == 0) {
close(fd2[WRITE]);
grandchild(fd2[READ]);
close(fd2[READ]);
exit(0);
}
close(fd[WRITE]); close(fd2[READ]);
child(fd[READ], fd2[WRITE]);
close(fd[READ]); close(fd2[WRITE]);
wait(NULL);
exit(0);
}
close(fd[READ]);
parent(fd[WRITE]);
close(fd[WRITE]);
wait(NULL);
return 0;
}
In the input thread, after the read call length = read(STDIN_FILENO, str, sizeof(str));, you are writing sizeof(str) and not of size length.
It should be
if(write(first[1], str, length) != length)
Another issue is your code is not matching your specification.
You say that input thread is reading until ';', but ch is never modified in the loop. Fix your code.

Sending data between threads using pipes

I have 3 threads - I have to read something in the first one, count characters in the second, and make an output in the third. So I'm using two pipes here; for the 1st - 2nd threads, and 2nd - 3rd.
However, it doesn't work at all. I mean, I can type the string in the console, but then nothing happenes, there's no output.
What's wrong here?
Also, is this possible to add something like "Type string here: " in the first thread somewhere? Adding it in the while loop seems to produce strange results - it displays randomly after running the program :P
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#include <pthread.h>
#include <string.h>
int first[2];
int second[2];
void *input(void *ptr)
{
while(1)
{
char str[100];
int result;
result = read(STDIN_FILENO, str, sizeof(str));
if(result <= 0)
{
if(result == -1)
perror("read");
close(first[1]);
exit(2);
}
if(write(first[1], str, result) != result)
{
perror("write");
exit(2);
}
}
}
void *countChars(void *ptr)
{
int result, result2, count = 0;
char str[100];
while(1)
{
result = read(first[0], str, sizeof(str));
if(result <= 0)
{
if(result == -1)
perror("read");
close(first[0]);
close(second[1]);
exit(2);
}
if(write(STDOUT_FILENO, str, result) != result)
{
perror("write");
exit(2);
}
while(str[count] != '\0') count++;
write(second[1], &count, sizeof(count));
}
}
void *output(void *ptr)
{
int result2, count = 0;
char str[100];
while(1)
{
result2 = read(second[0], str, sizeof(str));
if(result2 <= 0)
{
if(result2 == -1)
perror("read");
close(second[0]);
exit(2);
}
if(write(STDOUT_FILENO, str, result2) != result2)
{
perror("write");
exit(2);
}
while(str[count] != '\0') count++;
printf("Writer: %d\n", count - 1);
}
}
int main()
{
pthread_t t1, t2, t3;
if(pipe(first) == -1)
{
printf("First pipe error");
exit(1);
}
if(pipe(second) == -1)
{
printf("Second pipe error");
exit(1);
}
pthread_create(&t1, NULL, input, NULL);
pthread_create(&t2, NULL, countChars, NULL);
pthread_create(&t3, NULL, output, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}
You want the countChars function to count until a newline and write the int "count"
void *countChars(void *ptr)
{
int result, result2, count = 0;
char str[100];
while(1)
{
result = read(first[0], str, sizeof(str));
if(result <= 0)
{
if(result == -1)
perror("read");
close(first[0]);
close(second[1]);
exit(2);
}
if(write(STDOUT_FILENO, str, result) != result)
{
perror("write");
exit(2);
}
//while(str[count] != '\0') count++;
while(str[count] != '\n') count++;
write(second[1], &count, sizeof(count));
count = 0;
}
}
And the output function to read the sizeof an int into an int variable.
void *output(void *ptr)
{
int result2, count = 0;
//char str[100];
while(1)
{
//result2 = read(second[0], str, sizeof(str));
result2 = read(second[0], &count, sizeof(count));
if(result2 < sizeof(count))
{
close(second[0]);
exit(2);
}
printf("Writer: %d\n", count);
}
}
This works ok because only countChars is writing to the 2nd pipe. A pipe write/read is atomic up to PIPE_BUF chars and an int is way less than that so the reads are predictable.

Resources