KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Марк Митчелл - Программирование для Linux. Профессиональный подход

Марк Митчелл - Программирование для Linux. Профессиональный подход

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Марк Митчелл, "Программирование для Linux. Профессиональный подход" бесплатно, без регистрации.
Перейти на страницу:

    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(), которая отображает сообщение о правильном использовании программы и завершает работу.

11.3. Модули

В дополнение к основной программе созданы четыре модуля, в которых реализованы функции сервера. Чтобы создать собственный модуль, достаточно определить функцию 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 <assert.h>

#include <stdio.h>

#include <sys/time.h>

#include <time.h>

#include "server.h"


/* шаблон HTML-страницы, генерируемой данным модулем. */

static char* page_template =

 "<html>n"

 " <head>n"

 "  <meta http-equiv="refresh" content="5">n"

 " </head>n"

 " <body>n"

 " 

The current time is %s

n"

 " </body>n"

 "</html>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);


 /* Создание файлового потока, соответствующего дескриптору

    клиентского сокета. */

 fp = fdopen(fd, "w");

 assert(fp != NULL);


 /* Запись HTML-страницы. */

 fprintf(fp, page_template, time_string);

 /* Очистка буфера потока */

 fflush(fp);

}

Для удобства в этом модуле используются стандартные библиотечные функции ввода-вывода. Функция fdopen() возвращает указатель потока (FILE*), соответствующий дескриптору клиентского сокета (подробнее об этом рассказывается в приложении Б, "Низкоуровневый ввод-вывод"). Для отправки страницы клиенту вызывается обычная функция fprintf(), а функция fflush() предотвращает потерю данных в случае закрытия сокета.

HTML-страница, возвращаемая модулем time.so, содержит в заголовке тэг <meta>, который служит клиенту указанием перезагружать страницу каждые 5 секунд. Благодаря этому клиент всегда будет знать точное время.

11.3.2. Отображение версии Linux

Модуль issue.so (исходный текст приведен в листинге 11.7) выводит информацию о дистрибутиве Linux, с которым работает сервер. Традиционно эта информация хранится в файле /etc/issue. Модель посылает клиенту Web-страницу с содержимым файла, заключенным в тэге <pre></pre>.

Листинг 11.7. (issue.c) Серверный модуль, отображающий информацию о дистрибутиве Linux

#include <fcntl.h>

#include <string.h>

#include <sys/sendfile.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <unistd.h>

#include "server.h"


/* HTML-код начала генерируемой страницы. */

static char* page_start =

 "<html>n"

 " <body>n"

 "  <pre>n";


/* HTML-код конца генерируемой страницы. */


static char* page_end =

 "  </pre>n"

 " </body>n"

 "</html>n";


/* HTML-код страницы, сообщающей о том, что

   при открытии файла /etc/issue произошла ошибка. */

static char* error_page =

 "<html>n"

 " <body>n"

 " 

Error: Could not open /etc/issue.

n"

 " </body>n"

 "</html>n";


/* Сообщение об ошибке. */

static char* error_message =

 "Error reading /etc/issue.";


void module_generate(int fd) {

 int input_fd;

 struct stat file_info;

 int rval;


 /* Открытие файла /etc/issue */

 input_fd = open("/etc/issue", O_RDONLY);

 if (input_fd == -1)

  system_error("open");

 /* Получение информации о файле. */

 rval = fstat(input_fd, &file_info);

 if (rval == -1)

  /* не удалось открыть файл или прочитать данные из него. */

  write(fd, error_page, strlen(error_page));

 else {

  int rval;

  off_t offset = 0;


  /* Запись начала страницы */

  write(fd, page_start, strlen(page_start));

  /* Копирование данных из файла /etc/issue

     в клиентский сокет. */

  rval = sendfile(fd, input_fd, &offset, file_info.st_size);

  if (rval == -1)

   /* При отправке файла /etc/issue произошла ошибка.

      Выводим соответствующее сообщение. */

   write(fd, error_message, strlen(error_message));

  /* Конец страницы. */

  write(fd, page_end, strlen(page_end));

 }

 close(input_fd);

}

Сначала модуль пытается открыть файл /etc/issue. Если это не удалось, клиенту возвращается сообщение об ошибке. В противном случае посылается начальный код HTML-страницы, содержащийся в переменной page_start, затем — содержимое файла /etc/issue (это делается с помощью функции sendfile(), о которой рассказывалось в разделе 8.12. "Функция sendfile(): быстрая передача данных") и, наконец конечный код HTML-страницы, содержащийся в переменной page_end.

Этот модуль можно легко настроить на отправку любого другого файла. Если файл содержит HTML-страницу, переменные page_start и page_end будут не нужны.

11.3.3. Отображение объема свободного дискового пространства

Модуль diskfree.so (исходный текст приведен в листинге 11.8) генерирует страницу с информацией о свободном дисковом пространстве в файловых системах, смонтированных на серверном компьютере. Эта информация берется из выходных данных команды df -h. Как и в модуле issue.so, выходные данные заключаются в тэги <pre></pre>.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*