Annotation of src/usr.bin/tmux/server-fn.c, Revision 1.94
1.94 ! nicm 1: /* $OpenBSD: server-fn.c,v 1.93 2015/11/18 14:27:44 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
296: server_destroy_pane(struct window_pane *wp)
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.29 nicm 302:
1.51 nicm 303: old_fd = wp->fd;
1.36 nicm 304: if (wp->fd != -1) {
1.53 nicm 305: bufferevent_free(wp->event);
1.36 nicm 306: close(wp->fd);
307: wp->fd = -1;
308: }
1.29 nicm 309:
1.91 nicm 310: if (options_get_number(w->options, "remain-on-exit")) {
1.51 nicm 311: if (old_fd == -1)
312: return;
313: screen_write_start(&ctx, wp, &wp->base);
314: screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
315: screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
316: screen_write_linefeed(&ctx, 1);
317: memcpy(&gc, &grid_default_cell, sizeof gc);
318: gc.attr |= GRID_ATTR_BRIGHT;
319: screen_write_puts(&ctx, &gc, "Pane is dead");
320: screen_write_stop(&ctx);
321: wp->flags |= PANE_REDRAW;
1.29 nicm 322: return;
1.51 nicm 323: }
1.29 nicm 324:
1.66 nicm 325: server_unzoom_window(w);
1.29 nicm 326: layout_close_pane(wp);
327: window_remove_pane(w, wp);
328:
329: if (TAILQ_EMPTY(&w->panes))
330: server_kill_window(w);
331: else
332: server_redraw_window(w);
1.25 nicm 333: }
334:
335: void
336: server_destroy_session_group(struct session *s)
337: {
338: struct session_group *sg;
1.71 nicm 339: struct session *s1;
1.25 nicm 340:
341: if ((sg = session_group_find(s)) == NULL)
1.21 nicm 342: server_destroy_session(s);
1.25 nicm 343: else {
1.71 nicm 344: TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
1.25 nicm 345: server_destroy_session(s);
1.71 nicm 346: session_destroy(s);
347: }
1.25 nicm 348: }
1.19 nicm 349: }
350:
1.39 nicm 351: struct session *
352: server_next_session(struct session *s)
353: {
354: struct session *s_loop, *s_out;
355:
356: s_out = NULL;
1.47 nicm 357: RB_FOREACH(s_loop, sessions, &sessions) {
358: if (s_loop == s)
1.39 nicm 359: continue;
360: if (s_out == NULL ||
361: timercmp(&s_loop->activity_time, &s_out->activity_time, <))
362: s_out = s_loop;
363: }
364: return (s_out);
365: }
366:
1.19 nicm 367: void
368: server_destroy_session(struct session *s)
369: {
370: struct client *c;
1.39 nicm 371: struct session *s_new;
1.31 nicm 372:
1.91 nicm 373: if (!options_get_number(s->options, "detach-on-destroy"))
1.39 nicm 374: s_new = server_next_session(s);
375: else
376: s_new = NULL;
377:
1.84 nicm 378: TAILQ_FOREACH(c, &clients, entry) {
379: if (c->session != s)
1.19 nicm 380: continue;
1.39 nicm 381: if (s_new == NULL) {
382: c->session = NULL;
1.40 nicm 383: c->flags |= CLIENT_EXIT;
1.39 nicm 384: } else {
1.46 nicm 385: c->last_session = NULL;
1.39 nicm 386: c->session = s_new;
1.86 nicm 387: status_timer_start(c);
1.55 nicm 388: notify_attached_session_changed(c);
1.87 nicm 389: session_update_activity(s_new, NULL);
1.88 nicm 390: gettimeofday(&s_new->last_attached_time, NULL);
1.39 nicm 391: server_redraw_client(c);
392: }
1.19 nicm 393: }
1.38 nicm 394: recalculate_sizes();
1.42 nicm 395: }
396:
397: void
1.75 nicm 398: server_check_unattached(void)
1.42 nicm 399: {
400: struct session *s;
401:
402: /*
403: * If any sessions are no longer attached and have destroy-unattached
404: * set, collect them.
405: */
1.47 nicm 406: RB_FOREACH(s, sessions, &sessions) {
407: if (!(s->flags & SESSION_UNATTACHED))
1.42 nicm 408: continue;
1.91 nicm 409: if (options_get_number (s->options, "destroy-unattached"))
1.42 nicm 410: session_destroy(s);
411: }
1.15 nicm 412: }
413:
414: void
415: server_set_identify(struct client *c)
416: {
417: struct timeval tv;
418: int delay;
419:
1.91 nicm 420: delay = options_get_number(c->session->options, "display-panes-time");
1.15 nicm 421: tv.tv_sec = delay / 1000;
422: tv.tv_usec = (delay % 1000) * 1000L;
1.31 nicm 423:
1.75 nicm 424: if (event_initialized(&c->identify_timer))
1.54 nicm 425: evtimer_del(&c->identify_timer);
1.28 nicm 426: evtimer_set(&c->identify_timer, server_callback_identify, c);
427: evtimer_add(&c->identify_timer, &tv);
1.15 nicm 428:
429: c->flags |= CLIENT_IDENTIFY;
430: c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
431: server_redraw_client(c);
432: }
433:
434: void
435: server_clear_identify(struct client *c)
436: {
437: if (c->flags & CLIENT_IDENTIFY) {
438: c->flags &= ~CLIENT_IDENTIFY;
439: c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
440: server_redraw_client(c);
441: }
1.28 nicm 442: }
443:
444: void
1.93 nicm 445: server_callback_identify(__unused int fd, __unused short events, void *data)
1.28 nicm 446: {
447: struct client *c = data;
448:
449: server_clear_identify(c);
1.57 nicm 450: }
451:
452: /* Set stdin callback. */
453: int
454: server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
455: void *), void *cb_data, char **cause)
456: {
1.65 nicm 457: if (c == NULL || c->session != NULL) {
1.57 nicm 458: *cause = xstrdup("no client with stdin");
459: return (-1);
460: }
461: if (c->flags & CLIENT_TERMINAL) {
462: *cause = xstrdup("stdin is a tty");
463: return (-1);
464: }
465: if (c->stdin_callback != NULL) {
466: *cause = xstrdup("stdin in use");
467: return (-1);
468: }
469:
470: c->stdin_callback_data = cb_data;
471: c->stdin_callback = cb;
472:
473: c->references++;
474:
475: if (c->stdin_closed)
1.75 nicm 476: c->stdin_callback(c, 1, c->stdin_callback_data);
1.61 nicm 477:
1.90 nicm 478: proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
1.61 nicm 479:
1.57 nicm 480: return (0);
1.66 nicm 481: }
482:
483: void
484: server_unzoom_window(struct window *w)
485: {
1.80 nicm 486: if (window_unzoom(w) == 0) {
487: server_redraw_window(w);
488: server_status_window(w);
489: }
1.1 nicm 490: }