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