strlen(program_name) + strlen(user_name) +
strlen(group_name) + 128;
result = (char*)xmalloc(result_length);
/* Форматирование результата. */
snprintf(result, result_length,
"
%d |
%s |
%s |
%s |
%d |
\n",
(int)pid, program_name, user_name, group_name, rss);
/* Очистка памяти. */
free(program_name);
free(user_name);
free(group_name);
/* Конец работы. */
return result;
}
/* HTML-код начала страницы, содержащей таблицу процессов. */
static char* page_start =
"\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n"
"
\n";
/* HTML-код конца страницы, содержащей таблицу процессов. */
static char* page_end =
"
\n"
"
PID |
Program |
User |
Group |
RSS (KB) |
\n"
"
\n"
"
\n";
void module_generate(int fd) {
size_t i;
DIR* proc_listing;
/* Создание массива iovec. В этот массив помещается выходная
информации, причем массив может увеличиваться динамически. */
/* Число используемых элементов массива */
size_t vec_length = 0;
/* выделенный размер массива */
size_t vec_size = 16;
/* Массив элементов iovec. */
struct iovec* vec =
(struct iovec*)xmalloc(vec_size *
sizeof(struct iovec));
/* Сначала в массив записывается 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 =
"
ERROR |
";
/* Убеждаемся в том, что в массиве 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
, поскольку последние недоступны, если серверный процесс не принадлежит тому же пользователю, что и проверяемый процесс.
Читать дальше