Выбрать главу

 /* Сначала в массив записывается HTML-код начала страницы. */

 vec[vec_length].iov_base = page_start;

 vec[vec_length].iov_len = strlen(page_start);

 ++vec_length;

 /* Получаем список каталогов в файловой системе /proc. */

 proc_listing = opendir("/proc");

 if (proc_listing == NULL)

  system_error("opendir");

 /* Просматриваем список каталогов. */

 while (1) {

  struct dirent* proc_entry;

  const char* name;

  pid_t pid;

  char* process_info;

  /* Переходим к очередному элементу списка. */

  proc_entry = readdir(proc_listing);

  if (proc_entry == NULL)

   /* Достигнут конец списка. */

   break;

  /* Если имя каталога не состоит из одних цифр, то это не

     каталог процесса; пропускаем его. */

  name = proc_entry->d_name;

  if (strspn(name, "0123456789") != strlen(name))

   continue;

  /* Именем каталога является идентификатор процесса. */

  pid = (pid_t)atoi(name);

  /* генерируем HTML-код для строки таблицы, содержащей

     описание данного процесса. */

  process_info = format_process_info(pid);

  if (process_info == NULL)

   /* Произошла какая-то ошибка. Возможно, процесс уже

      завершился. Создаем строку-заглушку. */

   process_info =

    "<tr><td colspan=\"5\">ERROR</td></tr>";

  /* Убеждаемся в том, что в массиве iovec достаточно места

     для записи буфера (один элемент будет добавлен в массив

     по окончании обработки списка процессов). Если места

     не хватает, удваиваем размер массива. */

  if (vec_length == vec_size - 1) {

   vec_size *= 2;

   vec = xrealloc(vec, vec_size - sizeof(struct iovec));

  }

  /* Сохраняем в массиве информацию о процессе. */

  vec[vec_length].iov_base = process_info;

  vec[vec_length].iov_len = strlen(process_info);

  ++vec_length;

 }

 /* Конец обработки списка каталогов */

 closedir(proc_listing);

 /* Добавляем HTML-код конца страницы. */

 vec[vec_length].iov_base = page_end;

 vec[vec_length].iov_len = strlen(page_end);

 ++vec_length;

 /* Передаем всю страницу клиенту. */

 writev(fd, vec, vec_length);

 /* Удаляем выделенные буферы. Первый и последний буферы

    являются статическими, поэтому не должны удаляться. */

 for (i = 1; i < vec_length - 1; ++i)

  free(vec[i].iov_base);

 /* Удаляем массив iovec. */

 free(vec);

}

Задача сбора информации о процессах и представления ее в виде HTML-таблицы разбивается на ряд более простых операций.

■ Функция get_uid_gid() возвращает идентификатор пользователя и группы, которым принадлежит процесс. Для этого вызывается функция stat() (описана в приложении Б, "Низкоуровневый ввод-вывод"), берущая информацию из каталога процесса в файловой системе /proc.

■ Функция get_user_name() возвращает имя пользователя, соответствующее заданному идентификатору. Она просто вызывает библиотечную функцию getpwuid(), которая обращается к файлу /etc/passwd и возвращает копию строки из него. Функция get_group_name() находит имя группы по заданному идентификатору. Она вызывает функцию getgrgid().

■ Функция gеt_program_name() возвращает имя программы, соответствующей заданному процессу. Эта информация извлекается из файла stat, находящегося в каталоге процесса в файловой системе /proc (см. раздел 7.2, "Каталоги процессов"). Мы поступаем так, а не проверяем символические ссылки exe или cmdline, поскольку последние недоступны, если серверный процесс не принадлежит тому же пользователю, что и проверяемый процесс.

■ Функция get_rss() определяет объем резидентной части процесса. Эта информация содержится во втором элементе файла statm (см. раздел 7.2.6, "Статистика использования процессом памяти"), находящегося в каталоге процесса в файловой системе /proc.

■ Функция format_process_info() генерирует набор HTML-тэгов для строки таблицы, представляющей заданный процесс. Здесь вызываются все вышеперечисленные функции.

■ Функция module_generate() генерирует HTML-страницу с таблицей процессов. Выводная информация включает начальный HTML-блок (переменная page_start), строки с информацией о процессах (создаются функцией format_process_info()) и конечный HTML-блок (переменная page_end).