OpenSSL options min-version, max-version
[socat.git] / xio-pty.c
blobe502f262f14e8c574ede537fef78b095a52fb040
1 /* source: xio-pty.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for creating pty addresses */
7 #include "xiosysincludes.h"
8 #include "xioopen.h"
10 #include "xio-named.h"
11 #include "xio-termios.h"
14 #if WITH_PTY
16 /* here define the preferred polling intervall, in seconds */
17 #define PTY_INTERVALL 1,0 /* for struct timespec */
19 #define MAXPTYNAMELEN 64
21 static int xioopen_pty(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3);
23 const struct addrdesc addr_pty = { "pty", 3, xioopen_pty, GROUP_NAMED|GROUP_FD|GROUP_TERMIOS|GROUP_PTY, 0, 0, 0 HELP("") };
25 const struct optdesc opt_symbolic_link = { "symbolic-link", "link", OPT_SYMBOLIC_LINK, GROUP_PTY, PH_LATE, TYPE_FILENAME, OFUNC_SPEC, 0, 0 };
26 #if HAVE_POLL
27 const struct optdesc opt_pty_wait_slave = { "pty-wait-slave", "wait-slave", OPT_PTY_WAIT_SLAVE, GROUP_PTY, PH_EARLY, TYPE_BOOL, OFUNC_SPEC, 0, 0 };
28 const struct optdesc opt_pty_intervall = { "pty-interval", NULL, OPT_PTY_INTERVALL, GROUP_PTY, PH_EARLY, TYPE_TIMESPEC, OFUNC_SPEC, 0, 0 };
29 #endif /* HAVE_POLL */
31 static int xioopen_pty(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) {
32 /* we expect the form: filename */
33 int ptyfd = -1, ttyfd = -1;
34 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
35 bool useptmx = false; /* use /dev/ptmx or equivalent */
36 #endif
37 #if HAVE_OPENPTY
38 bool useopenpty = false; /* try only openpty */
39 #endif /* HAVE_OPENPTY */
40 char ptyname[MAXPTYNAMELEN];
41 char *tn = NULL;
42 char *linkname = NULL;
43 bool opt_unlink_close = true; /* remove symlink afterwards */
44 bool wait_slave = false; /* true would be better for many platforms, but
45 some OSes cannot handle this, and for common
46 default behaviour as well as backward
47 compatibility we choose "no" as default */
48 struct timespec pollintv = { PTY_INTERVALL };
50 if (argc != 1) {
51 Error2("%s: wrong number of parameters (%d instead of 0)", argv[0], argc-1);
54 xfd->stream.howtoend = END_CLOSE;
56 if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1;
57 applyopts(-1, opts, PH_INIT);
59 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
61 /* trying to set user-early, perm-early etc. here might be useless because
62 file system entry is eventually available only past pty creation */
63 /* name not yet known; umask should not be handled with this function! */
64 /* umask does not affect resulting mode, on Linux 2.4 */
65 applyopts_named("", opts, PH_EARLY); /* umask! */
67 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
68 retropt_bool(opts, OPT_PTMX, &useptmx);
69 #endif
70 #if HAVE_OPENPTY
71 retropt_bool(opts, OPT_OPENPTY, &useopenpty);
72 #endif
74 #if (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC))
75 # if HAVE_OPENPTY
76 useopenpty = !useptmx;
77 # else /* !HAVE_OPENPTY */
78 useptmx = true;
79 # endif /* !HAVE_OPENPTY */
80 #else
81 # if HAVE_OPENPTY
82 useopenpty = true;
83 # endif /* HAVE_OPENPTY */
84 #endif /* ! (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)) */
86 #if HAVE_POLL
87 retropt_bool(opts, OPT_PTY_WAIT_SLAVE, &wait_slave);
88 retropt_timespec(opts, OPT_PTY_INTERVALL, &pollintv);
89 #endif /* HAVE_POLL */
91 if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1;
92 applyopts2(-1, opts, PH_INIT, PH_EARLY);
94 applyopts(-1, opts, PH_PREBIGEN);
96 #if defined(HAVE_DEV_PTMX)
97 # define PTMX "/dev/ptmx" /* Linux */
98 #elif HAVE_DEV_PTC
99 # define PTMX "/dev/ptc" /* AIX 4.3.3 */
100 #endif
101 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
102 if (useptmx) {
103 if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
104 Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
105 strerror(errno));
106 /*!*/
107 } else {
108 ;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/
110 if (ptyfd >= 0 && ttyfd < 0) {
111 /* we used PTMX before forking */
112 /*0 extern char *ptsname(int);*/
113 #if HAVE_GRANTPT /* AIX, not Linux */
114 if (Grantpt(ptyfd)/*!*/ < 0) {
115 Warn2("grantpt(%d): %s", ptyfd, strerror(errno));
117 #endif /* HAVE_GRANTPT */
118 #if HAVE_UNLOCKPT
119 if (Unlockpt(ptyfd)/*!*/ < 0) {
120 Warn2("unlockpt(%d): %s", ptyfd, strerror(errno));
122 #endif /* HAVE_UNLOCKPT */
123 #if HAVE_PROTOTYPE_LIB_ptsname /* AIX, not Linux */
124 if ((tn = Ptsname(ptyfd)) == NULL) {
125 Warn2("ptsname(%d): %s", ptyfd, strerror(errno));
126 } else {
127 Notice1("PTY is %s", tn);
129 #endif /* HAVE_PROTOTYPE_LIB_ptsname */
130 if (tn == NULL) {
131 if ((tn = Ttyname(ptyfd)) == NULL) {
132 Warn2("ttyname(%d): %s", ptyfd, strerror(errno));
135 ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1);
138 #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
139 #if HAVE_OPENPTY
140 if (ptyfd < 0) {
141 int result;
142 if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
143 Error4("openpty(%p, %p, %p, NULL, NULL): %s",
144 &ptyfd, &ttyfd, ptyname, strerror(errno));
145 return -1;
147 Notice1("PTY is %s", ptyname);
149 #endif /* HAVE_OPENPTY */
151 if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) {
152 if (Unlink(linkname) < 0 && errno != ENOENT) {
153 Error2("unlink(\"%s\"): %s", linkname, strerror(errno));
155 if (Symlink(ptyname, linkname) < 0) {
156 Error3("symlink(\"%s\", \"%s\"): %s",
157 ptyname, linkname, strerror(errno));
159 if (opt_unlink_close) {
160 if ((xfd->stream.unlink_close = strdup(linkname)) == NULL) {
161 Error1("strdup(\"%s\"): out of memory", linkname);
163 xfd->stream.opt_unlink_close = true;
167 applyopts_named(ptyname, opts, PH_PASTOPEN);
168 applyopts_named(ptyname, opts, PH_FD);
170 applyopts_cloexec(ptyfd, opts);/*!*/
171 xfd->stream.dtype = XIODATA_PTY;
173 applyopts(ptyfd, opts, PH_FD);
176 /* special handling of user-late etc.; with standard behaviour (up to
177 1.7.1.1) they affected /dev/ptmx instead of /dev/pts/N */
178 uid_t uid = -1, gid = -1;
179 mode_t perm;
181 bool dont;
182 dont = retropt_uid(opts, OPT_USER_LATE, &uid);
183 dont &= retropt_gid(opts, OPT_GROUP_LATE, &gid);
185 if (!dont) {
186 if (Chown(ptyname, uid, gid) < 0) {
187 Error4("chown(\"%s\", %d, %d): %s",
188 ptyname, uid, gid, strerror(errno));
192 if (retropt_mode(opts, OPT_PERM_LATE, &perm) == 0) {
193 if (Chmod(ptyname, perm) < 0) {
194 Error3("chmod(\"%s\", %03o): %s",
195 ptyname, perm, strerror(errno));
201 xfd->stream.fd = ptyfd;
202 applyopts(ptyfd, opts, PH_LATE);
203 if (applyopts_single(&xfd->stream, opts, PH_LATE) < 0) return -1;
205 #if HAVE_POLL
206 /* if you can and wish: */
207 if (wait_slave) {
208 /* try to wait until someone opens the slave side of the pty */
209 /* we want to get a HUP (hangup) condition on the pty */
210 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
211 if (useptmx) {
212 ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620);
213 Close(ttyfd);
215 #endif
216 #if HAVE_OPENPTY
217 if (useopenpty) {
218 Close(ttyfd);
220 #endif /* HAVE_OPENPTY */
222 /* now we poll until the HUP vanishes - this indicates a slave conn. */
223 while (true) {
224 struct pollfd ufd;
225 ufd.fd = ptyfd;
226 ufd.events = (POLLHUP);
227 if (Poll(&ufd, 1, 0) < 0) {
228 Error3("poll({%d, 0x%04hu,}, 1, 0): %s",
229 ufd.fd, ufd.events, strerror(errno));
230 /*! close something */
231 return -1;
233 if (!(ufd.revents & POLLHUP)) {
234 break;
236 Nanosleep(&pollintv, NULL);
237 continue;
240 #endif /* HAVE_POLL */
242 return STAT_OK;
244 #endif /* WITH_PTY */