Blame view

arch/um/drivers/chan_user.c 6.91 KB
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
1
  /*
e99525f97   Jeff Dike   uml: console subs...
2
   * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
   * Licensed under the GPL
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
  #include <stdlib.h>
e99525f97   Jeff Dike   uml: console subs...
6
  #include <unistd.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
  #include <errno.h>
1d2ddcfb1   Jeff Dike   [PATCH] uml: clos...
8
  #include <sched.h>
e99525f97   Jeff Dike   uml: console subs...
9
10
  #include <signal.h>
  #include <termios.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include <sys/ioctl.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include "chan_user.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include "os.h"
89fe64766   Jeff Dike   uml: move userspa...
14
  #include "um_malloc.h"
e99525f97   Jeff Dike   uml: console subs...
15
  #include "user.h"
89fe64766   Jeff Dike   uml: move userspa...
16
17
18
  
  void generic_close(int fd, void *unused)
  {
8e2d10e1e   Jeff Dike   uml: tidy recentl...
19
  	close(fd);
89fe64766   Jeff Dike   uml: move userspa...
20
21
22
23
24
  }
  
  int generic_read(int fd, char *c_out, void *unused)
  {
  	int n;
8e2d10e1e   Jeff Dike   uml: tidy recentl...
25
26
27
28
  	n = read(fd, c_out, sizeof(*c_out));
  	if (n > 0)
  		return n;
  	else if (errno == EAGAIN)
89fe64766   Jeff Dike   uml: move userspa...
29
  		return 0;
8e2d10e1e   Jeff Dike   uml: tidy recentl...
30
  	else if (n == 0)
89fe64766   Jeff Dike   uml: move userspa...
31
  		return -EIO;
8e2d10e1e   Jeff Dike   uml: tidy recentl...
32
  	return -errno;
89fe64766   Jeff Dike   uml: move userspa...
33
  }
8e2d10e1e   Jeff Dike   uml: tidy recentl...
34
  /* XXX Trivial wrapper around write */
89fe64766   Jeff Dike   uml: move userspa...
35
36
37
  
  int generic_write(int fd, const char *buf, int n, void *unused)
  {
c59dbcadd   Jeff Dike   uml: fix console ...
38
39
40
41
42
43
44
45
46
47
  	int err;
  
  	err = write(fd, buf, n);
  	if (err > 0)
  		return err;
  	else if (errno == EAGAIN)
  		return 0;
  	else if (err == 0)
  		return -EIO;
  	return -errno;
89fe64766   Jeff Dike   uml: move userspa...
48
49
50
51
52
  }
  
  int generic_window_size(int fd, void *unused, unsigned short *rows_out,
  			unsigned short *cols_out)
  {
8e2d10e1e   Jeff Dike   uml: tidy recentl...
53
  	struct winsize size;
89fe64766   Jeff Dike   uml: move userspa...
54
  	int ret;
e99525f97   Jeff Dike   uml: console subs...
55
  	if (ioctl(fd, TIOCGWINSZ, &size) < 0)
8e2d10e1e   Jeff Dike   uml: tidy recentl...
56
  		return -errno;
89fe64766   Jeff Dike   uml: move userspa...
57

8e2d10e1e   Jeff Dike   uml: tidy recentl...
58
  	ret = ((*rows_out != size.ws_row) || (*cols_out != size.ws_col));
89fe64766   Jeff Dike   uml: move userspa...
59

8e2d10e1e   Jeff Dike   uml: tidy recentl...
60
61
  	*rows_out = size.ws_row;
  	*cols_out = size.ws_col;
89fe64766   Jeff Dike   uml: move userspa...
62
63
64
65
66
67
68
69
  
  	return ret;
  }
  
  void generic_free(void *data)
  {
  	kfree(data);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70

55c033c1f   Paolo 'Blaisorblade' Giarrusso   [PATCH] uml conso...
71
  int generic_console_write(int fd, const char *buf, int n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  {
ce3b642d4   Jeff Dike   uml: work around ...
73
  	sigset_t old, no_sigio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  	struct termios save, new;
  	int err;
e99525f97   Jeff Dike   uml: console subs...
76
  	if (isatty(fd)) {
ce3b642d4   Jeff Dike   uml: work around ...
77
78
79
80
  		sigemptyset(&no_sigio);
  		sigaddset(&no_sigio, SIGIO);
  		if (sigprocmask(SIG_BLOCK, &no_sigio, &old))
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
  		CATCH_EINTR(err = tcgetattr(fd, &save));
  		if (err)
  			goto error;
  		new = save;
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
85
86
87
  		/*
  		 * The terminal becomes a bit less raw, to handle 
   also as
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  		 * "Carriage Return", not only as "New Line". Otherwise, the new
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
89
90
  		 * line won't start at the first column.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
  		new.c_oflag |= OPOST;
  		CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new));
  		if (err)
  			goto error;
  	}
  	err = generic_write(fd, buf, n, NULL);
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
97
98
99
100
  	/*
  	 * Restore raw mode, in any case; we *must* ignore any error apart
  	 * EINTR, except for debug.
  	 */
ce3b642d4   Jeff Dike   uml: work around ...
101
  	if (isatty(fd)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  		CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save));
ce3b642d4   Jeff Dike   uml: work around ...
103
104
  		sigprocmask(SIG_SETMASK, &old, NULL);
  	}
e99525f97   Jeff Dike   uml: console subs...
105
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  error:
e99525f97   Jeff Dike   uml: console subs...
107
  	return -errno;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
  }
  
  /*
   * UML SIGWINCH handling
   *
42a359e31   Jeff Dike   uml: SIGIO suppor...
113
114
115
116
   * The point of this is to handle SIGWINCH on consoles which have host
   * ttys and relay them inside UML to whatever might be running on the
   * console and cares about the window size (since SIGWINCH notifies
   * about terminal size changes).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
   *
42a359e31   Jeff Dike   uml: SIGIO suppor...
118
119
120
121
   * So, we have a separate thread for each host tty attached to a UML
   * device (side-issue - I'm annoyed that one thread can't have
   * multiple controlling ttys for the purpose of handling SIGWINCH, but
   * I imagine there are other reasons that doesn't make any sense).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
   *
42a359e31   Jeff Dike   uml: SIGIO suppor...
123
124
125
126
127
   * SIGWINCH can't be received synchronously, so you have to set up to
   * receive it as a signal.  That being the case, if you are going to
   * wait for it, it is convenient to sit in sigsuspend() and wait for
   * the signal to bounce you out of it (see below for how we make sure
   * to exit only on SIGWINCH).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
134
135
136
   */
  
  static void winch_handler(int sig)
  {
  }
  
  struct winch_data {
  	int pty_fd;
  	int pipe_fd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
142
143
  };
  
  static int winch_thread(void *arg)
  {
  	struct winch_data *data = arg;
  	sigset_t sigs;
  	int pty_fd, pipe_fd;
8ca842c4b   Jeff Dike   uml: remove os_* ...
144
  	int count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  	char c = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
  	pty_fd = data->pty_fd;
  	pipe_fd = data->pipe_fd;
8ca842c4b   Jeff Dike   uml: remove os_* ...
148
  	count = write(pipe_fd, &c, sizeof(c));
e99525f97   Jeff Dike   uml: console subs...
149
150
151
152
  	if (count != sizeof(c))
  		printk(UM_KERN_ERR "winch_thread : failed to write "
  		       "synchronization byte, err = %d
  ", -count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153

e99525f97   Jeff Dike   uml: console subs...
154
155
  	/*
  	 * We are not using SIG_IGN on purpose, so don't fix it as I thought to
ed1b58d8b   Bodo Stroesser   [PATCH] uml: fix ...
156
  	 * do! If using SIG_IGN, the sigsuspend() call below would not stop on
e99525f97   Jeff Dike   uml: console subs...
157
158
  	 * SIGWINCH.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
  
  	signal(SIGWINCH, winch_handler);
  	sigfillset(&sigs);
ed1b58d8b   Bodo Stroesser   [PATCH] uml: fix ...
162
  	/* Block all signals possible. */
e99525f97   Jeff Dike   uml: console subs...
163
164
165
166
  	if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) {
  		printk(UM_KERN_ERR "winch_thread : sigprocmask failed, "
  		       "errno = %d
  ", errno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
  		exit(1);
  	}
ed1b58d8b   Bodo Stroesser   [PATCH] uml: fix ...
169
170
  	/* In sigsuspend(), block anything else than SIGWINCH. */
  	sigdelset(&sigs, SIGWINCH);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171

e99525f97   Jeff Dike   uml: console subs...
172
173
174
175
  	if (setsid() < 0) {
  		printk(UM_KERN_ERR "winch_thread : setsid failed, errno = %d
  ",
  		       errno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
  		exit(1);
  	}
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
178
  	if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) {
8ca842c4b   Jeff Dike   uml: remove os_* ...
179
180
181
182
183
  		printk(UM_KERN_ERR "winch_thread : TIOCSCTTY failed on "
  		       "fd %d err = %d
  ", pty_fd, errno);
  		exit(1);
  	}
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
184
  	if (tcsetpgrp(pty_fd, os_getpid()) < 0) {
8ca842c4b   Jeff Dike   uml: remove os_* ...
185
186
187
  		printk(UM_KERN_ERR "winch_thread : tcsetpgrp failed on "
  		       "fd %d err = %d
  ", pty_fd, errno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  		exit(1);
  	}
e99525f97   Jeff Dike   uml: console subs...
190
191
  	/*
  	 * These are synchronization calls between various UML threads on the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  	 * host - since they are not different kernel threads, we cannot use
  	 * kernel semaphores. We don't use SysV semaphores because they are
e99525f97   Jeff Dike   uml: console subs...
194
195
  	 * persistent.
  	 */
8ca842c4b   Jeff Dike   uml: remove os_* ...
196
  	count = read(pipe_fd, &c, sizeof(c));
e99525f97   Jeff Dike   uml: console subs...
197
198
  	if (count != sizeof(c))
  		printk(UM_KERN_ERR "winch_thread : failed to read "
8ca842c4b   Jeff Dike   uml: remove os_* ...
199
200
  		       "synchronization byte, err = %d
  ", errno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201

e99525f97   Jeff Dike   uml: console subs...
202
203
204
  	while(1) {
  		/*
  		 * This will be interrupted by SIGWINCH only, since
42a359e31   Jeff Dike   uml: SIGIO suppor...
205
206
  		 * other signals are blocked.
  		 */
ed1b58d8b   Bodo Stroesser   [PATCH] uml: fix ...
207
  		sigsuspend(&sigs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208

8ca842c4b   Jeff Dike   uml: remove os_* ...
209
  		count = write(pipe_fd, &c, sizeof(c));
e99525f97   Jeff Dike   uml: console subs...
210
211
  		if (count != sizeof(c))
  			printk(UM_KERN_ERR "winch_thread : write failed, "
8ca842c4b   Jeff Dike   uml: remove os_* ...
212
213
  			       "err = %d
  ", errno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  	}
  }
42a359e31   Jeff Dike   uml: SIGIO suppor...
216
217
  static int winch_tramp(int fd, struct tty_struct *tty, int *fd_out,
  		       unsigned long *stack_out)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
  {
  	struct winch_data data;
1f96ddb4f   Jeff Dike   [PATCH] uml: clea...
220
  	int fds[2], n, err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
  	char c;
  
  	err = os_pipe(fds, 1, 1);
e99525f97   Jeff Dike   uml: console subs...
224
225
226
227
  	if (err < 0) {
  		printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d
  ",
  		       -err);
1f96ddb4f   Jeff Dike   [PATCH] uml: clea...
228
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
  	}
  
  	data = ((struct winch_data) { .pty_fd 		= fd,
1d2ddcfb1   Jeff Dike   [PATCH] uml: clos...
232
  				      .pipe_fd 		= fds[1] } );
e99525f97   Jeff Dike   uml: console subs...
233
234
  	/*
  	 * CLONE_FILES so this thread doesn't hold open files which are open
42a359e31   Jeff Dike   uml: SIGIO suppor...
235
236
237
  	 * now, but later closed in a different thread.  This is a
  	 * problem with /dev/net/tun, which if held open by this
  	 * thread, prevents the TUN/TAP device from being reused.
1d2ddcfb1   Jeff Dike   [PATCH] uml: clos...
238
  	 */
c43990162   Jeff Dike   uml: simplify hel...
239
  	err = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out);
e99525f97   Jeff Dike   uml: console subs...
240
241
242
243
  	if (err < 0) {
  		printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d
  ",
  		       -err);
1f96ddb4f   Jeff Dike   [PATCH] uml: clea...
244
  		goto out_close;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  	*fd_out = fds[0];
8ca842c4b   Jeff Dike   uml: remove os_* ...
247
  	n = read(fds[0], &c, sizeof(c));
e99525f97   Jeff Dike   uml: console subs...
248
249
250
251
  	if (n != sizeof(c)) {
  		printk(UM_KERN_ERR "winch_tramp : failed to read "
  		       "synchronization byte
  ");
8ca842c4b   Jeff Dike   uml: remove os_* ...
252
253
  		printk(UM_KERN_ERR "read failed, err = %d
  ", errno);
e99525f97   Jeff Dike   uml: console subs...
254
255
256
  		printk(UM_KERN_ERR "fd %d will not support SIGWINCH
  ", fd);
  		err = -EINVAL;
1d2ddcfb1   Jeff Dike   [PATCH] uml: clos...
257
  		goto out_close;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  	}
89df6bfc0   Eduard-Gabriel Munteanu   uml: DEBUG_SHIRQ ...
259
260
  
  	if (os_set_fd_block(*fd_out, 0)) {
e99525f97   Jeff Dike   uml: console subs...
261
262
263
  		printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd "
  		       "non-blocking.
  ");
89df6bfc0   Eduard-Gabriel Munteanu   uml: DEBUG_SHIRQ ...
264
265
266
267
  		goto out_close;
  	}
  
  	return err;
1f96ddb4f   Jeff Dike   [PATCH] uml: clea...
268
269
  
   out_close:
8ca842c4b   Jeff Dike   uml: remove os_* ...
270
271
  	close(fds[1]);
  	close(fds[0]);
1f96ddb4f   Jeff Dike   [PATCH] uml: clea...
272
273
   out:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
  }
  
  void register_winch(int fd, struct tty_struct *tty)
  {
42a359e31   Jeff Dike   uml: SIGIO suppor...
278
279
  	unsigned long stack;
  	int pid, thread, count, thread_fd = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	char c = 1;
e99525f97   Jeff Dike   uml: console subs...
281
  	if (!isatty(fd))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
  		return;
  
  	pid = tcgetpgrp(fd);
6aa802ce6   Jeff Dike   uml: throw out CH...
285
  	if (!is_skas_winch(pid, fd, tty) && (pid == -1)) {
42a359e31   Jeff Dike   uml: SIGIO suppor...
286
287
288
289
290
  		thread = winch_tramp(fd, tty, &thread_fd, &stack);
  		if (thread < 0)
  			return;
  
  		register_winch_irq(thread_fd, fd, thread, tty, stack);
8ca842c4b   Jeff Dike   uml: remove os_* ...
291
  		count = write(thread_fd, &c, sizeof(c));
e99525f97   Jeff Dike   uml: console subs...
292
293
  		if (count != sizeof(c))
  			printk(UM_KERN_ERR "register_winch : failed to write "
8ca842c4b   Jeff Dike   uml: remove os_* ...
294
295
  			       "synchronization byte, err = %d
  ", errno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
  	}
  }