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