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