File: pty2.c

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 (139 lines) | stat: -rw-r--r-- 4,339 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
130
131
132
133
134
135
136
137
138
139
// Compile with -DDEBUG for debugging failure modes.

// grantpt, posix_openpt, etc., needs _XOPEN_SOURCE set to 600
#define _XOPEN_SOURCE 600
// Using _XOPEN_SOURCE to ensure ptsname returns 'char *' (recommended by Open Group)
#define _DEFAULT_SOURCE
// _DEFAULT_SOURCE used to expose sys_errlist[]
#include <stdio.h>
#include <pty.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <termios.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>

// Define DEBUG when running manually, to see what part of terminal
// was not restored properly:
// #define DEBUG

#ifdef DEBUG
// printing to stdout won't work, when it goes to /dev/pts/XX
# define PERROR(str) fprintf(fdopen(orig_stdout, "w"), \
			     #str ": %s\n", sys_errlist[errno])
# else
# define PERROR(str)
#endif

int setupslave(char *slavedevice, int orig_stdout);
int testslave(char *slavedevice, struct termios * orig_termios_p,
              int orig_stdout);

int main() {
  int masterfd, slavefd, pid;
  char *slavedevice;
#ifdef DEBUG
  int orig_stdout = dup(1);
#else
  int orig_stdout = -1;
#endif

  masterfd = posix_openpt(O_RDWR|O_NOCTTY);
  if (masterfd == -1
      || grantpt(masterfd) == -1
      || unlockpt(masterfd) == -1
      || (slavedevice = ptsname(masterfd)) == NULL)
    return 1;

  // Calling ptsname only once.  So, it's safe to continue using slavedevice.
  printf("slave device is: %s\n", slavedevice);
  slavefd = open(slavedevice, O_RDWR|O_NOCTTY);
  if (slavefd < 0)
    return 2;
  close(slavefd);
  if ((pid = fork()) < 0) {
    PERROR("fork");
    return 3;
  }
  if (pid == 0) {
    if (setupslave(slavedevice, orig_stdout) == 0) {
      struct termios orig_termios;
      tcgetattr(1, &orig_termios); /* fd 1 is now the slave terminal */
      int ppid = getppid();
      while (1) {
        testslave(slavedevice, &orig_termios, orig_stdout);
        if (kill(ppid, 0) == -1) /* If parent process died, then exit. */
          return 0;
      }
    }
  } else
    if (waitpid(pid, NULL, 0) == -1)
      PERROR("waitpid");
  return 0; /* Never returns */
}

int setupslave(char *slavedevice, int orig_stdout) {
  int fd;
  alarm(150); /* For safety; will not die when controlling terminal removed. */
  /* We are neither a session leader nor process group leader.
   * So, we are eligible to call setsid and become a new session/proc. grp ldr.
   */
  if (setsid() == -1) { /* set new sid and pgid */
    PERROR("setsid");
    return -1;
  }
  fd = open("/dev/tty", O_RDWR);
  if (fd != -1) /* if we have a controlling terminai, get rid of it. */
    ioctl(fd, TIOCNOTTY);
  /* We are now leader of a session and process group, without a
   * controlling terminal.  We are now eligible for controlling terminal.
   */
  close(0);
  close(1);
  close(2);
  fd = open(slavedevice, O_RDWR); /* Gains new controlling terminal */
  /* Alternative way to set controlling terminal:  ioctl(fd, TIOCSCTTY) */
  if (dup(fd) == -1) return 1;
  if (dup(fd) == -1) return 1;
  return 0;
}

int testslave(char *slavedevice, struct termios * orig_termios_p,
              int orig_stdout) {
  struct termios curr_termios;
  int fd = open(slavedevice, O_RDWR);;
  if (fd == -1)
    exit(1);

#ifdef DEBUG
  FILE *stream = fdopen(orig_stdout, "w");
  fprintf(stream, "pid: %d, ppid: %d, sid: %d, pgid: %d\n",
	  getpid(), getppid(), getsid(getpid()), getpgid(getpid()));
  fprintf(stream, "tcgetsid:  session id of %s is: %d\n",
	  slavedevice, tcgetsid(fd));
  if (tcgetattr(1, &curr_termios) == 0); /* fd 1 is now the slave terminal */
    fprintf(stream, "c_iflag: %d, c_oflag: %d, c_cflag: %d, c_lflag: %d\n",
            curr_termios.c_iflag, curr_termios.c_oflag,
            curr_termios.c_cflag, curr_termios.c_lflag);
  /* input, output, control, local modes */
  sleep(2);
#else
  if (getpid() != getsid(getpid()) || getpid() != getpgid(getpid()))
    exit(1);
  if (getpid() != tcgetsid(fd))
    exit(1);
  if (tcgetattr(1, &curr_termios) == -1) /* fd 1 is now the slave terminal */
    exit(1);
  if (curr_termios.c_iflag != orig_termios_p->c_iflag
      || curr_termios.c_oflag != orig_termios_p->c_oflag
      || curr_termios.c_cflag != orig_termios_p->c_cflag
      || curr_termios.c_lflag != orig_termios_p->c_lflag)
    exit(1);
#endif
  close(fd);
  return 0;
}