GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/server-fn.c Lines: 0 173 0.0 %
Date: 2017-11-07 Branches: 0 138 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: server-fn.c,v 1.111 2017/08/29 09:18:48 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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
#include <sys/queue.h>
21
#include <sys/uio.h>
22
23
#include <imsg.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <time.h>
27
#include <unistd.h>
28
29
#include "tmux.h"
30
31
static struct session	*server_next_session(struct session *);
32
static void		 server_destroy_session_group(struct session *);
33
34
void
35
server_redraw_client(struct client *c)
36
{
37
	c->flags |= CLIENT_REDRAW;
38
}
39
40
void
41
server_status_client(struct client *c)
42
{
43
	c->flags |= CLIENT_STATUS;
44
}
45
46
void
47
server_redraw_session(struct session *s)
48
{
49
	struct client	*c;
50
51
	TAILQ_FOREACH(c, &clients, entry) {
52
		if (c->session == s)
53
			server_redraw_client(c);
54
	}
55
}
56
57
void
58
server_redraw_session_group(struct session *s)
59
{
60
	struct session_group	*sg;
61
62
	if ((sg = session_group_contains(s)) == NULL)
63
		server_redraw_session(s);
64
	else {
65
		TAILQ_FOREACH(s, &sg->sessions, gentry)
66
			server_redraw_session(s);
67
	}
68
}
69
70
void
71
server_status_session(struct session *s)
72
{
73
	struct client	*c;
74
75
	TAILQ_FOREACH(c, &clients, entry) {
76
		if (c->session == s)
77
			server_status_client(c);
78
	}
79
}
80
81
void
82
server_status_session_group(struct session *s)
83
{
84
	struct session_group	*sg;
85
86
	if ((sg = session_group_contains(s)) == NULL)
87
		server_status_session(s);
88
	else {
89
		TAILQ_FOREACH(s, &sg->sessions, gentry)
90
			server_status_session(s);
91
	}
92
}
93
94
void
95
server_redraw_window(struct window *w)
96
{
97
	struct client	*c;
98
99
	TAILQ_FOREACH(c, &clients, entry) {
100
		if (c->session != NULL && c->session->curw->window == w)
101
			server_redraw_client(c);
102
	}
103
}
104
105
void
106
server_redraw_window_borders(struct window *w)
107
{
108
	struct client	*c;
109
110
	TAILQ_FOREACH(c, &clients, entry) {
111
		if (c->session != NULL && c->session->curw->window == w)
112
			c->flags |= CLIENT_BORDERS;
113
	}
114
}
115
116
void
117
server_status_window(struct window *w)
118
{
119
	struct session	*s;
120
121
	/*
122
	 * This is slightly different. We want to redraw the status line of any
123
	 * clients containing this window rather than anywhere it is the
124
	 * current window.
125
	 */
126
127
	RB_FOREACH(s, sessions, &sessions) {
128
		if (session_has(s, w))
129
			server_status_session(s);
130
	}
131
}
132
133
void
134
server_lock(void)
135
{
136
	struct client	*c;
137
138
	TAILQ_FOREACH(c, &clients, entry) {
139
		if (c->session != NULL)
140
			server_lock_client(c);
141
	}
142
}
143
144
void
145
server_lock_session(struct session *s)
146
{
147
	struct client	*c;
148
149
	TAILQ_FOREACH(c, &clients, entry) {
150
		if (c->session == s)
151
			server_lock_client(c);
152
	}
153
}
154
155
void
156
server_lock_client(struct client *c)
157
{
158
	const char	*cmd;
159
160
	if (c->flags & CLIENT_CONTROL)
161
		return;
162
163
	if (c->flags & CLIENT_SUSPENDED)
164
		return;
165
166
	cmd = options_get_string(c->session->options, "lock-command");
167
	if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
168
		return;
169
170
	tty_stop_tty(&c->tty);
171
	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
172
	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
173
	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
174
175
	c->flags |= CLIENT_SUSPENDED;
176
	proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
177
}
178
179
void
180
server_kill_window(struct window *w)
181
{
182
	struct session		*s, *next_s, *target_s;
183
	struct session_group	*sg;
184
	struct winlink		*wl;
185
186
	next_s = RB_MIN(sessions, &sessions);
187
	while (next_s != NULL) {
188
		s = next_s;
189
		next_s = RB_NEXT(sessions, &sessions, s);
190
191
		if (!session_has(s, w))
192
			continue;
193
		server_unzoom_window(w);
194
		while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
195
			if (session_detach(s, wl)) {
196
				server_destroy_session_group(s);
197
				break;
198
			} else
199
				server_redraw_session_group(s);
200
		}
201
202
		if (options_get_number(s->options, "renumber-windows")) {
203
			if ((sg = session_group_contains(s)) != NULL) {
204
				TAILQ_FOREACH(target_s, &sg->sessions, gentry)
205
					session_renumber_windows(target_s);
206
			} else
207
				session_renumber_windows(s);
208
		}
209
	}
210
	recalculate_sizes();
211
}
212
213
int
214
server_link_window(struct session *src, struct winlink *srcwl,
215
    struct session *dst, int dstidx, int killflag, int selectflag,
216
    char **cause)
217
{
218
	struct winlink		*dstwl;
219
	struct session_group	*srcsg, *dstsg;
220
221
	srcsg = session_group_contains(src);
222
	dstsg = session_group_contains(dst);
223
	if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
224
		xasprintf(cause, "sessions are grouped");
225
		return (-1);
226
	}
227
228
	dstwl = NULL;
229
	if (dstidx != -1)
230
		dstwl = winlink_find_by_index(&dst->windows, dstidx);
231
	if (dstwl != NULL) {
232
		if (dstwl->window == srcwl->window) {
233
			xasprintf(cause, "same index: %d", dstidx);
234
			return (-1);
235
		}
236
		if (killflag) {
237
			/*
238
			 * Can't use session_detach as it will destroy session
239
			 * if this makes it empty.
240
			 */
241
			notify_session_window("window-unlinked", dst,
242
			    dstwl->window);
243
			dstwl->flags &= ~WINLINK_ALERTFLAGS;
244
			winlink_stack_remove(&dst->lastw, dstwl);
245
			winlink_remove(&dst->windows, dstwl);
246
247
			/* Force select/redraw if current. */
248
			if (dstwl == dst->curw) {
249
				selectflag = 1;
250
				dst->curw = NULL;
251
			}
252
		}
253
	}
254
255
	if (dstidx == -1)
256
		dstidx = -1 - options_get_number(dst->options, "base-index");
257
	dstwl = session_attach(dst, srcwl->window, dstidx, cause);
258
	if (dstwl == NULL)
259
		return (-1);
260
261
	if (selectflag)
262
		session_select(dst, dstwl->idx);
263
	server_redraw_session_group(dst);
264
265
	return (0);
266
}
267
268
void
269
server_unlink_window(struct session *s, struct winlink *wl)
270
{
271
	if (session_detach(s, wl))
272
		server_destroy_session_group(s);
273
	else
274
		server_redraw_session_group(s);
275
}
276
277
void
278
server_destroy_pane(struct window_pane *wp, int notify)
279
{
280
	struct window		*w = wp->window;
281
	int			 old_fd;
282
	struct screen_write_ctx	 ctx;
283
	struct grid_cell	 gc;
284
285
	old_fd = wp->fd;
286
	if (wp->fd != -1) {
287
		bufferevent_free(wp->event);
288
		close(wp->fd);
289
		wp->fd = -1;
290
	}
291
292
	if (options_get_number(w->options, "remain-on-exit")) {
293
		if (old_fd == -1)
294
			return;
295
296
		if (notify)
297
			notify_pane("pane-died", wp);
298
299
		screen_write_start(&ctx, wp, &wp->base);
300
		screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
301
		screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
302
		screen_write_linefeed(&ctx, 1, 8);
303
		memcpy(&gc, &grid_default_cell, sizeof gc);
304
		gc.attr |= GRID_ATTR_BRIGHT;
305
		screen_write_puts(&ctx, &gc, "Pane is dead");
306
		screen_write_stop(&ctx);
307
		wp->flags |= PANE_REDRAW;
308
309
		return;
310
	}
311
312
	if (notify)
313
		notify_pane("pane-exited", wp);
314
315
	server_unzoom_window(w);
316
	layout_close_pane(wp);
317
	window_remove_pane(w, wp);
318
319
	if (TAILQ_EMPTY(&w->panes))
320
		server_kill_window(w);
321
	else
322
		server_redraw_window(w);
323
}
324
325
static void
326
server_destroy_session_group(struct session *s)
327
{
328
	struct session_group	*sg;
329
	struct session		*s1;
330
331
	if ((sg = session_group_contains(s)) == NULL)
332
		server_destroy_session(s);
333
	else {
334
		TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
335
			server_destroy_session(s);
336
			session_destroy(s, __func__);
337
		}
338
	}
339
}
340
341
static struct session *
342
server_next_session(struct session *s)
343
{
344
	struct session *s_loop, *s_out;
345
346
	s_out = NULL;
347
	RB_FOREACH(s_loop, sessions, &sessions) {
348
		if (s_loop == s)
349
			continue;
350
		if (s_out == NULL ||
351
		    timercmp(&s_loop->activity_time, &s_out->activity_time, <))
352
			s_out = s_loop;
353
	}
354
	return (s_out);
355
}
356
357
void
358
server_destroy_session(struct session *s)
359
{
360
	struct client	*c;
361
	struct session	*s_new;
362
363
	if (!options_get_number(s->options, "detach-on-destroy"))
364
		s_new = server_next_session(s);
365
	else
366
		s_new = NULL;
367
368
	TAILQ_FOREACH(c, &clients, entry) {
369
		if (c->session != s)
370
			continue;
371
		if (s_new == NULL) {
372
			c->session = NULL;
373
			c->flags |= CLIENT_EXIT;
374
		} else {
375
			c->last_session = NULL;
376
			c->session = s_new;
377
			server_client_set_key_table(c, NULL);
378
			status_timer_start(c);
379
			notify_client("client-session-changed", c);
380
			session_update_activity(s_new, NULL);
381
			gettimeofday(&s_new->last_attached_time, NULL);
382
			server_redraw_client(c);
383
			alerts_check_session(s_new);
384
		}
385
	}
386
	recalculate_sizes();
387
}
388
389
void
390
server_check_unattached(void)
391
{
392
	struct session	*s;
393
394
	/*
395
	 * If any sessions are no longer attached and have destroy-unattached
396
	 * set, collect them.
397
	 */
398
	RB_FOREACH(s, sessions, &sessions) {
399
		if (!(s->flags & SESSION_UNATTACHED))
400
			continue;
401
		if (options_get_number (s->options, "destroy-unattached"))
402
			session_destroy(s, __func__);
403
	}
404
}
405
406
/* Set stdin callback. */
407
int
408
server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
409
    void *), void *cb_data, char **cause)
410
{
411
	if (c == NULL || c->session != NULL) {
412
		*cause = xstrdup("no client with stdin");
413
		return (-1);
414
	}
415
	if (c->flags & CLIENT_TERMINAL) {
416
		*cause = xstrdup("stdin is a tty");
417
		return (-1);
418
	}
419
	if (c->stdin_callback != NULL) {
420
		*cause = xstrdup("stdin in use");
421
		return (-1);
422
	}
423
424
	c->stdin_callback_data = cb_data;
425
	c->stdin_callback = cb;
426
427
	c->references++;
428
429
	if (c->stdin_closed)
430
		c->stdin_callback(c, 1, c->stdin_callback_data);
431
432
	proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
433
434
	return (0);
435
}
436
437
void
438
server_unzoom_window(struct window *w)
439
{
440
	if (window_unzoom(w) == 0) {
441
		server_redraw_window(w);
442
		server_status_window(w);
443
	}
444
}