Drop main() prototype. Syncs with NetBSD-8
[minix.git] / external / bsd / tmux / dist / server-client.c
blob2b475db48d6c774c5df09fdb6c5498f8b2e229fc
1 /* Id */
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 <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
30 #include "tmux.h"
32 void server_client_check_focus(struct window_pane *);
33 void server_client_check_resize(struct window_pane *);
34 void server_client_check_mouse(struct client *, struct window_pane *);
35 void server_client_repeat_timer(int, short, void *);
36 void server_client_check_exit(struct client *);
37 void server_client_check_redraw(struct client *);
38 void server_client_set_title(struct client *);
39 void server_client_reset_state(struct client *);
40 int server_client_assume_paste(struct session *);
42 int server_client_msg_dispatch(struct client *);
43 void server_client_msg_command(struct client *, struct imsg *);
44 void server_client_msg_identify(struct client *, struct imsg *);
45 void server_client_msg_shell(struct client *);
47 /* Create a new client. */
48 void
49 server_client_create(int fd)
51 struct client *c;
52 u_int i;
54 setblocking(fd, 0);
56 c = xcalloc(1, sizeof *c);
57 c->references = 0;
58 imsg_init(&c->ibuf, fd);
59 server_update_event(c);
61 if (gettimeofday(&c->creation_time, NULL) != 0)
62 fatal("gettimeofday failed");
63 memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
65 environ_init(&c->environ);
67 c->cmdq = cmdq_new(c);
68 c->cmdq->client_exit = 1;
70 c->stdin_data = evbuffer_new();
71 c->stdout_data = evbuffer_new();
72 c->stderr_data = evbuffer_new();
74 c->tty.fd = -1;
75 c->title = NULL;
77 c->session = NULL;
78 c->last_session = NULL;
79 c->tty.sx = 80;
80 c->tty.sy = 24;
82 screen_init(&c->status, c->tty.sx, 1, 0);
83 RB_INIT(&c->status_new);
84 RB_INIT(&c->status_old);
86 c->message_string = NULL;
87 ARRAY_INIT(&c->message_log);
89 c->prompt_string = NULL;
90 c->prompt_buffer = NULL;
91 c->prompt_index = 0;
93 c->tty.mouse.xb = c->tty.mouse.button = 3;
94 c->tty.mouse.x = c->tty.mouse.y = -1;
95 c->tty.mouse.lx = c->tty.mouse.ly = -1;
96 c->tty.mouse.sx = c->tty.mouse.sy = -1;
97 c->tty.mouse.event = MOUSE_EVENT_UP;
98 c->tty.mouse.flags = 0;
100 c->flags |= CLIENT_FOCUSED;
102 evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
104 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
105 if (ARRAY_ITEM(&clients, i) == NULL) {
106 ARRAY_SET(&clients, i, c);
107 return;
110 ARRAY_ADD(&clients, c);
111 log_debug("new client %d", fd);
114 /* Open client terminal if needed. */
116 server_client_open(struct client *c, struct session *s, char **cause)
118 struct options *oo = s != NULL ? &s->options : &global_s_options;
119 char *overrides;
121 if (c->flags & CLIENT_CONTROL)
122 return (0);
124 if (!(c->flags & CLIENT_TERMINAL)) {
125 *cause = xstrdup("not a terminal");
126 return (-1);
129 overrides = options_get_string(oo, "terminal-overrides");
130 if (tty_open(&c->tty, overrides, cause) != 0)
131 return (-1);
133 return (0);
136 /* Lost a client. */
137 void
138 server_client_lost(struct client *c)
140 struct message_entry *msg;
141 u_int i;
143 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
144 if (ARRAY_ITEM(&clients, i) == c)
145 ARRAY_SET(&clients, i, NULL);
147 log_debug("lost client %d", c->ibuf.fd);
150 * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
151 * and tty_free might close an unrelated fd.
153 if (c->flags & CLIENT_TERMINAL)
154 tty_free(&c->tty);
155 free(c->ttyname);
156 free(c->term);
158 evbuffer_free(c->stdin_data);
159 evbuffer_free(c->stdout_data);
160 if (c->stderr_data != c->stdout_data)
161 evbuffer_free (c->stderr_data);
163 status_free_jobs(&c->status_new);
164 status_free_jobs(&c->status_old);
165 screen_free(&c->status);
167 free(c->title);
168 close(c->cwd);
170 evtimer_del(&c->repeat_timer);
172 if (event_initialized(&c->identify_timer))
173 evtimer_del(&c->identify_timer);
175 free(c->message_string);
176 if (event_initialized(&c->message_timer))
177 evtimer_del(&c->message_timer);
178 for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) {
179 msg = &ARRAY_ITEM(&c->message_log, i);
180 free(msg->msg);
182 ARRAY_FREE(&c->message_log);
184 free(c->prompt_string);
185 free(c->prompt_buffer);
187 c->cmdq->dead = 1;
188 cmdq_free(c->cmdq);
189 c->cmdq = NULL;
191 environ_free(&c->environ);
193 close(c->ibuf.fd);
194 imsg_clear(&c->ibuf);
195 if (event_initialized(&c->event))
196 event_del(&c->event);
198 for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
199 if (ARRAY_ITEM(&dead_clients, i) == NULL) {
200 ARRAY_SET(&dead_clients, i, c);
201 break;
204 if (i == ARRAY_LENGTH(&dead_clients))
205 ARRAY_ADD(&dead_clients, c);
206 c->flags |= CLIENT_DEAD;
208 server_add_accept(0); /* may be more file descriptors now */
210 recalculate_sizes();
211 server_check_unattached();
212 server_update_socket();
215 /* Process a single client event. */
216 void
217 server_client_callback(int fd, short events, void *data)
219 struct client *c = data;
221 if (c->flags & CLIENT_DEAD)
222 return;
224 if (fd == c->ibuf.fd) {
225 if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0 &&
226 errno != EAGAIN)
227 goto client_lost;
229 if (c->flags & CLIENT_BAD) {
230 if (c->ibuf.w.queued == 0)
231 goto client_lost;
232 return;
235 if (events & EV_READ && server_client_msg_dispatch(c) != 0)
236 goto client_lost;
239 server_push_stdout(c);
240 server_push_stderr(c);
242 server_update_event(c);
243 return;
245 client_lost:
246 server_client_lost(c);
249 /* Handle client status timer. */
250 void
251 server_client_status_timer(void)
253 struct client *c;
254 struct session *s;
255 struct timeval tv;
256 u_int i;
257 int interval;
258 time_t difference;
260 if (gettimeofday(&tv, NULL) != 0)
261 fatal("gettimeofday failed");
263 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
264 c = ARRAY_ITEM(&clients, i);
265 if (c == NULL || c->session == NULL)
266 continue;
267 if (c->message_string != NULL || c->prompt_string != NULL) {
269 * Don't need timed redraw for messages/prompts so bail
270 * now. The status timer isn't reset when they are
271 * redrawn anyway.
273 continue;
275 s = c->session;
277 if (!options_get_number(&s->options, "status"))
278 continue;
279 interval = options_get_number(&s->options, "status-interval");
281 difference = tv.tv_sec - c->status_timer.tv_sec;
282 if (interval != 0 && difference >= interval) {
283 status_update_jobs(c);
284 c->flags |= CLIENT_STATUS;
289 /* Check for mouse keys. */
290 void
291 server_client_check_mouse(struct client *c, struct window_pane *wp)
293 struct session *s = c->session;
294 struct options *oo = &s->options;
295 struct mouse_event *m = &c->tty.mouse;
296 int statusat;
298 statusat = status_at_line(c);
300 /* Is this a window selection click on the status line? */
301 if (statusat != -1 && m->y == (u_int)statusat &&
302 options_get_number(oo, "mouse-select-window")) {
303 if (m->event & MOUSE_EVENT_CLICK) {
304 status_set_window_at(c, m->x);
305 } else if (m->event == MOUSE_EVENT_WHEEL) {
306 if (m->wheel == MOUSE_WHEEL_UP)
307 session_previous(c->session, 0);
308 else if (m->wheel == MOUSE_WHEEL_DOWN)
309 session_next(c->session, 0);
310 server_redraw_session(s);
312 recalculate_sizes();
313 return;
317 * Not on status line - adjust mouse position if status line is at the
318 * top and limit if at the bottom. From here on a struct mouse
319 * represents the offset onto the window itself.
321 if (statusat == 0 && m->y > 0)
322 m->y--;
323 else if (statusat > 0 && m->y >= (u_int)statusat)
324 m->y = statusat - 1;
326 /* Is this a pane selection? */
327 if (options_get_number(oo, "mouse-select-pane") &&
328 (m->event == MOUSE_EVENT_DOWN || m->event == MOUSE_EVENT_WHEEL)) {
329 window_set_active_at(wp->window, m->x, m->y);
330 server_redraw_window_borders(wp->window);
331 wp = wp->window->active; /* may have changed */
334 /* Check if trying to resize pane. */
335 if (options_get_number(oo, "mouse-resize-pane"))
336 layout_resize_pane_mouse(c);
338 /* Update last and pass through to client. */
339 window_pane_mouse(wp, c->session, m);
342 /* Is this fast enough to probably be a paste? */
344 server_client_assume_paste(struct session *s)
346 struct timeval tv;
347 int t;
349 if ((t = options_get_number(&s->options, "assume-paste-time")) == 0)
350 return (0);
352 timersub(&s->activity_time, &s->last_activity_time, &tv);
353 if (tv.tv_sec == 0 && tv.tv_usec < t * 1000)
354 return (1);
355 return (0);
358 /* Handle data key input from client. */
359 void
360 server_client_handle_key(struct client *c, int key)
362 struct session *s;
363 struct window *w;
364 struct window_pane *wp;
365 struct timeval tv;
366 struct key_binding *bd;
367 int xtimeout, isprefix, ispaste;
369 /* Check the client is good to accept input. */
370 if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
371 return;
373 if (c->session == NULL)
374 return;
375 s = c->session;
377 /* Update the activity timer. */
378 if (gettimeofday(&c->activity_time, NULL) != 0)
379 fatal("gettimeofday failed");
381 memcpy(&s->last_activity_time, &s->activity_time,
382 sizeof s->last_activity_time);
383 memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
385 w = c->session->curw->window;
386 wp = w->active;
388 /* Special case: number keys jump to pane in identify mode. */
389 if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
390 if (c->flags & CLIENT_READONLY)
391 return;
392 window_unzoom(w);
393 wp = window_pane_at_index(w, key - '0');
394 if (wp != NULL && window_pane_visible(wp))
395 window_set_active_pane(w, wp);
396 server_clear_identify(c);
397 return;
400 /* Handle status line. */
401 if (!(c->flags & CLIENT_READONLY)) {
402 status_message_clear(c);
403 server_clear_identify(c);
405 if (c->prompt_string != NULL) {
406 if (!(c->flags & CLIENT_READONLY))
407 status_prompt_key(c, key);
408 return;
411 /* Check for mouse keys. */
412 if (key == KEYC_MOUSE) {
413 if (c->flags & CLIENT_READONLY)
414 return;
415 server_client_check_mouse(c, wp);
416 return;
419 /* Is this a prefix key? */
420 if (key == options_get_number(&s->options, "prefix"))
421 isprefix = 1;
422 else if (key == options_get_number(&s->options, "prefix2"))
423 isprefix = 1;
424 else
425 isprefix = 0;
427 /* Treat prefix as a regular key when pasting is detected. */
428 ispaste = server_client_assume_paste(s);
429 if (ispaste)
430 isprefix = 0;
432 /* No previous prefix key. */
433 if (!(c->flags & CLIENT_PREFIX)) {
434 if (isprefix) {
435 c->flags |= CLIENT_PREFIX;
436 server_status_client(c);
437 return;
440 /* Try as a non-prefix key binding. */
441 if (ispaste || (bd = key_bindings_lookup(key)) == NULL) {
442 if (!(c->flags & CLIENT_READONLY))
443 window_pane_key(wp, s, key);
444 } else
445 key_bindings_dispatch(bd, c);
446 return;
449 /* Prefix key already pressed. Reset prefix and lookup key. */
450 c->flags &= ~CLIENT_PREFIX;
451 server_status_client(c);
452 if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
453 /* If repeating, treat this as a key, else ignore. */
454 if (c->flags & CLIENT_REPEAT) {
455 c->flags &= ~CLIENT_REPEAT;
456 if (isprefix)
457 c->flags |= CLIENT_PREFIX;
458 else if (!(c->flags & CLIENT_READONLY))
459 window_pane_key(wp, s, key);
461 return;
464 /* If already repeating, but this key can't repeat, skip it. */
465 if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
466 c->flags &= ~CLIENT_REPEAT;
467 if (isprefix)
468 c->flags |= CLIENT_PREFIX;
469 else if (!(c->flags & CLIENT_READONLY))
470 window_pane_key(wp, s, key);
471 return;
474 /* If this key can repeat, reset the repeat flags and timer. */
475 xtimeout = options_get_number(&s->options, "repeat-time");
476 if (xtimeout != 0 && bd->can_repeat) {
477 c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
479 tv.tv_sec = xtimeout / 1000;
480 tv.tv_usec = (xtimeout % 1000) * 1000L;
481 evtimer_del(&c->repeat_timer);
482 evtimer_add(&c->repeat_timer, &tv);
485 /* Dispatch the command. */
486 key_bindings_dispatch(bd, c);
489 /* Client functions that need to happen every loop. */
490 void
491 server_client_loop(void)
493 struct client *c;
494 struct window *w;
495 struct window_pane *wp;
496 u_int i;
498 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
499 c = ARRAY_ITEM(&clients, i);
500 if (c == NULL)
501 continue;
503 server_client_check_exit(c);
504 if (c->session != NULL) {
505 server_client_check_redraw(c);
506 server_client_reset_state(c);
511 * Any windows will have been redrawn as part of clients, so clear
512 * their flags now. Also check pane focus and resize.
514 for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
515 w = ARRAY_ITEM(&windows, i);
516 if (w == NULL)
517 continue;
519 w->flags &= ~WINDOW_REDRAW;
520 TAILQ_FOREACH(wp, &w->panes, entry) {
521 if (wp->fd != -1) {
522 server_client_check_focus(wp);
523 server_client_check_resize(wp);
525 wp->flags &= ~PANE_REDRAW;
530 /* Check if pane should be resized. */
531 void
532 server_client_check_resize(struct window_pane *wp)
534 struct winsize ws;
536 if (!(wp->flags & PANE_RESIZE))
537 return;
539 memset(&ws, 0, sizeof ws);
540 ws.ws_col = wp->sx;
541 ws.ws_row = wp->sy;
543 if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
544 fatal("ioctl failed");
546 wp->flags &= ~PANE_RESIZE;
549 /* Check whether pane should be focused. */
550 void
551 server_client_check_focus(struct window_pane *wp)
553 u_int i;
554 struct client *c;
555 int push;
557 /* Are focus events off? */
558 if (!options_get_number(&global_options, "focus-events"))
559 return;
561 /* Do we need to push the focus state? */
562 push = wp->flags & PANE_FOCUSPUSH;
563 wp->flags &= ~PANE_FOCUSPUSH;
565 /* If we don't care about focus, forget it. */
566 if (!(wp->base.mode & MODE_FOCUSON))
567 return;
569 /* If we're not the active pane in our window, we're not focused. */
570 if (wp->window->active != wp)
571 goto not_focused;
573 /* If we're in a mode, we're not focused. */
574 if (wp->screen != &wp->base)
575 goto not_focused;
578 * If our window is the current window in any focused clients with an
579 * attached session, we're focused.
581 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
582 c = ARRAY_ITEM(&clients, i);
583 if (c == NULL || c->session == NULL)
584 continue;
586 if (!(c->flags & CLIENT_FOCUSED))
587 continue;
588 if (c->session->flags & SESSION_UNATTACHED)
589 continue;
591 if (c->session->curw->window == wp->window)
592 goto focused;
595 not_focused:
596 if (push || (wp->flags & PANE_FOCUSED))
597 bufferevent_write(wp->event, "\033[O", 3);
598 wp->flags &= ~PANE_FOCUSED;
599 return;
601 focused:
602 if (push || !(wp->flags & PANE_FOCUSED))
603 bufferevent_write(wp->event, "\033[I", 3);
604 wp->flags |= PANE_FOCUSED;
608 * Update cursor position and mode settings. The scroll region and attributes
609 * are cleared when idle (waiting for an event) as this is the most likely time
610 * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
611 * compromise between excessive resets and likelihood of an interrupt.
613 * tty_region/tty_reset/tty_update_mode already take care of not resetting
614 * things that are already in their default state.
616 void
617 server_client_reset_state(struct client *c)
619 struct window *w = c->session->curw->window;
620 struct window_pane *wp = w->active;
621 struct screen *s = wp->screen;
622 struct options *oo = &c->session->options;
623 struct options *wo = &w->options;
624 int status, mode, o;
626 if (c->flags & CLIENT_SUSPENDED)
627 return;
629 if (c->flags & CLIENT_CONTROL)
630 return;
632 tty_region(&c->tty, 0, c->tty.sy - 1);
634 status = options_get_number(oo, "status");
635 if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
636 tty_cursor(&c->tty, 0, 0);
637 else {
638 o = status && options_get_number (oo, "status-position") == 0;
639 tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
643 * Resizing panes with the mouse requires at least button mode to give
644 * a smooth appearance.
646 mode = s->mode;
647 if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) &&
648 !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY)))
649 mode |= MODE_MOUSE_BUTTON;
652 * Any mode will do for mouse-select-pane, but set standard mode if
653 * none.
655 if ((mode & ALL_MOUSE_MODES) == 0) {
656 if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
657 options_get_number(oo, "mouse-select-pane"))
658 mode |= MODE_MOUSE_STANDARD;
659 else if (options_get_number(oo, "mouse-resize-pane"))
660 mode |= MODE_MOUSE_STANDARD;
661 else if (options_get_number(oo, "mouse-select-window"))
662 mode |= MODE_MOUSE_STANDARD;
663 else if (options_get_number(wo, "mode-mouse"))
664 mode |= MODE_MOUSE_STANDARD;
668 * Set UTF-8 mouse input if required. If the terminal is UTF-8, the
669 * user has set mouse-utf8 and any mouse mode is in effect, turn on
670 * UTF-8 mouse input. If the receiving terminal hasn't requested it
671 * (that is, it isn't in s->mode), then it'll be converted in
672 * input_mouse.
674 if ((c->tty.flags & TTY_UTF8) &&
675 (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8"))
676 mode |= MODE_MOUSE_UTF8;
677 else
678 mode &= ~MODE_MOUSE_UTF8;
680 /* Set the terminal mode and reset attributes. */
681 tty_update_mode(&c->tty, mode, s);
682 tty_reset(&c->tty);
685 /* Repeat time callback. */
686 void
687 server_client_repeat_timer(unused int fd, unused short events, void *data)
689 struct client *c = data;
691 if (c->flags & CLIENT_REPEAT) {
692 if (c->flags & CLIENT_PREFIX)
693 server_status_client(c);
694 c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
698 /* Check if client should be exited. */
699 void
700 server_client_check_exit(struct client *c)
702 if (!(c->flags & CLIENT_EXIT))
703 return;
705 if (EVBUFFER_LENGTH(c->stdin_data) != 0)
706 return;
707 if (EVBUFFER_LENGTH(c->stdout_data) != 0)
708 return;
709 if (EVBUFFER_LENGTH(c->stderr_data) != 0)
710 return;
712 server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval);
713 c->flags &= ~CLIENT_EXIT;
716 /* Check for client redraws. */
717 void
718 server_client_check_redraw(struct client *c)
720 struct session *s = c->session;
721 struct window_pane *wp;
722 int flags, redraw;
724 if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
725 return;
727 flags = c->tty.flags & TTY_FREEZE;
728 c->tty.flags &= ~TTY_FREEZE;
730 if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
731 if (options_get_number(&s->options, "set-titles"))
732 server_client_set_title(c);
734 if (c->message_string != NULL)
735 redraw = status_message_redraw(c);
736 else if (c->prompt_string != NULL)
737 redraw = status_prompt_redraw(c);
738 else
739 redraw = status_redraw(c);
740 if (!redraw)
741 c->flags &= ~CLIENT_STATUS;
744 if (c->flags & CLIENT_REDRAW) {
745 screen_redraw_screen(c, 1, 1, 1);
746 c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
747 } else if (c->flags & CLIENT_REDRAWWINDOW) {
748 TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
749 screen_redraw_pane(c, wp);
750 c->flags &= ~CLIENT_REDRAWWINDOW;
751 } else {
752 TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
753 if (wp->flags & PANE_REDRAW)
754 screen_redraw_pane(c, wp);
758 if (c->flags & CLIENT_BORDERS)
759 screen_redraw_screen(c, 0, 0, 1);
761 if (c->flags & CLIENT_STATUS)
762 screen_redraw_screen(c, 0, 1, 0);
764 c->tty.flags |= flags;
766 c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS);
769 /* Set client title. */
770 void
771 server_client_set_title(struct client *c)
773 struct session *s = c->session;
774 const char *template;
775 char *title;
777 template = options_get_string(&s->options, "set-titles-string");
779 title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1);
780 if (c->title == NULL || strcmp(title, c->title) != 0) {
781 free(c->title);
782 c->title = xstrdup(title);
783 tty_set_title(&c->tty, c->title);
785 free(title);
788 /* Dispatch message from client. */
790 server_client_msg_dispatch(struct client *c)
792 struct imsg imsg;
793 struct msg_stdin_data stdindata;
794 const char *data;
795 ssize_t n, datalen;
797 if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
798 return (-1);
800 for (;;) {
801 if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
802 return (-1);
803 if (n == 0)
804 return (0);
806 data = imsg.data;
807 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
809 if (imsg.hdr.peerid != PROTOCOL_VERSION) {
810 server_write_client(c, MSG_VERSION, NULL, 0);
811 c->flags |= CLIENT_BAD;
812 if (imsg.fd != -1)
813 close(imsg.fd);
814 imsg_free(&imsg);
815 continue;
818 log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
819 switch (imsg.hdr.type) {
820 case MSG_IDENTIFY_FLAGS:
821 case MSG_IDENTIFY_TERM:
822 case MSG_IDENTIFY_TTYNAME:
823 case MSG_IDENTIFY_CWD:
824 case MSG_IDENTIFY_STDIN:
825 case MSG_IDENTIFY_ENVIRON:
826 case MSG_IDENTIFY_DONE:
827 server_client_msg_identify(c, &imsg);
828 break;
829 case MSG_COMMAND:
830 server_client_msg_command(c, &imsg);
831 break;
832 case MSG_STDIN:
833 if (datalen != sizeof stdindata)
834 fatalx("bad MSG_STDIN size");
835 memcpy(&stdindata, data, sizeof stdindata);
837 if (c->stdin_callback == NULL)
838 break;
839 if (stdindata.size <= 0)
840 c->stdin_closed = 1;
841 else {
842 evbuffer_add(c->stdin_data, stdindata.data,
843 stdindata.size);
845 c->stdin_callback(c, c->stdin_closed,
846 c->stdin_callback_data);
847 break;
848 case MSG_RESIZE:
849 if (datalen != 0)
850 fatalx("bad MSG_RESIZE size");
852 if (c->flags & CLIENT_CONTROL)
853 break;
854 if (tty_resize(&c->tty)) {
855 recalculate_sizes();
856 server_redraw_client(c);
858 break;
859 case MSG_EXITING:
860 if (datalen != 0)
861 fatalx("bad MSG_EXITING size");
863 c->session = NULL;
864 tty_close(&c->tty);
865 server_write_client(c, MSG_EXITED, NULL, 0);
866 break;
867 case MSG_WAKEUP:
868 case MSG_UNLOCK:
869 if (datalen != 0)
870 fatalx("bad MSG_WAKEUP size");
872 if (!(c->flags & CLIENT_SUSPENDED))
873 break;
874 c->flags &= ~CLIENT_SUSPENDED;
876 if (gettimeofday(&c->activity_time, NULL) != 0)
877 fatal("gettimeofday");
878 if (c->session != NULL)
879 session_update_activity(c->session);
881 tty_start_tty(&c->tty);
882 server_redraw_client(c);
883 recalculate_sizes();
884 break;
885 case MSG_SHELL:
886 if (datalen != 0)
887 fatalx("bad MSG_SHELL size");
889 server_client_msg_shell(c);
890 break;
893 imsg_free(&imsg);
897 /* Handle command message. */
898 void
899 server_client_msg_command(struct client *c, struct imsg *imsg)
901 struct msg_command_data data;
902 char *buf;
903 size_t len;
904 struct cmd_list *cmdlist = NULL;
905 int argc;
906 char **argv, *cause;
908 if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
909 fatalx("bad MSG_COMMAND size");
910 memcpy(&data, imsg->data, sizeof data);
912 buf = (char*)imsg->data + sizeof data;
913 len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data;
914 if (len > 0 && buf[len - 1] != '\0')
915 fatalx("bad MSG_COMMAND string");
917 argc = data.argc;
918 if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
919 cmdq_error(c->cmdq, "command too long");
920 goto error;
923 if (argc == 0) {
924 argc = 1;
925 argv = xcalloc(1, sizeof *argv);
926 *argv = xstrdup("new-session");
929 if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
930 cmdq_error(c->cmdq, "%s", cause);
931 cmd_free_argv(argc, argv);
932 goto error;
934 cmd_free_argv(argc, argv);
936 if (c != cfg_client || cfg_finished)
937 cmdq_run(c->cmdq, cmdlist);
938 else
939 cmdq_append(c->cmdq, cmdlist);
940 cmd_list_free(cmdlist);
941 return;
943 error:
944 if (cmdlist != NULL)
945 cmd_list_free(cmdlist);
947 c->flags |= CLIENT_EXIT;
950 /* Handle identify message. */
951 void
952 server_client_msg_identify(struct client *c, struct imsg *imsg)
954 const char *data;
955 size_t datalen;
956 int flags;
958 if (c->flags & CLIENT_IDENTIFIED)
959 fatalx("out-of-order identify message");
961 data = imsg->data;
962 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
964 switch (imsg->hdr.type) {
965 case MSG_IDENTIFY_FLAGS:
966 if (datalen != sizeof flags)
967 fatalx("bad MSG_IDENTIFY_FLAGS size");
968 memcpy(&flags, data, sizeof flags);
969 c->flags |= flags;
970 break;
971 case MSG_IDENTIFY_TERM:
972 if (datalen == 0 || data[datalen - 1] != '\0')
973 fatalx("bad MSG_IDENTIFY_TERM string");
974 c->term = xstrdup(data);
975 break;
976 case MSG_IDENTIFY_TTYNAME:
977 if (datalen == 0 || data[datalen - 1] != '\0')
978 fatalx("bad MSG_IDENTIFY_TTYNAME string");
979 c->ttyname = xstrdup(data);
980 break;
981 case MSG_IDENTIFY_CWD:
982 if (datalen != 0)
983 fatalx("bad MSG_IDENTIFY_CWD size");
984 c->cwd = imsg->fd;
985 break;
986 case MSG_IDENTIFY_STDIN:
987 if (datalen != 0)
988 fatalx("bad MSG_IDENTIFY_STDIN size");
989 c->fd = imsg->fd;
990 break;
991 case MSG_IDENTIFY_ENVIRON:
992 if (datalen == 0 || data[datalen - 1] != '\0')
993 fatalx("bad MSG_IDENTIFY_ENVIRON string");
994 if (strchr(data, '=') != NULL)
995 environ_put(&c->environ, data);
996 break;
997 default:
998 break;
1001 if (imsg->hdr.type != MSG_IDENTIFY_DONE)
1002 return;
1003 c->flags |= CLIENT_IDENTIFIED;
1005 #ifdef __CYGWIN__
1006 c->fd = open(c->ttyname, O_RDWR|O_NOCTTY);
1007 c->cwd = open(".", O_RDONLY);
1008 #endif
1010 if (c->flags & CLIENT_CONTROL) {
1011 c->stdin_callback = control_callback;
1013 evbuffer_free(c->stderr_data);
1014 c->stderr_data = c->stdout_data;
1016 if (c->flags & CLIENT_CONTROLCONTROL)
1017 evbuffer_add_printf(c->stdout_data, "\033P1000p");
1018 server_write_client(c, MSG_STDIN, NULL, 0);
1020 c->tty.fd = -1;
1021 c->tty.log_fd = -1;
1023 close(c->fd);
1024 c->fd = -1;
1026 return;
1029 if (c->fd == -1)
1030 return;
1031 if (!isatty(c->fd)) {
1032 close(c->fd);
1033 c->fd = -1;
1034 return;
1036 tty_init(&c->tty, c, c->fd, c->term);
1037 if (c->flags & CLIENT_UTF8)
1038 c->tty.flags |= TTY_UTF8;
1039 if (c->flags & CLIENT_256COLOURS)
1040 c->tty.term_flags |= TERM_256COLOURS;
1042 tty_resize(&c->tty);
1044 if (!(c->flags & CLIENT_CONTROL))
1045 c->flags |= CLIENT_TERMINAL;
1048 /* Handle shell message. */
1049 void
1050 server_client_msg_shell(struct client *c)
1052 const char *shell;
1054 shell = options_get_string(&global_s_options, "default-shell");
1055 if (*shell == '\0' || areshell(shell))
1056 shell = _PATH_BSHELL;
1057 server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1);
1059 c->flags |= CLIENT_BAD; /* it will die after exec */