12 err_sys("can't create %s", SERV_FIFO);
13 /* открытие FIFO-cepвepa на чтение */
14 readfifo = Open(SERV_FIFO, O_RDONLY, 0);
15 dummyfd = Open(SERV_FIFO, O_WRONLY, 0); /* не используется */
16 while ((n = Readline(readfifo, buff, MAXLINE)) > 0) {
17 if (buff[n-1] == '\n')
18 n--; /* delete newline from readline() */
19 buff[n] = '\0'; /* полное имя, завершаемое 0 */
20 if ((ptr = strchr(buff, ' ')) == NULL) {
21 err_msg("bogus request: ls", buff);
22 continue;
23 }
24 *ptr++ = 0; /* идентификатор процесса, указатель на имя файла */
25 pid = atol(buff);
26 snprintf(fifoname, sizeof(fifoname), "/tmp/fifo.%ld", (long) pid);
27 if ( (writefifo = open(fifoname, O_WRONLY, 0)) < 0) {
28 err_msg("cannot open: ls", fifoname);
29 continue;
30 }
31 if ((fd = open(ptr, O_RDONLY)) < 0) {
32 /* ошибка, нужно сообщить клиенту */
33 snprintf(buff + n, sizeof(buff) – n, ": can't open, %s\n",
34 strerror(errno));
35 n = strlen(ptr);
36 Write(writefifo, ptr, n);
37 Close(writefifo);
38
39 } else {
40 /* успешное открытие, копируем файл */
41 while ((n = Read(fd, buff, MAXLINE)) > 0)
42 Write(writefifo, buff, n);
43 Close(fd);
44 Close(writefifo);
45 }
46 }
47 }
10-15 Сервер создает канал FIFO с известным именем, обрабатывая ситуацию, когда такой канал уже существует. Затем этот канал открывается дважды: один раз только для чтения, а второй — только для записи. Дескриптор readfifo используется для приема запросов от клиентов, а дескриптор dummyfd не используется вовсе. Причина, по которой нужно открыть канал для записи, видна из табл. 4.1. Если канал не открыть на запись, то при завершении работы очередного клиента этот канал будет опустошаться и сервер будет считывать 0, означающий конец файла. Пришлось бы каждый раз закрывать канал вызовом close, а затем заново открывать его с флагом O_RDONLY, что приводило бы к блокированию демона до подключения следующего клиента. Мы же всегда будем иметь дескриптор, открытый на запись, поэтому функция read не будет возвращать 0, означающий конец файла, при отсутствии клиентов. Вместо этого сервер просто будет блокироваться при вызове read, ожидая подключения следующего клиента. Этот трюк упрощает код программы-сервера и уменьшает количество вызовов open для канала сервера.
При запуске сервера первый вызов open (с флагом O_RDONLY) приводит к блокированию процесса до появления первого клиента, открывающего канал сервера на запись (см. табл. 4.1). Второй вызов open (с флагом O_WRONLY) не приводит к блокированию, поскольку канал уже открыт на запись.
16 Каждый запрос, принимаемый от клиента, представляет собой одну строку, состоящую из идентификатора процесса, пробела и полного имени требуемого файла. Эта строка считывается функцией readline (приведенной в [24, с.79]).
17-26 Символ перевода строки, возвращаемый функцией readline, удаляется. Этот символ может отсутствовать только в том случае, если буфер был заполнен, прежде чем был обнаружен символ перевода строки, либо если последняя введенная строка не была завершена этим символом. Функция strchr возвращает указатель на первый пробел в этой строке, который затем увеличивается на единицу, чтобы он указывал на первый символ полного имени файла, следующего за пробелом. Полное имя канала клиента формируется из его идентификатора процесса, и этот канал открывается сервером на запись.
Открытие файла и отправка его в FIFO клиента