├── Makefile.am ├── configure.ac ├── README.md ├── qkill.c ├── fakecygpty.el └── fakecygpty.c /Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = fakecygpty qkill 2 | fakecygpty_SOURCES = fakecygpty.c 3 | qkill_SOURCES = qkill.c 4 | AM_CFLAGS = -D_GNU_SOURCE 5 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.69]) 5 | AC_INIT([fakecygpty], [1.1.0], [d5884jp@gmail.com]) 6 | AC_CONFIG_AUX_DIR([build-aux]) 7 | AM_INIT_AUTOMAKE([-Wall foreign]) 8 | AC_CONFIG_SRCDIR([fakecygpty.c]) 9 | AC_CONFIG_HEADERS([config.h]) 10 | 11 | # Checks for programs. 12 | AC_PROG_CC 13 | 14 | # Checks for libraries. 15 | 16 | # Checks for header files. 17 | AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/ioctl.h termios.h unistd.h]) 18 | 19 | # Checks for typedefs, structures, and compiler characteristics. 20 | AC_TYPE_PID_T 21 | AC_TYPE_SIZE_T 22 | AC_TYPE_SSIZE_T 23 | 24 | # Checks for library functions. 25 | AC_FUNC_FORK 26 | AC_CHECK_FUNCS([dup2 memchr memset select strdup strerror strrchr strstr strtol]) 27 | 28 | AC_CONFIG_FILES([Makefile]) 29 | AC_OUTPUT 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fakecygpty 2 | ============ 3 | 4 | Introduction 5 | ------------ 6 | This package supplies cygwin's pty feature for NTEmacs. 7 | If you want to use the command requires pty such as `bash`, try this. 8 | 9 | * `shell-mode` or `ansi-term` with cygwin's shell like `bash` will be run correctly. 10 | * I/O buffer on `gdb-mode` will be run correctly than native one. 11 | * `signal-process' can send arbitrary signals, except it was called interactively. 12 | 13 | An original version of `fakecygpty` was written by Kyotaro Horiguchi for Meadow project in 2005. 14 | In this package, improved following points: 15 | 16 | * Fix a TTY initialization parameters to fit current version of Emacs. 17 | * Fix to sub-process returns the correct return value. 18 | * Support pty allocation mode. (for `gdb-many-windows`) 19 | * Support window resizing. 20 | 21 | Installation 22 | ------------ 23 | 24 | Compile `fakecygpty.c` and `qkill.c` on cygwin enviroment: 25 | 26 | gcc -D_GNU_SOURCE -o fakecygpty fakecygpty.c 27 | gcc -o qkill qkill.c 28 | 29 | And copy *.exe into somewhere on you $PATH. 30 | 31 | If `autoreconf` package is installed, you can use configure script: 32 | 33 | autoreconf -ivf && ./configure && make install 34 | 35 | `fakecygpty.el` isn't installed automatically. put into your `load-path` by self. 36 | 37 | How to use 38 | ---------- 39 | If you want to activate only to a specific program, rename `fakecygpty.exe` to 40 | `f_TARGET.exe` like `f_ssh.exe`, and invoke it. 41 | 42 | If you want to apply to all, put below into your `init.el`: 43 | 44 | (require 'fakecygpty) 45 | (fakecygpty-activate) 46 | 47 | If you want to use new features (terminal window resizing and pty allocation mode), 48 | it's necessary to use this elisp file. 49 | And if you want to disable `fakecygpty` temporary, type `M-x fakecygpty-deactivate`. 50 | 51 | Customizing 52 | ----------- 53 | 54 | ### Ignored programs 55 | Some program using Windows native console, such as `cmd.exe`, don't work well 56 | with `fakecygpty.exe`. 57 | 58 | * `fakecygpty-ignored-program-regexps`: Set regexp list of program name that 59 | you don't want to apply `fakecygpty`. 60 | 61 | ### Path of programs 62 | 63 | * `fakecygpty-program`: Path of `fakecygpty.exe`. 64 | * `fakecygpty-qkill-program`: Path of `qkill.exe`. 65 | -------------------------------------------------------------------------------- /qkill.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Send signal by sigqueue(3) -- qkill -- 3 | * 4 | * Copyright (C) 2014 Daisuke Kobayashi 5 | * 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /* 22 | * COMPILATION 23 | * ------- 24 | * gcc -o qkill.exe qkill.c 25 | * 26 | */ 27 | 28 | #ifdef HAVE_CONFIG_H 29 | #include "config.h" 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #define PROGNAME "qkill" 46 | 47 | typedef enum { TRUE = 1, FALSE = 0 } bool_t; 48 | 49 | struct parsed_argv { 50 | bool_t pid_is_winpid; /* using pid as windows pid */ 51 | bool_t verbose; /* output more verbosely */ 52 | bool_t use_sigqueue; /* using sigqueue instead of kill */ 53 | char *tty_name; /* try to send tty's foreground pgrp */ 54 | bool_t excepting; /* use except pgid */ 55 | pid_t except_pgid; /* don't send signal when tty's foreground pgrp's pgid is this */ 56 | int signum; /* signal number */ 57 | int sigval; /* signal value */ 58 | }; 59 | 60 | bool_t parse_argv(int argc, char*argv[], struct parsed_argv *result, int *next_index); 61 | void usage(); 62 | 63 | pid_t get_foreground_pgrp(char *tty_name); 64 | 65 | bool_t string_to_integer(const char *str, int *ret); 66 | bool_t string_to_signum(const char *str, int *ret); 67 | bool_t signame_to_signum(const char*signame, int *ret); 68 | 69 | int main(int argc, char *argv[]) 70 | { 71 | struct parsed_argv params; 72 | bool_t succeeded = FALSE; 73 | int i; 74 | 75 | 76 | if (!parse_argv(argc, argv, ¶ms, &i)) 77 | exit(EXIT_FAILURE); 78 | 79 | /* tty's foreground pgid kill mode */ 80 | if (params.tty_name != NULL) { 81 | pid_t pgid = get_foreground_pgrp(params.tty_name); 82 | if (pgid <= 0) { 83 | fprintf(stderr, "%s: Cannot obtain foreground process group on %s - %s\n", 84 | PROGNAME, params.tty_name, strerror(errno)); 85 | exit(EXIT_FAILURE); 86 | } 87 | 88 | if (params.excepting && params.except_pgid == pgid) { 89 | if (params.verbose) 90 | fprintf(stderr, "info:tty's pgid = except pgid. dont kill.\n"); 91 | } else { 92 | if (params.verbose) 93 | fprintf(stderr, "invoke:kill(pid=%d(%s),signum=%d)\n", 94 | -pgid, params.tty_name, params.signum); 95 | 96 | if (kill(-pgid, params.signum) != 0) { 97 | fprintf(stderr, "%s:(%d on %s) %s\n", PROGNAME, pgid, params.tty_name, strerror(errno)); 98 | exit(EXIT_FAILURE); 99 | } 100 | } 101 | exit(EXIT_SUCCESS); 102 | } 103 | 104 | /* normal kill mode */ 105 | for (; i < argc; i++) { 106 | pid_t pid; 107 | union sigval sigval; 108 | 109 | if (!string_to_integer(argv[i], &pid)) { 110 | fprintf(stderr, "%s: Invalid pid - %s\n", PROGNAME, argv[i]); 111 | continue; 112 | } 113 | if (params.pid_is_winpid) { 114 | pid_t cyg_pid = cygwin_winpid_to_pid(abs(pid)); 115 | if (cyg_pid < 0) { 116 | fprintf(stderr, "%s: Not a cygwin process's pid - %s\n", PROGNAME, argv[i]); 117 | continue; 118 | } 119 | 120 | pid = pid < 0 ? -cyg_pid : cyg_pid; 121 | 122 | if (params.verbose) 123 | fprintf(stderr, "convert:win_pid=%s => cyg_pid=%d\n", argv[i], pid); 124 | } 125 | 126 | if (params.use_sigqueue) { 127 | if (params.verbose) 128 | fprintf(stderr, "invoke:sigqueue(pid=%d,signum=%d,sigval=%#08x)\n", 129 | pid, params.signum, params.sigval); 130 | sigval.sival_int = params.sigval; 131 | if (sigqueue(pid, params.signum, sigval) != 0) { 132 | fprintf(stderr, "%s:(%d) %s\n", PROGNAME, pid, strerror(errno)); 133 | } 134 | } else { 135 | if (params.verbose) 136 | fprintf(stderr, "invoke:kill(pid=%d,signum=%d)\n", pid, params.signum); 137 | if (kill(pid, params.signum) != 0) { 138 | fprintf(stderr, "%s:(%d) %s\n", PROGNAME, pid, strerror(errno)); 139 | } 140 | } 141 | 142 | succeeded = TRUE; 143 | } 144 | 145 | exit(succeeded ? EXIT_SUCCESS : EXIT_FAILURE); 146 | } 147 | 148 | bool_t parse_argv(int argc, char*argv[], struct parsed_argv *result, int *next_index) 149 | { 150 | bool_t flag_pid_found = FALSE; 151 | int opt; 152 | 153 | 154 | memset(result, 0, sizeof(struct parsed_argv)); 155 | result->signum = SIGTERM; 156 | 157 | opterr = 0; /* suppress auto error */ 158 | while(!flag_pid_found && (opt = getopt(argc, argv, "+vws:S:i:t:e:h")) != -1) { 159 | switch(opt) { 160 | case 'w': 161 | result->pid_is_winpid = TRUE; 162 | break; 163 | 164 | case 's': 165 | case 'S': /* for -S[IGSOME] to some */ 166 | if (!string_to_signum(optarg, &result->signum)) { 167 | fprintf(stderr, "%s: Unknown signal: %s\n", PROGNAME, optarg); 168 | return FALSE; 169 | } 170 | break; 171 | 172 | case 'i': 173 | if (!string_to_integer(optarg, &result->sigval)) { 174 | fprintf(stderr, "%s: Invalid sigval: %s\n", PROGNAME, optarg); 175 | return FALSE; 176 | } 177 | result->use_sigqueue = TRUE; 178 | break; 179 | 180 | case 't': 181 | result->tty_name = strdup(optarg); 182 | break; 183 | 184 | case 'e': 185 | if (!string_to_integer(optarg, &result->except_pgid)) { 186 | fprintf(stderr, "%s: Invalid excepting pgid: %s\n", PROGNAME, optarg); 187 | return FALSE; 188 | } 189 | result->excepting = TRUE; 190 | break; 191 | 192 | case 'v': 193 | result->verbose = TRUE; 194 | break; 195 | 196 | case 'h': 197 | usage(); 198 | return FALSE; 199 | 200 | case '?': 201 | if (isdigit(optopt)) { 202 | flag_pid_found = TRUE; 203 | } else { 204 | fprintf(stderr, "%s: Invalid option: -%c\n", PROGNAME, optopt); 205 | return FALSE; 206 | } 207 | } 208 | } 209 | 210 | if (optind >= argc && result->tty_name == NULL) { 211 | usage(); 212 | return FALSE; 213 | } else if (optind < argc && result->tty_name != NULL) { 214 | fprintf(stderr, "%s: Can't specify tty and pid at same time.\n", PROGNAME); 215 | usage(); 216 | return FALSE; 217 | } 218 | 219 | *next_index = optind; 220 | 221 | return TRUE; 222 | } 223 | 224 | void usage() 225 | { 226 | fprintf(stderr, "usage: %s [-wv] [-s sigcode] [-i sigval] pid [pids...]\n" 227 | " %s -t [-e pgid]\n\n" 228 | "Send signal by sigqueue(3).\n\n" 229 | " -w pid is windows pid\n" 230 | " -v verbose output\n" 231 | " -s sigcode send signal with sigcode (default: SIGTERM)\n" 232 | " -i sigval send signal with sigval\n" 233 | " -t tty-path send signal to tty_name's foreground process group\n" 234 | " -e pgid don't send signal when tty's foreground pgid is this pgid\n" 235 | " -h show this help\n\n", 236 | PROGNAME, 237 | PROGNAME); 238 | } 239 | 240 | pid_t get_foreground_pgrp(char *tty_name) 241 | { 242 | int pty_fd; 243 | int pipe_fd[2]; 244 | int ret; 245 | int status; 246 | 247 | pipe(pipe_fd); 248 | 249 | pid_t pid = fork(); 250 | if (pid < 0) { 251 | return -1; 252 | } else if (pid == 0) { 253 | /* discard current ctty */ 254 | if (setsid() < 0) { 255 | int saved_errno = errno; 256 | write(pipe_fd[1], &saved_errno, sizeof(saved_errno)); 257 | exit(EXIT_FAILURE); 258 | } 259 | 260 | /* open target's tty */ 261 | pty_fd = open(tty_name, O_RDWR); 262 | if (pty_fd < 0) { 263 | int saved_errno = errno; 264 | write(pipe_fd[1], &saved_errno, sizeof(saved_errno)); 265 | exit(EXIT_FAILURE); 266 | } 267 | 268 | /* get foreground pgrp */ 269 | ret = tcgetpgrp(pty_fd); 270 | if (ret < 0) { 271 | int saved_errno = errno; 272 | write(pipe_fd[1], &saved_errno, sizeof(saved_errno)); 273 | exit(EXIT_FAILURE); 274 | } 275 | 276 | write(pipe_fd[1], &ret, sizeof(ret)); 277 | 278 | exit(EXIT_SUCCESS); 279 | } 280 | read(pipe_fd[0], &ret, sizeof(ret)); 281 | waitpid(pid, &status, 0); 282 | 283 | if (WIFEXITED(status) && WEXITSTATUS(status) != EXIT_SUCCESS) { 284 | errno = ret; 285 | return -1; 286 | } 287 | 288 | return ret; 289 | } 290 | 291 | bool_t string_to_integer(const char *str, int *ret) 292 | { 293 | char *endptr; 294 | 295 | *ret = strtol(str, &endptr, 10); 296 | if (endptr == str) 297 | return FALSE; 298 | else 299 | return TRUE; 300 | } 301 | 302 | bool_t string_to_signum(const char *str, int *ret) 303 | { 304 | if (signame_to_signum(str, ret)) 305 | return TRUE; 306 | 307 | return string_to_integer(str, ret); 308 | } 309 | 310 | bool_t signame_to_signum(const char*signame, int *ret) 311 | { 312 | char *upname; 313 | int signum; 314 | int search_base = 0; 315 | 316 | if ((upname = strdup(signame)) == NULL) 317 | return FALSE; 318 | strupr(upname); 319 | 320 | if (strstr(upname, "IG") == upname) /* for -s[igsome] to some */ 321 | search_base = 1; 322 | else if (strstr(upname, "SIG") != upname) 323 | search_base = 3; 324 | 325 | for (signum = 0; signum < NSIG; signum++) { 326 | if (sys_sigabbrev[signum] != NULL) { 327 | if (strcmp(upname, sys_sigabbrev[signum] + search_base) == 0) { 328 | *ret = signum; 329 | 330 | free(upname); 331 | return TRUE; 332 | } 333 | } 334 | } 335 | 336 | free(upname); 337 | return FALSE; 338 | } 339 | -------------------------------------------------------------------------------- /fakecygpty.el: -------------------------------------------------------------------------------- 1 | ;;; fakecygpty.el --- Support for using cygwin pty with NTEmacs. 2 | 3 | ;; Copyright (C) 2014, 2016 Daisuke Kobayashi 4 | 5 | ;; Author: Daisuke Kobayashi 6 | ;; Version: 0.1 7 | ;; Keywords: processes 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; This package supplies cygwin's pty feature for NTEmacs. 25 | ;; If you want to use the command requires pty such as `bash', try this. 26 | 27 | ;;; Install: 28 | 29 | ;; 1. Compile `fakecygpty.exe' and `qkill.exe', and copy them into somewhere on `exec-path'. 30 | ;; 2. Copy this .el into somewhere on `load-path'. 31 | 32 | ;;; Usage: 33 | 34 | ;; Put below into your init.el: 35 | 36 | ;; (require 'fakecygpty) 37 | ;; (fakecygpty-activate) 38 | 39 | ;;; Code: 40 | 41 | (defgroup fakecygpty nil 42 | "Execute cygwin pty commands using pipe." 43 | :group 'processes) 44 | 45 | (defcustom fakecygpty-program (executable-find "fakecygpty") 46 | "Program file name of fakecygpty." 47 | :group 'fakecygpty 48 | :type 'file) 49 | 50 | (defcustom fakecygpty-qkill-program (executable-find "qkill") 51 | "Program file name of qkill." 52 | :group 'fakecygpty 53 | :type 'file) 54 | 55 | (defcustom fakecygpty-ignored-program-regexps 56 | '("[cC][mM][dD]" 57 | "[cC][mM][dD][pP][rR][oO][xX][yY]") 58 | "Regexp list for program that run without fakecygpty." 59 | :group 'fakecygpty 60 | :type '(repeat regexp)) 61 | 62 | (defvar fakecygpty--advice-defined nil 63 | "If t, advices for fakecygpty are defined.") 64 | 65 | (defvar fakecygpty--activated nil 66 | "If t, fakecygpty is activated.") 67 | 68 | (defconst fakecygpty--advice-regexp "^fakecygpty--" 69 | "Regexp for advice name of fakecygpty package.") 70 | 71 | ;;;###autoload 72 | (defun fakecygpty-activate () 73 | "Activate fakecygpty features." 74 | (interactive) 75 | (when (and (not fakecygpty--activated) 76 | (memq system-type '(ms-dos windows-nt)) 77 | fakecygpty-program 78 | (executable-find fakecygpty-program) 79 | fakecygpty-qkill-program 80 | (executable-find fakecygpty-qkill-program)) 81 | (unless fakecygpty--advice-defined 82 | (fakecygpty--make-advice) 83 | (setq fakecygpty--advice-defined t)) 84 | 85 | (ad-enable-regexp fakecygpty--advice-regexp) 86 | (ad-activate-regexp fakecygpty--advice-regexp) 87 | (setq fakecygpty--activated t))) 88 | 89 | ;;;###autoload 90 | (defun fakecygpty-deactivate () 91 | "Deactivate fakecygpty features." 92 | (interactive) 93 | (when fakecygpty--activated 94 | (ad-disable-regexp fakecygpty--advice-regexp) 95 | (ad-activate-regexp fakecygpty--advice-regexp) 96 | (setq fakecygpty--activated nil))) 97 | 98 | (defun fakecygpty-process-p (process) 99 | "Non-nil when PROCESS was invoked by fakecygpty." 100 | (and (processp process) 101 | (process-get process :fakecygpty-p))) 102 | 103 | (defun fakecygpty-qkill (process sigcode &optional sigval as-winpid tty except) 104 | "Send PROCESS the signal with code SIGCODE by `fakecygpty-qkill-program'. 105 | PROCESS may also be a number specifying the process id of the 106 | process to signal; in this case, the process need not be a child of 107 | this Emacs. 108 | SIGCODE may be an integer, or a symbol whose name is a signal name. 109 | SIGVAL may be integer. if it's nil, 0 will be used. 110 | If AS-WINPID is non-nil, PROCESS is considered as windows pid. 111 | If TTY is specified, send signal to TTY's foreground process group. 112 | If EXCEPT is non-nil and TTY is specified, don't send signal when 113 | TTY's foreground process group pgid equals PROCESS pid." 114 | (let ((pid (cond 115 | ((integerp process) 116 | process) 117 | ((processp process) 118 | (process-id process)) 119 | ((stringp process) 120 | (ignore-errors (process-id (get-process process)))) 121 | ((null process) 122 | (ignore-errors (process-id (get-buffer-process 123 | (current-buffer))))) 124 | (t nil)))) 125 | (when pid 126 | (zerop (apply 'call-process fakecygpty-qkill-program nil nil nil 127 | (delq nil `(,@(if as-winpid 128 | (list "-w")) 129 | "-s" ,(prin1-to-string sigcode t) 130 | ,@(when (integerp sigval) 131 | (list "-i" (number-to-string sigval))) 132 | ,@(when tty 133 | (list "-t" tty)) 134 | ,@(when except 135 | (list "-e" (number-to-string pid))) 136 | ,@(unless tty 137 | (list (number-to-string pid))))))) 138 | ))) 139 | 140 | (defun fakecygpty-real-process-id (process &optional as-winpid) 141 | "Return subprocess's process-id if PROCESS was invoked by fakecygpty." 142 | (if (fakecygpty-process-p process) 143 | (with-temp-buffer 144 | (when (zerop 145 | (call-process 146 | "sh" nil (current-buffer) nil 147 | "-c" 148 | (format (if as-winpid 149 | "cat `dirname \\`grep -l %s /proc/*/ppid 2>/dev/null\\``/winpid" 150 | "basename `dirname \\`grep -l %s /proc/*/ppid 2>/dev/null\\``") 151 | (process-id process)))) 152 | (ignore-errors 153 | (save-match-data 154 | (string-to-number (replace-regexp-in-string "\r?\n" "" 155 | (buffer-string)))))) 156 | ) 157 | (process-id process))) 158 | 159 | (defun fakecygpty--ignored-program (program) 160 | "Return non-nil if PROGRAM is run without fakecygpty on `start-process'. 161 | An ignored pattern is used from `fakecygpty-ignored-program-regexps'" 162 | (let ((program (file-name-nondirectory program))) 163 | (delq nil (mapcar (lambda (p) 164 | (string-match-p p program)) 165 | fakecygpty-ignored-program-regexps)))) 166 | 167 | (defun fakecygpty--normalize-process-arg (target) 168 | "Return process object of TARGET. 169 | TARGET may be a process, a buffer, or the name of process or buffer. 170 | nil means current buffer's process." 171 | (cond 172 | ((processp target) 173 | target) 174 | ((or (bufferp target) 175 | (null target)) 176 | (or (get-buffer-process (or target (current-buffer))) 177 | (error "Buffer %s has no process" (buffer-name target)))) 178 | ((stringp target) 179 | (fakecygpty--normalize-process-arg (or (get-process target) 180 | (get-buffer target) 181 | (error "Process %s does not exist." target)))) 182 | (t 183 | (signal 'wrong-type-argument (list 'processp target))))) 184 | 185 | (defun fakecygpty--process-send-special-char (process type) 186 | "Send PROCESS the special char of TYPE from PROCESS's tty." 187 | (let ((tty (process-tty-name process))) 188 | (when tty 189 | (let ((special-char 190 | (with-temp-buffer 191 | (when (zerop (call-process "stty" nil (current-buffer) nil "-a" "-F" tty)) 192 | (save-match-data 193 | (goto-char (point-min)) 194 | (when (re-search-forward (format "%s = \\(\\^?\\)\\([^;]+\\);" type) nil t) 195 | (unless (equal (match-string 2) "") 196 | (if (equal (match-string 1) "^") 197 | (logand (aref (match-string 2) 0) #o037) 198 | (aref (match-string 2) 0))))))))) 199 | (when special-char 200 | (process-send-string process (char-to-string special-char)) 201 | t))))) 202 | 203 | (defun fakecygpty--make-advice () 204 | "Make advices for fakecygpty and qkill." 205 | (defadvice start-process (around fakecygpty--start-process last activate) 206 | "If `process-connection-type' is non-nil, invoke PROGRAM by `fakecygpty-program'." 207 | (if (and process-connection-type ; if non-nil, required pty. 208 | ;; (ad-get-arg 2) 209 | (or (not (ad-get-arg 2)) 210 | (not (fakecygpty--ignored-program (ad-get-arg 2))))) 211 | (progn 212 | ;; insert fakecygpty at program file name position. 213 | (when (ad-get-arg 2) 214 | (ad-set-args 3 (cons (ad-get-arg 2) (ad-get-args 3)))) 215 | (ad-set-arg 2 fakecygpty-program) 216 | ad-do-it 217 | (when (processp ad-return-value) 218 | (process-put ad-return-value :fakecygpty-p 219 | (if (ad-get-arg 3) t 'pty)))) 220 | ad-do-it)) 221 | 222 | (defadvice process-command (after fakecygpty--process-command activate) 223 | "Return real command name if PROCESS was invoked by fakecygpty." 224 | (when (fakecygpty-process-p (ad-get-arg 0)) 225 | (setq ad-return-value (cdr ad-return-value)))) 226 | 227 | (defadvice process-tty-name (after fakecygpty--process-tty-name activate) 228 | "Return tty name if PROCESS was invoked by fakecygpty." 229 | (when (fakecygpty-process-p (ad-get-arg 0)) 230 | (setq ad-return-value 231 | (with-temp-buffer 232 | ;; NTEmacs cannot see cygwin's `/proc' file-system, so using cygwin program. 233 | ;; Finding fakecygpty's tty-name. 234 | (if (zerop (call-process 235 | "cat" nil (current-buffer) nil 236 | (format "/proc/%s/ctty" (fakecygpty-real-process-id (ad-get-arg 0))))) 237 | (replace-regexp-in-string "\r?\n" "" (buffer-string)) 238 | "?"))))) 239 | 240 | (defadvice process-status (after fakecygpty--process-status activate) 241 | "Change return value 'exit to 'failed for pty allocation only mode." 242 | (let ((proc (fakecygpty--normalize-process-arg (ad-get-arg 0)))) 243 | (when (and (eq (fakecygpty-process-p proc) 'pty) 244 | (memq ad-return-value '(exit signal))) 245 | (setq ad-return-value 'failed)))) 246 | 247 | (defadvice process-send-eof (around fakecygpty--send-process-eof activate) 248 | "Send raw C-d code if PROCESS was invoked by fakecygpty." 249 | (let ((proc (fakecygpty--normalize-process-arg (ad-get-arg 0)))) 250 | (if (fakecygpty-process-p proc) 251 | (progn 252 | (fakecygpty--process-send-special-char proc "eof") 253 | (setq ad-return-value proc)) 254 | ad-do-it))) 255 | 256 | (defadvice signal-process (around fakecygpty--signal-process activate) 257 | "Send signal by `fakecygpty-qkill' for cygwin process. 258 | So it's able to send any type signal. 259 | For windows process, Emacs native `signal-process' will be invoked." 260 | (if (fakecygpty-qkill (ad-get-arg 0) (ad-get-arg 1) nil t) 261 | (setq ad-return-value 0) 262 | ad-do-it 263 | )) 264 | 265 | (dolist (desc '((interrupt-process 'SIGINT "intr") 266 | (quit-process 'SIGQUIT "quit") 267 | (stop-process 'SIGTSTP "susp") 268 | (continue-process 'SIGCONT nil) 269 | (kill-process 'SIGKILL nil))) 270 | (let ((func (nth 0 desc)) 271 | (sig (nth 1 desc)) 272 | (cc (nth 2 desc))) 273 | (eval `(defadvice ,func (around ,(intern (format "fakecygpty--%s" func)) activate) 274 | ,(format "Send %s signal by `fakecygpty-qkill'" (eval sig)) 275 | (let* ((proc (fakecygpty--normalize-process-arg (ad-get-arg 0))) 276 | (current-grp (and (fakecygpty-process-p proc) (ad-get-arg 1))) 277 | special-char) 278 | (if (and (eq (process-type proc) 'real) 279 | (cond 280 | ((null current-grp) 281 | (fakecygpty-qkill (- (fakecygpty-real-process-id proc)) ,sig)) 282 | ,(if cc 283 | `((fakecygpty--process-send-special-char proc ,cc) 284 | t) 285 | '(nil nil)) 286 | ((eq current-grp 'lambda) 287 | (fakecygpty-qkill (fakecygpty-real-process-id proc) 288 | ,sig nil nil 289 | (process-tty-name proc) t)) 290 | (t 291 | (fakecygpty-qkill (fakecygpty-real-process-id proc) 292 | ,sig nil nil 293 | (process-tty-name proc))))) 294 | (setq ad-return-value proc) 295 | ad-do-it))) 296 | ))) 297 | 298 | (defadvice set-process-window-size (around fakecygpty--set-process-window-size activate) 299 | "Send SIGWINCH signal with a window size information when process is invoked by `fakecygpty'. 300 | The window size information is caluclated by lines * 65536 + columns." 301 | (if (fakecygpty-process-p (ad-get-arg 0)) 302 | (setq ad-return-value 303 | (fakecygpty-qkill (ad-get-arg 0) 'SIGWINCH 304 | (+ (* 65536 (ad-get-arg 1)) 305 | (ad-get-arg 2)))) 306 | ad-do-it)) 307 | 308 | (eval-after-load "gdb-mi" 309 | '(defadvice gdb-io-interrupt (around fakecygpty--gdb-io-interrupt-workaround activate) 310 | "Workaround for gdb-io-interrupt. This function needs real CTRL-C singal." 311 | (if (not fakecygpty--activated) 312 | ad-do-it 313 | (ad-disable-advice 'interrupt-process 'around 'fakecygpty--interrupt-process) 314 | (ad-activate 'interrupt-process) 315 | ad-do-it 316 | (ad-enable-advice 'interrupt-process 'around 'fakecygpty--interrupt-process) 317 | (ad-activate 'interrupt-process)))) 318 | ) 319 | 320 | (provide 'fakecygpty) 321 | 322 | ;;; fakecygpty.el ends here 323 | -------------------------------------------------------------------------------- /fakecygpty.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Fake cygwin pty -- fakecygpty -- 3 | * 4 | * Copyright (C) 2005 Kyotaro Horiguchi 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either versions 2, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with fakecygpty, see the file COPYING. If not, write to the 19 | * Free Software Foundation Inc., 59 Temple Place - Suite 330, Boston, 20 | * MA 02111-1307, USA. 21 | */ 22 | 23 | /* 24 | * HISTORY 25 | * ------- 26 | * 27 | * 09 Jun, 2005 : Version 1.0.0 - first release. 28 | * 15 Jun, 2005 : Version 1.0.1 - bug fix and change coding style. 29 | * 15 May, 2009 : Version 1.0.2 - Work around for Windows 7 RC 30 | * 25 Jan, 2014 : Version 1.1.0 - fix tty attribute like emacs tty. 31 | * accept SIGWINCH signal for resize. 32 | * transport some signals to child pid. 33 | */ 34 | 35 | /* 36 | * COMPILATION 37 | * ------- 38 | * gcc -D_GNU_SOURCE -o fakecygpty.exe fakecygpty.c 39 | * Note: You must compile it in Cygwin environment. NOT in MinGW32! 40 | * 41 | */ 42 | 43 | /* 44 | * HOW TO RESIZE TTY WINDOW SIZE? 45 | * ------- 46 | * Send SIGWINCH signal by sigqueue() with sigval stored window size. 47 | * window size format: high-16bit => rows, low-16bit => cols 48 | * 49 | * ex) set window size cols=140 rows=32 by C code. 50 | * 51 | * union sigval sigval; 52 | * pid_t pid = ; 53 | * int cols = 140, rows = 32; 54 | * 55 | * sigval.sival_int = rows << 16 + 0xFFFF & cols; 56 | * sigqueue(pid, SIGWINCH, sigval); 57 | */ 58 | 59 | #ifdef HAVE_CONFIG_H 60 | #include "config.h" 61 | #endif 62 | 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | 75 | #define BUFSIZE 4096 /* size of communication buffer */ 76 | #define COMMAND_PREFIX "f_" 77 | #define MY_NAME "fakecygpty" 78 | 79 | /* prototypes */ 80 | int open_master_pty(void); 81 | 82 | pid_t exec_target(int master_fd, char* argv[], int pty_alloc_only); 83 | 84 | void setup_tty_attributes(int tty_fd); 85 | void set_tty_echo_on(int tty_fd); 86 | int resize_tty_window(int tty_fd, int window_size_info); 87 | 88 | char *real_command_name(char* my_name); 89 | 90 | ssize_t safe_read(int fd, void *buf, size_t count); 91 | ssize_t safe_write_full(int fd, void *buf, size_t count); 92 | ssize_t safe_write_full_checking_eof(int fd, void *buf, size_t length); 93 | 94 | void setup_signal_handlers(); 95 | void signal_pass_handler(int signum, siginfo_t *info, void *unused); 96 | void sigwinch_handler(int signum, siginfo_t *info, void *unused); 97 | 98 | BOOL WINAPI ctrl_handler(DWORD e); 99 | 100 | /* signal trapping descriptor */ 101 | struct sigtrap_desc { 102 | int signum; 103 | void (*action)(int, siginfo_t *, void *); 104 | }; 105 | 106 | /* global variables */ 107 | int child_pid = -1; /* pid of child proces */ 108 | int master_fd = -1; /* fd of pty served to child process */ 109 | 110 | 111 | volatile sig_atomic_t sig_winch_caught = FALSE; /* flag for SIGWINCH caught */ 112 | volatile sig_atomic_t sig_window_size = -1; /* window size info */ 113 | 114 | /* signals trap required */ 115 | struct sigtrap_desc sigtrap_descs[] = { 116 | { SIGHUP, signal_pass_handler }, 117 | { SIGINT, signal_pass_handler }, 118 | { SIGQUIT, signal_pass_handler }, 119 | { SIGALRM, signal_pass_handler }, 120 | { SIGTERM, signal_pass_handler }, 121 | { SIGWINCH, sigwinch_handler }, 122 | { SIGUSR1, signal_pass_handler }, 123 | { SIGUSR2, signal_pass_handler }, 124 | { SIGTSTP, signal_pass_handler }, 125 | { SIGCONT, signal_pass_handler } 126 | }; 127 | 128 | #define SIGTRAP_COUNT (sizeof(sigtrap_descs)/sizeof(struct sigtrap_desc)) 129 | 130 | /* codes */ 131 | 132 | int main(int argc, char* argv[]) 133 | { 134 | fd_set sel, sel0; 135 | int status; 136 | int pty_alloc_only = FALSE; 137 | char* newarg0; 138 | 139 | /* SIGINT and SIGBREAK are indistinctive under cygwin environment. */ 140 | /* Using Win32API to handle SIGINT. */ 141 | SetConsoleCtrlHandler(ctrl_handler, TRUE); 142 | 143 | if (argc < 1) { 144 | fputs("Unable to get arg[0].", stderr); 145 | exit(EXIT_FAILURE); 146 | } 147 | 148 | newarg0 = real_command_name(argv[0]); 149 | 150 | if (newarg0) 151 | argv[0] = newarg0; 152 | else if (argc >=2) 153 | argv++; 154 | else 155 | pty_alloc_only = TRUE; 156 | 157 | if (isatty(STDIN_FILENO) && !pty_alloc_only) { 158 | execvp(argv[0], argv); 159 | fprintf(stderr, "Failed to execute \"%s\": %s\n", argv[0], strerror(errno)); 160 | exit(EXIT_FAILURE); 161 | } 162 | 163 | master_fd = open_master_pty(); 164 | 165 | child_pid = exec_target(master_fd, argv, pty_alloc_only); 166 | 167 | setup_signal_handlers(); 168 | 169 | FD_ZERO(&sel0); 170 | FD_SET(master_fd, &sel0); 171 | FD_SET(STDIN_FILENO, &sel0); 172 | 173 | /* communication loop */ 174 | while (1) { 175 | char buf[BUFSIZE]; 176 | int ret; 177 | 178 | if (sig_winch_caught == TRUE) { 179 | sig_winch_caught = FALSE; 180 | if (child_pid != -1 && resize_tty_window(master_fd, sig_window_size) == 0) 181 | kill(child_pid, SIGWINCH); 182 | } 183 | 184 | sel = sel0; 185 | if (select (FD_SETSIZE, &sel, NULL, NULL, NULL) <= 0) { 186 | if(errno == EINTR) 187 | continue; 188 | else 189 | break; 190 | } 191 | 192 | if (FD_ISSET(master_fd, &sel)) { 193 | ret = safe_read(master_fd, buf, BUFSIZE); 194 | if (ret > 0) { 195 | if (safe_write_full(STDOUT_FILENO, buf, ret) < 0) 196 | break; 197 | } 198 | else 199 | break; 200 | } 201 | else if (FD_ISSET(STDIN_FILENO, &sel)) { 202 | ret = safe_read(STDIN_FILENO, buf, BUFSIZE); 203 | if (ret > 0) { 204 | if (safe_write_full_checking_eof(master_fd, buf, ret) < 0) 205 | break; 206 | } else { 207 | FD_CLR(STDIN_FILENO, &sel0); 208 | close(master_fd); 209 | } 210 | } 211 | } 212 | 213 | if (pty_alloc_only) { 214 | kill(child_pid, SIGKILL); 215 | status = 0; 216 | } else { 217 | while(waitpid(child_pid, &status, 0) < 0 && errno == EINTR) 218 | ; 219 | 220 | if (WIFEXITED(status)) 221 | status = WEXITSTATUS(status); 222 | else if(WIFSIGNALED(status)) /* ntemacs cannot distinct killed by signal */ 223 | status = 0x80 + WTERMSIG(status); 224 | } 225 | 226 | return status; 227 | } 228 | 229 | int open_master_pty(void) 230 | { 231 | int fd; 232 | 233 | fd = open("/dev/ptmx", O_RDWR); 234 | if (fd < 0) { 235 | perror("Failed to open /dev/ptmx"); 236 | return fd; 237 | } 238 | 239 | setup_tty_attributes(fd); 240 | 241 | return fd; 242 | } 243 | 244 | /* Create pty and fork/exec target process */ 245 | pid_t exec_target(int master_fd, char* argv[], int pty_alloc_only) 246 | { 247 | pid_t pid; 248 | 249 | pid = fork(); 250 | 251 | if (pid < 0) { 252 | perror("Failed to fork"); 253 | exit(EXIT_FAILURE); 254 | } 255 | 256 | if (pid == 0) { 257 | int slave_fd; 258 | int i; 259 | 260 | /* new session for obtain ctty */ 261 | if (setsid() < 0) { 262 | perror("Failed to setsid"); 263 | exit(EXIT_FAILURE); 264 | } 265 | 266 | slave_fd = open(ptsname(master_fd), O_RDWR); 267 | close(master_fd); 268 | 269 | if (slave_fd < 0) { 270 | perror("Failed to open slave pty"); 271 | exit(EXIT_FAILURE); 272 | } 273 | 274 | if (pty_alloc_only) 275 | set_tty_echo_on(slave_fd); 276 | 277 | for (i = 0; i < 3; i++) { 278 | if (slave_fd != i) { 279 | dup2(slave_fd, i); 280 | fcntl(i, F_SETFD, 0); 281 | } 282 | } 283 | if (slave_fd > 2) 284 | close(slave_fd); 285 | 286 | /* make new process group and make it foreground */ 287 | if (setpgid(0, 0) < 0) 288 | perror("Failed to setpgid"); 289 | if (tcsetpgrp(0, getpgid(getpid())) < 0) 290 | perror("Failed to change foreground pgid"); 291 | 292 | if (pty_alloc_only) 293 | /* do nothing. wait for kill */ 294 | select(0, NULL, NULL, NULL, NULL); 295 | else 296 | execvp(argv[0], argv); 297 | 298 | fprintf(stderr, "Failed to execute \"%s\": %s\n", argv[0], strerror(errno)); 299 | exit(EXIT_FAILURE); 300 | } 301 | 302 | return pid; 303 | } 304 | 305 | void setup_tty_attributes (int tty_fd) 306 | { 307 | struct termios tm; 308 | 309 | /* set up tty attribute */ 310 | if (tcgetattr(tty_fd, &tm) < 0) 311 | perror("Faild to tcgetattr"); 312 | else { 313 | /* setup values from child_setup_tty() in emacs/src/sysdep.c */ 314 | tm.c_iflag &= ~(IUCLC | ISTRIP); 315 | tm.c_iflag |= IGNCR; 316 | tm.c_oflag &= ~(ONLCR | OLCUC | TAB3); 317 | tm.c_oflag |= OPOST; 318 | tm.c_lflag &= ~ECHO; 319 | tm.c_lflag |= ISIG | ICANON; 320 | tm.c_cc[VERASE] = _POSIX_VDISABLE; 321 | tm.c_cc[VKILL] = _POSIX_VDISABLE; 322 | tm.c_cc[VEOF] = CTRL('D'); 323 | 324 | if (tcsetattr(tty_fd, TCSANOW, &tm) < 0) 325 | perror("Failed to tcsetattr"); 326 | } 327 | } 328 | 329 | void set_tty_echo_on(int tty_fd) 330 | { 331 | struct termios tm; 332 | 333 | /* set up tty attribute */ 334 | if (tcgetattr(tty_fd, &tm) < 0) 335 | perror("Faild to tcgetattr"); 336 | else { 337 | tm.c_lflag |= ECHO; 338 | if (tcsetattr(tty_fd, TCSANOW, &tm) < 0) 339 | perror("Failed to tcsetattr"); 340 | } 341 | } 342 | 343 | 344 | int resize_tty_window(int fd, int window_size_info) 345 | { 346 | struct winsize w; 347 | int ret; 348 | 349 | if (window_size_info >= 0) { 350 | /* size info: high-16bit => rows, low-16bit => cols */ 351 | w.ws_row = window_size_info >> 16; 352 | w.ws_col = window_size_info & 0xFFFF; 353 | 354 | do { 355 | ret = ioctl(fd, TIOCSWINSZ, &w); 356 | } while (ret < 0 && errno == EINTR); 357 | } 358 | 359 | return ret; 360 | } 361 | 362 | char *real_command_name(char* my_name) 363 | { 364 | char *p; 365 | 366 | /* Assume mutlbyte characters do not occur here */ 367 | p = strrchr(my_name, '/'); 368 | if (p == NULL) 369 | p = strrchr(my_name, '\\'); 370 | 371 | if (p == NULL) 372 | p = my_name; 373 | else 374 | p++; 375 | 376 | if (strcmp(p, MY_NAME) == 0) 377 | return NULL; /* I am invoked as explicit wrapper program */ 378 | 379 | if (strncmp(p, COMMAND_PREFIX, strlen (COMMAND_PREFIX)) != 0) { 380 | fprintf(stderr, "Illegal program name format. \"%s\"\n", my_name); 381 | exit(1); 382 | } 383 | 384 | return p + strlen(COMMAND_PREFIX); 385 | } 386 | 387 | ssize_t safe_read(int fd, void *buf, size_t count) 388 | { 389 | ssize_t ret; 390 | 391 | do { 392 | ret = read(fd, buf, count); 393 | } while(ret < 0 && errno == EINTR); 394 | 395 | return ret; 396 | } 397 | 398 | ssize_t safe_write_full(int fd, void *buf, size_t count) 399 | { 400 | ssize_t ret; 401 | 402 | do { 403 | ret = write(fd, buf, count); 404 | if (ret > 0) { 405 | buf += ret; 406 | count -= ret; 407 | } 408 | } while(count > 0 && (ret >= 0 || (ret < 0 && errno == EINTR))); 409 | 410 | return ret; 411 | } 412 | 413 | /* 414 | * Workaround for cygwin's 'behavior of EOF detection. 415 | * On a linux system, write("\n[EOF]somthing") cause EOF, but cygwin is not. 416 | * so need to recognize indivisually before and after EOF. 417 | */ 418 | ssize_t safe_write_full_checking_eof(int fd, void *buf, size_t length) 419 | { 420 | struct termios tm; 421 | char eof_char; 422 | size_t rest; 423 | ssize_t ret; 424 | void *next_eof; 425 | 426 | /* retrieve EOF char on this pty */ 427 | if (tcgetattr(fd, &tm) == 0) 428 | eof_char = tm.c_cc[VEOF]; 429 | else 430 | eof_char = _POSIX_VDISABLE; 431 | 432 | if (eof_char == _POSIX_VDISABLE) 433 | return safe_write_full(fd, buf, length); 434 | 435 | rest = length; 436 | 437 | while (rest > 0 && (next_eof = memchr(buf, eof_char, rest)) != NULL) { 438 | ret = safe_write_full(fd, buf, next_eof - buf); 439 | if (ret < 0) return ret; 440 | 441 | /* workaround for flushing input buffer.. */ 442 | /* It seems continuous write(2) calls are combined, so insert sleep. */ 443 | usleep(1); 444 | ret = safe_write_full(fd, &eof_char, 1); 445 | if (ret < 0) return ret; 446 | 447 | /* workaround for flushing input buffer.. */ 448 | usleep(1); 449 | rest = rest - (next_eof - buf + 1); 450 | buf = next_eof + 1; 451 | } 452 | 453 | if (rest > 0) { 454 | ret = safe_write_full(fd, buf, rest); 455 | if (ret < 0) return ret; 456 | } 457 | 458 | return length; 459 | } 460 | 461 | void setup_signal_handlers() 462 | { 463 | struct sigaction newsig; 464 | int i; 465 | 466 | memset(&newsig, 0, sizeof(newsig)); 467 | newsig.sa_flags = SA_SIGINFO; 468 | sigemptyset(&newsig.sa_mask); 469 | 470 | for (i = 0; i < SIGTRAP_COUNT; i++) { 471 | newsig.sa_sigaction = sigtrap_descs[i].action; 472 | if (sigaction(sigtrap_descs[i].signum, &newsig, NULL) < 0) 473 | fprintf(stderr, "Failed to sigaction on %d: %s\n", 474 | sigtrap_descs[i].signum, strerror(errno)); 475 | } 476 | } 477 | 478 | /* pass signals to child */ 479 | void signal_pass_handler(int signum, siginfo_t *info, void *unused) 480 | { 481 | union sigval sigval; 482 | int saved_errno; 483 | 484 | if (child_pid == -1) 485 | return; 486 | 487 | saved_errno = errno; 488 | if (info->si_code == SI_QUEUE) { 489 | sigval = info->si_value; 490 | sigqueue(child_pid, signum, sigval); 491 | 492 | } else { 493 | kill(child_pid, signum); 494 | } 495 | errno = saved_errno; 496 | } 497 | 498 | void sigwinch_handler(int signum, siginfo_t *info, void *unused) 499 | { 500 | if (child_pid == -1) 501 | return; 502 | 503 | sig_winch_caught = TRUE; 504 | if (info->si_code == SI_QUEUE) 505 | sig_window_size = info->si_value.sival_int; 506 | else 507 | sig_window_size = -1; 508 | } 509 | 510 | /* Signal handler for convert SIGINT into ^C on pty */ 511 | /* This seems not able to be done within cygwin POSIX framework */ 512 | BOOL WINAPI ctrl_handler(DWORD e) 513 | { 514 | switch (e) { 515 | case CTRL_C_EVENT: 516 | if (master_fd != -1) { 517 | write(master_fd, "\003", 1); 518 | return TRUE; 519 | } 520 | break; 521 | 522 | case CTRL_CLOSE_EVENT: 523 | if (child_pid != -1) { 524 | kill(child_pid, SIGKILL); 525 | return FALSE; 526 | } 527 | } 528 | return FALSE; 529 | } 530 | --------------------------------------------------------------------------------