16.1.3. Принадлежность терминала
Существуют две системные базы данных, используемые для отслеживания зарегистрированных пользователей; utmp применяется для пользователей, зарегистрированных в данный момент, a wtmp является записью всех предыдущих регистраций со времени создания файла. Команда who использует базу данных utmp для отображения списка зарегистрированных пользователей, а команда last — базу данных wtmp для отображения списка пользователей, зарегистрированных в системе после регенерации базы данных wtmp. В системах Linux база данных utmp хранится в файле /var/run/utmp, а база данных wtmp — в файле /var/log/wtmp.
Программы, использующие tty для сеансов регистрации пользователей (независимо от того, ассоциируются ли они с графической регистрацией), должны обновлять эти две системные базы данных, пока пользователь явно не сделает иной запрос; например, некоторые пользователи не хотят, чтобы каждый сеанс оболочки, запускаемый ими в эмуляторе терминала в системе X Window, перечислялся как процесс входа. Добавляйте только интерактивные сеансы, поскольку utmp и wtmp не предназначены для регистрации автоматизированных программ. Любые tty, не являющиеся контролирующими терминалами, обычно в базы данных utmp и wtmp не добавляются.
16.1.4. Запись с помощью utempter
Приложения со встроенными средствами безопасности, использующие pty, имеют недостаточно полномочий для модификации файлов баз данных. Эти приложения должны предоставлять опцию для использования простой вспомогательной программы, доступной в большинстве систем Linux и в некоторых других системах, но не стандартизованной — утилиты utempter. Утилита utempter является setgid (или, при необходимости, setuid) с достаточными полномочиями для модификации баз данных utmp и wtmp. Доступ к ней можно получить через простую библиотеку. Утилита utempter проверяет, владеет ли процесс tty, который пытается войти в базу данных utmp до разрешения операции, utempter предназначена только для pty; другие tty обычно открываются демонами с достаточными полномочиями для модификации файлов системных баз данных.
#include <utempter.h>
void addToUtmp(const char *pty, const char *hostname, int ptyfd);
void removeLineFromUtmp(const char *pty, int ptyfd);
void removeFromUtmp(void);
Функция addToUtmp() принимает три аргумента. Первый, pty, является полным путем к добавляемому pty. Второй, hostname, может быть NULL или сетевым именем системы, из которой сетевое подключение использует этот порожденный pty (что запускает ut_host, рассматриваемый в следующем разделе главы). Третий, ptyfd, должен быть открытым файловым дескриптором, ссылающимся на устройство, названное в аргументе pty.
Функция removeLineFromUtmp() принимает два аргумента; они определяются в точности как аргументы с таким же именем, передаваемые функции addToUtmp().
Некоторые существующие приложения записываются с помощью структуры, усложняющей хранение имени и файлового дескриптора для очистки элемента utmp. Из-за этого библиотека utempter поддерживает кэш самого позднего имени устройства и файлового дескриптора, передаваемого addToUtmp(), и удобную функцию removeFromUtmp(), не принимающую никаких аргументов и действующую как removeLineFromUtmp() на кэшированную информацию. Это подходит только для приложений, добавляющих лишь один элемент utmp; более сложные приложения, использующие более одного pty, должны вместо этого применять removeLineFromUtmp().
16.1.5. Запись вручную
Область обработки utmp и wtmp является одной из тех противоречивых областей, где механизмы различаются между системами и меняются на протяжении лет; даже определение информации, доступной в utmp и wtmp, до сих пор различается между системами. Изначально utmp и wtmp были просто массивами структур, записанных на диск; через некоторое время были созданы программные интерфейсы приложений (API) для надежной обработки записей.
По крайней мере, два таких интерфейса были официально стандартизованы; исходный интерфейс utmp (описанный в XSI, XPG2 и SVID2) и расширенный интерфейс utmpx (описанный в XPG4.2 и в поздних версиях POSIX). В Linux доступны оба интерфейса (utmp и utmpx). Интерфейс utmp, широко варьирующийся между машинами, имеет набор определений, которые делают возможной запись переносимого кода. Этот код пользуется преимуществом расширений, предоставляемых glibc. Более строго стандартизованный интерфейс utmpx в данный момент не предоставляет эти определения, но все еще поддерживает расширения.
Интерфейс Linux utmp был изначально задуман как супермножество других существующих интерфейсов utmp, a utmpx был стандартизован как супермножество других существующих интерфейсов utmp; к счастью, оба набора во многом одинаковы. В Linux различие между структурами данных utmp и utmpx заключается лишь в букве x.
Если вы не хотите применять расширения, мы рекомендуем использовать интерфейс utmpx, поскольку он наиболее переносим, пока вы не используете расширения, и строго стандартизован.
Однако если вы хотите применять расширения, мы рекомендуем использовать интерфейс utmp, поскольку glibc предоставляет определения, позволяющие записать переносимый код, пользующийся преимуществами расширений.
Существует также смешанный подход — включите оба заголовочных файла и используйте определения, предоставляемые glibc для интерфейса utmp, чтобы решить, применять ли расширения в интерфейсе utmpx. Этого мы не рекомендуем, поскольку нет гарантии, что заголовочные файлы utmp.h и utmpx.h не будут конфликтовать с системами, не относящимися к Linux. Если ожидается максимальная переносимость и функциональность, в одной из этих областей придется записать некоторые коды дважды — первую версию с использованием utmpx для легкого переноса в новые системы, а вторую с применением #ifdef — для максимальной функциональности в каждой новой системе, в которую вы перемещаетесь.
Здесь документируются лишь наиболее распространенные расширения; документация glibc покрывает все поддерживаемые расширения. Функции utmp работают в терминах struct utmp; мы игнорируем некоторые расширения. Структура и функции utmpx работают точно так же, как структура и функции utmp, поэтому мы не документируем их отдельно. Обратите внимание, что такая же структура используется и для utmp, и для wtmp, поскольку обе базы данных очень похожи.
struct utmp {
short int ut_type; /* тип входа */
pid_t ut_pid; /* идентификатор процесса входа */
char ut_line[UT_LINESIZE]; /* 32 символа */
char ut_id[4]; /* идентификатор inittab */
char ut_user[UT_NAMESIZE]; /* 32 символа */
char ut_host[UT_HOSTSIZE]; /* 256 символов */
struct timeval ut_tv;
struct exit_status ut_exit; /* состояние бездействующего процесса */
long ut_session;
int32_t ut_addr_v6[4];
};
Многие одинаковые элементы являются частью struct utmpx под тем же именем. Элементы, от которых не требуется быть элементами struct utmpx, комментируются как "не стандартизованные POSIX" (ни один из них не стандартизован как часть struct utmp, поскольку сама struct utmp не стандартизована).
Элементы массива символов необязательно являются строками, завершающимися NULL. Используйте sizeof() либо другие ограничения размеров благоразумно.
ut_type Одно из следующих значений: EMPTY, INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, DEAD_PROCESS, BOOT_TIME, NEW_TIME, OLD_TIME, RUN_LVL или ACCOUNTING, каждое из которых описано ниже. ut_tv Время, ассоциированное с событием. Это единственный элемент, кроме ut_type, определяемый POSIX как всегда подходящий для непустых элементов. В некоторых системах вместо этого есть элемент ut_time, измеряемый только в секундах. ut_pid Идентификатор ассоциированного процесса для всех типов, заканчивающихся на _PROCESS. ut_id Идентификатор inittab ассоциированного процесса, для всех типов, заканчивающихся на _PROCESS. Это первое поле в незакомментированных строках файла /etc/inittab, где поля разделены символами :. Сетевые регистрации, не ассоциированные с inittab, могут использовать это по-другому; например, могут включать части информации об устройстве.4 ut_line Строка (базовое имя устройства или номер локального дисплея для X), ассоциированная с процессом. Спецификация POSIX о состоянии ut_line не ясна; она не считает ut_line значащей для LOGIN_PROCESS, но с другой стороны предполагает, что она значащая для LOGIN_PROCESS, и это подтверждается на практике. POSIX утверждает, что ut_line значащая для USER_PROCESS. На практике она также часто значащая для DEAD_PROCESS, в зависимости от происхождения бездействующего процесса. ut_user Обычно это имя зарегистрированного пользователя; это также может быть имя зарегистрированного процесса (обычно LOGIN) в зависимости от значения ut_type. ut_host Имя удаленного хоста, вошедшего в систему или иным образом ассоциированного с этим процессом. Элемент ut_host относится только к USER_PROCESS. Этот элемент не стандартизован POSIX. ut_exit ut_exit.e_exit дает код завершения, что предоставляется макросом WEXITSTATUS(), a ut_exit.e_termination дает сигнал, вызвавший завершение процесса (если он был завершен сигналом), что предоставляется макросом WTERMSIG(). Этот элемент не стандартизован POSIX. ut_session Идентификатор сеанса в системе X Window. Этот элемент не стандартизован POSIX. ut_addr_v6 IP-адрес удаленного хоста в случае активизации USER_PROCESS подключением с удаленного хоста. Используйте функцию inet_ntop() для генерирования печатного содержания. Если первая группа не равна нулю, тогда это адрес IPV4 (inet_ntop() принимает аргумент AF_INET); в противном случае это адрес IPV6 (inet_ntop() принимает аргумент AF_INET6). Этот элемент не стандартизован POSIX.
Элемент ut_type устанавливает, каким образом определяются остальные элементы. Некоторые величины ut_type зарезервированы для записи системной информации; они полезны только для специализированных системных программ и документируются не полностью.