Annotation of src/usr.bin/tmux/server-fn.c, Revision 1.112
1.112 ! nicm 1: /* $OpenBSD: server-fn.c,v 1.111 2017/08/29 09:18:48 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>
1.112 ! nicm 21: #include <sys/wait.h>
1.90 nicm 22: #include <sys/uio.h>
1.1 nicm 23:
1.90 nicm 24: #include <imsg.h>
1.60 nicm 25: #include <stdlib.h>
1.1 nicm 26: #include <string.h>
1.5 nicm 27: #include <time.h>
1.1 nicm 28: #include <unistd.h>
29:
30: #include "tmux.h"
31:
1.100 nicm 32: static struct session *server_next_session(struct session *);
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: }
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.111 nicm 168: if (*cmd == '\0' || 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.110 nicm 177: proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
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: struct screen_write_ctx ctx;
283: struct grid_cell gc;
1.112 ! nicm 284: time_t t;
! 285: char tim[26];
1.29 nicm 286:
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.112 ! nicm 294: if (~wp->flags & PANE_STATUSREADY)
1.51 nicm 295: return;
1.102 nicm 296:
1.112 ! nicm 297: if (wp->flags & PANE_STATUSDRAWN)
! 298: return;
! 299: wp->flags |= PANE_STATUSDRAWN;
! 300:
1.102 nicm 301: if (notify)
302: notify_pane("pane-died", wp);
303:
1.51 nicm 304: screen_write_start(&ctx, wp, &wp->base);
305: screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
306: screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
1.108 nicm 307: screen_write_linefeed(&ctx, 1, 8);
1.51 nicm 308: memcpy(&gc, &grid_default_cell, sizeof gc);
1.112 ! nicm 309:
! 310: time(&t);
! 311: ctime_r(&t, tim);
! 312:
! 313: if (WIFEXITED(wp->status)) {
! 314: screen_write_nputs(&ctx, -1, &gc,
! 315: "Pane is dead (status %d, %s)",
! 316: WEXITSTATUS(wp->status),
! 317: tim);
! 318: } else if (WIFSIGNALED(wp->status)) {
! 319: screen_write_nputs(&ctx, -1, &gc,
! 320: "Pane is dead (signal %d, %s)",
! 321: WTERMSIG(wp->status),
! 322: tim);
! 323: }
! 324:
1.51 nicm 325: screen_write_stop(&ctx);
326: wp->flags |= PANE_REDRAW;
1.29 nicm 327: return;
1.51 nicm 328: }
1.29 nicm 329:
1.102 nicm 330: if (notify)
331: notify_pane("pane-exited", wp);
332:
1.66 nicm 333: server_unzoom_window(w);
1.29 nicm 334: layout_close_pane(wp);
335: window_remove_pane(w, wp);
1.97 nicm 336:
1.29 nicm 337: if (TAILQ_EMPTY(&w->panes))
338: server_kill_window(w);
339: else
340: server_redraw_window(w);
1.25 nicm 341: }
342:
1.101 nicm 343: static void
1.25 nicm 344: server_destroy_session_group(struct session *s)
345: {
346: struct session_group *sg;
1.71 nicm 347: struct session *s1;
1.25 nicm 348:
1.104 nicm 349: if ((sg = session_group_contains(s)) == NULL)
1.21 nicm 350: server_destroy_session(s);
1.25 nicm 351: else {
1.71 nicm 352: TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
1.25 nicm 353: server_destroy_session(s);
1.109 nicm 354: session_destroy(s, __func__);
1.71 nicm 355: }
1.25 nicm 356: }
1.19 nicm 357: }
358:
1.100 nicm 359: static struct session *
1.39 nicm 360: server_next_session(struct session *s)
361: {
362: struct session *s_loop, *s_out;
363:
364: s_out = NULL;
1.47 nicm 365: RB_FOREACH(s_loop, sessions, &sessions) {
366: if (s_loop == s)
1.39 nicm 367: continue;
368: if (s_out == NULL ||
369: timercmp(&s_loop->activity_time, &s_out->activity_time, <))
370: s_out = s_loop;
371: }
372: return (s_out);
373: }
374:
1.19 nicm 375: void
376: server_destroy_session(struct session *s)
377: {
378: struct client *c;
1.39 nicm 379: struct session *s_new;
1.31 nicm 380:
1.91 nicm 381: if (!options_get_number(s->options, "detach-on-destroy"))
1.39 nicm 382: s_new = server_next_session(s);
383: else
384: s_new = NULL;
385:
1.84 nicm 386: TAILQ_FOREACH(c, &clients, entry) {
387: if (c->session != s)
1.19 nicm 388: continue;
1.39 nicm 389: if (s_new == NULL) {
390: c->session = NULL;
1.40 nicm 391: c->flags |= CLIENT_EXIT;
1.39 nicm 392: } else {
1.46 nicm 393: c->last_session = NULL;
1.39 nicm 394: c->session = s_new;
1.96 nicm 395: server_client_set_key_table(c, NULL);
1.86 nicm 396: status_timer_start(c);
1.102 nicm 397: notify_client("client-session-changed", c);
1.87 nicm 398: session_update_activity(s_new, NULL);
1.88 nicm 399: gettimeofday(&s_new->last_attached_time, NULL);
1.39 nicm 400: server_redraw_client(c);
1.95 nicm 401: alerts_check_session(s_new);
1.39 nicm 402: }
1.19 nicm 403: }
1.38 nicm 404: recalculate_sizes();
1.42 nicm 405: }
406:
407: void
1.75 nicm 408: server_check_unattached(void)
1.42 nicm 409: {
410: struct session *s;
411:
412: /*
413: * If any sessions are no longer attached and have destroy-unattached
414: * set, collect them.
415: */
1.47 nicm 416: RB_FOREACH(s, sessions, &sessions) {
417: if (!(s->flags & SESSION_UNATTACHED))
1.42 nicm 418: continue;
1.91 nicm 419: if (options_get_number (s->options, "destroy-unattached"))
1.109 nicm 420: session_destroy(s, __func__);
1.42 nicm 421: }
1.57 nicm 422: }
423:
424: /* Set stdin callback. */
425: int
426: server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
427: void *), void *cb_data, char **cause)
428: {
1.65 nicm 429: if (c == NULL || c->session != NULL) {
1.57 nicm 430: *cause = xstrdup("no client with stdin");
431: return (-1);
432: }
433: if (c->flags & CLIENT_TERMINAL) {
434: *cause = xstrdup("stdin is a tty");
435: return (-1);
436: }
437: if (c->stdin_callback != NULL) {
438: *cause = xstrdup("stdin in use");
439: return (-1);
440: }
441:
442: c->stdin_callback_data = cb_data;
443: c->stdin_callback = cb;
444:
445: c->references++;
446:
447: if (c->stdin_closed)
1.75 nicm 448: c->stdin_callback(c, 1, c->stdin_callback_data);
1.61 nicm 449:
1.90 nicm 450: proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
1.61 nicm 451:
1.57 nicm 452: return (0);
1.66 nicm 453: }
454:
455: void
456: server_unzoom_window(struct window *w)
457: {
1.80 nicm 458: if (window_unzoom(w) == 0) {
459: server_redraw_window(w);
460: server_status_window(w);
461: }
1.1 nicm 462: }