xref: /netbsd-src/external/bsd/tmux/dist/server-fn.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
1 /* $Id: server-fn.c,v 1.2 2011/03/12 03:02:59 christos Exp $ */
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>
22 #include <time.h>
23 #include <unistd.h>
24 
25 #include "tmux.h"
26 
27 struct session *server_next_session(struct session *);
28 void		server_callback_identify(int, short, void *);
29 
30 void
31 server_fill_environ(struct session *s, struct environ *env)
32 {
33 	char	tmuxvar[MAXPATHLEN], *term;
34 
35 	xsnprintf(tmuxvar, sizeof tmuxvar,
36 	    "%s,%ld,%u", socket_path, (long) getpid(), s->idx);
37 	environ_set(env, "TMUX", tmuxvar);
38 
39 	term = options_get_string(&s->options, "default-terminal");
40 	environ_set(env, "TERM", term);
41 }
42 
43 void
44 server_write_client(
45     struct client *c, enum msgtype type, const void *buf, size_t len)
46 {
47 	struct imsgbuf	*ibuf = &c->ibuf;
48 
49 	if (c->flags & CLIENT_BAD)
50 		return;
51 	log_debug("writing %d to client %d", type, c->ibuf.fd);
52 	imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, __UNCONST(buf), len);
53 	server_update_event(c);
54 }
55 
56 void
57 server_write_session(
58     struct session *s, enum msgtype type, const void *buf, size_t len)
59 {
60 	struct client	*c;
61 	u_int		 i;
62 
63 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
64 		c = ARRAY_ITEM(&clients, i);
65 		if (c == NULL || c->session == NULL)
66 			continue;
67 		if (c->session == s)
68 			server_write_client(c, type, buf, len);
69 	}
70 }
71 
72 void
73 server_redraw_client(struct client *c)
74 {
75 	c->flags |= CLIENT_REDRAW;
76 }
77 
78 void
79 server_status_client(struct client *c)
80 {
81 	c->flags |= CLIENT_STATUS;
82 }
83 
84 void
85 server_redraw_session(struct session *s)
86 {
87 	struct client	*c;
88 	u_int		 i;
89 
90 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
91 		c = ARRAY_ITEM(&clients, i);
92 		if (c == NULL || c->session == NULL)
93 			continue;
94 		if (c->session == s)
95 			server_redraw_client(c);
96 	}
97 }
98 
99 void
100 server_redraw_session_group(struct session *s)
101 {
102 	struct session_group	*sg;
103 
104 	if ((sg = session_group_find(s)) == NULL)
105 		server_redraw_session(s);
106 	else {
107 		TAILQ_FOREACH(s, &sg->sessions, gentry)
108 			server_redraw_session(s);
109 	}
110 }
111 
112 void
113 server_status_session(struct session *s)
114 {
115 	struct client	*c;
116 	u_int		 i;
117 
118 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
119 		c = ARRAY_ITEM(&clients, i);
120 		if (c == NULL || c->session == NULL)
121 			continue;
122 		if (c->session == s)
123 			server_status_client(c);
124 	}
125 }
126 
127 void
128 server_status_session_group(struct session *s)
129 {
130 	struct session_group	*sg;
131 
132 	if ((sg = session_group_find(s)) == NULL)
133 		server_status_session(s);
134 	else {
135 		TAILQ_FOREACH(s, &sg->sessions, gentry)
136 			server_status_session(s);
137 	}
138 }
139 
140 void
141 server_redraw_window(struct window *w)
142 {
143 	struct client	*c;
144 	u_int		 i;
145 
146 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
147 		c = ARRAY_ITEM(&clients, i);
148 		if (c == NULL || c->session == NULL)
149 			continue;
150 		if (c->session->curw->window == w)
151 			server_redraw_client(c);
152 	}
153 	w->flags |= WINDOW_REDRAW;
154 }
155 
156 void
157 server_redraw_window_borders(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 			c->flags |= CLIENT_BORDERS;
168 	}
169 }
170 
171 void
172 server_status_window(struct window *w)
173 {
174 	struct session	*s;
175 
176 	/*
177 	 * This is slightly different. We want to redraw the status line of any
178 	 * clients containing this window rather than any where it is the
179 	 * current window.
180 	 */
181 
182 	RB_FOREACH(s, sessions, &sessions) {
183 		if (session_has(s, w) != NULL)
184 			server_status_session(s);
185 	}
186 }
187 
188 void
189 server_lock(void)
190 {
191 	struct client	*c;
192 	u_int		 i;
193 
194 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
195 		c = ARRAY_ITEM(&clients, i);
196 		if (c == NULL || c->session == NULL)
197 			continue;
198 		server_lock_client(c);
199 	}
200 }
201 
202 void
203 server_lock_session(struct session *s)
204 {
205 	struct client	*c;
206 	u_int		 i;
207 
208 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
209 		c = ARRAY_ITEM(&clients, i);
210 		if (c == NULL || c->session == NULL || c->session != s)
211 			continue;
212 		server_lock_client(c);
213 	}
214 }
215 
216 void
217 server_lock_client(struct client *c)
218 {
219 	const char		*cmd;
220 	size_t			 cmdlen;
221 	struct msg_lock_data	 lockdata;
222 
223 	if (c->flags & CLIENT_SUSPENDED)
224 		return;
225 
226 	cmd = options_get_string(&c->session->options, "lock-command");
227 	cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd);
228 	if (cmdlen >= sizeof lockdata.cmd)
229 		return;
230 
231 	tty_stop_tty(&c->tty);
232 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
233 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
234 
235 	c->flags |= CLIENT_SUSPENDED;
236 	server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata);
237 }
238 
239 void
240 server_kill_window(struct window *w)
241 {
242 	struct session	*s, *next_s;
243 	struct winlink	*wl;
244 
245 	next_s = RB_MIN(sessions, &sessions);
246 	while (next_s != NULL) {
247 		s = next_s;
248 		next_s = RB_NEXT(sessions, &sessions, s);
249 
250 		if (session_has(s, w) == NULL)
251 			continue;
252 		while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
253 			if (session_detach(s, wl)) {
254 				server_destroy_session_group(s);
255 				break;
256 			} else
257 				server_redraw_session_group(s);
258 		}
259 	}
260 }
261 
262 int
263 server_link_window(struct session *src, struct winlink *srcwl,
264     struct session *dst, int dstidx, int killflag, int selectflag, char **cause)
265 {
266 	struct winlink		*dstwl;
267 	struct session_group	*srcsg, *dstsg;
268 
269 	srcsg = session_group_find(src);
270 	dstsg = session_group_find(dst);
271 	if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
272 		xasprintf(cause, "sessions are grouped");
273 		return (-1);
274 	}
275 
276 	dstwl = NULL;
277 	if (dstidx != -1)
278 		dstwl = winlink_find_by_index(&dst->windows, dstidx);
279 	if (dstwl != NULL) {
280 		if (dstwl->window == srcwl->window) {
281 			xasprintf(cause, "same index: %d", dstidx);
282 			return (-1);
283 		}
284 		if (killflag) {
285 			/*
286 			 * Can't use session_detach as it will destroy session
287 			 * if this makes it empty.
288 			 */
289 			dstwl->flags &= ~WINLINK_ALERTFLAGS;
290 			winlink_stack_remove(&dst->lastw, dstwl);
291 			winlink_remove(&dst->windows, dstwl);
292 
293 			/* Force select/redraw if current. */
294 			if (dstwl == dst->curw) {
295 				selectflag = 1;
296 				dst->curw = NULL;
297 			}
298 		}
299 	}
300 
301 	if (dstidx == -1)
302 		dstidx = -1 - options_get_number(&dst->options, "base-index");
303 	dstwl = session_attach(dst, srcwl->window, dstidx, cause);
304 	if (dstwl == NULL)
305 		return (-1);
306 
307 	if (selectflag)
308 		session_select(dst, dstwl->idx);
309 	server_redraw_session_group(dst);
310 
311 	return (0);
312 }
313 
314 void
315 server_unlink_window(struct session *s, struct winlink *wl)
316 {
317 	if (session_detach(s, wl))
318 		server_destroy_session_group(s);
319 	else
320 		server_redraw_session_group(s);
321 }
322 
323 void
324 server_destroy_pane(struct window_pane *wp)
325 {
326 	struct window	*w = wp->window;
327 
328 	if (wp->fd != -1) {
329 		close(wp->fd);
330 		bufferevent_free(wp->event);
331 		wp->fd = -1;
332 	}
333 
334 	if (options_get_number(&w->options, "remain-on-exit"))
335 		return;
336 
337 	layout_close_pane(wp);
338 	window_remove_pane(w, wp);
339 
340 	if (TAILQ_EMPTY(&w->panes))
341 		server_kill_window(w);
342 	else
343 		server_redraw_window(w);
344 }
345 
346 void
347 server_destroy_session_group(struct session *s)
348 {
349 	struct session_group	*sg;
350 
351 	if ((sg = session_group_find(s)) == NULL)
352 		server_destroy_session(s);
353 	else {
354 		TAILQ_FOREACH(s, &sg->sessions, gentry)
355 			server_destroy_session(s);
356 		TAILQ_REMOVE(&session_groups, sg, entry);
357 		xfree(sg);
358 	}
359 }
360 
361 struct session *
362 server_next_session(struct session *s)
363 {
364 	struct session *s_loop, *s_out;
365 
366 	s_out = NULL;
367 	RB_FOREACH(s_loop, sessions, &sessions) {
368 		if (s_loop == s)
369 			continue;
370 		if (s_out == NULL ||
371 		    timercmp(&s_loop->activity_time, &s_out->activity_time, <))
372 			s_out = s_loop;
373 	}
374 	return (s_out);
375 }
376 
377 void
378 server_destroy_session(struct session *s)
379 {
380 	struct client	*c;
381 	struct session	*s_new;
382 	u_int		 i;
383 
384 	if (!options_get_number(&s->options, "detach-on-destroy"))
385 		s_new = server_next_session(s);
386 	else
387 		s_new = NULL;
388 
389 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
390 		c = ARRAY_ITEM(&clients, i);
391 		if (c == NULL || c->session != s)
392 			continue;
393 		if (s_new == NULL) {
394 			c->session = NULL;
395 			c->flags |= CLIENT_EXIT;
396 		} else {
397 			c->last_session = NULL;
398 			c->session = s_new;
399 			server_redraw_client(c);
400 		}
401 	}
402 	recalculate_sizes();
403 }
404 
405 void
406 server_check_unattached (void)
407 {
408 	struct session	*s;
409 
410 	/*
411 	 * If any sessions are no longer attached and have destroy-unattached
412 	 * set, collect them.
413 	 */
414 	RB_FOREACH(s, sessions, &sessions) {
415 		if (!(s->flags & SESSION_UNATTACHED))
416 			continue;
417 		if (options_get_number (&s->options, "destroy-unattached"))
418 			session_destroy(s);
419 	}
420 }
421 
422 void
423 server_set_identify(struct client *c)
424 {
425 	struct timeval	tv;
426 	int		delay;
427 
428 	delay = options_get_number(&c->session->options, "display-panes-time");
429 	tv.tv_sec = delay / 1000;
430 	tv.tv_usec = (delay % 1000) * 1000L;
431 
432 	evtimer_del(&c->identify_timer);
433 	evtimer_set(&c->identify_timer, server_callback_identify, c);
434 	evtimer_add(&c->identify_timer, &tv);
435 
436 	c->flags |= CLIENT_IDENTIFY;
437 	c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
438 	server_redraw_client(c);
439 }
440 
441 void
442 server_clear_identify(struct client *c)
443 {
444 	if (c->flags & CLIENT_IDENTIFY) {
445 		c->flags &= ~CLIENT_IDENTIFY;
446 		c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
447 		server_redraw_client(c);
448 	}
449 }
450 
451 /* ARGSUSED */
452 void
453 server_callback_identify(unused int fd, unused short events, void *data)
454 {
455 	struct client	*c = data;
456 
457 	server_clear_identify(c);
458 }
459 
460 void
461 server_update_event(struct client *c)
462 {
463 	short	events;
464 
465 	events = 0;
466 	if (!(c->flags & CLIENT_BAD))
467 		events |= EV_READ;
468 	if (c->ibuf.w.queued > 0)
469 		events |= EV_WRITE;
470 	event_del(&c->event);
471 	event_set(&c->event, c->ibuf.fd, events, server_client_callback, c);
472 	event_add(&c->event, NULL);
473 }
474