Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Demon's Souls |#13| Storm King
Demon's Souls |#11| Мaneater part 2
Demon's Souls |#10| Мaneater (part 1)
Demon's Souls |#9| Heart of surprises

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Богатырев А. Весь текст 1009.15 Kb

Хрестоматия по программированию на Си в Unix

Предыдущая страница Следующая страница
1 ... 40 41 42 43 44 45 46  47 48 49 50 51 52 53 ... 87
раммы "общего пользования", обращающиеся к этим файлам, должны иметь бит set-uid.
     Откуда же изначально берутся значения uid и ruid (а также gid  и  rgid)  у  про-
цесса?  Они берутся из процесса регистрации пользователя в системе: /etc/getty.  Этот
процесс запускается на каждом терминале как процесс, принадлежащий  суперпользователю
(u_uid==0).  Сначала он запрашивает имя и пароль пользователя:

    #include   /* cc -lc_s */
    #include 
    #include 
    struct passwd *p;
    char userName[80], *pass, *crpass;
    extern char *getpass(), *crypt();
      ...
    /* Не прерываться по сигналам с клавиатуры */
    signal (SIGINT, SIG_IGN);
    for(;;){
      /* Запросить имя пользователя: */
      printf("Login: "); gets(userName);
      /* Запросить пароль (без эха): */
      pass = getpass("Password: ");
      /* Проверить имя: */
      if(p = getpwnam(userName)){
         /* есть такой пользователь */
         crpass = (p->pw_passwd[0]) ? /* если есть пароль */
                  crypt(pass, p->pw_passwd) : pass;
         if( !strcmp( crpass, p->pw_passwd))
                  break; /* верный пароль */
      }
      printf("Login incorrect.\a\n");
    }
    signal (SIGINT, SIG_DFL);

А. Богатырев, 1992-95                  - 239 -                              Си в UNIX

Затем он выполняет:

    // ... запись информации о входе пользователя в систему
    // в файлы /etc/utmp (кто работает в системе сейчас)
    // и       /etc/wtmp (список всех входов в систему)
            ...
    setuid( p->pw_uid ); setgid( p->pw_gid );
    chdir ( p->pw_dir ); /* GO HOME! */
    // эти параметры будут унаследованы
    // интерпретатором команд.
            ...
    // настройка некоторых переменных окружения envp:
    // HOME     = p->pw_dir
    // SHELL    = p->pw_shell
    // PATH     = нечто по умолчанию, вроде :/bin:/usr/bin
    // LOGNAME (USER) = p->pw_name
    // TERM     = считывается из файла
    //            /etc/ttytype по имени устройства av[1]
    // Делается это как-то подобно
    //   char *envp[MAXENV], buffer[512]; int envc = 0;
    //   ...
    //   sprintf(buffer, "HOME=%s", p->pw_dir);
    //   envp[envc++] = strdup(buffer);
    //   ...
    //   envp[envc] = NULL;
            ...
    // настройка кодов доступа к терминалу. Имя устройства
    // содержится в параметре av[1] функции main.
    chown (av[1], p->pw_uid, p->pw_gid);
    chmod (av[1], 0600 );  /* -rw------- */
    // теперь доступ к данному терминалу имеют только
    // вошедший в систему пользователь и суперпользователь.
    // В случае смерти интерпретатора команд,
    // которым заменится getty, процесс init сойдет
    // с системного вызова ожидания wait() и выполнит
    //  chown ( этот_терминал, 2 /*bin*/, 15 /*terminal*/ );
    //  chmod ( этот_терминал, 0600 );
    // и, если терминал числится в файле описания линий
    // связи /etc/inittab как активный (метка respawn), то
    // init перезапустит на этом_терминале новый
    // процесс getty при помощи пары вызовов fork() и exec().
            ...
    // запуск интерпретатора команд:
    execle( *p->pw_shell ? p->pw_shell : "/bin/sh",
                      "-", NULL, envp );

В результате он становится процессом пользователя, вошедшего в  систему.  Таковым  же
после exec-а, выполняемого getty, остается и интерпретатор команд p->pw_shell (обычно
/bin/sh или /bin/csh) и все его потомки.
     На самом деле, в описании регистрации пользователя при входе в  систему,  созна-
тельно  было  допущено  упрощение.  Дело в том, что все то, что мы приписали процессу
getty, в действительности выполняется двумя программами: /etc/getty и /bin/login.
     Сначала процесс getty занимается настройкой параметров линии связи (т.е.  терми-
нала) в соответствии с ее описанием в файле /etc/gettydefs.  Затем он запрашивает имя
пользователя и заменяет себя (при помощи сисвызова exec) процессом  login,  передавая
ему в качестве одного из аргументов полученное имя пользователя.
     Затем login запрашивает пароль, настраивает окружение, и.т.п., то есть именно он
производит  все операции, приведенные выше на схеме.  В конце концов он заменяет себя
интерпретатором команд.
     Такое разделение делается, в частности, для того, чтобы считанный пароль в  слу-
чае  опечатки  не  хранился  бы в памяти процесса getty, а уничтожался бы при очистке

А. Богатырев, 1992-95                  - 240 -                              Си в UNIX

памяти завершившегося процесса login. Таким образом пароль в истинном,  незашифрован-
ном  виде  хранится  в  системе  минимальное время, что затрудняет его подсматривание
средствами электронного или программного шпионажа.  Кроме того, это  позволяет  изме-
нять систему проверки паролей не изменяя программу инициализации терминала getty.
     Имя, под которым пользователь вошел в систему на данном терминале, можно  узнать
вызовом стандартной функции
     char *getlogin();
Эта функция не проверяет uid процесса, а просто извлекает запись про данный  терминал
из файла /etc/utmp.
     Наконец отметим, что владелец файла устанавливается  при  создании  этого  файла
(вызовами  creat  или mknod), и полагается равным эффективному идентификатору создаю-
щего процесса.

    di_uid = u_uid;     di_gid = u_gid;

6.8.4.  Напишите программу, узнающую у системы  и  распечатывающую:  номер  процесса,
номер  и  имя своего владельца, номер группы, название и тип терминала на котором она
работает (из переменной окружения TERM).

6.9.  Блокировка доступа к файлам.
     В базах данных нередко встречается ситуация одновременного доступа к одним и тем
же  данным. Допустим, что в некотором файле хранятся данные, которые могут читаться и
записываться произвольным числом процессов.
-    Допустим, что процесс A изменяет некоторую область файла, в то время как процесс
     B  пытается  прочесть  ту же область.  Итогом такого соревнования может быть то,
     что процесс B прочтет неверные данные.
-    Допустим, что процесс A изменяет некоторую область файла, в то время как процесс
     C  также  изменяет  ту  же  самую  область.  В итоге эта область может содержать
     неверные данные (часть - от процесса A, часть - от C).
     Ясно, что требуется механизм синхронизации  процессов,  позволяющий  не  пускать
другой  процесс (процессы) читать и/или записывать данные в указанной области.  Меха-
низмов синхронизации в UNIX существует множество: от семафоров до блокировок областей
файла. О последних мы и будем тут говорить.
     Прежде всего отметим, что блокировки файла носят в UNIX необязательный характер.
То есть, программа не использующая вызовов синхронизации, будет иметь доступ к данным
без каких либо ограничений. Увы.  Таким образом,  программы,  собирающиеся  корректно
пользоваться  общими  данными,  должны  все  использовать - и при том один и тот же -
механизм синхронизации: заключить между собой "джентльменское соглашение".

6.9.1.  Блокировка устанавливается при помощи вызова

    flock_t lock;

    fcntl(fd, operation, &lock);

Здесь operation может быть одним из трех:
F_SETLK
     Устанавливает или снимает замок, описываемый структурой lock.  Структура flock_t
     имеет такие поля:

         short  l_type;
         short  l_whence;
         off_t  l_start;
         size_t l_len;

         long   l_sysid;
         pid_t  l_pid;

l_type
     тип блокировки:

А. Богатырев, 1992-95                  - 241 -                              Си в UNIX

         F_RDLCK - на чтение;
         F_WRLCK - на запись;
         F_UNLCK - снять все замки.

l_whence, l_start, l_len
     описывают   сегмент   файла,   на   который    ставится    замок:    от    точки
     lseek(fd,l_start,l_whence);  длиной  l_len  байт.   Здесь  l_whence  может быть:
     SEEK_SET, SEEK_CUR, SEEK_END.  l_len равное нулю означает "до конца файла".  Так
     если все три параметра равны 0, то будет заблокирован весь файл.
F_SETLKW
     Устанавливает или снимает замок, описываемый структурой lock.   При  этом,  если
     замок  на  область,  пересекающуюся с указанной уже кем-то установлен, то сперва
     дождаться снятия этого замка.

         Пытаемся   | Нет        Уже есть                   уже есть
         поставить  | чужих      замок                      замок
         замок на   | замков     на READ                    на WRITE
         -----------|---------------------------------------------------------------
         READ       | читать     читать                     ждать;запереть;читать
         WRITE      | записать   ждать;запереть;записать    ждать;запереть;записать
         UNLOCK     | отпереть   отпереть                   отпереть

-    Если кто-то читает сегмент файла, то другие тоже могут его читать свободно,  ибо
     чтение не изменяет файла.
-    Если же кто-то записывает файл - то все  остальные  должны  дождаться  окончания
     записи и разблокировки.
-    Если кто-то читает сегмент, а другой процесс собрался изменить  (записать)  этот
     сегмент, то этот другой процесс обязан дождаться окончания чтения первым.
-    В момент, обозначенный как отпереть - будятся процессы, ждущие разблокировки,  и
     ровно один из них получает доступ (может установить свою блокировку).  Порядок -
     кто из них будет первым - вообще говоря не определен.
F_GETLK
     Запрашиваем возможность установить замок, описанный в lock.
-    Если мы можем установить такой замок (не заперто никем),  то  в  структуре  lock
     поле l_type становится равным F_UNLCK и поле l_whence равным SEEK_SET.
-    Если замок уже кем-то установлен (и вызов F_SETLKW заблокировал бы наш  процесс,
     привел  бы  к  ожиданию), мы получаем информацию о чужом замке в структуру lock.
     При этом в поле l_pid заносится идентификатор процесса, создавшего этот замок, а
     в  поле  l_sysid - идентификатор машины (поскольку блокировка файлов поддержива-
     ется через сетевые файловые системы).
     Замки автоматически снимаются при закрытии дескриптора файла.  Замки не наследу-
ются порожденным процессом при вызове fork.

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 

    char DataFile [] = "data.xxx";
    char info     [] = "abcdefghijklmnopqrstuvwxyz";
    #define OFFSET 5
    #define SIZE   12

    #define PAUSE 2

    int trial = 1;
    int fd, pid;
    char buffer[120], myname[20];
    void writeAccess(), readAccess();

А. Богатырев, 1992-95                  - 242 -                              Си в UNIX

    void fcleanup(int nsig){
            unlink(DataFile);
            printf("cleanup:%s\n", myname);
            if(nsig) exit(0);
    }

    int main(){
            int i;

            fd = creat(DataFile, 0644);
            write(fd, info, strlen(info));
            close(fd);

            signal(SIGINT, fcleanup);

            sprintf(myname, fork() ? "B-%06d" : "A-%06d", pid = getpid());

            srand(time(NULL)+pid);
            printf("%s:started\n", myname);

            fd = open(DataFile, O_RDWR|O_EXCL);
            printf("%s:opened %s\n", myname, DataFile);

            for(i=0; i < 30; i++){
                    if(rand()%2)    readAccess();
                    else            writeAccess();
            }

            close(fd);

            printf("%s:finished\n", myname);

            wait(NULL);
            fcleanup(0);
            return 0;
    }

А. Богатырев, 1992-95                  - 243 -                              Си в UNIX

    void writeAccess(){
            flock_t lock;

            printf("Write:%s #%d\n", myname, trial);

            lock.l_type   = F_WRLCK;
            lock.l_whence = SEEK_SET;
            lock.l_start  = (off_t)  OFFSET;
Предыдущая страница Следующая страница
1 ... 40 41 42 43 44 45 46  47 48 49 50 51 52 53 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама