Return the command client return code with MSG_EXIT now that MSG_ERROR and
[tmux-openbsd.git] / server-client.c
blob6dc0fd3a1bb46afcbbe0fc40c996afced85fe81a
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>
21 #include <event.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <time.h>
25 #include <paths.h>
26 #include <unistd.h>
28 #include "tmux.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. */
47 void
48 server_client_create(int fd)
50 struct client *c;
51 int mode;
52 u_int i;
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);
62 c->references = 0;
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);
72 c->stdin_file = NULL;
73 c->stdout_file = NULL;
74 c->stderr_file = NULL;
76 c->tty.fd = -1;
77 c->title = NULL;
79 c->session = NULL;
80 c->tty.sx = 80;
81 c->tty.sy = 24;
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;
91 c->prompt_index = 0;
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);
98 return;
101 ARRAY_ADD(&clients, c);
102 log_debug("new client %d", fd);
105 /* Lost a client. */
106 void
107 server_client_lost(struct client *c)
109 struct message_entry *msg;
110 u_int i;
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)
123 tty_free(&c->tty);
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)
136 xfree(c->title);
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);
147 xfree(msg->msg);
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);
159 if (c->cwd != NULL)
160 xfree(c->cwd);
162 close(c->ibuf.fd);
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);
169 break;
172 if (i == ARRAY_LENGTH(&dead_clients))
173 ARRAY_ADD(&dead_clients, c);
174 c->flags |= CLIENT_DEAD;
176 recalculate_sizes();
177 server_update_socket();
180 /* Process a single client event. */
181 void
182 server_client_callback(int fd, short events, void *data)
184 struct client *c = data;
186 if (c->flags & CLIENT_DEAD)
187 return;
189 if (fd == c->ibuf.fd) {
190 if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0)
191 goto client_lost;
193 if (c->flags & CLIENT_BAD) {
194 if (c->ibuf.w.queued == 0)
195 goto client_lost;
196 return;
199 if (events & EV_READ && server_client_msg_dispatch(c) != 0)
200 goto client_lost;
203 server_update_event(c);
204 return;
206 client_lost:
207 server_client_lost(c);
210 /* Handle client status timer. */
211 void
212 server_client_status_timer(void)
214 struct client *c;
215 struct session *s;
216 struct job *job;
217 struct timeval tv;
218 u_int i;
219 int interval;
220 time_t difference;
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)
228 continue;
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
233 * redrawn anyway.
235 continue;
237 s = c->session;
239 if (!options_get_number(&s->options, "status"))
240 continue;
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)
246 job_run(job);
247 c->flags |= CLIENT_STATUS;
252 /* Handle data key input from client. */
253 void
254 server_client_handle_key(int key, struct mouse_event *mouse, void *data)
256 struct client *c = data;
257 struct session *s;
258 struct window *w;
259 struct window_pane *wp;
260 struct options *oo;
261 struct timeval tv;
262 struct key_binding *bd;
263 struct keylist *keylist;
264 int xtimeout, isprefix;
265 u_int i;
267 /* Check the client is good to accept input. */
268 if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
269 return;
270 if (c->session == NULL)
271 return;
272 s = c->session;
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;
280 wp = w->active;
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)
286 return;
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);
291 return;
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);
302 return;
305 /* Check for mouse keys. */
306 if (key == KEYC_MOUSE) {
307 if (c->flags & CLIENT_READONLY)
308 return;
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);
312 wp = w->active;
314 window_pane_mouse(wp, c->session, mouse);
315 return;
318 /* Is this a prefix key? */
319 keylist = options_get_data(&c->session->options, "prefix");
320 isprefix = 0;
321 for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
322 if (key == ARRAY_ITEM(keylist, i)) {
323 isprefix = 1;
324 break;
328 /* No previous prefix key. */
329 if (!(c->flags & CLIENT_PREFIX)) {
330 if (isprefix)
331 c->flags |= CLIENT_PREFIX;
332 else {
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);
337 } else
338 key_bindings_dispatch(bd, c);
340 return;
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;
349 if (isprefix)
350 c->flags |= CLIENT_PREFIX;
351 else if (!(c->flags & CLIENT_READONLY))
352 window_pane_key(wp, c->session, key);
354 return;
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;
360 if (isprefix)
361 c->flags |= CLIENT_PREFIX;
362 else if (!(c->flags & CLIENT_READONLY))
363 window_pane_key(wp, c->session, key);
364 return;
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. */
383 void
384 server_client_loop(void)
386 struct client *c;
387 struct window *w;
388 struct window_pane *wp;
389 u_int i;
391 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
392 c = ARRAY_ITEM(&clients, i);
393 if (c == NULL || c->session == NULL)
394 continue;
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
402 * their flags now.
404 for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
405 w = ARRAY_ITEM(&windows, i);
406 if (w == NULL)
407 continue;
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.
424 void
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;
431 int status, mode;
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);
438 else
439 tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy);
441 mode = s->mode;
442 if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
443 options_get_number(oo, "mouse-select-pane"))
444 mode |= MODE_MOUSE;
445 tty_update_mode(&c->tty, mode);
446 tty_reset(&c->tty);
449 /* Repeat time callback. */
450 /* ARGSUSED */
451 void
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. */
461 void
462 server_client_check_redraw(struct client *c)
464 struct session *s = c->session;
465 struct window_pane *wp;
466 int flags, redraw;
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);
479 else
480 redraw = status_redraw(c);
481 if (!redraw)
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);
488 } else {
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. */
507 void
508 server_client_set_title(struct client *c)
510 struct session *s = c->session;
511 const char *template;
512 char *title;
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)
519 xfree(c->title);
520 c->title = xstrdup(title);
521 tty_set_title(&c->tty, c->title);
523 xfree(title);
526 /* Dispatch message from client. */
528 server_client_msg_dispatch(struct client *c)
530 struct imsg imsg;
531 struct msg_command_data commanddata;
532 struct msg_identify_data identifydata;
533 struct msg_environ_data environdata;
534 ssize_t n, datalen;
536 if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
537 return (-1);
539 for (;;) {
540 if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
541 return (-1);
542 if (n == 0)
543 return (0);
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;
549 imsg_free(&imsg);
550 continue;
553 log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
554 switch (imsg.hdr.type) {
555 case MSG_COMMAND:
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);
561 break;
562 case MSG_IDENTIFY:
563 if (datalen != sizeof identifydata)
564 fatalx("bad MSG_IDENTIFY size");
565 if (imsg.fd == -1)
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);
573 break;
574 case MSG_STDOUT:
575 if (datalen != 0)
576 fatalx("bad MSG_STDOUT size");
577 if (imsg.fd == -1)
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");
583 break;
584 case MSG_STDERR:
585 if (datalen != 0)
586 fatalx("bad MSG_STDERR size");
587 if (imsg.fd == -1)
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");
593 break;
594 case MSG_RESIZE:
595 if (datalen != 0)
596 fatalx("bad MSG_RESIZE size");
598 if (tty_resize(&c->tty)) {
599 recalculate_sizes();
600 server_redraw_client(c);
602 break;
603 case MSG_EXITING:
604 if (datalen != 0)
605 fatalx("bad MSG_EXITING size");
607 c->session = NULL;
608 tty_close(&c->tty);
609 server_write_client(c, MSG_EXITED, NULL, 0);
610 break;
611 case MSG_WAKEUP:
612 case MSG_UNLOCK:
613 if (datalen != 0)
614 fatalx("bad MSG_WAKEUP size");
616 if (!(c->flags & CLIENT_SUSPENDED))
617 break;
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,
624 &c->activity_time,
625 sizeof c->session->activity_time);
628 tty_start_tty(&c->tty);
629 server_redraw_client(c);
630 recalculate_sizes();
631 break;
632 case MSG_ENVIRON:
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);
640 break;
641 case MSG_SHELL:
642 if (datalen != 0)
643 fatalx("bad MSG_SHELL size");
645 server_client_msg_shell(c);
646 break;
647 default:
648 fatalx("unexpected message");
651 imsg_free(&imsg);
655 /* Callback to send error message to client. */
656 void printflike2
657 server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...)
659 va_list ap;
661 va_start(ap, fmt);
662 vfprintf(ctx->cmdclient->stderr_file, fmt, ap);
663 va_end(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. */
672 void printflike2
673 server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...)
675 va_list ap;
677 va_start(ap, fmt);
678 vfprintf(ctx->cmdclient->stdout_file, fmt, ap);
679 va_end(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. */
686 void printflike2
687 server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...)
689 va_list ap;
691 if (options_get_number(&global_options, "quiet"))
692 return;
694 va_start(ap, fmt);
695 vfprintf(ctx->cmdclient->stdout_file, fmt, ap);
696 va_end(ap);
698 fputc('\n', ctx->cmdclient->stderr_file);
699 fflush(ctx->cmdclient->stdout_file);
702 /* Handle command message. */
703 void
704 server_client_msg_command(struct client *c, struct msg_command_data *data)
706 struct cmd_ctx ctx;
707 struct cmd_list *cmdlist = NULL;
708 struct msg_exit_data exitdata;
709 int argc;
710 char **argv, *cause;
712 ctx.error = server_client_msg_error;
713 ctx.print = server_client_msg_print;
714 ctx.info = server_client_msg_info;
716 ctx.msgdata = data;
717 ctx.curclient = NULL;
719 ctx.cmdclient = c;
721 argc = data->argc;
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");
725 goto error;
728 if (argc == 0) {
729 argc = 1;
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);
737 goto error;
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);
746 return;
748 error:
749 if (cmdlist != NULL)
750 cmd_list_free(cmdlist);
751 exitdata.retcode = c->retcode;
752 server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
755 /* Handle identify message. */
756 void
757 server_client_msg_identify(
758 struct client *c, struct msg_identify_data *data, int fd)
760 int tty_fd;
762 c->cwd = NULL;
763 data->cwd[(sizeof data->cwd) - 1] = '\0';
764 if (*data->cwd != '\0')
765 c->cwd = xstrdup(data->cwd);
767 if (!isatty(fd))
768 return;
769 if ((tty_fd = dup(fd)) == -1)
770 fatal("dup failed");
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;
780 c->tty.key_data = c;
782 tty_resize(&c->tty);
784 c->flags |= CLIENT_TERMINAL;
787 /* Handle shell message. */
788 void
789 server_client_msg_shell(struct client *c)
791 struct msg_shell_data data;
792 const char *shell;
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 */