Annotation of src/usr.bin/tmux/server-fn.c, Revision 1.83
1.83 ! nicm 1: /* $OpenBSD: server-fn.c,v 1.82 2015/04/21 21:24:49 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 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.60 nicm 21: #include <stdlib.h>
1.1 nicm 22: #include <string.h>
1.5 nicm 23: #include <time.h>
1.1 nicm 24: #include <unistd.h>
25:
26: #include "tmux.h"
27:
1.39 nicm 28: struct session *server_next_session(struct session *);
29: void server_callback_identify(int, short, void *);
1.28 nicm 30:
1.13 nicm 31: void
32: server_fill_environ(struct session *s, struct environ *env)
1.1 nicm 33: {
1.77 nicm 34: char var[PATH_MAX], *term;
1.50 nicm 35: u_int idx;
36: long pid;
37:
38: if (s != NULL) {
39: term = options_get_string(&s->options, "default-terminal");
40: environ_set(env, "TERM", term);
41:
1.67 nicm 42: idx = s->id;
1.50 nicm 43: } else
1.79 nicm 44: idx = (u_int)-1;
1.50 nicm 45: pid = getpid();
1.79 nicm 46: xsnprintf(var, sizeof var, "%s,%ld,%u", socket_path, pid, idx);
1.50 nicm 47: environ_set(env, "TMUX", var);
1.58 nicm 48: }
49:
50: void
51: server_write_ready(struct client *c)
52: {
1.59 nicm 53: if (c->flags & CLIENT_CONTROL)
54: return;
1.58 nicm 55: server_write_client(c, MSG_READY, NULL, 0);
1.10 nicm 56: }
57:
1.57 nicm 58: int
1.74 nicm 59: server_write_client(struct client *c, enum msgtype type, const void *buf,
60: size_t len)
1.1 nicm 61: {
1.14 nicm 62: struct imsgbuf *ibuf = &c->ibuf;
1.57 nicm 63: int error;
1.1 nicm 64:
1.12 nicm 65: if (c->flags & CLIENT_BAD)
1.57 nicm 66: return (-1);
1.14 nicm 67: log_debug("writing %d to client %d", type, c->ibuf.fd);
1.57 nicm 68: error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1,
69: (void *) buf, len);
70: if (error == 1)
71: server_update_event(c);
72: return (error == 1 ? 0 : -1);
1.1 nicm 73: }
74:
75: void
1.74 nicm 76: server_write_session(struct session *s, enum msgtype type, const void *buf,
77: size_t len)
1.1 nicm 78: {
79: struct client *c;
80: u_int i;
81:
82: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
83: c = ARRAY_ITEM(&clients, i);
84: if (c == NULL || c->session == NULL)
85: continue;
86: if (c->session == s)
87: server_write_client(c, type, buf, len);
88: }
89: }
90:
91: void
92: server_redraw_client(struct client *c)
93: {
94: c->flags |= CLIENT_REDRAW;
95: }
96:
97: void
98: server_status_client(struct client *c)
99: {
100: c->flags |= CLIENT_STATUS;
101: }
102:
103: void
104: server_redraw_session(struct session *s)
105: {
106: struct client *c;
107: u_int i;
108:
109: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
110: c = ARRAY_ITEM(&clients, i);
111: if (c == NULL || c->session == NULL)
112: continue;
113: if (c->session == s)
114: server_redraw_client(c);
115: }
116: }
117:
118: void
1.25 nicm 119: server_redraw_session_group(struct session *s)
120: {
121: struct session_group *sg;
122:
123: if ((sg = session_group_find(s)) == NULL)
124: server_redraw_session(s);
125: else {
126: TAILQ_FOREACH(s, &sg->sessions, gentry)
127: server_redraw_session(s);
128: }
129: }
130:
131: void
1.1 nicm 132: server_status_session(struct session *s)
133: {
134: struct client *c;
135: u_int i;
136:
137: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
138: c = ARRAY_ITEM(&clients, i);
139: if (c == NULL || c->session == NULL)
140: continue;
141: if (c->session == s)
142: server_status_client(c);
143: }
144: }
145:
146: void
1.25 nicm 147: server_status_session_group(struct session *s)
148: {
149: struct session_group *sg;
150:
151: if ((sg = session_group_find(s)) == NULL)
152: server_status_session(s);
153: else {
154: TAILQ_FOREACH(s, &sg->sessions, gentry)
155: server_status_session(s);
156: }
157: }
158:
159: void
1.1 nicm 160: server_redraw_window(struct window *w)
161: {
162: struct client *c;
163: u_int i;
164:
165: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
166: c = ARRAY_ITEM(&clients, i);
167: if (c == NULL || c->session == NULL)
168: continue;
169: if (c->session->curw->window == w)
170: server_redraw_client(c);
171: }
172: w->flags |= WINDOW_REDRAW;
1.33 nicm 173: }
174:
175: void
176: server_redraw_window_borders(struct window *w)
177: {
178: struct client *c;
179: u_int i;
180:
181: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
182: c = ARRAY_ITEM(&clients, i);
183: if (c == NULL || c->session == NULL)
184: continue;
185: if (c->session->curw->window == w)
186: c->flags |= CLIENT_BORDERS;
187: }
1.1 nicm 188: }
189:
190: void
191: server_status_window(struct window *w)
192: {
193: struct session *s;
194:
195: /*
196: * This is slightly different. We want to redraw the status line of any
1.65 nicm 197: * clients containing this window rather than anywhere it is the
1.1 nicm 198: * current window.
199: */
200:
1.47 nicm 201: RB_FOREACH(s, sessions, &sessions) {
1.83 ! nicm 202: if (session_has(s, w))
1.1 nicm 203: server_status_session(s);
204: }
205: }
206:
207: void
208: server_lock(void)
209: {
1.23 nicm 210: struct client *c;
211: u_int i;
1.17 nicm 212:
1.1 nicm 213: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
214: c = ARRAY_ITEM(&clients, i);
215: if (c == NULL || c->session == NULL)
216: continue;
1.23 nicm 217: server_lock_client(c);
218: }
219: }
1.1 nicm 220:
1.23 nicm 221: void
222: server_lock_session(struct session *s)
223: {
224: struct client *c;
225: u_int i;
226:
227: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
228: c = ARRAY_ITEM(&clients, i);
229: if (c == NULL || c->session == NULL || c->session != s)
230: continue;
231: server_lock_client(c);
1.31 nicm 232: }
1.23 nicm 233: }
1.1 nicm 234:
1.23 nicm 235: void
236: server_lock_client(struct client *c)
237: {
1.72 nicm 238: const char *cmd;
1.62 nicm 239:
1.64 nicm 240: if (c->flags & CLIENT_CONTROL)
1.62 nicm 241: return;
1.24 nicm 242:
243: if (c->flags & CLIENT_SUSPENDED)
244: return;
1.1 nicm 245:
1.23 nicm 246: cmd = options_get_string(&c->session->options, "lock-command");
1.72 nicm 247: if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
1.23 nicm 248: return;
1.31 nicm 249:
1.23 nicm 250: tty_stop_tty(&c->tty);
251: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
252: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
1.52 nicm 253: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
1.23 nicm 254:
255: c->flags |= CLIENT_SUSPENDED;
1.72 nicm 256: server_write_client(c, MSG_LOCK, cmd, strlen(cmd) + 1);
1.8 nicm 257: }
258:
259: void
260: server_kill_window(struct window *w)
261: {
1.70 nicm 262: struct session *s, *next_s, *target_s;
263: struct session_group *sg;
264: struct winlink *wl;
1.31 nicm 265:
1.48 nicm 266: next_s = RB_MIN(sessions, &sessions);
267: while (next_s != NULL) {
268: s = next_s;
269: next_s = RB_NEXT(sessions, &sessions, s);
270:
1.83 ! nicm 271: if (!session_has(s, w))
1.8 nicm 272: continue;
1.78 nicm 273: server_unzoom_window(w);
1.34 nicm 274: while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
275: if (session_detach(s, wl)) {
276: server_destroy_session_group(s);
277: break;
278: } else
279: server_redraw_session_group(s);
280: }
1.56 nicm 281:
1.70 nicm 282: if (options_get_number(&s->options, "renumber-windows")) {
283: if ((sg = session_group_find(s)) != NULL) {
284: TAILQ_FOREACH(target_s, &sg->sessions, gentry)
285: session_renumber_windows(target_s);
286: } else
287: session_renumber_windows(s);
288: }
1.8 nicm 289: }
1.69 nicm 290: recalculate_sizes();
1.21 nicm 291: }
292:
293: int
1.25 nicm 294: server_link_window(struct session *src, struct winlink *srcwl,
1.76 nicm 295: struct session *dst, int dstidx, int killflag, int selectflag,
296: char **cause)
1.21 nicm 297: {
1.25 nicm 298: struct winlink *dstwl;
299: struct session_group *srcsg, *dstsg;
300:
301: srcsg = session_group_find(src);
302: dstsg = session_group_find(dst);
303: if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
304: xasprintf(cause, "sessions are grouped");
305: return (-1);
306: }
1.21 nicm 307:
308: dstwl = NULL;
309: if (dstidx != -1)
310: dstwl = winlink_find_by_index(&dst->windows, dstidx);
311: if (dstwl != NULL) {
1.44 nicm 312: if (dstwl->window == srcwl->window) {
313: xasprintf(cause, "same index: %d", dstidx);
1.41 nicm 314: return (-1);
1.44 nicm 315: }
1.21 nicm 316: if (killflag) {
317: /*
318: * Can't use session_detach as it will destroy session
319: * if this makes it empty.
320: */
1.55 nicm 321: notify_window_unlinked(dst, dstwl->window);
1.37 nicm 322: dstwl->flags &= ~WINLINK_ALERTFLAGS;
1.21 nicm 323: winlink_stack_remove(&dst->lastw, dstwl);
324: winlink_remove(&dst->windows, dstwl);
325:
326: /* Force select/redraw if current. */
1.26 nicm 327: if (dstwl == dst->curw) {
1.21 nicm 328: selectflag = 1;
1.26 nicm 329: dst->curw = NULL;
330: }
1.21 nicm 331: }
332: }
333:
334: if (dstidx == -1)
335: dstidx = -1 - options_get_number(&dst->options, "base-index");
336: dstwl = session_attach(dst, srcwl->window, dstidx, cause);
337: if (dstwl == NULL)
338: return (-1);
339:
1.25 nicm 340: if (selectflag)
1.21 nicm 341: session_select(dst, dstwl->idx);
1.25 nicm 342: server_redraw_session_group(dst);
1.21 nicm 343:
344: return (0);
345: }
346:
347: void
348: server_unlink_window(struct session *s, struct winlink *wl)
349: {
350: if (session_detach(s, wl))
1.25 nicm 351: server_destroy_session_group(s);
352: else
353: server_redraw_session_group(s);
1.29 nicm 354: }
355:
356: void
357: server_destroy_pane(struct window_pane *wp)
358: {
1.51 nicm 359: struct window *w = wp->window;
360: int old_fd;
361: struct screen_write_ctx ctx;
362: struct grid_cell gc;
1.29 nicm 363:
1.51 nicm 364: old_fd = wp->fd;
1.36 nicm 365: if (wp->fd != -1) {
1.53 nicm 366: bufferevent_free(wp->event);
1.36 nicm 367: close(wp->fd);
368: wp->fd = -1;
369: }
1.29 nicm 370:
1.51 nicm 371: if (options_get_number(&w->options, "remain-on-exit")) {
372: if (old_fd == -1)
373: return;
374: screen_write_start(&ctx, wp, &wp->base);
375: screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
376: screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
377: screen_write_linefeed(&ctx, 1);
378: memcpy(&gc, &grid_default_cell, sizeof gc);
379: gc.attr |= GRID_ATTR_BRIGHT;
380: screen_write_puts(&ctx, &gc, "Pane is dead");
381: screen_write_stop(&ctx);
382: wp->flags |= PANE_REDRAW;
1.29 nicm 383: return;
1.51 nicm 384: }
1.29 nicm 385:
1.66 nicm 386: server_unzoom_window(w);
1.29 nicm 387: layout_close_pane(wp);
388: window_remove_pane(w, wp);
389:
390: if (TAILQ_EMPTY(&w->panes))
391: server_kill_window(w);
392: else
393: server_redraw_window(w);
1.25 nicm 394: }
395:
396: void
397: server_destroy_session_group(struct session *s)
398: {
399: struct session_group *sg;
1.71 nicm 400: struct session *s1;
1.25 nicm 401:
402: if ((sg = session_group_find(s)) == NULL)
1.21 nicm 403: server_destroy_session(s);
1.25 nicm 404: else {
1.71 nicm 405: TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
1.25 nicm 406: server_destroy_session(s);
1.71 nicm 407: session_destroy(s);
408: }
1.25 nicm 409: }
1.19 nicm 410: }
411:
1.39 nicm 412: struct session *
413: server_next_session(struct session *s)
414: {
415: struct session *s_loop, *s_out;
416:
417: s_out = NULL;
1.47 nicm 418: RB_FOREACH(s_loop, sessions, &sessions) {
419: if (s_loop == s)
1.39 nicm 420: continue;
421: if (s_out == NULL ||
422: timercmp(&s_loop->activity_time, &s_out->activity_time, <))
423: s_out = s_loop;
424: }
425: return (s_out);
426: }
427:
1.19 nicm 428: void
429: server_destroy_session(struct session *s)
430: {
431: struct client *c;
1.39 nicm 432: struct session *s_new;
1.19 nicm 433: u_int i;
1.31 nicm 434:
1.39 nicm 435: if (!options_get_number(&s->options, "detach-on-destroy"))
436: s_new = server_next_session(s);
437: else
438: s_new = NULL;
439:
1.19 nicm 440: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
441: c = ARRAY_ITEM(&clients, i);
442: if (c == NULL || c->session != s)
443: continue;
1.39 nicm 444: if (s_new == NULL) {
445: c->session = NULL;
1.40 nicm 446: c->flags |= CLIENT_EXIT;
1.39 nicm 447: } else {
1.46 nicm 448: c->last_session = NULL;
1.39 nicm 449: c->session = s_new;
1.55 nicm 450: notify_attached_session_changed(c);
1.49 nicm 451: session_update_activity(s_new);
1.39 nicm 452: server_redraw_client(c);
453: }
1.19 nicm 454: }
1.38 nicm 455: recalculate_sizes();
1.42 nicm 456: }
457:
458: void
1.75 nicm 459: server_check_unattached(void)
1.42 nicm 460: {
461: struct session *s;
462:
463: /*
464: * If any sessions are no longer attached and have destroy-unattached
465: * set, collect them.
466: */
1.47 nicm 467: RB_FOREACH(s, sessions, &sessions) {
468: if (!(s->flags & SESSION_UNATTACHED))
1.42 nicm 469: continue;
470: if (options_get_number (&s->options, "destroy-unattached"))
471: session_destroy(s);
472: }
1.15 nicm 473: }
474:
475: void
476: server_set_identify(struct client *c)
477: {
478: struct timeval tv;
479: int delay;
480:
481: delay = options_get_number(&c->session->options, "display-panes-time");
482: tv.tv_sec = delay / 1000;
483: tv.tv_usec = (delay % 1000) * 1000L;
1.31 nicm 484:
1.75 nicm 485: if (event_initialized(&c->identify_timer))
1.54 nicm 486: evtimer_del(&c->identify_timer);
1.28 nicm 487: evtimer_set(&c->identify_timer, server_callback_identify, c);
488: evtimer_add(&c->identify_timer, &tv);
1.15 nicm 489:
490: c->flags |= CLIENT_IDENTIFY;
491: c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
492: server_redraw_client(c);
493: }
494:
495: void
496: server_clear_identify(struct client *c)
497: {
498: if (c->flags & CLIENT_IDENTIFY) {
499: c->flags &= ~CLIENT_IDENTIFY;
500: c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
501: server_redraw_client(c);
502: }
1.28 nicm 503: }
504:
505: void
506: server_callback_identify(unused int fd, unused short events, void *data)
507: {
508: struct client *c = data;
509:
510: server_clear_identify(c);
1.27 nicm 511: }
512:
513: void
514: server_update_event(struct client *c)
515: {
516: short events;
517:
518: events = 0;
519: if (!(c->flags & CLIENT_BAD))
520: events |= EV_READ;
521: if (c->ibuf.w.queued > 0)
522: events |= EV_WRITE;
1.54 nicm 523: if (event_initialized(&c->event))
524: event_del(&c->event);
1.27 nicm 525: event_set(&c->event, c->ibuf.fd, events, server_client_callback, c);
1.31 nicm 526: event_add(&c->event, NULL);
1.57 nicm 527: }
528:
529: /* Push stdout to client if possible. */
530: void
531: server_push_stdout(struct client *c)
532: {
533: struct msg_stdout_data data;
534: size_t size;
535:
536: size = EVBUFFER_LENGTH(c->stdout_data);
537: if (size == 0)
538: return;
539: if (size > sizeof data.data)
540: size = sizeof data.data;
541:
542: memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size);
543: data.size = size;
544:
545: if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0)
546: evbuffer_drain(c->stdout_data, size);
547: }
548:
549: /* Push stderr to client if possible. */
550: void
551: server_push_stderr(struct client *c)
552: {
553: struct msg_stderr_data data;
554: size_t size;
555:
1.68 nicm 556: if (c->stderr_data == c->stdout_data) {
557: server_push_stdout(c);
558: return;
559: }
1.57 nicm 560: size = EVBUFFER_LENGTH(c->stderr_data);
561: if (size == 0)
562: return;
563: if (size > sizeof data.data)
564: size = sizeof data.data;
565:
566: memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size);
567: data.size = size;
568:
569: if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0)
570: evbuffer_drain(c->stderr_data, size);
571: }
572:
573: /* Set stdin callback. */
574: int
575: server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
576: void *), void *cb_data, char **cause)
577: {
1.65 nicm 578: if (c == NULL || c->session != NULL) {
1.57 nicm 579: *cause = xstrdup("no client with stdin");
580: return (-1);
581: }
582: if (c->flags & CLIENT_TERMINAL) {
583: *cause = xstrdup("stdin is a tty");
584: return (-1);
585: }
586: if (c->stdin_callback != NULL) {
587: *cause = xstrdup("stdin in use");
588: return (-1);
589: }
590:
591: c->stdin_callback_data = cb_data;
592: c->stdin_callback = cb;
593:
594: c->references++;
595:
596: if (c->stdin_closed)
1.75 nicm 597: c->stdin_callback(c, 1, c->stdin_callback_data);
1.61 nicm 598:
599: server_write_client(c, MSG_STDIN, NULL, 0);
600:
1.57 nicm 601: return (0);
1.66 nicm 602: }
603:
604: void
605: server_unzoom_window(struct window *w)
606: {
1.80 nicm 607: if (window_unzoom(w) == 0) {
608: server_redraw_window(w);
609: server_status_window(w);
610: }
1.1 nicm 611: }