File: channel_forkpty.c

package info (click to toggle)
tinyssh 20250501-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,388 kB
  • sloc: ansic: 20,245; sh: 1,582; python: 1,449; makefile: 913
file content (186 lines) | stat: -rw-r--r-- 4,075 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
20150212
20241209 - reformated using clang-format
Jan Mojzis
Public domain.
*/

#include <unistd.h>
#if defined(sun) || defined(__hpux)
#include <sys/stropts.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
extern char *ptsname(int);
extern int grantpt(int);
extern int unlockpt(int);

#include "hasopenpty.h"
#ifdef HASOPENPTY
extern int openpty(int *, int *, char *, struct termios *, struct winsize *);
#endif

#include "haslogintty.h"
#ifdef HASLOGINTTY
extern int login_tty(int);
#endif

#include "e.h"
#include "coe.h"
#include "blocking.h"
#include "global.h"
#include "channel.h"

#ifndef HASLOGINTTY
static int login_tty_(int fd) {

    char *name;

    setsid();

#ifdef TIOCSCTTY
    if (ioctl(fd, TIOCSCTTY, (char *) 0) == -1) return -1;
#endif

    name = ttyname(fd);
    if (!name) return -1;

#ifndef TIOCSCTTY
    if (fd != 0) close(0);
    if (fd != 1) close(1);
    if (fd != 2) close(2);
    close(open(name, O_RDWR));
#endif

    if (dup2(fd, 0) == -1) return -1;
    if (dup2(fd, 1) == -1) return -1;
    if (dup2(fd, 2) == -1) return -1;
    if (fd > 2) close(fd);
    return 0;
}
#endif

#ifndef HASOPENPTY
static int openpty_(int *amaster, int *aslave) {

    int master = -1, slave = -1;
    char *slave_name;
    static const char *fn[] = {"/dev/ptmx", "/dev/ptc", 0};
    long long i;

    for (i = 0; fn[i]; ++i) {
        master = open(fn[i], O_RDWR | O_NOCTTY);
        if (master != -1) break;
    }
    if (master == -1) return -1;

    if (grantpt(master) == -1) {
        close(master);
        return -1;
    }
    if (unlockpt(master) == -1) {
        close(master);
        return -1;
    }
    slave_name = ptsname(master);
    if (!slave_name) {
        close(master);
        return -1;
    }
    slave = open(slave_name, O_RDWR | O_NOCTTY);
    if (slave == -1) {
        close(master);
        return -1;
    }
#if defined(sun) || defined(__hpux)
    ioctl(slave, I_PUSH, "ptem");
    ioctl(slave, I_PUSH, "ldterm");
#endif
#if defined(sun)
    ioctl(slave, I_PUSH, "ttcompat");
#endif

    if (amaster) *amaster = master;
    if (aslave) *aslave = slave;
    return 0;
}
#endif

int channel_openpty(int *amaster, int *aslave) {

#ifdef HASOPENPTY
    if (openpty(amaster, aslave, 0, 0, 0) == -1) return 0;
#else
    if (openpty_(amaster, aslave) == -1) return 0;
#endif

    if (!ttyname(*aslave)) {
        close(*amaster);
        close(*aslave);
        return 0;
    }
    return 1;
}

/*
The 'channel_forkpty' function is used to create a new process
operating in a pseudoterminal. Function sets 3 integers in 'fd[3]':
fd[0] and fd[1] is pseudoterminal fd
fd[2] is always -1
*/

long long channel_forkpty(int fd[3], int master, int slave) {

    long long pid, r;
    char ch;
    int pi[2];

    if (!ttyname(slave)) return -1;
    if (pipe(pi) == -1) return -1;

    fd[0] = fd[1] = master;
    fd[2] = -1;

    pid = fork();
    switch (pid) {
        case -1:
            close(pi[0]);
            close(pi[1]);
            close(slave);
            close(master);
            return -1;
        case 0:
            close(master);
            close(pi[0]);
#ifdef HASLOGINTTY
            if (login_tty(slave) == -1) global_die(111);
#else
            if (login_tty_(slave) == -1) global_die(111);
#endif
            /* Trigger a read event on the other side of the pipe. */
            do { r = write(pi[1], "", 1); } while (r == -1 && errno == EINTR);
            close(pi[1]);

            return 0;
        default:
            close(pi[1]);
            coe_enable(master);
            blocking_disable(master);

            /*
            Wait until child calls login_tty(slave), so that we can safely
            close(slave). Fixes race condition between close(slave) in parent
            and login_tty(slave) in child process.
            */
            do {
                r = read(pi[0], &ch, sizeof ch);
            } while (r == -1 && errno == EINTR);
            close(pi[0]);

            close(slave);
            return pid;
    }
}