summaryrefslogtreecommitdiff
path: root/dht/Presence/monitortty.c
blob: 7582aa560d146918d672d6e50cb2224b222b6055 (plain)
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// monitortty.c

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <linux/vt.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <stdlib.h>

static char *conspath[] = {
	"/proc/self/fd/0",
	"/dev/tty",
	"/dev/tty0",
	"/dev/vc/0",
	"/dev/systty",
	"/dev/console",
	NULL
};

static int
is_a_console(int fd) {
	char arg;

	arg = 0;
	return (isatty (fd)
		&& ioctl(fd, KDGKBTYPE, &arg) == 0
		&& ((arg == KB_101) || (arg == KB_84)));
}

static int
open_a_console(const char *fnam) {
	int fd;

	/*
	 * For ioctl purposes we only need some fd and permissions
	 * do not matter. But setfont:activatemap() does a write.
	 */
	fd = open(fnam, O_RDWR);
	if (fd < 0)
		fd = open(fnam, O_WRONLY);
	if (fd < 0)
		fd = open(fnam, O_RDONLY);
	if (fd < 0)
		return -1;
	return fd;
}

int ttyfd() {
	// We try several things because opening /dev/console will fail
	// if someone else used X (which does a chown on /dev/console).
	int i;
	int fd;
	for (i = 0; conspath[i]; i++) {
		if ((fd = open_a_console(conspath[i])) >= 0) {
			if (is_a_console(fd)) {
				printf("using %s\n",conspath[i]);
				return fd;
			}
			close(fd);
		}
	}
	for (fd = 0; fd < 3; fd++)
		if (is_a_console(fd))
			return fd;
	printf("failed to find console fd\n");
	return -1;
}

void vt_wait(int tty_fd) {
	struct vt_event vt;
	memset(&vt,'\0',sizeof(vt));
	vt.event = VT_EVENT_SWITCH;
	int res;
	// printf("started wait\n");
	res = ioctl (tty_fd, VT_WAITEVENT, &vt);
	if (res==-1) {
		printf("vt_wait error fd=%i\n",tty_fd);
		perror("vt_wait");
		// printf("vt_wait: %u - %s\n", errno, errmsg(errno));
		sleep(1);
	}
	// printf("finished wait\n");
}

int8_t get_active(int tty_fd) {
	struct vt_stat vtstat;
	memset(&vtstat,'\0',sizeof(vtstat));
	if (ioctl(tty_fd, VT_GETSTATE, &vtstat)) {
		perror ("get_active: VT_GETSTATE");
		return 7;
	}
	return vtstat.v_active;
}

void chvt(int tty_fd, int n) {
	if (ioctl(tty_fd, VT_ACTIVATE, n)) {
		perror ("chvt: VT_ACTIVATE");
	}

}

pthread_mutex_t mu;
pthread_t mt;
int tty = -1;

void *write_vtch(void *pfd) {
	int fd = (int)(intptr_t)pfd;
	printf("started VT_WAITEVENT loop fd=%i\n",fd);
	pthread_mutex_lock(&mu);
	tty = ttyfd();
	pthread_mutex_unlock(&mu);
	int8_t active_tty = get_active(tty);
	int8_t reported_tty;
	ssize_t e;

	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	for (;;) {
		// ssize_t write(int fd, const void *buf, size_t count);
		e = write(fd, &active_tty, 1);
		if (e<0 ) {
			if( errno==EAGAIN) continue;
			break;
		}
		else if(e==1) {
			reported_tty = active_tty;
		}
		do {
			vt_wait(tty);
			// printf("vt_wait() finished. tty=%d fd=%d\n",tty,fd);
			active_tty = get_active(tty);
		} while (active_tty==reported_tty);
	}
	
	// TODO:
	//  use VT_GETSTATE
	//  use VT_WAITEVENT
	printf("stopped VT_WAITEVENT loop\n");
	tty = -1;
	pthread_mutex_destroy(&mu);
	return NULL;
}


// Returns 0 on success.
int monitorTTY(int fd) {
    int er = -1, dev = -1;
	pthread_mutex_init(&mu,NULL);
    // Ensure we can open a device before we bother forking a thread.
	dev = ttyfd();
    if( dev != -1 ) {
        er = pthread_create (&mt, NULL, write_vtch, (void*)(intptr_t)fd);
        return er;
    }
    else {
        return -1;
    }
}

void closeTTY() {
	int fd = -1;
	int active = 7;
	pthread_mutex_lock(&mu);
	active = get_active(tty);
	fd = tty;
	pthread_mutex_unlock(&mu);
#ifndef VTHACK
	pthread_cancel(mt);
#endif
	char cmd[40]; cmd[39] = '\0';
	// Hack to wake up from VT_WAITEVENT ioctl
#ifdef VTHACK
	snprintf(cmd,39,"chvt %i;chvt %i",active+1,active);
	system(cmd);
	pthread_join(mt,NULL);
#endif
	close(fd);
}