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