0

My task is to have multiple childs and a parents which are communicating between each other. I read the tasks from a file, and create childs depends on the number of the tasks. In the parent I send tasks to different children. So a child can "work on" a task. Every child has its pipe. After a child got the data and do some work it has to send a signal to his parent, and a bit later send a message through his pipe to say that "I'm finished today". I'm not sure how to handle receiving multiple signals. How should I achieve this?

Code:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>

#define MAX_PROCESS 10
const char *defaultPipe = "/tmp/child";
static int orderNum = 0;
static int workerNum = 0;
static pid_t shutDown[MAX_PROCESS];
static char pipes[MAX_PROCESS][100];

typedef struct Order
{
    int id;
    char created[256];
    char fullName[256];
    char email[256];
    char phone[256];
    char status;
    int performance;
    int days;
    struct Order *next;
} Order;

typedef struct Order *node;

void startJob();
void sendPriorityJobs(node priorityHead);
void handler(int signo, siginfo_t *info, void *context);
void createWorker(node orderA, int workerID);

node createNode();
node createOrder(char *fullName, char *email, char *phone, char *created, int performance);

int main()
{
    //char msgFromWorker[256];

    struct sigaction sa;
    sa.sa_handler = (void *)handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR2, &sa, NULL);

    node test1 = createOrder("Jane Doe", "test1@gmail.com", "12345678", "2018-12-25 8:00", 1000);
    node test2 = createOrder("John Doe", "test2@gmail.com", "87654321", "2018-12-25 9:00", 1001);
    test1->next = test2;

    printf("Parent pid: %d\n", getpid());

    /*This is where we send the task */
    sendPriorityJobs(test1);

    int i;
    for (i = 0; i < workerNum; i++)
    {
        /*int p = open(pipes[i], O_RDONLY);
        read(p, msgFromWorker, sizeof(msgFromWorker));
        sleep(1);
        printf("%s\n", msgFromWorker);
        close(p);*/
        waitpid(shutDown[i], NULL, 0);
    }

    return 0;
}

void createWorker(node orderA, int workerID)
{
    int parent; // child;
    pid_t worker;
    char strID[12];
    sprintf(strID, "%d", workerID);
    char pipe[100];
    strcpy(pipe, defaultPipe);
    strcat(pipe, strID);
    mkfifo(pipe, S_IRUSR | S_IWUSR);

    worker = fork();

    if (worker == 0)
    {

        //this is a temporarily variable for the received structure.(order)
        node order_A = createNode();

        parent = open(pipe, O_RDONLY);
        int ret;
        if ((ret = read(parent, &order_A, sizeof(Order))) > 0)
        {
            printf("[Child %d]: started work on %d. order.\n", getpid(), order_A->id);
            //printf("ret: %d\n", ret);
            //printf("%d,%s,%s,%s,%s,%d\n", order_A->id,order_A->fullName,order_A->email,order_A->phone,order_A->created,order_A->performance);
        }

        startJob();

        char endMessage[256];
        sprintf(endMessage, "[Child %d]: ended his daily task.", getpid());

        /*Sending the done message via pipe. This is questionable part, 
        how to do this properly. */
        /*child = open(pipe, O_WRONLY);
        write(child, &endMessage, strlen(endMessage) + 1);*/

        free(order_A);
        exit(0);
    }
    else
    {
        //Save the child's pid
        shutDown[workerID] = worker;

        //Save the child's pipe name.
        strcpy(pipes[workerID], pipe);

        parent = open(pipe, O_WRONLY);
        int ret;
        if ((ret = write(parent, &orderA, sizeof(Order))) > 0)
        {
            printf("[Parent]: sending %d. order!\n", orderA->id);
            //printf("ret: %d\n", ret);
        }
        close(parent);
    }
}

void startJob()
{
    pid_t parentPID = getppid();
    sleep(2);
    printf("[Child %d]: is done, sending signal.\n", getpid());
    kill(parentPID, SIGUSR2);
}

void sendPriorityJobs(node priorityHead)
{
    node current = priorityHead;
    while (current != NULL)
    {
        createWorker(current, workerNum);
        workerNum++;
        current = current->next;
    }
}

node createNode()
{
    node tmp;
    tmp = (node)malloc(sizeof(struct Order));
    tmp->next = NULL;
    return tmp;
}

node createOrder(char *fullName, char *email, char *phone, char *created, int performance)
{
    node newOrder;
    newOrder = createNode();

    strcpy(newOrder->fullName, fullName);
    strcpy(newOrder->email, email);
    strcpy(newOrder->phone, phone);
    strcpy(newOrder->created, created);
    newOrder->performance = performance;
    newOrder->status = 'N';
    newOrder->id = orderNum + 1;
    orderNum++;

    return newOrder;
}

void handler(int signo, siginfo_t *info, void *context)
{
    char msg[256];
    time_t t;
    time(&t);
    sprintf(msg, "[Parent]: i got the signal(%d) from [Child %d] time: %s", signo, info->si_pid, ctime(&t));
    write(1, msg, strlen(msg) + 1);
}

I'm not sure how to send multiple signals and end of the day messages, and receive it in the parent.

Bence Szabari
  • 143
  • 1
  • 2
  • 12
  • Okay, thank you! :) – Bence Szabari Dec 29 '18 at 13:31
  • 1
    Hmmm — you're using FIFOs, not pipes. Pipes are anonymous, created by the `pipe()` system call; FIFOs are named, created by the `mkfifo()` system call. – Jonathan Leffler Dec 29 '18 at 20:41
  • The code is not very complete, for all it is better than originally. You've not shown a specification for the `node` type. I guess you've used a variant on `typedef struct node *node;` — at least, using that reduces the number of compilation errors dramatically. But the code is not an MCVE ([MCVE]). Note that 'complete' means "it compiles". This code does not, and I'm not willing to spend the time guessing what extra changes are needed to make it compile. I added 9 headers (I might not need both `` and ``, so that may be one too many), and some function declarations. – Jonathan Leffler Dec 29 '18 at 20:48
  • See [Is it a good idea to typedef pointers?](https://stackoverflow.com/questions/750178/) for a discussion on whether to define a pointer typedef. TL;DR — the answer is mostly 'No'. – Jonathan Leffler Dec 29 '18 at 20:49
  • @JonathanLeffler true, unfortunately I forgot them to write down to my question, but for now I think completed the code enough to understand my goal, and highlighted the questionable part of it. – Bence Szabari Dec 29 '18 at 21:18
  • Your structures are bigger than a `void *`, but you have `read(parent, &order_A, sizeof(void *));` — happiness does not ensue. Similarly, you have `write(parent, &orderA, sizeof(void *));` — same lack of happiness! You need to test the return value from `read()` assiduously; every time! You should test the return value from `write()` too, but you can more frequently get away without doing so. You cannot possibly write robust code without knowing how much data was read. You should print the data, too. I think the parent code should `close(parent);` after writing. – Jonathan Leffler Dec 29 '18 at 21:42
  • You seem to wait for all the children to die before reading anything from any of the FIFOs. That isn't very reliable — especially if the response could be so big it doesn't fit in the FIFO buffer (which might be as large as 64 KiB, but could be much smaller). I can't run the code — I had to comment out calls to quite a lot of (undefined) functions. An MCVE can hard-code data in a list. You don't need both the priority and normal lists; one is sufficient in an MCVE. I had to guess at `createNode()` as a wrapper around `calloc()`. I shouldn't have to guess. – Jonathan Leffler Dec 29 '18 at 21:46
  • You could lose the '1 or 2 items' code too; while you're getting the basics working, always send 1 item. Leave the complications of 1 or 2 for later. You should avoid using casts when assigning function pointers (`sa.sa_handler = (void *)handler;`). You invoke undefined behaviour when the function is called differently. The POSIX standard for [`sigaction()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html) says "_shall_ … `void func(int signo, siginfo_t *info, void *context);`". Don't cheat; computers get their own back when you lie to them. – Jonathan Leffler Dec 29 '18 at 21:51
  • I fully completed my code, removed the unnecessary parts, so now you can compile it and then run. I did most of your recommendation, put a test before read&write, send `sizeof(Order)` and so on. I've also modified my question. Thanks for helping! – Bence Szabari Dec 30 '18 at 13:09
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/185939/discussion-between-bence-szabari-and-jonathan-leffler). – Bence Szabari Dec 30 '18 at 15:37

2 Answers2

1

To some extent rehashing points made in the commentary to the question, not excluding the chat session, here is an answer in several parts.

Terminology

It is generally best to reserve the term 'pipes' for the type of pipe created by the pipe() system call (or function wrapping a system call), using the term FIFO for 'named pipes' created by the mkfifo() system call. Note that you can't open pipes with the open() system call; you can't open a FIFO except with open() or its minor variant openat().

Also, see Is it a good idea to typedef pointers? to which the short answer is generally "No". It caused some confusion in your code, where you have:

if ((ret = write(parent, &orderA, sizeof(Order))) > 0)

if ((ret = read(parent, &order_A, sizeof(Order))) > 0)

In both cases, the variable is a pointer to an Order (a node), and the extra indirection of the & is wrong because sizeof(Order *) != sizeof(Order) — and you're sending the wrong data down the pipe, and reading to the wrong place. There would be less chance of confusion if you had typedef struct Order Order; and the variables were of type Order *.

Version 1

At one point in the discussion I sent the following (flawed) code. Surprisingly, it worked, more or less. But that was mostly by accident, not least because of of the read/write problems shown.

This code uses library function available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory.

Defective code — do not use

/* SO 5396-9266 */
#include "posixver.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include "stderr.h"

#define MAX_PROCESS 10
const char *defaultPipe = "/tmp/child";
static int orderNum = 0;
static int workerNum = 0;
static pid_t shutDown[MAX_PROCESS];
static char pipes[MAX_PROCESS][100];

typedef struct Order
{
    int id;
    char created[256];
    char fullName[256];
    char email[256];
    char phone[256];
    char status;
    int performance;
    int days;
    struct Order *next;
} Order;

typedef struct Order *node;

void startJob(void);
void sendPriorityJobs(node priorityHead);
void handler(int signo, siginfo_t *info, void *context);
void createWorker(node orderA, int workerID);

node createNode(void);
node createOrder(char *fullName, char *email, char *phone, char *created, int performance);

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    if (argc != 1)
        err_usage("");
    err_setlogopts(ERR_PID|ERR_MILLI);

    struct sigaction sa;
    sa.sa_handler = (void *)handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_SIGINFO;
    sigaction(SIGUSR2, &sa, NULL);

    node test1 = createOrder("Jane Doe", "test1@gmail.com", "12345678", "2018-12-25 8:00", 1000);
    node test2 = createOrder("John Doe", "test2@gmail.com", "87654321", "2018-12-25 9:00", 1001);
    test1->next = test2;

    printf("Parent pid: %d\n", getpid());

    /*This is where we send the task */
    sendPriorityJobs(test1);

    for (int i = 0; i < workerNum; i++)
    {
        /*int p = open(pipes[i], O_RDONLY);
        read(p, msgFromWorker, sizeof(msgFromWorker));
        sleep(1);
        printf("%s\n", msgFromWorker);
        close(p);*/
        int status;
        int corpse = waitpid(shutDown[i], &status, 0);
        if (corpse < 0)
            err_sysrem("child %d - no status available: ", shutDown[i]);
        else
            err_remark("child %d (corpse %d) exited with status 0x%.4X\n", shutDown[i], corpse, status);
    }
    err_remark("All done!\n");

    return 0;
}

static void dump_order(const char *tag, const node order)
{
    err_remark("%s (%p):\n", tag, (void *)order);
    err_remark("Order: %d, %s, %s, %s, %s, %d\n", order->id, order->fullName,
               order->email, order->phone, order->created, order->performance);
}

void createWorker(node orderA, int workerID)
{
    int parent; // child;
    pid_t worker;
    char strID[12];
    sprintf(strID, "%d", workerID);
    char pipe[100];
    strcpy(pipe, defaultPipe);
    strcat(pipe, strID);
    if (mkfifo(pipe, S_IRUSR | S_IWUSR) != 0)
        err_syserr("failed to create FIFO '%s': ", pipe);
    err_remark("FIFO %s created\n", pipe);

    worker = fork();
    if (worker < 0)
        err_syserr("failed to fork: ");

    if (worker == 0)
    {
        err_remark("worker at play!\n");
        //this is a temporarily variable for the received structure.(order)
        node order_A = createNode();

        parent = open(pipe, O_RDONLY);
        if (parent < 0)
            err_syserr("failed to open FIFO '%s' for reading: ", pipe);
        int ret;
        if ((ret = read(parent, order_A, sizeof(Order))) > 0)
        {
            printf("[Child %d]: started work on %d. order.\n", getpid(), order_A->id);
            //printf("ret: %d\n", ret);
            //printf("%d,%s,%s,%s,%s,%d\n", order_A->id,order_A->fullName,order_A->email,order_A->phone,order_A->created,order_A->performance);
            dump_order("Read by child:", order_A);
        }

        startJob();

        char endMessage[256];
        sprintf(endMessage, "[Child %d]: ended his daily task.", getpid());

        /*Sending the done message via pipe. This is questionable part, 
        how to do this properly. */
        /*child = open(pipe, O_WRONLY);
        write(child, &endMessage, strlen(endMessage) + 1);*/

        err_remark("Message to parent: %s\n", endMessage);
        free(order_A);
        exit(0);
    }
    else
    {
        //Save the child's pid
        shutDown[workerID] = worker;

        //Save the child's pipe name.
        strcpy(pipes[workerID], pipe);

        parent = open(pipe, O_WRONLY);
        int ret;
        if ((ret = write(parent, &orderA, sizeof(Order))) > 0)   // BUG!
        {
            printf("[Parent]: sending %d. order!\n", orderA->id);
            dump_order("Parent sends", orderA);
            //printf("ret: %d\n", ret);
        }
        else
            err_syserr("faileds to writ to child %d\n", (int)worker);  // Ick!
        close(parent);
    }
}

void startJob(void)
{
    pid_t parentPID = getppid();
    sleep(1);
    printf("[Child %d]: is done, sending signal.\n", getpid());
    if (kill(parentPID, SIGUSR2) != 0)
        err_syserr("failed to signal parent process %d\n", (int)parentPID);
    else
        err_remark("signalled parent process %d with SIGUSR2\n", (int)parentPID);
}

void sendPriorityJobs(node priorityHead)
{
    node current = priorityHead;
    while (current != NULL)
    {
        createWorker(current, workerNum);
        workerNum++;
        current = current->next;
    }
    err_remark("All priority jobs sent\n");
}

node createNode(void)
{
    node tmp;
    tmp = (node)malloc(sizeof(struct Order));
    tmp->next = NULL;
    return tmp;
}

node createOrder(char *fullName, char *email, char *phone, char *created, int performance)
{
    node newOrder;
    newOrder = createNode();

    strcpy(newOrder->fullName, fullName);
    strcpy(newOrder->email, email);
    strcpy(newOrder->phone, phone);
    strcpy(newOrder->created, created);
    newOrder->performance = performance;
    newOrder->status = 'N';
    newOrder->id = orderNum + 1;
    orderNum++;

    return newOrder;
}

void handler(int signo, siginfo_t *info, void *context)
{
    (void)context;  // Unused
    char msg[256];
    time_t t;
    time(&t);
    sprintf(msg, "[Parent]: i got the signal(%d) from [Child %d] time: %s", signo, info->si_pid, ctime(&t));
    err_remark("%s: %d from %d\n", __func__, signo, info->si_pid);
    int nbytes = strlen(msg) + 1;
    int obytes;
    if ((obytes = write(1, msg, nbytes)) != nbytes)
        err_syserr("short write %d bytes (%d expected): ", obytes, nbytes);
    err_remark("return from %s\n", __func__);
}

Despite its flaws, and the extent to which it ignores the advice on How to avoid using printf() in a signal handler, it sort of works. Fortunately, the child doesn't try to do anything with the data it is supposed to read from the parent.

Sample run

Parent pid: 89291
signal67: 2018-12-30 15:54:17.298 - pid=89291: FIFO /tmp/child0 created
signal67: 2018-12-30 15:54:17.299 - pid=89292: worker at play!
[Parent]: sending 1. order!
[Child 89292]: started work on 2088763392. order.
signal67: 2018-12-30 15:54:17.299 - pid=89291: Parent sends (0x7fba7c800000):
signal67: 2018-12-30 15:54:17.299 - pid=89291: Order: 1, Jane Doe, test1@gmail.com, 12345678, 2018-12-25 8:00, 1000
signal67: 2018-12-30 15:54:17.299 - pid=89291: FIFO /tmp/child1 created
signal67: 2018-12-30 15:54:17.299 - pid=89292: Read by child: (0x7fba7d001800):
signal67: 2018-12-30 15:54:17.300 - pid=89292: Order: 2088763392, , ?, ?, ?, -413540392
signal67: 2018-12-30 15:54:17.300 - pid=89293: worker at play!
[Parent]: sending 2. order!
signal67: 2018-12-30 15:54:17.300 - pid=89291: Parent sends (0x7fba7c801000):
signal67: 2018-12-30 15:54:17.300 - pid=89291: Order: 2, John Doe, test2@gmail.com, 87654321, 2018-12-25 9:00, 1001
[Child 89293]: started work on 2088767488. order.
signal67: 2018-12-30 15:54:17.300 - pid=89291: All priority jobs sent
signal67: 2018-12-30 15:54:17.300 - pid=89293: Read by child: (0x7fba7d001800):
signal67: 2018-12-30 15:54:17.300 - pid=89293: Order: 2088767488, , ?, ?, ?, -413540392
[Child 89292]: is done, sending signal.
signal67: 2018-12-30 15:54:18.301 - pid=89291: handler: 31 from 89292
[Parent]: i got the signal(31) from [Child 89292] time: Sun Dec 30 15:54:18 2018
signal67: 2018-12-30 15:54:18.301 - pid=89291: return from handler
signal67: 2018-12-30 15:54:18.300 - pid=89292: signalled parent process 89291 with SIGUSR2
[Child 89293]: is done, sending signal.
signal67: 2018-12-30 15:54:18.301 - pid=89291: handler: 31 from 89293
[Parent]: i got the signal(31) from [Child 89293] time: Sun Dec 30 15:54:18 2018
signal67: 2018-12-30 15:54:18.301 - pid=89291: return from handler
signal67: 2018-12-30 15:54:18.301 - pid=89292: Message to parent: [Child 89292]: ended his daily task.
signal67: 2018-12-30 15:54:18.301 - pid=89293: signalled parent process 89291 with SIGUSR2
signal67: 2018-12-30 15:54:18.301 - pid=89293: Message to parent: [Child 89293]: ended his daily task.
signal67: 2018-12-30 15:54:18.302 - pid=89291: child 89292 (corpse 89292) exited with status 0x0000
signal67: 2018-12-30 15:54:18.302 - pid=89291: child 89293 (corpse 89293) exited with status 0x0000
signal67: 2018-12-30 15:54:18.302 - pid=89291: All done!

You can see garbage in the dump of the information received from the parent. However, this does show that the SA_RESTART flag is important in the sigaction() call. Without that, the waitpid() function returns errors, which this program catches and reports.

Version 2

This is mostly working code, which actually uses a sigsuspend() loop roughly like an earlier incarnation of your code did. However, it is somewhat different. In particular, this gets information back from the children. It does so using the same FIFO that was used to relay the information from the parent to the children. The parent initially opens the FIFO for writing, then writes a message to the child, then closes the FIFO. Meanwhile, the child opens the FIFO for reading, reads the message, and then closes the FIFO. When all the children have signalled the parent (using the sigsuspend loop), then it reads the responses, with the children opening the FIFO for writing, writing and closing the FIFO, while the parent opens the FIFO for reading, reads the response, and closes the FIFO again. Only then does the parent go into a loop waiting for its children to die (multi-process work on Unix-like systems is a morbid business, what with dead children and zombies and all the rest).

This code also removes the FIFOs both before and after it is run. The FIFO names in the /tmp directory are easy to deduce, and hence it is easy to break the program (create a directory /tmp/child.0, for example). A better solution would use mkstemp() or a similar function to create the FIFO names (noting that mkstemp() actually creates a file, not a FIFO; there isn't a direct mechanism to create a uniquely-named FIFO that I know of).

Usable code, but still unpolished

/* SO 5396-9266 */
#include "posixver.h"
#include "stderr.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#define MAX_PROCESS 10

static const char defaultPipe[] = "/tmp/child.";
static int orderNum = 0;
static int workerNum = 0;
static pid_t shutDown[MAX_PROCESS];
static char pipes[MAX_PROCESS][100];

struct SigCaught
{
    int             sig_number;
    struct timespec sig_tstamp;
    int             sig_sender;
    void           *sig_contxt;
};

static struct SigCaught sig_list[MAX_PROCESS];
static int sig_cur = 0;
static int sig_prt = 0;

typedef struct Order
{
    int id;
    char created[256];
    char fullName[256];
    char email[256];
    char phone[256];
    char status;
    int performance;
    int days;
    struct Order *next;
} Order;

typedef struct Order *node;

static void startJob(void);
static void sendPriorityJobs(node priorityHead);
static void sig_handler(int signo, siginfo_t *info, void *context);
static void sig_printer(void);
static void createWorker(node orderA, int workerID);
static node createNode(void);
static node createOrder(char *fullName, char *email, char *phone, char *created, int performance);
static void get_response(void);

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    if (argc != 1)
        err_usage("");
    err_setlogopts(ERR_PID|ERR_MILLI);

    struct sigaction sa;
    sa.sa_handler = (void *)sig_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_SIGINFO;
    sigaction(SIGUSR2, &sa, NULL);

    node test1 = createOrder("Jane Doe", "test1@gmail.com", "12345678", "2018-12-25 8:00", 1000);
    node test2 = createOrder("John Doe", "test2@gmail.com", "87654321", "2018-12-25 9:00", 1001);
    test1->next = test2;

    printf("Parent pid: %d\n", getpid());

    /* This is where we send the task */
    sendPriorityJobs(test1);

    for (int i = 0; i < workerNum; i++)
    {
        sigset_t es;
        sigemptyset(&es);
        err_remark("suspending...\n");
        sigsuspend(&es);
        err_remark("awake again.\n");
    }

    sig_printer();
    get_response();

    /* Wait for child processes to signal, write, and exit */
    for (int i = 0; i < workerNum; i++)
    {
        int status;
        int corpse = waitpid(shutDown[i], &status, 0);
        if (corpse > 0)
            err_remark("child %d (corpse %d) exited with status 0x%.4X\n", shutDown[i], corpse, status);
        else
            err_sysrem("child %d - no status available: ", shutDown[i]);
    }
    err_remark("All done!\n");

    /* Clean up FIFOs */
    for (int i = 0; i < workerNum; i++)
        unlink(pipes[i]);

    return 0;
}

static void dump_order(const char *tag, const node order)
{
    err_remark("%s (%p):\n", tag, (void *)order);
    err_remark("Order: %d, %s, %s, %s, %s, %d\n", order->id, order->fullName,
               order->email, order->phone, order->created, order->performance);
}

void createWorker(node orderA, int workerID)
{
    int parent;
    pid_t worker;
    char strID[12];
    sprintf(strID, "%d", workerID);
    char pipe[100];
    strcpy(pipe, defaultPipe);
    strcat(pipe, strID);
    if (unlink(pipe) != 0 && errno != ENOENT)
        err_syserr("failed to remove FIFO '%s': ", pipe);
    if (mkfifo(pipe, S_IRUSR | S_IWUSR) != 0)
        err_syserr("failed to create FIFO '%s': ", pipe);
    err_remark("FIFO %s created\n", pipe);

    worker = fork();
    if (worker < 0)
        err_syserr("failed to fork: ");

    if (worker == 0)
    {
        err_remark("worker at play!\n");
        node order_A = createNode();

        parent = open(pipe, O_RDONLY);
        if (parent < 0)
            err_syserr("failed to open FIFO '%s' for reading: ", pipe);
        int ret;
        if ((ret = read(parent, order_A, sizeof(Order))) != sizeof(Order))
            err_syserr("short read of %d bytes (%zu expected) from parent: ", ret, sizeof(Order));
        printf("[Child %d]: started work on %d. order.\n", getpid(), order_A->id);
        dump_order("Read by child:", order_A);
        close(parent);

        startJob();     /* Signal to parent */

        char endMessage[256];
        sprintf(endMessage, "[Child %d]: ended his daily task.", getpid());

        parent = open(pipe, O_WRONLY);
        if (parent < 0)
            err_syserr("failed to open FIFO '%s' for writing: ", pipe);
        err_remark("successfully reopened FIFO '%s' for writing\n", pipe);

        int len = strlen(endMessage);
        if (write(parent, endMessage, len) != len)
            err_syserr("faied to write message of %d bytes to parent: ", len);
        close(parent);

        err_remark("Message sent to parent: %s\n", endMessage);
        free(order_A);
        exit(0);
    }
    else
    {
        shutDown[workerID] = worker;
        strcpy(pipes[workerID], pipe);
        workerID++;

        parent = open(pipe, O_WRONLY);
        int ret;
        if ((ret = write(parent, orderA, sizeof(Order))) == sizeof(Order))
        {
            printf("[Parent]: sending %d. order to child %d!\n", orderA->id, (int)worker);
            dump_order("Parent sends", orderA);
        }
        else
            err_syserr("failed to write %zu bytes to child %d\n", sizeof(Order), (int)worker);
        close(parent);
    }
}

static void read_response(int worker)
{
    err_remark("Starting to read response from worker %d\n", worker);
    int fd = open(pipes[worker], O_RDONLY);
    if (fd < 0)
        err_syserr("failed to open FIFO '%s' for reading\n", pipes[worker]);
    err_remark("successfully opened FIFO '%s' for reading\n", pipes[worker]);
    int nbytes;
    char buffer[1024];
    while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0)
        err_remark("MSG %i (%d): %.*s\n", worker, shutDown[worker], nbytes, buffer);
    fflush(stdout);
    close(fd);
    err_remark("Finished reading response from worker %d\n", worker);
}

/* There's probably a better way to do this! */
static void get_response(void)
{
    for (int i = 0; i < workerNum; i++)
    {
        for (int j = 0; j < sig_cur; j++)
        {
            if (shutDown[i] == sig_list[j].sig_sender)
            {
                read_response(i);
                sig_list[j].sig_sender = 0;     /* Don't try again */
            }
        }
    }
}

void startJob(void)
{
    pid_t parentPID = getppid();
    sleep(1);
    printf("[Child %d]: is done, sending signal.\n", getpid());
    if (kill(parentPID, SIGUSR2) != 0)
        err_syserr("failed to signal parent process %d\n", (int)parentPID);
    else
        err_remark("signalled parent process %d with SIGUSR2\n", (int)parentPID);
}

void sendPriorityJobs(node priorityHead)
{
    node current = priorityHead;
    while (current != NULL)
    {
        createWorker(current, workerNum);
        workerNum++;
        current = current->next;
    }
    err_remark("All priority jobs sent\n");
}

node createNode(void)
{
    node tmp;
    tmp = (node)malloc(sizeof(struct Order));
    tmp->next = NULL;
    return tmp;
}

node createOrder(char *fullName, char *email, char *phone, char *created, int performance)
{
    node newOrder;
    newOrder = createNode();

    strcpy(newOrder->fullName, fullName);
    strcpy(newOrder->email, email);
    strcpy(newOrder->phone, phone);
    strcpy(newOrder->created, created);
    newOrder->performance = performance;
    newOrder->status = 'N';
    newOrder->id = orderNum + 1;
    orderNum++;

    return newOrder;
}

void sig_handler(int signo, siginfo_t *info, void *context)
{
    sig_list[sig_cur].sig_number = signo;
    clock_gettime(CLOCK_REALTIME, &sig_list[sig_cur].sig_tstamp);
    sig_list[sig_cur].sig_sender = info->si_pid;
    sig_list[sig_cur].sig_contxt = context;
    static const char sig_message[] = "return from signal handler\n";
    write(STDERR_FILENO, sig_message, sizeof(sig_message) - 1);
    sig_cur++;
}

static void print_siginfo(struct SigCaught *info)
{
    struct tm *lt = localtime(&info->sig_tstamp.tv_sec);
    char buffer[32];
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
    err_remark("%s.%.3ld: signal %d received from PID %d\n", buffer,
               info->sig_tstamp.tv_nsec / 1000000, info->sig_number,
               info->sig_sender);
}

static void sig_printer(void)
{
    while (sig_prt < sig_cur)
        print_siginfo(&sig_list[sig_prt++]);
}

Sample run

Parent pid: 89404
signal41: 2018-12-30 16:02:57.458 - pid=89404: FIFO /tmp/child.0 created
signal41: 2018-12-30 16:02:57.459 - pid=89405: worker at play!
[Child 89405]: started work on 1. order.
[Parent]: sending 1. order to child 89405!
signal41: 2018-12-30 16:02:57.459 - pid=89404: Parent sends (0x7fc14a800000):
signal41: 2018-12-30 16:02:57.459 - pid=89404: Order: 1, Jane Doe, test1@gmail.com, 12345678, 2018-12-25 8:00, 1000
signal41: 2018-12-30 16:02:57.460 - pid=89404: FIFO /tmp/child.1 created
signal41: 2018-12-30 16:02:57.459 - pid=89405: Read by child: (0x7fc14b001800):
signal41: 2018-12-30 16:02:57.460 - pid=89405: Order: 1, Jane Doe, test1@gmail.com, 12345678, 2018-12-25 8:00, 1000
signal41: 2018-12-30 16:02:57.460 - pid=89406: worker at play!
[Child 89406]: started work on 2. order.
[Parent]: sending 2. order to child 89406!
signal41: 2018-12-30 16:02:57.461 - pid=89404: Parent sends (0x7fc14a801000):
signal41: 2018-12-30 16:02:57.461 - pid=89404: Order: 2, John Doe, test2@gmail.com, 87654321, 2018-12-25 9:00, 1001
signal41: 2018-12-30 16:02:57.461 - pid=89404: All priority jobs sent
signal41: 2018-12-30 16:02:57.461 - pid=89404: suspending...
signal41: 2018-12-30 16:02:57.461 - pid=89406: Read by child: (0x7fc14b80a200):
signal41: 2018-12-30 16:02:57.461 - pid=89406: Order: 2, John Doe, test2@gmail.com, 87654321, 2018-12-25 9:00, 1001
[Child 89405]: is done, sending signal.
return from signal handler
signal41: 2018-12-30 16:02:58.461 - pid=89404: awake again.
signal41: 2018-12-30 16:02:58.461 - pid=89404: suspending...
signal41: 2018-12-30 16:02:58.461 - pid=89405: signalled parent process 89404 with SIGUSR2
[Child 89406]: is done, sending signal.
return from signal handler
signal41: 2018-12-30 16:02:58.462 - pid=89404: awake again.
signal41: 2018-12-30 16:02:58.462 - pid=89404: 2018-12-30 16:02:58.461: signal 31 received from PID 89405
signal41: 2018-12-30 16:02:58.462 - pid=89404: 2018-12-30 16:02:58.462: signal 31 received from PID 89406
signal41: 2018-12-30 16:02:58.462 - pid=89404: Starting to read response from worker 0
signal41: 2018-12-30 16:02:58.462 - pid=89404: successfully opened FIFO '/tmp/child.0' for reading
signal41: 2018-12-30 16:02:58.462 - pid=89406: signalled parent process 89404 with SIGUSR2
signal41: 2018-12-30 16:02:58.462 - pid=89405: successfully reopened FIFO '/tmp/child.0' for writing
signal41: 2018-12-30 16:02:58.463 - pid=89404: MSG 0 (89405): [Child 89405]: ended his daily task.
signal41: 2018-12-30 16:02:58.463 - pid=89405: Message sent to parent: [Child 89405]: ended his daily task.
signal41: 2018-12-30 16:02:58.463 - pid=89404: Finished reading response from worker 0
signal41: 2018-12-30 16:02:58.463 - pid=89404: Starting to read response from worker 1
signal41: 2018-12-30 16:02:58.463 - pid=89404: successfully opened FIFO '/tmp/child.1' for reading
signal41: 2018-12-30 16:02:58.463 - pid=89406: successfully reopened FIFO '/tmp/child.1' for writing
signal41: 2018-12-30 16:02:58.463 - pid=89404: MSG 1 (89406): [Child 89406]: ended his daily task.
signal41: 2018-12-30 16:02:58.464 - pid=89404: Finished reading response from worker 1
signal41: 2018-12-30 16:02:58.464 - pid=89404: child 89405 (corpse 89405) exited with status 0x0000
signal41: 2018-12-30 16:02:58.463 - pid=89406: Message sent to parent: [Child 89406]: ended his daily task.
signal41: 2018-12-30 16:02:58.464 - pid=89404: child 89406 (corpse 89406) exited with status 0x0000
signal41: 2018-12-30 16:02:58.464 - pid=89404: All done!
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

Basically, most of the cases @JonathanLeffler's code work but when the system receive two signal at the same time it won't work at all, and you'll be not able to get the second signal. What I did, to solve this issue was to use real time signal instead of SIGUSR1|2.

Bence Szabari
  • 143
  • 1
  • 2
  • 12