Annotation of src/usr.bin/tmux/server-client.c, Revision 1.34
1.34 ! nicm 1: /* $OpenBSD: server-client.c,v 1.33 2010/06/28 22:10:42 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>
20:
1.12 nicm 21: #include <event.h>
1.1 nicm 22: #include <fcntl.h>
23: #include <string.h>
1.4 nicm 24: #include <time.h>
1.1 nicm 25: #include <paths.h>
26: #include <unistd.h>
27:
28: #include "tmux.h"
29:
1.18 nicm 30: void server_client_handle_key(int, struct mouse_event *, void *);
1.17 nicm 31: void server_client_repeat_timer(int, short, void *);
1.1 nicm 32: void server_client_check_redraw(struct client *);
33: void server_client_set_title(struct client *);
1.18 nicm 34: void server_client_reset_state(struct client *);
1.1 nicm 35:
36: int server_client_msg_dispatch(struct client *);
37: void server_client_msg_command(struct client *, struct msg_command_data *);
38: void server_client_msg_identify(
1.25 nicm 39: struct client *, struct msg_identify_data *, int);
1.1 nicm 40: void server_client_msg_shell(struct client *);
41:
42: void printflike2 server_client_msg_error(struct cmd_ctx *, const char *, ...);
43: void printflike2 server_client_msg_print(struct cmd_ctx *, const char *, ...);
44: void printflike2 server_client_msg_info(struct cmd_ctx *, const char *, ...);
45:
46: /* Create a new client. */
47: void
48: server_client_create(int fd)
49: {
50: struct client *c;
51: int mode;
52: u_int i;
53:
54: if ((mode = fcntl(fd, F_GETFL)) == -1)
55: fatal("fcntl failed");
56: if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
57: fatal("fcntl failed");
58: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
59: fatal("fcntl failed");
60:
61: c = xcalloc(1, sizeof *c);
62: c->references = 0;
63: imsg_init(&c->ibuf, fd);
1.14 nicm 64: server_update_event(c);
1.25 nicm 65:
1.10 nicm 66: if (gettimeofday(&c->creation_time, NULL) != 0)
1.1 nicm 67: fatal("gettimeofday failed");
1.11 nicm 68: memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
1.1 nicm 69:
70: ARRAY_INIT(&c->prompt_hdata);
71:
1.33 nicm 72: c->stdin_file = NULL;
73: c->stdout_file = NULL;
74: c->stderr_file = NULL;
75:
1.1 nicm 76: c->tty.fd = -1;
77: c->title = NULL;
78:
79: c->session = NULL;
80: c->tty.sx = 80;
81: c->tty.sy = 24;
82:
83: screen_init(&c->status, c->tty.sx, 1, 0);
84: job_tree_init(&c->status_jobs);
85:
86: c->message_string = NULL;
1.21 nicm 87: ARRAY_INIT(&c->message_log);
1.1 nicm 88:
89: c->prompt_string = NULL;
90: c->prompt_buffer = NULL;
91: c->prompt_index = 0;
92:
1.17 nicm 93: evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
94:
1.1 nicm 95: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
96: if (ARRAY_ITEM(&clients, i) == NULL) {
97: ARRAY_SET(&clients, i, c);
98: return;
99: }
100: }
101: ARRAY_ADD(&clients, c);
102: log_debug("new client %d", fd);
103: }
104:
105: /* Lost a client. */
106: void
107: server_client_lost(struct client *c)
108: {
1.21 nicm 109: struct message_entry *msg;
110: u_int i;
1.1 nicm 111:
112: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
113: if (ARRAY_ITEM(&clients, i) == c)
114: ARRAY_SET(&clients, i, NULL);
115: }
116: log_debug("lost client %d", c->ibuf.fd);
117:
118: /*
119: * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
120: * and tty_free might close an unrelated fd.
121: */
122: if (c->flags & CLIENT_TERMINAL)
123: tty_free(&c->tty);
124:
1.33 nicm 125: if (c->stdin_file != NULL)
126: fclose(c->stdin_file);
127: if (c->stdout_file != NULL)
128: fclose(c->stdout_file);
129: if (c->stderr_file != NULL)
130: fclose(c->stderr_file);
131:
1.1 nicm 132: screen_free(&c->status);
133: job_tree_free(&c->status_jobs);
134:
135: if (c->title != NULL)
136: xfree(c->title);
137:
1.17 nicm 138: evtimer_del(&c->repeat_timer);
139:
1.15 nicm 140: evtimer_del(&c->identify_timer);
141:
1.1 nicm 142: if (c->message_string != NULL)
143: xfree(c->message_string);
1.15 nicm 144: evtimer_del(&c->message_timer);
1.21 nicm 145: for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) {
146: msg = &ARRAY_ITEM(&c->message_log, i);
147: xfree(msg->msg);
148: }
149: ARRAY_FREE(&c->message_log);
1.1 nicm 150:
151: if (c->prompt_string != NULL)
152: xfree(c->prompt_string);
153: if (c->prompt_buffer != NULL)
154: xfree(c->prompt_buffer);
155: for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
156: xfree(ARRAY_ITEM(&c->prompt_hdata, i));
157: ARRAY_FREE(&c->prompt_hdata);
158:
159: if (c->cwd != NULL)
160: xfree(c->cwd);
161:
162: close(c->ibuf.fd);
163: imsg_clear(&c->ibuf);
1.12 nicm 164: event_del(&c->event);
1.13 nicm 165:
1.1 nicm 166: for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
167: if (ARRAY_ITEM(&dead_clients, i) == NULL) {
168: ARRAY_SET(&dead_clients, i, c);
169: break;
170: }
171: }
172: if (i == ARRAY_LENGTH(&dead_clients))
173: ARRAY_ADD(&dead_clients, c);
174: c->flags |= CLIENT_DEAD;
175:
176: recalculate_sizes();
1.19 nicm 177: server_update_socket();
1.9 nicm 178: }
179:
1.1 nicm 180: /* Process a single client event. */
181: void
1.12 nicm 182: server_client_callback(int fd, short events, void *data)
1.1 nicm 183: {
184: struct client *c = data;
1.7 nicm 185:
186: if (c->flags & CLIENT_DEAD)
187: return;
1.1 nicm 188:
189: if (fd == c->ibuf.fd) {
1.12 nicm 190: if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0)
1.1 nicm 191: goto client_lost;
192:
193: if (c->flags & CLIENT_BAD) {
194: if (c->ibuf.w.queued == 0)
195: goto client_lost;
196: return;
197: }
198:
1.12 nicm 199: if (events & EV_READ && server_client_msg_dispatch(c) != 0)
1.1 nicm 200: goto client_lost;
201: }
1.14 nicm 202:
1.25 nicm 203: server_update_event(c);
1.1 nicm 204: return;
205:
206: client_lost:
207: server_client_lost(c);
208: }
209:
1.16 nicm 210: /* Handle client status timer. */
211: void
212: server_client_status_timer(void)
213: {
214: struct client *c;
215: struct session *s;
216: struct job *job;
217: struct timeval tv;
1.20 nicm 218: u_int i;
219: int interval;
220: time_t difference;
1.16 nicm 221:
222: if (gettimeofday(&tv, NULL) != 0)
223: fatal("gettimeofday failed");
224:
225: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
226: c = ARRAY_ITEM(&clients, i);
227: if (c == NULL || c->session == NULL)
228: continue;
229: if (c->message_string != NULL || c->prompt_string != NULL) {
230: /*
231: * Don't need timed redraw for messages/prompts so bail
232: * now. The status timer isn't reset when they are
233: * redrawn anyway.
234: */
235: continue;
236: }
237: s = c->session;
238:
239: if (!options_get_number(&s->options, "status"))
240: continue;
241: interval = options_get_number(&s->options, "status-interval");
242:
1.20 nicm 243: difference = tv.tv_sec - c->status_timer.tv_sec;
244: if (difference >= interval) {
1.16 nicm 245: RB_FOREACH(job, jobs, &c->status_jobs)
1.20 nicm 246: job_run(job);
1.16 nicm 247: c->flags |= CLIENT_STATUS;
248: }
249: }
250: }
251:
1.18 nicm 252: /* Handle data key input from client. */
253: void
254: server_client_handle_key(int key, struct mouse_event *mouse, void *data)
255: {
256: struct client *c = data;
257: struct session *s;
258: struct window *w;
259: struct window_pane *wp;
260: struct options *oo;
261: struct timeval tv;
262: struct key_binding *bd;
263: struct keylist *keylist;
264: int xtimeout, isprefix;
265: u_int i;
266:
267: /* Check the client is good to accept input. */
268: if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
269: return;
270: if (c->session == NULL)
271: return;
272: s = c->session;
273:
274: /* Update the activity timer. */
275: if (gettimeofday(&c->activity_time, NULL) != 0)
276: fatal("gettimeofday failed");
277: memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
278:
279: w = c->session->curw->window;
280: wp = w->active;
281: oo = &c->session->options;
282:
283: /* Special case: number keys jump to pane in identify mode. */
1.25 nicm 284: if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
1.30 nicm 285: if (c->flags & CLIENT_READONLY)
286: return;
1.18 nicm 287: wp = window_pane_at_index(w, key - '0');
288: if (wp != NULL && window_pane_visible(wp))
289: window_set_active_pane(w, wp);
290: server_clear_identify(c);
291: return;
292: }
293:
294: /* Handle status line. */
1.30 nicm 295: if (!(c->flags & CLIENT_READONLY)) {
296: status_message_clear(c);
297: server_clear_identify(c);
298: }
1.18 nicm 299: if (c->prompt_string != NULL) {
1.30 nicm 300: if (!(c->flags & CLIENT_READONLY))
301: status_prompt_key(c, key);
1.18 nicm 302: return;
303: }
304:
305: /* Check for mouse keys. */
306: if (key == KEYC_MOUSE) {
1.30 nicm 307: if (c->flags & CLIENT_READONLY)
308: return;
1.18 nicm 309: if (options_get_number(oo, "mouse-select-pane")) {
310: window_set_active_at(w, mouse->x, mouse->y);
1.28 nicm 311: server_redraw_window_borders(w);
1.18 nicm 312: wp = w->active;
313: }
1.31 nicm 314: window_pane_mouse(wp, c->session, mouse);
1.18 nicm 315: return;
316: }
317:
318: /* Is this a prefix key? */
319: keylist = options_get_data(&c->session->options, "prefix");
320: isprefix = 0;
321: for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
322: if (key == ARRAY_ITEM(keylist, i)) {
323: isprefix = 1;
324: break;
325: }
326: }
327:
328: /* No previous prefix key. */
329: if (!(c->flags & CLIENT_PREFIX)) {
330: if (isprefix)
331: c->flags |= CLIENT_PREFIX;
332: else {
333: /* Try as a non-prefix key binding. */
1.30 nicm 334: if ((bd = key_bindings_lookup(key)) == NULL) {
335: if (!(c->flags & CLIENT_READONLY))
1.31 nicm 336: window_pane_key(wp, c->session, key);
1.30 nicm 337: } else
1.18 nicm 338: key_bindings_dispatch(bd, c);
339: }
340: return;
341: }
342:
343: /* Prefix key already pressed. Reset prefix and lookup key. */
344: c->flags &= ~CLIENT_PREFIX;
345: if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
346: /* If repeating, treat this as a key, else ignore. */
347: if (c->flags & CLIENT_REPEAT) {
348: c->flags &= ~CLIENT_REPEAT;
349: if (isprefix)
350: c->flags |= CLIENT_PREFIX;
1.30 nicm 351: else if (!(c->flags & CLIENT_READONLY))
1.31 nicm 352: window_pane_key(wp, c->session, key);
1.18 nicm 353: }
354: return;
355: }
356:
357: /* If already repeating, but this key can't repeat, skip it. */
358: if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
359: c->flags &= ~CLIENT_REPEAT;
360: if (isprefix)
361: c->flags |= CLIENT_PREFIX;
1.30 nicm 362: else if (!(c->flags & CLIENT_READONLY))
1.31 nicm 363: window_pane_key(wp, c->session, key);
1.18 nicm 364: return;
365: }
366:
367: /* If this key can repeat, reset the repeat flags and timer. */
368: xtimeout = options_get_number(&c->session->options, "repeat-time");
369: if (xtimeout != 0 && bd->can_repeat) {
370: c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
1.25 nicm 371:
1.18 nicm 372: tv.tv_sec = xtimeout / 1000;
373: tv.tv_usec = (xtimeout % 1000) * 1000L;
374: evtimer_del(&c->repeat_timer);
375: evtimer_add(&c->repeat_timer, &tv);
376: }
377:
378: /* Dispatch the command. */
379: key_bindings_dispatch(bd, c);
380: }
381:
1.2 nicm 382: /* Client functions that need to happen every loop. */
383: void
384: server_client_loop(void)
385: {
386: struct client *c;
387: struct window *w;
388: struct window_pane *wp;
389: u_int i;
390:
391: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
392: c = ARRAY_ITEM(&clients, i);
393: if (c == NULL || c->session == NULL)
394: continue;
395:
1.18 nicm 396: server_client_check_redraw(c);
397: server_client_reset_state(c);
1.2 nicm 398: }
399:
400: /*
401: * Any windows will have been redrawn as part of clients, so clear
402: * their flags now.
403: */
404: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
405: w = ARRAY_ITEM(&windows, i);
406: if (w == NULL)
407: continue;
408:
409: w->flags &= ~WINDOW_REDRAW;
410: TAILQ_FOREACH(wp, &w->panes, entry)
411: wp->flags &= ~PANE_REDRAW;
412: }
413: }
414:
1.18 nicm 415: /*
416: * Update cursor position and mode settings. The scroll region and attributes
417: * are cleared when idle (waiting for an event) as this is the most likely time
418: * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
419: * compromise between excessive resets and likelihood of an interrupt.
420: *
421: * tty_region/tty_reset/tty_update_mode already take care of not resetting
422: * things that are already in their default state.
423: */
1.1 nicm 424: void
1.18 nicm 425: server_client_reset_state(struct client *c)
1.1 nicm 426: {
1.18 nicm 427: struct window *w = c->session->curw->window;
428: struct window_pane *wp = w->active;
429: struct screen *s = wp->screen;
430: struct options *oo = &c->session->options;
431: int status, mode;
1.1 nicm 432:
433: tty_region(&c->tty, 0, c->tty.sy - 1);
434:
435: status = options_get_number(oo, "status");
436: if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
437: tty_cursor(&c->tty, 0, 0);
438: else
439: tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy);
440:
441: mode = s->mode;
442: if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
443: options_get_number(oo, "mouse-select-pane"))
444: mode |= MODE_MOUSE;
445: tty_update_mode(&c->tty, mode);
446: tty_reset(&c->tty);
1.17 nicm 447: }
448:
449: /* Repeat time callback. */
1.24 nicm 450: /* ARGSUSED */
1.17 nicm 451: void
452: server_client_repeat_timer(unused int fd, unused short events, void *data)
453: {
454: struct client *c = data;
455:
456: if (c->flags & CLIENT_REPEAT)
457: c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
1.1 nicm 458: }
459:
460: /* Check for client redraws. */
461: void
462: server_client_check_redraw(struct client *c)
463: {
464: struct session *s = c->session;
465: struct window_pane *wp;
466: int flags, redraw;
467:
468: flags = c->tty.flags & TTY_FREEZE;
469: c->tty.flags &= ~TTY_FREEZE;
470:
471: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
472: if (options_get_number(&s->options, "set-titles"))
473: server_client_set_title(c);
1.12 nicm 474:
1.1 nicm 475: if (c->message_string != NULL)
476: redraw = status_message_redraw(c);
477: else if (c->prompt_string != NULL)
478: redraw = status_prompt_redraw(c);
479: else
480: redraw = status_redraw(c);
481: if (!redraw)
482: c->flags &= ~CLIENT_STATUS;
483: }
484:
485: if (c->flags & CLIENT_REDRAW) {
1.27 nicm 486: screen_redraw_screen(c, 0, 0);
487: c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
1.1 nicm 488: } else {
489: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
490: if (wp->flags & PANE_REDRAW)
491: screen_redraw_pane(c, wp);
492: }
493: }
494:
1.27 nicm 495: if (c->flags & CLIENT_BORDERS)
496: screen_redraw_screen(c, 0, 1);
497:
1.1 nicm 498: if (c->flags & CLIENT_STATUS)
1.27 nicm 499: screen_redraw_screen(c, 1, 0);
1.1 nicm 500:
501: c->tty.flags |= flags;
502:
1.27 nicm 503: c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS);
1.1 nicm 504: }
505:
506: /* Set client title. */
507: void
508: server_client_set_title(struct client *c)
509: {
510: struct session *s = c->session;
511: const char *template;
512: char *title;
513:
514: template = options_get_string(&s->options, "set-titles-string");
1.25 nicm 515:
1.23 nicm 516: title = status_replace(c, NULL, template, time(NULL), 1);
1.1 nicm 517: if (c->title == NULL || strcmp(title, c->title) != 0) {
518: if (c->title != NULL)
519: xfree(c->title);
520: c->title = xstrdup(title);
521: tty_set_title(&c->tty, c->title);
522: }
523: xfree(title);
524: }
525:
526: /* Dispatch message from client. */
527: int
528: server_client_msg_dispatch(struct client *c)
529: {
530: struct imsg imsg;
531: struct msg_command_data commanddata;
532: struct msg_identify_data identifydata;
533: struct msg_environ_data environdata;
534: ssize_t n, datalen;
535:
1.8 deraadt 536: if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
537: return (-1);
1.1 nicm 538:
539: for (;;) {
540: if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
541: return (-1);
542: if (n == 0)
543: return (0);
544: datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
545:
546: if (imsg.hdr.peerid != PROTOCOL_VERSION) {
547: server_write_client(c, MSG_VERSION, NULL, 0);
548: c->flags |= CLIENT_BAD;
549: imsg_free(&imsg);
550: continue;
551: }
552:
553: log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
554: switch (imsg.hdr.type) {
555: case MSG_COMMAND:
556: if (datalen != sizeof commanddata)
557: fatalx("bad MSG_COMMAND size");
558: memcpy(&commanddata, imsg.data, sizeof commanddata);
559:
560: server_client_msg_command(c, &commanddata);
561: break;
562: case MSG_IDENTIFY:
563: if (datalen != sizeof identifydata)
564: fatalx("bad MSG_IDENTIFY size");
565: if (imsg.fd == -1)
566: fatalx("MSG_IDENTIFY missing fd");
567: memcpy(&identifydata, imsg.data, sizeof identifydata);
568:
1.33 nicm 569: c->stdin_file = fdopen(imsg.fd, "r");
570: if (c->stdin_file == NULL)
571: fatal("fdopen(stdin) failed");
1.1 nicm 572: server_client_msg_identify(c, &identifydata, imsg.fd);
573: break;
1.33 nicm 574: case MSG_STDOUT:
575: if (datalen != 0)
576: fatalx("bad MSG_STDOUT size");
577: if (imsg.fd == -1)
578: fatalx("MSG_STDOUT missing fd");
579:
580: c->stdout_file = fdopen(imsg.fd, "w");
581: if (c->stdout_file == NULL)
582: fatal("fdopen(stdout) failed");
583: break;
584: case MSG_STDERR:
585: if (datalen != 0)
586: fatalx("bad MSG_STDERR size");
587: if (imsg.fd == -1)
588: fatalx("MSG_STDERR missing fd");
589:
590: c->stderr_file = fdopen(imsg.fd, "w");
591: if (c->stderr_file == NULL)
592: fatal("fdopen(stderr) failed");
593: break;
1.1 nicm 594: case MSG_RESIZE:
595: if (datalen != 0)
596: fatalx("bad MSG_RESIZE size");
597:
1.32 nicm 598: if (tty_resize(&c->tty)) {
599: recalculate_sizes();
600: server_redraw_client(c);
601: }
1.1 nicm 602: break;
603: case MSG_EXITING:
604: if (datalen != 0)
605: fatalx("bad MSG_EXITING size");
606:
607: c->session = NULL;
608: tty_close(&c->tty);
609: server_write_client(c, MSG_EXITED, NULL, 0);
610: break;
611: case MSG_WAKEUP:
612: case MSG_UNLOCK:
613: if (datalen != 0)
614: fatalx("bad MSG_WAKEUP size");
615:
616: if (!(c->flags & CLIENT_SUSPENDED))
617: break;
618: c->flags &= ~CLIENT_SUSPENDED;
1.10 nicm 619:
1.11 nicm 620: if (gettimeofday(&c->activity_time, NULL) != 0)
621: fatal("gettimeofday");
622: if (c->session != NULL) {
623: memcpy(&c->session->activity_time,
624: &c->activity_time,
625: sizeof c->session->activity_time);
626: }
1.10 nicm 627:
1.1 nicm 628: tty_start_tty(&c->tty);
629: server_redraw_client(c);
630: recalculate_sizes();
631: break;
632: case MSG_ENVIRON:
633: if (datalen != sizeof environdata)
634: fatalx("bad MSG_ENVIRON size");
635: memcpy(&environdata, imsg.data, sizeof environdata);
636:
637: environdata.var[(sizeof environdata.var) - 1] = '\0';
638: if (strchr(environdata.var, '=') != NULL)
639: environ_put(&c->environ, environdata.var);
640: break;
641: case MSG_SHELL:
642: if (datalen != 0)
643: fatalx("bad MSG_SHELL size");
644:
645: server_client_msg_shell(c);
646: break;
647: default:
648: fatalx("unexpected message");
649: }
650:
651: imsg_free(&imsg);
652: }
653: }
654:
655: /* Callback to send error message to client. */
656: void printflike2
657: server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...)
658: {
1.33 nicm 659: va_list ap;
1.1 nicm 660:
661: va_start(ap, fmt);
1.33 nicm 662: vfprintf(ctx->cmdclient->stderr_file, fmt, ap);
1.1 nicm 663: va_end(ap);
664:
1.33 nicm 665: fputc('\n', ctx->cmdclient->stderr_file);
666: fflush(ctx->cmdclient->stderr_file);
1.34 ! nicm 667:
! 668: ctx->cmdclient->retcode = 1;
1.1 nicm 669: }
670:
671: /* Callback to send print message to client. */
672: void printflike2
673: server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...)
674: {
1.33 nicm 675: va_list ap;
1.1 nicm 676:
677: va_start(ap, fmt);
1.33 nicm 678: vfprintf(ctx->cmdclient->stdout_file, fmt, ap);
1.1 nicm 679: va_end(ap);
680:
1.33 nicm 681: fputc('\n', ctx->cmdclient->stdout_file);
682: fflush(ctx->cmdclient->stdout_file);
1.1 nicm 683: }
684:
685: /* Callback to send print message to client, if not quiet. */
686: void printflike2
687: server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...)
688: {
1.33 nicm 689: va_list ap;
1.1 nicm 690:
1.26 nicm 691: if (options_get_number(&global_options, "quiet"))
1.1 nicm 692: return;
693:
694: va_start(ap, fmt);
1.33 nicm 695: vfprintf(ctx->cmdclient->stdout_file, fmt, ap);
1.1 nicm 696: va_end(ap);
697:
1.33 nicm 698: fputc('\n', ctx->cmdclient->stderr_file);
699: fflush(ctx->cmdclient->stdout_file);
1.1 nicm 700: }
701:
702: /* Handle command message. */
703: void
704: server_client_msg_command(struct client *c, struct msg_command_data *data)
705: {
1.34 ! nicm 706: struct cmd_ctx ctx;
! 707: struct cmd_list *cmdlist = NULL;
! 708: struct msg_exit_data exitdata;
! 709: int argc;
! 710: char **argv, *cause;
1.1 nicm 711:
712: ctx.error = server_client_msg_error;
713: ctx.print = server_client_msg_print;
714: ctx.info = server_client_msg_info;
715:
716: ctx.msgdata = data;
717: ctx.curclient = NULL;
718:
719: ctx.cmdclient = c;
720:
721: argc = data->argc;
722: data->argv[(sizeof data->argv) - 1] = '\0';
723: if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) {
724: server_client_msg_error(&ctx, "command too long");
725: goto error;
726: }
727:
728: if (argc == 0) {
729: argc = 1;
730: argv = xcalloc(1, sizeof *argv);
731: *argv = xstrdup("new-session");
732: }
733:
734: if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
735: server_client_msg_error(&ctx, "%s", cause);
736: cmd_free_argv(argc, argv);
737: goto error;
738: }
739: cmd_free_argv(argc, argv);
740:
1.34 ! nicm 741: if (cmd_list_exec(cmdlist, &ctx) != 1) {
! 742: exitdata.retcode = c->retcode;
! 743: server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
! 744: }
1.1 nicm 745: cmd_list_free(cmdlist);
746: return;
747:
748: error:
749: if (cmdlist != NULL)
750: cmd_list_free(cmdlist);
1.34 ! nicm 751: exitdata.retcode = c->retcode;
! 752: server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
1.1 nicm 753: }
754:
755: /* Handle identify message. */
756: void
757: server_client_msg_identify(
758: struct client *c, struct msg_identify_data *data, int fd)
759: {
1.33 nicm 760: int tty_fd;
761:
1.1 nicm 762: c->cwd = NULL;
763: data->cwd[(sizeof data->cwd) - 1] = '\0';
764: if (*data->cwd != '\0')
765: c->cwd = xstrdup(data->cwd);
766:
1.33 nicm 767: if (!isatty(fd))
768: return;
769: if ((tty_fd = dup(fd)) == -1)
770: fatal("dup failed");
1.1 nicm 771: data->term[(sizeof data->term) - 1] = '\0';
1.33 nicm 772: tty_init(&c->tty, tty_fd, data->term);
1.1 nicm 773: if (data->flags & IDENTIFY_UTF8)
774: c->tty.flags |= TTY_UTF8;
775: if (data->flags & IDENTIFY_256COLOURS)
776: c->tty.term_flags |= TERM_256COLOURS;
777: else if (data->flags & IDENTIFY_88COLOURS)
778: c->tty.term_flags |= TERM_88COLOURS;
1.18 nicm 779: c->tty.key_callback = server_client_handle_key;
780: c->tty.key_data = c;
1.1 nicm 781:
782: tty_resize(&c->tty);
783:
784: c->flags |= CLIENT_TERMINAL;
785: }
786:
787: /* Handle shell message. */
788: void
789: server_client_msg_shell(struct client *c)
790: {
791: struct msg_shell_data data;
792: const char *shell;
1.25 nicm 793:
1.1 nicm 794: shell = options_get_string(&global_s_options, "default-shell");
795:
796: if (*shell == '\0' || areshell(shell))
797: shell = _PATH_BSHELL;
798: if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
799: strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
1.25 nicm 800:
1.1 nicm 801: server_write_client(c, MSG_SHELL, &data, sizeof data);
802: c->flags |= CLIENT_BAD; /* it will die after exec */
803: }