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