Книга: Программирование для Linux. Профессиональный подход

Листинг 11.5. (main.c) Главная серверная функция, выполняющая анализ аргументов командной строки

Листинг 11.5. (main.c) Главная серверная функция, выполняющая анализ аргументов командной строки

#include <assert.h>
#include <getopt.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "server.h"
/* Описание длинных опций для функции getopt_long(). */
static const struct option long_options[] = {
 { "address",    1, NULL, 'a' },
 { "help",       0, NULL, 'h' },
 { "module-dir", 1, NULL, 'm' },
 { "port",       1, NULL, 'p' },
 { "verbose",    0, NULL, 'v' },
};
/* Описание коротких опций для функции getopt_long(). */
static const char* const short_options = "a:hm:p:v";
/* Сообщение о том, как правильно использовать программу. */
static const char* const usage_template =
 "Usage: %s { options }n"
 " -a, --address ADDR   Bind to local address (by default, bindn"
 "                      to all local addresses).n"
 " -h, --help           Print this information.n"
 " -m, --module-dir DIR Load modules from specified directoryn"
 "                      (by default, use executable directory).n"
 " -p, --port PORT      Bind to specified port.n"
 " -v, --verbose        Print verbose messages.n";
/* Вывод сообщения о правильном использовании программы
   и завершение работы. Если аргумент IS_ERROR не равен нулю,
   сообщение записывается в поток stderr и возвращается
   признак ошибки, в противном случае сообщение выводится в
   поток stdout и возвращается обычный нулевой код. */
static void print_usage(int is_error) {
 fprintf(is_error ? stderr : stdout, usage_template,
  program_name);
 exit(is_error ? 1 : 0);
}
int main(int argc, char* const argv[]) {
 struct in_addr local_address;
 uint16_t port;
 int next_option;
 /* Сохранение имени программы для отображения в сообщениях
    об ошибке. */
 program_name = argv[0];
 /* Назначение стандартных установок. По умолчанию сервер
    связан со всеми локальными адресами, и ему автоматически
    назначается неиспользуемый порт. */
 local_address.s_addr = INADDR_ANY;
 port = 0;
 /* He отображать развернутые сообщения. */
 verbose = 0;
 /* Загружать модули из каталога, в котором содержится
    исполняемый файл. */
 module_dir = get_self_executable_directory();
 assert(module_dir != NULL);
 /* Анализ опций. */
 do {
  next_option =
   getopt_long(argc, argv, short_options,
  long_options, NULL);
  switch (next_option) {
  case 'a':
   /* Пользователь ввел -a или --address. */
   {
    struct hostent* local_host_name;
    /* Поиск заданного адреса. */
    local_host_name = gethostbyname(optarg);
    if (local_host_name == NULL ||
     local_host_name->h_length == 0)
     /* He удалось распознать имя. */
     error(optarg, "invalid host name");
    else
     /* Введено правильное имя */
     local_address.s_addr =
      *((int*)(local_host_name->h_addr_list[0]));
   }
   break;
  case 'h':
   /* Пользователь ввёл -h или --help. */
   print_usage(0);
  case 'm':
   /* Пользователь ввел -m или --module-dir. */
   {
    struct stat dir_info;
    /* Проверка существования каталога */
    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 != '')
     /* В номере порта указаны не только цифры. */
     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 %sn", 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]

По умолчанию серверные модули загружаются из каталога, где находится исполняемый файл. Этот каталог определяется с помощью функции get_self_executable_directory(). Данную установку можно переопределить с помощью опции --module(-m). В таком случае проверяется, является ли указанный каталог доступным.

По умолчанию развернутые сообщения не отображаются, если не указать опцию --verbose(-v).

? Если пользователь ввел опцию --help(-h) или указал неправильную опцию, вызывается функция print_usage(), которая отображает сообщение о правильном использовании программы и завершает работу.

Оглавление книги


Генерация: 1.141. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз