Annotation of src/usr.bin/tmux/server-fn.c, Revision 1.57
1.57 ! nicm 1: /* $OpenBSD: server-fn.c,v 1.56 2012/04/29 17:20:01 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>
20:
21: #include <string.h>
1.5 nicm 22: #include <time.h>
1.1 nicm 23: #include <unistd.h>
24:
25: #include "tmux.h"
26:
1.39 nicm 27: struct session *server_next_session(struct session *);
28: void server_callback_identify(int, short, void *);
1.28 nicm 29:
1.13 nicm 30: void
31: server_fill_environ(struct session *s, struct environ *env)
1.1 nicm 32: {
1.50 nicm 33: char var[MAXPATHLEN], *term;
34: u_int idx;
35: long pid;
36:
37: if (s != NULL) {
38: term = options_get_string(&s->options, "default-terminal");
39: environ_set(env, "TERM", term);
40:
41: idx = s->idx;
42: } else
43: idx = -1;
44: pid = getpid();
45: xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx);
46: environ_set(env, "TMUX", var);
1.10 nicm 47: }
48:
1.57 ! nicm 49: int
1.1 nicm 50: server_write_client(
1.11 nicm 51: struct client *c, enum msgtype type, const void *buf, size_t len)
1.1 nicm 52: {
1.14 nicm 53: struct imsgbuf *ibuf = &c->ibuf;
1.57 ! nicm 54: int error;
1.1 nicm 55:
1.12 nicm 56: if (c->flags & CLIENT_BAD)
1.57 ! nicm 57: return (-1);
1.14 nicm 58: log_debug("writing %d to client %d", type, c->ibuf.fd);
1.57 ! nicm 59: error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1,
! 60: (void *) buf, len);
! 61: if (error == 1)
! 62: server_update_event(c);
! 63: return (error == 1 ? 0 : -1);
1.1 nicm 64: }
65:
66: void
67: server_write_session(
1.11 nicm 68: struct session *s, enum msgtype type, const void *buf, size_t len)
1.1 nicm 69: {
70: struct client *c;
71: u_int i;
72:
73: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
74: c = ARRAY_ITEM(&clients, i);
75: if (c == NULL || c->session == NULL)
76: continue;
77: if (c->session == s)
78: server_write_client(c, type, buf, len);
79: }
80: }
81:
82: void
83: server_redraw_client(struct client *c)
84: {
85: c->flags |= CLIENT_REDRAW;
86: }
87:
88: void
89: server_status_client(struct client *c)
90: {
91: c->flags |= CLIENT_STATUS;
92: }
93:
94: void
95: server_redraw_session(struct session *s)
96: {
97: struct client *c;
98: u_int i;
99:
100: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
101: c = ARRAY_ITEM(&clients, i);
102: if (c == NULL || c->session == NULL)
103: continue;
104: if (c->session == s)
105: server_redraw_client(c);
106: }
107: }
108:
109: void
1.25 nicm 110: server_redraw_session_group(struct session *s)
111: {
112: struct session_group *sg;
113:
114: if ((sg = session_group_find(s)) == NULL)
115: server_redraw_session(s);
116: else {
117: TAILQ_FOREACH(s, &sg->sessions, gentry)
118: server_redraw_session(s);
119: }
120: }
121:
122: void
1.1 nicm 123: server_status_session(struct session *s)
124: {
125: struct client *c;
126: u_int i;
127:
128: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
129: c = ARRAY_ITEM(&clients, i);
130: if (c == NULL || c->session == NULL)
131: continue;
132: if (c->session == s)
133: server_status_client(c);
134: }
135: }
136:
137: void
1.25 nicm 138: server_status_session_group(struct session *s)
139: {
140: struct session_group *sg;
141:
142: if ((sg = session_group_find(s)) == NULL)
143: server_status_session(s);
144: else {
145: TAILQ_FOREACH(s, &sg->sessions, gentry)
146: server_status_session(s);
147: }
148: }
149:
150: void
1.1 nicm 151: server_redraw_window(struct window *w)
152: {
153: struct client *c;
154: u_int i;
155:
156: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
157: c = ARRAY_ITEM(&clients, i);
158: if (c == NULL || c->session == NULL)
159: continue;
160: if (c->session->curw->window == w)
161: server_redraw_client(c);
162: }
163: w->flags |= WINDOW_REDRAW;
1.33 nicm 164: }
165:
166: void
167: server_redraw_window_borders(struct window *w)
168: {
169: struct client *c;
170: u_int i;
171:
172: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
173: c = ARRAY_ITEM(&clients, i);
174: if (c == NULL || c->session == NULL)
175: continue;
176: if (c->session->curw->window == w)
177: c->flags |= CLIENT_BORDERS;
178: }
1.1 nicm 179: }
180:
181: void
182: server_status_window(struct window *w)
183: {
184: struct session *s;
185:
186: /*
187: * This is slightly different. We want to redraw the status line of any
188: * clients containing this window rather than any where it is the
189: * current window.
190: */
191:
1.47 nicm 192: RB_FOREACH(s, sessions, &sessions) {
193: if (session_has(s, w) != NULL)
1.1 nicm 194: server_status_session(s);
195: }
196: }
197:
198: void
199: server_lock(void)
200: {
1.23 nicm 201: struct client *c;
202: u_int i;
1.17 nicm 203:
1.1 nicm 204: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
205: c = ARRAY_ITEM(&clients, i);
206: if (c == NULL || c->session == NULL)
207: continue;
1.23 nicm 208: server_lock_client(c);
209: }
210: }
1.1 nicm 211:
1.23 nicm 212: void
213: server_lock_session(struct session *s)
214: {
215: struct client *c;
216: u_int i;
217:
218: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
219: c = ARRAY_ITEM(&clients, i);
220: if (c == NULL || c->session == NULL || c->session != s)
221: continue;
222: server_lock_client(c);
1.31 nicm 223: }
1.23 nicm 224: }
1.1 nicm 225:
1.23 nicm 226: void
227: server_lock_client(struct client *c)
228: {
229: const char *cmd;
230: size_t cmdlen;
231: struct msg_lock_data lockdata;
1.24 nicm 232:
233: if (c->flags & CLIENT_SUSPENDED)
234: return;
1.1 nicm 235:
1.23 nicm 236: cmd = options_get_string(&c->session->options, "lock-command");
237: cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd);
238: if (cmdlen >= sizeof lockdata.cmd)
239: return;
1.31 nicm 240:
1.23 nicm 241: tty_stop_tty(&c->tty);
242: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
243: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
1.52 nicm 244: tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
1.23 nicm 245:
246: c->flags |= CLIENT_SUSPENDED;
247: server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata);
1.8 nicm 248: }
249:
250: void
251: server_kill_window(struct window *w)
252: {
1.48 nicm 253: struct session *s, *next_s;
1.8 nicm 254: struct winlink *wl;
1.31 nicm 255:
1.48 nicm 256: next_s = RB_MIN(sessions, &sessions);
257: while (next_s != NULL) {
258: s = next_s;
259: next_s = RB_NEXT(sessions, &sessions, s);
260:
1.47 nicm 261: if (session_has(s, w) == NULL)
1.8 nicm 262: continue;
1.34 nicm 263: while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
264: if (session_detach(s, wl)) {
265: server_destroy_session_group(s);
266: break;
267: } else
268: server_redraw_session_group(s);
269: }
1.56 nicm 270:
271: if (options_get_number(&s->options, "renumber-windows"))
272: session_renumber_windows(s);
1.8 nicm 273: }
1.21 nicm 274: }
275:
276: int
1.25 nicm 277: server_link_window(struct session *src, struct winlink *srcwl,
278: struct session *dst, int dstidx, int killflag, int selectflag, char **cause)
1.21 nicm 279: {
1.25 nicm 280: struct winlink *dstwl;
281: struct session_group *srcsg, *dstsg;
282:
283: srcsg = session_group_find(src);
284: dstsg = session_group_find(dst);
285: if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
286: xasprintf(cause, "sessions are grouped");
287: return (-1);
288: }
1.21 nicm 289:
290: dstwl = NULL;
291: if (dstidx != -1)
292: dstwl = winlink_find_by_index(&dst->windows, dstidx);
293: if (dstwl != NULL) {
1.44 nicm 294: if (dstwl->window == srcwl->window) {
295: xasprintf(cause, "same index: %d", dstidx);
1.41 nicm 296: return (-1);
1.44 nicm 297: }
1.21 nicm 298: if (killflag) {
299: /*
300: * Can't use session_detach as it will destroy session
301: * if this makes it empty.
302: */
1.55 nicm 303: notify_window_unlinked(dst, dstwl->window);
1.37 nicm 304: dstwl->flags &= ~WINLINK_ALERTFLAGS;
1.21 nicm 305: winlink_stack_remove(&dst->lastw, dstwl);
306: winlink_remove(&dst->windows, dstwl);
307:
308: /* Force select/redraw if current. */
1.26 nicm 309: if (dstwl == dst->curw) {
1.21 nicm 310: selectflag = 1;
1.26 nicm 311: dst->curw = NULL;
312: }
1.21 nicm 313: }
314: }
315:
316: if (dstidx == -1)
317: dstidx = -1 - options_get_number(&dst->options, "base-index");
318: dstwl = session_attach(dst, srcwl->window, dstidx, cause);
319: if (dstwl == NULL)
320: return (-1);
321:
1.25 nicm 322: if (selectflag)
1.21 nicm 323: session_select(dst, dstwl->idx);
1.25 nicm 324: server_redraw_session_group(dst);
1.21 nicm 325:
326: return (0);
327: }
328:
329: void
330: server_unlink_window(struct session *s, struct winlink *wl)
331: {
332: if (session_detach(s, wl))
1.25 nicm 333: server_destroy_session_group(s);
334: else
335: server_redraw_session_group(s);
1.29 nicm 336: }
337:
338: void
339: server_destroy_pane(struct window_pane *wp)
340: {
1.51 nicm 341: struct window *w = wp->window;
342: int old_fd;
343: struct screen_write_ctx ctx;
344: struct grid_cell gc;
1.29 nicm 345:
1.51 nicm 346: old_fd = wp->fd;
1.36 nicm 347: if (wp->fd != -1) {
1.53 nicm 348: bufferevent_free(wp->event);
1.36 nicm 349: close(wp->fd);
350: wp->fd = -1;
351: }
1.29 nicm 352:
1.51 nicm 353: if (options_get_number(&w->options, "remain-on-exit")) {
354: if (old_fd == -1)
355: return;
356: screen_write_start(&ctx, wp, &wp->base);
357: screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
358: screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
359: screen_write_linefeed(&ctx, 1);
360: memcpy(&gc, &grid_default_cell, sizeof gc);
361: gc.attr |= GRID_ATTR_BRIGHT;
362: screen_write_puts(&ctx, &gc, "Pane is dead");
363: screen_write_stop(&ctx);
364: wp->flags |= PANE_REDRAW;
1.29 nicm 365: return;
1.51 nicm 366: }
1.29 nicm 367:
368: layout_close_pane(wp);
369: window_remove_pane(w, wp);
370:
371: if (TAILQ_EMPTY(&w->panes))
372: server_kill_window(w);
373: else
374: server_redraw_window(w);
1.25 nicm 375: }
376:
377: void
378: server_destroy_session_group(struct session *s)
379: {
380: struct session_group *sg;
381:
382: if ((sg = session_group_find(s)) == NULL)
1.21 nicm 383: server_destroy_session(s);
1.25 nicm 384: else {
385: TAILQ_FOREACH(s, &sg->sessions, gentry)
386: server_destroy_session(s);
387: TAILQ_REMOVE(&session_groups, sg, entry);
388: xfree(sg);
389: }
1.19 nicm 390: }
391:
1.39 nicm 392: struct session *
393: server_next_session(struct session *s)
394: {
395: struct session *s_loop, *s_out;
396:
397: s_out = NULL;
1.47 nicm 398: RB_FOREACH(s_loop, sessions, &sessions) {
399: if (s_loop == s)
1.39 nicm 400: continue;
401: if (s_out == NULL ||
402: timercmp(&s_loop->activity_time, &s_out->activity_time, <))
403: s_out = s_loop;
404: }
405: return (s_out);
406: }
407:
1.19 nicm 408: void
409: server_destroy_session(struct session *s)
410: {
411: struct client *c;
1.39 nicm 412: struct session *s_new;
1.19 nicm 413: u_int i;
1.31 nicm 414:
1.39 nicm 415: if (!options_get_number(&s->options, "detach-on-destroy"))
416: s_new = server_next_session(s);
417: else
418: s_new = NULL;
419:
1.19 nicm 420: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
421: c = ARRAY_ITEM(&clients, i);
422: if (c == NULL || c->session != s)
423: continue;
1.39 nicm 424: if (s_new == NULL) {
425: c->session = NULL;
1.40 nicm 426: c->flags |= CLIENT_EXIT;
1.39 nicm 427: } else {
1.46 nicm 428: c->last_session = NULL;
1.39 nicm 429: c->session = s_new;
1.55 nicm 430: notify_attached_session_changed(c);
1.49 nicm 431: session_update_activity(s_new);
1.39 nicm 432: server_redraw_client(c);
433: }
1.19 nicm 434: }
1.38 nicm 435: recalculate_sizes();
1.42 nicm 436: }
437:
438: void
439: server_check_unattached (void)
440: {
441: struct session *s;
442:
443: /*
444: * If any sessions are no longer attached and have destroy-unattached
445: * set, collect them.
446: */
1.47 nicm 447: RB_FOREACH(s, sessions, &sessions) {
448: if (!(s->flags & SESSION_UNATTACHED))
1.42 nicm 449: continue;
450: if (options_get_number (&s->options, "destroy-unattached"))
451: session_destroy(s);
452: }
1.15 nicm 453: }
454:
455: void
456: server_set_identify(struct client *c)
457: {
458: struct timeval tv;
459: int delay;
460:
461: delay = options_get_number(&c->session->options, "display-panes-time");
462: tv.tv_sec = delay / 1000;
463: tv.tv_usec = (delay % 1000) * 1000L;
1.31 nicm 464:
1.54 nicm 465: if (event_initialized (&c->identify_timer))
466: evtimer_del(&c->identify_timer);
1.28 nicm 467: evtimer_set(&c->identify_timer, server_callback_identify, c);
468: evtimer_add(&c->identify_timer, &tv);
1.15 nicm 469:
470: c->flags |= CLIENT_IDENTIFY;
471: c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
472: server_redraw_client(c);
473: }
474:
475: void
476: server_clear_identify(struct client *c)
477: {
478: if (c->flags & CLIENT_IDENTIFY) {
479: c->flags &= ~CLIENT_IDENTIFY;
480: c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
481: server_redraw_client(c);
482: }
1.28 nicm 483: }
484:
1.30 nicm 485: /* ARGSUSED */
1.28 nicm 486: void
487: server_callback_identify(unused int fd, unused short events, void *data)
488: {
489: struct client *c = data;
490:
491: server_clear_identify(c);
1.27 nicm 492: }
493:
494: void
495: server_update_event(struct client *c)
496: {
497: short events;
498:
499: events = 0;
500: if (!(c->flags & CLIENT_BAD))
501: events |= EV_READ;
502: if (c->ibuf.w.queued > 0)
503: events |= EV_WRITE;
1.54 nicm 504: if (event_initialized(&c->event))
505: event_del(&c->event);
1.27 nicm 506: event_set(&c->event, c->ibuf.fd, events, server_client_callback, c);
1.31 nicm 507: event_add(&c->event, NULL);
1.57 ! nicm 508: }
! 509:
! 510: /* Push stdout to client if possible. */
! 511: void
! 512: server_push_stdout(struct client *c)
! 513: {
! 514: struct msg_stdout_data data;
! 515: size_t size;
! 516:
! 517: size = EVBUFFER_LENGTH(c->stdout_data);
! 518: if (size == 0)
! 519: return;
! 520: if (size > sizeof data.data)
! 521: size = sizeof data.data;
! 522:
! 523: memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size);
! 524: data.size = size;
! 525:
! 526: if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0)
! 527: evbuffer_drain(c->stdout_data, size);
! 528: }
! 529:
! 530: /* Push stderr to client if possible. */
! 531: void
! 532: server_push_stderr(struct client *c)
! 533: {
! 534: struct msg_stderr_data data;
! 535: size_t size;
! 536:
! 537: size = EVBUFFER_LENGTH(c->stderr_data);
! 538: if (size == 0)
! 539: return;
! 540: if (size > sizeof data.data)
! 541: size = sizeof data.data;
! 542:
! 543: memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size);
! 544: data.size = size;
! 545:
! 546: if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0)
! 547: evbuffer_drain(c->stderr_data, size);
! 548: }
! 549:
! 550: /* Set stdin callback. */
! 551: int
! 552: server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
! 553: void *), void *cb_data, char **cause)
! 554: {
! 555: if (c == NULL) {
! 556: *cause = xstrdup("no client with stdin");
! 557: return (-1);
! 558: }
! 559: if (c->flags & CLIENT_TERMINAL) {
! 560: *cause = xstrdup("stdin is a tty");
! 561: return (-1);
! 562: }
! 563: if (c->stdin_callback != NULL) {
! 564: *cause = xstrdup("stdin in use");
! 565: return (-1);
! 566: }
! 567:
! 568: c->stdin_callback_data = cb_data;
! 569: c->stdin_callback = cb;
! 570:
! 571: c->references++;
! 572:
! 573: if (c->stdin_closed)
! 574: c->stdin_callback (c, 1, c->stdin_callback_data);
! 575: return (0);
1.1 nicm 576: }