Two child processes to trigger using epoll and signals - c

process 1 is by timerfd, process 2 is triggered by signal from process1
tc2_main is equal to main
I tried having child1 triggered by timerfd and epoll, child 2 triggered by child 1's epoll and signal, so that child 2 gets triggered every 2 seconds.
but child 2 is never triggered
process timer is wait timer and send signal to child 2
void process_timer(int efd, int timerfd, int pid) {
uint64_t exp;
int ret, nfds;
struct epoll_event event;
for(int i = 0; i < 2; i++){
nfds = epoll_wait(efd, &event, 1, -1);
if (nfds == -1)
handle_error("epoll_wait");
ret = read(timerfd, &exp, sizeof(uint64_t));
if (ret != sizeof(uint64_t))
handle_error("read");
kill(pid, SIGUSR1);
printf("Process %d: Timer expired %lu times\n", getpid(), exp);
fflush(stdout);
}
}
this code is set epoll, timer and signal
and also set child 2
child 2 is wait SIGUSR1 signal
void tc2_main() {
int efd, timerfd;
struct epoll_event event;
struct itimerspec new_value;
cnt = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1 ,0);
struct sched_param param;
param.sched_priority = 99;
if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
perror("sched_setscheduler");
}
if (efd == -1)
handle_error("epoll_create1");
nice(10);
timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (timerfd == -1)
handle_error("timerfd_create");
new_value.it_value.tv_sec = 2;
new_value.it_value.tv_nsec = 0;
new_value.it_interval.tv_sec = 2;
new_value.it_interval.tv_nsec = 0;
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask (SIG_BLOCK, &sigset, NULL);
int sfd = signalfd(-1, &sigset, 0);
printf("sigfd : %d\n", sfd);
int ep_sig_fd = epoll_create1(0);
struct epoll_event sig_ev,ev;
sig_ev.data.fd = sfd;
sig_ev.events = EPOLLINT;
if(epoll_ctl(ep_sig_fd, EPOLL_CTL_ADD, sfd, &sig_ev) == -1){
handle_error("epoll_ctl");
}
printf("Child process waiting for signal...\n");
if (timerfd_settime(timerfd, 0, &new_value, NULL) == -1)
handle_error("timerfd_settime");
efd = epoll_create1(0);
if (efd == -1)
handle_error("epoll_create1");
event.data.fd = timerfd;
event.events = EPOLLIN EPOLLET;
if (epoll_ctl(efd, EPOLL_CTL_ADD, timerfd, &event) == -1)
handle_error("epoll_ctl");
pid_t pid = fork();
if (pid == -1)
handle_error("fork");
else if (pid == 0) {
process_timer(efd, timerfd, getppid());
(*cnt)++;
exit(EXIT_SUCCESS);
} else {
pid_t pid2 = fork();
fflush(stdout);
if(pid2 == -1) {
handle_error("fork");
} else if(pid2 == 0) {
struct signalfd_siginfo siginfo;
printf("Child 2: waiting for signal...\n");
int s = epoll_wait(ep_sig_fd, &sig_ev, 1, -1);
printf("aaa\n");
fflush(stdout);
if(s < 0) handle_error("epoll wait");
if (read(sfd, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) {
perror("read");
exit(EXIT_FAILURE);
}
} else {
wait(NULL);
}
}
exit(EXIT_SUCCESS);
}

Related

Child process not being generated and program seems to get caught in infinite loop

I am experimenting with signals forking but I am not sure what is going on with my code.
I should get 3 total processes. 1 parent 1 child and 1 grandchild. I decrement the level variable by 1 each time I make a fork so the program should terminate.
I don't understand why the program isn't generating any child processes. When I do ps aux| grep selfCaller I only ever see 1 process being run at a time.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
const int TEXT_LEN = 16;
const int NUM_SECS_TO_RUN = 30;
#define PROGNAME "selfCaller"
int numTimesCalled[3]
= {0,0,0};
pid_t pidToSignal = -1;
pid_t childPid = -1;
int level = +2;
int shouldRun = 1;
void sigAlarmHandler(int sig){
printf("Process %d: called level 0\n",level);
int w = rand() % 10 + 1;
alarm(w);
numTimesCalled[0]++;
if(level != 2){
pid_t pid = getppid();
kill(SIGUSR1, pid);
}
}
void sigUs1Handler(int sig){
printf("Process %d: called level 1\n",level);
numTimesCalled[1]++;
if(level != 2){
pid_t pid = getppid();
kill(SIGUSR2, pid);
}
}
void sigUs2Handler(int sig){
printf("Process %d: called level 2\n",level);
numTimesCalled[2]++;
}
void sigIntHandler(int sig){
shouldRun = 0;
}
int main (int argc,
char* argv[]
)
{
int comm;
if (argc > 1){
comm = strtol(argv[1], NULL, 0);
}
if (comm == 0 || comm == 1){
level = comm;
}
srand(getpid());
struct sigaction act;
memset(&act, '\0', sizeof(act));
act.sa_handler = sigAlarmHandler;
sigaction(SIGALRM, &act, NULL);
act.sa_handler = sigAlarmHandler;
sigaction(SIGUSR1, &act, NULL);
act.sa_handler = sigUs1Handler;
sigaction(SIGUSR2, &act, NULL);
act.sa_handler = sigIntHandler;
sigaction(SIGINT, &act, NULL);
// alarm(0);
pid_t pi;
if(level > 0){
pi = fork();
}
printf("pid is %d", pi);
if (pi ==-1){
exit(EXIT_FAILURE);
}
char text[TEXT_LEN];
if(pi == 0){
printf("This is the child");
int r;
snprintf(text,TEXT_LEN,"%d",level-1);
r =execl(PROGNAME, text, NULL);
if (r==-1){
fprintf(stderr,"Cannot find %s\n",PROGNAME);
exit(EXIT_FAILURE);
}
}
if (level == 2)
{
int i;
for (i = 0; i < NUM_SECS_TO_RUN; i++)
{
sleep(1);
}
}
else
{
pidToSignal = getppid();
while (shouldRun)
{
sleep(1);
}
}
printf("Level %d: %d %d %d\n",level,
numTimesCalled[0],numTimesCalled[1],numTimesCalled[2]
);
return(EXIT_SUCCESS);
}

How to terminate waitpid on user input?

I am working on a ncurses based file manager in C. The problem is that some child processes can take some time to complete and till that happens it remains stuck due to waitpid.
I can't use the WNOHANG flag because the next block of code is dependent on the output of the child process.
void getArchivePreview(char *filepath, int maxy, int maxx)
{
pid_t pid;
int fd;
int null_fd;
// Reallocate `temp_dir` and store path to preview file
char *preview_path = NULL;
allocSize = snprintf(NULL, 0, "%s/preview", cache_path);
preview_path = malloc(allocSize+1);
if(preview_path == NULL)
{
endwin();
printf("%s\n", "Couldn't allocate memory!");
exit(1);
}
snprintf(preview_path, allocSize+1, "%s/preview", cache_path);
// Create a child process to run "atool -lq filepath > ~/.cache/cfiles/preview"
pid = fork();
if( pid == 0 )
{
remove(preview_path);
fd = open(preview_path, O_CREAT | O_WRONLY, 0755);
null_fd = open("/dev/null", O_WRONLY);
// Redirect stdout
dup2(fd, 1);
// Redirect errors to /dev/null
dup2(null_fd, 2);
execlp("atool", "atool", "-lq", filepath, (char *)0);
exit(1);
}
else
{
int status;
waitpid(pid, &status, 0);
getTextPreview(preview_path, maxy, maxx);
free(preview_path);
}
}
In this case, I would like to carry forward with the rest of the program if the user decides to go to some other file. In what way can I change the architecture of the program?
If I have understood the question correctly then you want to unblock parent on either completion of child or any user input.
As suggested in this comment, you could handle SIGCHLD and one more signal say SIGUSR1. SIGUSR1 will be raised when you get user input. Following is the example where both SIGCHLD and 'SIGUSR1' is handled. If use inputs any number then it raises SIGUSR1 to parent and parent kill child. Else child will raise SIGCHLD on exit.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int raised_signal = -1;
static void cb_sig(int signal)
{
if (signal == SIGUSR1)
raised_signal = SIGUSR1;
else if (signal == SIGCHLD)
raised_signal = SIGCHLD;
}
int main()
{
int pid;
int i, a;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = cb_sig;
if (sigaction(SIGUSR1, &act, NULL) == -1)
printf("unable to handle siguser1\n");
if (sigaction(SIGCHLD, &act, NULL) == -1)
printf("unable to handle sigchild\n");
pid = fork();
if (pid == 0) {
/* child */
for (i = 0; i < 10; i++) {
sleep(1);
printf("child is working\n");
}
exit(1);
} else {
/* parent */
if (-1 == scanf("%d", &a)) {
if (errno == EINTR)
printf("scanf interrupted by signal\n");
} else {
raise(SIGUSR1);
}
if (raised_signal == SIGUSR1) {
printf("user terminated\n");
kill(pid, SIGINT);
} else if (raised_signal == SIGCHLD) {
printf("child done working\n");
}
exit(1);
}
return 0;
}

Creating daemon process in UNIX enviroment

I have picked the following example from APUE :
void daemonize(const char* cmd)
{
int i,fd0,fd1,fd2;
pid_t pid;
struct rlimit r1;
struct sigaction sa;
//clear all file masks
umask(0);
//get max number of file descriptors
if(getrlimit(RLIMIT_NOFILE,&r1) < 0)
{
perror("error getting file descriptor size");
return;
}
//become a session leader
if((pid = fork()) < 0)
{
perror("error forking");
return;
}
else if(pid == 0)
{
setsid();
}
else
{
exit(0); //parent exits
}
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP,&sa,NULL) < 0)
{
return;
}
if((pid = fork()) < 0)
{
return;
}
else if(pid != 0)
{
exit(0); //parent
}
//child continues
syslog(LOG_ERR,"chile continuing with pid : %d",getpid());
//change the working directory
if(chdir("/") < 0)
{
return;
}
if(r1.rlim_max == RLIM_INFINITY)
r1.rlim_max = 1024;
for(i=0;i<r1.rlim_max;i++)
close(i);
//attach the file descriptors to /dev/null
fd0 = open("/dev/null",O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
//initialize the log file
openlog(cmd, LOG_CONS,LOG_DAEMON);
if(fd0!=0 || fd1!=1 || fd2!=2)
{
syslog(LOG_ERR,"unexpected file descriptors %d %d %d\n",fd0,fd1,fd2);
exit(1);
}
}
int main()
{
daemonize("date");
pause(); //how is this working???
}
What I don't understand is how the pause() from the main function is working? What I was expecting is that since we have done exit(0) for the parent process in daemonize(), it should have exited and resulted in the normal termination of the main() process. It should have never returned to the main() and the call to pause() should not even happen. Why it did not terminate and why the pause() got called?
The code forks twice, producing a parent, a child, and a grandchild. The first two each exit(0); the last returns from daemonize.

Communication with child process using pipes and epoll

I'm writing an application which will start some processes (fork and exec) depending on users input and should inform user about every error in those processes (Print some internal ID + message written to stderr by the process). I'd also like to detect exiting processes. I'm facing the problem, that I cannot receive data after executing execl() command. Test data is received in epoll_wait loop, but process called by exec seems to not write anything to stderr nor even end (I don't know if I'm correct, but I expect pipe to be readable after it's writing end has been closed, when process exited). Some code reproducing my problem:
#include <unistd.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <thread>
#include <stdlib.h>
#include <sys/wait.h>
thread_local int epoll;
int do_fork()
{
int pip[2];
pipe(pip);
int pid = fork();
if (pid == 0)
{
dup2(pip[1], STDERR_FILENO);
close(pip[0]);
close(pip[1]);
write(STDERR_FILENO, "test data", 10);
sleep(3);
//exit(1);
execl("tr", "tr", NULL); //this process will end immediately and write error on stderr
}
else
{
printf("PID %d\n", pid);
close(pip[1]);
epoll_event ev;
ev.data.fd = pip[0];
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
printf("pipe %d\n", pip[0]);
epoll_ctl(epoll, EPOLL_CTL_ADD, pip[0], &ev);
}
}
int thread()
{
epoll_event events[10];
epoll = epoll_create1(0);
epoll_event ev;
ev.data.fd = STDIN_FILENO;
ev.events = EPOLLIN;
epoll_ctl(epoll, EPOLL_CTL_ADD, STDIN_FILENO, &ev);
char buf[1000];
while(true)
{
int r = epoll_wait(epoll, events, 10, -1);
printf("r = %d\n", r);
for (int i = 0; i < r; ++i)
{
if(events[i].data.fd == STDIN_FILENO)
{
int t = read(events[i].data.fd, buf, 1000);
do_fork();
}
else
{
printf("event? %d\n", events[i].events);
printf("pipe %d\n", events[i].data.fd);
int t = read(events[i].data.fd, buf, 1000);
printf("t == %d\n", t);
if(t == -1)
printf("errno: %d\n", errno);
if(events[i].events & EPOLLHUP || t == 0)
{
epoll_ctl(epoll, EPOLL_CTL_DEL, events[i].data.fd, NULL);
if(close(events[i].data.fd) == -1)
printf("cannot close fd\n");
}
}
}
}
return 0;
}
int main()
{
std::thread t{thread};
t.detach();
while(true);
}

Processes synchronization with message queues and signals

I have to create three processes:
Reading expression like 1+3+5+12
Checking if expression has correct syntax
Adding numbers and displaying them
Data between processes is shared using pipes mechanism. Processes synchronization uses message queues and signals.I also should be able to manually send signals to each process through console.
The problem I am running into this is that all processes seem to run randomly. Why is that, what's wrong here? It should work...
This is the whole code that compiles correctly:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define SIZEBUFF 256
// Signal handling for each process
void h_sig1(int signo);
void h_sig2(int signo);
void h_sig3(int signo);
void h_S4(int signo);
// Processes functions
void process1(void);
void process2(void);
void process3(void);
// helper functions
bool isInteger(double val);
static pid_t P1, P2, P3; // PIDs for each process
int P; // parent PID
int pfd12[2], pfd23[2]; // Pipes between processes 1-2 and 2-3
int providePIDY1[2], providePIDY2[2]; // provide all PIDs to 1/2 process
// message in message queue
typedef struct
{
long type;
int sigNum;
} message;
int queue_ID;
int main(void)
{
P = getpid();
// Message queue created
if ((queue_ID = msgget(IPC_PRIVATE,IPC_CREAT|0666)) < 0)
{
printf("msgget\n");
return 1;
}
if((P1 = fork()) == 0)
{
P1 = getpid();
process1();
}
else if((P2 = fork()) == 0)
{
P2 = getpid();
process2();
}
else if((P3 = fork()) == 0)
{
P3 = getpid();
process3();
}
else
{ // Sending signals from parent through operator
// Sending PIDs to process 1
close(providePIDY1[0]);
write(providePIDY1[1], &P, sizeof(int));
write(providePIDY1[1], &P1, sizeof(int));
write(providePIDY1[1], &P2, sizeof(int));
write(providePIDY1[1], &P3, sizeof(int));
close(providePIDY1[1]);
// Sending PIDs to process 2
close(providePIDY2[0]);
write(providePIDY2[1], &P, sizeof(int));
write(providePIDY2[1], &P1, sizeof(int));
write(providePIDY2[1], &P2, sizeof(int));
write(providePIDY2[1], &P3, sizeof(int));
close(providePIDY2[1]);
printf("\nProgram options:\n");
printf("Send signal - 's'(send)\n");
printf("Display processes PIDs 'p'(pids)\n");
printf("Quit program - 'q'(quit)\n");
char choice, choice2, choice3;
while(1)
{
choice = getchar();
if(choice == 's')
{
printf("Which process is receiving the signal - 1, 2, or 3?: ");
choice2 = getchar();
choice2 = getchar();
printf("\n");
if((choice2 < 1) && (choice2 > 3))
{
printf("No such process!");
continue;
}
printf("What signal to send?:\n");
printf("1-S1(end execution)\n2-S2(pause execution)\n3-S3(renew execution)?\n ");
printf("Choice: ");
choice3 = getchar();
choice3 = getchar();
switch(choice2)
{
case '1': //nie można przechwycić sygnałów SIGKILL oraz SIGSTOP (zabicia oraz zatrzymania)
if(choice3 == '1') { kill(0,SIGCONT); kill(P1,SIGUSR1); }
if(choice3 == '2') kill(P1,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '2':
if(choice3 == '1') { kill(0,SIGCONT); kill(P2,SIGUSR1); }
if(choice3 == '2') kill(P2,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '3':
if(choice3 == '1') { kill(0,SIGCONT); kill(P3,SIGUSR1); }
if(choice3 == '2') kill(P3,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
default: printf("No such operation!!! \n\n");
}
}
if(choice == 'p')
{
// do something
}
if(choice == 'q')
{
// do something
}
}
}
}
void process1(void)
{
// Receiving PIDs
close(providePIDY1[1]);
read(providePIDY1[0], &P, sizeof(int));
read(providePIDY1[0], &P1, sizeof(int));
read(providePIDY1[0], &P2, sizeof(int));
read(providePIDY1[0], &P3, sizeof(int));
close(providePIDY1[0]);
struct sigaction act1;
act1.sa_handler = h_sig1;
sigemptyset(&act1.sa_mask);
act1.sa_flags = 0;
sigaction(SIGUSR1, &act1, 0);
sigaction(SIGTSTP, &act1, 0);
sigaction(SIGALRM, &act1, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// do something
}
void process2(void)
{
close(providePIDY2[1]);
read(providePIDY2[0], &P, sizeof(int));
read(providePIDY2[0], &P1, sizeof(int));
read(providePIDY2[0], &P2, sizeof(int));
read(providePIDY2[0], &P3, sizeof(int));
close(providePIDY2[0]);
struct sigaction act2;
act2.sa_handler = h_sig2;
sigemptyset(&act2.sa_mask);
act2.sa_flags = 0;
sigaction(SIGUSR1, &act2, 0);
sigaction(SIGTSTP, &act2, 0);
sigaction(SIGALRM, &act2, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// do something
}
void process3(void)
{
struct sigaction act3;
act3.sa_handler = h_sig3;
sigemptyset(&act3.sa_mask);
act3.sa_flags = 0;
sigaction(SIGUSR1, &act3, 0);
sigaction(SIGTSTP, &act3, 0);
sigaction(SIGALRM, &act3, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// do something
}
void h_sig1(int signo)
{
message msg;
msg.type = P2;
msg.sigNum = signo;
kill(P2, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
}
if(signo == SIGTSTP)
{
}
if(signo == SIGALRM)
{
}
}
void h_sig2(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
}
if(signo == SIGTSTP)
{
}
if(signo == SIGALRM)
{
}
}
void h_sig3(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P2;
kill(P2, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
}
if(signo == SIGTSTP)
{
}
if(signo == SIGALRM)
{
}
}
void h_S4(int signo)
{
int res;
message msg;
printf("\nProcess with PID=%d received signal S4", getpid());
if(signo == SIGINT)
{
res = msgrcv(queue_ID, &msg, sizeof(msg.sigNum), msg.type, 0);
if(res >= 0)
{
if(msg.sigNum == SIGUSR1)
{
}
if(msg.sigNum == SIGTSTP)
{
}
if(msg.sigNum == SIGALRM)
{
}
}
}
}
Long version, to compile:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define SIZEBUFF 200
// Signal handling for each process
void h_sig1(int signo);
void h_sig2(int signo);
void h_sig3(int signo);
void h_S4(int signo); // signal handling for the 4th signal
// Processes functions
void process1(void);
void process2(void);
void process3(void);
// helper functions
bool isInteger(double val);
static pid_t P1, P2, P3; // PIDs for each process
int P; // parent PID
int pfd12[2], pfd23[2]; // Pipes between processes 1-2 and 2-3
int providePIDY1[2], providePIDY2[2]; // provide all PIDs to 1/2 process
// message in message queue
typedef struct
{
long type;
int sigNum;
} message;
int queue_ID;
int main(void)
{
P = getpid();
if (pipe(pfd12) == -1)
{
perror("pipe failed");
exit(1);
}
if (pipe(pfd23) == -1)
{
perror("pipe failed");
exit(1);
}
// Message queue created
if ((queue_ID = msgget(IPC_PRIVATE,IPC_CREAT|0666)) < 0)
{
printf("msgget\n");
return 1;
}
if (pipe(providePIDY1) == -1)
{
perror("pipe failed");
exit(1);
}
if (pipe(providePIDY2) == -1)
{
perror("pipe failed");
exit(1);
}
if((P1 = fork()) == 0)
{
P1 = getpid();
process1();
}
else if(P1 < 0)
{
perror("fork failed");
exit(2);
}
else if((P2 = fork()) == 0)
{
P2 = getpid();
process2();
}
else if(P2 < 0)
{
perror("fork failed");
exit(2);
}
else if((P3 = fork()) == 0)
{
P3 = getpid();
process3();
}
else if(P3 < 0)
{
perror("fork failed");
exit(2);
}
else
{ // Sending signals from parent through operator
// Sending PIDs to process 1
close(providePIDY1[0]);
write(providePIDY1[1], &P, sizeof(int));
write(providePIDY1[1], &P1, sizeof(int));
write(providePIDY1[1], &P2, sizeof(int));
write(providePIDY1[1], &P3, sizeof(int));
close(providePIDY1[1]);
// Sending PIDs to process 2
close(providePIDY2[0]);
write(providePIDY2[1], &P, sizeof(int));
write(providePIDY2[1], &P1, sizeof(int));
write(providePIDY2[1], &P2, sizeof(int));
write(providePIDY2[1], &P3, sizeof(int));
close(providePIDY2[1]);
printf("\nProgram options:\n");
printf("Send signal - 's'(send)\n");
printf("Display processes PIDs 'p'(pids)\n");
printf("Quit program - 'q'(quit)\n");
char choice, choice2, choice3;
while(1)
{
choice = getchar();
if(choice == 's')
{
printf("Which process is receiving the signal - 1, 2, or 3?: ");
choice2 = getchar();
choice2 = getchar();
printf("\n");
if((choice2 < 1) && (choice2 > 3))
{
printf("No such process!");
continue;
}
printf("What signal to send?:\n");
printf("1-S1(end execution)\n2-S2(pause execution)\n3-S3(renew execution)?\n ");
printf("Choice: ");
choice3 = getchar();
choice3 = getchar();
switch(choice2)
{
case '1': //nie można przechwycić sygnałów SIGKILL oraz SIGSTOP (zabicia oraz zatrzymania)
if(choice3 == '1') { kill(0,SIGCONT); kill(P1,SIGUSR1); }
if(choice3 == '2') kill(P1,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '2':
if(choice3 == '1') { kill(0,SIGCONT); kill(P2,SIGUSR1); }
if(choice3 == '2') kill(P2,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
case '3':
if(choice3 == '1') { kill(0,SIGCONT); kill(P3,SIGUSR1); }
if(choice3 == '2') kill(P3,SIGTSTP);
if(choice3 == '3') { kill(0,SIGCONT); kill(P3,SIGALRM); }
break;
default: printf("No such operation!!! \n\n");
}
}
if(choice == 'p')
{
printf("\n<Processes PIDs:>\n");
printf("P(initial process)=%d\n",P);
printf("P1(process 1)=%d\n",P1);
printf("P2(process 2)=%d\n",P2);
printf("P3(process 3)=%d\n\n",P3);
}
if(choice == 'q')
{
printf("\n<Quitting program>\n");
msgctl(queue_ID, IPC_RMID, 0);
kill(0, SIGKILL);
}
}
}
}
void process1(void)
{
int dataSize;
char buff[SIZEBUFF];
// Receiving PIDs
close(providePIDY1[1]);
read(providePIDY1[0], &P, sizeof(int));
read(providePIDY1[0], &P1, sizeof(int));
read(providePIDY1[0], &P2, sizeof(int));
read(providePIDY1[0], &P3, sizeof(int));
close(providePIDY1[0]);
printf("\n<Process 1 execution in progress>\n");
/*SIGACTION*/
struct sigaction act1;
act1.sa_handler = h_sig1;
sigemptyset(&act1.sa_mask);
act1.sa_flags = 0;
sigaction(SIGUSR1, &act1, 0);
sigaction(SIGTSTP, &act1, 0);
sigaction(SIGALRM, &act1, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
close(pfd12[0]);
while(1)
{
printf("Provide expr: ");
scanf("%s", buff);
if(strcmp(buff, "0") == 0)
break;
dataSize = strlen(buff) + 1; // plus NULL
if(dataSize > 0)
write(pfd12[1], &dataSize, sizeof(int));
write(pfd12[1], &buff, sizeof(char)*dataSize);
}
dataSize = 0; // info that there's no more data
write(pfd12[1], &dataSize, sizeof(int));
close(pfd12[1]);
printf("\n---Process 1 finished execution---\n");
exit(0);
}
void process2(void)
{
int dataSize;
char buff[SIZEBUFF];
char *token, *end;
int number;
const char delim[2] = "+";
//Odebranie pidow
close(providePIDY2[1]);
read(providePIDY2[0], &P, sizeof(int));
read(providePIDY2[0], &P1, sizeof(int));
read(providePIDY2[0], &P2, sizeof(int));
read(providePIDY2[0], &P3, sizeof(int));
close(providePIDY2[0]);
printf("\n<Process 2 execution in progress>\n");
/*SIGACTION*/
struct sigaction act2;
act2.sa_handler = h_sig2;
sigemptyset(&act2.sa_mask);
act2.sa_flags = 0;
sigaction(SIGUSR1, &act2, 0);
sigaction(SIGTSTP, &act2, 0);
sigaction(SIGALRM, &act2, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
close(pfd12[1]);
read(pfd12[0], &dataSize, sizeof(int));
if(dataSize > 0)
{
sleep(3);
read(pfd12[0], buff, dataSize);
token = strtok(buff, delim);
while( token != NULL )
{
number = strtol(token, &end, 0);
if(!isInteger(number))
break;
}
}
close(pfd12[0]);
// Sending result to process 3
close(pfd23[0]);
write(pfd23[1], &buff, sizeof(int));
close(pfd23[1]);
printf("\n---Process 2 finished execution---\n");
}
void process3(void)
{
int sum = 0;
char buff[SIZEBUFF];
char* token, *end;
int number;
const char delim[2] = "+";
/*SIGACTION*/
struct sigaction act3;
act3.sa_handler = h_sig3;
sigemptyset(&act3.sa_mask);
act3.sa_flags = 0;
sigaction(SIGUSR1, &act3, 0);
sigaction(SIGTSTP, &act3, 0);
sigaction(SIGALRM, &act3, 0);
struct sigaction act;
act.sa_handler = h_S4;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
printf("\n<Process 3 execution in progress>");
close(pfd23[1]);
read(pfd23[0], &buff, sizeof(int));
token = strtok(buff, delim);
while( token != NULL )
{
number = strtol(token, &end, 0);
sum += number;
}
printf("Sum = %d\n", sum);
close(pfd23[0]);
printf("\n---Process 3 finished execution ---\n");
printf("\n---Program finished execution---\n");
kill(getppid(),SIGKILL);
}
/*******************************************************************************************************/
/****************************************SIGNAL HANDLING (S1-S3)***********************************************/
/*******************************************************************************************************/
void h_sig1(int signo)
{
message msg;
msg.type = P2;
msg.sigNum = signo;
kill(P2, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
printf("\nProcess 1 received signal S1\n");
printf("Terminating parent process!\n");
kill(getppid(), SIGKILL);
printf("Terminating process 1!\n");
kill(getpid(), SIGKILL);
}
if(signo == SIGTSTP)
{
printf("\nProcess 1 received signal S2\n");
printf("Pausing process 1!\n");
kill(getpid(), SIGSTOP);
}
if(signo == SIGALRM)
{
printf("\nProcess 1 received signal S3\n");
printf("Renewing execution of process 1!\n");
kill(getpid(), SIGCONT);
}
}
void h_sig2(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P3;
kill(P3, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
printf("\nProcess 2 received signal S1\n");
printf("Terminating parent process!\n");
kill(getppid(), SIGKILL);
printf("Terminating process 2!\n");
kill(getpid(), SIGKILL);
}
if(signo == SIGTSTP)
{
printf("\nProcess 2 received signal S2\n");
printf("Pausing process 2!\n");
kill(getpid(), SIGSTOP);
}
if(signo == SIGALRM)
{
printf("\nProcess 2 received signal S3\n");
printf("Renewing execution of process 2!\n");
kill(getpid(), SIGCONT);
}
}
void h_sig3(int signo)
{
message msg;
msg.type = P1;
msg.sigNum = signo;
kill(P1, SIGINT);
// send type of receiving signal to message queue
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
msg.type = P2;
kill(P2, SIGINT);
msgsnd(queue_ID, &msg, sizeof(msg.sigNum), 0);
if(signo == SIGUSR1)
{
printf("\nProcess 3 received signal S1\n");
printf("Terminating parent process!\n");
kill(getppid(), SIGKILL);
printf("Terminating process 3!\n");
kill(getpid(), SIGKILL);
}
if(signo == SIGTSTP)
{
printf("\nProcess 3 received signal S2\n");
printf("Pausing process 3!\n");
kill(getpid(), SIGSTOP);
}
if(signo == SIGALRM)
{
printf("\nProcess 3 received signal S3\n");
printf("Renewing execution of process 3!\n");
kill(getpid(), SIGCONT);
}
}
/*******************************************************************************************************/
/****************************************Handling S4 signal***********************************/
/*******************************************************************************************************/
void h_S4(int signo)
{
int res;
message msg;
printf("\nProcess with PID=%d received signal S4", getpid());
if(signo == SIGINT)
{
res = msgrcv(queue_ID, &msg, sizeof(msg.sigNum), msg.type, 0);
if(res >= 0)
{
if(msg.sigNum == SIGUSR1)
{
printf("Terminating process\n");
kill(getpid(),SIGKILL);
}
if(msg.sigNum == SIGTSTP)
{
printf("Pausing process\n");
kill(getpid(),SIGSTOP);
}
if(msg.sigNum == SIGALRM)
{
printf("Renewing process\n");
kill(getpid(),SIGCONT);
}
}
}
}
bool isInteger(double val)
{
int truncated = (int)val;
return (val == truncated);
}
Source code analysis
There are consistency problems:
static pid_t P1, P2, P3; // PIDs for each process
int P; // parent PID
Why is P and int instead of a pid_t? Why is it global instead of static? Why is it P instead of P0? Should the whole lot be an array?
static pid_t P[4];
(There would be benefits to using an array!)
There is repetition that should be in a function invoked multiple times:
// Sending PIDs to process 1
close(providePIDY1[0]);
write(providePIDY1[1], &P, sizeof(int));
write(providePIDY1[1], &P1, sizeof(int));
write(providePIDY1[1], &P2, sizeof(int));
write(providePIDY1[1], &P3, sizeof(int));
close(providePIDY1[1]);
// Sending PIDs to process 2
close(providePIDY2[0]);
write(providePIDY2[1], &P, sizeof(int));
write(providePIDY2[1], &P1, sizeof(int));
write(providePIDY2[1], &P2, sizeof(int));
write(providePIDY2[1], &P3, sizeof(int));
close(providePIDY2[1]);
Note that there's also repetition because the P values aren't an array. There's also a possible portability liability; pid_t does not have to be the same size as int.
There are problems with checking inputs:
choice = getchar();
if(choice == 's')
Since choice is a char, you can get erroneous handling of EOF — if you bothered to test for it. You also leave a newline (at least) in the input, and don't skip leading spaces in the input. You'd likely do better with reading a line of data (fgets() or POSIX
readline()) and then using if (sscanf(buffer, " %c", &choice) != 1) { …handle error… } to get the character.
Your next input block is curious:
printf("Which process is receiving the signal - 1, 2, or 3?: ");
choice2 = getchar();
choice2 = getchar();
printf("\n");
if((choice2 < 1) && (choice2 > 3))
The first input reads the newline (assuming there were no trailing spaces, etc), and the second gets a '1', '2', or '3'. However, you test whether the input value is both less than 1 and greater than 3, and there's no known value in the universe for which both conditions are true (NaN values are unknown values). You really wanted something like:
if (choice2 < '1' || choice2 > '3')
After you've determined which signal to send (more or less), you have another block of repeated code because you used P1 etc instead of an array P.
There are chunks of repeated code in your child processes, such as the code that reads the process numbers. These should be in a function, too. The signal handling setup should probably be in a function too, though I've not spent a lot of time checking for the differences and similarities between the different process functions.
Major problem
You say the code is supposed to be processing arithmetic expressions. You have the main program loop reading choices about signal handling, but you seem to have process1() also trying to read expressions. This is bad news; it is indeterminate which of the processes will get to read any given input.
Back to the small stuff
You have:
dataSize = strlen(buff) + 1; // plus NULL
if(dataSize > 0)
write(pfd12[1], &dataSize, sizeof(int));
write(pfd12[1], &buff, sizeof(char)*dataSize);
The test is a little pointless; the minimum value that strlen() can return is 0, so the minimum value in dataSize is 1, so the condition will always be true. (Theoretically, I suppose, you could enter so much data that the size_t returned by strlen() overflows the int dataSize, but you've not allocated enough space for that to be an actual problem — your code will have had other problems before that.)
In process2(), this code is curious:
token = strtok(buff, delim);
while( token != NULL )
{
number = strtol(token, &end, 0);
if(!isInteger(number))
break;
}
There are no circumstances under which the int number; is going to be a non-integer when you scan the string with strtol(). You have a risk of overflow (sizeof(int) != sizeof(long) on 64-bit Unix systems, for example). You have a risk of not being able to interpret the remnants of floating point value (because the . is not a valid part of an integer). You'll need to rework that code.
There's a lot of repetition in the signal handling code; it should be refactored so that you need fewer functions. It'll be easier to understand in the long run. Copy'n'paste'n'edit is a very bad way of programming when the result is near clones of the code in a single program.
I'm not clear what the differences are between the two versions you show; I've not scrutinized them. You should look at how to create an MCVE (How to create a Minimal, Complete, and Verifiable Example?) or SSCCE (Short, Self-Contained, Correct Example) — two names and links for the same basic idea. I'm not sure that either lot of code qualifies as an MCVE; both versions is overkill. Just supply the compilable code.
After compilation
I've compiled the second chunk of code (saved in a file called procsync.c) on my Mac running Mac OS X 10.10.3 with GCC 5.1.0, and using the command line:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror procsync.c -o procsync
$
To my considerable surprise, the code compiled under those very stringent options with no complaints — that is something I very seldom see in code on SO.
Congratulations!
(But there are still the other issues to worry about.)

Resources