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