Add comments to ACS table matching terminfo(5).
[tmux-openbsd.git] / server-client.c
blobc6257edbd6f12c2c1a3a70505c0c65a2caec922e
1 /* $OpenBSD$ */
3 /*
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>
20 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <event.h>
24 #include <fcntl.h>
25 #include <paths.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
31 #include "tmux.h"
33 void server_client_check_focus(struct window_pane *);
34 void server_client_check_resize(struct window_pane *);
35 void server_client_check_mouse(struct client *, struct window_pane *);
36 void server_client_repeat_timer(int, short, void *);
37 void server_client_check_exit(struct client *);
38 void server_client_check_redraw(struct client *);
39 void server_client_set_title(struct client *);
40 void server_client_reset_state(struct client *);
41 int server_client_assume_paste(struct session *);
43 int server_client_msg_dispatch(struct client *);
44 void server_client_msg_command(struct client *, struct imsg *);
45 void server_client_msg_identify(struct client *, struct imsg *);
46 void server_client_msg_shell(struct client *);
48 /* Create a new client. */
49 void
50 server_client_create(int fd)
52 struct client *c;
53 u_int i;
55 setblocking(fd, 0);
57 c = xcalloc(1, sizeof *c);
58 c->references = 0;
59 imsg_init(&c->ibuf, fd);
60 server_update_event(c);
62 if (gettimeofday(&c->creation_time, NULL) != 0)
63 fatal("gettimeofday failed");
64 memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
66 environ_init(&c->environ);
68 c->cmdq = cmdq_new(c);
69 c->cmdq->client_exit = 1;
71 c->stdin_data = evbuffer_new ();
72 c->stdout_data = evbuffer_new ();
73 c->stderr_data = evbuffer_new ();
75 c->tty.fd = -1;
76 c->title = NULL;
78 c->session = NULL;
79 c->last_session = NULL;
80 c->tty.sx = 80;
81 c->tty.sy = 24;
83 screen_init(&c->status, c->tty.sx, 1, 0);
84 RB_INIT(&c->status_new);
85 RB_INIT(&c->status_old);
87 c->message_string = NULL;
88 ARRAY_INIT(&c->message_log);
90 c->prompt_string = NULL;
91 c->prompt_buffer = NULL;
92 c->prompt_index = 0;
94 c->tty.mouse.xb = c->tty.mouse.button = 3;
95 c->tty.mouse.x = c->tty.mouse.y = -1;
96 c->tty.mouse.lx = c->tty.mouse.ly = -1;
97 c->tty.mouse.sx = c->tty.mouse.sy = -1;
98 c->tty.mouse.event = MOUSE_EVENT_UP;
99 c->tty.mouse.flags = 0;
101 c->flags |= CLIENT_FOCUSED;
103 evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
105 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
106 if (ARRAY_ITEM(&clients, i) == NULL) {
107 ARRAY_SET(&clients, i, c);
108 return;
111 ARRAY_ADD(&clients, c);
112 log_debug("new client %d", fd);
115 /* Open client terminal if needed. */
117 server_client_open(struct client *c, struct session *s, char **cause)
119 struct options *oo = s != NULL ? &s->options : &global_s_options;
120 char *overrides;
122 if (c->flags & CLIENT_CONTROL)
123 return (0);
125 if (!(c->flags & CLIENT_TERMINAL)) {
126 *cause = xstrdup ("not a terminal");
127 return (-1);
130 overrides = options_get_string(oo, "terminal-overrides");
131 if (tty_open(&c->tty, overrides, cause) != 0)
132 return (-1);
134 return (0);
137 /* Lost a client. */
138 void
139 server_client_lost(struct client *c)
141 struct message_entry *msg;
142 u_int i;
144 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
145 if (ARRAY_ITEM(&clients, i) == c)
146 ARRAY_SET(&clients, i, NULL);
148 log_debug("lost client %d", c->ibuf.fd);
151 * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
152 * and tty_free might close an unrelated fd.
154 if (c->flags & CLIENT_TERMINAL)
155 tty_free(&c->tty);
156 free(c->ttyname);
157 free(c->term);
159 evbuffer_free(c->stdin_data);
160 evbuffer_free(c->stdout_data);
161 if (c->stderr_data != c->stdout_data)
162 evbuffer_free (c->stderr_data);
164 status_free_jobs(&c->status_new);
165 status_free_jobs(&c->status_old);
166 screen_free(&c->status);
168 free(c->title);
169 close(c->cwd);
171 evtimer_del(&c->repeat_timer);
173 if (event_initialized(&c->identify_timer))
174 evtimer_del(&c->identify_timer);
176 free(c->message_string);
177 if (event_initialized (&c->message_timer))
178 evtimer_del(&c->message_timer);
179 for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) {
180 msg = &ARRAY_ITEM(&c->message_log, i);
181 free(msg->msg);
183 ARRAY_FREE(&c->message_log);
185 free(c->prompt_string);
186 free(c->prompt_buffer);
188 c->cmdq->dead = 1;
189 cmdq_free(c->cmdq);
190 c->cmdq = NULL;
192 environ_free(&c->environ);
194 close(c->ibuf.fd);
195 imsg_clear(&c->ibuf);
196 if (event_initialized(&c->event))
197 event_del(&c->event);
199 for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
200 if (ARRAY_ITEM(&dead_clients, i) == NULL) {
201 ARRAY_SET(&dead_clients, i, c);
202 break;
205 if (i == ARRAY_LENGTH(&dead_clients))
206 ARRAY_ADD(&dead_clients, c);
207 c->flags |= CLIENT_DEAD;
209 server_add_accept(0); /* may be more file descriptors now */
211 recalculate_sizes();
212 server_check_unattached();
213 server_update_socket();
216 /* Process a single client event. */
217 void
218 server_client_callback(int fd, short events, void *data)
220 struct client *c = data;
222 if (c->flags & CLIENT_DEAD)
223 return;
225 if (fd == c->ibuf.fd) {
226 if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0 &&
227 errno != EAGAIN)
228 goto client_lost;
230 if (c->flags & CLIENT_BAD) {
231 if (c->ibuf.w.queued == 0)
232 goto client_lost;
233 return;
236 if (events & EV_READ && server_client_msg_dispatch(c) != 0)
237 goto client_lost;
240 server_push_stdout(c);
241 server_push_stderr(c);
243 server_update_event(c);
244 return;
246 client_lost:
247 server_client_lost(c);
250 /* Handle client status timer. */
251 void
252 server_client_status_timer(void)
254 struct client *c;
255 struct session *s;
256 struct timeval tv;
257 u_int i;
258 int interval;
259 time_t difference;
261 if (gettimeofday(&tv, NULL) != 0)
262 fatal("gettimeofday failed");
264 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
265 c = ARRAY_ITEM(&clients, i);
266 if (c == NULL || c->session == NULL)
267 continue;
268 if (c->message_string != NULL || c->prompt_string != NULL) {
270 * Don't need timed redraw for messages/prompts so bail
271 * now. The status timer isn't reset when they are
272 * redrawn anyway.
274 continue;
276 s = c->session;
278 if (!options_get_number(&s->options, "status"))
279 continue;
280 interval = options_get_number(&s->options, "status-interval");
282 difference = tv.tv_sec - c->status_timer.tv_sec;
283 if (difference >= interval) {
284 status_update_jobs(c);
285 c->flags |= CLIENT_STATUS;
290 /* Check for mouse keys. */
291 void
292 server_client_check_mouse(struct client *c, struct window_pane *wp)
294 struct session *s = c->session;
295 struct options *oo = &s->options;
296 struct mouse_event *m = &c->tty.mouse;
297 int statusat;
299 statusat = status_at_line(c);
301 /* Is this a window selection click on the status line? */
302 if (statusat != -1 && m->y == (u_int)statusat &&
303 options_get_number(oo, "mouse-select-window")) {
304 if (m->event & MOUSE_EVENT_CLICK) {
305 status_set_window_at(c, m->x);
306 } else if (m->event == MOUSE_EVENT_WHEEL) {
307 if (m->wheel == MOUSE_WHEEL_UP)
308 session_previous(c->session, 0);
309 else if (m->wheel == MOUSE_WHEEL_DOWN)
310 session_next(c->session, 0);
311 server_redraw_session(s);
313 recalculate_sizes();
314 return;
318 * Not on status line - adjust mouse position if status line is at the
319 * top and limit if at the bottom. From here on a struct mouse
320 * represents the offset onto the window itself.
322 if (statusat == 0 && m->y > 0)
323 m->y--;
324 else if (statusat > 0 && m->y >= (u_int)statusat)
325 m->y = statusat - 1;
327 /* Is this a pane selection? Allow down only in copy mode. */
328 if (options_get_number(oo, "mouse-select-pane") &&
329 (m->event == MOUSE_EVENT_DOWN || wp->mode != &window_copy_mode)) {
330 window_set_active_at(wp->window, m->x, m->y);
331 server_redraw_window_borders(wp->window);
332 wp = wp->window->active; /* may have changed */
335 /* Check if trying to resize pane. */
336 if (options_get_number(oo, "mouse-resize-pane"))
337 layout_resize_pane_mouse(c);
339 /* Update last and pass through to client. */
340 window_pane_mouse(wp, c->session, m);
343 /* Is this fast enough to probably be a paste? */
345 server_client_assume_paste(struct session *s)
347 struct timeval tv;
348 int t;
350 if ((t = options_get_number(&s->options, "assume-paste-time")) == 0)
351 return (0);
353 timersub(&s->activity_time, &s->last_activity_time, &tv);
354 if (tv.tv_sec == 0 && tv.tv_usec < t * 1000)
355 return (1);
356 return (0);
359 /* Handle data key input from client. */
360 void
361 server_client_handle_key(struct client *c, int key)
363 struct session *s;
364 struct window *w;
365 struct window_pane *wp;
366 struct timeval tv;
367 struct key_binding *bd;
368 int xtimeout, isprefix, ispaste;
370 /* Check the client is good to accept input. */
371 if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
372 return;
374 if (c->session == NULL)
375 return;
376 s = c->session;
378 /* Update the activity timer. */
379 if (gettimeofday(&c->activity_time, NULL) != 0)
380 fatal("gettimeofday failed");
382 memcpy(&s->last_activity_time, &s->activity_time,
383 sizeof s->last_activity_time);
384 memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
386 w = c->session->curw->window;
387 wp = w->active;
389 /* Special case: number keys jump to pane in identify mode. */
390 if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
391 if (c->flags & CLIENT_READONLY)
392 return;
393 window_unzoom(w);
394 wp = window_pane_at_index(w, key - '0');
395 if (wp != NULL && window_pane_visible(wp))
396 window_set_active_pane(w, wp);
397 server_clear_identify(c);
398 return;
401 /* Handle status line. */
402 if (!(c->flags & CLIENT_READONLY)) {
403 status_message_clear(c);
404 server_clear_identify(c);
406 if (c->prompt_string != NULL) {
407 if (!(c->flags & CLIENT_READONLY))
408 status_prompt_key(c, key);
409 return;
412 /* Check for mouse keys. */
413 if (key == KEYC_MOUSE) {
414 if (c->flags & CLIENT_READONLY)
415 return;
416 server_client_check_mouse(c, wp);
417 return;
420 /* Is this a prefix key? */
421 if (key == options_get_number(&s->options, "prefix"))
422 isprefix = 1;
423 else if (key == options_get_number(&s->options, "prefix2"))
424 isprefix = 1;
425 else
426 isprefix = 0;
428 /* Treat prefix as a regular key when pasting is detected. */
429 ispaste = server_client_assume_paste(s);
430 if (ispaste)
431 isprefix = 0;
433 /* No previous prefix key. */
434 if (!(c->flags & CLIENT_PREFIX)) {
435 if (isprefix) {
436 c->flags |= CLIENT_PREFIX;
437 server_status_client(c);
438 return;
441 /* Try as a non-prefix key binding. */
442 if (ispaste || (bd = key_bindings_lookup(key)) == NULL) {
443 if (!(c->flags & CLIENT_READONLY))
444 window_pane_key(wp, s, key);
445 } else
446 key_bindings_dispatch(bd, c);
447 return;
450 /* Prefix key already pressed. Reset prefix and lookup key. */
451 c->flags &= ~CLIENT_PREFIX;
452 server_status_client(c);
453 if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
454 /* If repeating, treat this as a key, else ignore. */
455 if (c->flags & CLIENT_REPEAT) {
456 c->flags &= ~CLIENT_REPEAT;
457 if (isprefix)
458 c->flags |= CLIENT_PREFIX;
459 else if (!(c->flags & CLIENT_READONLY))
460 window_pane_key(wp, s, key);
462 return;
465 /* If already repeating, but this key can't repeat, skip it. */
466 if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
467 c->flags &= ~CLIENT_REPEAT;
468 if (isprefix)
469 c->flags |= CLIENT_PREFIX;
470 else if (!(c->flags & CLIENT_READONLY))
471 window_pane_key(wp, s, key);
472 return;
475 /* If this key can repeat, reset the repeat flags and timer. */
476 xtimeout = options_get_number(&s->options, "repeat-time");
477 if (xtimeout != 0 && bd->can_repeat) {
478 c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
480 tv.tv_sec = xtimeout / 1000;
481 tv.tv_usec = (xtimeout % 1000) * 1000L;
482 evtimer_del(&c->repeat_timer);
483 evtimer_add(&c->repeat_timer, &tv);
486 /* Dispatch the command. */
487 key_bindings_dispatch(bd, c);
490 /* Client functions that need to happen every loop. */
491 void
492 server_client_loop(void)
494 struct client *c;
495 struct window *w;
496 struct window_pane *wp;
497 u_int i;
499 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
500 c = ARRAY_ITEM(&clients, i);
501 if (c == NULL)
502 continue;
504 server_client_check_exit(c);
505 if (c->session != NULL) {
506 server_client_check_redraw(c);
507 server_client_reset_state(c);
512 * Any windows will have been redrawn as part of clients, so clear
513 * their flags now. Also check pane focus and resize.
515 for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
516 w = ARRAY_ITEM(&windows, i);
517 if (w == NULL)
518 continue;
520 w->flags &= ~WINDOW_REDRAW;
521 TAILQ_FOREACH(wp, &w->panes, entry) {
522 if (wp->fd != -1) {
523 server_client_check_focus(wp);
524 server_client_check_resize(wp);
526 wp->flags &= ~PANE_REDRAW;
531 /* Check if pane should be resized. */
532 void
533 server_client_check_resize(struct window_pane *wp)
535 struct winsize ws;
537 if (!(wp->flags & PANE_RESIZE))
538 return;
540 memset(&ws, 0, sizeof ws);
541 ws.ws_col = wp->sx;
542 ws.ws_row = wp->sy;
544 if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
545 fatal("ioctl failed");
547 wp->flags &= ~PANE_RESIZE;
550 /* Check whether pane should be focused. */
551 void
552 server_client_check_focus(struct window_pane *wp)
554 u_int i;
555 struct client *c;
556 int push;
558 /* Are focus events off? */
559 if (!options_get_number(&global_options, "focus-events"))
560 return;
562 /* Do we need to push the focus state? */
563 push = wp->flags & PANE_FOCUSPUSH;
564 wp->flags &= ~PANE_FOCUSPUSH;
566 /* If we don't care about focus, forget it. */
567 if (!(wp->base.mode & MODE_FOCUSON))
568 return;
570 /* If we're not the active pane in our window, we're not focused. */
571 if (wp->window->active != wp)
572 goto not_focused;
574 /* If we're in a mode, we're not focused. */
575 if (wp->screen != &wp->base)
576 goto not_focused;
579 * If our window is the current window in any focused clients with an
580 * attached session, we're focused.
582 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
583 c = ARRAY_ITEM(&clients, i);
584 if (c == NULL || c->session == NULL)
585 continue;
587 if (!(c->flags & CLIENT_FOCUSED))
588 continue;
589 if (c->session->flags & SESSION_UNATTACHED)
590 continue;
592 if (c->session->curw->window == wp->window)
593 goto focused;
596 not_focused:
597 if (push || (wp->flags & PANE_FOCUSED))
598 bufferevent_write(wp->event, "\033[O", 3);
599 wp->flags &= ~PANE_FOCUSED;
600 return;
602 focused:
603 if (push || !(wp->flags & PANE_FOCUSED))
604 bufferevent_write(wp->event, "\033[I", 3);
605 wp->flags |= PANE_FOCUSED;
609 * Update cursor position and mode settings. The scroll region and attributes
610 * are cleared when idle (waiting for an event) as this is the most likely time
611 * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
612 * compromise between excessive resets and likelihood of an interrupt.
614 * tty_region/tty_reset/tty_update_mode already take care of not resetting
615 * things that are already in their default state.
617 void
618 server_client_reset_state(struct client *c)
620 struct window *w = c->session->curw->window;
621 struct window_pane *wp = w->active;
622 struct screen *s = wp->screen;
623 struct options *oo = &c->session->options;
624 struct options *wo = &w->options;
625 int status, mode, o;
627 if (c->flags & CLIENT_SUSPENDED)
628 return;
630 if (c->flags & CLIENT_CONTROL)
631 return;
633 tty_region(&c->tty, 0, c->tty.sy - 1);
635 status = options_get_number(oo, "status");
636 if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
637 tty_cursor(&c->tty, 0, 0);
638 else {
639 o = status && options_get_number (oo, "status-position") == 0;
640 tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
644 * Resizing panes with the mouse requires at least button mode to give
645 * a smooth appearance.
647 mode = s->mode;
648 if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) &&
649 !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY)))
650 mode |= MODE_MOUSE_BUTTON;
653 * Any mode will do for mouse-select-pane, but set standard mode if
654 * none.
656 if ((mode & ALL_MOUSE_MODES) == 0) {
657 if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
658 options_get_number(oo, "mouse-select-pane"))
659 mode |= MODE_MOUSE_STANDARD;
660 else if (options_get_number(oo, "mouse-resize-pane"))
661 mode |= MODE_MOUSE_STANDARD;
662 else if (options_get_number(oo, "mouse-select-window"))
663 mode |= MODE_MOUSE_STANDARD;
664 else if (options_get_number(wo, "mode-mouse"))
665 mode |= MODE_MOUSE_STANDARD;
669 * Set UTF-8 mouse input if required. If the terminal is UTF-8, the
670 * user has set mouse-utf8 and any mouse mode is in effect, turn on
671 * UTF-8 mouse input. If the receiving terminal hasn't requested it
672 * (that is, it isn't in s->mode), then it'll be converted in
673 * input_mouse.
675 if ((c->tty.flags & TTY_UTF8) &&
676 (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8"))
677 mode |= MODE_MOUSE_UTF8;
678 else
679 mode &= ~MODE_MOUSE_UTF8;
681 /* Set the terminal mode and reset attributes. */
682 tty_update_mode(&c->tty, mode, s);
683 tty_reset(&c->tty);
686 /* Repeat time callback. */
687 void
688 server_client_repeat_timer(unused int fd, unused short events, void *data)
690 struct client *c = data;
692 if (c->flags & CLIENT_REPEAT) {
693 if (c->flags & CLIENT_PREFIX)
694 server_status_client(c);
695 c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
699 /* Check if client should be exited. */
700 void
701 server_client_check_exit(struct client *c)
703 if (!(c->flags & CLIENT_EXIT))
704 return;
706 if (EVBUFFER_LENGTH(c->stdin_data) != 0)
707 return;
708 if (EVBUFFER_LENGTH(c->stdout_data) != 0)
709 return;
710 if (EVBUFFER_LENGTH(c->stderr_data) != 0)
711 return;
713 server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval);
714 c->flags &= ~CLIENT_EXIT;
717 /* Check for client redraws. */
718 void
719 server_client_check_redraw(struct client *c)
721 struct session *s = c->session;
722 struct window_pane *wp;
723 int flags, redraw;
725 if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
726 return;
728 flags = c->tty.flags & TTY_FREEZE;
729 c->tty.flags &= ~TTY_FREEZE;
731 if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
732 if (options_get_number(&s->options, "set-titles"))
733 server_client_set_title(c);
735 if (c->message_string != NULL)
736 redraw = status_message_redraw(c);
737 else if (c->prompt_string != NULL)
738 redraw = status_prompt_redraw(c);
739 else
740 redraw = status_redraw(c);
741 if (!redraw)
742 c->flags &= ~CLIENT_STATUS;
745 if (c->flags & CLIENT_REDRAW) {
746 screen_redraw_screen(c, 0, 0);
747 c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
748 } else if (c->flags & CLIENT_REDRAWWINDOW) {
749 TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
750 screen_redraw_pane(c, wp);
751 c->flags &= ~CLIENT_REDRAWWINDOW;
752 } else {
753 TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
754 if (wp->flags & PANE_REDRAW)
755 screen_redraw_pane(c, wp);
759 if (c->flags & CLIENT_BORDERS)
760 screen_redraw_screen(c, 0, 1);
762 if (c->flags & CLIENT_STATUS)
763 screen_redraw_screen(c, 1, 0);
765 c->tty.flags |= flags;
767 c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS);
770 /* Set client title. */
771 void
772 server_client_set_title(struct client *c)
774 struct session *s = c->session;
775 const char *template;
776 char *title;
778 template = options_get_string(&s->options, "set-titles-string");
780 title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1);
781 if (c->title == NULL || strcmp(title, c->title) != 0) {
782 free(c->title);
783 c->title = xstrdup(title);
784 tty_set_title(&c->tty, c->title);
786 free(title);
789 /* Dispatch message from client. */
791 server_client_msg_dispatch(struct client *c)
793 struct imsg imsg;
794 struct msg_stdin_data stdindata;
795 const char *data;
796 ssize_t n, datalen;
798 if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
799 return (-1);
801 for (;;) {
802 if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
803 return (-1);
804 if (n == 0)
805 return (0);
807 data = imsg.data;
808 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
810 if (imsg.hdr.peerid != PROTOCOL_VERSION) {
811 server_write_client(c, MSG_VERSION, NULL, 0);
812 c->flags |= CLIENT_BAD;
813 if (imsg.fd != -1)
814 close(imsg.fd);
815 imsg_free(&imsg);
816 continue;
819 log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
820 switch (imsg.hdr.type) {
821 case MSG_IDENTIFY_FLAGS:
822 case MSG_IDENTIFY_TERM:
823 case MSG_IDENTIFY_TTYNAME:
824 case MSG_IDENTIFY_CWD:
825 case MSG_IDENTIFY_STDIN:
826 case MSG_IDENTIFY_ENVIRON:
827 case MSG_IDENTIFY_DONE:
828 server_client_msg_identify(c, &imsg);
829 break;
830 case MSG_COMMAND:
831 server_client_msg_command(c, &imsg);
832 break;
833 case MSG_STDIN:
834 if (datalen != sizeof stdindata)
835 fatalx("bad MSG_STDIN size");
836 memcpy(&stdindata, data, sizeof stdindata);
838 if (c->stdin_callback == NULL)
839 break;
840 if (stdindata.size <= 0)
841 c->stdin_closed = 1;
842 else {
843 evbuffer_add(c->stdin_data, stdindata.data,
844 stdindata.size);
846 c->stdin_callback(c, c->stdin_closed,
847 c->stdin_callback_data);
848 break;
849 case MSG_RESIZE:
850 if (datalen != 0)
851 fatalx("bad MSG_RESIZE size");
853 if (c->flags & CLIENT_CONTROL)
854 break;
855 if (tty_resize(&c->tty)) {
856 recalculate_sizes();
857 server_redraw_client(c);
859 break;
860 case MSG_EXITING:
861 if (datalen != 0)
862 fatalx("bad MSG_EXITING size");
864 c->session = NULL;
865 tty_close(&c->tty);
866 server_write_client(c, MSG_EXITED, NULL, 0);
867 break;
868 case MSG_WAKEUP:
869 case MSG_UNLOCK:
870 if (datalen != 0)
871 fatalx("bad MSG_WAKEUP size");
873 if (!(c->flags & CLIENT_SUSPENDED))
874 break;
875 c->flags &= ~CLIENT_SUSPENDED;
877 if (gettimeofday(&c->activity_time, NULL) != 0)
878 fatal("gettimeofday");
879 if (c->session != NULL)
880 session_update_activity(c->session);
882 tty_start_tty(&c->tty);
883 server_redraw_client(c);
884 recalculate_sizes();
885 break;
886 case MSG_SHELL:
887 if (datalen != 0)
888 fatalx("bad MSG_SHELL size");
890 server_client_msg_shell(c);
891 break;
894 imsg_free(&imsg);
898 /* Handle command message. */
899 void
900 server_client_msg_command(struct client *c, struct imsg *imsg)
902 struct msg_command_data data;
903 char *buf;
904 size_t len;
905 struct cmd_list *cmdlist = NULL;
906 int argc;
907 char **argv, *cause;
909 if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
910 fatalx("bad MSG_COMMAND size");
911 memcpy(&data, imsg->data, sizeof data);
913 buf = (char*)imsg->data + sizeof data;
914 len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data;
915 if (len > 0 && buf[len - 1] != '\0')
916 fatalx("bad MSG_COMMAND string");
918 argc = data.argc;
919 if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
920 cmdq_error(c->cmdq, "command too long");
921 goto error;
924 if (argc == 0) {
925 argc = 1;
926 argv = xcalloc(1, sizeof *argv);
927 *argv = xstrdup("new-session");
930 if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
931 cmdq_error(c->cmdq, "%s", cause);
932 cmd_free_argv(argc, argv);
933 goto error;
935 cmd_free_argv(argc, argv);
937 if (c != cfg_client || cfg_finished)
938 cmdq_run(c->cmdq, cmdlist);
939 else
940 cmdq_append(c->cmdq, cmdlist);
941 cmd_list_free(cmdlist);
942 return;
944 error:
945 if (cmdlist != NULL)
946 cmd_list_free(cmdlist);
948 c->flags |= CLIENT_EXIT;
951 /* Handle identify message. */
952 void
953 server_client_msg_identify(struct client *c, struct imsg *imsg)
955 const char *data;
956 size_t datalen;
957 int flags;
959 if (c->flags & CLIENT_IDENTIFIED)
960 fatalx("out-of-order identify message");
962 data = imsg->data;
963 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
965 switch (imsg->hdr.type) {
966 case MSG_IDENTIFY_FLAGS:
967 if (datalen != sizeof flags)
968 fatalx("bad MSG_IDENTIFY_FLAGS size");
969 memcpy(&flags, data, sizeof flags);
970 c->flags |= flags;
971 break;
972 case MSG_IDENTIFY_TERM:
973 if (datalen == 0 || data[datalen - 1] != '\0')
974 fatalx("bad MSG_IDENTIFY_TERM string");
975 c->term = xstrdup(data);
976 break;
977 case MSG_IDENTIFY_TTYNAME:
978 if (datalen == 0 || data[datalen - 1] != '\0')
979 fatalx("bad MSG_IDENTIFY_TTYNAME string");
980 c->ttyname = xstrdup(data);
981 break;
982 case MSG_IDENTIFY_CWD:
983 if (datalen != 0)
984 fatalx("bad MSG_IDENTIFY_CWD size");
985 c->cwd = imsg->fd;
986 break;
987 case MSG_IDENTIFY_STDIN:
988 if (datalen != 0)
989 fatalx("bad MSG_IDENTIFY_STDIN size");
990 c->fd = imsg->fd;
991 break;
992 case MSG_IDENTIFY_ENVIRON:
993 if (datalen == 0 || data[datalen - 1] != '\0')
994 fatalx("bad MSG_IDENTIFY_ENVIRON string");
995 if (strchr(data, '=') != NULL)
996 environ_put(&c->environ, data);
997 break;
998 default:
999 break;
1002 if (imsg->hdr.type != MSG_IDENTIFY_DONE)
1003 return;
1004 c->flags |= CLIENT_IDENTIFIED;
1006 #ifdef __CYGWIN__
1007 c->fd = open(c->ttyname, O_RDWR|O_NOCTTY);
1008 c->cwd = open(".", O_RDONLY);
1009 #endif
1011 if (c->flags & CLIENT_CONTROL) {
1012 c->stdin_callback = control_callback;
1014 evbuffer_free(c->stderr_data);
1015 c->stderr_data = c->stdout_data;
1017 if (c->flags & CLIENT_CONTROLCONTROL)
1018 evbuffer_add_printf(c->stdout_data, "\033P1000p");
1019 server_write_client(c, MSG_STDIN, NULL, 0);
1021 c->tty.fd = -1;
1022 c->tty.log_fd = -1;
1024 close(c->fd);
1025 c->fd = -1;
1027 return;
1030 if (c->fd == -1)
1031 return;
1032 if (!isatty(c->fd)) {
1033 close(c->fd);
1034 c->fd = -1;
1035 return;
1037 tty_init(&c->tty, c, c->fd, c->term);
1038 if (c->flags & CLIENT_UTF8)
1039 c->tty.flags |= TTY_UTF8;
1040 if (c->flags & CLIENT_256COLOURS)
1041 c->tty.term_flags |= TERM_256COLOURS;
1043 tty_resize(&c->tty);
1045 if (!(c->flags & CLIENT_CONTROL))
1046 c->flags |= CLIENT_TERMINAL;
1049 /* Handle shell message. */
1050 void
1051 server_client_msg_shell(struct client *c)
1053 const char *shell;
1055 shell = options_get_string(&global_s_options, "default-shell");
1056 if (*shell == '\0' || areshell(shell))
1057 shell = _PATH_BSHELL;
1058 server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1);
1060 c->flags |= CLIENT_BAD; /* it will die after exec */