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