File: terminal.cpp

package info (click to toggle)
dmtcp 2.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,496 kB
  • sloc: cpp: 33,592; ansic: 28,099; sh: 6,735; makefile: 1,950; perl: 1,690; python: 1,241; asm: 138; java: 13
file content (129 lines) | stat: -rw-r--r-- 4,261 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <termios.h>
#include <signal.h>
#include <sys/ioctl.h>
#include "config.h"
#ifdef HAS_PR_SET_PTRACER
# include <sys/prctl.h>
#endif
#include "dmtcp.h"
#include "../jalib/jassert.h"

/*************************************************************************
 *
 *  Save and restore terminal settings.
 *
 *************************************************************************/

static int saved_termios_exists = 0;
static struct termios saved_termios;
static struct winsize win;

static void save_term_settings();
static void restore_term_settings();

void dmtcp_Terminal_EventHook(DmtcpEvent_t event, DmtcpEventData_t *data)
{
  switch (event) {
    case DMTCP_EVENT_THREADS_SUSPEND:
      save_term_settings();
      break;

    case DMTCP_EVENT_THREADS_RESUME:
      if (data->resumeInfo.isRestart) {
        restore_term_settings();
        /* If DMTCP_RESTART_PAUSE2 set, sleep 15 seconds to allow gdb attach.*/
        if (getenv("MTCP_RESTART_PAUSE2") || getenv("DMTCP_RESTART_PAUSE2")) {
#ifdef HAS_PR_SET_PTRACER
          prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); //For: gdb attach
#endif
          struct timespec delay = {15, 0}; /* 15 seconds */
          printf("Pausing 15 seconds. Do:  gdb <PROGNAME> %d\n",
          dmtcp_virtual_to_real_pid(getpid()));
          nanosleep(&delay, NULL);
#ifdef HAS_PR_SET_PTRACER
          prctl(PR_SET_PTRACER, 0, 0, 0, 0); // Revert permission to default.
#endif
        }
      }
      break;

    default:
      break;
  }
}

static void save_term_settings()
{
  /* Drain stdin and stdout before checkpoint */
  tcdrain(STDOUT_FILENO);
  tcdrain(STDERR_FILENO);

  saved_termios_exists = ( isatty(STDIN_FILENO)
  		           && tcgetattr(STDIN_FILENO, &saved_termios) >= 0 );
  if (saved_termios_exists)
    ioctl (STDIN_FILENO, TIOCGWINSZ, (char *) &win);
}

static int safe_tcsetattr(int fd, int optional_actions,
                          const struct termios *termios_p)
{
  struct termios old_termios, new_termios;
  /* We will compare old and new, and we don't want uninitialized data */
  memset(&new_termios, 0, sizeof(new_termios));
  /* tcgetattr returns success as long as at least one of requested
   * changes was executed.  So, repeat until no more changes.
   */
  do {
    memcpy(&old_termios, &new_termios, sizeof(new_termios));
    if (tcsetattr(fd, TCSANOW, termios_p) == -1) return -1;
    if (tcgetattr(fd, &new_termios) == -1) return -1;
  } while (memcmp(&new_termios, &old_termios, sizeof(new_termios)) != 0);
  return 0;
}

// FIXME: Handle Virtual Pids
static void restore_term_settings()
{
  if (saved_termios_exists){
    /* First check if we are in foreground. If not, skip this and print
     *   warning.  If we try to call tcsetattr in background, we will hang up.
     */
    int foreground = (tcgetpgrp(STDIN_FILENO) == getpgrp());
    JLOG(DMTCP)("restore terminal attributes, check foreground status first")
      (foreground);
    if (foreground) {
      if ( ( ! isatty(STDIN_FILENO)
             || safe_tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios) == -1) ) {
        JWARNING(false) .Text("failed to restore terminal");
      }
      else {
        struct winsize cur_win;
        JLOG(DMTCP)("restored terminal");
        ioctl (STDIN_FILENO, TIOCGWINSZ, (char *) &cur_win);
	/* ws_row/ws_col was probably not 0/0 prior to checkpoint.  We change
	 * it back to last known row/col prior to checkpoint, and then send a
	 * SIGWINCH (see below) to notify process that window might have changed
	 */
        if (cur_win.ws_row == 0 && cur_win.ws_col == 0)
          ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win);
      }
    } else {
      JWARNING(false)
        .Text(":skip restore terminal step -- we are in BACKGROUND");
    }
  }
  /*
   * NOTE:
   * Apache, when running in debug mode (-X), uses SIGWINCH
   * as a signal for stopping gracefully. Please comment out
   * the next line to prevent DMTCP from sending a SIGWINCH
   * on restart when testing with Apache.
   *
   * TODO:
   * This should be done automatically by wrapping it in an ifdef
   * or if condition that disables the SIGWINCH using configure or
   * a runtime option (--no-sigwinch).
   */
  if (kill(getpid(), SIGWINCH) == -1) {}  /* No remedy if error */
}