1cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike/*
2e99525f9706900417f37721e601d2b414d41bfeeJeff Dike * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Licensed under the GPL
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <stdlib.h>
7e99525f9706900417f37721e601d2b414d41bfeeJeff Dike#include <unistd.h>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <errno.h>
91d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike#include <sched.h>
10e99525f9706900417f37721e601d2b414d41bfeeJeff Dike#include <signal.h>
11e99525f9706900417f37721e601d2b414d41bfeeJeff Dike#include <termios.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <sys/ioctl.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "chan_user.h"
1437185b33240870719b6b5913a46e6a441f1ae96fAl Viro#include <os.h>
1537185b33240870719b6b5913a46e6a441f1ae96fAl Viro#include <um_malloc.h>
1689fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
1789fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dikevoid generic_close(int fd, void *unused)
1889fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike{
198e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	close(fd);
2089fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike}
2189fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
2289fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dikeint generic_read(int fd, char *c_out, void *unused)
2389fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike{
2489fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike	int n;
2589fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
268e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	n = read(fd, c_out, sizeof(*c_out));
278e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	if (n > 0)
288e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike		return n;
298e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	else if (errno == EAGAIN)
3089fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike		return 0;
318e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	else if (n == 0)
3289fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike		return -EIO;
338e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	return -errno;
3489fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike}
3589fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
368e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike/* XXX Trivial wrapper around write */
3789fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
3889fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dikeint generic_write(int fd, const char *buf, int n, void *unused)
3989fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike{
40c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike	int err;
41c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike
42c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike	err = write(fd, buf, n);
43c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike	if (err > 0)
44c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike		return err;
45c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike	else if (errno == EAGAIN)
46c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike		return 0;
47c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike	else if (err == 0)
48c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike		return -EIO;
49c59dbcadd5d125ba40595612dc91ab18924164d3Jeff Dike	return -errno;
5089fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike}
5189fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
5289fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dikeint generic_window_size(int fd, void *unused, unsigned short *rows_out,
5389fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike			unsigned short *cols_out)
5489fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike{
558e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	struct winsize size;
5689fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike	int ret;
5789fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
58e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (ioctl(fd, TIOCGWINSZ, &size) < 0)
598e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike		return -errno;
6089fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
618e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	ret = ((*rows_out != size.ws_row) || (*cols_out != size.ws_col));
6289fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
638e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	*rows_out = size.ws_row;
648e2d10e1e76d894ec73d66dd63b641ccf5f5fb67Jeff Dike	*cols_out = size.ws_col;
6589fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
6689fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike	return ret;
6789fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike}
6889fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike
6989fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dikevoid generic_free(void *data)
7089fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike{
7189fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike	kfree(data);
7289fe64766ab76b02c65a806b8b5a864652493bd6Jeff Dike}
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7455c033c1f6cdedc350c79c3198b542e3ab496899Paolo 'Blaisorblade' Giarrussoint generic_console_write(int fd, const char *buf, int n)
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
76ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike	sigset_t old, no_sigio;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct termios save, new;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
80e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (isatty(fd)) {
81ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike		sigemptyset(&no_sigio);
82ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike		sigaddset(&no_sigio, SIGIO);
83ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike		if (sigprocmask(SIG_BLOCK, &no_sigio, &old))
84ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike			goto error;
85ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		CATCH_EINTR(err = tcgetattr(fd, &save));
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err)
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		new = save;
90cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike		/*
91cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike		 * The terminal becomes a bit less raw, to handle \n also as
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * "Carriage Return", not only as "New Line". Otherwise, the new
93cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike		 * line won't start at the first column.
94cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike		 */
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		new.c_oflag |= OPOST;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new));
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err)
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = generic_write(fd, buf, n, NULL);
101cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike	/*
102cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike	 * Restore raw mode, in any case; we *must* ignore any error apart
103cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike	 * EINTR, except for debug.
104cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike	 */
105ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike	if (isatty(fd)) {
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save));
107ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike		sigprocmask(SIG_SETMASK, &old, NULL);
108ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike	}
109ce3b642d42f36406112ab474c03d81c5941d9398Jeff Dike
110e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	return err;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
112e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	return -errno;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * UML SIGWINCH handling
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
11842a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * The point of this is to handle SIGWINCH on consoles which have host
11942a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * ttys and relay them inside UML to whatever might be running on the
12042a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * console and cares about the window size (since SIGWINCH notifies
12142a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * about terminal size changes).
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
12342a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * So, we have a separate thread for each host tty attached to a UML
12442a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * device (side-issue - I'm annoyed that one thread can't have
12542a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * multiple controlling ttys for the purpose of handling SIGWINCH, but
12642a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * I imagine there are other reasons that doesn't make any sense).
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
12842a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * SIGWINCH can't be received synchronously, so you have to set up to
12942a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * receive it as a signal.  That being the case, if you are going to
13042a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * wait for it, it is convenient to sit in sigsuspend() and wait for
13142a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * the signal to bounce you out of it (see below for how we make sure
13242a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike * to exit only on SIGWINCH).
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void winch_handler(int sig)
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct winch_data {
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pty_fd;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pipe_fd;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int winch_thread(void *arg)
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct winch_data *data = arg;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sigset_t sigs;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pty_fd, pipe_fd;
1498ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike	int count;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char c = 1;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pty_fd = data->pty_fd;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pipe_fd = data->pipe_fd;
1548ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike	count = write(pipe_fd, &c, sizeof(c));
155e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (count != sizeof(c))
156e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "winch_thread : failed to write "
157e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		       "synchronization byte, err = %d\n", -count);
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
159e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	/*
160e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	 * We are not using SIG_IGN on purpose, so don't fix it as I thought to
161ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser	 * do! If using SIG_IGN, the sigsuspend() call below would not stop on
162e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	 * SIGWINCH.
163e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	 */
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	signal(SIGWINCH, winch_handler);
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sigfillset(&sigs);
167ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser	/* Block all signals possible. */
168e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) {
169e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "winch_thread : sigprocmask failed, "
170e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		       "errno = %d\n", errno);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		exit(1);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
173ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser	/* In sigsuspend(), block anything else than SIGWINCH. */
174ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser	sigdelset(&sigs, SIGWINCH);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
176e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (setsid() < 0) {
177e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "winch_thread : setsid failed, errno = %d\n",
178e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		       errno);
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		exit(1);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
182cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike	if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) {
1838ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		printk(UM_KERN_ERR "winch_thread : TIOCSCTTY failed on "
1848ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		       "fd %d err = %d\n", pty_fd, errno);
1858ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		exit(1);
1868ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike	}
1878ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike
188cb8fa61c2b8b29d422d7310f064d60022f18f89bJeff Dike	if (tcsetpgrp(pty_fd, os_getpid()) < 0) {
1898ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		printk(UM_KERN_ERR "winch_thread : tcsetpgrp failed on "
1908ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		       "fd %d err = %d\n", pty_fd, errno);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		exit(1);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
194e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	/*
195e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	 * These are synchronization calls between various UML threads on the
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * host - since they are not different kernel threads, we cannot use
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * kernel semaphores. We don't use SysV semaphores because they are
198e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	 * persistent.
199e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	 */
2008ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike	count = read(pipe_fd, &c, sizeof(c));
201e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (count != sizeof(c))
202e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "winch_thread : failed to read "
2038ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		       "synchronization byte, err = %d\n", errno);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
205e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	while(1) {
206e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		/*
207e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		 * This will be interrupted by SIGWINCH only, since
20842a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike		 * other signals are blocked.
20942a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike		 */
210ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser		sigsuspend(&sigs);
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2128ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		count = write(pipe_fd, &c, sizeof(c));
213e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		if (count != sizeof(c))
214e99525f9706900417f37721e601d2b414d41bfeeJeff Dike			printk(UM_KERN_ERR "winch_thread : write failed, "
2158ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike			       "err = %d\n", errno);
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2192116bda6ad937d7acb6e2316fd9e65ad6ca01d42Richard Weinbergerstatic int winch_tramp(int fd, struct tty_port *port, int *fd_out,
22042a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike		       unsigned long *stack_out)
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct winch_data data;
2231f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike	int fds[2], n, err;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char c;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = os_pipe(fds, 1, 1);
227e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (err < 0) {
228e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d\n",
229e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		       -err);
2301f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike		goto out;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data = ((struct winch_data) { .pty_fd 		= fd,
2341d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike				      .pipe_fd 		= fds[1] } );
235e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	/*
236e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	 * CLONE_FILES so this thread doesn't hold open files which are open
23742a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike	 * now, but later closed in a different thread.  This is a
23842a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike	 * problem with /dev/net/tun, which if held open by this
23942a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike	 * thread, prevents the TUN/TAP device from being reused.
2401d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike	 */
241c43990162fc7f9d2f15a12797fdc6f9c0905f704Jeff Dike	err = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out);
242e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (err < 0) {
243e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d\n",
244e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		       -err);
2451f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike		goto out_close;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*fd_out = fds[0];
2498ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike	n = read(fds[0], &c, sizeof(c));
250e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (n != sizeof(c)) {
251e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "winch_tramp : failed to read "
252e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		       "synchronization byte\n");
2538ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		printk(UM_KERN_ERR "read failed, err = %d\n", errno);
254e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "fd %d will not support SIGWINCH\n", fd);
255e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		err = -EINVAL;
2561d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike		goto out_close;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
25889df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu
25989df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu	if (os_set_fd_block(*fd_out, 0)) {
260e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd "
261e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		       "non-blocking.\n");
26289df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu		goto out_close;
26389df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu	}
26489df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu
26589df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu	return err;
2661f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike
2671f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike out_close:
2688ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike	close(fds[1]);
2698ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike	close(fds[0]);
2701f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike out:
2711f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike	return err;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2742116bda6ad937d7acb6e2316fd9e65ad6ca01d42Richard Weinbergervoid register_winch(int fd, struct tty_port *port)
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
27642a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike	unsigned long stack;
27742a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike	int pid, thread, count, thread_fd = -1;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char c = 1;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
280e99525f9706900417f37721e601d2b414d41bfeeJeff Dike	if (!isatty(fd))
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pid = tcgetpgrp(fd);
2842116bda6ad937d7acb6e2316fd9e65ad6ca01d42Richard Weinberger	if (is_skas_winch(pid, fd, port)) {
2852116bda6ad937d7acb6e2316fd9e65ad6ca01d42Richard Weinberger		register_winch_irq(-1, fd, -1, port, 0);
28617e052093bcd21eaf9eb6e792cd76fdc4f0e3505Al Viro		return;
28717e052093bcd21eaf9eb6e792cd76fdc4f0e3505Al Viro	}
28817e052093bcd21eaf9eb6e792cd76fdc4f0e3505Al Viro
28917e052093bcd21eaf9eb6e792cd76fdc4f0e3505Al Viro	if (pid == -1) {
2902116bda6ad937d7acb6e2316fd9e65ad6ca01d42Richard Weinberger		thread = winch_tramp(fd, port, &thread_fd, &stack);
29142a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike		if (thread < 0)
29242a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike			return;
29342a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike
2942116bda6ad937d7acb6e2316fd9e65ad6ca01d42Richard Weinberger		register_winch_irq(thread_fd, fd, thread, port, stack);
29542a359e31a0e438b5b978a8f0fecdbd3c86bb033Jeff Dike
2968ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike		count = write(thread_fd, &c, sizeof(c));
297e99525f9706900417f37721e601d2b414d41bfeeJeff Dike		if (count != sizeof(c))
298e99525f9706900417f37721e601d2b414d41bfeeJeff Dike			printk(UM_KERN_ERR "register_winch : failed to write "
2998ca842c4b5cbc70b9180617e9f26b6ac9f40dbb9Jeff Dike			       "synchronization byte, err = %d\n", errno);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
302