Если запись не найдена, то getlogin() возвращает NULL и сигнализирует об ошибке с помощью значения errno. Это может произойти в ситуации, когда стандартный ввод процесса не связан с терминалом (ошибка ENOTTY) — к примеру, если процесс является демоном. Еще одна потенциальная причина возникновения ошибки — сессия терминала не записывается в файл utmp; так, например, поступают некоторые программные эмуляторы терминала.
Но даже в случаях, когда одному и тому же идентификатору пользователя в файле /etc/passwd соответствует сразу несколько имен (что довольно необычно), функция getlogin() способна вернуть то самое имя, которое применялось при входе в систему в текущем терминале, так как она берет данные из файла utmp. Для сравнения, вызов getpwuid(getuid()) всегда возвращает первую подходящую запись, найденную в файле /etc/passwd, вне зависимости от того, какое имя использовалось при входе в систему.
Реентерабельная версия функции getlogin() под названием getlogin_r() предусмотрена стандартом SUSv3 и входит в библиотеку glibc.
Для поиска имени, под которым пользователь вошел в систему, можно также применять переменную среды LOGNAME. Однако значение данной переменной может быть изменено самим пользователем; это значит, что оно не подходит для безопасной идентификации.
При написании приложения, позволяющего входить в систему (такого как login или sshd), файлы utmp и wtmp необходимо обновлять следующим образом.
• Когда пользователь входит систему, в файл utmp нужно добавить запись, сигнализирующую об этом факте. Приложение должно проверить, существует ли такая запись для текущего терминала. В случае положительного ответа ее необходимо перезаписать; в противном случае в файл добавляется новая запись. Часто для корректного выполнения данной процедуры достаточно сделать вызов pututxline() (пример его использования показан в листинге 40.3). Итоговая запись формата utmpx должна содержать как минимум поля ut_type, ut_user, ut_tv, ut_pid, ut_id и ut_line. Поле ut_type должно быть равно USER_PROCESS. Поле ut_id должно содержать суффикс имени устройства (то есть терминала или псевдотерминала), с помощью которого пользователь вошел в систему, а в поле ut_line должно находиться его полное имя, но без /dev/ в начале (пример того, как могут выглядеть эти два поля, показан в выводе программы из листинга 40.2). В файл wtmp добавляется запись с точно таким же содержимым.
Имя терминала (представленное полями ut_line и ut_id) играет роль уникального ключа для записей в файле utmp.
• Когда пользователь выходит из системы, запись, добавленная ранее в файл utmp, должна быть стерта. Для этого создается новая запись типа DEAD_PROCESS с теми же полями ut_id и ut_line, которые использовались при входе, но с обнуленным полем ut_user. Она заменяет собой предыдущую запись. Ее копия также добавляется в файл wtmp.
Если не удастся очистить запись utmp при выходе из системы (возможно, из-за сбоя программы), то процесс init сделает это за вас во время следующей перезагрузки; полю ut_type будет присвоено значение DEAD_PROCESS, а другие поля будут обнулены.
Обычно файлы utmp и wtmp защищены таким образом, чтобы только привилегированные пользователи могли вносить в них изменения. Точность вызова getlogin() зависит от целостности файла utmp. Ввиду этой и других причин права доступа к файлам utmp и wtmp никогда не должны позволять запись обычным пользователям.
Что такое сессия входа в систему? Как можно было бы ожидать, в соответствующие учетные файлы заносится информация о входе в систему с помощью программ login, telnet и ssh. Большинство FTP-клиентов также создают подобные учетные данные. Но относятся ли к этой категории записи, которые создаются при запуске каждого окна терминала в системе или, например, вызове команды su? Ответ на этот вопрос может варьироваться в зависимости от конкретной реализации UNIX.
Отдельные эмуляторы терминалов (например, xterm) предоставляют параметры командной строки или другие механизмы для определения, изменяет ли программа учетные файлы, связанные со входом в систему.
Функция pututxline() записывает структуру utmpx, на которую указывает аргумент ut, в файл /var/run/utmp (или какой-то другой, если перед этим был выполнен вызов utmpxname()).
#include
struct utmpx *pututxline(const struct utmpx *ut);
Возвращает указатель на копию успешно измененной записи или NULL при ошибке