emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: w32 does not have emacsclient/server


From: Juanma Barranquero
Subject: Re: w32 does not have emacsclient/server
Date: Fri, 5 Aug 2005 03:43:06 +0200

On 7/15/05, Stefan Monnier <address@hidden> wrote:

> Actually, I don't think so.  IIRC What we need for emacsclient/server to
> work on Windows is to make it work over TCP sockets rather than only over
> Unix sockets (because Windows supports TCL sockets but not Unix sockets).
> 
> A good approach could be something like: in server.el, use a TCP server
> socket (on a non-specified port).  Once opened, check to see which port was
> used.  Write the port and hostname together with a secret random string into
> ~/.emacs_server.  When a connection comes in, check that the first bytes
> sent are exactly the same as the random string (authentication).

The following is a first cut at implementing this.

server.el now creates an AF_INET socket, at an unspecified port. The
host address is local (127.0.0.1) unless the user customizes the
variable `server-host' to the IP or name to use to bind the socket.
The server generates a 64-byte random string (not random bytes, but
printable characters in the range `!'..`~'; it's still about 420 bits
of entropy) and writes in `server-file' (by default "~/.emacs.server")
the host, port and authentication string. Connections are expected to
pass as the first thing in a communication exchange the string "-auth
AUTHENTICATION-STRING" where AUTHENTICATION-STRING is the 64-byte
secret key; otherwise the connection is closed immediately.

In emacsclient.c I've got rid of all AF_UNIX stuff. I've also changed
the code to use send/recv instead of writing to a file handle (was
easier than fighting two Windows C compilers' idiosincrasies) and
added buffering so data is only sent on receiving "\n" or filling the
buffer. Argument `socket-name' has been replaced by `server-file', to
indicate the path to the server config/auth file (alternatively, the
variable EMACS_SERVER_FILE can be set to point to the file; and BTW, a
better name for the variable would be very welcome). The last change
has been to make it consider \path and c:path as absolute paths
(previously it would prepend the current directory to Windows-style
absolute paths).

As it stands, it builds on Windows XP with GCC and MSVC. I'd like help with:

 - testing on other Windows environments
 - building *and* testing on non-Windows environments (it should work, but...)
 - configuration of lib-src/Makefile.w32-in (my change is a bit of a
mess, I think)

To do:

 - more testing
 - a bit of a cleanup, perhaps
 - documentation changes

and of course any change, fix or redesign that is deemed necessary.
Personally I'd like to add an option to force the host port and allow
alternative (or even null) authentication strings, so in secure
environments it'd be posible to use emacsclient/server.el with a fixed
address/port and no .emacs.server file (emacsclient would have to grow
--server-address and --server-port options, of course). But I
digress...

-- 
                    /L/e/k/t/u


Index: lib-src/emacsclient.c
===================================================================
RCS file: /cvsroot/emacs/emacs/lib-src/emacsclient.c,v
retrieving revision 1.74
diff -c -r1.74 emacsclient.c
*** lib-src/emacsclient.c       4 Jul 2005 15:24:11 -0000       1.74
--- lib-src/emacsclient.c       5 Aug 2005 00:33:54 -0000
***************
*** 20,25 ****
--- 20,28 ----
  Boston, MA 02110-1301, USA.  */
  
  
+ #define AUTH_STRING_LENGTH   64
+ #define SEND_BUFFER_SIZE   4096
+ 
  #define NO_SHORTNAMES
  
  #ifdef HAVE_CONFIG_H
***************
*** 30,45 ****
  
  #include <ctype.h>
  #include <stdio.h>
! #include <getopt.h>
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  
  #ifdef VMS
! # include "vms-pwd.h"
  #else
! # include <pwd.h>
  #endif /* not VMS */
  
  char *getenv (), *getwd ();
  char *(getcwd) ();
--- 33,53 ----
  
  #include <ctype.h>
  #include <stdio.h>
! #include "getopt.h"
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  
+ #ifdef WINDOWSNT
+ #include <winsock2.h>
+ #else /* not WINDOWSNT */
+ #include <sys/socket.h>
  #ifdef VMS
! #include "vms-pwd.h"
  #else
! #include <pwd.h>
  #endif /* not VMS */
+ #endif /* not WINDOWSNT */
  
  char *getenv (), *getwd ();
  char *(getcwd) ();
***************
*** 47,52 ****
--- 55,72 ----
  #ifndef VERSION
  #define VERSION "unspecified"
  #endif
+ 
+ #define SEND_STRING(data) (send_to_emacs (s, (data), 0))
+ #define SEND_QUOTED(data) (quote_file_name (s, (data)))
+ 
+ #ifndef EXIT_SUCCESS
+ #define EXIT_SUCCESS 0
+ #endif
+ 
+ #ifndef EXIT_FAILURE
+ #define EXIT_FAILURE 1
+ #endif
+ 
  
  /* Name used to invoke this program.  */
  char *progname;
***************
*** 61,71 ****
  char *display = NULL;
  
  /* If non-NULL, the name of an editor to fallback to if the server
!    is not running.  --alternate-editor.   */
! const char * alternate_editor = NULL;
  
! /* If non-NULL, the filename of the UNIX socket.  */
! char *socket_name = NULL;
  
  void print_help_and_exit ();
  
--- 81,96 ----
  char *display = NULL;
  
  /* If non-NULL, the name of an editor to fallback to if the server
!    is not running.  --alternate-editor.  */
! const char *alternate_editor = NULL;
  
! /* If non-NULL, the filename to use as configuration and
!    authentication file.  --server-file.  */
! const char *server_file = NULL;
! 
! /* Buffer to accumulate data to send.  */
! char send_buffer[SEND_BUFFER_SIZE+1];
! int sblen = 0;
  
  void print_help_and_exit ();
  
***************
*** 76,82 ****
    { "help",   no_argument,       NULL, 'H' },
    { "version",        no_argument,       NULL, 'V' },
    { "alternate-editor", required_argument, NULL, 'a' },
!   { "socket-name",    required_argument, NULL, 's' },
    { "display",        required_argument, NULL, 'd' },
    { 0, 0, 0, 0 }
  };
--- 101,107 ----
    { "help",   no_argument,       NULL, 'H' },
    { "version",        no_argument,       NULL, 'V' },
    { "alternate-editor", required_argument, NULL, 'a' },
!   { "server-file",    required_argument, NULL, 's' },
    { "display",        required_argument, NULL, 'd' },
    { 0, 0, 0, 0 }
  };
***************
*** 90,95 ****
--- 115,121 ----
       char **argv;
  {
    alternate_editor = getenv ("ALTERNATE_EDITOR");
+   server_file = getenv("EMACS_SERVER_FILE");
  
    while (1)
      {
***************
*** 111,117 ****
          break;
  
        case 's':
!         socket_name = optarg;
          break;
  
        case 'd':
--- 137,143 ----
          break;
  
        case 's':
!         server_file = optarg;
          break;
  
        case 'd':
***************
*** 157,164 ****
  -n, --no-wait           Don't wait for the server to return\n\
  -e, --eval              Evaluate the FILE arguments as ELisp expressions\n\
  -d, --display=DISPLAY   Visit the file in the given display\n\
! -s, --socket-name=FILENAME\n\
!                         Set the filename of the UNIX socket for
communication\n\
  -a, --alternate-editor=EDITOR\n\
                          Editor to fallback to if the server is not running\n\
  \n\
--- 183,190 ----
  -n, --no-wait           Don't wait for the server to return\n\
  -e, --eval              Evaluate the FILE arguments as ELisp expressions\n\
  -d, --display=DISPLAY   Visit the file in the given display\n\
! -s, --server-file=FILENAME\n\
!                         Server configuration and authentication file\n\
  -a, --alternate-editor=EDITOR\n\
                          Editor to fallback to if the server is not running\n\
  \n\
***************
*** 166,179 ****
    exit (EXIT_SUCCESS);
  }
  
  /* In NAME, insert a & before each &, each space, each newline, and
     any initial -.  Change spaces to underscores, too, so that the
     return value never contains a space.  */
  
  void
! quote_file_name (name, stream)
       char *name;
-      FILE *stream;
  {
    char *copy = (char *) malloc (strlen (name) * 2 + 1);
    char *p, *q;
--- 192,242 ----
    exit (EXIT_SUCCESS);
  }
  
+ 
+ /* Let's send the data to Emacs when either
+    - the data ends in "\n", or
+    - the buffer is full (but this shouldn't happen)
+    Otherwise, we just accumulate it.  */
+ 
+ void send_to_emacs (s, data, force)
+      SOCKET s;
+      char *data;
+      int force;
+ {
+   int dlen = strlen (data);
+   int sent;
+ 
+   while (force || (dlen + sblen >= SEND_BUFFER_SIZE))
+     {
+       sent = send (s, send_buffer, sblen, 0);
+       if (sent != sblen)
+         strcpy (send_buffer, &send_buffer[sent]);
+ 
+       sblen -= sent;
+ 
+       if (force)
+         /* Just the first time around.  */
+         force = 0;
+     }
+ 
+   if (dlen)
+     {
+       strcpy (&send_buffer[sblen], data);
+       sblen += dlen;
+     }
+ 
+   if (sblen && (send_buffer[sblen-1] == '\n'))
+     send_to_emacs (s, "\0", 1);
+ }
+ 
  /* In NAME, insert a & before each &, each space, each newline, and
     any initial -.  Change spaces to underscores, too, so that the
     return value never contains a space.  */
  
  void
! quote_file_name (s, name)
!      SOCKET s;
       char *name;
  {
    char *copy = (char *) malloc (strlen (name) * 2 + 1);
    char *p, *q;
***************
*** 203,227 ****
      }
    *q++ = 0;
  
!   fprintf (stream, "%s", copy);
  
    free (copy);
  }
  
- /* Like malloc but get fatal error if memory is exhausted.  */
- 
- long *
- xmalloc (size)
-      unsigned int size;
- {
-   long *result = (long *) malloc (size);
-   if (result == NULL)
-   {
-     perror ("malloc");
-     exit (EXIT_FAILURE);
-   }
-   return result;
- }
  
  /*
    Try to run a different command, or --if no alternate editor is
--- 266,276 ----
      }
    *q++ = 0;
  
!   SEND_STRING (copy);
  
    free (copy);
  }
  
  
  /*
    Try to run a different command, or --if no alternate editor is
***************
*** 244,459 ****
      }
  }
  
- 
  
- #if !defined (HAVE_SOCKETS) || defined (NO_SOCKETS_IN_FILE_SYSTEM)
  
! int
! main (argc, argv)
       int argc;
!      char **argv;
  {
!   fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
!          argv[0]);
!   fprintf (stderr, "on systems with Berkeley sockets.\n");
  
!   fail (argc, argv);
! }
  
! #else /* HAVE_SOCKETS */
  
! #include <sys/types.h>
! #include <sys/socket.h>
! #include <sys/un.h>
! #include <sys/stat.h>
! #include <errno.h>
  
! extern char *strerror ();
! extern int errno;
  
! /* Three possibilities:
!    2 - can't be `stat'ed              (sets errno)
!    1 - isn't owned by us
!    0 - success: none of the above */
  
! static int
! socket_status (socket_name)
!      char *socket_name;
  {
!   struct stat statbfr;
  
!   if (stat (socket_name, &statbfr) == -1)
!     return 2;
  
!   if (statbfr.st_uid != geteuid ())
!     return 1;
  
!   return 0;
  }
  
! int
! main (argc, argv)
!      int argc;
!      char **argv;
  {
!   int s, i, needlf = 0;
!   FILE *out, *in;
!   struct sockaddr_un server;
!   char *cwd, *str;
!   char string[BUFSIZ];
! 
!   progname = argv[0];
  
!   /* Process options.  */
!   decode_options (argc, argv);
  
!   if ((argc - optind < 1) && !eval)
      {
!       fprintf (stderr, "%s: file name or argument required\n", progname);
!       fprintf (stderr, "Try `%s --help' for more information\n", progname);
        exit (EXIT_FAILURE);
      }
  
!   /*
!    * Open up an AF_UNIX socket in this person's home directory
!    */
  
!   if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
      {
!       fprintf (stderr, "%s: ", argv[0]);
!       perror ("socket");
!       fail (argc, argv);
      }
  
!   server.sun_family = AF_UNIX;
  
!   {
!     int sock_status = 0;
!     int default_sock = !socket_name;
!     int saved_errno;
!     char *server_name = "server";
! 
!     if (socket_name && !index (socket_name, '/') && !index
(socket_name, '\\'))
!       { /* socket_name is a file name component.  */
!       server_name = socket_name;
!       socket_name = NULL;
!       default_sock = 1;       /* Try both UIDs.  */
!       }
! 
!     if (default_sock)
!       {
!       socket_name = alloca (100 + strlen (server_name));
!       sprintf (socket_name, "/tmp/emacs%d/%s",
!                (int) geteuid (), server_name);
!       }
! 
!     if (strlen (socket_name) < sizeof (server.sun_path))
!       strcpy (server.sun_path, socket_name);
!     else
!       {
!       fprintf (stderr, "%s: socket-name %s too long",
!                argv[0], socket_name);
!       exit (EXIT_FAILURE);
!       }
! 
!     /* See if the socket exists, and if it's owned by us. */
!     sock_status = socket_status (server.sun_path);
!     saved_errno = errno;
!     if (sock_status && default_sock)
!       {
!       /* Failing that, see if LOGNAME or USER exist and differ from
!          our euid.  If so, look for a socket based on the UID
!          associated with the name.  This is reminiscent of the logic
!          that init_editfns uses to set the global Vuser_full_name.  */
! 
!       char *user_name = (char *) getenv ("LOGNAME");
! 
!       if (!user_name)
!         user_name = (char *) getenv ("USER");
! 
!       if (user_name)
!         {
!           struct passwd *pw = getpwnam (user_name);
! 
!           if (pw && (pw->pw_uid != geteuid ()))
!             {
!               /* We're running under su, apparently. */
!               socket_name = alloca (100 + strlen (server_name));
!               sprintf (socket_name, "/tmp/emacs%d/%s",
!                        (int) pw->pw_uid, server_name);
! 
!               if (strlen (socket_name) < sizeof (server.sun_path))
!                 strcpy (server.sun_path, socket_name);
!               else
!                 {
!                   fprintf (stderr, "%s: socket-name %s too long",
!                            argv[0], socket_name);
!                   exit (EXIT_FAILURE);
!                 }
! 
!               sock_status = socket_status (server.sun_path);
!               saved_errno = errno;
!             }
!           else
!             errno = saved_errno;
!         }
!       }
! 
!      switch (sock_status)
!        {
!        case 1:
!        /* There's a socket, but it isn't owned by us.  This is OK if
!           we are root. */
!        if (0 != geteuid ())
!          {
!            fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
!            fail (argc, argv);
!          }
!        break;
! 
!        case 2:
!        /* `stat' failed */
!        if (saved_errno == ENOENT)
!          fprintf (stderr,
!                   "%s: can't find socket; have you started the server?\n\
! To start the server in Emacs, type \"M-x server-start\".\n",
!                   argv[0]);
!        else
!          fprintf (stderr, "%s: can't stat %s: %s\n",
!                   argv[0], server.sun_path, strerror (saved_errno));
!        fail (argc, argv);
!        break;
!        }
!   }
  
!   if (connect (s, (struct sockaddr *) &server, strlen (server.sun_path) + 2)
!       < 0)
!     {
!       fprintf (stderr, "%s: ", argv[0]);
!       perror ("connect");
!       fail (argc, argv);
!     }
  
!   /* We use the stream OUT to send our command to the server.  */
!   if ((out = fdopen (s, "r+")) == NULL)
      {
!       fprintf (stderr, "%s: ", argv[0]);
!       perror ("fdopen");
!       fail (argc, argv);
      }
  
!   /* We use the stream IN to read the response.
!      We used to use just one stream for both output and input
!      on the socket, but reversing direction works nonportably:
!      on some systems, the output appears as the first input;
!      on other systems it does not.  */
!   if ((in = fdopen (s, "r+")) == NULL)
      {
!       fprintf (stderr, "%s: ", argv[0]);
!       perror ("fdopen");
!       fail (argc, argv);
      }
  
  #ifdef HAVE_GETCWD
    cwd = getcwd (string, sizeof string);
  #else
--- 293,475 ----
      }
  }
  
  
  
! /* Process options and check some sane defaults.  */
! void process_options (argc, argv)
       int argc;
!      char *argv[];
  {
!   decode_options (argc, argv);
  
!   if (!server_file)
!     {
!       fprintf (stderr, "%s: server configuration file required\n", progname);
!       goto error;
!     }
  
!   if ((argc - optind < 1) && !eval)
!     {
!       fprintf (stderr, "%s: file name or argument required\n", progname);
!       goto error;
!     }
  
!   return;
  
!  error:
  
!   fprintf (stderr, "Try `%s --help' for more information\n", progname);
!   exit (EXIT_FAILURE);
! }
  
! #ifdef WINDOWSNT
! /* Wrapper to make WSACleanup a cdecl, as required by atexit().  */
! void close_winsock ()
  {
!   WSACleanup();
! }
! #endif /* WINDOWSNT */
  
! void initialize_sockets ()
! {
! #ifdef WINDOWSNT
! 
!   /* Initialize the WinSock2 library.  */
!   WSADATA wsaData;
  
!   if (WSAStartup (MAKEWORD (2, 0), &wsaData))
!     {
!       fprintf (stderr, "%s: error initializing WinSock2", progname);
!       exit (EXIT_FAILURE);
!     }
  
!   atexit (close_winsock);
! #endif
  }
  
! /*
!  * Read the information needed to set up the comm channel with
!  * the Emacs server: host, port and authentication string.
!  */
! void get_server_config (server, authentication)
!      struct sockaddr_in *server;
!      char *authentication;
  {
!   FILE *config;
!   char dotted[32];
!   char *port;
  
!   if (! (config = fopen (server_file, "rb")))
!     {
!       fprintf (stderr, "%s: cannot read configuration file %s",
!                progname, server_file);
!       exit (EXIT_FAILURE);
!     }
  
!   if (fgets (dotted, sizeof dotted, config)
!       && (port = strchr (dotted, ':')))
      {
!       *port++ = '\0';
!     }
!   else
!     {
!       fprintf (stderr, "%s: invalid configuration info", progname);
        exit (EXIT_FAILURE);
      }
  
!   server->sin_family = AF_INET;
!   server->sin_addr.s_addr = inet_addr (dotted);
!   server->sin_port = htons (atoi (port));
  
!   if (! fread (authentication, AUTH_STRING_LENGTH, 1, config))
      {
!       fprintf (stderr, "%s: cannot read authentication info", progname);
!       exit (EXIT_FAILURE);
      }
  
!   fclose (config);
! }
  
! /*
!  * Perform all required initialization; at the end either the socket
!  * is available and correctly configured, or we've finished with an
!  * error status so the main program can try the alternate editor.
!  */
! SOCKET
! set_socket ()
! {
!   SOCKET s;
!   struct sockaddr_in server;
!   unsigned long c_arg = 0;
!   struct linger l_arg = {1, 1};
!   char auth_string[AUTH_STRING_LENGTH+1];
  
!   initialize_sockets();
! 
!   get_server_config (&server, auth_string);
  
!   /*
!    * Open up an AF_INET socket
!    */
!   if ((s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
      {
!       fprintf (stderr, "%s: ", progname);
!       perror ("socket");
!       return INVALID_SOCKET;
      }
  
!   /*
!    * Set up the socket
!    */
!   if (connect (s, (struct sockaddr *) &server, sizeof server) < 0)
      {
!       fprintf (stderr, "%s: ", progname);
!       perror ("connect");
!       return INVALID_SOCKET;
      }
  
+   /*
+    * Force socket to be blocking and SO_LINGER
+    */
+   ioctlsocket (s, FIONBIO, &c_arg);
+   setsockopt (s, SOL_SOCKET, SO_LINGER, (char *) &l_arg, sizeof l_arg);
+ 
+   /*
+    * Now, let's send the authentication
+    */
+   auth_string[AUTH_STRING_LENGTH] = '\0';
+   SEND_STRING ("-auth ");
+   SEND_STRING (auth_string);
+   SEND_STRING ("\n");
+ 
+   return s;
+ }
+ 
+ 
+ #include <sys/types.h>
+ 
+ #include <sys/stat.h>
+ #include <errno.h>
+ 
+ extern char *strerror ();
+ extern int errno;
+ 
+ int
+ main (argc, argv)
+      int argc;
+      char **argv;
+ {
+   SOCKET s;
+   int i, rl, needlf = 0;
+   char *cwd, *str;
+   char string[BUFSIZ+1];
+ 
+   progname = argv[0];
+   process_options (argc, argv);
+ 
+   if ((s = set_socket ()) == INVALID_SOCKET)
+     fail (argc, argv);
+ 
  #ifdef HAVE_GETCWD
    cwd = getcwd (string, sizeof string);
  #else
***************
*** 464,488 ****
        /* getwd puts message in STRING if it fails.  */
  
  #ifdef HAVE_GETCWD
!       fprintf (stderr, "%s: %s (%s)\n", argv[0],
               "Cannot get current working directory", strerror (errno));
  #else
!       fprintf (stderr, "%s: %s (%s)\n", argv[0], string, strerror (errno));
  #endif
        fail (argc, argv);
      }
  
    if (nowait)
!     fprintf (out, "-nowait ");
  
    if (eval)
!     fprintf (out, "-eval ");
  
    if (display)
      {
!       fprintf (out, "-display ");
!       quote_file_name (display, out);
!       fprintf (out, " ");
      }
  
    if ((argc - optind > 0))
--- 480,504 ----
        /* getwd puts message in STRING if it fails.  */
  
  #ifdef HAVE_GETCWD
!       fprintf (stderr, "%s: %s (%s)\n", progname,
               "Cannot get current working directory", strerror (errno));
  #else
!       fprintf (stderr, "%s: %s (%s)\n", progname, string, strerror (errno));
  #endif
        fail (argc, argv);
      }
  
    if (nowait)
!     SEND_STRING ("-nowait ");
  
    if (eval)
!     SEND_STRING ("-eval ");
  
    if (display)
      {
!       SEND_STRING ("-display ");
!       SEND_QUOTED (display);
!       SEND_STRING (" ");
      }
  
    if ((argc - optind > 0))
***************
*** 497,556 ****
              while (isdigit ((unsigned char) *p) || *p == ':') p++;
              if (*p != 0)
                {
!                 quote_file_name (cwd, out);
!                 fprintf (out, "/");
                }
            }
!         else if (*argv[i] != '/')
            {
!             quote_file_name (cwd, out);
!             fprintf (out, "/");
            }
  
!         quote_file_name (argv[i], out);
!         fprintf (out, " ");
        }
      }
    else
      {
        while ((str = fgets (string, BUFSIZ, stdin)))
        {
!         quote_file_name (str, out);
        }
!       fprintf (out, " ");
      }
  
!   fprintf (out, "\n");
!   fflush (out);
  
!   /* Maybe wait for an answer.   */
!   if (nowait)
!     return EXIT_SUCCESS;
! 
!   if (!eval)
      {
!       printf ("Waiting for Emacs...");
!       needlf = 2;
!     }
!   fflush (stdout);
  
!   /* Now, wait for an answer and print any messages.  */
!   while ((str = fgets (string, BUFSIZ, in)))
!     {
!       if (needlf == 2)
!       printf ("\n");
!       printf ("%s", str);
!       needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
!     }
  
!   if (needlf)
!     printf ("\n");
!   fflush (stdout);
  
    return EXIT_SUCCESS;
  }
  
- #endif /* HAVE_SOCKETS */
  
  #ifndef HAVE_STRERROR
  char *
--- 513,578 ----
              while (isdigit ((unsigned char) *p) || *p == ':') p++;
              if (*p != 0)
                {
!                 SEND_QUOTED (cwd);
!                 SEND_STRING ("/");
                }
            }
!           /* Absolute paths can also start with backslash or drive
letters.  */
!           else if ((*argv[i] != '/')
!                     && (*argv[i] != '\\')
!                     && (!islower (tolower (*argv[i]))
!                         || (argv[i][1] != ':')))
            {
!             SEND_QUOTED (cwd);
!             SEND_STRING ("/");
            }
  
!         SEND_QUOTED (argv[i]);
!         SEND_STRING (" ");
        }
      }
    else
      {
        while ((str = fgets (string, BUFSIZ, stdin)))
        {
!         SEND_QUOTED (str);
        }
!       SEND_STRING (" ");
      }
  
!   SEND_STRING ("\n");
  
!   /* Maybe wait for an answer.  */
!   if (!nowait)
      {
!       if (!eval)
!         {
!           printf ("Waiting for Emacs...");
!           needlf = 2;
!         }
!       fflush (stdout);
  
!       /* Now, wait for an answer and print any messages.  */
  
!       while ((rl = recv (s, string, BUFSIZ, 0)) > 0)
!         {
!           string[rl] = '\0';
!           if (needlf == 2)
!             printf ("\n");
!           printf ("%s", str);
!           needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
!         }
  
+       if (needlf)
+         printf ("\n");
+       fflush (stdout);
+     }
+ 
+   closesocket (s);
    return EXIT_SUCCESS;
+ 
  }
  
  
  #ifndef HAVE_STRERROR
  char *
Index: lib-src/makefile.w32-in
===================================================================
RCS file: /cvsroot/emacs/emacs/lib-src/makefile.w32-in,v
retrieving revision 2.36
diff -c -r2.36 makefile.w32-in
*** lib-src/makefile.w32-in     28 Jul 2005 07:58:52 -0000      2.36
--- lib-src/makefile.w32-in     5 Aug 2005 01:07:33 -0000
***************
*** 19,25 ****
  #  Boston, MA 02110-1301, USA.
  #
  
! ALL         = make-docfile hexl ctags etags movemail ebrowse
  
  .PHONY: $(ALL)
  
--- 19,25 ----
  #  Boston, MA 02110-1301, USA.
  #
  
! ALL         = make-docfile hexl ctags etags movemail ebrowse emacsclient
  
  .PHONY: $(ALL)
  
***************
*** 34,40 ****
  #             $(BLD)/server.exe       \
  #             $(BLD)/emacstool.exe    \
  #             $(BLD)/leditcfns.exe    \
- #             $(BLD)/emacsclient.exe  \
  #             $(BLD)/cvtmail.exe      \
  #             $(BLD)/digest-doc.exe   \
  #             $(BLD)/test-distrib.exe \
--- 34,39 ----
***************
*** 55,60 ****
--- 54,60 ----
  hexl:         $(BLD) $(BLD)/hexl.exe
  movemail:     $(BLD) $(BLD)/movemail.exe
  fakemail:     $(BLD) $(BLD)/fakemail.exe
+ emacsclient:  $(BLD) $(BLD)/emacsclient.exe
  
  GETOPTOBJS = $(BLD)/getopt.$(O) $(BLD)/getopt1.$(O)
  GETOPTDEPS = $(GETOPTOBJS) getopt.h
***************
*** 67,72 ****
--- 67,85 ----
  # put wsock32.lib before $(LIBS) to ensure we don't link to ws2_32.lib
                $(LINK) $(LINK_OUT)$@ $(LINK_FLAGS) $(MOVEMAILOBJS) $(WSOCK32) 
$(LIBS)
  
+ ECLIENT_CFLAGS =      -DWINDOWSNT -DHAVE_GETCWD -DHAVE_STRERROR -c
+ ECLIENTOBJS = $(BLD)/emacsclient.$(O) \
+               $(BLD)/getopt.$(O) \
+               $(BLD)/getopt1.$(O) \
+               $(BLD)/ntlib.$(O)
+ 
+ $(BLD)/emacsclient.exe:       $(ECLIENTOBJS)
+ # put wsock32.lib before $(LIBS) to ensure we don't link to ws2_32.lib
+               $(LINK) $(LINK_OUT)$@ $(LINK_FLAGS) $(ECLIENTOBJS) $(WSOCK32) 
$(LIBS)
+ 
+ $(BLD)/emacsclient.$(O):      emacsclient.c
+               $(CC) $(ECLIENT_CFLAGS) $(CC_OUT)$@ emacsclient.c
+ 
  ETAGSOBJ      = $(BLD)/etags.$(O) \
                $(BLD)/getopt.$(O) \
                $(BLD)/getopt1.$(O) \
***************
*** 76,82 ****
  $(BLD)/etags.exe:     $(ETAGSOBJ)
                $(LINK) $(LINK_OUT)$@ $(LINK_FLAGS) $(ETAGSOBJ) $(LIBS)
  
- 
  EBROWSEOBJ    = $(BLD)/ebrowse.$(O) \
                  $(BLD)/getopt.$(O) \
                  $(BLD)/getopt1.$(O) \
--- 89,94 ----
***************
*** 120,126 ****
  # $(BLD)/server.exe:          $(BLD)/server.$(O)
  # $(BLD)/cvtmail.exe:                 $(BLD)/cvtmail.$(O)
  # $(BLD)/digest-doc.exe:      $(BLD)/digest-doc.$(O)
- # $(BLD)/emacsclient.exe:     $(BLD)/emacsclient.$(O)
  # $(BLD)/test-distrib.exe:    $(BLD)/test-distrib.$(O)
  
  #
--- 132,137 ----
***************
*** 130,136 ****
  # as it is required by code in doc.c.
  #
  obj=    sunfns.o dosfns.o msdos.o \
!       xterm.o xfns.o xmenu.o xselect.o xrdb.o  fringe.o image.o \
        mac.o macterm.o macfns.o macmenu.o fontset.o \
        w32.o w32bdf.o w32console.o w32fns.o w32heap.o w32inevt.o \
        w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
--- 141,147 ----
  # as it is required by code in doc.c.
  #
  obj=    sunfns.o dosfns.o msdos.o \
!       xterm.o xfns.o xmenu.o xselect.o xrdb.o fringe.o image.o \
        mac.o macterm.o macfns.o macmenu.o fontset.o \
        w32.o w32bdf.o w32console.o w32fns.o w32heap.o w32inevt.o \
        w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
***************
*** 277,282 ****
--- 288,294 ----
                $(CP) $(BLD)/ctags.exe $(INSTALL_DIR)/bin
                $(CP) $(BLD)/hexl.exe $(INSTALL_DIR)/bin
                $(CP) $(BLD)/movemail.exe $(INSTALL_DIR)/bin
+               $(CP) $(BLD)/emacsclient.exe $(INSTALL_DIR)/bin
                - mkdir "$(INSTALL_DIR)/etc"
                $(CP) $(DOC) $(INSTALL_DIR)/etc
  
Index: lisp/server.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/server.el,v
retrieving revision 1.104
diff -c -r1.104 server.el
*** lisp/server.el      16 Jul 2005 11:58:10 -0000      1.104
--- lisp/server.el      4 Aug 2005 15:07:58 -0000
***************
*** 82,87 ****
--- 82,103 ----
    "Emacs running as a server process."
    :group 'external)
  
+ (defcustom server-host nil
+   "The name or IP address to use as host address of the server process.
+ If set, the server accepts remote connections; otherwise it is local."
+   :group 'server
+   :type '(choice
+           (string :tag "Name or IP address")
+           (const :tag "Local" nil))
+   :version "22.1")
+ 
+ (defcustom server-file "~/.emacs.server"
+   "The server authentication file.
+ Its value will be passed through `expand-file-name'."
+   :group 'server
+   :type 'file
+   :version "22.1")
+ 
  (defcustom server-visit-hook nil
    "*Hook run when visiting a file for the Emacs server."
    :group 'server
***************
*** 100,105 ****
--- 116,124 ----
  (defvar server-process nil
    "The current server process.")
  
+ (defvar server-auth-string nil
+   "The current server authentication string.")
+ 
  (defvar server-clients nil
    "List of current server clients.
  Each element is (CLIENTID BUFFERS...) where CLIENTID is a string
***************
*** 161,194 ****
  
  (defvar server-name "server")
  
- (defvar server-socket-dir
-   (format "/tmp/emacs%d" (user-uid)))
- 
  (defun server-log (string &optional client)
    "If a *server* buffer exists, write STRING to it for logging purposes."
!   (if (get-buffer "*server*")
!       (with-current-buffer "*server*"
!       (goto-char (point-max))
!       (insert (current-time-string)
!               (if client (format " %s:" client) " ")
!               string)
!       (or (bolp) (newline)))))
  
  (defun server-sentinel (proc msg)
!   (let ((client (assq proc server-clients)))
!     ;; Remove PROC from the list of clients.
!     (when client
!       (setq server-clients (delq client server-clients))
!       (dolist (buf (cdr client))
!       (with-current-buffer buf
!         ;; Remove PROC from the clients of each buffer.
!         (setq server-buffer-clients (delq proc server-buffer-clients))
!         ;; Kill the buffer if necessary.
!         (when (and (null server-buffer-clients)
!                    (or (and server-kill-new-buffers
!                             (not server-existing-buffer))
!                        (server-temp-file-p)))
!           (kill-buffer (current-buffer)))))))
    (server-log (format "Status changed to %s" (process-status proc)) proc))
  
  (defun server-select-display (display)
--- 180,213 ----
  
  (defvar server-name "server")
  
  (defun server-log (string &optional client)
    "If a *server* buffer exists, write STRING to it for logging purposes."
!   (when (get-buffer "*server*")
!     (with-current-buffer "*server*"
!       (goto-char (point-max))
!       (insert (current-time-string)
!               (if client (format " %s:" client) " ")
!               string)
!       (or (bolp) (newline)))))
  
  (defun server-sentinel (proc msg)
!   (if (eq proc server-process)
!       (ignore-errors
!         (delete-file (expand-file-name server-file)))
!     (let ((client (assq proc server-clients)))
!       ;; Remove PROC from the list of clients.
!       (when client
!         (setq server-clients (delq client server-clients))
!         (dolist (buf (cdr client))
!           (with-current-buffer buf
!             ;; Remove PROC from the clients of each buffer.
!             (setq server-buffer-clients (delq proc server-buffer-clients))
!             ;; Kill the buffer if necessary.
!             (when (and (null server-buffer-clients)
!                        (or (and server-kill-new-buffers
!                                 (not server-existing-buffer))
!                            (server-temp-file-p)))
!               (kill-buffer (current-buffer))))))))
    (server-log (format "Status changed to %s" (process-status proc)) proc))
  
  (defun server-select-display (display)
***************
*** 221,241 ****
            (t " ")))
     arg t t))
  
! (defun server-ensure-safe-dir (dir)
!   "Make sure DIR is a directory with no race-condition issues.
! Creates the directory if necessary and makes sure:
! - there's no symlink involved
! - it's owned by us
! - it's not readable/writable by anybody else."
!   (setq dir (directory-file-name dir))
!   (let ((attrs (file-attributes dir)))
!     (unless attrs
!       (letf (((default-file-modes) ?\700)) (make-directory dir))
!       (setq attrs (file-attributes dir)))
!     ;; Check that it's safe for use.
!     (unless (and (eq t (car attrs)) (eq (nth 2 attrs) (user-uid))
!                (zerop (logand ?\077 (file-modes dir))))
!       (error "The directory %s is unsafe" dir))))
  
  ;;;###autoload
  (defun server-start (&optional leave-dead)
--- 240,254 ----
            (t " ")))
     arg t t))
  
! (defun server-auth-string ()
!   (or server-auth-string
!       ;; If the authentication string does not exist, create it on the fly:
!       ;; it's a 64-byte string of random chars in the range `!'..`~'.
!       (setq server-auth-string
!             (loop
!                for i below 64
!                collect (+ 33 (random 94)) into auth
!                finally return (concat auth)))))
  
  ;;;###autoload
  (defun server-start (&optional leave-dead)
***************
*** 247,278 ****
  
  Prefix arg means just kill any existing server communications subprocess."
    (interactive "P")
!   ;; Make sure there is a safe directory in which to place the socket.
!   (server-ensure-safe-dir server-socket-dir)
!   ;; kill it dead!
!   (if server-process
!       (condition-case () (delete-process server-process) (error nil)))
!   ;; Delete the socket files made by previous server invocations.
!   (condition-case ()
!       (delete-file (expand-file-name server-name server-socket-dir))
!     (error nil))
    ;; If this Emacs already had a server, clear out associated status.
    (while server-clients
      (let ((buffer (nth 1 (car server-clients))))
        (server-buffer-done buffer)))
    (unless leave-dead
!     (if server-process
!       (server-log (message "Restarting server")))
      (letf (((default-file-modes) ?\700))
        (setq server-process
            (make-network-process
!            :name "server" :family 'local :server t :noquery t
!            :service (expand-file-name server-name server-socket-dir)
!            :sentinel 'server-sentinel :filter 'server-process-filter
             ;; We must receive file names without being decoded.
             ;; Those are decoded by server-process-filter according
             ;; to file-name-coding-system.
!            :coding 'raw-text)))))
  
  ;;;###autoload
  (define-minor-mode server-mode
--- 260,301 ----
  
  Prefix arg means just kill any existing server communications subprocess."
    (interactive "P")
!   ;; Kill it dead!
!   (when server-process
!     (condition-case ()
!         (progn
!           (delete-process server-process)
!           (delete-file (expand-file-name server-file))
!           (setq server-auth-string nil))
!       (error nil)))
    ;; If this Emacs already had a server, clear out associated status.
    (while server-clients
      (let ((buffer (nth 1 (car server-clients))))
        (server-buffer-done buffer)))
    (unless leave-dead
!     (when server-process
!       (server-log (message "Restarting server")))
      (letf (((default-file-modes) ?\700))
        (setq server-process
            (make-network-process
!              :name server-name :noquery t :server t
!              :service t :host (or server-host 'local)
!              :filter 'server-process-filter :sentinel 'server-sentinel
!              :plist '(:authenticated nil)
             ;; We must receive file names without being decoded.
             ;; Those are decoded by server-process-filter according
             ;; to file-name-coding-system.
!            :coding 'raw-text))
!       (unless server-process
!         (error "Could not start server process"))
!       ;; We must create the server info file, or no one will be able
to contact us
!       (with-temp-file (expand-file-name server-file)
!         (set-buffer-multibyte nil)
!         (setq buffer-file-coding-system 'no-conversion)
!         (insert (format-network-address (cadr (memq :local
!                                                     (process-contact
server-process
!                                                                      t))))
!                 "\n" (server-auth-string))))))
  
  ;;;###autoload
  (define-minor-mode server-mode
***************
*** 287,300 ****
    ;; nothing if there is one (for multiple Emacs sessions)?
    (server-start (not server-mode)))
  
! (defun server-process-filter (proc string)
    "Process a request from the server to edit some files.
  PROC is the server process.  Format of STRING is \"PATH PATH PATH... \\n\"."
    (server-log string proc)
!   (let ((prev (process-get proc 'previous-string)))
      (when prev
        (setq string (concat prev string))
!       (process-put proc 'previous-string nil)))
    ;; If the input is multiple lines,
    ;; process each line individually.
    (while (string-match "\n" string)
--- 310,334 ----
    ;; nothing if there is one (for multiple Emacs sessions)?
    (server-start (not server-mode)))
  
! (defun* server-process-filter (proc string)
    "Process a request from the server to edit some files.
  PROC is the server process.  Format of STRING is \"PATH PATH PATH... \\n\"."
+   ;; First things first: let's check the authentication
+   (unless (process-get proc :authenticated)
+     (if (and (string-match "-auth \\(.*?\\)\n" string)
+              (string= (match-string 1 string) server-auth-string))
+         (progn
+           (setq string (substring string (match-end 0)))
+           (process-put proc :authenticated t)
+           (server-log "Authentication successful" proc))
+       (server-log "Authentication failed" proc)
+       (delete-process proc)
+       (return-from server-process-filter)))
    (server-log string proc)
!   (let ((prev (process-get proc :previous-string)))
      (when prev
        (setq string (concat prev string))
!       (process-put proc :previous-string nil)))
    ;; If the input is multiple lines,
    ;; process each line individually.
    (while (string-match "\n" string)
***************
*** 377,383 ****
        (if tmp-frame (delete-frame tmp-frame))))
    ;; Save for later any partial line that remains.
    (when (> (length string) 0)
!     (process-put proc 'previous-string string)))
  
  (defun server-goto-line-column (file-line-col)
    (goto-line (nth 1 file-line-col))
--- 411,417 ----
        (if tmp-frame (delete-frame tmp-frame))))
    ;; Save for later any partial line that remains.
    (when (> (length string) 0)
!     (process-put proc :previous-string string)))
  
  (defun server-goto-line-column (file-line-col)
    (goto-line (nth 1 file-line-col))




reply via email to

[Prev in Thread] Current Thread [Next in Thread]