[Mono.Runtime.Tests] Exclude simd tests
[mono-project.git] / mono / metadata / console-unix.c
blob1947e481baefdb690ec33432caa95bc7bb7fa256
1 /**
2 * \file
3 * ConsoleDriver internal calls for Unix systems.
5 * Author:
6 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 * Copyright (C) 2005-2009 Novell, Inc. (http://www.novell.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 #include <config.h>
12 #include <glib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <signal.h>
18 #ifdef HAVE_SYS_SELECT_H
19 # include <sys/select.h>
20 #endif
21 #ifdef HAVE_SYS_TIME_H
22 # include <sys/time.h>
23 #endif
24 #include <sys/types.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
28 #include <mono/metadata/appdomain.h>
29 #include <mono/metadata/object-internals.h>
30 #include <mono/metadata/class-internals.h>
31 #include <mono/metadata/domain-internals.h>
32 #include <mono/metadata/gc-internals.h>
33 #include <mono/metadata/metadata.h>
34 #include <mono/metadata/threadpool.h>
35 #include <mono/utils/mono-signal-handler.h>
36 #include <mono/utils/mono-proclib.h>
37 #include <mono/utils/w32api.h>
38 #include <mono/utils/mono-errno.h>
40 /* On solaris, curses.h must come before both termios.h and term.h */
41 #ifdef HAVE_CURSES_H
42 # include <curses.h>
43 #endif
44 #ifdef HAVE_TERMIOS_H
45 # include <termios.h>
46 #endif
47 #ifdef HAVE_TERM_H
48 # include <term.h>
49 #endif
51 /* Needed for FIONREAD under solaris */
52 #ifdef HAVE_SYS_FILIO_H
53 # include <sys/filio.h>
54 #endif
55 #ifdef HAVE_SYS_IOCTL_H
56 # include <sys/ioctl.h>
57 #endif
59 #include <mono/metadata/console-io.h>
60 #include <mono/metadata/exception.h>
61 #include "icall-decl.h"
63 static gboolean setup_finished;
64 static gboolean atexit_called;
66 /* The string used to return the terminal to its previous state */
67 static gchar *teardown_str;
69 /* The string used to set the terminal into keypad xmit mode after SIGCONT is received */
70 static gchar *keypad_xmit_str;
72 #ifdef HAVE_TERMIOS_H
73 /* This is the last state used by Mono, used after a CONT signal is received */
74 static struct termios mono_attr;
75 #endif
77 /* static void console_restore_signal_handlers (void); */
78 static void console_set_signal_handlers (void);
81 static GENERATE_TRY_GET_CLASS_WITH_CACHE (console, "System", "Console");
84 void
85 mono_console_init (void)
87 int fd;
89 /* Make sure the standard file descriptors are opened */
90 fd = open ("/dev/null", O_RDWR);
91 while (fd >= 0 && fd < 3) {
92 fd = open ("/dev/null", O_RDWR);
94 close (fd);
97 static struct termios initial_attr;
99 MonoBoolean
100 ves_icall_System_ConsoleDriver_Isatty (HANDLE handle, MonoError* error)
102 return isatty (GPOINTER_TO_INT (handle));
105 static MonoBoolean
106 set_property (gint property, gboolean value)
108 struct termios attr;
109 gboolean callset = FALSE;
110 gboolean check;
112 if (tcgetattr (STDIN_FILENO, &attr) == -1)
113 return FALSE;
115 check = (attr.c_lflag & property) != 0;
116 if ((value || check) && !(value && check)) {
117 callset = TRUE;
118 if (value)
119 attr.c_lflag |= property;
120 else
121 attr.c_lflag &= ~property;
124 if (!callset)
125 return TRUE;
127 if (tcsetattr (STDIN_FILENO, TCSANOW, &attr) == -1)
128 return FALSE;
130 mono_attr = attr;
131 return TRUE;
134 MonoBoolean
135 ves_icall_System_ConsoleDriver_SetEcho (MonoBoolean want_echo, MonoError* error)
137 return set_property (ECHO, want_echo);
140 MonoBoolean
141 ves_icall_System_ConsoleDriver_SetBreak (MonoBoolean want_break, MonoError* error)
143 return set_property (IGNBRK, !want_break);
146 gint32
147 ves_icall_System_ConsoleDriver_InternalKeyAvailable (gint32 timeout, MonoError* error)
149 fd_set rfds;
150 struct timeval tv;
151 struct timeval *tvptr;
152 div_t divvy;
153 int ret, nbytes;
155 do {
156 FD_ZERO (&rfds);
157 FD_SET (STDIN_FILENO, &rfds);
158 if (timeout >= 0) {
159 divvy = div (timeout, 1000);
160 tv.tv_sec = divvy.quot;
161 tv.tv_usec = divvy.rem;
162 tvptr = &tv;
163 } else {
164 tvptr = NULL;
166 ret = select (STDIN_FILENO + 1, &rfds, NULL, NULL, tvptr);
167 } while (ret == -1 && errno == EINTR);
169 if (ret > 0) {
170 nbytes = 0;
171 ret = ioctl (STDIN_FILENO, FIONREAD, &nbytes);
172 if (ret >= 0)
173 ret = nbytes;
176 return (ret > 0) ? ret : 0;
179 static gint32 cols_and_lines;
181 #ifdef TIOCGWINSZ
182 static int
183 terminal_get_dimensions (void)
185 struct winsize ws;
186 int ret;
187 int save_errno = errno;
189 if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) == 0){
190 ret = (ws.ws_col << 16) | ws.ws_row;
191 mono_set_errno (save_errno);
192 return ret;
194 return -1;
196 #else
197 static int
198 terminal_get_dimensions (void)
200 return -1;
202 #endif
204 static void
205 tty_teardown (void)
207 int unused G_GNUC_UNUSED;
209 if (!setup_finished)
210 return;
212 if (teardown_str != NULL) {
213 unused = write (STDOUT_FILENO, teardown_str, strlen (teardown_str));
214 g_free (teardown_str);
215 teardown_str = NULL;
218 tcflush (STDIN_FILENO, TCIFLUSH);
219 tcsetattr (STDIN_FILENO, TCSANOW, &initial_attr);
220 set_property (ECHO, TRUE);
221 setup_finished = FALSE;
224 static void
225 do_console_cancel_event (void)
227 static MonoMethod *System_Console_DoConsoleCancelEventBackground_method = (MonoMethod*)(intptr_t)-1;
228 ERROR_DECL (error);
230 if (mono_class_try_get_console_class () == NULL)
231 return;
233 if (System_Console_DoConsoleCancelEventBackground_method == (gpointer)(intptr_t)-1) {
234 System_Console_DoConsoleCancelEventBackground_method = mono_class_get_method_from_name_checked (mono_class_try_get_console_class (), "DoConsoleCancelEventInBackground", 0, 0, error);
235 mono_error_assert_ok (error);
237 if (System_Console_DoConsoleCancelEventBackground_method == NULL)
238 return;
240 mono_runtime_invoke_checked (System_Console_DoConsoleCancelEventBackground_method, NULL, NULL, error);
241 mono_error_assert_ok (error);
244 static int need_cancel = FALSE;
245 /* this is executed from the finalizer thread */
246 void
247 mono_console_handle_async_ops (void)
249 if (need_cancel) {
250 need_cancel = FALSE;
251 do_console_cancel_event ();
255 static gboolean in_sigint;
257 MONO_SIG_HANDLER_FUNC (static, sigint_handler)
259 int save_errno;
261 if (in_sigint)
262 return;
264 in_sigint = TRUE;
265 save_errno = errno;
266 need_cancel = TRUE;
267 mono_gc_finalize_notify ();
268 mono_set_errno (save_errno);
269 in_sigint = FALSE;
272 static struct sigaction save_sigcont, save_sigint, save_sigwinch;
274 MONO_SIG_HANDLER_FUNC (static, sigcont_handler)
276 int unused G_GNUC_UNUSED;
277 // Ignore error, there is not much we can do in the sigcont handler.
278 tcsetattr (STDIN_FILENO, TCSANOW, &mono_attr);
280 if (keypad_xmit_str != NULL)
281 unused = write (STDOUT_FILENO, keypad_xmit_str, strlen (keypad_xmit_str));
283 // Call previous handler
284 if (save_sigcont.sa_sigaction != NULL &&
285 save_sigcont.sa_sigaction != (void *)SIG_DFL &&
286 save_sigcont.sa_sigaction != (void *)SIG_IGN)
287 (*save_sigcont.sa_sigaction) (MONO_SIG_HANDLER_PARAMS);
290 MONO_SIG_HANDLER_FUNC (static, sigwinch_handler)
292 int dims = terminal_get_dimensions ();
293 if (dims != -1)
294 cols_and_lines = dims;
296 // Call previous handler
297 if (save_sigwinch.sa_sigaction != NULL &&
298 save_sigwinch.sa_sigaction != (void *)SIG_DFL &&
299 save_sigwinch.sa_sigaction != (void *)SIG_IGN)
300 (*save_sigwinch.sa_sigaction) (MONO_SIG_HANDLER_PARAMS);
304 * console_set_signal_handlers:
306 * Installs various signals handlers for the use of the console, as
307 * follows:
309 * SIGCONT: this is received after the application has resumed execution
310 * if it was suspended with Control-Z before. This signal handler needs
311 * to resend the terminal sequence to send keyboard in keypad mode (this
312 * is the difference between getting a cuu1 code or a kcuu1 code for up-arrow
313 * for example
315 * SIGINT: invokes the System.Console.DoConsoleCancelEvent method using
316 * a thread from the thread pool which notifies all registered cancel_event
317 * listeners.
319 * SIGWINCH: is used to track changes to the console window when a GUI
320 * terminal is resized. It sets an internal variable that is checked
321 * by System.Console when the Terminfo driver has been activated.
323 static void
324 console_set_signal_handlers ()
326 #if defined(HAVE_SIGACTION)
327 struct sigaction sigcont, sigint, sigwinch;
329 memset (&sigcont, 0, sizeof (struct sigaction));
330 memset (&sigint, 0, sizeof (struct sigaction));
331 memset (&sigwinch, 0, sizeof (struct sigaction));
333 // Continuing
334 sigcont.sa_handler = (void (*)(int)) sigcont_handler;
335 sigcont.sa_flags = SA_RESTART;
336 sigemptyset (&sigcont.sa_mask);
337 sigaction (SIGCONT, &sigcont, &save_sigcont);
339 // Interrupt handler
340 sigint.sa_handler = (void (*)(int)) sigint_handler;
341 sigint.sa_flags = SA_RESTART;
342 sigemptyset (&sigint.sa_mask);
343 sigaction (SIGINT, &sigint, &save_sigint);
345 // Window size changed
346 sigwinch.sa_handler = (void (*)(int)) sigwinch_handler;
347 sigwinch.sa_flags = SA_RESTART;
348 sigemptyset (&sigwinch.sa_mask);
349 sigaction (SIGWINCH, &sigwinch, &save_sigwinch);
350 #endif
353 #if currently_unuused
355 // Currently unused, should we ever call the restore handler?
356 // Perhaps before calling into Process.Start?
358 void
359 console_restore_signal_handlers ()
361 sigaction (SIGCONT, &save_sigcont, NULL);
362 sigaction (SIGINT, &save_sigint, NULL);
363 sigaction (SIGWINCH, &save_sigwinch, NULL);
365 #endif
367 static void
368 set_control_chars (gchar *control_chars, const guchar *cc)
370 /* The index into the array comes from corlib/System/ControlCharacters.cs */
371 #ifdef VINTR
372 control_chars [0] = cc [VINTR];
373 #endif
374 #ifdef VQUIT
375 control_chars [1] = cc [VQUIT];
376 #endif
377 #ifdef VERASE
378 control_chars [2] = cc [VERASE];
379 #endif
380 #ifdef VKILL
381 control_chars [3] = cc [VKILL];
382 #endif
383 #ifdef VEOF
384 control_chars [4] = cc [VEOF];
385 #endif
386 #ifdef VTIME
387 control_chars [5] = cc [VTIME];
388 #endif
389 #ifdef VMIN
390 control_chars [6] = cc [VMIN];
391 #endif
392 #ifdef VSWTC
393 control_chars [7] = cc [VSWTC];
394 #endif
395 #ifdef VSTART
396 control_chars [8] = cc [VSTART];
397 #endif
398 #ifdef VSTOP
399 control_chars [9] = cc [VSTOP];
400 #endif
401 #ifdef VSUSP
402 control_chars [10] = cc [VSUSP];
403 #endif
404 #ifdef VEOL
405 control_chars [11] = cc [VEOL];
406 #endif
407 #ifdef VREPRINT
408 control_chars [12] = cc [VREPRINT];
409 #endif
410 #ifdef VDISCARD
411 control_chars [13] = cc [VDISCARD];
412 #endif
413 #ifdef VWERASE
414 control_chars [14] = cc [VWERASE];
415 #endif
416 #ifdef VLNEXT
417 control_chars [15] = cc [VLNEXT];
418 #endif
419 #ifdef VEOL2
420 control_chars [16] = cc [VEOL2];
421 #endif
424 MonoBoolean
425 ves_icall_System_ConsoleDriver_TtySetup (MonoStringHandle keypad, MonoStringHandle teardown, MonoArrayHandleOut control_chars, int **size, MonoError* error)
427 // FIXME Lock around the globals?
429 int dims;
431 dims = terminal_get_dimensions ();
432 if (dims == -1){
433 int cols = 0, rows = 0;
435 char *str = g_getenv ("COLUMNS");
436 if (str != NULL) {
437 cols = atoi (str);
438 g_free (str);
440 str = g_getenv ("LINES");
441 if (str != NULL) {
442 rows = atoi (str);
443 g_free (str);
446 if (cols != 0 && rows != 0)
447 cols_and_lines = (cols << 16) | rows;
448 else
449 cols_and_lines = -1;
450 } else {
451 cols_and_lines = dims;
454 *size = &cols_and_lines;
456 /* 17 is the number of entries set in set_control_chars() above.
457 * NCCS is the total size, but, by now, we only care about those 17 values*/
458 MonoArrayHandle control_chars_arr = mono_array_new_handle (mono_domain_get (), mono_defaults.byte_class, 17, error);
459 return_val_if_nok (error, FALSE);
461 MONO_HANDLE_ASSIGN (control_chars, control_chars_arr);
462 if (tcgetattr (STDIN_FILENO, &initial_attr) == -1)
463 return FALSE;
465 mono_attr = initial_attr;
466 mono_attr.c_lflag &= ~(ICANON);
467 mono_attr.c_iflag &= ~(IXON|IXOFF);
468 mono_attr.c_cc [VMIN] = 1;
469 mono_attr.c_cc [VTIME] = 0;
470 #ifdef VDSUSP
471 /* Disable C-y being used as a suspend character on OSX */
472 mono_attr.c_cc [VDSUSP] = 255;
473 #endif
474 gint ret;
475 do {
476 MONO_ENTER_GC_SAFE;
477 ret = tcsetattr (STDIN_FILENO, TCSANOW, &mono_attr);
478 MONO_EXIT_GC_SAFE;
479 } while (ret == -1 && errno == EINTR);
481 if (ret == -1)
482 return FALSE;
484 uint32_t h;
485 set_control_chars (MONO_ARRAY_HANDLE_PIN (control_chars_arr, gchar, 0, &h), mono_attr.c_cc);
486 mono_gchandle_free_internal (h);
487 /* If initialized from another appdomain... */
488 if (setup_finished)
489 return TRUE;
491 keypad_xmit_str = NULL;
492 if (!MONO_HANDLE_IS_NULL (keypad)) {
493 keypad_xmit_str = mono_string_handle_to_utf8 (keypad, error);
494 return_val_if_nok (error, FALSE);
497 console_set_signal_handlers ();
498 setup_finished = TRUE;
499 if (!atexit_called) {
500 if (!MONO_HANDLE_IS_NULL (teardown)) {
501 teardown_str = mono_string_handle_to_utf8 (teardown, error);
502 return_val_if_nok (error, FALSE);
505 mono_atexit (tty_teardown);
508 return TRUE;