/* Сначала в массив записывается 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).