4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/socket.h>
35 static struct tmuxproc
*client_proc
;
36 static struct tmuxpeer
*client_peer
;
37 static uint64_t client_flags
;
38 static int client_suspended
;
42 CLIENT_EXIT_DETACHED_HUP
,
44 CLIENT_EXIT_TERMINATED
,
45 CLIENT_EXIT_LOST_SERVER
,
47 CLIENT_EXIT_SERVER_EXITED
,
48 CLIENT_EXIT_MESSAGE_PROVIDED
49 } client_exitreason
= CLIENT_EXIT_NONE
;
50 static int client_exitflag
;
51 static int client_exitval
;
52 static enum msgtype client_exittype
;
53 static const char *client_exitsession
;
54 static char *client_exitmessage
;
55 static const char *client_execshell
;
56 static const char *client_execcmd
;
57 static int client_attached
;
58 static struct client_files client_files
= RB_INITIALIZER(&client_files
);
60 static __dead
void client_exec(const char *,const char *);
61 static int client_get_lock(char *);
62 static int client_connect(struct event_base
*, const char *,
64 static void client_send_identify(const char *, const char *,
65 char **, u_int
, const char *, int);
66 static void client_signal(int);
67 static void client_dispatch(struct imsg
*, void *);
68 static void client_dispatch_attached(struct imsg
*);
69 static void client_dispatch_wait(struct imsg
*);
70 static const char *client_exit_message(void);
73 * Get server create lock. If already held then server start is happening in
74 * another client, so block until the lock is released and return -2 to
75 * retry. Return -1 on failure to continue and start the server anyway.
78 client_get_lock(char *lockfile
)
82 log_debug("lock file is %s", lockfile
);
84 if ((lockfd
= open(lockfile
, O_WRONLY
|O_CREAT
, 0600)) == -1) {
85 log_debug("open failed: %s", strerror(errno
));
89 if (flock(lockfd
, LOCK_EX
|LOCK_NB
) == -1) {
90 log_debug("flock failed: %s", strerror(errno
));
93 while (flock(lockfd
, LOCK_EX
) == -1 && errno
== EINTR
)
98 log_debug("flock succeeded");
103 /* Connect client to server. */
105 client_connect(struct event_base
*base
, const char *path
, uint64_t flags
)
107 struct sockaddr_un sa
;
109 int fd
, lockfd
= -1, locked
= 0;
110 char *lockfile
= NULL
;
112 memset(&sa
, 0, sizeof sa
);
113 sa
.sun_family
= AF_UNIX
;
114 size
= strlcpy(sa
.sun_path
, path
, sizeof sa
.sun_path
);
115 if (size
>= sizeof sa
.sun_path
) {
116 errno
= ENAMETOOLONG
;
119 log_debug("socket is %s", path
);
122 if ((fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1)
125 log_debug("trying connect");
126 if (connect(fd
, (struct sockaddr
*)&sa
, sizeof sa
) == -1) {
127 log_debug("connect failed: %s", strerror(errno
));
128 if (errno
!= ECONNREFUSED
&& errno
!= ENOENT
)
130 if (flags
& CLIENT_NOSTARTSERVER
)
132 if (~flags
& CLIENT_STARTSERVER
)
137 xasprintf(&lockfile
, "%s.lock", path
);
138 if ((lockfd
= client_get_lock(lockfile
)) < 0) {
139 log_debug("didn't get lock (%d)", lockfd
);
147 log_debug("got lock (%d)", lockfd
);
150 * Always retry at least once, even if we got the lock,
151 * because another client could have taken the lock,
152 * started the server and released the lock between our
153 * connect() and flock().
159 if (lockfd
>= 0 && unlink(path
) != 0 && errno
!= ENOENT
) {
164 fd
= server_start(client_proc
, flags
, base
, lockfd
, lockfile
);
167 if (locked
&& lockfd
>= 0) {
183 /* Get exit string from reason number. */
185 client_exit_message(void)
187 static char msg
[256];
189 switch (client_exitreason
) {
190 case CLIENT_EXIT_NONE
:
192 case CLIENT_EXIT_DETACHED
:
193 if (client_exitsession
!= NULL
) {
194 xsnprintf(msg
, sizeof msg
, "detached "
195 "(from session %s)", client_exitsession
);
199 case CLIENT_EXIT_DETACHED_HUP
:
200 if (client_exitsession
!= NULL
) {
201 xsnprintf(msg
, sizeof msg
, "detached and SIGHUP "
202 "(from session %s)", client_exitsession
);
205 return ("detached and SIGHUP");
206 case CLIENT_EXIT_LOST_TTY
:
208 case CLIENT_EXIT_TERMINATED
:
209 return ("terminated");
210 case CLIENT_EXIT_LOST_SERVER
:
211 return ("server exited unexpectedly");
212 case CLIENT_EXIT_EXITED
:
214 case CLIENT_EXIT_SERVER_EXITED
:
215 return ("server exited");
216 case CLIENT_EXIT_MESSAGE_PROVIDED
:
217 return (client_exitmessage
);
219 return ("unknown reason");
222 /* Exit if all streams flushed. */
226 if (!file_write_left(&client_files
))
227 proc_exit(client_proc
);
230 /* Client main loop. */
232 client_main(struct event_base
*base
, int argc
, char **argv
, uint64_t flags
,
235 struct cmd_parse_result
*pr
;
236 struct msg_command
*data
;
238 const char *ttynam
, *termname
, *cwd
;
241 struct termios tio
, saved_tio
;
242 size_t size
, linesize
= 0;
244 char *line
= NULL
, **caps
= NULL
, *cause
;
246 struct args_value
*values
;
248 /* Set up the initial command. */
249 if (shell_command
!= NULL
) {
251 flags
|= CLIENT_STARTSERVER
;
252 } else if (argc
== 0) {
254 flags
|= CLIENT_STARTSERVER
;
259 * It's annoying parsing the command string twice (in client
260 * and later in server) but it is necessary to get the start
263 values
= args_from_vector(argc
, argv
);
264 pr
= cmd_parse_from_arguments(values
, argc
, NULL
);
265 if (pr
->status
== CMD_PARSE_SUCCESS
) {
266 if (cmd_list_any_have(pr
->cmdlist
, CMD_STARTSERVER
))
267 flags
|= CLIENT_STARTSERVER
;
268 cmd_list_free(pr
->cmdlist
);
271 args_free_values(values
, argc
);
275 /* Create client process structure (starts logging). */
276 client_proc
= proc_start("client");
277 proc_set_signals(client_proc
, client_signal
);
279 /* Save the flags. */
280 client_flags
= flags
;
281 log_debug("flags are %#llx", (unsigned long long)client_flags
);
283 /* Initialize the client socket and start the server. */
285 if (systemd_activated()) {
286 /* socket-based activation, do not even try to be a client. */
287 fd
= server_start(client_proc
, flags
, base
, 0, NULL
);
290 fd
= client_connect(base
, socket_path
, client_flags
);
292 if (errno
== ECONNREFUSED
) {
293 fprintf(stderr
, "no server running on %s\n",
296 fprintf(stderr
, "error connecting to %s (%s)\n",
297 socket_path
, strerror(errno
));
301 client_peer
= proc_add_peer(client_proc
, fd
, client_dispatch
, NULL
);
303 /* Save these before pledge(). */
304 if ((cwd
= find_cwd()) == NULL
&& (cwd
= find_home()) == NULL
)
306 if ((ttynam
= ttyname(STDIN_FILENO
)) == NULL
)
308 if ((termname
= getenv("TERM")) == NULL
)
312 * Drop privileges for client. "proc exec" is needed for -c and for
313 * locking (which uses system(3)).
315 * "tty" is needed to restore termios(4) and also for some reason -CC
316 * does not work properly without it (input is not recognised).
318 * "sendfd" is dropped later in client_dispatch_wait().
321 "stdio rpath wpath cpath unix sendfd proc exec tty",
323 fatal("pledge failed");
325 /* Load terminfo entry if any. */
326 if (isatty(STDIN_FILENO
) &&
328 tty_term_read_list(termname
, STDIN_FILENO
, &caps
, &ncaps
,
330 fprintf(stderr
, "%s\n", cause
);
335 /* Free stuff that is not used in the client. */
338 options_free(global_options
);
339 options_free(global_s_options
);
340 options_free(global_w_options
);
341 environ_free(global_environ
);
343 /* Set up control mode. */
344 if (client_flags
& CLIENT_CONTROLCONTROL
) {
345 if (tcgetattr(STDIN_FILENO
, &saved_tio
) != 0) {
346 fprintf(stderr
, "tcgetattr failed: %s\n",
351 tio
.c_iflag
= ICRNL
|IXANY
;
352 tio
.c_oflag
= OPOST
|ONLCR
;
354 tio
.c_lflag
= NOKERNINFO
;
356 tio
.c_cflag
= CREAD
|CS8
|HUPCL
;
359 cfsetispeed(&tio
, cfgetispeed(&saved_tio
));
360 cfsetospeed(&tio
, cfgetospeed(&saved_tio
));
361 tcsetattr(STDIN_FILENO
, TCSANOW
, &tio
);
364 /* Send identify messages. */
365 client_send_identify(ttynam
, termname
, caps
, ncaps
, cwd
, feat
);
366 tty_term_free_list(caps
, ncaps
);
367 proc_flush_peer(client_peer
);
369 /* Send first command. */
370 if (msg
== MSG_COMMAND
) {
371 /* How big is the command? */
373 for (i
= 0; i
< argc
; i
++)
374 size
+= strlen(argv
[i
]) + 1;
375 if (size
> MAX_IMSGSIZE
- (sizeof *data
)) {
376 fprintf(stderr
, "command too long\n");
379 data
= xmalloc((sizeof *data
) + size
);
381 /* Prepare command for server. */
383 if (cmd_pack_argv(argc
, argv
, (char *)(data
+ 1), size
) != 0) {
384 fprintf(stderr
, "command too long\n");
388 size
+= sizeof *data
;
390 /* Send the command. */
391 if (proc_send(client_peer
, msg
, -1, data
, size
) != 0) {
392 fprintf(stderr
, "failed to send command\n");
397 } else if (msg
== MSG_SHELL
)
398 proc_send(client_peer
, msg
, -1, NULL
, 0);
400 /* Start main loop. */
401 proc_loop(client_proc
, NULL
);
403 /* Run command if user requested exec, instead of exiting. */
404 if (client_exittype
== MSG_EXEC
) {
405 if (client_flags
& CLIENT_CONTROLCONTROL
)
406 tcsetattr(STDOUT_FILENO
, TCSAFLUSH
, &saved_tio
);
407 client_exec(client_execshell
, client_execcmd
);
410 /* Restore streams to blocking. */
411 setblocking(STDIN_FILENO
, 1);
412 setblocking(STDOUT_FILENO
, 1);
413 setblocking(STDERR_FILENO
, 1);
415 /* Print the exit message, if any, and exit. */
416 if (client_attached
) {
417 if (client_exitreason
!= CLIENT_EXIT_NONE
)
418 printf("[%s]\n", client_exit_message());
421 if (client_exittype
== MSG_DETACHKILL
&& ppid
> 1)
423 } else if (client_flags
& CLIENT_CONTROL
) {
424 if (client_exitreason
!= CLIENT_EXIT_NONE
)
425 printf("%%exit %s\n", client_exit_message());
429 if (client_flags
& CLIENT_CONTROL_WAITEXIT
) {
430 setvbuf(stdin
, NULL
, _IOLBF
, 0);
432 linelen
= getline(&line
, &linesize
, stdin
);
438 if (client_flags
& CLIENT_CONTROLCONTROL
) {
441 tcsetattr(STDOUT_FILENO
, TCSAFLUSH
, &saved_tio
);
443 } else if (client_exitreason
!= CLIENT_EXIT_NONE
)
444 fprintf(stderr
, "%s\n", client_exit_message());
445 return (client_exitval
);
448 /* Send identify messages to server. */
450 client_send_identify(const char *ttynam
, const char *termname
, char **caps
,
451 u_int ncaps
, const char *cwd
, int feat
)
455 int fd
, flags
= client_flags
;
459 proc_send(client_peer
, MSG_IDENTIFY_FLAGS
, -1, &flags
, sizeof flags
);
460 proc_send(client_peer
, MSG_IDENTIFY_LONGFLAGS
, -1, &client_flags
,
461 sizeof client_flags
);
463 proc_send(client_peer
, MSG_IDENTIFY_TERM
, -1, termname
,
464 strlen(termname
) + 1);
465 proc_send(client_peer
, MSG_IDENTIFY_FEATURES
, -1, &feat
, sizeof feat
);
467 proc_send(client_peer
, MSG_IDENTIFY_TTYNAME
, -1, ttynam
,
469 proc_send(client_peer
, MSG_IDENTIFY_CWD
, -1, cwd
, strlen(cwd
) + 1);
471 for (i
= 0; i
< ncaps
; i
++) {
472 proc_send(client_peer
, MSG_IDENTIFY_TERMINFO
, -1,
473 caps
[i
], strlen(caps
[i
]) + 1);
476 if ((fd
= dup(STDIN_FILENO
)) == -1)
478 proc_send(client_peer
, MSG_IDENTIFY_STDIN
, fd
, NULL
, 0);
479 if ((fd
= dup(STDOUT_FILENO
)) == -1)
481 proc_send(client_peer
, MSG_IDENTIFY_STDOUT
, fd
, NULL
, 0);
484 proc_send(client_peer
, MSG_IDENTIFY_CLIENTPID
, -1, &pid
, sizeof pid
);
486 for (ss
= environ
; *ss
!= NULL
; ss
++) {
487 sslen
= strlen(*ss
) + 1;
488 if (sslen
> MAX_IMSGSIZE
- IMSG_HEADER_SIZE
)
490 proc_send(client_peer
, MSG_IDENTIFY_ENVIRON
, -1, *ss
, sslen
);
493 proc_send(client_peer
, MSG_IDENTIFY_DONE
, -1, NULL
, 0);
496 /* Run command in shell; used for -c. */
498 client_exec(const char *shell
, const char *shellcmd
)
500 const char *name
, *ptr
;
503 log_debug("shell %s, command %s", shell
, shellcmd
);
505 ptr
= strrchr(shell
, '/');
506 if (ptr
!= NULL
&& *(ptr
+ 1) != '\0')
510 if (client_flags
& CLIENT_LOGIN
)
511 xasprintf(&argv0
, "-%s", name
);
513 xasprintf(&argv0
, "%s", name
);
514 setenv("SHELL", shell
, 1);
516 proc_clear_signals(client_proc
, 1);
518 setblocking(STDIN_FILENO
, 1);
519 setblocking(STDOUT_FILENO
, 1);
520 setblocking(STDERR_FILENO
, 1);
521 closefrom(STDERR_FILENO
+ 1);
523 execl(shell
, argv0
, "-c", shellcmd
, (char *) NULL
);
524 fatal("execl failed");
527 /* Callback to handle signals in the client. */
529 client_signal(int sig
)
531 struct sigaction sigact
;
535 log_debug("%s: %s", __func__
, strsignal(sig
));
536 if (sig
== SIGCHLD
) {
538 pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
);
544 log_debug("waitpid failed: %s",
548 } else if (!client_attached
) {
549 if (sig
== SIGTERM
|| sig
== SIGHUP
)
550 proc_exit(client_proc
);
554 client_exitreason
= CLIENT_EXIT_LOST_TTY
;
556 proc_send(client_peer
, MSG_EXITING
, -1, NULL
, 0);
559 if (!client_suspended
)
560 client_exitreason
= CLIENT_EXIT_TERMINATED
;
562 proc_send(client_peer
, MSG_EXITING
, -1, NULL
, 0);
565 proc_send(client_peer
, MSG_RESIZE
, -1, NULL
, 0);
568 memset(&sigact
, 0, sizeof sigact
);
569 sigemptyset(&sigact
.sa_mask
);
570 sigact
.sa_flags
= SA_RESTART
;
571 sigact
.sa_handler
= SIG_IGN
;
572 if (sigaction(SIGTSTP
, &sigact
, NULL
) != 0)
573 fatal("sigaction failed");
574 proc_send(client_peer
, MSG_WAKEUP
, -1, NULL
, 0);
575 client_suspended
= 0;
581 /* Callback for file write error or close. */
583 client_file_check_cb(__unused
struct client
*c
, __unused
const char *path
,
584 __unused
int error
, __unused
int closed
, __unused
struct evbuffer
*buffer
,
591 /* Callback for client read events. */
593 client_dispatch(struct imsg
*imsg
, __unused
void *arg
)
596 if (!client_exitflag
) {
597 client_exitreason
= CLIENT_EXIT_LOST_SERVER
;
600 proc_exit(client_proc
);
605 client_dispatch_attached(imsg
);
607 client_dispatch_wait(imsg
);
610 /* Process an exit message. */
612 client_dispatch_exit_message(char *data
, size_t datalen
)
616 if (datalen
< sizeof retval
&& datalen
!= 0)
617 fatalx("bad MSG_EXIT size");
619 if (datalen
>= sizeof retval
) {
620 memcpy(&retval
, data
, sizeof retval
);
621 client_exitval
= retval
;
624 if (datalen
> sizeof retval
) {
625 datalen
-= sizeof retval
;
626 data
+= sizeof retval
;
628 client_exitmessage
= xmalloc(datalen
);
629 memcpy(client_exitmessage
, data
, datalen
);
630 client_exitmessage
[datalen
- 1] = '\0';
632 client_exitreason
= CLIENT_EXIT_MESSAGE_PROVIDED
;
636 /* Dispatch imsgs when in wait state (before MSG_READY). */
638 client_dispatch_wait(struct imsg
*imsg
)
642 static int pledge_applied
;
645 * "sendfd" is no longer required once all of the identify messages
646 * have been sent. We know the server won't send us anything until that
647 * point (because we don't ask it to), so we can drop "sendfd" once we
648 * get the first message from the server.
650 if (!pledge_applied
) {
652 "stdio rpath wpath cpath unix proc exec tty",
654 fatal("pledge failed");
659 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
661 switch (imsg
->hdr
.type
) {
664 client_dispatch_exit_message(data
, datalen
);
670 fatalx("bad MSG_READY size");
673 proc_send(client_peer
, MSG_RESIZE
, -1, NULL
, 0);
677 fatalx("bad MSG_VERSION size");
679 fprintf(stderr
, "protocol version mismatch "
680 "(client %d, server %u)\n", PROTOCOL_VERSION
,
681 imsg
->hdr
.peerid
& 0xff);
683 proc_exit(client_proc
);
686 if (datalen
!= sizeof client_flags
)
687 fatalx("bad MSG_FLAGS string");
689 memcpy(&client_flags
, data
, sizeof client_flags
);
690 log_debug("new flags are %#llx",
691 (unsigned long long)client_flags
);
694 if (datalen
== 0 || data
[datalen
- 1] != '\0')
695 fatalx("bad MSG_SHELL string");
697 client_exec(data
, shell_command
);
701 proc_send(client_peer
, MSG_EXITING
, -1, NULL
, 0);
704 proc_exit(client_proc
);
707 file_read_open(&client_files
, client_peer
, imsg
, 1,
708 !(client_flags
& CLIENT_CONTROL
), client_file_check_cb
,
711 case MSG_READ_CANCEL
:
712 file_read_cancel(&client_files
, imsg
);
715 file_write_open(&client_files
, client_peer
, imsg
, 1,
716 !(client_flags
& CLIENT_CONTROL
), client_file_check_cb
,
720 file_write_data(&client_files
, imsg
);
722 case MSG_WRITE_CLOSE
:
723 file_write_close(&client_files
, imsg
);
728 fprintf(stderr
, "server version is too old for client\n");
729 proc_exit(client_proc
);
734 /* Dispatch imsgs in attached state (after MSG_READY). */
736 client_dispatch_attached(struct imsg
*imsg
)
738 struct sigaction sigact
;
743 datalen
= imsg
->hdr
.len
- IMSG_HEADER_SIZE
;
745 switch (imsg
->hdr
.type
) {
747 if (datalen
!= sizeof client_flags
)
748 fatalx("bad MSG_FLAGS string");
750 memcpy(&client_flags
, data
, sizeof client_flags
);
751 log_debug("new flags are %#llx",
752 (unsigned long long)client_flags
);
756 if (datalen
== 0 || data
[datalen
- 1] != '\0')
757 fatalx("bad MSG_DETACH string");
759 client_exitsession
= xstrdup(data
);
760 client_exittype
= imsg
->hdr
.type
;
761 if (imsg
->hdr
.type
== MSG_DETACHKILL
)
762 client_exitreason
= CLIENT_EXIT_DETACHED_HUP
;
764 client_exitreason
= CLIENT_EXIT_DETACHED
;
765 proc_send(client_peer
, MSG_EXITING
, -1, NULL
, 0);
768 if (datalen
== 0 || data
[datalen
- 1] != '\0' ||
769 strlen(data
) + 1 == (size_t)datalen
)
770 fatalx("bad MSG_EXEC string");
771 client_execcmd
= xstrdup(data
);
772 client_execshell
= xstrdup(data
+ strlen(data
) + 1);
774 client_exittype
= imsg
->hdr
.type
;
775 proc_send(client_peer
, MSG_EXITING
, -1, NULL
, 0);
778 client_dispatch_exit_message(data
, datalen
);
779 if (client_exitreason
== CLIENT_EXIT_NONE
)
780 client_exitreason
= CLIENT_EXIT_EXITED
;
781 proc_send(client_peer
, MSG_EXITING
, -1, NULL
, 0);
785 fatalx("bad MSG_EXITED size");
787 proc_exit(client_proc
);
791 fatalx("bad MSG_SHUTDOWN size");
793 proc_send(client_peer
, MSG_EXITING
, -1, NULL
, 0);
794 client_exitreason
= CLIENT_EXIT_SERVER_EXITED
;
799 fatalx("bad MSG_SUSPEND size");
801 memset(&sigact
, 0, sizeof sigact
);
802 sigemptyset(&sigact
.sa_mask
);
803 sigact
.sa_flags
= SA_RESTART
;
804 sigact
.sa_handler
= SIG_DFL
;
805 if (sigaction(SIGTSTP
, &sigact
, NULL
) != 0)
806 fatal("sigaction failed");
807 client_suspended
= 1;
808 kill(getpid(), SIGTSTP
);
811 if (datalen
== 0 || data
[datalen
- 1] != '\0')
812 fatalx("bad MSG_LOCK string");
815 proc_send(client_peer
, MSG_UNLOCK
, -1, NULL
, 0);