Annotation of src/usr.bin/tmux/server-fn.c, Revision 1.135
1.135 ! nicm 1: /* $OpenBSD: server-fn.c,v 1.134 2023/09/01 13:48:54 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.134 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: {
1.116 nicm 37: c->flags |= CLIENT_ALLREDRAWFLAGS;
1.1 nicm 38: }
39:
40: void
41: server_status_client(struct client *c)
42: {
1.116 nicm 43: c->flags |= CLIENT_REDRAWSTATUS;
1.1 nicm 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.116 nicm 112: c->flags |= CLIENT_REDRAWBORDERS;
1.33 nicm 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.111 nicm 167: if (*cmd == '\0' || 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.110 nicm 176: proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
1.114 nicm 177: }
178:
179: void
180: server_kill_pane(struct window_pane *wp)
181: {
182: struct window *w = wp->window;
183:
184: if (window_count_panes(w) == 1) {
1.127 nicm 185: server_kill_window(w, 1);
1.114 nicm 186: recalculate_sizes();
187: } else {
188: server_unzoom_window(w);
1.126 nicm 189: server_client_remove_pane(wp);
1.114 nicm 190: layout_close_pane(wp);
191: window_remove_pane(w, wp);
192: server_redraw_window(w);
193: }
1.8 nicm 194: }
195:
196: void
1.127 nicm 197: server_kill_window(struct window *w, int renumber)
1.8 nicm 198: {
1.127 nicm 199: struct session *s, *s1;
200: struct winlink *wl;
1.48 nicm 201:
1.127 nicm 202: RB_FOREACH_SAFE(s, sessions, &sessions, s1) {
1.83 nicm 203: if (!session_has(s, w))
1.8 nicm 204: continue;
1.127 nicm 205:
1.78 nicm 206: server_unzoom_window(w);
1.34 nicm 207: while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
208: if (session_detach(s, wl)) {
209: server_destroy_session_group(s);
210: break;
1.134 nicm 211: }
212: server_redraw_session_group(s);
1.34 nicm 213: }
1.56 nicm 214:
1.127 nicm 215: if (renumber)
216: server_renumber_session(s);
1.8 nicm 217: }
1.69 nicm 218: recalculate_sizes();
1.21 nicm 219: }
220:
1.127 nicm 221: void
222: server_renumber_session(struct session *s)
223: {
224: struct session_group *sg;
225:
226: if (options_get_number(s->options, "renumber-windows")) {
227: if ((sg = session_group_contains(s)) != NULL) {
228: TAILQ_FOREACH(s, &sg->sessions, gentry)
229: session_renumber_windows(s);
230: } else
231: session_renumber_windows(s);
232: }
233: }
234:
235: void
236: server_renumber_all(void)
237: {
238: struct session *s;
239:
240: RB_FOREACH(s, sessions, &sessions)
241: server_renumber_session(s);
242: }
243:
1.21 nicm 244: int
1.25 nicm 245: server_link_window(struct session *src, struct winlink *srcwl,
1.76 nicm 246: struct session *dst, int dstidx, int killflag, int selectflag,
247: char **cause)
1.21 nicm 248: {
1.25 nicm 249: struct winlink *dstwl;
250: struct session_group *srcsg, *dstsg;
251:
1.104 nicm 252: srcsg = session_group_contains(src);
253: dstsg = session_group_contains(dst);
1.25 nicm 254: if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
255: xasprintf(cause, "sessions are grouped");
256: return (-1);
257: }
1.21 nicm 258:
259: dstwl = NULL;
260: if (dstidx != -1)
261: dstwl = winlink_find_by_index(&dst->windows, dstidx);
262: if (dstwl != NULL) {
1.44 nicm 263: if (dstwl->window == srcwl->window) {
264: xasprintf(cause, "same index: %d", dstidx);
1.41 nicm 265: return (-1);
1.44 nicm 266: }
1.21 nicm 267: if (killflag) {
268: /*
269: * Can't use session_detach as it will destroy session
270: * if this makes it empty.
271: */
1.102 nicm 272: notify_session_window("window-unlinked", dst,
273: dstwl->window);
1.37 nicm 274: dstwl->flags &= ~WINLINK_ALERTFLAGS;
1.21 nicm 275: winlink_stack_remove(&dst->lastw, dstwl);
276: winlink_remove(&dst->windows, dstwl);
277:
278: /* Force select/redraw if current. */
1.26 nicm 279: if (dstwl == dst->curw) {
1.21 nicm 280: selectflag = 1;
1.26 nicm 281: dst->curw = NULL;
282: }
1.21 nicm 283: }
284: }
285:
286: if (dstidx == -1)
1.91 nicm 287: dstidx = -1 - options_get_number(dst->options, "base-index");
1.21 nicm 288: dstwl = session_attach(dst, srcwl->window, dstidx, cause);
289: if (dstwl == NULL)
290: return (-1);
291:
1.25 nicm 292: if (selectflag)
1.21 nicm 293: session_select(dst, dstwl->idx);
1.25 nicm 294: server_redraw_session_group(dst);
1.21 nicm 295:
296: return (0);
297: }
298:
299: void
300: server_unlink_window(struct session *s, struct winlink *wl)
301: {
302: if (session_detach(s, wl))
1.25 nicm 303: server_destroy_session_group(s);
304: else
305: server_redraw_session_group(s);
1.29 nicm 306: }
307:
308: void
1.102 nicm 309: server_destroy_pane(struct window_pane *wp, int notify)
1.29 nicm 310: {
1.51 nicm 311: struct window *w = wp->window;
312: struct screen_write_ctx ctx;
313: struct grid_cell gc;
1.129 nicm 314: int remain_on_exit;
1.133 nicm 315: const char *s;
316: char *expanded;
317: u_int sx = screen_size_x(&wp->base);
318: u_int sy = screen_size_y(&wp->base);
1.29 nicm 319:
1.36 nicm 320: if (wp->fd != -1) {
1.53 nicm 321: bufferevent_free(wp->event);
1.118 nicm 322: wp->event = NULL;
1.36 nicm 323: close(wp->fd);
324: wp->fd = -1;
325: }
1.29 nicm 326:
1.129 nicm 327: remain_on_exit = options_get_number(wp->options, "remain-on-exit");
328: if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
329: return;
330: switch (remain_on_exit) {
331: case 0:
332: break;
333: case 2:
334: if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
335: break;
336: /* FALLTHROUGH */
337: case 1:
1.112 nicm 338: if (wp->flags & PANE_STATUSDRAWN)
339: return;
340: wp->flags |= PANE_STATUSDRAWN;
341:
1.133 nicm 342: gettimeofday(&wp->dead_time, NULL);
1.102 nicm 343: if (notify)
344: notify_pane("pane-died", wp);
345:
1.133 nicm 346: s = options_get_string(wp->options, "remain-on-exit-format");
347: if (*s != '\0') {
348: screen_write_start_pane(&ctx, wp, &wp->base);
349: screen_write_scrollregion(&ctx, 0, sy - 1);
350: screen_write_cursormove(&ctx, 0, sy - 1, 0);
351: screen_write_linefeed(&ctx, 1, 8);
352: memcpy(&gc, &grid_default_cell, sizeof gc);
353:
354: expanded = format_single(NULL, s, NULL, NULL, NULL, wp);
355: format_draw(&ctx, &gc, sx, expanded, NULL, 0);
356: free(expanded);
357:
358: screen_write_stop(&ctx);
1.112 nicm 359: }
1.133 nicm 360: wp->base.mode &= ~MODE_CURSOR;
1.112 nicm 361:
1.51 nicm 362: wp->flags |= PANE_REDRAW;
1.29 nicm 363: return;
1.51 nicm 364: }
1.29 nicm 365:
1.102 nicm 366: if (notify)
367: notify_pane("pane-exited", wp);
368:
1.66 nicm 369: server_unzoom_window(w);
1.126 nicm 370: server_client_remove_pane(wp);
1.29 nicm 371: layout_close_pane(wp);
372: window_remove_pane(w, wp);
1.97 nicm 373:
1.29 nicm 374: if (TAILQ_EMPTY(&w->panes))
1.127 nicm 375: server_kill_window(w, 1);
1.29 nicm 376: else
377: server_redraw_window(w);
1.25 nicm 378: }
379:
1.101 nicm 380: static void
1.25 nicm 381: server_destroy_session_group(struct session *s)
382: {
383: struct session_group *sg;
1.71 nicm 384: struct session *s1;
1.25 nicm 385:
1.134 nicm 386: if ((sg = session_group_contains(s)) == NULL) {
1.21 nicm 387: server_destroy_session(s);
1.134 nicm 388: session_destroy(s, 1, __func__);
389: } else {
1.71 nicm 390: TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
1.25 nicm 391: server_destroy_session(s);
1.120 nicm 392: session_destroy(s, 1, __func__);
1.71 nicm 393: }
1.25 nicm 394: }
1.19 nicm 395: }
396:
1.100 nicm 397: static struct session *
1.134 nicm 398: server_find_session(struct session *s,
399: int (*f)(struct session *, struct session *))
1.39 nicm 400: {
1.130 nicm 401: struct session *s_loop, *s_out = NULL;
1.39 nicm 402:
1.47 nicm 403: RB_FOREACH(s_loop, sessions, &sessions) {
1.134 nicm 404: if (s_loop != s && (s_out == NULL || f(s_loop, s_out)))
1.39 nicm 405: s_out = s_loop;
406: }
407: return (s_out);
408: }
409:
1.134 nicm 410: static int
411: server_newer_session(struct session *s_loop, struct session *s_out)
1.130 nicm 412: {
1.134 nicm 413: return (timercmp(&s_loop->activity_time, &s_out->activity_time, <));
414: }
1.130 nicm 415:
1.134 nicm 416: static int
417: server_newer_detached_session(struct session *s_loop, struct session *s_out)
418: {
419: if (s_loop->attached)
420: return (0);
421: return (server_newer_session(s_loop, s_out));
1.130 nicm 422: }
423:
1.19 nicm 424: void
425: server_destroy_session(struct session *s)
426: {
427: struct client *c;
1.134 nicm 428: struct session *s_new = NULL;
1.130 nicm 429: int detach_on_destroy;
1.31 nicm 430:
1.130 nicm 431: detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
432: if (detach_on_destroy == 0)
1.134 nicm 433: s_new = server_find_session(s, server_newer_session);
1.130 nicm 434: else if (detach_on_destroy == 2)
1.134 nicm 435: s_new = server_find_session(s, server_newer_detached_session);
436: else if (detach_on_destroy == 3)
437: s_new = session_previous_session(s);
438: else if (detach_on_destroy == 4)
439: s_new = session_next_session(s);
440: if (s_new == s)
1.39 nicm 441: s_new = NULL;
1.84 nicm 442: TAILQ_FOREACH(c, &clients, entry) {
443: if (c->session != s)
1.19 nicm 444: continue;
1.134 nicm 445: c->session = NULL;
446: c->last_session = NULL;
1.132 nicm 447: server_client_set_session(c, s_new);
1.131 nicm 448: if (s_new == NULL)
1.40 nicm 449: c->flags |= CLIENT_EXIT;
1.19 nicm 450: }
1.38 nicm 451: recalculate_sizes();
1.42 nicm 452: }
453:
454: void
1.75 nicm 455: server_check_unattached(void)
1.42 nicm 456: {
1.135 ! nicm 457: struct session *s;
! 458: struct session_group *sg;
1.42 nicm 459:
460: /*
461: * If any sessions are no longer attached and have destroy-unattached
462: * set, collect them.
463: */
1.47 nicm 464: RB_FOREACH(s, sessions, &sessions) {
1.115 nicm 465: if (s->attached != 0)
1.42 nicm 466: continue;
1.135 ! nicm 467: switch (options_get_number(s->options, "destroy-unattached")) {
! 468: case 0: /* off */
! 469: continue;
! 470: case 1: /* on */
! 471: break;
! 472: case 2: /* keep-last */
! 473: sg = session_group_contains(s);
! 474: if (sg == NULL || session_group_count(sg) <= 1)
! 475: continue;
! 476: break;
! 477: case 3: /* keep-group */
! 478: sg = session_group_contains(s);
! 479: if (sg != NULL && session_group_count(sg) == 1)
! 480: continue;
! 481: break;
! 482: }
! 483: session_destroy(s, 1, __func__);
1.42 nicm 484: }
1.66 nicm 485: }
486:
487: void
488: server_unzoom_window(struct window *w)
489: {
1.113 nicm 490: if (window_unzoom(w) == 0)
1.80 nicm 491: server_redraw_window(w);
1.1 nicm 492: }