Blame view

arch/um/drivers/chan_user.c 6.95 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
15
16
17
  #include "um_malloc.h"
  
  void generic_close(int fd, void *unused)
  {
8e2d10e1e   Jeff Dike   uml: tidy recentl...
18
  	close(fd);
89fe64766   Jeff Dike   uml: move userspa...
19
20
21
22
23
  }
  
  int generic_read(int fd, char *c_out, void *unused)
  {
  	int n;
8e2d10e1e   Jeff Dike   uml: tidy recentl...
24
25
26
27
  	n = read(fd, c_out, sizeof(*c_out));
  	if (n > 0)
  		return n;
  	else if (errno == EAGAIN)
89fe64766   Jeff Dike   uml: move userspa...
28
  		return 0;
8e2d10e1e   Jeff Dike   uml: tidy recentl...
29
  	else if (n == 0)
89fe64766   Jeff Dike   uml: move userspa...
30
  		return -EIO;
8e2d10e1e   Jeff Dike   uml: tidy recentl...
31
  	return -errno;
89fe64766   Jeff Dike   uml: move userspa...
32
  }
8e2d10e1e   Jeff Dike   uml: tidy recentl...
33
  /* XXX Trivial wrapper around write */
89fe64766   Jeff Dike   uml: move userspa...
34
35
36
  
  int generic_write(int fd, const char *buf, int n, void *unused)
  {
c59dbcadd   Jeff Dike   uml: fix console ...
37
38
39
40
41
42
43
44
45
46
  	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...
47
48
49
50
51
  }
  
  int generic_window_size(int fd, void *unused, unsigned short *rows_out,
  			unsigned short *cols_out)
  {
8e2d10e1e   Jeff Dike   uml: tidy recentl...
52
  	struct winsize size;
89fe64766   Jeff Dike   uml: move userspa...
53
  	int ret;
e99525f97   Jeff Dike   uml: console subs...
54
  	if (ioctl(fd, TIOCGWINSZ, &size) < 0)
8e2d10e1e   Jeff Dike   uml: tidy recentl...
55
  		return -errno;
89fe64766   Jeff Dike   uml: move userspa...
56

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

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

55c033c1f   Paolo 'Blaisorblade' Giarrusso   [PATCH] uml conso...
70
  int generic_console_write(int fd, const char *buf, int n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  {
ce3b642d4   Jeff Dike   uml: work around ...
72
  	sigset_t old, no_sigio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  	struct termios save, new;
  	int err;
e99525f97   Jeff Dike   uml: console subs...
75
  	if (isatty(fd)) {
ce3b642d4   Jeff Dike   uml: work around ...
76
77
78
79
  		sigemptyset(&no_sigio);
  		sigaddset(&no_sigio, SIGIO);
  		if (sigprocmask(SIG_BLOCK, &no_sigio, &old))
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
  		CATCH_EINTR(err = tcgetattr(fd, &save));
  		if (err)
  			goto error;
  		new = save;
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
84
85
86
  		/*
  		 * The terminal becomes a bit less raw, to handle 
   also as
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  		 * "Carriage Return", not only as "New Line". Otherwise, the new
cb8fa61c2   Jeff Dike   uml: arch/um/driv...
88
89
  		 * line won't start at the first column.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
  		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...
96
97
98
99
  	/*
  	 * Restore raw mode, in any case; we *must* ignore any error apart
  	 * EINTR, except for debug.
  	 */
ce3b642d4   Jeff Dike   uml: work around ...
100
  	if (isatty(fd)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
  		CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save));
ce3b642d4   Jeff Dike   uml: work around ...
102
103
  		sigprocmask(SIG_SETMASK, &old, NULL);
  	}
e99525f97   Jeff Dike   uml: console subs...
104
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
  error:
e99525f97   Jeff Dike   uml: console subs...
106
  	return -errno;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
  }
  
  /*
   * UML SIGWINCH handling
   *
42a359e31   Jeff Dike   uml: SIGIO suppor...
112
113
114
115
   * 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
116
   *
42a359e31   Jeff Dike   uml: SIGIO suppor...
117
118
119
120
   * 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
121
   *
42a359e31   Jeff Dike   uml: SIGIO suppor...
122
123
124
125
126
   * 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
127
128
129
130
131
132
133
134
135
   */
  
  static void winch_handler(int sig)
  {
  }
  
  struct winch_data {
  	int pty_fd;
  	int pipe_fd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
142
  };
  
  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_* ...
143
  	int count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  	char c = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
  	pty_fd = data->pty_fd;
  	pipe_fd = data->pipe_fd;
8ca842c4b   Jeff Dike   uml: remove os_* ...
147
  	count = write(pipe_fd, &c, sizeof(c));
e99525f97   Jeff Dike   uml: console subs...
148
149
150
151
  	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
152

e99525f97   Jeff Dike   uml: console subs...
153
154
  	/*
  	 * We are not using SIG_IGN on purpose, so don't fix it as I thought to
ed1b58d8b   Bodo Stroesser   [PATCH] uml: fix ...
155
  	 * do! If using SIG_IGN, the sigsuspend() call below would not stop on
e99525f97   Jeff Dike   uml: console subs...
156
157
  	 * SIGWINCH.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
  
  	signal(SIGWINCH, winch_handler);
  	sigfillset(&sigs);
ed1b58d8b   Bodo Stroesser   [PATCH] uml: fix ...
161
  	/* Block all signals possible. */
e99525f97   Jeff Dike   uml: console subs...
162
163
164
165
  	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
166
167
  		exit(1);
  	}
ed1b58d8b   Bodo Stroesser   [PATCH] uml: fix ...
168
169
  	/* In sigsuspend(), block anything else than SIGWINCH. */
  	sigdelset(&sigs, SIGWINCH);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170

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

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

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