Права доступа в именованных каналах - PullRequest
2 голосов
/ 03 мая 2019

У меня есть этот простой код, который передает значение из дочернего процесса в родительский, используя именованный канал. Код:

#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);
  }  
}

Я прочитал, что 0666 дает разрешение на чтение и запись, 0200 только для записи, 0400 только для чтения и 0000 нет разрешения. Однако, если я изменю 0666 на любой из них, код будет работать правильно. Почему это происходит и чего мне не хватает?

1 Ответ

1 голос
/ 04 мая 2019

Как я отметил в комментариях, использование 0666 дает разрешение на чтение и запись пользователю, группе и другим, тогда как 0400 означает, что только пользователь может получить доступ к каналу, и они могут только читать из него (только суперпользователь может писать в него ) в то время как 0200 означает, что только пользователь может получить доступ к каналу, и они могут только писать в него (только суперпользователь может читать из него). (Название «superuser» является сокращением для «пользователя с соответствующими привилегиями», что обычно означает root в системах на основе Unix.)

Вот версия вашего кода, которая демонстрирует проверку ошибок и создание отчетов. Он принимает необязательный аргумент командной строки для имени FIFO, который он будет использовать. Он тестирует каждый из режимов 0666, 0400, 0200 и 0. Это запутано с тайм-аутом, если операция занимает слишком много времени. Код обработки сигналов заботится о том, чтобы избегал использования printf() в обработчике сигналов , а в строке достаточно места для 7-значных идентификаторов PID, таких как в AIX.

Программа использует мои «стандартные функции отчетов об ошибках», код которых доступен в моем репозитории SOQ (Вопросы о переполнении стека) на GitHub в виде файлов stderr.c и stderr.h в src / libsoq подкаталог. Их использование упрощает код обработки ошибок. Имена функций, содержащие rem, делают замечания (и продолжают); те, которые заканчиваются err, выходят после сообщения об ошибке. Опции сообщают время с точностью до миллисекунды и обеспечивают отображение PID в сообщении об ошибке.

#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);
    }
}

Я сознательно решил не сообщать об ошибках от close(); вы ничего не можете сделать, если close() все равно сообщит о сбое. Первый unlink() не проверен; ожидается, что он потерпит неудачу, если FIFO не существует, и будет успешным, если он существует. Проверка отчета об ошибках mkfifo() будет обрабатывать проблемы, если FIFO существует, но не может быть удален (потому что это каталог, или у пользователя нет разрешения, или что-то еще). Расположение цикла wait() помогает обеспечить отображение сообщений об ошибках и т. Д., Даже если родительский процесс завершается быстро, поскольку он не может открыть FIFO, но дочерний процесс завершается медленно, поскольку он может открыть FIFO.

Пример вывода (я назвал программу fifo37, а исходный код был в 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
$
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...