1

I have this simple code that passes a value from child process to parent using named pipe. Code:

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

int mkfifo(const char *path, mode_t mode);

int main(int argc, char **argv)
{
  int fd, val;
  char *myfifo = "/tmp/myfifo";
  mkfifo(myfifo, 0666);
  pid_t c = fork();

  if (c==0) {
    val=10;
    fd = open(myfifo, O_WRONLY);
    write(fd, &val, sizeof(val));
  }

  if (c>0) {
    fd = open(myfifo, O_RDONLY);
    read(fd, &val, sizeof(val));
    printf("val is %d",val);
  }  
}

I read that 0666 gives permission for read and write, 0200 for write only, 0400 for read only and 0000 no permission. However if I change 0666 to any of these the code works correctly. Why is this happening and what am I missing?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Using `0666` gives read and write permission to the user, the group and others, whereas `0400` means only the user can access the pipe and they can only read from it (only super user can write to it). Did you remove the FIFO between attempts? It would be more convincing if you had `unlink(myfifo);` before you created it. Or if you checked the return from `mkfifo()`. – Jonathan Leffler May 03 '19 at 20:46
  • 3
    And why did you declare `mkfifo()`? It's declared in a header — `` according to POSIX. If you didn't get it declared automatically, maybe you need to review how to enable POSIX functions (if you are using `-std=c11`, the POSIX functions are not declared automatically; using `-std=gnu11` avoids the problem — if you're using GCC). (Also, why declare `argc` and `argv` if you don't use them?) – Jonathan Leffler May 03 '19 at 20:53
  • 3
    In less words: you should check for return codes and `errno` values. – Lorinczy Zsigmond May 04 '19 at 06:15

1 Answers1

0

As I noted in the comments, using 0666 gives read and write permission to the user, the group and others, whereas 0400 means only the user can access the pipe and they can only read from it (only superuser can write to it) while 0200 means only the user can access the pipe and they can only write to it (only superuser can read from it). (The name "superuser" is a shorthand for "a user with appropriate privileges", which typically means root on Unix-based systems.)

Here is a version of your code that demonstrates error checking and reporting. It takes an optional command line argument for the name of the FIFO it is to use. It tests each of the modes 0666, 0400, 0200 and 0. It is convoluted by the timing out if an operation takes too long. The signal handling code takes care to avoid using printf() in a signal handler, and the string has space enough for 7-digit PIDs, such as are found on AIX.

The program uses my 'standard error reporting functions', the code for which is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. Using them simplifies the error handling code. The function names containing rem make remarks (and continue); those ending err exit after reporting the error message. The options report times to millisecond resolution, and ensure the PID appears in the error message.

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

#include "stderr.h"

static void test_fifo(const char *fifo, int mode);
static void alarm_handler(int signum);

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    err_setlogopts(ERR_PID|ERR_MILLI);
    int modes[] = { 0666, 0400, 0200, 0 };
    enum { NUM_MODES = sizeof(modes) / sizeof(modes[0]) };
    char *myfifo = "/tmp/myfifo";
    if (argc > 1)
        myfifo = argv[1];

    signal(SIGALRM, alarm_handler);

    for (int i = 0; i < NUM_MODES; i++)
    {
        putchar('\n');
        test_fifo(myfifo, modes[i]);
        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            err_remark("Child %d exited with status 0x%.4X\n", corpse, status);
    }

    return 0;
}

static void set_pid(int pid, char *str)
{
    if (pid / 10 > 0)
        set_pid(pid / 10, str + 1);
    else
    {
        str[1] = '\n';
        str[2] = '\0';
    }
    *str = (pid % 10) + '0';
}

static void alarm_handler(int signum)
{
    char msg[] = "Signal ?? received by ???????\n";
    msg[7] = (signum / 10) > 0 ? (signum / 10) + '0' : ' ';
    msg[8] = (signum % 10) + '0';
    set_pid(getpid(), &msg[22]);
    write(0, msg, strlen(msg));
}

static void test_fifo(const char *fifo, int mode)
{
    unlink(fifo);
    if (mkfifo(fifo, mode) != 0)
    {
        err_sysrem("failed to create FIFO '%s' with mode %0o\n", fifo, mode);
        return;
    }
    pid_t c = fork();
    /* With ERR_PID set, the PID is reported automatically */
    err_remark("(PPID %d) at work\n", (int)getppid());

    if (c == 0)
    {
        /* Child */
        srand(time(0));
        int val = rand() % 100;
        alarm(10);
        int fd = open(fifo, O_WRONLY);
        if (fd < 0)
            err_syserr("failed to open FIFO '%s' for writing\n", fifo);
        if (write(fd, &val, sizeof(val)) != sizeof(val))
            err_syserr("failed to write to FIFO '%s'\n", fifo);
        printf("Wrote %d to FIFO '%s'\n", val, fifo);
        close(fd);
        exit(0);
    }

    if (c > 0)
    {
        /* Parent */
        alarm(10);
        int fd = open(fifo, O_RDONLY);
        if (fd < 0)
        {
            err_sysrem("failed to open FIFO '%s' for reading\n", fifo);
            return;
        }
        int val;
        if (read(fd, &val, sizeof(val)) != sizeof(val))
        {
            err_sysrem("failed to write to FIFO '%s'\n", fifo);
            return;
        }
        printf("read value %d from FIFO '%s'\n", val, fifo);
        close(fd);
        if (unlink(fifo) != 0)
            err_sysrem("failed to remove FIFO '%s'\n", fifo);
    }
}

I consciously chose not to report errors from close(); there isn't anything you can do if close() reports failure anyway. The first unlink() is not checked; it is expected to fail if the FIFO did not exist, and to succeed if it does. The error report checking mkfifo() will handle problems if the FIFO existed but could not be removed (because it is a directory, or the user doesn't have permission, or whatever). The location of the wait() loop helps ensure error messages etc are displayed even if the parent finishes quickly because it couldn't open the FIFO but the child finishes slowly because it could open the FIFO.

Sample output (I called the program fifo37, and the source code was in fifo37.c):

$ ./fifo37

fifo37: 2019-05-03 14:58:48.612 - pid=94485: (PPID 51845) at work
fifo37: 2019-05-03 14:58:48.612 - pid=94486: (PPID 94485) at work
Wrote 1 to FIFO '/tmp/myfifo'
read value 1 from FIFO '/tmp/myfifo'
fifo37: 2019-05-03 14:58:48.614 - pid=94485: Child 94486 exited with status 0x0000

fifo37: 2019-05-03 14:58:48.614 - pid=94485: (PPID 51845) at work
fifo37: 2019-05-03 14:58:48.614 - pid=94487: (PPID 94485) at work
fifo37: 2019-05-03 14:58:48.614 - pid=94487: failed to open FIFO '/tmp/myfifo' for writing
error (13) Permission denied
Signal 14 received by 58449
fifo37: 2019-05-03 14:58:58.615 - pid=94485: failed to open FIFO '/tmp/myfifo' for reading
error (4) Interrupted system call
fifo37: 2019-05-03 14:58:58.615 - pid=94485: Child 94487 exited with status 0x0100

fifo37: 2019-05-03 14:58:58.616 - pid=94485: (PPID 51845) at work
fifo37: 2019-05-03 14:58:58.616 - pid=94485: failed to open FIFO '/tmp/myfifo' for reading
error (13) Permission denied
fifo37: 2019-05-03 14:58:58.616 - pid=94488: (PPID 94485) at work
Signal 14 received by 58449
Signal 14 received by 88449
fifo37: 2019-05-03 14:59:08.619 - pid=94488: failed to open FIFO '/tmp/myfifo' for writing
error (4) Interrupted system call
fifo37: 2019-05-03 14:59:08.621 - pid=94485: Child 94488 exited with status 0x0100

fifo37: 2019-05-03 14:59:08.621 - pid=94485: (PPID 51845) at work
fifo37: 2019-05-03 14:59:08.621 - pid=94485: failed to open FIFO '/tmp/myfifo' for reading
error (13) Permission denied
fifo37: 2019-05-03 14:59:08.621 - pid=94489: (PPID 94485) at work
fifo37: 2019-05-03 14:59:08.622 - pid=94489: failed to open FIFO '/tmp/myfifo' for writing
error (13) Permission denied
fifo37: 2019-05-03 14:59:08.622 - pid=94485: Child 94489 exited with status 0x0100
$
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278