[BACK]Return to client.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Diff for /src/usr.bin/tmux/client.c between version 1.45 and 1.46

version 1.45, 2010/10/16 08:31:55 version 1.46, 2010/10/18 20:00:02
Line 28 
Line 28 
 #include <pwd.h>  #include <pwd.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
 #include <syslog.h>  
 #include <unistd.h>  #include <unistd.h>
   
 #include "tmux.h"  #include "tmux.h"
Line 37 
Line 36 
 struct event    client_event;  struct event    client_event;
 const char     *client_exitmsg;  const char     *client_exitmsg;
 int             client_exitval;  int             client_exitval;
   int             client_attached;
   
   int             client_connect(char *, int);
 void            client_send_identify(int);  void            client_send_identify(int);
 void            client_send_environ(void);  void            client_send_environ(void);
 void            client_write_server(enum msgtype, void *, size_t);  void            client_write_server(enum msgtype, void *, size_t);
 void            client_update_event(void);  void            client_update_event(void);
 void            client_signal(int, short, void *);  void            client_signal(int, short, void *);
 void            client_callback(int, short, void *);  void            client_callback(int, short, void *);
 int             client_dispatch(void);  int             client_dispatch_attached(void);
   int             client_dispatch_wait(void *);
   
 struct imsgbuf *  /* Connect client to server. */
 client_init(char *path, int cmdflags, int flags)  int
   client_connect(char *path, int start_server)
 {  {
         struct sockaddr_un      sa;          struct sockaddr_un      sa;
         size_t                  size;          size_t                  size;
         int                     fd, mode;          int                     fd, mode;
         char                    rpathbuf[MAXPATHLEN];  
   
         if (realpath(path, rpathbuf) == NULL)  
                 strlcpy(rpathbuf, path, sizeof rpathbuf);  
         setproctitle("client (%s)", rpathbuf);  
   
         memset(&sa, 0, sizeof sa);          memset(&sa, 0, sizeof sa);
         sa.sun_family = AF_UNIX;          sa.sun_family = AF_UNIX;
         size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);          size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
         if (size >= sizeof sa.sun_path) {          if (size >= sizeof sa.sun_path) {
                 errno = ENAMETOOLONG;                  errno = ENAMETOOLONG;
                 goto not_found;                  return (-1);
         }          }
   
         if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)          if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
                 fatal("socket failed");                  fatal("socket failed");
   
         if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {          if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
                 if (!(cmdflags & CMD_STARTSERVER))                  if (!start_server)
                         goto not_found;                          goto failed;
                 switch (errno) {                  switch (errno) {
                 case ECONNREFUSED:                  case ECONNREFUSED:
                         if (unlink(path) != 0)                          if (unlink(path) != 0)
                                 goto not_found;                                  goto failed;
                         /* FALLTHROUGH */                          /* FALLTHROUGH */
                 case ENOENT:                  case ENOENT:
                         if ((fd = server_start(path)) == -1)                          if ((fd = server_start()) == -1)
                                 goto start_failed;                                  goto failed;
                         goto server_started;                          break;
                   default:
                           goto failed;
                 }                  }
                 goto not_found;  
         }          }
   
 server_started:  
         if ((mode = fcntl(fd, F_GETFL)) == -1)          if ((mode = fcntl(fd, F_GETFL)) == -1)
                 fatal("fcntl failed");                  fatal("fcntl failed");
         if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)          if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
                 fatal("fcntl failed");                  fatal("fcntl failed");
           return (fd);
   
   failed:
           close(fd);
           return (-1);
   }
   
   /* Client main loop. */
   int
   client_main(int argc, char **argv, int flags)
   {
           struct cmd              *cmd;
           struct cmd_list         *cmdlist;
           struct msg_command_data  cmddata;
           int                      cmdflags, fd;
           enum msgtype             msg;
           char                    *cause;
   
           /* Set up the initial command. */
           cmdflags = 0;
           if (shell_cmd != NULL) {
                   msg = MSG_SHELL;
                   cmdflags = CMD_STARTSERVER;
           } else if (argc == 0) {
                   msg = MSG_COMMAND;
                   cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
           } else {
                   msg = MSG_COMMAND;
   
                   /*
                    * It sucks parsing the command string twice (in client and
                    * later in server) but it is necessary to get the start server
                    * flag.
                    */
                   if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
                           log_warnx("%s", cause);
                           return (1);
                   }
                   cmdflags &= ~CMD_STARTSERVER;
                   TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
                           if (cmd->entry->flags & CMD_STARTSERVER)
                                   cmdflags |= CMD_STARTSERVER;
                           if (cmd->entry->flags & CMD_SENDENVIRON)
                                   cmdflags |= CMD_SENDENVIRON;
                           if (cmd->entry->flags & CMD_CANTNEST)
                                   cmdflags |= CMD_CANTNEST;
                   }
                   cmd_list_free(cmdlist);
           }
   
           /*
            * Check if this could be a nested session, if the command can't nest:
            * if the socket path matches $TMUX, this is probably the same server.
            */
           if (shell_cmd == NULL && environ_path != NULL &&
               cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) {
                   log_warnx("sessions should be nested with care. "
                       "unset $TMUX to force.");
                   return (1);
           }
   
           /* Initialise the client socket and start the server. */
           fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
           if (fd == -1) {
                   log_warn("failed to connect to server");
                   return (1);
           }
   
           /* Set process title, log and signals now this is the client. */
           setproctitle("client (%s)", socket_path);
           logfile("client");
   
           /* Create imsg. */
         imsg_init(&client_ibuf, fd);          imsg_init(&client_ibuf, fd);
         event_set(&client_event, fd, EV_READ, client_callback, NULL);          event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
   
           /* Establish signal handlers. */
           set_signals(client_signal);
   
           /* Send initial environment. */
         if (cmdflags & CMD_SENDENVIRON)          if (cmdflags & CMD_SENDENVIRON)
                 client_send_environ();                  client_send_environ();
         client_send_identify(flags);          client_send_identify(flags);
   
         return (&client_ibuf);          /* Send first command. */
           if (msg == MSG_COMMAND) {
                   /* Fill in command line arguments. */
                   cmddata.pid = environ_pid;
                   cmddata.idx = environ_idx;
   
 start_failed:                  /* Prepare command for server. */
         log_warnx("server failed to start");                  cmddata.argc = argc;
         return (NULL);                  if (cmd_pack_argv(
                       argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
                           log_warnx("command too long");
                           return (1);
                   }
   
 not_found:                  client_write_server(msg, &cmddata, sizeof cmddata);
         log_warn("server not found");          } else if (msg == MSG_SHELL)
         return (NULL);                  client_write_server(msg, NULL, 0);
   
           /* Set the event and dispatch. */
           client_update_event();
           event_dispatch();
   
           /* Print the exit message, if any, and exit. */
           if (client_attached && client_exitmsg != NULL && !login_shell)
                   printf("[%s]\n", client_exitmsg);
           return (client_exitval);
 }  }
   
   /* Send identify message to server with the file descriptors. */
 void  void
 client_send_identify(int flags)  client_send_identify(int flags)
 {  {
Line 132 
Line 225 
   
         if ((fd = dup(STDOUT_FILENO)) == -1)          if ((fd = dup(STDOUT_FILENO)) == -1)
                 fatal("dup failed");                  fatal("dup failed");
         imsg_compose(&client_ibuf, MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);          imsg_compose(&client_ibuf,
               MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
   
         if ((fd = dup(STDERR_FILENO)) == -1)          if ((fd = dup(STDERR_FILENO)) == -1)
                 fatal("dup failed");                  fatal("dup failed");
         imsg_compose(&client_ibuf, MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);          imsg_compose(&client_ibuf,
               MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
 }  }
   
   /* Forward entire environment to server. */
 void  void
 client_send_environ(void)  client_send_environ(void)
 {  {
Line 152 
Line 248 
         }          }
 }  }
   
   /* Write a message to the server without a file descriptor. */
 void  void
 client_write_server(enum msgtype type, void *buf, size_t len)  client_write_server(enum msgtype type, void *buf, size_t len)
 {  {
         imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);          imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
 }  }
   
   /* Update client event based on whether it needs to read or read and write. */
 void  void
 client_update_event(void)  client_update_event(void)
 {  {
Line 167 
Line 265 
         events = EV_READ;          events = EV_READ;
         if (client_ibuf.w.queued > 0)          if (client_ibuf.w.queued > 0)
                 events |= EV_WRITE;                  events |= EV_WRITE;
         event_set(&client_event, client_ibuf.fd, events, client_callback, NULL);          event_set(
               &client_event, client_ibuf.fd, events, client_callback, shell_cmd);
         event_add(&client_event, NULL);          event_add(&client_event, NULL);
 }  }
   
 __dead void  /* Callback to handle signals in the client. */
 client_main(void)  
 {  
         logfile("client");  
   
         /* Note: event_init() has already been called. */  
   
         /* Set up signals. */  
         set_signals(client_signal);  
   
         /*  
          * Send a resize message immediately in case the terminal size has  
          * changed between the identify message to the server and the MSG_READY  
          * telling us to move into the client code.  
          */  
          client_write_server(MSG_RESIZE, NULL, 0);  
   
         /*  
          * imsg_read in the first client poll loop (before the terminal has  
          * been initialised) may have read messages into the buffer after the  
          * MSG_READY switched to here. Process anything outstanding now to  
          * avoid hanging waiting for messages that have already arrived.  
          */  
         if (client_dispatch() != 0)  
                 goto out;  
   
         /* Set the event and dispatch. */  
         client_update_event();  
         event_dispatch();  
   
 out:  
         /* Print the exit message, if any, and exit. */  
         if (client_exitmsg != NULL && !login_shell)  
                 printf("[%s]\n", client_exitmsg);  
         exit(client_exitval);  
 }  
   
 /* ARGSUSED */  /* ARGSUSED */
 void  void
 client_signal(int sig, unused short events, unused void *data)  client_signal(int sig, unused short events, unused void *data)
 {  {
         struct sigaction        sigact;          struct sigaction sigact;
           int              status;
   
         switch (sig) {          if (!client_attached) {
         case SIGHUP:                  switch (sig) {
                 client_exitmsg = "lost tty";                  case SIGCHLD:
                 client_exitval = 1;                          waitpid(WAIT_ANY, &status, WNOHANG);
                 client_write_server(MSG_EXITING, NULL, 0);                          break;
                 break;                  case SIGTERM:
         case SIGTERM:                          event_loopexit(NULL);
                 client_exitmsg = "terminated";                          break;
                 client_exitval = 1;                  }
                 client_write_server(MSG_EXITING, NULL, 0);          } else {
                 break;                  switch (sig) {
         case SIGWINCH:                  case SIGHUP:
                 client_write_server(MSG_RESIZE, NULL, 0);                          client_exitmsg = "lost tty";
                 break;                          client_exitval = 1;
         case SIGCONT:                          client_write_server(MSG_EXITING, NULL, 0);
                 memset(&sigact, 0, sizeof sigact);                          break;
                 sigemptyset(&sigact.sa_mask);                  case SIGTERM:
                 sigact.sa_flags = SA_RESTART;                          client_exitmsg = "terminated";
                 sigact.sa_handler = SIG_IGN;                          client_exitval = 1;
                 if (sigaction(SIGTSTP, &sigact, NULL) != 0)                          client_write_server(MSG_EXITING, NULL, 0);
                         fatal("sigaction failed");                          break;
                 client_write_server(MSG_WAKEUP, NULL, 0);                  case SIGWINCH:
                 break;                          client_write_server(MSG_RESIZE, NULL, 0);
                           break;
                   case SIGCONT:
                           memset(&sigact, 0, sizeof sigact);
                           sigemptyset(&sigact.sa_mask);
                           sigact.sa_flags = SA_RESTART;
                           sigact.sa_handler = SIG_IGN;
                           if (sigaction(SIGTSTP, &sigact, NULL) != 0)
                                   fatal("sigaction failed");
                           client_write_server(MSG_WAKEUP, NULL, 0);
                           break;
                   }
         }          }
   
         client_update_event();          client_update_event();
 }  }
   
   /* Callback for client imsg read events. */
 /* ARGSUSED */  /* ARGSUSED */
 void  void
 client_callback(unused int fd, short events, unused void *data)  client_callback(unused int fd, short events, void *data)
 {  {
         ssize_t n;          ssize_t n;
           int     retval;
   
         if (events & EV_READ) {          if (events & EV_READ) {
                 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)                  if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
                         goto lost_server;                          goto lost_server;
                 if (client_dispatch() != 0) {                  if (client_attached)
                           retval = client_dispatch_attached();
                   else
                           retval = client_dispatch_wait(data);
                   if (retval != 0) {
                         event_loopexit(NULL);                          event_loopexit(NULL);
                         return;                          return;
                 }                  }
Line 271 
Line 352 
         event_loopexit(NULL);          event_loopexit(NULL);
 }  }
   
   /* Dispatch imsgs when in wait state (before MSG_READY). */
 int  int
 client_dispatch(void)  client_dispatch_wait(void *data)
   {
           struct imsg             imsg;
           ssize_t                 n, datalen;
           struct msg_shell_data   shelldata;
           struct msg_exit_data    exitdata;
           const char             *shellcmd = data;
   
           if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
                   fatalx("imsg_read failed");
   
           for (;;) {
                   if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
                           fatalx("imsg_get failed");
                   if (n == 0)
                           return (0);
                   datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
   
                   switch (imsg.hdr.type) {
                   case MSG_EXIT:
                   case MSG_SHUTDOWN:
                           if (datalen != sizeof exitdata) {
                                   if (datalen != 0)
                                           fatalx("bad MSG_EXIT size");
                           } else {
                                   memcpy(&exitdata, imsg.data, sizeof exitdata);
                                   client_exitval = exitdata.retcode;
                           }
                           imsg_free(&imsg);
                           return (-1);
                   case MSG_READY:
                           if (datalen != 0)
                                   fatalx("bad MSG_READY size");
   
                           client_attached = 1;
                           break;
                   case MSG_VERSION:
                           if (datalen != 0)
                                   fatalx("bad MSG_VERSION size");
   
                           log_warnx("protocol version mismatch (client %u, "
                               "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
                           client_exitval = 1;
   
                           imsg_free(&imsg);
                           return (-1);
                   case MSG_SHELL:
                           if (datalen != sizeof shelldata)
                                   fatalx("bad MSG_SHELL size");
                           memcpy(&shelldata, imsg.data, sizeof shelldata);
                           shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
   
                           clear_signals(0);
   
                           shell_exec(shelldata.shell, shellcmd);
                           /* NOTREACHED */
                   default:
                           fatalx("unexpected message");
                   }
   
                   imsg_free(&imsg);
           }
   }
   
   /* Dispatch imsgs in attached state (after MSG_READY). */
   /* ARGSUSED */
   int
   client_dispatch_attached(void)
 {  {
         struct imsg             imsg;          struct imsg             imsg;
         struct msg_lock_data    lockdata;          struct msg_lock_data    lockdata;

Legend:
Removed from v.1.45  
changed lines
  Added in v.1.46