Опубликован: 06.12.2004 | Уровень: специалист | Доступ: платный
Лекция 9:

Технологические интерфейсы

Функции для работы с базой данных учетной информации о пользователях

Мы продолжаем рассматривать функции, находящиеся на стыке пользовательских и административных средств.

Стандартом POSIX-2001 предусмотрен набор функций для работы с базой данных учетной информации о пользователях. Эти функции реализуют последовательный просмотр учетных записей ( getutxent() ), поиск в базе ( getutxid(), getutxline() ), модификацию или добавление записей ( pututxline() ), возврат к началу ( setutxent() ) и завершение работы с базой ( endutxent() ) (см. листинг 9.7).

#include <utmpx.h>

struct utmpx *getutxent (void);

struct utmpx *getutxid (
    const struct utmpx *id);

struct utmpx *getutxline (
    const struct utmpx *line);

struct utmpx *pututxline (
    const struct utmpx *utmpx);

void setutxent (void);

void endutxent (void);
Листинг 9.7. Описание функций для работы с базой данных учетной информации о пользователях.

Центральную роль для описываемого набора функций играет структура типа utmpx, которая, согласно стандарту, должна содержать по крайней мере следующие поля.

char     ut_user [];     
    /* Входное имя пользователя         */
char     ut_id [];       
    /* Неспецифицированный              */
    /* инициализационный идентификатор  */    
    /* процесса (например, первое поле  */
    /* в строке файла inittab)          */
char     ut_line [];     
    /* Имя устройства                   */
pid_t     ut_pid;         
    /* Идентификатор процесса           */
short     ut_type;        
    /* Тип записи                       */
struct timeval ut_tv;   
    /* Время создания записи            */

В зависимости от типа записи (элемент ut_type ) определяется подмножество полей, содержащих осмысленные значения. Для пустых записей ( тип   EMPTY ) таких полей нет вообще. Для типов   BOOT_TIME (идентифицирует время загрузки системы), OLD_TIME (время изменения показаний системных часов), NEW_TIME (показания системных часов после изменения) имеет смысл только время создания записи (элемент ut_tv ). Записи   типа   USER_PROCESS идентифицируют пользовательские процессы и содержат полезную информацию в элементах ut_user ( входное имя пользователя ), ut_id, ut_line, ut_pid и ut_tv. Почти такое же подмножество полей имеет смысл для типа   LOGIN_PROCESS (по стандарту он идентифицирует лидера сеанса вошедшего в систему пользователя): ut_user (зависящее от реализации имя входного процесса), ut_id, ut_pid, ut_tv. Наконец, для типов   INIT_PROCESS (идентифицирует процесс, порожденный системным процессом init ) и DEAD_PROCESS (по стандарту он идентифицирует лидера сеанса, завершившего выполнение) имеют смысл лишь элементы ut_id, ut_pid и ut_tv.

Разумеется, реальные размеры массивов, являющихся элементами структуры, можно узнать, применяя к ним на целевой платформе функцию sizeof().

Функция getutxent() читает очередную запись из базы данных учетной информации о пользователях. Если база данных еще не открыта, она открывается. При достижении конца базы данных выполнение функции завершается неудачей и возвращается пустой указатель. Нормальным результатом является указатель на копию прочитанной записи, размещенную в структуре типа utmpx.

Функция getutxid(), начиная с текущей позиции, разыскивает запись   базы данных, в которой поле ut_type соответствует значению id->ut_type. Если элемент id->ut_type равен BOOT_TIME, OLD_TIME, или NEW_TIME, то требуется точное равенство типов. Если же id->ut_type равняется INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS или DEAD_PROCESS, то функция getutxid() вернет указатель на копию первой записи, тип которой равен одному из четырех перечисленных, и поле ut_id соответствует значению id->ut_id.

Функция getutxline() аналогичным образом разыскивает запись, тип которой равен LOGIN_PROCESS или USER_PROCESS, а поле ut_line соответствует значению line->ut_line.

Доступ к записям   базы данных учетной информации о пользователях может быть подвержен зависящему от реализации контролю прав доступа. В частности, система может не выдавать сведений о некоторых или всех пользователях, отличных от вызывающего.

Функция pututxline() записывает указанную utmpx -структуру в базу данных (если вызывающий процесс обладает соответствующими привилегиями). При этом для поиска нужного места используется функция getutxid(), если обнаруживается, что текущая позиция не является подходящей. В случае неудачи поиска   запись добавляется в конец базы данных.

Отметим, что запись   базы данных, к которой было последнее обращение, может сохраняться реализацией в статической структуре (и указатель на нее возвращаться в качестве результата функций), поэтому доступ к нескольким записям требует копирования структур. Далее, при обращении к getutxid() или getutxline() реализация имеет право сначала проанализировать упомянутую статическую структуру и, если та окажется подходящей, не производить поиск, а вернуть ее же. Следовательно, если приложению требуется найти несколько подходящих записей, то после очередного успешного поиска и копирования структуры ее необходимо очистить.

В свою очередь, согласно стандарту, приложение имеет право передать функции pututxline() указатель на статическую структуру, заполненную в результате обращения к getutxent(), getutxid() или getutxline(), предварительно изменив ее требуемым образом. Неявное чтение, осуществляемое функцией pututxline() для определения замещаемой записи, эту структуру не испортит.

Функция setutxent() устанавливает указатель текущей позиции на начало базы данных. Ее следует вызывать перед поиском каждой новой записи, если предполагается, что поиск должен проводиться во всей базе.

Функция endutxent() закрывает базу данных учетной информации о пользователях.

Приведем пример использования описанных функций (см. листинг 9.8). Прочитаем и выведем все записи   базы данных учетной информации о пользователях.

/* * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Пример использования функций для работы           */
/* с базой данных учетной информации о пользователях */
/* * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <stdio.h>
#include <limits.h>
#include <time.h>
#include <utmpx.h>
#include <string.h>

int main (void) {
    struct utmpx *utmpx_ptr;    /* Указатель на текущую запись    */
    char dtbuf [LINE_MAX];      /* Буфер для данных о времени     */
    struct utmpx spat;          /* Шаблон для поиска в базе     */

    /* Прочитаем и распечатаем все записи в базе */
    printf ("Содержимое базы данных учетной информации "
                "о пользователях\n");
    while ((utmpx_ptr = getutxent ()) != NULL) {
        (void) strftime (dtbuf, sizeof (dtbuf), "%c", 
            localtime (&(utmpx_ptr->ut_tv.tv_sec)));
        switch (utmpx_ptr->ut_type) {
                case EMPTY:
            printf ("Пустая запись\n");
            break;
                case BOOT_TIME:
            printf ("Время загрузки системы: %s\n", dtbuf);
            break;
                case OLD_TIME:
            printf ("Время изменения показаний системных "
                        "часов: %s\n", dtbuf);
            break;
                case NEW_TIME:
            printf ("Показания системных часов после "
                        "изменения: %s\n", dtbuf);
            break;
                case USER_PROCESS:
            printf ("Процесс пользователя: %s, идентификатор: "
                        "%d,\n", utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
            printf ("Инициализационный идентификатор процесса: "
                        "%s,\n", utmpx_ptr->ut_id);
            printf ("Имя устройства: %s,\n", 
                        utmpx_ptr->ut_line);
            printf ("Время создания записи: %s\n", dtbuf);
            break;
                case LOGIN_PROCESS:
            printf ("Входной процесс: %s, идентификатор: %d,\n", 
                        utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
            printf ("Инициализационный идентификатор процесса: "
                        "%s,\n",     utmpx_ptr->ut_id);
            printf ("Время создания записи: %s\n", dtbuf);
            break;
                case INIT_PROCESS:
            printf ("Процесс, порожденный системным процессом "
                        "init:\n");
            printf ("Идентификатор: %d,\n", 
                        utmpx_ptr->ut_pid);
            printf ("Инициализационный идентификатор процесса: "
                        "%s,\n", utmpx_ptr->ut_id);
            printf ("Время создания записи: %s\n", dtbuf);
            break;
                case DEAD_PROCESS:
            printf ("Лидер сеанса, завершивший выполнение:\n");
            printf ("Идентификатор: %d,\n", utmpx_ptr->ut_pid);
            printf ("Инициализационный идентификатор процесса: "
                        "%s,\n", utmpx_ptr->ut_id);
            printf ("Время создания записи: %s\n", dtbuf);
            break;
                default:
            printf ("Нестандартный тип записи: %x\n", 
                        utmpx_ptr->ut_type);
            break;
        }
    }

    /* Найдем и распечатаем записи,             */
    /* инициализационный идентификатор которых  */
    /* равняется S4                             */
    spat.ut_type = INIT_PROCESS;
    (void) strncpy (spat.ut_id, "S4", sizeof (spat.ut_id));

    /* Позиционируемся на начало базы */
    setutxent ();

    printf ("Записи, инициализационный идентификатор "
                "которых равняется S4:\n");
    while ((utmpx_ptr = getutxid (&spat)) != NULL) {
        switch (utmpx_ptr->ut_type) {
                case USER_PROCESS:
            printf ("Процесс пользователя: %s, "
                        "идентификатор: %d\n", utmpx_ptr->ut_user, 
                            utmpx_ptr->ut_pid);
            break;
                case LOGIN_PROCESS:
            printf ("Входной процесс: %s, идентификатор: "
                        "%d\n",utmpx_ptr->ut_user, utmpx_ptr->ut_pid);
            break;
                case INIT_PROCESS:
            printf ("Процесс, порожденный системным процессом "
                        "init:\n");
            printf ("Идентификатор: %d\n", utmpx_ptr->ut_pid);
            break;
                case DEAD_PROCESS:
            printf ("Лидер сеанса, завершивший "
                        "выполнение:\n");
            printf ("Идентификатор: %d\n", utmpx_ptr->ut_pid);
            break;
                default:
            printf ("Нестандартный тип результата поиска: "
                        "%x\n", utmpx_ptr->ut_type);
            break;
        }

        /* Обеспечим сдвиг поиска с текущей записи */
        utmpx_ptr->ut_id [0] = 0;
    }

    endutxent ();

    return 0;
}
Листинг 9.8. Пример применения функций для работы с базой данных учетной информации о пользователях.

Фрагмент возможных результатов выполнения приведенной программы на платформе ОС Linux показан на листинге 9.9. Его изучение позволяет лучше уяснить смысл элементов структуры utmpx для разных типов записей и, попутно, увидеть некоторые нестандартности в поведении ОС Linux.

Содержимое базы данных учетной информации о пользователях
    Лидер сеанса, завершивший выполнение:
        Идентификатор: 17,
        Инициализационный идентификатор процесса: si,
        Время создания записи: Tue Apr 27 10:08:42 2004
    Время загрузки системы: Tue Apr 27 10:08:42 2004
    Нестандартный тип записи: 1
    Лидер сеанса, завершивший выполнение:
        Идентификатор: 284,
        Инициализационный идентификатор процесса: l5,
        Время создания записи: Tue Apr 27 10:09:15 2004
    Лидер сеанса, завершивший выполнение:
        Идентификатор: 1115,
        Инициализационный идентификатор процесса: ud,
        Время создания записи: Tue Apr 27 10:09:15 2004
        . . .
    Входной процесс: LOGIN, идентификатор: 1123,
        Инициализационный идентификатор процесса: S3,
        Время создания записи: Tue Apr 27 10:09:15 2004
    Процесс пользователя: galat, идентификатор: 1124,
        Инициализационный идентификатор процесса: S4,
        Имя устройства: ttyS4,
        Время создания записи: Tue Apr 27 12:52:51 2004
    Процесс пользователя: sambor, идентификатор: 1125,
        Инициализационный идентификатор процесса: S5,
        Имя устройства: ttyS5,
        Время создания записи: Tue Apr 27 13:57:31 2004
    Процесс пользователя: kost, идентификатор: 1126,
        Инициализационный идентификатор процесса: S6,
        Имя устройства: ttyS6,
        Время создания записи: Tue Apr 27 10:09:30 2004
        . . .
    Процесс, порожденный системным процессом init:
        Идентификатор: 1128,
        Инициализационный идентификатор процесса: x,
        Время создания записи: Tue Apr 27 10:09:15 2004
        . . .
    Лидер сеанса, завершивший выполнение:
        Идентификатор: 11708,
        Инициализационный идентификатор процесса: /1,
        Время создания записи: Tue Apr 27 11:19:33 2004
        . . .
Записи, инициализационный идентификатор которых равняется S4:
    Процесс пользователя: galat, идентификатор: 1124
Листинг 9.9. Фрагмент возможных результатов выполнения программы, применяющей функции для работы с базой данных учетной информации о пользователях.

Отметим, что нестандартный тип записи   1 соответствует смене уровня выполнения.

Обратим также внимание на то, что в шаблоне поиска, производимого с помощью функции getutxid(), значение поля ut_type задано как INIT_PROCESS, а в результате поиска оно оказалось равным USER_PROCESS (в полном соответствии со стандартом).

Максим Соколов
Максим Соколов
Россия, г. Москва
фыв ыва
фыв ыва
Афганистан