root/terminal.cpp
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- dmtcp_Terminal_EventHook
- save_term_settings
- safe_tcsetattr
- restore_term_settings
1 #include <termios.h>
2 #include <signal.h>
3 #include <sys/ioctl.h>
4 #include "config.h"
5 #ifdef HAS_PR_SET_PTRACER
6 # include <sys/prctl.h>
7 #endif
8 #include "dmtcp.h"
9 #include "../jalib/jassert.h"
10
11 /*************************************************************************
12 *
13 * Save and restore terminal settings.
14 *
15 *************************************************************************/
16
17 static int saved_termios_exists = 0;
18 static struct termios saved_termios;
19 static struct winsize win;
20
21 static void save_term_settings();
22 static void restore_term_settings();
23
24 void dmtcp_Terminal_EventHook(DmtcpEvent_t event, DmtcpEventData_t *data)
25 {
26 switch (event) {
27 case DMTCP_EVENT_THREADS_SUSPEND:
28 save_term_settings();
29 break;
30
31 case DMTCP_EVENT_THREADS_RESUME:
32 if (data->resumeInfo.isRestart) {
33 restore_term_settings();
34 /* If DMTCP_RESTART_PAUSE2 set, sleep 15 seconds to allow gdb attach.*/
35 if (getenv("MTCP_RESTART_PAUSE2") || getenv("DMTCP_RESTART_PAUSE2")) {
36 #ifdef HAS_PR_SET_PTRACER
37 prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); //For: gdb attach
38 #endif
39 struct timespec delay = {15, 0}; /* 15 seconds */
40 printf("Pausing 15 seconds. Do: gdb <PROGNAME> %d\n",
41 dmtcp_virtual_to_real_pid(getpid()));
42 nanosleep(&delay, NULL);
43 #ifdef HAS_PR_SET_PTRACER
44 prctl(PR_SET_PTRACER, 0, 0, 0, 0); // Revert permission to default.
45 #endif
46 }
47 }
48 break;
49
50 default:
51 break;
52 }
53 }
54
55 static void save_term_settings()
56 {
57 /* Drain stdin and stdout before checkpoint */
58 tcdrain(STDOUT_FILENO);
59 tcdrain(STDERR_FILENO);
60
61 saved_termios_exists = ( isatty(STDIN_FILENO)
62 && tcgetattr(STDIN_FILENO, &saved_termios) >= 0 );
63 if (saved_termios_exists)
64 ioctl (STDIN_FILENO, TIOCGWINSZ, (char *) &win);
65 }
66
67 static int safe_tcsetattr(int fd, int optional_actions,
68 const struct termios *termios_p)
69 {
70 struct termios old_termios, new_termios;
71 /* We will compare old and new, and we don't want uninitialized data */
72 memset(&new_termios, 0, sizeof(new_termios));
73 /* tcgetattr returns success as long as at least one of requested
74 * changes was executed. So, repeat until no more changes.
75 */
76 do {
77 memcpy(&old_termios, &new_termios, sizeof(new_termios));
78 if (tcsetattr(fd, TCSANOW, termios_p) == -1) return -1;
79 if (tcgetattr(fd, &new_termios) == -1) return -1;
80 } while (memcmp(&new_termios, &old_termios, sizeof(new_termios)) != 0);
81 return 0;
82 }
83
84 // FIXME: Handle Virtual Pids
85 static void restore_term_settings()
86 {
87 if (saved_termios_exists){
88 /* First check if we are in foreground. If not, skip this and print
89 * warning. If we try to call tcsetattr in background, we will hang up.
90 */
91 int foreground = (tcgetpgrp(STDIN_FILENO) == getpgrp());
92 JTRACE("restore terminal attributes, check foreground status first")
93 (foreground);
94 if (foreground) {
95 if ( ( ! isatty(STDIN_FILENO)
96 || safe_tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios) == -1) )
97 JWARNING(false) .Text("failed to restore terminal");
98 else {
99 struct winsize cur_win;
100 JTRACE("restored terminal");
101 ioctl (STDIN_FILENO, TIOCGWINSZ, (char *) &cur_win);
102 /* ws_row/ws_col was probably not 0/0 prior to checkpoint. We change
103 * it back to last known row/col prior to checkpoint, and then send a
104 * SIGWINCH (see below) to notify process that window might have changed
105 */
106 if (cur_win.ws_row == 0 && cur_win.ws_col == 0)
107 ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win);
108 }
109 } else {
110 JWARNING(false)
111 .Text(":skip restore terminal step -- we are in BACKGROUND");
112 }
113 }
114 /*
115 * NOTE:
116 * Apache, when running in debug mode (-X), uses SIGWINCH
117 * as a signal for stopping gracefully. Please comment out
118 * the next line to prevent DMTCP from sending a SIGWINCH
119 * on restart when testing with Apache.
120 *
121 * TODO:
122 * This should be done automatically by wrapping it in an ifdef
123 * or if condition that disables the SIGWINCH using configure or
124 * a runtime option (--no-sigwinch).
125 */
126 if (kill(getpid(), SIGWINCH) == -1) {} /* No remedy if error */
127 }
128