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