4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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>
30 void server_client_handle_key(int, struct mouse_event
*, void *);
31 void server_client_repeat_timer(int, short, void *);
32 void server_client_check_redraw(struct client
*);
33 void server_client_set_title(struct client
*);
34 void server_client_reset_state(struct client
*);
36 int server_client_msg_dispatch(struct client
*);
37 void server_client_msg_command(struct client
*, struct msg_command_data
*);
38 void server_client_msg_identify(
39 struct client
*, struct msg_identify_data
*, int);
40 void server_client_msg_shell(struct client
*);
42 void printflike2
server_client_msg_error(struct cmd_ctx
*, const char *, ...);
43 void printflike2
server_client_msg_print(struct cmd_ctx
*, const char *, ...);
44 void printflike2
server_client_msg_info(struct cmd_ctx
*, const char *, ...);
46 /* Create a new client. */
48 server_client_create(int fd
)
54 if ((mode
= fcntl(fd
, F_GETFL
)) == -1)
55 fatal("fcntl failed");
56 if (fcntl(fd
, F_SETFL
, mode
|O_NONBLOCK
) == -1)
57 fatal("fcntl failed");
58 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
) == -1)
59 fatal("fcntl failed");
61 c
= xcalloc(1, sizeof *c
);
63 imsg_init(&c
->ibuf
, fd
);
64 server_update_event(c
);
66 if (gettimeofday(&c
->creation_time
, NULL
) != 0)
67 fatal("gettimeofday failed");
68 memcpy(&c
->activity_time
, &c
->creation_time
, sizeof c
->activity_time
);
70 ARRAY_INIT(&c
->prompt_hdata
);
73 c
->stdout_file
= NULL
;
74 c
->stderr_file
= NULL
;
83 screen_init(&c
->status
, c
->tty
.sx
, 1, 0);
84 job_tree_init(&c
->status_jobs
);
86 c
->message_string
= NULL
;
87 ARRAY_INIT(&c
->message_log
);
89 c
->prompt_string
= NULL
;
90 c
->prompt_buffer
= NULL
;
93 evtimer_set(&c
->repeat_timer
, server_client_repeat_timer
, c
);
95 for (i
= 0; i
< ARRAY_LENGTH(&clients
); i
++) {
96 if (ARRAY_ITEM(&clients
, i
) == NULL
) {
97 ARRAY_SET(&clients
, i
, c
);
101 ARRAY_ADD(&clients
, c
);
102 log_debug("new client %d", fd
);
107 server_client_lost(struct client
*c
)
109 struct message_entry
*msg
;
112 for (i
= 0; i
< ARRAY_LENGTH(&clients
); i
++) {
113 if (ARRAY_ITEM(&clients
, i
) == c
)
114 ARRAY_SET(&clients
, i
, NULL
);
116 log_debug("lost client %d", c
->ibuf
.fd
);
119 * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
120 * and tty_free might close an unrelated fd.
122 if (c
->flags
& CLIENT_TERMINAL
)
125 if (c
->stdin_file
!= NULL
)
126 fclose(c
->stdin_file
);
127 if (c
->stdout_file
!= NULL
)
128 fclose(c
->stdout_file
);
129 if (c
->stderr_file
!= NULL
)
130 fclose(c
->stderr_file
);
132 screen_free(&c
->status
);
133 job_tree_free(&c
->status_jobs
);
135 if (c
->title
!= NULL
)
138 evtimer_del(&c
->repeat_timer
);
140 evtimer_del(&c
->identify_timer
);
142 if (c
->message_string
!= NULL
)
143 xfree(c
->message_string
);
144 evtimer_del(&c
->message_timer
);
145 for (i
= 0; i
< ARRAY_LENGTH(&c
->message_log
); i
++) {
146 msg
= &ARRAY_ITEM(&c
->message_log
, i
);
149 ARRAY_FREE(&c
->message_log
);
151 if (c
->prompt_string
!= NULL
)
152 xfree(c
->prompt_string
);
153 if (c
->prompt_buffer
!= NULL
)
154 xfree(c
->prompt_buffer
);
155 for (i
= 0; i
< ARRAY_LENGTH(&c
->prompt_hdata
); i
++)
156 xfree(ARRAY_ITEM(&c
->prompt_hdata
, i
));
157 ARRAY_FREE(&c
->prompt_hdata
);
163 imsg_clear(&c
->ibuf
);
164 event_del(&c
->event
);
166 for (i
= 0; i
< ARRAY_LENGTH(&dead_clients
); i
++) {
167 if (ARRAY_ITEM(&dead_clients
, i
) == NULL
) {
168 ARRAY_SET(&dead_clients
, i
, c
);
172 if (i
== ARRAY_LENGTH(&dead_clients
))
173 ARRAY_ADD(&dead_clients
, c
);
174 c
->flags
|= CLIENT_DEAD
;
177 server_update_socket();
180 /* Process a single client event. */
182 server_client_callback(int fd
, short events
, void *data
)
184 struct client
*c
= data
;
186 if (c
->flags
& CLIENT_DEAD
)
189 if (fd
== c
->ibuf
.fd
) {
190 if (events
& EV_WRITE
&& msgbuf_write(&c
->ibuf
.w
) < 0)
193 if (c
->flags
& CLIENT_BAD
) {
194 if (c
->ibuf
.w
.queued
== 0)
199 if (events
& EV_READ
&& server_client_msg_dispatch(c
) != 0)
203 server_update_event(c
);
207 server_client_lost(c
);
210 /* Handle client status timer. */
212 server_client_status_timer(void)
222 if (gettimeofday(&tv
, NULL
) != 0)
223 fatal("gettimeofday failed");
225 for (i
= 0; i
< ARRAY_LENGTH(&clients
); i
++) {
226 c
= ARRAY_ITEM(&clients
, i
);
227 if (c
== NULL
|| c
->session
== NULL
)
229 if (c
->message_string
!= NULL
|| c
->prompt_string
!= NULL
) {
231 * Don't need timed redraw for messages/prompts so bail
232 * now. The status timer isn't reset when they are
239 if (!options_get_number(&s
->options
, "status"))
241 interval
= options_get_number(&s
->options
, "status-interval");
243 difference
= tv
.tv_sec
- c
->status_timer
.tv_sec
;
244 if (difference
>= interval
) {
245 RB_FOREACH(job
, jobs
, &c
->status_jobs
)
247 c
->flags
|= CLIENT_STATUS
;
252 /* Handle data key input from client. */
254 server_client_handle_key(int key
, struct mouse_event
*mouse
, void *data
)
256 struct client
*c
= data
;
259 struct window_pane
*wp
;
262 struct key_binding
*bd
;
263 struct keylist
*keylist
;
264 int xtimeout
, isprefix
;
267 /* Check the client is good to accept input. */
268 if ((c
->flags
& (CLIENT_DEAD
|CLIENT_SUSPENDED
)) != 0)
270 if (c
->session
== NULL
)
274 /* Update the activity timer. */
275 if (gettimeofday(&c
->activity_time
, NULL
) != 0)
276 fatal("gettimeofday failed");
277 memcpy(&s
->activity_time
, &c
->activity_time
, sizeof s
->activity_time
);
279 w
= c
->session
->curw
->window
;
281 oo
= &c
->session
->options
;
283 /* Special case: number keys jump to pane in identify mode. */
284 if (c
->flags
& CLIENT_IDENTIFY
&& key
>= '0' && key
<= '9') {
285 if (c
->flags
& CLIENT_READONLY
)
287 wp
= window_pane_at_index(w
, key
- '0');
288 if (wp
!= NULL
&& window_pane_visible(wp
))
289 window_set_active_pane(w
, wp
);
290 server_clear_identify(c
);
294 /* Handle status line. */
295 if (!(c
->flags
& CLIENT_READONLY
)) {
296 status_message_clear(c
);
297 server_clear_identify(c
);
299 if (c
->prompt_string
!= NULL
) {
300 if (!(c
->flags
& CLIENT_READONLY
))
301 status_prompt_key(c
, key
);
305 /* Check for mouse keys. */
306 if (key
== KEYC_MOUSE
) {
307 if (c
->flags
& CLIENT_READONLY
)
309 if (options_get_number(oo
, "mouse-select-pane")) {
310 window_set_active_at(w
, mouse
->x
, mouse
->y
);
311 server_redraw_window_borders(w
);
314 window_pane_mouse(wp
, c
->session
, mouse
);
318 /* Is this a prefix key? */
319 keylist
= options_get_data(&c
->session
->options
, "prefix");
321 for (i
= 0; i
< ARRAY_LENGTH(keylist
); i
++) {
322 if (key
== ARRAY_ITEM(keylist
, i
)) {
328 /* No previous prefix key. */
329 if (!(c
->flags
& CLIENT_PREFIX
)) {
331 c
->flags
|= CLIENT_PREFIX
;
333 /* Try as a non-prefix key binding. */
334 if ((bd
= key_bindings_lookup(key
)) == NULL
) {
335 if (!(c
->flags
& CLIENT_READONLY
))
336 window_pane_key(wp
, c
->session
, key
);
338 key_bindings_dispatch(bd
, c
);
343 /* Prefix key already pressed. Reset prefix and lookup key. */
344 c
->flags
&= ~CLIENT_PREFIX
;
345 if ((bd
= key_bindings_lookup(key
| KEYC_PREFIX
)) == NULL
) {
346 /* If repeating, treat this as a key, else ignore. */
347 if (c
->flags
& CLIENT_REPEAT
) {
348 c
->flags
&= ~CLIENT_REPEAT
;
350 c
->flags
|= CLIENT_PREFIX
;
351 else if (!(c
->flags
& CLIENT_READONLY
))
352 window_pane_key(wp
, c
->session
, key
);
357 /* If already repeating, but this key can't repeat, skip it. */
358 if (c
->flags
& CLIENT_REPEAT
&& !bd
->can_repeat
) {
359 c
->flags
&= ~CLIENT_REPEAT
;
361 c
->flags
|= CLIENT_PREFIX
;
362 else if (!(c
->flags
& CLIENT_READONLY
))
363 window_pane_key(wp
, c
->session
, key
);
367 /* If this key can repeat, reset the repeat flags and timer. */
368 xtimeout
= options_get_number(&c
->session
->options
, "repeat-time");
369 if (xtimeout
!= 0 && bd
->can_repeat
) {
370 c
->flags
|= CLIENT_PREFIX
|CLIENT_REPEAT
;
372 tv
.tv_sec
= xtimeout
/ 1000;
373 tv
.tv_usec
= (xtimeout
% 1000) * 1000L;
374 evtimer_del(&c
->repeat_timer
);
375 evtimer_add(&c
->repeat_timer
, &tv
);
378 /* Dispatch the command. */
379 key_bindings_dispatch(bd
, c
);
382 /* Client functions that need to happen every loop. */
384 server_client_loop(void)
388 struct window_pane
*wp
;
391 for (i
= 0; i
< ARRAY_LENGTH(&clients
); i
++) {
392 c
= ARRAY_ITEM(&clients
, i
);
393 if (c
== NULL
|| c
->session
== NULL
)
396 server_client_check_redraw(c
);
397 server_client_reset_state(c
);
401 * Any windows will have been redrawn as part of clients, so clear
404 for (i
= 0; i
< ARRAY_LENGTH(&windows
); i
++) {
405 w
= ARRAY_ITEM(&windows
, i
);
409 w
->flags
&= ~WINDOW_REDRAW
;
410 TAILQ_FOREACH(wp
, &w
->panes
, entry
)
411 wp
->flags
&= ~PANE_REDRAW
;
416 * Update cursor position and mode settings. The scroll region and attributes
417 * are cleared when idle (waiting for an event) as this is the most likely time
418 * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
419 * compromise between excessive resets and likelihood of an interrupt.
421 * tty_region/tty_reset/tty_update_mode already take care of not resetting
422 * things that are already in their default state.
425 server_client_reset_state(struct client
*c
)
427 struct window
*w
= c
->session
->curw
->window
;
428 struct window_pane
*wp
= w
->active
;
429 struct screen
*s
= wp
->screen
;
430 struct options
*oo
= &c
->session
->options
;
433 tty_region(&c
->tty
, 0, c
->tty
.sy
- 1);
435 status
= options_get_number(oo
, "status");
436 if (!window_pane_visible(wp
) || wp
->yoff
+ s
->cy
>= c
->tty
.sy
- status
)
437 tty_cursor(&c
->tty
, 0, 0);
439 tty_cursor(&c
->tty
, wp
->xoff
+ s
->cx
, wp
->yoff
+ s
->cy
);
442 if (TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
) != NULL
&&
443 options_get_number(oo
, "mouse-select-pane"))
445 tty_update_mode(&c
->tty
, mode
);
449 /* Repeat time callback. */
452 server_client_repeat_timer(unused
int fd
, unused
short events
, void *data
)
454 struct client
*c
= data
;
456 if (c
->flags
& CLIENT_REPEAT
)
457 c
->flags
&= ~(CLIENT_PREFIX
|CLIENT_REPEAT
);
460 /* Check for client redraws. */
462 server_client_check_redraw(struct client
*c
)
464 struct session
*s
= c
->session
;
465 struct window_pane
*wp
;
468 flags
= c
->tty
.flags
& TTY_FREEZE
;
469 c
->tty
.flags
&= ~TTY_FREEZE
;
471 if (c
->flags
& (CLIENT_REDRAW
|CLIENT_STATUS
)) {
472 if (options_get_number(&s
->options
, "set-titles"))
473 server_client_set_title(c
);
475 if (c
->message_string
!= NULL
)
476 redraw
= status_message_redraw(c
);
477 else if (c
->prompt_string
!= NULL
)
478 redraw
= status_prompt_redraw(c
);
480 redraw
= status_redraw(c
);
482 c
->flags
&= ~CLIENT_STATUS
;
485 if (c
->flags
& CLIENT_REDRAW
) {
486 screen_redraw_screen(c
, 0, 0);
487 c
->flags
&= ~(CLIENT_STATUS
|CLIENT_BORDERS
);
489 TAILQ_FOREACH(wp
, &c
->session
->curw
->window
->panes
, entry
) {
490 if (wp
->flags
& PANE_REDRAW
)
491 screen_redraw_pane(c
, wp
);
495 if (c
->flags
& CLIENT_BORDERS
)
496 screen_redraw_screen(c
, 0, 1);
498 if (c
->flags
& CLIENT_STATUS
)
499 screen_redraw_screen(c
, 1, 0);
501 c
->tty
.flags
|= flags
;
503 c
->flags
&= ~(CLIENT_REDRAW
|CLIENT_STATUS
|CLIENT_BORDERS
);
506 /* Set client title. */
508 server_client_set_title(struct client
*c
)
510 struct session
*s
= c
->session
;
511 const char *template;
514 template = options_get_string(&s
->options
, "set-titles-string");
516 title
= status_replace(c
, NULL
, template, time(NULL
), 1);
517 if (c
->title
== NULL
|| strcmp(title
, c
->title
) != 0) {
518 if (c
->title
!= NULL
)
520 c
->title
= xstrdup(title
);
521 tty_set_title(&c
->tty
, c
->title
);
526 /* Dispatch message from client. */
528 server_client_msg_dispatch(struct client
*c
)
531 struct msg_command_data commanddata
;
532 struct msg_identify_data identifydata
;
533 struct msg_environ_data environdata
;
536 if ((n
= imsg_read(&c
->ibuf
)) == -1 || n
== 0)
540 if ((n
= imsg_get(&c
->ibuf
, &imsg
)) == -1)
544 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
546 if (imsg
.hdr
.peerid
!= PROTOCOL_VERSION
) {
547 server_write_client(c
, MSG_VERSION
, NULL
, 0);
548 c
->flags
|= CLIENT_BAD
;
553 log_debug("got %d from client %d", imsg
.hdr
.type
, c
->ibuf
.fd
);
554 switch (imsg
.hdr
.type
) {
556 if (datalen
!= sizeof commanddata
)
557 fatalx("bad MSG_COMMAND size");
558 memcpy(&commanddata
, imsg
.data
, sizeof commanddata
);
560 server_client_msg_command(c
, &commanddata
);
563 if (datalen
!= sizeof identifydata
)
564 fatalx("bad MSG_IDENTIFY size");
566 fatalx("MSG_IDENTIFY missing fd");
567 memcpy(&identifydata
, imsg
.data
, sizeof identifydata
);
569 c
->stdin_file
= fdopen(imsg
.fd
, "r");
570 if (c
->stdin_file
== NULL
)
571 fatal("fdopen(stdin) failed");
572 server_client_msg_identify(c
, &identifydata
, imsg
.fd
);
576 fatalx("bad MSG_STDOUT size");
578 fatalx("MSG_STDOUT missing fd");
580 c
->stdout_file
= fdopen(imsg
.fd
, "w");
581 if (c
->stdout_file
== NULL
)
582 fatal("fdopen(stdout) failed");
586 fatalx("bad MSG_STDERR size");
588 fatalx("MSG_STDERR missing fd");
590 c
->stderr_file
= fdopen(imsg
.fd
, "w");
591 if (c
->stderr_file
== NULL
)
592 fatal("fdopen(stderr) failed");
596 fatalx("bad MSG_RESIZE size");
598 if (tty_resize(&c
->tty
)) {
600 server_redraw_client(c
);
605 fatalx("bad MSG_EXITING size");
609 server_write_client(c
, MSG_EXITED
, NULL
, 0);
614 fatalx("bad MSG_WAKEUP size");
616 if (!(c
->flags
& CLIENT_SUSPENDED
))
618 c
->flags
&= ~CLIENT_SUSPENDED
;
620 if (gettimeofday(&c
->activity_time
, NULL
) != 0)
621 fatal("gettimeofday");
622 if (c
->session
!= NULL
) {
623 memcpy(&c
->session
->activity_time
,
625 sizeof c
->session
->activity_time
);
628 tty_start_tty(&c
->tty
);
629 server_redraw_client(c
);
633 if (datalen
!= sizeof environdata
)
634 fatalx("bad MSG_ENVIRON size");
635 memcpy(&environdata
, imsg
.data
, sizeof environdata
);
637 environdata
.var
[(sizeof environdata
.var
) - 1] = '\0';
638 if (strchr(environdata
.var
, '=') != NULL
)
639 environ_put(&c
->environ
, environdata
.var
);
643 fatalx("bad MSG_SHELL size");
645 server_client_msg_shell(c
);
648 fatalx("unexpected message");
655 /* Callback to send error message to client. */
657 server_client_msg_error(struct cmd_ctx
*ctx
, const char *fmt
, ...)
662 vfprintf(ctx
->cmdclient
->stderr_file
, fmt
, ap
);
665 fputc('\n', ctx
->cmdclient
->stderr_file
);
666 fflush(ctx
->cmdclient
->stderr_file
);
668 ctx
->cmdclient
->retcode
= 1;
671 /* Callback to send print message to client. */
673 server_client_msg_print(struct cmd_ctx
*ctx
, const char *fmt
, ...)
678 vfprintf(ctx
->cmdclient
->stdout_file
, fmt
, ap
);
681 fputc('\n', ctx
->cmdclient
->stdout_file
);
682 fflush(ctx
->cmdclient
->stdout_file
);
685 /* Callback to send print message to client, if not quiet. */
687 server_client_msg_info(struct cmd_ctx
*ctx
, const char *fmt
, ...)
691 if (options_get_number(&global_options
, "quiet"))
695 vfprintf(ctx
->cmdclient
->stdout_file
, fmt
, ap
);
698 fputc('\n', ctx
->cmdclient
->stderr_file
);
699 fflush(ctx
->cmdclient
->stdout_file
);
702 /* Handle command message. */
704 server_client_msg_command(struct client
*c
, struct msg_command_data
*data
)
707 struct cmd_list
*cmdlist
= NULL
;
708 struct msg_exit_data exitdata
;
712 ctx
.error
= server_client_msg_error
;
713 ctx
.print
= server_client_msg_print
;
714 ctx
.info
= server_client_msg_info
;
717 ctx
.curclient
= NULL
;
722 data
->argv
[(sizeof data
->argv
) - 1] = '\0';
723 if (cmd_unpack_argv(data
->argv
, sizeof data
->argv
, argc
, &argv
) != 0) {
724 server_client_msg_error(&ctx
, "command too long");
730 argv
= xcalloc(1, sizeof *argv
);
731 *argv
= xstrdup("new-session");
734 if ((cmdlist
= cmd_list_parse(argc
, argv
, &cause
)) == NULL
) {
735 server_client_msg_error(&ctx
, "%s", cause
);
736 cmd_free_argv(argc
, argv
);
739 cmd_free_argv(argc
, argv
);
741 if (cmd_list_exec(cmdlist
, &ctx
) != 1) {
742 exitdata
.retcode
= c
->retcode
;
743 server_write_client(c
, MSG_EXIT
, &exitdata
, sizeof exitdata
);
745 cmd_list_free(cmdlist
);
750 cmd_list_free(cmdlist
);
751 exitdata
.retcode
= c
->retcode
;
752 server_write_client(c
, MSG_EXIT
, &exitdata
, sizeof exitdata
);
755 /* Handle identify message. */
757 server_client_msg_identify(
758 struct client
*c
, struct msg_identify_data
*data
, int fd
)
763 data
->cwd
[(sizeof data
->cwd
) - 1] = '\0';
764 if (*data
->cwd
!= '\0')
765 c
->cwd
= xstrdup(data
->cwd
);
769 if ((tty_fd
= dup(fd
)) == -1)
771 data
->term
[(sizeof data
->term
) - 1] = '\0';
772 tty_init(&c
->tty
, tty_fd
, data
->term
);
773 if (data
->flags
& IDENTIFY_UTF8
)
774 c
->tty
.flags
|= TTY_UTF8
;
775 if (data
->flags
& IDENTIFY_256COLOURS
)
776 c
->tty
.term_flags
|= TERM_256COLOURS
;
777 else if (data
->flags
& IDENTIFY_88COLOURS
)
778 c
->tty
.term_flags
|= TERM_88COLOURS
;
779 c
->tty
.key_callback
= server_client_handle_key
;
784 c
->flags
|= CLIENT_TERMINAL
;
787 /* Handle shell message. */
789 server_client_msg_shell(struct client
*c
)
791 struct msg_shell_data data
;
794 shell
= options_get_string(&global_s_options
, "default-shell");
796 if (*shell
== '\0' || areshell(shell
))
797 shell
= _PATH_BSHELL
;
798 if (strlcpy(data
.shell
, shell
, sizeof data
.shell
) >= sizeof data
.shell
)
799 strlcpy(data
.shell
, _PATH_BSHELL
, sizeof data
.shell
);
801 server_write_client(c
, MSG_SHELL
, &data
, sizeof data
);
802 c
->flags
|= CLIENT_BAD
; /* it will die after exec */