Annotation of src/usr.bin/tmux/server-client.c, Revision 1.191
1.191 ! nicm 1: /* $OpenBSD: server-client.c,v 1.190 2016/10/10 21:29:23 nicm Exp $ */
1.1 nicm 2:
3: /*
1.181 nicm 4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 5: *
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.
9: *
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.
17: */
18:
19: #include <sys/types.h>
1.92 nicm 20: #include <sys/ioctl.h>
1.162 nicm 21: #include <sys/uio.h>
1.1 nicm 22:
1.114 benno 23: #include <errno.h>
1.12 nicm 24: #include <event.h>
1.1 nicm 25: #include <fcntl.h>
1.162 nicm 26: #include <imsg.h>
1.98 nicm 27: #include <paths.h>
28: #include <stdlib.h>
1.1 nicm 29: #include <string.h>
1.4 nicm 30: #include <time.h>
1.1 nicm 31: #include <unistd.h>
32:
33: #include "tmux.h"
34:
1.190 nicm 35: static void server_client_free(int, short, void *);
36: static void server_client_check_focus(struct window_pane *);
37: static void server_client_check_resize(struct window_pane *);
38: static key_code server_client_check_mouse(struct client *);
39: static void server_client_repeat_timer(int, short, void *);
40: static void server_client_check_exit(struct client *);
41: static void server_client_check_redraw(struct client *);
42: static void server_client_set_title(struct client *);
43: static void server_client_reset_state(struct client *);
44: static int server_client_assume_paste(struct session *);
45:
46: static void server_client_dispatch(struct imsg *, void *);
47: static void server_client_dispatch_command(struct client *, struct imsg *);
48: static void server_client_dispatch_identify(struct client *, struct imsg *);
49: static void server_client_dispatch_shell(struct client *);
1.140 nicm 50:
51: /* Check if this client is inside this server. */
52: int
53: server_client_check_nested(struct client *c)
54: {
55: struct environ_entry *envent;
56: struct window_pane *wp;
57:
58: if (c->tty.path == NULL)
59: return (0);
60:
1.164 nicm 61: envent = environ_find(c->environ, "TMUX");
1.140 nicm 62: if (envent == NULL || *envent->value == '\0')
63: return (0);
64:
65: RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
66: if (strcmp(wp->tty, c->tty.path) == 0)
67: return (1);
68: }
69: return (0);
70: }
1.1 nicm 71:
1.132 nicm 72: /* Set client key table. */
73: void
1.178 nicm 74: server_client_set_key_table(struct client *c, const char *name)
1.132 nicm 75: {
1.178 nicm 76: if (name == NULL)
77: name = server_client_get_key_table(c);
78:
1.132 nicm 79: key_bindings_unref_table(c->keytable);
80: c->keytable = key_bindings_get_table(name, 1);
81: c->keytable->references++;
82: }
83:
1.178 nicm 84: /* Get default key table. */
85: const char *
86: server_client_get_key_table(struct client *c)
87: {
88: struct session *s = c->session;
89: const char *name;
90:
91: if (s == NULL)
92: return ("root");
93:
94: name = options_get_string(s->options, "key-table");
95: if (*name == '\0')
96: return ("root");
97: return (name);
98: }
99:
1.191 ! nicm 100: /* Is this client using the default key table? */
! 101: int
! 102: server_client_is_default_key_table(struct client *c)
! 103: {
! 104: return (strcmp(c->keytable->name, server_client_get_key_table(c)) == 0);
! 105: }
! 106:
1.1 nicm 107: /* Create a new client. */
108: void
109: server_client_create(int fd)
110: {
111: struct client *c;
112:
1.49 nicm 113: setblocking(fd, 0);
1.1 nicm 114:
115: c = xcalloc(1, sizeof *c);
1.141 nicm 116: c->references = 1;
1.162 nicm 117: c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c);
1.25 nicm 118:
1.10 nicm 119: if (gettimeofday(&c->creation_time, NULL) != 0)
1.1 nicm 120: fatal("gettimeofday failed");
1.11 nicm 121: memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
1.111 nicm 122:
1.164 nicm 123: c->environ = environ_create();
1.1 nicm 124:
1.146 nicm 125: c->fd = -1;
1.165 nicm 126: c->cwd = NULL;
1.145 nicm 127:
1.94 nicm 128: c->cmdq = cmdq_new(c);
129: c->cmdq->client_exit = 1;
130:
1.116 nicm 131: c->stdin_data = evbuffer_new();
132: c->stdout_data = evbuffer_new();
133: c->stderr_data = evbuffer_new();
1.33 nicm 134:
1.1 nicm 135: c->tty.fd = -1;
136: c->title = NULL;
137:
138: c->session = NULL;
1.45 nicm 139: c->last_session = NULL;
1.1 nicm 140: c->tty.sx = 80;
141: c->tty.sy = 24;
142:
143: screen_init(&c->status, c->tty.sx, 1, 0);
144:
145: c->message_string = NULL;
1.136 nicm 146: TAILQ_INIT(&c->message_log);
1.1 nicm 147:
148: c->prompt_string = NULL;
149: c->prompt_buffer = NULL;
150: c->prompt_index = 0;
151:
1.93 nicm 152: c->flags |= CLIENT_FOCUSED;
153:
1.132 nicm 154: c->keytable = key_bindings_get_table("root", 1);
155: c->keytable->references++;
156:
1.17 nicm 157: evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
158:
1.135 nicm 159: TAILQ_INSERT_TAIL(&clients, c, entry);
1.157 nicm 160: log_debug("new client %p", c);
1.72 nicm 161: }
162:
163: /* Open client terminal if needed. */
164: int
1.119 nicm 165: server_client_open(struct client *c, char **cause)
1.72 nicm 166: {
1.75 nicm 167: if (c->flags & CLIENT_CONTROL)
168: return (0);
1.120 nicm 169:
170: if (strcmp(c->ttyname, "/dev/tty") == 0) {
171: *cause = xstrdup("can't use /dev/tty");
172: return (-1);
173: }
1.75 nicm 174:
1.72 nicm 175: if (!(c->flags & CLIENT_TERMINAL)) {
1.116 nicm 176: *cause = xstrdup("not a terminal");
1.72 nicm 177: return (-1);
178: }
179:
1.119 nicm 180: if (tty_open(&c->tty, cause) != 0)
1.72 nicm 181: return (-1);
182:
183: return (0);
1.1 nicm 184: }
185:
186: /* Lost a client. */
187: void
188: server_client_lost(struct client *c)
189: {
1.136 nicm 190: struct message_entry *msg, *msg1;
1.1 nicm 191:
1.141 nicm 192: c->flags |= CLIENT_DEAD;
193:
1.187 nicm 194: server_clear_identify(c, NULL);
1.141 nicm 195: status_prompt_clear(c);
196: status_message_clear(c);
197:
198: if (c->stdin_callback != NULL)
199: c->stdin_callback(c, 1, c->stdin_callback_data);
200:
1.135 nicm 201: TAILQ_REMOVE(&clients, c, entry);
1.157 nicm 202: log_debug("lost client %p", c);
1.1 nicm 203:
204: /*
205: * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
206: * and tty_free might close an unrelated fd.
207: */
208: if (c->flags & CLIENT_TERMINAL)
209: tty_free(&c->tty);
1.109 nicm 210: free(c->ttyname);
211: free(c->term);
1.1 nicm 212:
1.113 nicm 213: evbuffer_free(c->stdin_data);
214: evbuffer_free(c->stdout_data);
1.97 nicm 215: if (c->stderr_data != c->stdout_data)
1.126 nicm 216: evbuffer_free(c->stderr_data);
1.33 nicm 217:
1.148 nicm 218: if (event_initialized(&c->status_timer))
219: evtimer_del(&c->status_timer);
1.1 nicm 220: screen_free(&c->status);
221:
1.77 nicm 222: free(c->title);
1.165 nicm 223: free((void *)c->cwd);
1.1 nicm 224:
1.17 nicm 225: evtimer_del(&c->repeat_timer);
226:
1.132 nicm 227: key_bindings_unref_table(c->keytable);
228:
1.69 nicm 229: if (event_initialized(&c->identify_timer))
230: evtimer_del(&c->identify_timer);
1.15 nicm 231:
1.77 nicm 232: free(c->message_string);
1.116 nicm 233: if (event_initialized(&c->message_timer))
1.69 nicm 234: evtimer_del(&c->message_timer);
1.136 nicm 235: TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) {
1.77 nicm 236: free(msg->msg);
1.136 nicm 237: TAILQ_REMOVE(&c->message_log, msg, entry);
238: free(msg);
1.21 nicm 239: }
1.1 nicm 240:
1.77 nicm 241: free(c->prompt_string);
242: free(c->prompt_buffer);
1.61 nicm 243:
1.154 nicm 244: c->cmdq->flags |= CMD_Q_DEAD;
1.94 nicm 245: cmdq_free(c->cmdq);
246: c->cmdq = NULL;
247:
1.164 nicm 248: environ_free(c->environ);
1.1 nicm 249:
1.162 nicm 250: proc_remove_peer(c->peer);
251: c->peer = NULL;
1.13 nicm 252:
1.142 nicm 253: server_client_unref(c);
1.71 nicm 254:
255: server_add_accept(0); /* may be more file descriptors now */
1.1 nicm 256:
257: recalculate_sizes();
1.41 nicm 258: server_check_unattached();
1.19 nicm 259: server_update_socket();
1.141 nicm 260: }
261:
262: /* Remove reference from a client. */
263: void
1.142 nicm 264: server_client_unref(struct client *c)
1.141 nicm 265: {
1.157 nicm 266: log_debug("unref client %p (%d references)", c, c->references);
1.141 nicm 267:
268: c->references--;
269: if (c->references == 0)
270: event_once(-1, EV_TIMEOUT, server_client_free, c, NULL);
271: }
272:
273: /* Free dead client. */
1.190 nicm 274: static void
1.170 nicm 275: server_client_free(__unused int fd, __unused short events, void *arg)
1.141 nicm 276: {
277: struct client *c = arg;
278:
1.157 nicm 279: log_debug("free client %p (%d references)", c, c->references);
1.141 nicm 280:
281: if (c->references == 0)
282: free(c);
1.9 nicm 283: }
284:
1.174 nicm 285: /* Detach a client. */
286: void
287: server_client_detach(struct client *c, enum msgtype msgtype)
288: {
289: struct session *s = c->session;
290:
291: if (s == NULL)
292: return;
293:
1.180 nicm 294: hooks_run(c->session->hooks, c, NULL, "client-detached");
1.174 nicm 295: proc_send_s(c->peer, msgtype, s->name);
296: }
297:
1.66 nicm 298: /* Check for mouse keys. */
1.190 nicm 299: static key_code
1.131 nicm 300: server_client_check_mouse(struct client *c)
1.66 nicm 301: {
1.131 nicm 302: struct session *s = c->session;
303: struct mouse_event *m = &c->tty.mouse;
304: struct window *w;
305: struct window_pane *wp;
306: enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE;
307: enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE;
308: u_int x, y, b;
1.168 nicm 309: key_code key;
1.131 nicm 310:
311: log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y,
312: m->lx, m->ly, c->tty.mouse_drag_flag);
313:
314: /* What type of event is this? */
315: if (MOUSE_DRAG(m->b)) {
316: type = DRAG;
317: if (c->tty.mouse_drag_flag) {
318: x = m->x, y = m->y, b = m->b;
319: log_debug("drag update at %u,%u", x, y);
320: } else {
321: x = m->lx, y = m->ly, b = m->lb;
322: log_debug("drag start at %u,%u", x, y);
323: }
324: } else if (MOUSE_WHEEL(m->b)) {
325: type = WHEEL;
326: x = m->x, y = m->y, b = m->b;
327: log_debug("wheel at %u,%u", x, y);
328: } else if (MOUSE_BUTTONS(m->b) == 3) {
329: type = UP;
330: x = m->x, y = m->y, b = m->lb;
331: log_debug("up at %u,%u", x, y);
332: } else {
333: type = DOWN;
334: x = m->x, y = m->y, b = m->b;
335: log_debug("down at %u,%u", x, y);
336: }
337: if (type == NOTYPE)
1.177 nicm 338: return (KEYC_UNKNOWN);
1.131 nicm 339:
340: /* Always save the session. */
341: m->s = s->id;
342:
343: /* Is this on the status line? */
344: m->statusat = status_at_line(c);
345: if (m->statusat != -1 && y == (u_int)m->statusat) {
346: w = status_get_window_at(c, x);
347: if (w == NULL)
1.177 nicm 348: return (KEYC_UNKNOWN);
1.131 nicm 349: m->w = w->id;
350: where = STATUS;
351: } else
352: m->w = -1;
353:
354: /* Not on status line. Adjust position and check for border or pane. */
355: if (where == NOWHERE) {
356: if (m->statusat == 0 && y > 0)
357: y--;
358: else if (m->statusat > 0 && y >= (u_int)m->statusat)
359: y = m->statusat - 1;
360:
361: TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
362: if ((wp->xoff + wp->sx == x &&
363: wp->yoff <= 1 + y &&
364: wp->yoff + wp->sy >= y) ||
365: (wp->yoff + wp->sy == y &&
366: wp->xoff <= 1 + x &&
367: wp->xoff + wp->sx >= x))
368: break;
369: }
370: if (wp != NULL)
371: where = BORDER;
372: else {
373: wp = window_get_active_at(s->curw->window, x, y);
1.173 nicm 374: if (wp != NULL) {
1.131 nicm 375: where = PANE;
1.173 nicm 376: log_debug("mouse at %u,%u is on pane %%%u",
377: x, y, wp->id);
378: }
1.131 nicm 379: }
380: if (where == NOWHERE)
1.177 nicm 381: return (KEYC_UNKNOWN);
1.131 nicm 382: m->wp = wp->id;
383: m->w = wp->window->id;
384: } else
385: m->wp = -1;
386:
387: /* Stop dragging if needed. */
388: if (type != DRAG && c->tty.mouse_drag_flag) {
389: if (c->tty.mouse_drag_release != NULL)
390: c->tty.mouse_drag_release(c, m);
391:
392: c->tty.mouse_drag_update = NULL;
393: c->tty.mouse_drag_release = NULL;
394:
1.182 nicm 395: /*
1.183 nicm 396: * End a mouse drag by passing a MouseDragEnd key corresponding
397: * to the button that started the drag.
1.182 nicm 398: */
399: switch (c->tty.mouse_drag_flag) {
400: case 1:
401: if (where == PANE)
1.183 nicm 402: key = KEYC_MOUSEDRAGEND1_PANE;
1.182 nicm 403: if (where == STATUS)
1.183 nicm 404: key = KEYC_MOUSEDRAGEND1_STATUS;
1.182 nicm 405: if (where == BORDER)
1.183 nicm 406: key = KEYC_MOUSEDRAGEND1_BORDER;
1.182 nicm 407: break;
408: case 2:
409: if (where == PANE)
1.183 nicm 410: key = KEYC_MOUSEDRAGEND2_PANE;
1.182 nicm 411: if (where == STATUS)
1.183 nicm 412: key = KEYC_MOUSEDRAGEND2_STATUS;
1.182 nicm 413: if (where == BORDER)
1.183 nicm 414: key = KEYC_MOUSEDRAGEND2_BORDER;
1.182 nicm 415: break;
416: case 3:
417: if (where == PANE)
1.183 nicm 418: key = KEYC_MOUSEDRAGEND3_PANE;
1.182 nicm 419: if (where == STATUS)
1.183 nicm 420: key = KEYC_MOUSEDRAGEND3_STATUS;
1.182 nicm 421: if (where == BORDER)
1.183 nicm 422: key = KEYC_MOUSEDRAGEND3_BORDER;
1.182 nicm 423: break;
424: default:
425: key = KEYC_MOUSE;
426: break;
427: }
1.131 nicm 428: c->tty.mouse_drag_flag = 0;
1.182 nicm 429:
430: return (key);
1.131 nicm 431: }
432:
433: /* Convert to a key binding. */
1.177 nicm 434: key = KEYC_UNKNOWN;
1.131 nicm 435: switch (type) {
436: case NOTYPE:
437: break;
438: case DRAG:
439: if (c->tty.mouse_drag_update != NULL)
440: c->tty.mouse_drag_update(c, m);
441: else {
442: switch (MOUSE_BUTTONS(b)) {
443: case 0:
444: if (where == PANE)
445: key = KEYC_MOUSEDRAG1_PANE;
446: if (where == STATUS)
447: key = KEYC_MOUSEDRAG1_STATUS;
448: if (where == BORDER)
449: key = KEYC_MOUSEDRAG1_BORDER;
450: break;
451: case 1:
452: if (where == PANE)
453: key = KEYC_MOUSEDRAG2_PANE;
454: if (where == STATUS)
455: key = KEYC_MOUSEDRAG2_STATUS;
456: if (where == BORDER)
457: key = KEYC_MOUSEDRAG2_BORDER;
458: break;
459: case 2:
460: if (where == PANE)
461: key = KEYC_MOUSEDRAG3_PANE;
462: if (where == STATUS)
463: key = KEYC_MOUSEDRAG3_STATUS;
464: if (where == BORDER)
465: key = KEYC_MOUSEDRAG3_BORDER;
466: break;
467: }
468: }
1.66 nicm 469:
1.182 nicm 470: /*
471: * Begin a drag by setting the flag to a non-zero value that
472: * corresponds to the mouse button in use.
473: */
474: c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1;
1.131 nicm 475: break;
476: case WHEEL:
477: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) {
478: if (where == PANE)
479: key = KEYC_WHEELUP_PANE;
480: if (where == STATUS)
481: key = KEYC_WHEELUP_STATUS;
482: if (where == BORDER)
483: key = KEYC_WHEELUP_BORDER;
484: } else {
485: if (where == PANE)
486: key = KEYC_WHEELDOWN_PANE;
487: if (where == STATUS)
488: key = KEYC_WHEELDOWN_STATUS;
489: if (where == BORDER)
490: key = KEYC_WHEELDOWN_BORDER;
491: }
492: break;
493: case UP:
494: switch (MOUSE_BUTTONS(b)) {
495: case 0:
496: if (where == PANE)
497: key = KEYC_MOUSEUP1_PANE;
498: if (where == STATUS)
499: key = KEYC_MOUSEUP1_STATUS;
500: if (where == BORDER)
501: key = KEYC_MOUSEUP1_BORDER;
502: break;
503: case 1:
504: if (where == PANE)
505: key = KEYC_MOUSEUP2_PANE;
506: if (where == STATUS)
507: key = KEYC_MOUSEUP2_STATUS;
508: if (where == BORDER)
509: key = KEYC_MOUSEUP2_BORDER;
510: break;
511: case 2:
512: if (where == PANE)
513: key = KEYC_MOUSEUP3_PANE;
514: if (where == STATUS)
515: key = KEYC_MOUSEUP3_STATUS;
516: if (where == BORDER)
517: key = KEYC_MOUSEUP3_BORDER;
518: break;
519: }
520: break;
521: case DOWN:
522: switch (MOUSE_BUTTONS(b)) {
523: case 0:
524: if (where == PANE)
525: key = KEYC_MOUSEDOWN1_PANE;
526: if (where == STATUS)
527: key = KEYC_MOUSEDOWN1_STATUS;
528: if (where == BORDER)
529: key = KEYC_MOUSEDOWN1_BORDER;
530: break;
531: case 1:
532: if (where == PANE)
533: key = KEYC_MOUSEDOWN2_PANE;
534: if (where == STATUS)
535: key = KEYC_MOUSEDOWN2_STATUS;
536: if (where == BORDER)
537: key = KEYC_MOUSEDOWN2_BORDER;
538: break;
539: case 2:
540: if (where == PANE)
541: key = KEYC_MOUSEDOWN3_PANE;
542: if (where == STATUS)
543: key = KEYC_MOUSEDOWN3_STATUS;
544: if (where == BORDER)
545: key = KEYC_MOUSEDOWN3_BORDER;
546: break;
1.66 nicm 547: }
1.131 nicm 548: break;
1.66 nicm 549: }
1.177 nicm 550: if (key == KEYC_UNKNOWN)
551: return (KEYC_UNKNOWN);
1.66 nicm 552:
1.131 nicm 553: /* Apply modifiers if any. */
554: if (b & MOUSE_MASK_META)
555: key |= KEYC_ESCAPE;
556: if (b & MOUSE_MASK_CTRL)
557: key |= KEYC_CTRL;
558: if (b & MOUSE_MASK_SHIFT)
559: key |= KEYC_SHIFT;
1.66 nicm 560:
1.131 nicm 561: return (key);
1.66 nicm 562: }
563:
1.82 nicm 564: /* Is this fast enough to probably be a paste? */
1.190 nicm 565: static int
1.82 nicm 566: server_client_assume_paste(struct session *s)
567: {
568: struct timeval tv;
1.84 nicm 569: int t;
1.82 nicm 570:
1.163 nicm 571: if ((t = options_get_number(s->options, "assume-paste-time")) == 0)
1.83 nicm 572: return (0);
1.82 nicm 573:
574: timersub(&s->activity_time, &s->last_activity_time, &tv);
1.171 nicm 575: if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) {
576: log_debug("session %s pasting (flag %d)", s->name,
577: !!(s->flags & SESSION_PASTING));
578: if (s->flags & SESSION_PASTING)
579: return (1);
580: s->flags |= SESSION_PASTING;
581: return (0);
582: }
583: log_debug("session %s not pasting", s->name);
584: s->flags &= ~SESSION_PASTING;
1.83 nicm 585: return (0);
1.82 nicm 586: }
587:
1.18 nicm 588: /* Handle data key input from client. */
589: void
1.168 nicm 590: server_client_handle_key(struct client *c, key_code key)
1.18 nicm 591: {
1.131 nicm 592: struct mouse_event *m = &c->tty.mouse;
1.132 nicm 593: struct session *s = c->session;
1.18 nicm 594: struct window *w;
595: struct window_pane *wp;
596: struct timeval tv;
1.191 ! nicm 597: const char *name;
1.156 nicm 598: struct key_table *table;
1.132 nicm 599: struct key_binding bd_find, *bd;
600: int xtimeout;
1.18 nicm 601:
602: /* Check the client is good to accept input. */
1.132 nicm 603: if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
1.18 nicm 604: return;
1.132 nicm 605: w = s->curw->window;
1.191 ! nicm 606: if (KEYC_IS_MOUSE(key))
! 607: wp = cmd_mouse_pane(m, NULL, NULL);
! 608: else
! 609: wp = w->active;
1.18 nicm 610:
611: /* Update the activity timer. */
612: if (gettimeofday(&c->activity_time, NULL) != 0)
613: fatal("gettimeofday failed");
1.149 nicm 614: session_update_activity(s, &c->activity_time);
1.18 nicm 615:
1.132 nicm 616: /* Number keys jump to pane in identify mode. */
1.25 nicm 617: if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
1.30 nicm 618: if (c->flags & CLIENT_READONLY)
619: return;
1.95 nicm 620: window_unzoom(w);
1.18 nicm 621: wp = window_pane_at_index(w, key - '0');
1.187 nicm 622: if (wp != NULL && !window_pane_visible(wp))
623: wp = NULL;
624: server_clear_identify(c, wp);
1.18 nicm 625: return;
626: }
627:
628: /* Handle status line. */
1.30 nicm 629: if (!(c->flags & CLIENT_READONLY)) {
630: status_message_clear(c);
1.187 nicm 631: server_clear_identify(c, NULL);
1.30 nicm 632: }
1.18 nicm 633: if (c->prompt_string != NULL) {
1.30 nicm 634: if (!(c->flags & CLIENT_READONLY))
635: status_prompt_key(c, key);
1.18 nicm 636: return;
637: }
638:
639: /* Check for mouse keys. */
640: if (key == KEYC_MOUSE) {
1.30 nicm 641: if (c->flags & CLIENT_READONLY)
642: return;
1.131 nicm 643: key = server_client_check_mouse(c);
1.177 nicm 644: if (key == KEYC_UNKNOWN)
1.131 nicm 645: return;
646:
647: m->valid = 1;
648: m->key = key;
649:
1.163 nicm 650: if (!options_get_number(s->options, "mouse"))
1.161 nicm 651: goto forward;
1.131 nicm 652: } else
653: m->valid = 0;
1.18 nicm 654:
1.132 nicm 655: /* Treat everything as a regular key when pasting is detected. */
1.161 nicm 656: if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s))
657: goto forward;
1.18 nicm 658:
1.132 nicm 659: retry:
1.191 ! nicm 660: /*
! 661: * Work out the current key table. If the pane is in a mode, use
! 662: * the mode table instead of the default key table.
! 663: */
! 664: name = NULL;
! 665: if (wp != NULL && wp->mode != NULL && wp->mode->key_table != NULL)
! 666: name = wp->mode->key_table(wp);
! 667: if (name == NULL || !server_client_is_default_key_table(c))
! 668: table = c->keytable;
! 669: else
! 670: table = key_bindings_get_table(name, 1);
! 671:
1.132 nicm 672: /* Try to see if there is a key binding in the current table. */
673: bd_find.key = key;
1.191 ! nicm 674: bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
1.132 nicm 675: if (bd != NULL) {
676: /*
677: * Key was matched in this table. If currently repeating but a
678: * non-repeating binding was found, stop repeating and try
679: * again in the root table.
680: */
681: if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
1.178 nicm 682: server_client_set_key_table(c, NULL);
1.132 nicm 683: c->flags &= ~CLIENT_REPEAT;
1.85 nicm 684: server_status_client(c);
1.132 nicm 685: goto retry;
1.18 nicm 686: }
1.82 nicm 687:
1.132 nicm 688: /*
689: * Take a reference to this table to make sure the key binding
690: * doesn't disappear.
691: */
692: table->references++;
693:
694: /*
695: * If this is a repeating key, start the timer. Otherwise reset
696: * the client back to the root table.
697: */
1.163 nicm 698: xtimeout = options_get_number(s->options, "repeat-time");
1.132 nicm 699: if (xtimeout != 0 && bd->can_repeat) {
700: c->flags |= CLIENT_REPEAT;
701:
702: tv.tv_sec = xtimeout / 1000;
703: tv.tv_usec = (xtimeout % 1000) * 1000L;
704: evtimer_del(&c->repeat_timer);
705: evtimer_add(&c->repeat_timer, &tv);
706: } else {
1.18 nicm 707: c->flags &= ~CLIENT_REPEAT;
1.178 nicm 708: server_client_set_key_table(c, NULL);
1.18 nicm 709: }
1.132 nicm 710: server_status_client(c);
711:
712: /* Dispatch the key binding. */
713: key_bindings_dispatch(bd, c, m);
714: key_bindings_unref_table(table);
1.18 nicm 715: return;
716: }
717:
1.132 nicm 718: /*
719: * No match in this table. If repeating, switch the client back to the
720: * root table and try again.
721: */
722: if (c->flags & CLIENT_REPEAT) {
1.178 nicm 723: server_client_set_key_table(c, NULL);
1.18 nicm 724: c->flags &= ~CLIENT_REPEAT;
1.132 nicm 725: server_status_client(c);
726: goto retry;
1.18 nicm 727: }
728:
1.132 nicm 729: /* If no match and we're not in the root table, that's it. */
1.191 ! nicm 730: if (name == NULL && !server_client_is_default_key_table(c)) {
1.178 nicm 731: server_client_set_key_table(c, NULL);
1.132 nicm 732: server_status_client(c);
733: return;
1.18 nicm 734: }
735:
1.132 nicm 736: /*
737: * No match, but in the root table. Prefix switches to the prefix table
738: * and everything else is passed through.
739: */
1.168 nicm 740: if (key == (key_code)options_get_number(s->options, "prefix") ||
741: key == (key_code)options_get_number(s->options, "prefix2")) {
1.178 nicm 742: server_client_set_key_table(c, "prefix");
1.132 nicm 743: server_status_client(c);
1.161 nicm 744: return;
745: }
746:
747: forward:
748: if (c->flags & CLIENT_READONLY)
749: return;
750: if (wp != NULL)
1.132 nicm 751: window_pane_key(wp, c, s, key, m);
1.18 nicm 752: }
753:
1.2 nicm 754: /* Client functions that need to happen every loop. */
755: void
756: server_client_loop(void)
757: {
758: struct client *c;
759: struct window *w;
760: struct window_pane *wp;
761:
1.135 nicm 762: TAILQ_FOREACH(c, &clients, entry) {
1.36 nicm 763: server_client_check_exit(c);
764: if (c->session != NULL) {
765: server_client_check_redraw(c);
766: server_client_reset_state(c);
767: }
1.2 nicm 768: }
769:
770: /*
771: * Any windows will have been redrawn as part of clients, so clear
1.92 nicm 772: * their flags now. Also check pane focus and resize.
1.2 nicm 773: */
1.134 nicm 774: RB_FOREACH(w, windows, &windows) {
1.2 nicm 775: w->flags &= ~WINDOW_REDRAW;
1.91 nicm 776: TAILQ_FOREACH(wp, &w->panes, entry) {
1.101 nicm 777: if (wp->fd != -1) {
778: server_client_check_focus(wp);
779: server_client_check_resize(wp);
780: }
1.2 nicm 781: wp->flags &= ~PANE_REDRAW;
1.91 nicm 782: }
1.150 nicm 783: check_window_name(w);
1.2 nicm 784: }
1.92 nicm 785: }
786:
1.188 nicm 787: static void
788: server_client_resize_event(__unused int fd, __unused short events, void *data)
1.92 nicm 789: {
1.188 nicm 790: struct window_pane *wp = data;
791: struct winsize ws;
792:
793: evtimer_del(&wp->resize_timer);
1.92 nicm 794:
1.101 nicm 795: if (!(wp->flags & PANE_RESIZE))
1.92 nicm 796: return;
797:
798: memset(&ws, 0, sizeof ws);
799: ws.ws_col = wp->sx;
800: ws.ws_row = wp->sy;
801:
1.100 nicm 802: if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
1.92 nicm 803: fatal("ioctl failed");
804:
805: wp->flags &= ~PANE_RESIZE;
1.188 nicm 806: }
807:
808: /* Check if pane should be resized. */
1.190 nicm 809: static void
1.188 nicm 810: server_client_check_resize(struct window_pane *wp)
811: {
812: struct timeval tv = { .tv_usec = 250000 };
813:
814: if (!(wp->flags & PANE_RESIZE))
815: return;
816:
817: if (!event_initialized(&wp->resize_timer))
818: evtimer_set(&wp->resize_timer, server_client_resize_event, wp);
819:
820: /*
821: * The first resize should happen immediately, so if the timer is not
822: * running, do it now.
823: */
824: if (!evtimer_pending(&wp->resize_timer, NULL))
825: server_client_resize_event(-1, 0, wp);
826:
827: /*
828: * If the pane is in the alternate screen, let the timer expire and
829: * resize to give the application a chance to redraw. If not, keep
830: * pushing the timer back.
831: */
832: if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL))
833: return;
834: evtimer_del(&wp->resize_timer);
835: evtimer_add(&wp->resize_timer, &tv);
1.91 nicm 836: }
837:
838: /* Check whether pane should be focused. */
1.190 nicm 839: static void
1.91 nicm 840: server_client_check_focus(struct window_pane *wp)
841: {
1.93 nicm 842: struct client *c;
1.102 nicm 843: int push;
1.103 nicm 844:
845: /* Are focus events off? */
1.163 nicm 846: if (!options_get_number(global_options, "focus-events"))
1.103 nicm 847: return;
1.102 nicm 848:
849: /* Do we need to push the focus state? */
850: push = wp->flags & PANE_FOCUSPUSH;
851: wp->flags &= ~PANE_FOCUSPUSH;
1.91 nicm 852:
853: /* If we don't care about focus, forget it. */
854: if (!(wp->base.mode & MODE_FOCUSON))
855: return;
856:
857: /* If we're not the active pane in our window, we're not focused. */
858: if (wp->window->active != wp)
859: goto not_focused;
860:
861: /* If we're in a mode, we're not focused. */
862: if (wp->screen != &wp->base)
863: goto not_focused;
864:
865: /*
1.93 nicm 866: * If our window is the current window in any focused clients with an
867: * attached session, we're focused.
1.91 nicm 868: */
1.135 nicm 869: TAILQ_FOREACH(c, &clients, entry) {
870: if (c->session == NULL || !(c->flags & CLIENT_FOCUSED))
1.91 nicm 871: continue;
1.93 nicm 872: if (c->session->flags & SESSION_UNATTACHED)
873: continue;
874:
875: if (c->session->curw->window == wp->window)
1.91 nicm 876: goto focused;
877: }
878:
879: not_focused:
1.102 nicm 880: if (push || (wp->flags & PANE_FOCUSED))
1.91 nicm 881: bufferevent_write(wp->event, "\033[O", 3);
882: wp->flags &= ~PANE_FOCUSED;
883: return;
884:
885: focused:
1.102 nicm 886: if (push || !(wp->flags & PANE_FOCUSED))
1.91 nicm 887: bufferevent_write(wp->event, "\033[I", 3);
888: wp->flags |= PANE_FOCUSED;
1.2 nicm 889: }
890:
1.18 nicm 891: /*
892: * Update cursor position and mode settings. The scroll region and attributes
893: * are cleared when idle (waiting for an event) as this is the most likely time
894: * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
895: * compromise between excessive resets and likelihood of an interrupt.
896: *
897: * tty_region/tty_reset/tty_update_mode already take care of not resetting
898: * things that are already in their default state.
899: */
1.190 nicm 900: static void
1.18 nicm 901: server_client_reset_state(struct client *c)
1.1 nicm 902: {
1.18 nicm 903: struct window *w = c->session->curw->window;
904: struct window_pane *wp = w->active;
905: struct screen *s = wp->screen;
1.163 nicm 906: struct options *oo = c->session->options;
1.66 nicm 907: int status, mode, o;
1.60 nicm 908:
1.186 nicm 909: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.86 nicm 910: return;
911:
1.1 nicm 912: tty_region(&c->tty, 0, c->tty.sy - 1);
913:
914: status = options_get_number(oo, "status");
915: if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
916: tty_cursor(&c->tty, 0, 0);
1.66 nicm 917: else {
1.126 nicm 918: o = status && options_get_number(oo, "status-position") == 0;
1.66 nicm 919: tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy);
920: }
1.1 nicm 921:
1.50 nicm 922: /*
1.131 nicm 923: * Set mouse mode if requested. To support dragging, always use button
924: * mode.
1.57 nicm 925: */
926: mode = s->mode;
1.131 nicm 927: if (options_get_number(oo, "mouse"))
928: mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON;
1.48 nicm 929:
930: /* Set the terminal mode and reset attributes. */
1.59 nicm 931: tty_update_mode(&c->tty, mode, s);
1.1 nicm 932: tty_reset(&c->tty);
1.17 nicm 933: }
934:
935: /* Repeat time callback. */
1.190 nicm 936: static void
1.170 nicm 937: server_client_repeat_timer(__unused int fd, __unused short events, void *data)
1.17 nicm 938: {
939: struct client *c = data;
940:
1.85 nicm 941: if (c->flags & CLIENT_REPEAT) {
1.178 nicm 942: server_client_set_key_table(c, NULL);
1.132 nicm 943: c->flags &= ~CLIENT_REPEAT;
944: server_status_client(c);
1.85 nicm 945: }
1.1 nicm 946: }
947:
1.36 nicm 948: /* Check if client should be exited. */
1.190 nicm 949: static void
1.36 nicm 950: server_client_check_exit(struct client *c)
951: {
952: if (!(c->flags & CLIENT_EXIT))
953: return;
954:
1.73 nicm 955: if (EVBUFFER_LENGTH(c->stdin_data) != 0)
956: return;
957: if (EVBUFFER_LENGTH(c->stdout_data) != 0)
1.36 nicm 958: return;
1.73 nicm 959: if (EVBUFFER_LENGTH(c->stderr_data) != 0)
1.36 nicm 960: return;
961:
1.162 nicm 962: proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
1.36 nicm 963: c->flags &= ~CLIENT_EXIT;
1.38 nicm 964: }
965:
1.1 nicm 966: /* Check for client redraws. */
1.190 nicm 967: static void
1.1 nicm 968: server_client_check_redraw(struct client *c)
969: {
970: struct session *s = c->session;
1.137 nicm 971: struct tty *tty = &c->tty;
1.1 nicm 972: struct window_pane *wp;
1.189 nicm 973: int flags, masked;
1.1 nicm 974:
1.86 nicm 975: if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
1.79 nicm 976: return;
977:
1.1 nicm 978: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
1.163 nicm 979: if (options_get_number(s->options, "set-titles"))
1.1 nicm 980: server_client_set_title(c);
1.189 nicm 981: screen_redraw_update(c); /* will adjust flags */
1.1 nicm 982: }
983:
1.137 nicm 984: flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR);
985: tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR;
986:
1.1 nicm 987: if (c->flags & CLIENT_REDRAW) {
1.137 nicm 988: tty_update_mode(tty, tty->mode, NULL);
1.115 nicm 989: screen_redraw_screen(c, 1, 1, 1);
1.27 nicm 990: c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
1.38 nicm 991: } else if (c->flags & CLIENT_REDRAWWINDOW) {
1.137 nicm 992: tty_update_mode(tty, tty->mode, NULL);
1.38 nicm 993: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
994: screen_redraw_pane(c, wp);
995: c->flags &= ~CLIENT_REDRAWWINDOW;
1.1 nicm 996: } else {
997: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
1.137 nicm 998: if (wp->flags & PANE_REDRAW) {
999: tty_update_mode(tty, tty->mode, NULL);
1.1 nicm 1000: screen_redraw_pane(c, wp);
1.137 nicm 1001: }
1.1 nicm 1002: }
1003: }
1004:
1.185 nicm 1005: masked = c->flags & (CLIENT_BORDERS|CLIENT_STATUS);
1006: if (masked != 0)
1.137 nicm 1007: tty_update_mode(tty, tty->mode, NULL);
1.185 nicm 1008: if (masked == CLIENT_BORDERS)
1.115 nicm 1009: screen_redraw_screen(c, 0, 0, 1);
1.185 nicm 1010: else if (masked == CLIENT_STATUS)
1.115 nicm 1011: screen_redraw_screen(c, 0, 1, 0);
1.185 nicm 1012: else if (masked != 0)
1013: screen_redraw_screen(c, 0, 1, 1);
1.1 nicm 1014:
1.137 nicm 1015: tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags;
1016: tty_update_mode(tty, tty->mode, NULL);
1.1 nicm 1017:
1.153 nicm 1018: c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS|
1019: CLIENT_STATUSFORCE);
1.1 nicm 1020: }
1021:
1022: /* Set client title. */
1.190 nicm 1023: static void
1.1 nicm 1024: server_client_set_title(struct client *c)
1025: {
1.128 nicm 1026: struct session *s = c->session;
1027: const char *template;
1028: char *title;
1029: struct format_tree *ft;
1.1 nicm 1030:
1.163 nicm 1031: template = options_get_string(s->options, "set-titles-string");
1.25 nicm 1032:
1.176 nicm 1033: ft = format_create(NULL, 0);
1.128 nicm 1034: format_defaults(ft, c, NULL, NULL, NULL);
1035:
1036: title = format_expand_time(ft, template, time(NULL));
1.1 nicm 1037: if (c->title == NULL || strcmp(title, c->title) != 0) {
1.77 nicm 1038: free(c->title);
1.1 nicm 1039: c->title = xstrdup(title);
1040: tty_set_title(&c->tty, c->title);
1041: }
1.77 nicm 1042: free(title);
1.128 nicm 1043:
1044: format_free(ft);
1.1 nicm 1045: }
1046:
1047: /* Dispatch message from client. */
1.190 nicm 1048: static void
1.162 nicm 1049: server_client_dispatch(struct imsg *imsg, void *arg)
1.1 nicm 1050: {
1.162 nicm 1051: struct client *c = arg;
1.73 nicm 1052: struct msg_stdin_data stdindata;
1.108 nicm 1053: const char *data;
1.162 nicm 1054: ssize_t datalen;
1.149 nicm 1055: struct session *s;
1.1 nicm 1056:
1.162 nicm 1057: if (c->flags & CLIENT_DEAD)
1058: return;
1059:
1060: if (imsg == NULL) {
1061: server_client_lost(c);
1062: return;
1063: }
1.1 nicm 1064:
1.162 nicm 1065: data = imsg->data;
1066: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1.1 nicm 1067:
1.162 nicm 1068: switch (imsg->hdr.type) {
1069: case MSG_IDENTIFY_FLAGS:
1070: case MSG_IDENTIFY_TERM:
1071: case MSG_IDENTIFY_TTYNAME:
1072: case MSG_IDENTIFY_CWD:
1073: case MSG_IDENTIFY_STDIN:
1074: case MSG_IDENTIFY_ENVIRON:
1075: case MSG_IDENTIFY_CLIENTPID:
1076: case MSG_IDENTIFY_DONE:
1077: server_client_dispatch_identify(c, imsg);
1078: break;
1079: case MSG_COMMAND:
1080: server_client_dispatch_command(c, imsg);
1081: break;
1082: case MSG_STDIN:
1083: if (datalen != sizeof stdindata)
1084: fatalx("bad MSG_STDIN size");
1085: memcpy(&stdindata, data, sizeof stdindata);
1.36 nicm 1086:
1.162 nicm 1087: if (c->stdin_callback == NULL)
1.33 nicm 1088: break;
1.162 nicm 1089: if (stdindata.size <= 0)
1090: c->stdin_closed = 1;
1091: else {
1092: evbuffer_add(c->stdin_data, stdindata.data,
1093: stdindata.size);
1094: }
1095: c->stdin_callback(c, c->stdin_closed,
1096: c->stdin_callback_data);
1097: break;
1098: case MSG_RESIZE:
1099: if (datalen != 0)
1100: fatalx("bad MSG_RESIZE size");
1.1 nicm 1101:
1.162 nicm 1102: if (c->flags & CLIENT_CONTROL)
1.1 nicm 1103: break;
1.162 nicm 1104: if (tty_resize(&c->tty)) {
1105: recalculate_sizes();
1106: server_redraw_client(c);
1107: }
1.174 nicm 1108: if (c->session != NULL)
1.180 nicm 1109: hooks_run(c->session->hooks, c, NULL, "client-resized");
1.162 nicm 1110: break;
1111: case MSG_EXITING:
1112: if (datalen != 0)
1113: fatalx("bad MSG_EXITING size");
1.1 nicm 1114:
1.162 nicm 1115: c->session = NULL;
1116: tty_close(&c->tty);
1117: proc_send(c->peer, MSG_EXITED, -1, NULL, 0);
1118: break;
1119: case MSG_WAKEUP:
1120: case MSG_UNLOCK:
1121: if (datalen != 0)
1122: fatalx("bad MSG_WAKEUP size");
1.121 nicm 1123:
1.162 nicm 1124: if (!(c->flags & CLIENT_SUSPENDED))
1125: break;
1126: c->flags &= ~CLIENT_SUSPENDED;
1.10 nicm 1127:
1.162 nicm 1128: if (c->tty.fd == -1) /* exited in the meantime */
1.1 nicm 1129: break;
1.162 nicm 1130: s = c->session;
1.1 nicm 1131:
1.162 nicm 1132: if (gettimeofday(&c->activity_time, NULL) != 0)
1133: fatal("gettimeofday failed");
1134:
1135: tty_start_tty(&c->tty);
1136: server_redraw_client(c);
1137: recalculate_sizes();
1.184 nicm 1138:
1139: if (s != NULL)
1140: session_update_activity(s, &c->activity_time);
1.162 nicm 1141: break;
1142: case MSG_SHELL:
1143: if (datalen != 0)
1144: fatalx("bad MSG_SHELL size");
1.1 nicm 1145:
1.162 nicm 1146: server_client_dispatch_shell(c);
1147: break;
1.1 nicm 1148: }
1149: }
1150:
1151: /* Handle command message. */
1.190 nicm 1152: static void
1.162 nicm 1153: server_client_dispatch_command(struct client *c, struct imsg *imsg)
1.1 nicm 1154: {
1.108 nicm 1155: struct msg_command_data data;
1156: char *buf;
1157: size_t len;
1158: struct cmd_list *cmdlist = NULL;
1159: int argc;
1160: char **argv, *cause;
1161:
1162: if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
1163: fatalx("bad MSG_COMMAND size");
1164: memcpy(&data, imsg->data, sizeof data);
1165:
1.124 nicm 1166: buf = (char *)imsg->data + sizeof data;
1.108 nicm 1167: len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data;
1168: if (len > 0 && buf[len - 1] != '\0')
1169: fatalx("bad MSG_COMMAND string");
1170:
1171: argc = data.argc;
1172: if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
1.94 nicm 1173: cmdq_error(c->cmdq, "command too long");
1.1 nicm 1174: goto error;
1175: }
1176:
1177: if (argc == 0) {
1178: argc = 1;
1179: argv = xcalloc(1, sizeof *argv);
1180: *argv = xstrdup("new-session");
1181: }
1182:
1.94 nicm 1183: if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
1184: cmdq_error(c->cmdq, "%s", cause);
1.1 nicm 1185: cmd_free_argv(argc, argv);
1186: goto error;
1187: }
1188: cmd_free_argv(argc, argv);
1189:
1.113 nicm 1190: if (c != cfg_client || cfg_finished)
1.131 nicm 1191: cmdq_run(c->cmdq, cmdlist, NULL);
1.113 nicm 1192: else
1.131 nicm 1193: cmdq_append(c->cmdq, cmdlist, NULL);
1.1 nicm 1194: cmd_list_free(cmdlist);
1195: return;
1196:
1197: error:
1198: if (cmdlist != NULL)
1199: cmd_list_free(cmdlist);
1.88 nicm 1200:
1.36 nicm 1201: c->flags |= CLIENT_EXIT;
1.1 nicm 1202: }
1203:
1204: /* Handle identify message. */
1.190 nicm 1205: static void
1.162 nicm 1206: server_client_dispatch_identify(struct client *c, struct imsg *imsg)
1.1 nicm 1207: {
1.165 nicm 1208: const char *data, *home;
1.109 nicm 1209: size_t datalen;
1210: int flags;
1211:
1212: if (c->flags & CLIENT_IDENTIFIED)
1213: fatalx("out-of-order identify message");
1214:
1215: data = imsg->data;
1216: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1217:
1218: switch (imsg->hdr.type) {
1219: case MSG_IDENTIFY_FLAGS:
1220: if (datalen != sizeof flags)
1221: fatalx("bad MSG_IDENTIFY_FLAGS size");
1222: memcpy(&flags, data, sizeof flags);
1223: c->flags |= flags;
1.158 nicm 1224: log_debug("client %p IDENTIFY_FLAGS %#x", c, flags);
1.109 nicm 1225: break;
1226: case MSG_IDENTIFY_TERM:
1.110 nicm 1227: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1228: fatalx("bad MSG_IDENTIFY_TERM string");
1229: c->term = xstrdup(data);
1.158 nicm 1230: log_debug("client %p IDENTIFY_TERM %s", c, data);
1.109 nicm 1231: break;
1232: case MSG_IDENTIFY_TTYNAME:
1.110 nicm 1233: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1234: fatalx("bad MSG_IDENTIFY_TTYNAME string");
1235: c->ttyname = xstrdup(data);
1.158 nicm 1236: log_debug("client %p IDENTIFY_TTYNAME %s", c, data);
1.109 nicm 1237: break;
1238: case MSG_IDENTIFY_CWD:
1.155 nicm 1239: if (datalen == 0 || data[datalen - 1] != '\0')
1240: fatalx("bad MSG_IDENTIFY_CWD string");
1.165 nicm 1241: if (access(data, X_OK) == 0)
1242: c->cwd = xstrdup(data);
1243: else if ((home = find_home()) != NULL)
1244: c->cwd = xstrdup(home);
1245: else
1246: c->cwd = xstrdup("/");
1.158 nicm 1247: log_debug("client %p IDENTIFY_CWD %s", c, data);
1.109 nicm 1248: break;
1249: case MSG_IDENTIFY_STDIN:
1250: if (datalen != 0)
1251: fatalx("bad MSG_IDENTIFY_STDIN size");
1252: c->fd = imsg->fd;
1.158 nicm 1253: log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd);
1.109 nicm 1254: break;
1255: case MSG_IDENTIFY_ENVIRON:
1.110 nicm 1256: if (datalen == 0 || data[datalen - 1] != '\0')
1.109 nicm 1257: fatalx("bad MSG_IDENTIFY_ENVIRON string");
1258: if (strchr(data, '=') != NULL)
1.164 nicm 1259: environ_put(c->environ, data);
1.158 nicm 1260: log_debug("client %p IDENTIFY_ENVIRON %s", c, data);
1.143 nicm 1261: break;
1262: case MSG_IDENTIFY_CLIENTPID:
1263: if (datalen != sizeof c->pid)
1264: fatalx("bad MSG_IDENTIFY_CLIENTPID size");
1265: memcpy(&c->pid, data, sizeof c->pid);
1.158 nicm 1266: log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid);
1.109 nicm 1267: break;
1268: default:
1269: break;
1270: }
1271:
1272: if (imsg->hdr.type != MSG_IDENTIFY_DONE)
1273: return;
1274: c->flags |= CLIENT_IDENTIFIED;
1.75 nicm 1275:
1.109 nicm 1276: if (c->flags & CLIENT_CONTROL) {
1.75 nicm 1277: c->stdin_callback = control_callback;
1.109 nicm 1278:
1.97 nicm 1279: evbuffer_free(c->stderr_data);
1280: c->stderr_data = c->stdout_data;
1.109 nicm 1281:
1282: if (c->flags & CLIENT_CONTROLCONTROL)
1.96 nicm 1283: evbuffer_add_printf(c->stdout_data, "\033P1000p");
1.162 nicm 1284: proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
1.75 nicm 1285:
1286: c->tty.fd = -1;
1287:
1.109 nicm 1288: close(c->fd);
1289: c->fd = -1;
1290:
1.75 nicm 1291: return;
1292: }
1.1 nicm 1293:
1.109 nicm 1294: if (c->fd == -1)
1.104 nicm 1295: return;
1.145 nicm 1296: if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
1.109 nicm 1297: close(c->fd);
1298: c->fd = -1;
1.80 nicm 1299: return;
1300: }
1.109 nicm 1301: if (c->flags & CLIENT_UTF8)
1.1 nicm 1302: c->tty.flags |= TTY_UTF8;
1.109 nicm 1303: if (c->flags & CLIENT_256COLOURS)
1.1 nicm 1304: c->tty.term_flags |= TERM_256COLOURS;
1305:
1306: tty_resize(&c->tty);
1307:
1.109 nicm 1308: if (!(c->flags & CLIENT_CONTROL))
1.86 nicm 1309: c->flags |= CLIENT_TERMINAL;
1.1 nicm 1310: }
1311:
1312: /* Handle shell message. */
1.190 nicm 1313: static void
1.162 nicm 1314: server_client_dispatch_shell(struct client *c)
1.1 nicm 1315: {
1.107 nicm 1316: const char *shell;
1.25 nicm 1317:
1.163 nicm 1318: shell = options_get_string(global_s_options, "default-shell");
1.1 nicm 1319: if (*shell == '\0' || areshell(shell))
1320: shell = _PATH_BSHELL;
1.162 nicm 1321: proc_send_s(c->peer, MSG_SHELL, shell);
1.25 nicm 1322:
1.162 nicm 1323: proc_kill_peer(c->peer);
1.169 nicm 1324: }
1325:
1326: /* Event callback to push more stdout data if any left. */
1327: static void
1.170 nicm 1328: server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
1.169 nicm 1329: {
1330: struct client *c = arg;
1331:
1332: if (~c->flags & CLIENT_DEAD)
1333: server_client_push_stdout(c);
1334: server_client_unref(c);
1335: }
1336:
1337: /* Push stdout to client if possible. */
1338: void
1339: server_client_push_stdout(struct client *c)
1340: {
1341: struct msg_stdout_data data;
1342: size_t sent, left;
1343:
1344: left = EVBUFFER_LENGTH(c->stdout_data);
1345: while (left != 0) {
1346: sent = left;
1347: if (sent > sizeof data.data)
1348: sent = sizeof data.data;
1349: memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
1350: data.size = sent;
1351:
1352: if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
1353: break;
1354: evbuffer_drain(c->stdout_data, sent);
1355:
1356: left = EVBUFFER_LENGTH(c->stdout_data);
1357: log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
1358: sent, left);
1359: }
1360: if (left != 0) {
1361: c->references++;
1362: event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
1363: log_debug("%s: client %p, queued", __func__, c);
1364: }
1365: }
1366:
1367: /* Event callback to push more stderr data if any left. */
1368: static void
1.170 nicm 1369: server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
1.169 nicm 1370: {
1371: struct client *c = arg;
1372:
1373: if (~c->flags & CLIENT_DEAD)
1374: server_client_push_stderr(c);
1375: server_client_unref(c);
1376: }
1377:
1378: /* Push stderr to client if possible. */
1379: void
1380: server_client_push_stderr(struct client *c)
1381: {
1382: struct msg_stderr_data data;
1383: size_t sent, left;
1384:
1385: if (c->stderr_data == c->stdout_data) {
1386: server_client_push_stdout(c);
1387: return;
1388: }
1389:
1390: left = EVBUFFER_LENGTH(c->stderr_data);
1391: while (left != 0) {
1392: sent = left;
1393: if (sent > sizeof data.data)
1394: sent = sizeof data.data;
1395: memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
1396: data.size = sent;
1397:
1398: if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
1399: break;
1400: evbuffer_drain(c->stderr_data, sent);
1401:
1402: left = EVBUFFER_LENGTH(c->stderr_data);
1403: log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
1404: sent, left);
1405: }
1406: if (left != 0) {
1407: c->references++;
1408: event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
1409: log_debug("%s: client %p, queued", __func__, c);
1410: }
1.1 nicm 1411: }