diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/vt_ioctl.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/vt_ioctl.c')
-rw-r--r-- | drivers/char/vt_ioctl.c | 1201 |
1 files changed, 1201 insertions, 0 deletions
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c new file mode 100644 index 000000000000..5d386f4bea49 --- /dev/null +++ b/drivers/char/vt_ioctl.c | |||
@@ -0,0 +1,1201 @@ | |||
1 | /* | ||
2 | * linux/drivers/char/vt_ioctl.c | ||
3 | * | ||
4 | * Copyright (C) 1992 obz under the linux copyright | ||
5 | * | ||
6 | * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993 | ||
7 | * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994 | ||
8 | * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995 | ||
9 | * Some code moved for less code duplication - Andi Kleen - Mar 1997 | ||
10 | * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001 | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/tty.h> | ||
18 | #include <linux/timer.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/kd.h> | ||
21 | #include <linux/vt.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/major.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/console.h> | ||
27 | |||
28 | #include <asm/io.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | |||
31 | #include <linux/kbd_kern.h> | ||
32 | #include <linux/vt_kern.h> | ||
33 | #include <linux/kbd_diacr.h> | ||
34 | #include <linux/selection.h> | ||
35 | |||
36 | static char vt_dont_switch; | ||
37 | extern struct tty_driver *console_driver; | ||
38 | |||
39 | #define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count) | ||
40 | #define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_cons[i].d == sel_cons) | ||
41 | |||
42 | /* | ||
43 | * Console (vt and kd) routines, as defined by USL SVR4 manual, and by | ||
44 | * experimentation and study of X386 SYSV handling. | ||
45 | * | ||
46 | * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and | ||
47 | * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console, | ||
48 | * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will | ||
49 | * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to | ||
50 | * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using | ||
51 | * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing | ||
52 | * to the current console is done by the main ioctl code. | ||
53 | */ | ||
54 | |||
55 | #ifdef CONFIG_X86 | ||
56 | #include <linux/syscalls.h> | ||
57 | #endif | ||
58 | |||
59 | static void complete_change_console(struct vc_data *vc); | ||
60 | |||
61 | /* | ||
62 | * these are the valid i/o ports we're allowed to change. they map all the | ||
63 | * video ports | ||
64 | */ | ||
65 | #define GPFIRST 0x3b4 | ||
66 | #define GPLAST 0x3df | ||
67 | #define GPNUM (GPLAST - GPFIRST + 1) | ||
68 | |||
69 | #define i (tmp.kb_index) | ||
70 | #define s (tmp.kb_table) | ||
71 | #define v (tmp.kb_value) | ||
72 | static inline int | ||
73 | do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_struct *kbd) | ||
74 | { | ||
75 | struct kbentry tmp; | ||
76 | ushort *key_map, val, ov; | ||
77 | |||
78 | if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) | ||
79 | return -EFAULT; | ||
80 | |||
81 | switch (cmd) { | ||
82 | case KDGKBENT: | ||
83 | key_map = key_maps[s]; | ||
84 | if (key_map) { | ||
85 | val = U(key_map[i]); | ||
86 | if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) | ||
87 | val = K_HOLE; | ||
88 | } else | ||
89 | val = (i ? K_HOLE : K_NOSUCHMAP); | ||
90 | return put_user(val, &user_kbe->kb_value); | ||
91 | case KDSKBENT: | ||
92 | if (!perm) | ||
93 | return -EPERM; | ||
94 | if (!i && v == K_NOSUCHMAP) { | ||
95 | /* disallocate map */ | ||
96 | key_map = key_maps[s]; | ||
97 | if (s && key_map) { | ||
98 | key_maps[s] = NULL; | ||
99 | if (key_map[0] == U(K_ALLOCATED)) { | ||
100 | kfree(key_map); | ||
101 | keymap_count--; | ||
102 | } | ||
103 | } | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | if (KTYP(v) < NR_TYPES) { | ||
108 | if (KVAL(v) > max_vals[KTYP(v)]) | ||
109 | return -EINVAL; | ||
110 | } else | ||
111 | if (kbd->kbdmode != VC_UNICODE) | ||
112 | return -EINVAL; | ||
113 | |||
114 | /* ++Geert: non-PC keyboards may generate keycode zero */ | ||
115 | #if !defined(__mc68000__) && !defined(__powerpc__) | ||
116 | /* assignment to entry 0 only tests validity of args */ | ||
117 | if (!i) | ||
118 | break; | ||
119 | #endif | ||
120 | |||
121 | if (!(key_map = key_maps[s])) { | ||
122 | int j; | ||
123 | |||
124 | if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && | ||
125 | !capable(CAP_SYS_RESOURCE)) | ||
126 | return -EPERM; | ||
127 | |||
128 | key_map = (ushort *) kmalloc(sizeof(plain_map), | ||
129 | GFP_KERNEL); | ||
130 | if (!key_map) | ||
131 | return -ENOMEM; | ||
132 | key_maps[s] = key_map; | ||
133 | key_map[0] = U(K_ALLOCATED); | ||
134 | for (j = 1; j < NR_KEYS; j++) | ||
135 | key_map[j] = U(K_HOLE); | ||
136 | keymap_count++; | ||
137 | } | ||
138 | ov = U(key_map[i]); | ||
139 | if (v == ov) | ||
140 | break; /* nothing to do */ | ||
141 | /* | ||
142 | * Attention Key. | ||
143 | */ | ||
144 | if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) | ||
145 | return -EPERM; | ||
146 | key_map[i] = U(v); | ||
147 | if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT)) | ||
148 | compute_shiftstate(); | ||
149 | break; | ||
150 | } | ||
151 | return 0; | ||
152 | } | ||
153 | #undef i | ||
154 | #undef s | ||
155 | #undef v | ||
156 | |||
157 | static inline int | ||
158 | do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm) | ||
159 | { | ||
160 | struct kbkeycode tmp; | ||
161 | int kc = 0; | ||
162 | |||
163 | if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode))) | ||
164 | return -EFAULT; | ||
165 | switch (cmd) { | ||
166 | case KDGETKEYCODE: | ||
167 | kc = getkeycode(tmp.scancode); | ||
168 | if (kc >= 0) | ||
169 | kc = put_user(kc, &user_kbkc->keycode); | ||
170 | break; | ||
171 | case KDSETKEYCODE: | ||
172 | if (!perm) | ||
173 | return -EPERM; | ||
174 | kc = setkeycode(tmp.scancode, tmp.keycode); | ||
175 | break; | ||
176 | } | ||
177 | return kc; | ||
178 | } | ||
179 | |||
180 | static inline int | ||
181 | do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) | ||
182 | { | ||
183 | struct kbsentry *kbs; | ||
184 | char *p; | ||
185 | u_char *q; | ||
186 | u_char __user *up; | ||
187 | int sz; | ||
188 | int delta; | ||
189 | char *first_free, *fj, *fnw; | ||
190 | int i, j, k; | ||
191 | int ret; | ||
192 | |||
193 | kbs = kmalloc(sizeof(*kbs), GFP_KERNEL); | ||
194 | if (!kbs) { | ||
195 | ret = -ENOMEM; | ||
196 | goto reterr; | ||
197 | } | ||
198 | |||
199 | /* we mostly copy too much here (512bytes), but who cares ;) */ | ||
200 | if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) { | ||
201 | ret = -EFAULT; | ||
202 | goto reterr; | ||
203 | } | ||
204 | kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0'; | ||
205 | i = kbs->kb_func; | ||
206 | |||
207 | switch (cmd) { | ||
208 | case KDGKBSENT: | ||
209 | sz = sizeof(kbs->kb_string) - 1; /* sz should have been | ||
210 | a struct member */ | ||
211 | up = user_kdgkb->kb_string; | ||
212 | p = func_table[i]; | ||
213 | if(p) | ||
214 | for ( ; *p && sz; p++, sz--) | ||
215 | if (put_user(*p, up++)) { | ||
216 | ret = -EFAULT; | ||
217 | goto reterr; | ||
218 | } | ||
219 | if (put_user('\0', up)) { | ||
220 | ret = -EFAULT; | ||
221 | goto reterr; | ||
222 | } | ||
223 | kfree(kbs); | ||
224 | return ((p && *p) ? -EOVERFLOW : 0); | ||
225 | case KDSKBSENT: | ||
226 | if (!perm) { | ||
227 | ret = -EPERM; | ||
228 | goto reterr; | ||
229 | } | ||
230 | |||
231 | q = func_table[i]; | ||
232 | first_free = funcbufptr + (funcbufsize - funcbufleft); | ||
233 | for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) | ||
234 | ; | ||
235 | if (j < MAX_NR_FUNC) | ||
236 | fj = func_table[j]; | ||
237 | else | ||
238 | fj = first_free; | ||
239 | |||
240 | delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); | ||
241 | if (delta <= funcbufleft) { /* it fits in current buf */ | ||
242 | if (j < MAX_NR_FUNC) { | ||
243 | memmove(fj + delta, fj, first_free - fj); | ||
244 | for (k = j; k < MAX_NR_FUNC; k++) | ||
245 | if (func_table[k]) | ||
246 | func_table[k] += delta; | ||
247 | } | ||
248 | if (!q) | ||
249 | func_table[i] = fj; | ||
250 | funcbufleft -= delta; | ||
251 | } else { /* allocate a larger buffer */ | ||
252 | sz = 256; | ||
253 | while (sz < funcbufsize - funcbufleft + delta) | ||
254 | sz <<= 1; | ||
255 | fnw = (char *) kmalloc(sz, GFP_KERNEL); | ||
256 | if(!fnw) { | ||
257 | ret = -ENOMEM; | ||
258 | goto reterr; | ||
259 | } | ||
260 | |||
261 | if (!q) | ||
262 | func_table[i] = fj; | ||
263 | if (fj > funcbufptr) | ||
264 | memmove(fnw, funcbufptr, fj - funcbufptr); | ||
265 | for (k = 0; k < j; k++) | ||
266 | if (func_table[k]) | ||
267 | func_table[k] = fnw + (func_table[k] - funcbufptr); | ||
268 | |||
269 | if (first_free > fj) { | ||
270 | memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj); | ||
271 | for (k = j; k < MAX_NR_FUNC; k++) | ||
272 | if (func_table[k]) | ||
273 | func_table[k] = fnw + (func_table[k] - funcbufptr) + delta; | ||
274 | } | ||
275 | if (funcbufptr != func_buf) | ||
276 | kfree(funcbufptr); | ||
277 | funcbufptr = fnw; | ||
278 | funcbufleft = funcbufleft - delta + sz - funcbufsize; | ||
279 | funcbufsize = sz; | ||
280 | } | ||
281 | strcpy(func_table[i], kbs->kb_string); | ||
282 | break; | ||
283 | } | ||
284 | ret = 0; | ||
285 | reterr: | ||
286 | kfree(kbs); | ||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | static inline int | ||
291 | do_fontx_ioctl(int cmd, struct consolefontdesc __user *user_cfd, int perm, struct console_font_op *op) | ||
292 | { | ||
293 | struct consolefontdesc cfdarg; | ||
294 | int i; | ||
295 | |||
296 | if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) | ||
297 | return -EFAULT; | ||
298 | |||
299 | switch (cmd) { | ||
300 | case PIO_FONTX: | ||
301 | if (!perm) | ||
302 | return -EPERM; | ||
303 | op->op = KD_FONT_OP_SET; | ||
304 | op->flags = KD_FONT_FLAG_OLD; | ||
305 | op->width = 8; | ||
306 | op->height = cfdarg.charheight; | ||
307 | op->charcount = cfdarg.charcount; | ||
308 | op->data = cfdarg.chardata; | ||
309 | return con_font_op(vc_cons[fg_console].d, op); | ||
310 | case GIO_FONTX: { | ||
311 | op->op = KD_FONT_OP_GET; | ||
312 | op->flags = KD_FONT_FLAG_OLD; | ||
313 | op->width = 8; | ||
314 | op->height = cfdarg.charheight; | ||
315 | op->charcount = cfdarg.charcount; | ||
316 | op->data = cfdarg.chardata; | ||
317 | i = con_font_op(vc_cons[fg_console].d, op); | ||
318 | if (i) | ||
319 | return i; | ||
320 | cfdarg.charheight = op->height; | ||
321 | cfdarg.charcount = op->charcount; | ||
322 | if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc))) | ||
323 | return -EFAULT; | ||
324 | return 0; | ||
325 | } | ||
326 | } | ||
327 | return -EINVAL; | ||
328 | } | ||
329 | |||
330 | static inline int | ||
331 | do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_data *vc) | ||
332 | { | ||
333 | struct unimapdesc tmp; | ||
334 | |||
335 | if (copy_from_user(&tmp, user_ud, sizeof tmp)) | ||
336 | return -EFAULT; | ||
337 | if (tmp.entries) | ||
338 | if (!access_ok(VERIFY_WRITE, tmp.entries, | ||
339 | tmp.entry_ct*sizeof(struct unipair))) | ||
340 | return -EFAULT; | ||
341 | switch (cmd) { | ||
342 | case PIO_UNIMAP: | ||
343 | if (!perm) | ||
344 | return -EPERM; | ||
345 | return con_set_unimap(vc, tmp.entry_ct, tmp.entries); | ||
346 | case GIO_UNIMAP: | ||
347 | if (!perm && fg_console != vc->vc_num) | ||
348 | return -EPERM; | ||
349 | return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries); | ||
350 | } | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * We handle the console-specific ioctl's here. We allow the | ||
356 | * capability to modify any console, not just the fg_console. | ||
357 | */ | ||
358 | int vt_ioctl(struct tty_struct *tty, struct file * file, | ||
359 | unsigned int cmd, unsigned long arg) | ||
360 | { | ||
361 | struct vc_data *vc = (struct vc_data *)tty->driver_data; | ||
362 | struct console_font_op op; /* used in multiple places here */ | ||
363 | struct kbd_struct * kbd; | ||
364 | unsigned int console; | ||
365 | unsigned char ucval; | ||
366 | void __user *up = (void __user *)arg; | ||
367 | int i, perm; | ||
368 | |||
369 | console = vc->vc_num; | ||
370 | |||
371 | if (!vc_cons_allocated(console)) /* impossible? */ | ||
372 | return -ENOIOCTLCMD; | ||
373 | |||
374 | /* | ||
375 | * To have permissions to do most of the vt ioctls, we either have | ||
376 | * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. | ||
377 | */ | ||
378 | perm = 0; | ||
379 | if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) | ||
380 | perm = 1; | ||
381 | |||
382 | kbd = kbd_table + console; | ||
383 | switch (cmd) { | ||
384 | case KIOCSOUND: | ||
385 | if (!perm) | ||
386 | return -EPERM; | ||
387 | if (arg) | ||
388 | arg = 1193182 / arg; | ||
389 | kd_mksound(arg, 0); | ||
390 | return 0; | ||
391 | |||
392 | case KDMKTONE: | ||
393 | if (!perm) | ||
394 | return -EPERM; | ||
395 | { | ||
396 | unsigned int ticks, count; | ||
397 | |||
398 | /* | ||
399 | * Generate the tone for the appropriate number of ticks. | ||
400 | * If the time is zero, turn off sound ourselves. | ||
401 | */ | ||
402 | ticks = HZ * ((arg >> 16) & 0xffff) / 1000; | ||
403 | count = ticks ? (arg & 0xffff) : 0; | ||
404 | if (count) | ||
405 | count = 1193182 / count; | ||
406 | kd_mksound(count, ticks); | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | case KDGKBTYPE: | ||
411 | /* | ||
412 | * this is naive. | ||
413 | */ | ||
414 | ucval = KB_101; | ||
415 | goto setchar; | ||
416 | |||
417 | /* | ||
418 | * These cannot be implemented on any machine that implements | ||
419 | * ioperm() in user level (such as Alpha PCs) or not at all. | ||
420 | * | ||
421 | * XXX: you should never use these, just call ioperm directly.. | ||
422 | */ | ||
423 | #ifdef CONFIG_X86 | ||
424 | case KDADDIO: | ||
425 | case KDDELIO: | ||
426 | /* | ||
427 | * KDADDIO and KDDELIO may be able to add ports beyond what | ||
428 | * we reject here, but to be safe... | ||
429 | */ | ||
430 | if (arg < GPFIRST || arg > GPLAST) | ||
431 | return -EINVAL; | ||
432 | return sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0; | ||
433 | |||
434 | case KDENABIO: | ||
435 | case KDDISABIO: | ||
436 | return sys_ioperm(GPFIRST, GPNUM, | ||
437 | (cmd == KDENABIO)) ? -ENXIO : 0; | ||
438 | #endif | ||
439 | |||
440 | /* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */ | ||
441 | |||
442 | case KDKBDREP: | ||
443 | { | ||
444 | struct kbd_repeat kbrep; | ||
445 | int err; | ||
446 | |||
447 | if (!capable(CAP_SYS_TTY_CONFIG)) | ||
448 | return -EPERM; | ||
449 | |||
450 | if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) | ||
451 | return -EFAULT; | ||
452 | err = kbd_rate(&kbrep); | ||
453 | if (err) | ||
454 | return err; | ||
455 | if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat))) | ||
456 | return -EFAULT; | ||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | case KDSETMODE: | ||
461 | /* | ||
462 | * currently, setting the mode from KD_TEXT to KD_GRAPHICS | ||
463 | * doesn't do a whole lot. i'm not sure if it should do any | ||
464 | * restoration of modes or what... | ||
465 | * | ||
466 | * XXX It should at least call into the driver, fbdev's definitely | ||
467 | * need to restore their engine state. --BenH | ||
468 | */ | ||
469 | if (!perm) | ||
470 | return -EPERM; | ||
471 | switch (arg) { | ||
472 | case KD_GRAPHICS: | ||
473 | break; | ||
474 | case KD_TEXT0: | ||
475 | case KD_TEXT1: | ||
476 | arg = KD_TEXT; | ||
477 | case KD_TEXT: | ||
478 | break; | ||
479 | default: | ||
480 | return -EINVAL; | ||
481 | } | ||
482 | if (vc->vc_mode == (unsigned char) arg) | ||
483 | return 0; | ||
484 | vc->vc_mode = (unsigned char) arg; | ||
485 | if (console != fg_console) | ||
486 | return 0; | ||
487 | /* | ||
488 | * explicitly blank/unblank the screen if switching modes | ||
489 | */ | ||
490 | acquire_console_sem(); | ||
491 | if (arg == KD_TEXT) | ||
492 | do_unblank_screen(1); | ||
493 | else | ||
494 | do_blank_screen(1); | ||
495 | release_console_sem(); | ||
496 | return 0; | ||
497 | |||
498 | case KDGETMODE: | ||
499 | ucval = vc->vc_mode; | ||
500 | goto setint; | ||
501 | |||
502 | case KDMAPDISP: | ||
503 | case KDUNMAPDISP: | ||
504 | /* | ||
505 | * these work like a combination of mmap and KDENABIO. | ||
506 | * this could be easily finished. | ||
507 | */ | ||
508 | return -EINVAL; | ||
509 | |||
510 | case KDSKBMODE: | ||
511 | if (!perm) | ||
512 | return -EPERM; | ||
513 | switch(arg) { | ||
514 | case K_RAW: | ||
515 | kbd->kbdmode = VC_RAW; | ||
516 | break; | ||
517 | case K_MEDIUMRAW: | ||
518 | kbd->kbdmode = VC_MEDIUMRAW; | ||
519 | break; | ||
520 | case K_XLATE: | ||
521 | kbd->kbdmode = VC_XLATE; | ||
522 | compute_shiftstate(); | ||
523 | break; | ||
524 | case K_UNICODE: | ||
525 | kbd->kbdmode = VC_UNICODE; | ||
526 | compute_shiftstate(); | ||
527 | break; | ||
528 | default: | ||
529 | return -EINVAL; | ||
530 | } | ||
531 | tty_ldisc_flush(tty); | ||
532 | return 0; | ||
533 | |||
534 | case KDGKBMODE: | ||
535 | ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW : | ||
536 | (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW : | ||
537 | (kbd->kbdmode == VC_UNICODE) ? K_UNICODE : | ||
538 | K_XLATE); | ||
539 | goto setint; | ||
540 | |||
541 | /* this could be folded into KDSKBMODE, but for compatibility | ||
542 | reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */ | ||
543 | case KDSKBMETA: | ||
544 | switch(arg) { | ||
545 | case K_METABIT: | ||
546 | clr_vc_kbd_mode(kbd, VC_META); | ||
547 | break; | ||
548 | case K_ESCPREFIX: | ||
549 | set_vc_kbd_mode(kbd, VC_META); | ||
550 | break; | ||
551 | default: | ||
552 | return -EINVAL; | ||
553 | } | ||
554 | return 0; | ||
555 | |||
556 | case KDGKBMETA: | ||
557 | ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); | ||
558 | setint: | ||
559 | return put_user(ucval, (int __user *)arg); | ||
560 | |||
561 | case KDGETKEYCODE: | ||
562 | case KDSETKEYCODE: | ||
563 | if(!capable(CAP_SYS_TTY_CONFIG)) | ||
564 | perm=0; | ||
565 | return do_kbkeycode_ioctl(cmd, up, perm); | ||
566 | |||
567 | case KDGKBENT: | ||
568 | case KDSKBENT: | ||
569 | return do_kdsk_ioctl(cmd, up, perm, kbd); | ||
570 | |||
571 | case KDGKBSENT: | ||
572 | case KDSKBSENT: | ||
573 | return do_kdgkb_ioctl(cmd, up, perm); | ||
574 | |||
575 | case KDGKBDIACR: | ||
576 | { | ||
577 | struct kbdiacrs __user *a = up; | ||
578 | |||
579 | if (put_user(accent_table_size, &a->kb_cnt)) | ||
580 | return -EFAULT; | ||
581 | if (copy_to_user(a->kbdiacr, accent_table, accent_table_size*sizeof(struct kbdiacr))) | ||
582 | return -EFAULT; | ||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | case KDSKBDIACR: | ||
587 | { | ||
588 | struct kbdiacrs __user *a = up; | ||
589 | unsigned int ct; | ||
590 | |||
591 | if (!perm) | ||
592 | return -EPERM; | ||
593 | if (get_user(ct,&a->kb_cnt)) | ||
594 | return -EFAULT; | ||
595 | if (ct >= MAX_DIACR) | ||
596 | return -EINVAL; | ||
597 | accent_table_size = ct; | ||
598 | if (copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr))) | ||
599 | return -EFAULT; | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | /* the ioctls below read/set the flags usually shown in the leds */ | ||
604 | /* don't use them - they will go away without warning */ | ||
605 | case KDGKBLED: | ||
606 | ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4); | ||
607 | goto setchar; | ||
608 | |||
609 | case KDSKBLED: | ||
610 | if (!perm) | ||
611 | return -EPERM; | ||
612 | if (arg & ~0x77) | ||
613 | return -EINVAL; | ||
614 | kbd->ledflagstate = (arg & 7); | ||
615 | kbd->default_ledflagstate = ((arg >> 4) & 7); | ||
616 | set_leds(); | ||
617 | return 0; | ||
618 | |||
619 | /* the ioctls below only set the lights, not the functions */ | ||
620 | /* for those, see KDGKBLED and KDSKBLED above */ | ||
621 | case KDGETLED: | ||
622 | ucval = getledstate(); | ||
623 | setchar: | ||
624 | return put_user(ucval, (char __user *)arg); | ||
625 | |||
626 | case KDSETLED: | ||
627 | if (!perm) | ||
628 | return -EPERM; | ||
629 | setledstate(kbd, arg); | ||
630 | return 0; | ||
631 | |||
632 | /* | ||
633 | * A process can indicate its willingness to accept signals | ||
634 | * generated by pressing an appropriate key combination. | ||
635 | * Thus, one can have a daemon that e.g. spawns a new console | ||
636 | * upon a keypress and then changes to it. | ||
637 | * See also the kbrequest field of inittab(5). | ||
638 | */ | ||
639 | case KDSIGACCEPT: | ||
640 | { | ||
641 | extern int spawnpid, spawnsig; | ||
642 | if (!perm || !capable(CAP_KILL)) | ||
643 | return -EPERM; | ||
644 | if (arg < 1 || arg > _NSIG || arg == SIGKILL) | ||
645 | return -EINVAL; | ||
646 | spawnpid = current->pid; | ||
647 | spawnsig = arg; | ||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | case VT_SETMODE: | ||
652 | { | ||
653 | struct vt_mode tmp; | ||
654 | |||
655 | if (!perm) | ||
656 | return -EPERM; | ||
657 | if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) | ||
658 | return -EFAULT; | ||
659 | if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) | ||
660 | return -EINVAL; | ||
661 | acquire_console_sem(); | ||
662 | vc->vt_mode = tmp; | ||
663 | /* the frsig is ignored, so we set it to 0 */ | ||
664 | vc->vt_mode.frsig = 0; | ||
665 | vc->vt_pid = current->pid; | ||
666 | /* no switch is required -- saw@shade.msu.ru */ | ||
667 | vc->vt_newvt = -1; | ||
668 | release_console_sem(); | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | case VT_GETMODE: | ||
673 | { | ||
674 | struct vt_mode tmp; | ||
675 | int rc; | ||
676 | |||
677 | acquire_console_sem(); | ||
678 | memcpy(&tmp, &vc->vt_mode, sizeof(struct vt_mode)); | ||
679 | release_console_sem(); | ||
680 | |||
681 | rc = copy_to_user(up, &tmp, sizeof(struct vt_mode)); | ||
682 | return rc ? -EFAULT : 0; | ||
683 | } | ||
684 | |||
685 | /* | ||
686 | * Returns global vt state. Note that VT 0 is always open, since | ||
687 | * it's an alias for the current VT, and people can't use it here. | ||
688 | * We cannot return state for more than 16 VTs, since v_state is short. | ||
689 | */ | ||
690 | case VT_GETSTATE: | ||
691 | { | ||
692 | struct vt_stat __user *vtstat = up; | ||
693 | unsigned short state, mask; | ||
694 | |||
695 | if (put_user(fg_console + 1, &vtstat->v_active)) | ||
696 | return -EFAULT; | ||
697 | state = 1; /* /dev/tty0 is always open */ | ||
698 | for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1) | ||
699 | if (VT_IS_IN_USE(i)) | ||
700 | state |= mask; | ||
701 | return put_user(state, &vtstat->v_state); | ||
702 | } | ||
703 | |||
704 | /* | ||
705 | * Returns the first available (non-opened) console. | ||
706 | */ | ||
707 | case VT_OPENQRY: | ||
708 | for (i = 0; i < MAX_NR_CONSOLES; ++i) | ||
709 | if (! VT_IS_IN_USE(i)) | ||
710 | break; | ||
711 | ucval = i < MAX_NR_CONSOLES ? (i+1) : -1; | ||
712 | goto setint; | ||
713 | |||
714 | /* | ||
715 | * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num, | ||
716 | * with num >= 1 (switches to vt 0, our console, are not allowed, just | ||
717 | * to preserve sanity). | ||
718 | */ | ||
719 | case VT_ACTIVATE: | ||
720 | if (!perm) | ||
721 | return -EPERM; | ||
722 | if (arg == 0 || arg > MAX_NR_CONSOLES) | ||
723 | return -ENXIO; | ||
724 | arg--; | ||
725 | acquire_console_sem(); | ||
726 | i = vc_allocate(arg); | ||
727 | release_console_sem(); | ||
728 | if (i) | ||
729 | return i; | ||
730 | set_console(arg); | ||
731 | return 0; | ||
732 | |||
733 | /* | ||
734 | * wait until the specified VT has been activated | ||
735 | */ | ||
736 | case VT_WAITACTIVE: | ||
737 | if (!perm) | ||
738 | return -EPERM; | ||
739 | if (arg == 0 || arg > MAX_NR_CONSOLES) | ||
740 | return -ENXIO; | ||
741 | return vt_waitactive(arg-1); | ||
742 | |||
743 | /* | ||
744 | * If a vt is under process control, the kernel will not switch to it | ||
745 | * immediately, but postpone the operation until the process calls this | ||
746 | * ioctl, allowing the switch to complete. | ||
747 | * | ||
748 | * According to the X sources this is the behavior: | ||
749 | * 0: pending switch-from not OK | ||
750 | * 1: pending switch-from OK | ||
751 | * 2: completed switch-to OK | ||
752 | */ | ||
753 | case VT_RELDISP: | ||
754 | if (!perm) | ||
755 | return -EPERM; | ||
756 | if (vc->vt_mode.mode != VT_PROCESS) | ||
757 | return -EINVAL; | ||
758 | |||
759 | /* | ||
760 | * Switching-from response | ||
761 | */ | ||
762 | if (vc->vt_newvt >= 0) { | ||
763 | if (arg == 0) | ||
764 | /* | ||
765 | * Switch disallowed, so forget we were trying | ||
766 | * to do it. | ||
767 | */ | ||
768 | vc->vt_newvt = -1; | ||
769 | |||
770 | else { | ||
771 | /* | ||
772 | * The current vt has been released, so | ||
773 | * complete the switch. | ||
774 | */ | ||
775 | int newvt; | ||
776 | acquire_console_sem(); | ||
777 | newvt = vc->vt_newvt; | ||
778 | vc->vt_newvt = -1; | ||
779 | i = vc_allocate(newvt); | ||
780 | if (i) { | ||
781 | release_console_sem(); | ||
782 | return i; | ||
783 | } | ||
784 | /* | ||
785 | * When we actually do the console switch, | ||
786 | * make sure we are atomic with respect to | ||
787 | * other console switches.. | ||
788 | */ | ||
789 | complete_change_console(vc_cons[newvt].d); | ||
790 | release_console_sem(); | ||
791 | } | ||
792 | } | ||
793 | |||
794 | /* | ||
795 | * Switched-to response | ||
796 | */ | ||
797 | else | ||
798 | { | ||
799 | /* | ||
800 | * If it's just an ACK, ignore it | ||
801 | */ | ||
802 | if (arg != VT_ACKACQ) | ||
803 | return -EINVAL; | ||
804 | } | ||
805 | |||
806 | return 0; | ||
807 | |||
808 | /* | ||
809 | * Disallocate memory associated to VT (but leave VT1) | ||
810 | */ | ||
811 | case VT_DISALLOCATE: | ||
812 | if (arg > MAX_NR_CONSOLES) | ||
813 | return -ENXIO; | ||
814 | if (arg == 0) { | ||
815 | /* disallocate all unused consoles, but leave 0 */ | ||
816 | acquire_console_sem(); | ||
817 | for (i=1; i<MAX_NR_CONSOLES; i++) | ||
818 | if (! VT_BUSY(i)) | ||
819 | vc_disallocate(i); | ||
820 | release_console_sem(); | ||
821 | } else { | ||
822 | /* disallocate a single console, if possible */ | ||
823 | arg--; | ||
824 | if (VT_BUSY(arg)) | ||
825 | return -EBUSY; | ||
826 | if (arg) { /* leave 0 */ | ||
827 | acquire_console_sem(); | ||
828 | vc_disallocate(arg); | ||
829 | release_console_sem(); | ||
830 | } | ||
831 | } | ||
832 | return 0; | ||
833 | |||
834 | case VT_RESIZE: | ||
835 | { | ||
836 | struct vt_sizes __user *vtsizes = up; | ||
837 | ushort ll,cc; | ||
838 | if (!perm) | ||
839 | return -EPERM; | ||
840 | if (get_user(ll, &vtsizes->v_rows) || | ||
841 | get_user(cc, &vtsizes->v_cols)) | ||
842 | return -EFAULT; | ||
843 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | ||
844 | acquire_console_sem(); | ||
845 | vc_resize(vc_cons[i].d, cc, ll); | ||
846 | release_console_sem(); | ||
847 | } | ||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | case VT_RESIZEX: | ||
852 | { | ||
853 | struct vt_consize __user *vtconsize = up; | ||
854 | ushort ll,cc,vlin,clin,vcol,ccol; | ||
855 | if (!perm) | ||
856 | return -EPERM; | ||
857 | if (!access_ok(VERIFY_READ, vtconsize, | ||
858 | sizeof(struct vt_consize))) | ||
859 | return -EFAULT; | ||
860 | __get_user(ll, &vtconsize->v_rows); | ||
861 | __get_user(cc, &vtconsize->v_cols); | ||
862 | __get_user(vlin, &vtconsize->v_vlin); | ||
863 | __get_user(clin, &vtconsize->v_clin); | ||
864 | __get_user(vcol, &vtconsize->v_vcol); | ||
865 | __get_user(ccol, &vtconsize->v_ccol); | ||
866 | vlin = vlin ? vlin : vc->vc_scan_lines; | ||
867 | if (clin) { | ||
868 | if (ll) { | ||
869 | if (ll != vlin/clin) | ||
870 | return -EINVAL; /* Parameters don't add up */ | ||
871 | } else | ||
872 | ll = vlin/clin; | ||
873 | } | ||
874 | if (vcol && ccol) { | ||
875 | if (cc) { | ||
876 | if (cc != vcol/ccol) | ||
877 | return -EINVAL; | ||
878 | } else | ||
879 | cc = vcol/ccol; | ||
880 | } | ||
881 | |||
882 | if (clin > 32) | ||
883 | return -EINVAL; | ||
884 | |||
885 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | ||
886 | if (!vc_cons[i].d) | ||
887 | continue; | ||
888 | acquire_console_sem(); | ||
889 | if (vlin) | ||
890 | vc_cons[i].d->vc_scan_lines = vlin; | ||
891 | if (clin) | ||
892 | vc_cons[i].d->vc_font.height = clin; | ||
893 | vc_resize(vc_cons[i].d, cc, ll); | ||
894 | release_console_sem(); | ||
895 | } | ||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | case PIO_FONT: { | ||
900 | if (!perm) | ||
901 | return -EPERM; | ||
902 | op.op = KD_FONT_OP_SET; | ||
903 | op.flags = KD_FONT_FLAG_OLD | KD_FONT_FLAG_DONT_RECALC; /* Compatibility */ | ||
904 | op.width = 8; | ||
905 | op.height = 0; | ||
906 | op.charcount = 256; | ||
907 | op.data = up; | ||
908 | return con_font_op(vc_cons[fg_console].d, &op); | ||
909 | } | ||
910 | |||
911 | case GIO_FONT: { | ||
912 | op.op = KD_FONT_OP_GET; | ||
913 | op.flags = KD_FONT_FLAG_OLD; | ||
914 | op.width = 8; | ||
915 | op.height = 32; | ||
916 | op.charcount = 256; | ||
917 | op.data = up; | ||
918 | return con_font_op(vc_cons[fg_console].d, &op); | ||
919 | } | ||
920 | |||
921 | case PIO_CMAP: | ||
922 | if (!perm) | ||
923 | return -EPERM; | ||
924 | return con_set_cmap(up); | ||
925 | |||
926 | case GIO_CMAP: | ||
927 | return con_get_cmap(up); | ||
928 | |||
929 | case PIO_FONTX: | ||
930 | case GIO_FONTX: | ||
931 | return do_fontx_ioctl(cmd, up, perm, &op); | ||
932 | |||
933 | case PIO_FONTRESET: | ||
934 | { | ||
935 | if (!perm) | ||
936 | return -EPERM; | ||
937 | |||
938 | #ifdef BROKEN_GRAPHICS_PROGRAMS | ||
939 | /* With BROKEN_GRAPHICS_PROGRAMS defined, the default | ||
940 | font is not saved. */ | ||
941 | return -ENOSYS; | ||
942 | #else | ||
943 | { | ||
944 | op.op = KD_FONT_OP_SET_DEFAULT; | ||
945 | op.data = NULL; | ||
946 | i = con_font_op(vc_cons[fg_console].d, &op); | ||
947 | if (i) | ||
948 | return i; | ||
949 | con_set_default_unimap(vc_cons[fg_console].d); | ||
950 | return 0; | ||
951 | } | ||
952 | #endif | ||
953 | } | ||
954 | |||
955 | case KDFONTOP: { | ||
956 | if (copy_from_user(&op, up, sizeof(op))) | ||
957 | return -EFAULT; | ||
958 | if (!perm && op.op != KD_FONT_OP_GET) | ||
959 | return -EPERM; | ||
960 | i = con_font_op(vc, &op); | ||
961 | if (i) return i; | ||
962 | if (copy_to_user(up, &op, sizeof(op))) | ||
963 | return -EFAULT; | ||
964 | return 0; | ||
965 | } | ||
966 | |||
967 | case PIO_SCRNMAP: | ||
968 | if (!perm) | ||
969 | return -EPERM; | ||
970 | return con_set_trans_old(up); | ||
971 | |||
972 | case GIO_SCRNMAP: | ||
973 | return con_get_trans_old(up); | ||
974 | |||
975 | case PIO_UNISCRNMAP: | ||
976 | if (!perm) | ||
977 | return -EPERM; | ||
978 | return con_set_trans_new(up); | ||
979 | |||
980 | case GIO_UNISCRNMAP: | ||
981 | return con_get_trans_new(up); | ||
982 | |||
983 | case PIO_UNIMAPCLR: | ||
984 | { struct unimapinit ui; | ||
985 | if (!perm) | ||
986 | return -EPERM; | ||
987 | i = copy_from_user(&ui, up, sizeof(struct unimapinit)); | ||
988 | if (i) return -EFAULT; | ||
989 | con_clear_unimap(vc, &ui); | ||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | case PIO_UNIMAP: | ||
994 | case GIO_UNIMAP: | ||
995 | return do_unimap_ioctl(cmd, up, perm, vc); | ||
996 | |||
997 | case VT_LOCKSWITCH: | ||
998 | if (!capable(CAP_SYS_TTY_CONFIG)) | ||
999 | return -EPERM; | ||
1000 | vt_dont_switch = 1; | ||
1001 | return 0; | ||
1002 | case VT_UNLOCKSWITCH: | ||
1003 | if (!capable(CAP_SYS_TTY_CONFIG)) | ||
1004 | return -EPERM; | ||
1005 | vt_dont_switch = 0; | ||
1006 | return 0; | ||
1007 | default: | ||
1008 | return -ENOIOCTLCMD; | ||
1009 | } | ||
1010 | } | ||
1011 | |||
1012 | /* | ||
1013 | * Sometimes we want to wait until a particular VT has been activated. We | ||
1014 | * do it in a very simple manner. Everybody waits on a single queue and | ||
1015 | * get woken up at once. Those that are satisfied go on with their business, | ||
1016 | * while those not ready go back to sleep. Seems overkill to add a wait | ||
1017 | * to each vt just for this - usually this does nothing! | ||
1018 | */ | ||
1019 | static DECLARE_WAIT_QUEUE_HEAD(vt_activate_queue); | ||
1020 | |||
1021 | /* | ||
1022 | * Sleeps until a vt is activated, or the task is interrupted. Returns | ||
1023 | * 0 if activation, -EINTR if interrupted. | ||
1024 | */ | ||
1025 | int vt_waitactive(int vt) | ||
1026 | { | ||
1027 | int retval; | ||
1028 | DECLARE_WAITQUEUE(wait, current); | ||
1029 | |||
1030 | add_wait_queue(&vt_activate_queue, &wait); | ||
1031 | for (;;) { | ||
1032 | set_current_state(TASK_INTERRUPTIBLE); | ||
1033 | retval = 0; | ||
1034 | if (vt == fg_console) | ||
1035 | break; | ||
1036 | retval = -EINTR; | ||
1037 | if (signal_pending(current)) | ||
1038 | break; | ||
1039 | schedule(); | ||
1040 | } | ||
1041 | remove_wait_queue(&vt_activate_queue, &wait); | ||
1042 | current->state = TASK_RUNNING; | ||
1043 | return retval; | ||
1044 | } | ||
1045 | |||
1046 | #define vt_wake_waitactive() wake_up(&vt_activate_queue) | ||
1047 | |||
1048 | void reset_vc(struct vc_data *vc) | ||
1049 | { | ||
1050 | vc->vc_mode = KD_TEXT; | ||
1051 | kbd_table[vc->vc_num].kbdmode = VC_XLATE; | ||
1052 | vc->vt_mode.mode = VT_AUTO; | ||
1053 | vc->vt_mode.waitv = 0; | ||
1054 | vc->vt_mode.relsig = 0; | ||
1055 | vc->vt_mode.acqsig = 0; | ||
1056 | vc->vt_mode.frsig = 0; | ||
1057 | vc->vt_pid = -1; | ||
1058 | vc->vt_newvt = -1; | ||
1059 | if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */ | ||
1060 | reset_palette(vc); | ||
1061 | } | ||
1062 | |||
1063 | /* | ||
1064 | * Performs the back end of a vt switch | ||
1065 | */ | ||
1066 | static void complete_change_console(struct vc_data *vc) | ||
1067 | { | ||
1068 | unsigned char old_vc_mode; | ||
1069 | |||
1070 | last_console = fg_console; | ||
1071 | |||
1072 | /* | ||
1073 | * If we're switching, we could be going from KD_GRAPHICS to | ||
1074 | * KD_TEXT mode or vice versa, which means we need to blank or | ||
1075 | * unblank the screen later. | ||
1076 | */ | ||
1077 | old_vc_mode = vc_cons[fg_console].d->vc_mode; | ||
1078 | switch_screen(vc); | ||
1079 | |||
1080 | /* | ||
1081 | * This can't appear below a successful kill_proc(). If it did, | ||
1082 | * then the *blank_screen operation could occur while X, having | ||
1083 | * received acqsig, is waking up on another processor. This | ||
1084 | * condition can lead to overlapping accesses to the VGA range | ||
1085 | * and the framebuffer (causing system lockups). | ||
1086 | * | ||
1087 | * To account for this we duplicate this code below only if the | ||
1088 | * controlling process is gone and we've called reset_vc. | ||
1089 | */ | ||
1090 | if (old_vc_mode != vc->vc_mode) { | ||
1091 | if (vc->vc_mode == KD_TEXT) | ||
1092 | do_unblank_screen(1); | ||
1093 | else | ||
1094 | do_blank_screen(1); | ||
1095 | } | ||
1096 | |||
1097 | /* | ||
1098 | * If this new console is under process control, send it a signal | ||
1099 | * telling it that it has acquired. Also check if it has died and | ||
1100 | * clean up (similar to logic employed in change_console()) | ||
1101 | */ | ||
1102 | if (vc->vt_mode.mode == VT_PROCESS) { | ||
1103 | /* | ||
1104 | * Send the signal as privileged - kill_proc() will | ||
1105 | * tell us if the process has gone or something else | ||
1106 | * is awry | ||
1107 | */ | ||
1108 | if (kill_proc(vc->vt_pid, vc->vt_mode.acqsig, 1) != 0) { | ||
1109 | /* | ||
1110 | * The controlling process has died, so we revert back to | ||
1111 | * normal operation. In this case, we'll also change back | ||
1112 | * to KD_TEXT mode. I'm not sure if this is strictly correct | ||
1113 | * but it saves the agony when the X server dies and the screen | ||
1114 | * remains blanked due to KD_GRAPHICS! It would be nice to do | ||
1115 | * this outside of VT_PROCESS but there is no single process | ||
1116 | * to account for and tracking tty count may be undesirable. | ||
1117 | */ | ||
1118 | reset_vc(vc); | ||
1119 | |||
1120 | if (old_vc_mode != vc->vc_mode) { | ||
1121 | if (vc->vc_mode == KD_TEXT) | ||
1122 | do_unblank_screen(1); | ||
1123 | else | ||
1124 | do_blank_screen(1); | ||
1125 | } | ||
1126 | } | ||
1127 | } | ||
1128 | |||
1129 | /* | ||
1130 | * Wake anyone waiting for their VT to activate | ||
1131 | */ | ||
1132 | vt_wake_waitactive(); | ||
1133 | return; | ||
1134 | } | ||
1135 | |||
1136 | /* | ||
1137 | * Performs the front-end of a vt switch | ||
1138 | */ | ||
1139 | void change_console(struct vc_data *new_vc) | ||
1140 | { | ||
1141 | struct vc_data *vc; | ||
1142 | |||
1143 | if (!new_vc || new_vc->vc_num == fg_console || vt_dont_switch) | ||
1144 | return; | ||
1145 | |||
1146 | /* | ||
1147 | * If this vt is in process mode, then we need to handshake with | ||
1148 | * that process before switching. Essentially, we store where that | ||
1149 | * vt wants to switch to and wait for it to tell us when it's done | ||
1150 | * (via VT_RELDISP ioctl). | ||
1151 | * | ||
1152 | * We also check to see if the controlling process still exists. | ||
1153 | * If it doesn't, we reset this vt to auto mode and continue. | ||
1154 | * This is a cheap way to track process control. The worst thing | ||
1155 | * that can happen is: we send a signal to a process, it dies, and | ||
1156 | * the switch gets "lost" waiting for a response; hopefully, the | ||
1157 | * user will try again, we'll detect the process is gone (unless | ||
1158 | * the user waits just the right amount of time :-) and revert the | ||
1159 | * vt to auto control. | ||
1160 | */ | ||
1161 | vc = vc_cons[fg_console].d; | ||
1162 | if (vc->vt_mode.mode == VT_PROCESS) { | ||
1163 | /* | ||
1164 | * Send the signal as privileged - kill_proc() will | ||
1165 | * tell us if the process has gone or something else | ||
1166 | * is awry | ||
1167 | */ | ||
1168 | if (kill_proc(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) { | ||
1169 | /* | ||
1170 | * It worked. Mark the vt to switch to and | ||
1171 | * return. The process needs to send us a | ||
1172 | * VT_RELDISP ioctl to complete the switch. | ||
1173 | */ | ||
1174 | vc->vt_newvt = new_vc->vc_num; | ||
1175 | return; | ||
1176 | } | ||
1177 | |||
1178 | /* | ||
1179 | * The controlling process has died, so we revert back to | ||
1180 | * normal operation. In this case, we'll also change back | ||
1181 | * to KD_TEXT mode. I'm not sure if this is strictly correct | ||
1182 | * but it saves the agony when the X server dies and the screen | ||
1183 | * remains blanked due to KD_GRAPHICS! It would be nice to do | ||
1184 | * this outside of VT_PROCESS but there is no single process | ||
1185 | * to account for and tracking tty count may be undesirable. | ||
1186 | */ | ||
1187 | reset_vc(vc); | ||
1188 | |||
1189 | /* | ||
1190 | * Fall through to normal (VT_AUTO) handling of the switch... | ||
1191 | */ | ||
1192 | } | ||
1193 | |||
1194 | /* | ||
1195 | * Ignore all switches in KD_GRAPHICS+VT_AUTO mode | ||
1196 | */ | ||
1197 | if (vc->vc_mode == KD_GRAPHICS) | ||
1198 | return; | ||
1199 | |||
1200 | complete_change_console(new_vc); | ||
1201 | } | ||