server-fn.c revision 1.2
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
27struct session *server_next_session(struct session *);
28void		server_callback_identify(int, short, void *);
29
30void
31server_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
43void
44server_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
56void
57server_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
72void
73server_redraw_client(struct client *c)
74{
75	c->flags |= CLIENT_REDRAW;
76}
77
78void
79server_status_client(struct client *c)
80{
81	c->flags |= CLIENT_STATUS;
82}
83
84void
85server_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
99void
100server_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
112void
113server_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
127void
128server_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
140void
141server_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
156void
157server_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
171void
172server_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
188void
189server_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
202void
203server_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
216void
217server_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
239void
240server_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
262int
263server_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
314void
315server_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
323void
324server_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
346void
347server_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
361struct session *
362server_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
377void
378server_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
405void
406server_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
422void
423server_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
441void
442server_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 */
452void
453server_callback_identify(unused int fd, unused short events, void *data)
454{
455	struct client	*c = data;
456
457	server_clear_identify(c);
458}
459
460void
461server_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