]> code.delx.au - gnu-emacs/blobdiff - lib-src/emacsclient.c
*** empty log message ***
[gnu-emacs] / lib-src / emacsclient.c
index 11946ff2ec0ac88b608ab7e7996236dcad56f042..03fe66598c8469a1852dc3bb64abcd383b5de59e 100644 (file)
@@ -1,5 +1,5 @@
 /* Client process that communicates with GNU Emacs acting as server.
-   Copyright (C) 1986, 1987, 1994, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1987, 1994, 1999, 2000 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -29,6 +29,12 @@ Boston, MA 02111-1307, USA.  */
 #include <unistd.h>
 #endif
 
+#ifdef VMS
+# include "vms-pwd.h"
+#else
+# include <pwd.h>
+#endif /* not VMS */
+
 char *getenv (), *getwd ();
 char *getcwd ();
 
@@ -52,9 +58,13 @@ struct option longopts[] =
   { "no-wait", no_argument,       NULL, 'n' },
   { "help",    no_argument,       NULL, 'H' },
   { "version", no_argument,       NULL, 'V' },
+  { "alternate-editor",required_argument, NULL, 'a' },
   { 0 }
 };
 
+
+const char * alternate_editor = NULL;
+
 /* Decode the options from argv and argc.
    The global variable `optind' will say how many arguments we used up.  */
 
@@ -66,18 +76,24 @@ decode_options (argc, argv)
   while (1)
     {
       int opt = getopt_long (argc, argv,
-                            "VHn", longopts, 0);
+                            "VHna:", longopts, 0);
 
       if (opt == EOF)
        break;
 
+      alternate_editor = getenv ("ALTERNATE_EDITOR");
+      
       switch (opt)
        {
        case 0:
          /* If getopt returns 0, then it has already processed a
             long-named option.  We should do nothing.  */
          break;
-
+         
+       case 'a':
+         alternate_editor = optarg;
+         break;
+         
        case 'n':
          nowait = 1;
          break;
@@ -98,7 +114,7 @@ void
 print_help_and_exit ()
 {
   fprintf (stderr,
-          "Usage: %s [-n] [--no-wait] [+LINENUMBER] FILENAME\n",
+          "Usage: %s [-a ALTERNATE-EDITOR] [-n] [--no-wait] [+LINENUMBER] FILENAME\n",
           progname);
   fprintf (stderr,
           "Or %s --version\n",
@@ -139,6 +155,7 @@ quote_file_name (name)
     }
   *q++ = 0;
 
+  
   return copy;
 }
 
@@ -156,6 +173,28 @@ xmalloc (size)
   }
   return result;
 }
+\f
+/*
+  Try to run a different command, or --if no alternate editor is
+  defined-- exit with an errorcode.
+*/
+fail (argc, argv)
+     int argc;
+     char **argv;
+{
+  if (alternate_editor)
+    {
+      int i = optind -1 ;
+      execvp (alternate_editor, argv + i);
+    }
+  else
+    {
+      exit (1);
+    }
+}
+
+
+       
 \f
 #if !defined (HAVE_SOCKETS) && !defined (HAVE_SYSVIPC)
 
@@ -166,7 +205,8 @@ main (argc, argv)
   fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
           argv[0]);
   fprintf (stderr, "on systems with Berkeley sockets or System V IPC.\n");
-  exit (1);
+
+  fail (argc, argv);
 }
 
 #else /* HAVE_SOCKETS or HAVE_SYSVIPC */
@@ -183,6 +223,26 @@ main (argc, argv)
 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;
@@ -193,7 +253,10 @@ main (argc, argv)
   int s, i;
   FILE *out, *in;
   struct sockaddr_un server;
-  char *homedir, *cwd, *str;
+#ifdef SERVER_HOME_DIR
+  char *homedir;
+#endif
+  char *cwd, *str;
   char string[BUFSIZ];
 
   progname = argv[0];
@@ -212,8 +275,9 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("socket");
-      exit (1);
+      fail (argc, argv);
     }
+  
   server.sun_family = AF_UNIX;
 
   {
@@ -236,32 +300,66 @@ main (argc, argv)
 
 #ifndef SERVER_HOME_DIR
   {
-    struct stat statbfr;
+    int sock_status = 0;
 
     sprintf (server.sun_path, "/tmp/esrv%d-%s", geteuid (), system_name);
 
-    if (stat (server.sun_path, &statbfr) == -1)
+    /* See if the socket exists, and if it's owned by us. */
+    sock_status = socket_status (server.sun_path);
+    if (sock_status)
       {
-       if (errno == ENOENT)
-         fprintf (stderr,
-                  "%s: can't find socket; have you started the server?\n",
-                  argv[0]);
-       else
-         fprintf (stderr, "%s: can't stat %s: %s\n",
-                  argv[0], server.sun_path, strerror (errno));
-       exit (1);
-      }
-    if (statbfr.st_uid != geteuid ())
-      {
-       fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
-       exit (1);
+       /* 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. */
+               sprintf (server.sun_path, "/tmp/esrv%d-%s",
+                        pw->pw_uid, system_name);
+               sock_status = socket_status (server.sun_path);
+             }
+         }
       }
+     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 (errno == ENOENT)
+          fprintf (stderr,
+                   "%s: can't find socket; have you started the server?\n",
+                   argv[0]);
+        else
+          fprintf (stderr, "%s: can't stat %s: %s\n",
+                   argv[0], server.sun_path, strerror (errno));
+        fail (argc, argv);
+        break;
+       }
   }
 #else
   if ((homedir = getenv ("HOME")) == NULL)
     {
       fprintf (stderr, "%s: No home directory\n", argv[0]);
-      exit (1);
+      fail (argc, argv);
     }
   strcpy (server.sun_path, homedir);
   strcat (server.sun_path, "/.emacs-server-");
@@ -273,7 +371,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("connect");
-      exit (1);
+      fail (argc, argv);
     }
 
   /* We use the stream OUT to send our command to the server.  */
@@ -281,7 +379,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("fdopen");
-      exit (1);
+      fail (argc, argv);
     }
 
   /* We use the stream IN to read the response.
@@ -293,7 +391,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("fdopen");
-      exit (1);
+      fail (argc, argv);
     }
 
 #ifdef BSD_SYSTEM
@@ -311,7 +409,7 @@ main (argc, argv)
               "Cannot get current working directory",
 #endif
               strerror (errno));
-      exit (1);
+      fail (argc, argv);
     }
 
   if (nowait)
@@ -442,7 +540,7 @@ main (argc, argv)
       fprintf (stderr, "%s: Cannot get current working directory: %s\n",
               argv[0], strerror (errno));
 #endif
-      exit (1);
+      fail (argc, argv);
     }
 
   msgp->mtext[0] = 0;
@@ -498,7 +596,7 @@ main (argc, argv)
   if (strlen (msgp->mtext) >= 512)
     {
       fprintf (stderr, "%s: args too long for msgsnd\n", progname);
-      exit (1);
+      fail (argc, argv);
     }
 #endif
   msgp->mtype = 1;
@@ -506,7 +604,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", progname);
       perror ("msgsnd");
-      exit (1);
+      fail (argc, argv);
     }
 
   /* Maybe wait for an answer.   */