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