Skip to content
2010/05/01 / highmt

Windowsでのtrampまわりいくつか

emacs: GNU Emacs 23.1.1 (i386-mingw-nt6.0.6002)
tramp: 2.1.15 (included in emacs 23.1)
cygwin: CYGWIN_NT-6.0 1.5.25(0.156/4/2) 2008-11-18 15:24 i686 Cygwin (UTF-8 Cygwin: http://www.okisoft.co.jp/esc/utf8-cygwin/download.html)

NTEmacsでTrampを使う でとりあえずTrampが使えるようになったのだけれども、
そのあといろいろ問題があったのでこの機会にいろいろやってみた。

問題1:tramp-copy-size-limitを超えるときscpにパスワードを渡せない
問題2:utf-8な環境への接続時、diredの表示が文字化けする

(let ((my-login-program '("f_ssh_utf-8"))
      (my-copy-program '("f_scp")))
  (mapc (lambda (x)
          (let ((tlp (assq 'tramp-login-program (cdr x)))
                (tcp (assq 'tramp-copy-program (cdr x))))
            (when (and tlp (equal (cadr tlp) "ssh"))
              (setcdr tlp my-login-program))
            (when (and tcp (equal (cadr tcp) "scp"))
              (setcdr tcp my-copy-program))))
        tramp-methods))

まず、scpは、sshと同様にf_scpに置き換え。

f_sshのほうは、

f_ssh_utf-8.cmd:

@fakecygpty cocot -p utf-8 ssh %*

utf-8じゃない環境と同時につなぐときには問題になるのだけれど、うまい解決が思いつかないのでまた困ったときに考える。

問題3:tramp-copy-size-limitを超えるときscpで正常にファイルを開けない

http://d.hatena.ne.jp/holidays-l/20080229 に既出のとおり、ローカルファイルのパスに":"が含まれるため。

パッチを当てずになんとかがんばろうとしたのだけれども、
defsubstがあってadviseできなかったり、
expand-file-pathが何回も重なったりとかでいろいろ難しく、
結局以下のとおり強引に修正。
最新のtrampは結構ソースが変わっているので注意。

tramp.el (2.1.15):

--- tramp.el.orig	2009-07-08 22:22:51.000000000 +0900
+++ tramp.el	2010-05-01 00:34:34.421000000 +0900
@@ -3367,6 +3367,10 @@
       (setq source (if t1 (tramp-make-copy-program-file-name v) filename)
             target (if t2 (tramp-make-copy-program-file-name v) newname))
 
+      (let ((replacer #'(lambda (s) (replace-regexp-in-string "^\\([A-z]\\):" "/cygdrive/\\1" s t))))
+        (setq source (funcall replacer source)
+              target (funcall replacer target)))
+
       ;; Check for port number.  Until now, there's no need for handling
       ;; like method, user, host.
       (setq host (tramp-file-name-real-host v)

ひとまずこれで使えるようになったのだけれども、cocotがはさまることで新たな問題が。

問題4:emacs終了後もsshのプロセスが残る

fakecygpty.c (http://www.meadowy.org/meadow/browser/trunk/nt/fakecygpty.c, rev4245):

--- fakecygpty.c.orig	2010-04-06 00:14:58.922000000 +0900
+++ fakecygpty.c	2010-05-01 14:41:23.133000000 +0900
@@ -42,6 +42,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <termios.h>
+#include <signal.h>
 
 #define BUFSIZE		 1024	/* size of communication buffer */
 #define COMMAND_PREFIX	 "f_"
@@ -52,6 +53,26 @@
 int masterfd;		/* fd of pty served to child process */
 
 
+typedef void (*sighandler_t)(int);
+
+sighandler_t
+trap_signal(int sig, sighandler_t handler)
+{
+  struct sigaction act, old;
+
+  act.sa_handler = handler;
+  sigemptyset(&act.sa_mask);
+  if (sig != SIGALRM)
+    {
+      act.sa_flags = SA_RESTART;
+    }
+  if (sigaction(sig, &act, &old) < 0)
+    {
+      return NULL;
+    }
+  return old.sa_handler;
+}
+
 /* Console window of child process is made visible on Windows 7 RC, 
    Forcefully make it as workaround... */
 BOOL CALLBACK
@@ -216,12 +237,21 @@
       return TRUE;
 
     case CTRL_CLOSE_EVENT:
-      kill (child_pid, SIGKILL);
+      kill (child_pid, SIGTERM);
       return FALSE;
     }
   return FALSE;
 }
 
+void
+signal_handler (int sig)
+{
+  int status;
+  kill (child_pid, sig);
+  waitpid (child_pid, &status, 0);
+  exit (status);
+}
+
 int
 main (int argc, char* argv[])
 {
@@ -233,6 +263,8 @@
   /* SIGINT and SIGBREAK are indistinctive under cygwin environment. */
   /* Using Win32API to handle SIGINT.                              */
   SetConsoleCtrlHandler (ctrl_handler, TRUE);
+  trap_signal (SIGHUP, signal_handler);
+  trap_signal (SIGTERM, signal_handler);
 
   if (argc < 1)
     {
@@ -246,6 +278,11 @@
       argv[0] = newarg0;
       exec_target (argv);     /* This sets globals masterfd, child_pid */
     }
+  else if (argc < 2)
+    {
+      fprintf (stderr, "Unable to get arg[1].");
+      exit (1);
+    }
   else
     exec_target (argv + 1); /* This sets globals masterfd, child_pid */
 
@@ -288,7 +325,7 @@
 
   restore_tty_attributes ();
 
-  kill (child_pid, SIGKILL);
+  kill (child_pid, SIGTERM);
   waitpid (child_pid, &status, 0);
   return status;
 }

cocot.c (http://vmi.jp/software/cygwin/cocot.html, cocot-20080315):

--- cocot.c.orig	2008-03-15 17:04:31.000000000 +0900
+++ cocot.c	2010-04-30 23:39:45.612000000 +0900
@@ -5,6 +5,8 @@
  * All rights reserved.
  */
 
+#include <windows.h>
+
 #if HAVE_CONFIG_H
 #  include <config.h>
 #endif
@@ -28,6 +30,9 @@
 #if HAVE_SYS_WAIT_H
 #  include <sys wait.h>
 #endif
+
+#include <signal.h>
+
 #include 
 
 #include "init.h"
@@ -38,6 +43,29 @@
 FILE *debug = NULL;
 #endif
 
+static int child_pid;
+static int masterfd;
+
+typedef void (*sighandler_t)(int);
+
+sighandler_t
+trap_signal(int sig, sighandler_t handler)
+{
+  struct sigaction act, old;
+
+  act.sa_handler = handler;
+  sigemptyset(&act.sa_mask);
+  if (sig != SIGALRM)
+    {
+      act.sa_flags = SA_RESTART;
+    }
+  if (sigaction(sig, &act, &old) < 0)
+    {
+      return NULL;
+    }
+  return old.sa_handler;
+}
+
 static void
 show_version(void)
 {
@@ -66,6 +94,30 @@
     exit(1);
 }
 
+BOOL WINAPI
+ctrl_handler(DWORD e)
+{
+  switch (e)
+    {
+    case CTRL_C_EVENT:
+      write (masterfd, "03", 1);
+      return TRUE;
+    case CTRL_CLOSE_EVENT:
+      kill (child_pid, SIGTERM);
+      return FALSE;
+    }
+  return FALSE;
+}
+
+void
+signal_handler (int sig)
+{
+  int status;
+  kill (child_pid, sig);
+  waitpid (child_pid, &status, 0);
+  exit (status);
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -82,6 +134,10 @@
     int status;
     pid_t pid;
 
+    SetConsoleCtrlHandler (ctrl_handler, TRUE);
+    trap_signal (SIGHUP, signal_handler);
+    trap_signal (SIGTERM, signal_handler);
+
     if (argc == 1)
 	usage(argc, argv);
     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
@@ -143,11 +199,15 @@
 	fatal("Can't open file '%s' (%s).", DEBUG_LOG, strerror(errno));
     setvbuf(debug, NULL, _IONBF, 0);
 #endif
+    child_pid = pid;
+    masterfd = mfd;
     loop(mfd, logfp, term_code, proc_code, dec_jis);
     close(mfd);
     if (logfp)
 	fclose(logfp);
-    wait(&status);
+    kill (child_pid, SIGTERM);
+    /* wait(&status); */
+    waitpid (child_pid, &status, 0);
     done();
     return 0;
 }

fakecygptyの終了時、子プロセスであるcocotだけをSIGKILLで終了しているのが問題っぽいので、

  • fakecygptyのほうは、CTRL_CLOSE_EVENT時、子プロセスにSIGKILLを送出していたのをSIGTERMに変更。
  • cocotのほうは、SIGTERMに対するシグナルハンドラを追加し、SIGTERM時、子プロセスにSIGTERMを送出するように変更。

基本的にはこれだけだけれど、ついでなので、

  • cocotのほうに、fakecygptyでやっているSetConsoleCtrlHandlerを追加し、CTRL_CLOSE_EVENTを拾うように。
    (CTRL_C_EVENTは意味がよくわかっていないが、ひとまずfakecygptyと同じようにした。)
  • fakecygptyのほうに、cocotと同じようにSIGTERMに対するシグナルハンドラを追加。
    (そんなことはしないけど)fakecygptyを2つ重ねたときに、その孫プロセスにSIGTERMが伝わらないので。
  • fakecygptyに引数を与えずに起動したときの問題に対処。

——–

2010/05/01 cocotの変更はautotoolsに未対応。やっつけです。

2010/05/01 今回はfakecygptyの方式にならったけれども、試してないけどひょっとしてsetpgidすればもっと簡単に解決するのかも。

2010/05/01 今回はあまり関係なさそうだけれどもexitを_exitに変更。

2010/05/01 fakecygptyに引数を与えずに起動したときの問題に対処。

広告
%d人のブロガーが「いいね」をつけました。