How to send arrays between threads using pipes? - c

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.

Related

how to use pipe and execvp in c via linux

I'm trying to use pipe command and I can't understand how to.
I've a lot of versions but I can't make it work.
first of all the hierarchy:
main prog - nicecmp - that will execute the child prog and print the result
child prog - loopcmp - that will execute his child prog and get the returned value and send it back to the parent in nicecmp.
loopcmp's childs - lencmp/lexcmp - both prog will be executed in loopcmp and return value between -1 to 2. (100% works)
shortly, I need to create a pipe and a new process that will run new program (loopcmp - added in the end of the code) using execvp, and I need to print the res of the loopcmp in the parent.
I can send it directly from the prog that I executed and I can use WEXITSTATUS in the child after the end of the loopcmp.
what's the right way to do so (from the progrem execution or after that I've returned from the loopcmp)
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define LINELEN (80)
#define READFROM ("./loopcmp")
typedef enum { eLexcmp, eLencmp, eNumOfCmp } eCmpstr;
const char* cmpstr[eNumOfCmp] = { "./lexcmp", "./lencmp" };
int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);
int mygeti();
int main(int argc, char *argv[])
{
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index, rc, status, res;
int pfd[2];/* Pipe file descriptors */
if (pipe(pfd) == -1) /* Create pipe */
exit(-2); // pipe failed !
char* myargs[4];
myargs[0]=strdup(READFROM);
while (1)
{
printf("Please enter first string:\n");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string:\n");
if (mygets(str2, LINELEN) == NULL)
break;
myargs[2] = strdup(str1);
myargs[3] = strdup(str2);
do {
printf("Please choose:\n");
for (int i = 0; i < eNumOfCmp; i++)
printf("%d - %s\n", i, cmpstr[i]);
index = mygeti();
} while ((index < 0) || (index >= eNumOfCmp));
myargs[1] = strdup(cmpstr[index]);
rc = fork();
if (rc < 0) // fork failed !
{
printf("fork failed\n");
return -2;
}
else if (rc == 0) { // child !
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
execvp(myargs[0],myargs);
}
else { // parent
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
read(pfd[0], &res, sizeof(int));
printf("%d\n", res);
if (close(pfd[0]) == -1)
exit(-2);
}
}
return 0;
}
loopcmp ->
int main(int argc, char *argv[])
{
int status,rc,res = 0;
if (argc != 4)
{
return -1;
}
char* myargs[3];
for(int i=0;i<3;i++){
myargs[i]=argv[i+1];
}
rc = fork();
if (rc < 0) //fork failed
{
return -2;
}
else if (rc == 0) //I'm the child
{
if(execvp(myargs[1], myargs)==-1)
return -2;
}
else // parent
{
wait(&status);
res = WEXITSTATUS(status);
if(res ==254) // invalid file path ! (254== -2)
return -2 ;
}
write(fileno(stdout),&res,sizeof(int));
return res;
}

How to use pipe and dup2 in c

I have to simulate this command using pipes in c: echo "<exp>" | bc -lq.
Process A must read a string and send it to process B;
Process B executes the "bc -lq" command and returns the result to A.
The code is this, but I can't understand why it doesn't work; in particular, the "bc" command appears to be unable to read the expression from stdin.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define N 1024
#define LEGGI(stringa) if(scanf("%s", stringa) == 0) \
{ \
perror("Impossibile leggere la stringa"); \
exit(EXIT_FAILURE); \
} \
void closePipe(int pipeFd);
void Dup2(int pipeTempFd, int fd);
void Write(int pipeTempFd, char stringa[], int n);
void Read(int pipeTempFd, char stringa[], int n);
int main()
{
char stringa[N];
LEGGI(stringa);
int pipeFd[2];
int pRicezioneFd[2];
if(pipe(pipeFd) == -1 || pipe(pRicezioneFd) == -1)
{
perror("impossibile eseguire la pipe");
exit(EXIT_FAILURE);
}
if(strncmp(stringa, "exit", N) != 0)
{
pid_t processPid;
if((processPid = fork()) == 1)
{
perror("impossibile eseguire la fork");
exit(EXIT_FAILURE);
}
if(processPid != 0)
{
closePipe(pipeFd[0]);
closePipe(pRicezioneFd[1]);
printf("operazione: %s\n", stringa);
Write(pipeFd[1], stringa, N);
closePipe(pipeFd[1]);
read(pRicezioneFd[0], stringa, N);
closePipe(pRicezioneFd[0]);
if(wait(NULL) == -1)
{
perror("Impossibile eseguire la wait");
exit(EXIT_FAILURE);
}
printf("%s\n", stringa);
}
else
{
closePipe(pipeFd[1]);
closePipe(pRicezioneFd[0]);
Dup2(pipeFd[0], 0);
Dup2(pRicezioneFd[1], 1);
Dup2(pRicezioneFd[1], 2);
// closePipe(pipeFd[0]);
// closePipe(pRicezioneFd[1]);
if(execl("/usr/bin/bc", "bc", "-lq", NULL) == -1)
{
perror("programma non reperibile");
exit(EXIT_FAILURE);
}
}
}
return 0;
}
void closePipe(int pipeFd)
{
if(close(pipeFd) == -1)
{
perror("impossibile chiudere il fd della pipe");
exit(EXIT_FAILURE);
}
}
void Dup2(int pipeTempFd, int fd)
{
if(dup2(pipeTempFd, fd) == -1)
{
perror("Impossibile eseguire la dup2");
exit(EXIT_FAILURE);
}
}
void Write(int pipeTempFd, char stringa[], int n)
{
if(write(pipeTempFd, stringa, n) == -1)
{
perror("impossibile scrivere sulla pipa");
exit(EXIT_FAILURE);
}
}
void Read(int pipeTempFd, char stringa[], int n)
{
if(read(pipeTempFd, stringa, n) == -1)
{
perror("impossibile leggere dalla pipe pipa");
exit(EXIT_FAILURE);
}
}
You are writing all 1024 bytes of the mostly uninitialized stringa to bc, which is then choking on illegal characters. bc expects newline terminated, "plain text" expressions.
#define N 1024
char stringa[N]; // stringa := "\236\0\0\241\177>\0\0\3704\241..."
scanf("%s", stringa); // stringa := "1+2\0\177>\0\0\3704\241..."
// ^^^^^^^^^^^^^^^^^^^^
....
Write(pipeFd[1], stringa, N); // ohimè!
You'll want something like this, instead:
Write(pipeFd[1], stringa, strlen(stringa));
Write(pipeFd[1], "\n", 1);
I think my code is similar to yours, one main change was the arguments to the exec call. Another change was that I only have 2 dup2 calls in the child process, one for changing stdin and the other for stdout. You only need to change these.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define N 1024
#define READ_END 0
#define WRITE_END 1
int main()
{
char *input = NULL;
size_t len = 0;
ssize_t amt_read = 0;
puts("Please enter a string: ");
amt_read = getline(&input, &len, stdin);
if(amt_read < 0)
exit(EXIT_FAILURE);
pid_t pid;
int fd[2];
int fd_return[2];
// Create the pipes
pipe(fd);
pipe(fd_return);
pid = fork();
if(pid==0)
{ // If child process
dup2(fd[READ_END], STDIN_FILENO);
dup2(fd_return[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
close(fd[READ_END]);
close(fd_return[WRITE_END]);
close(fd_return[READ_END]);
execl("/usr/bin/bc", "/usr/bin/bc", "-lq", (char *)NULL);
fprintf(stderr, "Failed to execute\n");
exit(EXIT_FAILURE);
}
else
{ // If parent process
int status;
close(fd[READ_END]);
close(fd_return[WRITE_END]);
// Write to the pipe
write(fd[WRITE_END], input, strlen(input));
close(fd[WRITE_END]);
// Wait for the child to finish
waitpid(pid, &status, 0);
char buff[N];
buff[N-1] = 0;
read(fd_return[READ_END], buff, N-1);
*(index(buff, '\n')) = '\0'; // Add null terminator on your own
close(fd_return[READ_END]);
printf("The Result is: %s\n", buff);
}
free(input);
return 0;
}
EDIT:
I edited the code and debugged it. I also changed the user input schematic so now you no longer allocated static storage for the user input, rather it is allocated dynamically and then this storage is freed at the end with free();
Note, I did leave the static storage for reading the input back in, you can change this in the future if you want.
illegal character: ^# comes from the fact that write() was writing too much, notice that now we only write strlen(buff) amount.
Note: The input to bc will not work unless the string ends like "...\n\0" luckily getline() does this by default.
Also, I left all the pipe closing the same, that was not causing the issue.

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.

Strange output using pipes for thread communication

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.

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