Книга: Разработка приложений в среде Linux. Второе издание

16.6.3. Сложные способы открытия псевдотерминалов

16.6.3. Сложные способы открытия псевдотерминалов

Интерфейс Unix98 для распределения пары псевдотерминала представляет собой следующий набор функций.

#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
int posix_openpt(int oflag);
int grantpt(int fildes);
int unlockpt(int fildes);
char * ptsname(int fildes);

Функция posix_openpt() — это то же, что и открытие устройства /dev/ptmx, но теоретически она более переносима (поскольку везде принимается). Рекомендуется в этот раз использовать open("/dev/ptmx", oflag) для максимальной практической переносимости. Если вы хотите установить один или два флага open() или posix_openpt(), используйте O_RDWR, как обычно; если вы вместо этого не открываете управляющий tty для процесса, используйте O_RDWR | O_NOCTTY. open() или posix_openpt() вернет открытый файловый дескриптор управляющему устройству псевдотерминала. Затем вызовите grantpt() с файловым дескриптором управляющего устройства псевдотерминала, возвращенным из posix_openpt(), для изменения режима и владельца подчиненного компонента псевдотерминала, а потом — unlockpt(), чтобы сделать подчиненный компонент псевдотерминала доступным для открытия. Интерфейс Unix98 для открытия подчиненного устройства псевдотерминала должен просто открыть имя, возвращенное ptsname(). Все эти функции возвращают -1 в случае ошибки, кроме ptsname(), возвращающей в такой ситуации NULL.

Функции в ptypair.c распределяют согласованную пару устройств pty. Пример функции get_master_pty() в строке 22 ptypair.с открывает управляющее устройство pty и возвращает файловый дескриптор родительскому процессу, а также предоставляет имя соответствующему подчиненному компоненту pty. Он сначала испытывает интерфейс Unix98 на распределение управляющего устройства pty, а если это не работает (например, если ядро скомпилировано без поддержки pty Unix98, возможно, для встроенных систем), возвращается к старому интерфейсу стиля BSD. Соответствующая функция get_slave_pty() в строке 87 может быть использована после fork() для открытия соответствующего подчиненного компонента pty.

  1: /* ptypair.c */
  2:
  3: #define _XOPEN_SOURCE 600
  4: #include <errno.h>
  5: #include <fcntl.h>
  6: #include <grp.h>
  7: #include <stdlib.h>
  8: #include <string.h>
  9: #include <sys/types.h>
 10: #include <sys/stat.h>
 11: #include <unistd.h>
 12:
 13:
 14: /* get_master_pty() принимает дважды косвенный символьный указатель на
 15:  * место помещения имени подчиненного компонента pty и возвращает целочисленный
 16:  * файловый дескриптор. Если возвращается значение < 0, значит, возникла ошибка.
 17:  * В противном случае возвращается файловый дескриптор ведущего устройства pty
 18:  * и заполняет *name именем соответствующего подчиненного компонента pty. После
 19:  * открытия подчиненного компонента pty, вы отвечаете за освобождение *name.
 20:  */
 21:
 22: int get_master_pty(char **name) {
 23:  int i, j;
 24:  /* значение по умолчанию, соответствующее ошибке */
 25:  int master = -1;
 26:  char *slavename;
 27:
 28:  master = open("/dev/ptmx", O_RDWR);
 29:  /* Это эквивалентно, хотя и более широко реализовано,
 30:   * но теоретически менее переносимо, следующему:
 31:   * master = posix_openpt(O_RDWR);
 32:   */
 33:
 34:  if (master >= 0 && grantpt(master) >= 0 &&
 35:   unlockpt(master) >= 0) {
 36:   slavename = ptsname(master);
 37:   if (!slavename) {
 38:    close(master);
 39:    master = -1;
 40:    /* сквозной проход для нейтрализации ошибки */
 41:   } else {
 42:    *name = strdup(slavename);
 43:    return master;
 44:   }
 45:  }
 46:
 47:  /* Остаток этой функции — нейтрализация ошибки для старых систем */
 48:
 49:  /* создать фиктивное имя для заполнения */
 50:  *name = strdup("/dev/ptyXX");
 51:
 52:  /* искать неиспользуемый pty */
 53:  for (i=0; i<16 && master <= 0; i++) {
 54:   for (j = 0; j<16 && master <= 0; j++) {
 55:    (*name)[8] = "pqrstuvwxyzPQRST"[i];
 56:    (*name)[9] = "0123456789abcdef"[j];
 57:    /* открыть ведущее устройство pty */
 58:    if ((master = open(*name, O_RDWR)) < 0) {
 59:     if (errno == ENOENT) {
 60:      /* устройства pty исчерпаны */
 61:      free(*name);
 62:      return(master);
 63:     }
 64:    }
 65:   }
 66:  }
 67:
 68:  if ((master < 0) && (i == 16) && (j == 16)) {
 69:   /* необходимо для каждого неудачного pty */
 70:   free(*name);
 71:   return(master);
 72:  }
 73:
 74:  /* Подставляя букву, изменить имя ведущего устройства pty
 75:   * в имени подчиненного компонента pty.
 76:   */
 77:  (*name)[5] = 't';
 78:
 79:  return(master);
 80: }
 81:
 82: /* get_slave_pty() возвращает целочисленный файловый дескриптор.
 83:  * Если возвращается значение < 0, значит, возникла ошибка.
 84:  * В противном случае возвращается файловый дескриптор подчиненного
 85:  * компонента. */
 86:
 87: int get_slave_pty(char * name) {
 88:  struct group *gptr;
 89:  gid_t gid;
 90:  int slave = -1;
 91:
 92:  if (strcmp(name, "/dev/pts/")) {
 93:   /* Интерфейс Unix98 не использовался, необходима
 94:    * специальная обработка полномочий или прав владения.
 95:    *
 96:    * Выполнить chown/chmod для соответствующего pty, если возможно.
 97:    * Это будет работать, только если имеет полномочия root.
 98:    * В качестве альтернативы можно написать и запустить небольшую
 99:    * setuid-программу, которая сделает все это.
100:    *
101:    * В противном случае все проигнорировать и пользоваться
102:    * только интерфейсом Unix98.
103:    */
104:   if ((gptr = getgrnam("tty")) != 0) {
105:    gid = gptr->gr_gid;
106:   } else {
107:    /* если группа tty не существует, не изменять группу
108:     * на подчиненном компоненте pty, а только владельца
109:     */
110:    gid = -1;
111:   }
112:
113:   /* Обратите внимание, что здесь не осуществляется проверка на ошибки.
114:    * Однако если выполняемые действия являются критически важными,
115:    * проверка ошибок должна быть. */
116:   chown(name, getuid(), gid);
117:
118:   /* Этот код делает подчиненный компонент доступным для чтения/записи
119:    * только конкретному пользователю. Если код предназначен для
120:    * интерактивной оболочки, которая должна получать сообщения
121:    * "write" и "wall", добавьте ниже "ИЛИ" с S_IWGRP во второй аргумент.
122:    * В таком случае потребуется перенести эту строку за пределы
123:    * оператора if(), чтобы код мог выполняться для интерфейсов как
124:    * BSD-стиля, так и Unix98-стиля.
125:    */
126:   chmod(name, S_IRUSR|S_IWUSR);
127:  }
128:
129:  /* открыть соответствующий подчиненный компонент pty */
130:  slave = open(name, O_RDWR);
131:
132:  return(slave);
133: }

Функция get_slave_pty() не делает ничего нового. Все функции описываются в других местах этой книги, поэтому здесь они не объясняются.

Оглавление книги


Генерация: 1.276. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз