if (access(optarg, F_OK) != 0)
error(optarg, "module directory does not exist");
/* Проверка доступности каталога. */
if (access(optarg, R_OK | X_OK) != 0)
error(optarg, "module directory is not accessible");
/* Проверка того, что это каталог. */
if (stat(optarg, &dir_info) != 0 || !S_ISDIR(dir_info.st_mode))
error(optarg, "not a directory");
/* Все правильно. */
module_dir = strdup(optarg);
}
break;
case 'p':
/* Пользователь ввел -p или --port. */
{
long value;
char* end;
value = strtol(optarg, &end, 10);
if (*end != '\0')
/* В номере порта указаны не только цифры. */
print_usage(1);
/* Преобразуем номер порта в число с сетевым (обратным)
порядком следования байтов. */
port = (uint16_t)htons(value);
}
break;
case 'v':
/* Пользователь ввел -v или --verbose. */
verbose = 1;
break;
case '?':
/* Пользователь ввел непонятную опцию. */
print_usage(1);
case -1:
/* Обработка опций завершена. */
break;
default:
abort();
}
} while (next_option != -1);
/* Программа не принимает никаких дополнительных аргументов.
Если они есть, выдается сообщение об ошибке. */
if (optind != argc)
print_usage(1);
/* Отображение имени каталога, если программа работает в режиме
развернутых сообщений. */
if (verbose)
printf("modules will be loaded from %s\n", module_dir);
/* Запуск сервера. */
server_run(local_address, port);
return 0;
}
Файл main.c
содержит следующие функции.
■ Функция getopt_long()
(см. раздел 21.3, "Функция getopt_long()") вызывается для анализа опций командной строки. Опции могут задаваться в двух форматах: длинном и коротком. Описание длинных опций приведено в массиве long_options
, а коротких — в массиве short_options
.
По умолчанию серверный порт имеет номер 0, а локальный адрес задан в виде константы INADDR_ANY
. Эти установки можно переопределить с помощью опций --port
( -p
) и - -address
( -a
) соответственно. Если пользователь ввел адрес, вызывается библиотечная функция gethostbyname()
, преобразующая его в числовой Internet-адрес. [38] При необходимости функция gethostbyname() осуществляет поиск имен в DNS.
По умолчанию серверные модули загружаются из каталога, где находится исполняемый файл. Этот каталог определяется с помощью функции get_self_executable_directory()
. Данную установку можно переопределить с помощью опции --module
( -m
). В таком случае проверяется, является ли указанный каталог доступным.
По умолчанию развернутые сообщения не отображаются, если не указать опцию --verbose
( -v
).
■ Если пользователь ввел опцию --help
( -h
) или указал неправильную опцию, вызывается функция print_usage()
, которая отображает сообщение о правильном использовании программы и завершает работу.
В дополнение к основной программе созданы четыре модуля, в которых реализованы функции сервера. Чтобы создать собственный модуль, достаточно определить функцию module_generate()
, которая будет возвращать HTML-код.
11.3.1. Отображение текущего времени
Модуль time.so
(исходный текст приведен в листинге 11.6) генерирует простую страницу, где отображается текущее время на сервере. В функции module_generate()
вызывается функция gettimeofday()
, возвращающая значение текущего времени (см. раздел 8.7, "Функция gettimeofday(): системные часы"), после чего функции localtime()
и strftime()
преобразуют это значение в текстовый формат. Полученная строка встраивается в шаблон HTML-страницы page_template.
Листинг 11.6. ( time.c ) серверный модуль, отображающий текущее время
#include
#include
#include
#include
#include "server.h"
/* шаблон HTML-страницы, генерируемой данным модулем. */
static char* page_template =
"\n"
"
\n"
"
\n"
" \n"
"
\n"
"
The current time is %s
\n"
"
\n"
"
\n";
void module_generate(int fd) {
struct timeval tv;
struct tm* ptm;
char time_string[40];
FILE* fp;
/* Определение времени суток и заполнение структуры типа tm. */
gettimeofday(&tv, NULL);
ptm = localtime(&tv.tv_sec);
/* Получение строкового представления времени с точностью
до секунды. */
strftime(time_string, sizeof(time_string), "%H:%M:%S", ptm);
/* Создание файлового потока, соответствующего дескриптору
Читать дальше