Annotation of src/usr.bin/tmux/server-fn.c, Revision 1.105
1.105 ! nicm 1: /* $OpenBSD: server-fn.c,v 1.104 2017/02/09 15:04:53 nicm Exp $ */
1.1 nicm 2:
3: /*
1.98 nicm 4: * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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.90 nicm 20: #include <sys/queue.h>
21: #include <sys/uio.h>
1.1 nicm 22:
1.90 nicm 23: #include <imsg.h>
1.60 nicm 24: #include <stdlib.h>
1.1 nicm 25: #include <string.h>
1.5 nicm 26: #include <time.h>
1.1 nicm 27: #include <unistd.h>
28:
29: #include "tmux.h"
30:
1.100 nicm 31: static struct session *server_next_session(struct session *);
32: static void server_callback_identify(int, short, void *);
1.101 nicm 33: static void server_destroy_session_group(struct session *);
1.58 nicm 34:
35: void
1.1 nicm 36: server_redraw_client(struct client *c)
37: {
38: c->flags |= CLIENT_REDRAW;
39: }
40:
41: void
42: server_status_client(struct client *c)
43: {
44: c->flags |= CLIENT_STATUS;
45: }
46:
47: void
48: server_redraw_session(struct session *s)
49: {
50: struct client *c;
51:
1.84 nicm 52: TAILQ_FOREACH(c, &clients, entry) {
1.1 nicm 53: if (c->session == s)
54: server_redraw_client(c);
55: }
56: }
57:
58: void
1.25 nicm 59: server_redraw_session_group(struct session *s)
60: {
61: struct session_group *sg;
62:
1.104 nicm 63: if ((sg = session_group_contains(s)) == NULL)
1.25 nicm 64: server_redraw_session(s);
65: else {
66: TAILQ_FOREACH(s, &sg->sessions, gentry)
67: server_redraw_session(s);
68: }
69: }
70:
71: void
1.1 nicm 72: server_status_session(struct session *s)
73: {
74: struct client *c;
75:
1.84 nicm 76: TAILQ_FOREACH(c, &clients, entry) {
1.1 nicm 77: if (c->session == s)
78: server_status_client(c);
79: }
80: }
81:
82: void
1.25 nicm 83: server_status_session_group(struct session *s)
84: {
85: struct session_group *sg;
86:
1.104 nicm 87: if ((sg = session_group_contains(s)) == NULL)
1.25 nicm 88: server_status_session(s);
89: else {
90: TAILQ_FOREACH(s, &sg->sessions, gentry)
91: server_status_session(s);
92: }
93: }
94:
95: void
1.1 nicm 96: server_redraw_window(struct window *w)
97: {
98: struct client *c;
99:
1.84 nicm 100: TAILQ_FOREACH(c, &clients, entry) {
101: if (c->session != NULL && c->session->curw->window == w)
1.1 nicm 102: server_redraw_client(c);
103: }
104: w->flags |= WINDOW_REDRAW;
1.33 nicm 105: }
106:
107: void
108: server_redraw_window_borders(struct window *w)
109: {
110: struct client *c;
111:
1.84 nicm 112: TAILQ_FOREACH(c, &clients, entry) {
113: if (c->session != NULL && c->session->curw->window == w)
1.33 nicm 114: c->flags |= CLIENT_BORDERS;
115: }
1.1 nicm 116: }
117:
118: void
119: server_status_window(struct window *w)
120: {
121: struct session *s;
122:
123: /*
124: * This is slightly different. We want to redraw the status line of any
1.65 nicm 125: * clients containing this window rather than anywhere it is the
1.1 nicm 126: * current window.
127: */
128:
1.47 nicm 129: RB_FOREACH(s, sessions, &sessions) {
1.83 nicm 130: if (session_has(s, w))
1.1 nicm 131: server_status_session(s);
132: }
133: }
134:
135: void
136: server_lock(void)
137: {
1.23 nicm 138: struct client *c;
1.17 nicm 139:
1.84 nicm 140: TAILQ_FOREACH(c, &clients, entry) {
141: if (c->session != NULL)
142: server_lock_client(c);
1.23 nicm 143: }
144: }
1.1 nicm 145:
1.23 nicm 146: void
147: server_lock_session(struct session *s)
148: {
149: struct client *c;
150:
1.84 nicm 151: TAILQ_FOREACH(c, &clients, entry) {
152: if (c->session == s)
153: server_lock_client(c);
1.31 nicm 154: }
1.23 nicm 155: }
1.1 nicm 156:
1.23 nicm 157: void
158: server_lock_client(struct client *c)
159: {
1.72 nicm 160: const char *cmd;
1.62 nicm 161:
1.64 nicm 162: if (c->flags & CLIENT_CONTROL)
1.62 nicm 163: return;
1.24 nicm 164:
165: if (c->flags & CLIENT_SUSPENDED)
166: return;
1.1 nicm 167:
1.91 nicm 168: cmd = options_get_string(c->session->options, "lock-command");
1.72 nicm 169: if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
1.23 nicm 170: return;
1.31 nicm 171:
1.23 nicm 172: tty_stop_tty(&c->tty);
173: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
174: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
1.52 nicm 175: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
1.23 nicm 176:
177: c->flags |= CLIENT_SUSPENDED;
1.90 nicm 178: proc_send_s(c->peer, MSG_LOCK, cmd);
1.8 nicm 179: }
180:
181: void
182: server_kill_window(struct window *w)
183: {
1.70 nicm 184: struct session *s, *next_s, *target_s;
185: struct session_group *sg;
186: struct winlink *wl;
1.31 nicm 187:
1.48 nicm 188: next_s = RB_MIN(sessions, &sessions);
189: while (next_s != NULL) {
190: s = next_s;
191: next_s = RB_NEXT(sessions, &sessions, s);
192:
1.83 nicm 193: if (!session_has(s, w))
1.8 nicm 194: continue;
1.78 nicm 195: server_unzoom_window(w);
1.34 nicm 196: while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
197: if (session_detach(s, wl)) {
198: server_destroy_session_group(s);
199: break;
200: } else
201: server_redraw_session_group(s);
202: }
1.56 nicm 203:
1.91 nicm 204: if (options_get_number(s->options, "renumber-windows")) {
1.104 nicm 205: if ((sg = session_group_contains(s)) != NULL) {
1.70 nicm 206: TAILQ_FOREACH(target_s, &sg->sessions, gentry)
207: session_renumber_windows(target_s);
208: } else
209: session_renumber_windows(s);
210: }
1.8 nicm 211: }
1.69 nicm 212: recalculate_sizes();
1.21 nicm 213: }
214:
215: int
1.25 nicm 216: server_link_window(struct session *src, struct winlink *srcwl,
1.76 nicm 217: struct session *dst, int dstidx, int killflag, int selectflag,
218: char **cause)
1.21 nicm 219: {
1.25 nicm 220: struct winlink *dstwl;
221: struct session_group *srcsg, *dstsg;
222:
1.104 nicm 223: srcsg = session_group_contains(src);
224: dstsg = session_group_contains(dst);
1.25 nicm 225: if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
226: xasprintf(cause, "sessions are grouped");
227: return (-1);
228: }
1.21 nicm 229:
230: dstwl = NULL;
231: if (dstidx != -1)
232: dstwl = winlink_find_by_index(&dst->windows, dstidx);
233: if (dstwl != NULL) {
1.44 nicm 234: if (dstwl->window == srcwl->window) {
235: xasprintf(cause, "same index: %d", dstidx);
1.41 nicm 236: return (-1);
1.44 nicm 237: }
1.21 nicm 238: if (killflag) {
239: /*
240: * Can't use session_detach as it will destroy session
241: * if this makes it empty.
242: */
1.102 nicm 243: notify_session_window("window-unlinked", dst,
244: dstwl->window);
1.37 nicm 245: dstwl->flags &= ~WINLINK_ALERTFLAGS;
1.21 nicm 246: winlink_stack_remove(&dst->lastw, dstwl);
247: winlink_remove(&dst->windows, dstwl);
248:
249: /* Force select/redraw if current. */
1.26 nicm 250: if (dstwl == dst->curw) {
1.21 nicm 251: selectflag = 1;
1.26 nicm 252: dst->curw = NULL;
253: }
1.21 nicm 254: }
255: }
256:
257: if (dstidx == -1)
1.91 nicm 258: dstidx = -1 - options_get_number(dst->options, "base-index");
1.21 nicm 259: dstwl = session_attach(dst, srcwl->window, dstidx, cause);
260: if (dstwl == NULL)
261: return (-1);
262:
1.25 nicm 263: if (selectflag)
1.21 nicm 264: session_select(dst, dstwl->idx);
1.25 nicm 265: server_redraw_session_group(dst);
1.21 nicm 266:
267: return (0);
268: }
269:
270: void
271: server_unlink_window(struct session *s, struct winlink *wl)
272: {
273: if (session_detach(s, wl))
1.25 nicm 274: server_destroy_session_group(s);
275: else
276: server_redraw_session_group(s);
1.29 nicm 277: }
278:
279: void
1.102 nicm 280: server_destroy_pane(struct window_pane *wp, int notify)
1.29 nicm 281: {
1.51 nicm 282: struct window *w = wp->window;
283: int old_fd;
284: struct screen_write_ctx ctx;
285: struct grid_cell gc;
1.29 nicm 286:
1.51 nicm 287: old_fd = wp->fd;
1.36 nicm 288: if (wp->fd != -1) {
1.53 nicm 289: bufferevent_free(wp->event);
1.36 nicm 290: close(wp->fd);
291: wp->fd = -1;
292: }
1.29 nicm 293:
1.91 nicm 294: if (options_get_number(w->options, "remain-on-exit")) {
1.51 nicm 295: if (old_fd == -1)
296: return;
1.102 nicm 297:
298: if (notify)
299: notify_pane("pane-died", wp);
300:
1.51 nicm 301: screen_write_start(&ctx, wp, &wp->base);
302: screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
303: screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
304: screen_write_linefeed(&ctx, 1);
305: memcpy(&gc, &grid_default_cell, sizeof gc);
306: gc.attr |= GRID_ATTR_BRIGHT;
307: screen_write_puts(&ctx, &gc, "Pane is dead");
308: screen_write_stop(&ctx);
309: wp->flags |= PANE_REDRAW;
1.97 nicm 310:
1.29 nicm 311: return;
1.51 nicm 312: }
1.29 nicm 313:
1.102 nicm 314: if (notify)
315: notify_pane("pane-exited", wp);
316:
1.66 nicm 317: server_unzoom_window(w);
1.29 nicm 318: layout_close_pane(wp);
319: window_remove_pane(w, wp);
1.97 nicm 320:
1.29 nicm 321: if (TAILQ_EMPTY(&w->panes))
322: server_kill_window(w);
323: else
324: server_redraw_window(w);
1.25 nicm 325: }
326:
1.101 nicm 327: static void
1.25 nicm 328: server_destroy_session_group(struct session *s)
329: {
330: struct session_group *sg;
1.71 nicm 331: struct session *s1;
1.25 nicm 332:
1.104 nicm 333: if ((sg = session_group_contains(s)) == NULL)
1.21 nicm 334: server_destroy_session(s);
1.25 nicm 335: else {
1.71 nicm 336: TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
1.25 nicm 337: server_destroy_session(s);
1.71 nicm 338: session_destroy(s);
339: }
1.25 nicm 340: }
1.19 nicm 341: }
342:
1.100 nicm 343: static struct session *
1.39 nicm 344: server_next_session(struct session *s)
345: {
346: struct session *s_loop, *s_out;
347:
348: s_out = NULL;
1.47 nicm 349: RB_FOREACH(s_loop, sessions, &sessions) {
350: if (s_loop == s)
1.39 nicm 351: continue;
352: if (s_out == NULL ||
353: timercmp(&s_loop->activity_time, &s_out->activity_time, <))
354: s_out = s_loop;
355: }
356: return (s_out);
357: }
358:
1.19 nicm 359: void
360: server_destroy_session(struct session *s)
361: {
362: struct client *c;
1.39 nicm 363: struct session *s_new;
1.31 nicm 364:
1.91 nicm 365: if (!options_get_number(s->options, "detach-on-destroy"))
1.39 nicm 366: s_new = server_next_session(s);
367: else
368: s_new = NULL;
369:
1.84 nicm 370: TAILQ_FOREACH(c, &clients, entry) {
371: if (c->session != s)
1.19 nicm 372: continue;
1.39 nicm 373: if (s_new == NULL) {
374: c->session = NULL;
1.40 nicm 375: c->flags |= CLIENT_EXIT;
1.39 nicm 376: } else {
1.46 nicm 377: c->last_session = NULL;
1.39 nicm 378: c->session = s_new;
1.96 nicm 379: server_client_set_key_table(c, NULL);
1.86 nicm 380: status_timer_start(c);
1.102 nicm 381: notify_client("client-session-changed", c);
1.87 nicm 382: session_update_activity(s_new, NULL);
1.88 nicm 383: gettimeofday(&s_new->last_attached_time, NULL);
1.39 nicm 384: server_redraw_client(c);
1.95 nicm 385: alerts_check_session(s_new);
1.39 nicm 386: }
1.19 nicm 387: }
1.38 nicm 388: recalculate_sizes();
1.42 nicm 389: }
390:
391: void
1.75 nicm 392: server_check_unattached(void)
1.42 nicm 393: {
394: struct session *s;
395:
396: /*
397: * If any sessions are no longer attached and have destroy-unattached
398: * set, collect them.
399: */
1.47 nicm 400: RB_FOREACH(s, sessions, &sessions) {
401: if (!(s->flags & SESSION_UNATTACHED))
1.42 nicm 402: continue;
1.91 nicm 403: if (options_get_number (s->options, "destroy-unattached"))
1.42 nicm 404: session_destroy(s);
405: }
1.15 nicm 406: }
407:
408: void
409: server_set_identify(struct client *c)
410: {
411: struct timeval tv;
412: int delay;
413:
1.91 nicm 414: delay = options_get_number(c->session->options, "display-panes-time");
1.15 nicm 415: tv.tv_sec = delay / 1000;
416: tv.tv_usec = (delay % 1000) * 1000L;
1.31 nicm 417:
1.75 nicm 418: if (event_initialized(&c->identify_timer))
1.54 nicm 419: evtimer_del(&c->identify_timer);
1.28 nicm 420: evtimer_set(&c->identify_timer, server_callback_identify, c);
421: evtimer_add(&c->identify_timer, &tv);
1.15 nicm 422:
423: c->flags |= CLIENT_IDENTIFY;
424: c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
425: server_redraw_client(c);
426: }
427:
428: void
1.99 nicm 429: server_clear_identify(struct client *c, struct window_pane *wp)
1.15 nicm 430: {
1.99 nicm 431: if (~c->flags & CLIENT_IDENTIFY)
432: return;
433: c->flags &= ~CLIENT_IDENTIFY;
434:
435: if (c->identify_callback != NULL)
436: c->identify_callback(c, wp);
437:
438: c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
439: server_redraw_client(c);
1.28 nicm 440: }
441:
1.100 nicm 442: static void
1.93 nicm 443: server_callback_identify(__unused int fd, __unused short events, void *data)
1.28 nicm 444: {
1.99 nicm 445: server_clear_identify(data, NULL);
1.57 nicm 446: }
447:
448: /* Set stdin callback. */
449: int
450: server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
451: void *), void *cb_data, char **cause)
452: {
1.65 nicm 453: if (c == NULL || c->session != NULL) {
1.57 nicm 454: *cause = xstrdup("no client with stdin");
455: return (-1);
456: }
457: if (c->flags & CLIENT_TERMINAL) {
458: *cause = xstrdup("stdin is a tty");
459: return (-1);
460: }
461: if (c->stdin_callback != NULL) {
462: *cause = xstrdup("stdin in use");
463: return (-1);
464: }
465:
466: c->stdin_callback_data = cb_data;
467: c->stdin_callback = cb;
468:
469: c->references++;
470:
471: if (c->stdin_closed)
1.75 nicm 472: c->stdin_callback(c, 1, c->stdin_callback_data);
1.61 nicm 473:
1.90 nicm 474: proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
1.61 nicm 475:
1.57 nicm 476: return (0);
1.66 nicm 477: }
478:
479: void
480: server_unzoom_window(struct window *w)
481: {
1.80 nicm 482: if (window_unzoom(w) == 0) {
483: server_redraw_window(w);
484: server_status_window(w);
485: }
1.1 nicm 486: }