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