I have to write program in C on Linux. It has to have 3 processes - first reads from STDIN, sends message through FIFO to second process, which counts lenght of recevied message and sends result to third process (also through FIFO), which displays it on STDOUT. I have to synchronize it using semaphores. Also I have to add signal handling (which I am doing using shared memory) - one signal to end program, second to stop it, third to resume. Signal can be send to any of the processes. I have some code already, but it's not working as it should.
First problem is with synchronization - as you can see by running it, first message is received by second process, but then it stuck. First and second processes are showing their messages, but not the third one. There are very similar, so it's confusing. I have to send another message, then P3 is showing the length of previous one.
Second problem is with signals - after sending one, I have to press enter (for SIGUSR) or send message (for SIGINT) for it to be served.
Any ideas what's wrong? There are some improvements from what I posted before, but it's still not working properly and I don't have much time to finish it (till monday). I know it's a lot of code, but if someone could just analyze communication of second and third process, I will be very grateful.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#define WRITE 1
#define READ 0
#define MEM_ID 1
#define MEM_SIZE 1
#define OK "[ \033[1;32mOK\033[0m ]\n"
#define ERR "[\033[1;31mFAIL\033[0m] "
#define SIGNAL "\033[1;33m\033[5m>> SIGNAL <<\033[0m\n"
#define S1 SIGINT
#define S2 SIGUSR1
#define S3 SIGUSR2
#define S4 SIGCONT
/* union semun - from POSIX specification for semctl() */
/* NB: on Mac OS X, and apparently in defiance of POSIX, <sys/sem.h> declares union semun */
/*
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
*/
/*
union semun
{
int val;
ushort *array;
};
*/
int process1(void);
int process2(void);
int process3(void);
void signal_callback(int signo);
//void signal_handling(int snd, int rcv); // JL
void signal_handling(void); // JL
void sem_down(int semid, int semnum);
void sem_up(int semid, int semnum);
char work = 1, quit = 0;
char inter_snd = 0, inter_rcv = 0;
struct sembuf semstruct;
int sem23, sem12, mem;
char *message;
int res[3];
static const char *fifoname[] = { "1fifo2", "2fifo3" };
static
void main_quit(int n)
{
printf("%s(): signal %d\n", __func__, n); // JL
kill(res[0], S1);
}
int main(void)
{
//union semun arg; // JL
printf("[G] Launching\n");
signal(SIGINT, main_quit);
signal(SIGTERM, main_quit);
// creating FIFO
printf("[G] Creating FIFO... ");
res[0] = mkfifo(fifoname[0], 0644);
res[1] = mkfifo(fifoname[1], 0644);
if ((res[0] == -1) || (res[1] == -1))
{
perror(ERR);
unlink(fifoname[0]);
unlink(fifoname[1]);
return 1;
}
else
printf(OK);
// create two semaphores and set values
printf("[G] Creating semaphores... ");
sem12 = semget(READ, 1, IPC_CREAT | 0644);
sem23 = semget(WRITE, 1, IPC_CREAT | 0644);
if ((sem23 == -1) || (sem12 == -1))
{
perror(ERR);
return 1;
}
else
printf(OK);
printf("[G] Initializing semaphores values... ");
semctl(sem12, 0, SETVAL, 0);
semctl(sem12, 1, SETVAL, 1);
semctl(sem23, 0, SETVAL, 0);
semctl(sem23, 1, SETVAL, 1);
printf(OK);
// creating shared memory
printf("[G] Reserving shared memory... ");
mem = shmget(MEM_ID, MEM_SIZE, IPC_CREAT | 0644);
message = (char *)shmat(mem, 0, 0);
if (mem == -1)
{
perror(ERR);
return 1;
}
else
printf(OK);
if ((res[0] = fork()) == 0)
{
process1();
exit(0);
}
if ((res[1] = fork()) == 0)
{
process2();
exit(0);
}
if ((res[2] = fork()) == 0)
{
process3();
exit(0);
}
printf("[G] Building process tree... ");
if ((res[0] == -1) || (res[1] == -1) || (res[2] == -1))
{
perror(ERR);
return 1;
}
else
{
printf(OK);
printf("[G] P1[pid]: %d, P2[pid]: %d, P3[pid]: %d\n", res[0], res[1], res[2]);
}
wait(NULL);
wait(NULL);
wait(NULL);
printf("[G] Deleting FIFO... ");
res[0] = unlink(fifoname[0]);
res[1] = unlink(fifoname[1]);
if ((res[0] == -1) || (res[1] == -1))
perror(ERR);
else
printf(OK);
printf("[G] Freeing shared memory... ");
res[0] = shmdt((char *)message);
res[1] = shmctl(mem, IPC_RMID, 0);
if ((res[0] == -1) || (res[1] == -1))
perror(ERR);
else
printf(OK);
printf("[G] Deleting semaphores... ");
res[0] = semctl(sem23, 0, IPC_RMID, 0);
res[1] = semctl(sem12, 0, IPC_RMID, 0);
if ((res[0] == -1) || (res[1] == -1))
perror(ERR);
else
printf(OK);
printf("[G] Ending...\n");
return 0;
}
int process1(void)
{
char tab[100];
FILE *fifoh;
signal(S1, signal_callback);
signal(S2, signal_callback);
signal(S3, signal_callback);
signal(S4, signal_callback);
fifoh = fopen(fifoname[0], "w");
setbuf(fifoh, NULL);
printf("[P1] Ready.\n");
//while (fgets(tab, sizeof(tab), stdin) > 0) // JL
while (fgets(tab, sizeof(tab), stdin) != 0) // JL
{
if (work)
{
sem_down(sem12, WRITE);
printf("[P1] Sending: %s", tab);
fprintf(fifoh, "%s\n", tab);
sem_up(sem12, READ);
}
//signal_handling(inter_snd, inter_rcv); // JL
signal_handling(); // JL
}
fclose(fifoh);
printf("[P1] Ending...\n");
return 0;
}
int process2(void)
{
char tab[100];
FILE *fifo_in, *fifo_out;
printf("[P2] Ready.\n");
fifo_in = fopen(fifoname[0], "r");
fifo_out = fopen(fifoname[1], "w");
setbuf(fifo_out, NULL);
setbuf(fifo_in, NULL);
signal(S1, signal_callback);
signal(S2, signal_callback);
signal(S3, signal_callback);
signal(S4, signal_callback);
do
{
if (work)
{
sem_down(sem12, READ);
fscanf(fifo_in, "%s", (char *)tab);
sem_up(sem12, WRITE);
printf("[P2] Received \"%s\" with length %zu.\n", tab, strlen(tab));
sem_down(sem23, WRITE);
fprintf(fifo_out, "%d\n", (int)strlen(tab));
sem_up(sem23, READ);
}
//signal_handling(inter_snd, inter_rcv); // JL
signal_handling(); // JL
} while (!quit);
fclose(fifo_in);
fclose(fifo_out);
printf("[P2] Ending...\n");
return 0;
}
int process3(void)
{
FILE *fifo_in;
int count;
printf("[P3] Ready.\n");
signal(S1, signal_callback);
signal(S2, signal_callback);
signal(S3, signal_callback);
signal(S4, signal_callback);
fifo_in = fopen(fifoname[1], "r");
setbuf(fifo_in, NULL);
do
{
if (work)
{
sem_down(sem23, READ);
fscanf(fifo_in, "%d\n", (int *)&count);
sem_up(sem23, WRITE);
printf("[P3] Received: %d characters.\n", count);
}
//signal_handling(inter_snd, inter_rcv); // JL
signal_handling(); // JL
} while (!quit);
fclose(fifo_in);
printf("[P3] Ending...\n");
return 0;
}
//void signal_handling(int snd, int rvc)
void signal_handling(void)
{
if (inter_snd > 0)
{
printf("Signal received...\n");
semstruct.sem_op = -3;
semop(sem23, &semstruct, 1);
*message = inter_snd;
inter_snd = 0;
semstruct.sem_op = 3;
semop(sem12, &semstruct, 1);
printf("Sending to other processes\n");
kill(0, S4);
}
if (inter_rcv)
{
inter_rcv = 0;
semstruct.sem_op = -1;
semop(sem12, &semstruct, 1);
switch (*message)
{
case 1:
printf("Quitting...\n");
quit = 1;
break;
case 2:
printf("Stopping...\n");
work = 0;
break;
case 3:
printf("Starting...\n");
work = 1;
break;
default:
printf("There's garbage in memory :/..\n");
}
semstruct.sem_op = 1;
semop(sem23, &semstruct, 1);
}
}
void signal_callback(int signo)
{
printf(SIGNAL);
switch (signo)
{
case S1:
inter_snd = 1;
break;
case S2:
inter_snd = 2;
break;
case S3:
inter_snd = 3;
break;
case S4:
inter_rcv = 1;
break;
}
}
void sem_down(int semid, int semnum)
{
semstruct.sem_flg = 0;
semstruct.sem_num = semnum;
semstruct.sem_op = -1;
do
{
errno = 0;
semop(semid, &semstruct, 1);
} while (errno == EINTR);
}
void sem_up(int semid, int semnum)
{
semstruct.sem_flg = 0;
semstruct.sem_num = semnum;
semstruct.sem_op = 1;
semop(semid, &semstruct, 1);
}
Expected behaviour:
[P3] Ready.
[P2] Ready.
[P1] Ready.
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 3 characters.
as
[P1] Sending: as
[P2] Received "as" with length 2.
[P3] Received: 2 characters.
Signals:
Signal should be recieved and sent to every other processes. What happens next is specified in singnal_handling. Every process should show their message (Quitting/Stopping/etc).
Actual behaviour:
[P3] Ready.
[P2] Ready.
[P1] Ready.
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
dd
[P1] Sending: dd
[P2] Received "dd" with length 2.
[P3] Received: 3 characters. //first message
as
[P1] Sending: as
[P2] Received "as" with length 2.
[P3] Received: 2 characters. //second
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 2 characters. // third
And with signals, well... I figured out that after SIGINT I could send message, hit enter twice and then I got expected behaviour (quitting program). Pressing enter also works when I'm sending another signals:
$ kill -s SIGUSR1 2900
$ kill -s SIGUSR2 2900
Gives:
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
[P3] Received: 3 characters.
Signal received...
Sending to other processes
>> SIGNAL <<
Stopping...
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
Signal received...
Sending to other processes
>> SIGNAL <<
>> SIGNAL <<
>> SIGNAL <<
Starting...
[P1] Sending:
Starting...
asd
[P1] Sending: asd
[P2] Received "asd" with length 3.
Starting...
[P3] Received: 3 characters.
So again - after sending a signal I have to send a message for it to be handled. So, it's kinda working. But very, very bad.