Функции в 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() не делает ничего нового. Все функции описываются в других местах этой книги, поэтому здесь они не объясняются.
16.6.4. Примеры псевдотерминалов
Возможно, одной из самых простых программ, которая может быть написана для использования pty, является программа, открывающая пару pty и запускающая оболочку на подчиненном компоненте pty, соединяя его с управляющим устройством pty. Написав эту программу, вы можете расширять ее любым подходящим способом, forkptytest.с является примером использования функции forkpty(), a ptytest.с — это пример, который использует функции, определенные в ptypair.с, и является несколько более сложным.
1: /* forkptytest.с */
2:
3: #include <errno.h>
4: #include <signal.h>
5: #include <stdio.h>
6: #include <stdlib.h>
7: #include <sys/ioctl.h>
8: #include <sys/poll.h>
9: #include <termios.h>
10: #include <unistd.h>
11: #include <pty.h>
12:
13:
14: volatile int propagate_sigwinch = 0;
15:
16: /* sigwinch_handler
17: * распространяет изменения размеров окна из входного файлового
18: * дескриптора на ведущую сторону pty.
19: */
20: void sigwinch_handler(int signal) {
21: propagate_sigwinch = 1;
22: }
23:
24:
25: /* forkptytest пытается открыть пару pty с запуском оболочки
26: * на подчиненной стороне pty.
27: */
28: int main(void) {
29: int master;
30: int pid;
31: struct pollfd ufds[2];
32: int i;
33: #define BUFSIZE 1024
34: char buf[1024];
35: struct termios ot, t;
36: struct winsize ws;
37: int done = 0;
38: struct sigaction act;
39:
40: if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
41: perror("ptypair: не удается получить размеры окна");
42: exit(1);
43: }
44:
45: if ((pid = forkpty(&master, NULL, NULL, &ws)) < 0) {
46: perror("ptypair");
47: exit(1);
48: }
49:
50: if (pid == 0) {
51: /* запустить оболочку */
52: execl("/bin/sh", "/bin/sh", 0);
53:
54: /* сюда управление никогда не попадет */