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