Version 1.8.0.0
[socat.git] / xio-pty.c
blobe48f8590e5d3cbc85fe82e3f4c74c3c8b5f4cd9a
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, const struct addrdesc *addrdesc);
23 const struct addrdesc xioaddr_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(
32 int argc,
33 const char *argv[],
34 struct opt *opts,
35 int xioflags,
36 xiofile_t *xfd,
37 const struct addrdesc *addrdesc)
39 /* we expect the form: filename */
40 struct single *sfd = &xfd->stream;
41 int ptyfd = -1; /* master */
42 int ttyfd = -1; /* slave */
43 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
44 bool useptmx = false; /* use /dev/ptmx or equivalent */
45 #endif
46 #if HAVE_OPENPTY
47 bool useopenpty = false; /* try only openpty */
48 #endif /* HAVE_OPENPTY */
49 char ptyname[MAXPTYNAMELEN];
50 char *tn = NULL;
51 char *linkname = NULL;
52 bool opt_unlink_close = true; /* remove symlink afterwards */
53 bool wait_slave = false; /* true would be better for many platforms, but
54 some OSes cannot handle this, and for common
55 default behaviour as well as backward
56 compatibility we choose "no" as default */
57 struct timespec pollintv = { PTY_INTERVALL };
59 if (argc != 1) {
60 xio_syntax(argv[0], 0, argc-1, addrdesc->syntax);
61 return STAT_NORETRY;
64 if (sfd->howtoend == END_UNSPEC)
65 sfd->howtoend = END_CLOSE;
67 if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
68 applyopts(sfd, -1, opts, PH_INIT);
70 retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
72 /* trying to set user-early, perm-early etc. here might be useless because
73 file system entry is eventually available only past pty creation */
74 /* name not yet known; umask should not be handled with this function! */
75 /* umask does not affect resulting mode, on Linux 2.4 */
76 applyopts_named("", opts, PH_EARLY); /* umask! */
78 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
79 retropt_bool(opts, OPT_PTMX, &useptmx);
80 #endif
81 #if HAVE_OPENPTY
82 retropt_bool(opts, OPT_OPENPTY, &useopenpty);
83 #endif
85 #if (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC))
86 # if HAVE_OPENPTY
87 useopenpty = !useptmx;
88 # else /* !HAVE_OPENPTY */
89 useptmx = true;
90 # endif /* !HAVE_OPENPTY */
91 #else
92 # if HAVE_OPENPTY
93 useopenpty = true;
94 # endif /* HAVE_OPENPTY */
95 #endif /* ! (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)) */
97 #if HAVE_POLL
98 retropt_bool(opts, OPT_PTY_WAIT_SLAVE, &wait_slave);
99 retropt_timespec(opts, OPT_PTY_INTERVALL, &pollintv);
100 #endif /* HAVE_POLL */
102 if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
103 applyopts2(sfd, -1, opts, PH_INIT, PH_EARLY);
105 applyopts(sfd, -1, opts, PH_PREBIGEN);
107 #if HAVE_OPENPTY
108 if (ptyfd < 0) {
109 int result;
110 if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
111 Error4("openpty(%p, %p, %p, NULL, NULL): %s",
112 &ptyfd, &ttyfd, ptyname, strerror(errno));
113 return -1;
115 Notice1("PTY is %s", ptyname);
117 #endif /* HAVE_OPENPTY */
119 #if defined(HAVE_DEV_PTMX)
120 # define PTMX "/dev/ptmx" /* Linux */
121 #elif HAVE_DEV_PTC
122 # define PTMX "/dev/ptc" /* AIX 4.3.3 */
123 #endif
124 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
125 if (useptmx || ptyfd < 0) {
126 if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
127 Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
128 strerror(errno));
129 /*!*/
130 } else {
131 ;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/
133 if (ptyfd >= 0) {
134 /* we used PTMX before forking */
135 /*0 extern char *ptsname(int);*/
136 #if HAVE_GRANTPT /* AIX, not Linux */
137 if (Grantpt(ptyfd)/*!*/ < 0) {
138 Warn2("grantpt(%d): %s", ptyfd, strerror(errno));
140 #endif /* HAVE_GRANTPT */
141 #if HAVE_UNLOCKPT
142 if (Unlockpt(ptyfd)/*!*/ < 0) {
143 Warn2("unlockpt(%d): %s", ptyfd, strerror(errno));
145 #endif /* HAVE_UNLOCKPT */
146 #if HAVE_PROTOTYPE_LIB_ptsname /* AIX, not Linux */
147 if ((tn = Ptsname(ptyfd)) == NULL) {
148 Warn2("ptsname(%d): %s", ptyfd, strerror(errno));
149 } else {
150 Notice1("PTY is %s", tn);
152 #endif /* HAVE_PROTOTYPE_LIB_ptsname */
153 if (tn == NULL) {
154 if ((tn = Ttyname(ptyfd)) == NULL) {
155 Warn2("ttyname(%d): %s", ptyfd, strerror(errno));
158 ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1);
161 #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
163 if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) {
164 xio_unlink(linkname, E_ERROR);
165 if (Symlink(ptyname, linkname) < 0) {
166 Error3("symlink(\"%s\", \"%s\"): %s",
167 ptyname, linkname, strerror(errno));
169 if (opt_unlink_close) {
170 if ((sfd->unlink_close = strdup(linkname)) == NULL) {
171 Error1("strdup(\"%s\"): out of memory", linkname);
173 sfd->opt_unlink_close = true;
177 applyopts_named(ptyname, opts, PH_PASTOPEN);
178 applyopts_named(ptyname, opts, PH_FD);
180 applyopts_cloexec(ptyfd, opts);/*!*/
181 sfd->dtype = XIODATA_PTY;
183 applyopts(sfd, ttyfd, opts, PH_FD);
186 /* special handling of user-late etc.; with standard behaviour (up to
187 1.7.1.1) they affected /dev/ptmx instead of /dev/pts/N */
188 uid_t uid = -1, gid = -1;
189 mode_t perm;
191 bool dont;
192 dont = retropt_uid(opts, OPT_USER_LATE, &uid);
193 dont &= retropt_gid(opts, OPT_GROUP_LATE, &gid);
195 if (!dont) {
196 if (Chown(ptyname, uid, gid) < 0) {
197 Error4("chown(\"%s\", %d, %d): %s",
198 ptyname, uid, gid, strerror(errno));
202 if (retropt_mode(opts, OPT_PERM_LATE, &perm) == 0) {
203 if (Chmod(ptyname, perm) < 0) {
204 Error3("chmod(\"%s\", %03o): %s",
205 ptyname, perm, strerror(errno));
211 sfd->fd = ptyfd;
212 applyopts(sfd, -1, opts, PH_LATE);
213 if (applyopts_single(sfd, opts, PH_LATE) < 0)
214 return -1;
216 #if HAVE_POLL
217 /* if you can and wish: */
218 if (wait_slave) {
219 /* try to wait until someone opens the slave side of the pty */
220 /* we want to get a HUP (hangup) condition on the pty */
221 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
222 if (useptmx) {
223 ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620);
224 Close(ttyfd);
226 #endif
227 #if HAVE_OPENPTY
228 if (useopenpty) {
229 Close(ttyfd);
231 #endif /* HAVE_OPENPTY */
233 /* now we poll until the HUP vanishes - this indicates a slave conn. */
234 while (true) {
235 struct pollfd ufd;
236 ufd.fd = ptyfd;
237 ufd.events = (POLLHUP);
238 if (Poll(&ufd, 1, 0) < 0) {
239 Error3("poll({%d, 0x%04hu,}, 1, 0): %s",
240 ufd.fd, ufd.events, strerror(errno));
241 /*! close something */
242 return -1;
244 if (!(ufd.revents & POLLHUP)) {
245 break;
247 Nanosleep(&pollintv, NULL);
248 continue;
251 #endif /* HAVE_POLL */
253 return STAT_OK;
255 #endif /* WITH_PTY */