// monitortty.c #include #include #include #include #include #include #include #include #include #include #include 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); }