1 #include <sys/utsname.h>
2 #include "tmate.h"
3 #include "tmate-protocol.h"
4 #include "window-copy.h"
5
6 #define pack(what, ...) _pack(&tmate_session.encoder, what, ##__VA_ARGS__)
7
tmate_write_header(void)8 void tmate_write_header(void)
9 {
10 pack(array, 3);
11 pack(int, TMATE_OUT_HEADER);
12 pack(int, TMATE_PROTOCOL_VERSION);
13 pack(string, VERSION);
14 }
15
tmate_write_uname(void)16 void tmate_write_uname(void)
17 {
18 struct utsname name;
19 if (uname(&name) < 0) {
20 tmate_debug("uname() failed");
21 return;
22 }
23
24 pack(array, 6);
25 pack(int, TMATE_OUT_UNAME);
26 pack(string, name.sysname);
27 pack(string, name.nodename);
28 pack(string, name.release);
29 pack(string, name.version);
30 pack(string, name.machine);
31 }
32
tmate_write_ready(void)33 void tmate_write_ready(void)
34 {
35 pack(array, 1);
36 pack(int, TMATE_OUT_READY);
37 }
38
tmate_sync_layout(void)39 void tmate_sync_layout(void)
40 {
41 struct session *s;
42 struct winlink *wl;
43 struct window *w;
44 struct window_pane *wp;
45 int num_panes = 0;
46 int num_windows = 0;
47 int active_pane_id;
48 int active_window_idx = -1;
49
50 /*
51 * TODO this can get a little heavy.
52 * We are shipping the full layout whenever a window name changes,
53 * that is, at every shell command.
54 * Might be better to do something incremental.
55 */
56
57 /*
58 * We only allow one session, it makes our lives easier.
59 * Especially when the HTML5 client will come along.
60 * We make no distinction between a winlink and its window except
61 * that we send the winlink idx to draw the status bar properly.
62 */
63
64 s = RB_MIN(sessions, &sessions);
65 if (!s)
66 return;
67
68 num_windows = 0;
69 RB_FOREACH(wl, winlinks, &s->windows) {
70 if (wl->window)
71 num_windows++;
72 }
73
74 if (!num_windows)
75 return;
76
77 pack(array, 5);
78 pack(int, TMATE_OUT_SYNC_LAYOUT);
79
80 pack(int, s->sx);
81 pack(int, s->sy);
82
83 pack(array, num_windows);
84 RB_FOREACH(wl, winlinks, &s->windows) {
85 w = wl->window;
86 if (!w)
87 continue;
88
89 w->tmate_last_sync_active_pane = NULL;
90 active_pane_id = -1;
91
92 if (active_window_idx == -1)
93 active_window_idx = wl->idx;
94
95 pack(array, 4);
96 pack(int, wl->idx);
97 pack(string, w->name);
98
99 num_panes = 0;
100 TAILQ_FOREACH(wp, &w->panes, entry)
101 num_panes++;
102
103 pack(array, num_panes);
104 TAILQ_FOREACH(wp, &w->panes, entry) {
105 pack(array, 5);
106 pack(int, wp->id);
107 pack(int, wp->sx);
108 pack(int, wp->sy);
109 pack(int, wp->xoff);
110 pack(int, wp->yoff);
111
112 if (wp == w->active) {
113 w->tmate_last_sync_active_pane = wp;
114 active_pane_id = wp->id;
115 }
116
117 }
118 pack(int, active_pane_id);
119 }
120
121 if (s->curw)
122 active_window_idx = s->curw->idx;
123
124 pack(int, active_window_idx);
125 }
126
127 /* TODO add a buffer for pty_data ? */
128
129 #define TMATE_MAX_PTY_SIZE (16*1024)
130
tmate_pty_data(struct window_pane * wp,const char * buf,size_t len)131 void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len)
132 {
133 size_t to_write;
134
135 while (len > 0) {
136 to_write = len < TMATE_MAX_PTY_SIZE ? len : TMATE_MAX_PTY_SIZE;
137
138 pack(array, 3);
139 pack(int, TMATE_OUT_PTY_DATA);
140 pack(int, wp->id);
141 pack(str, to_write);
142 pack(str_body, buf, to_write);
143
144 buf += to_write;
145 len -= to_write;
146 }
147 }
148
149 extern const struct cmd_entry cmd_bind_key_entry;
150 extern const struct cmd_entry cmd_unbind_key_entry;
151 extern const struct cmd_entry cmd_set_option_entry;
152 extern const struct cmd_entry cmd_set_window_option_entry;
153
154 static const struct cmd_entry *replicated_cmds[] = {
155 &cmd_bind_key_entry,
156 &cmd_unbind_key_entry,
157 &cmd_set_option_entry,
158 &cmd_set_window_option_entry,
159 NULL
160 };
161
tmate_should_replicate_cmd(const struct cmd_entry * cmd)162 int tmate_should_replicate_cmd(const struct cmd_entry *cmd)
163 {
164 const struct cmd_entry **ptr;
165
166 for (ptr = replicated_cmds; *ptr; ptr++)
167 if (*ptr == cmd)
168 return 1;
169 return 0;
170 }
171
172 #define sc (&session->saved_tmux_cmds)
173 #define SAVED_TMUX_CMD_INITIAL_SIZE 256
174 static void __tmate_exec_cmd_args(int argc, const char **argv);
175
append_saved_cmd(struct tmate_session * session,int argc,const char ** argv)176 static void append_saved_cmd(struct tmate_session *session,
177 int argc, const char **argv)
178 {
179 if (!sc->cmds) {
180 sc->capacity = SAVED_TMUX_CMD_INITIAL_SIZE;
181 sc->cmds = xmalloc(sizeof(*sc->cmds) * sc->capacity);
182 sc->tail = 0;
183 }
184
185 if (sc->tail == sc->capacity) {
186 sc->capacity *= 2;
187 sc->cmds = xrealloc(sc->cmds, sizeof(*sc->cmds) * sc->capacity);
188 }
189
190 sc->cmds[sc->tail].argc = argc;
191 sc->cmds[sc->tail].argv = cmd_copy_argv(argc, (char **)argv);
192
193 sc->tail++;
194 }
195
replay_saved_cmd(struct tmate_session * session)196 static void replay_saved_cmd(struct tmate_session *session)
197 {
198 unsigned int i;
199 for (i = 0; i < sc->tail; i++)
200 __tmate_exec_cmd_args(sc->cmds[i].argc, (const char **)sc->cmds[i].argv);
201 }
202 #undef sc
203
204 struct args_entry {
205 u_char flag;
206 char *value;
207 RB_ENTRY(args_entry) entry;
208 };
209
extract_cmd(struct cmd * cmd,int * _argc,char *** _argv)210 static void extract_cmd(struct cmd *cmd, int *_argc, char ***_argv)
211 {
212 struct args_entry *entry;
213 struct args* args = cmd->args;
214 int argc = 0;
215 char **argv;
216 int next = 0, i;
217
218 argc++; /* cmd name */
219 RB_FOREACH(entry, args_tree, &args->tree) {
220 argc++;
221 if (entry->value != NULL)
222 argc++;
223 }
224 argc += args->argc;
225 argv = xmalloc(sizeof(char *) * argc);
226
227 argv[next++] = xstrdup(cmd->entry->name);
228
229 RB_FOREACH(entry, args_tree, &args->tree) {
230 xasprintf(&argv[next++], "-%c", entry->flag);
231 if (entry->value != NULL)
232 argv[next++] = xstrdup(entry->value);
233 }
234
235 for (i = 0; i < args->argc; i++)
236 argv[next++] = xstrdup(args->argv[i]);
237
238 *_argc = argc;
239 *_argv = argv;
240 }
241
__tmate_exec_cmd_args(int argc,const char ** argv)242 static void __tmate_exec_cmd_args(int argc, const char **argv)
243 {
244 int i;
245
246 pack(array, argc + 1);
247 pack(int, TMATE_OUT_EXEC_CMD);
248
249 for (i = 0; i < argc; i++)
250 pack(string, argv[i]);
251 }
252
tmate_exec_cmd_args(int argc,const char ** argv)253 void tmate_exec_cmd_args(int argc, const char **argv)
254 {
255 __tmate_exec_cmd_args(argc, argv);
256 append_saved_cmd(&tmate_session, argc, argv);
257 }
258
tmate_set_val(const char * name,const char * value)259 void tmate_set_val(const char *name, const char *value)
260 {
261 char *buf;
262 xasprintf(&buf, "%s=%s", name, value);
263 tmate_exec_cmd_args(3, (const char *[]){"set-option", "tmate-set", buf});
264 free(buf);
265 }
266
tmate_exec_cmd(struct cmd * cmd)267 void tmate_exec_cmd(struct cmd *cmd)
268 {
269 int argc;
270 char **argv;
271
272 extract_cmd(cmd, &argc, &argv);
273 tmate_exec_cmd_args(argc, (const char **)argv);
274 cmd_free_argv(argc, argv);
275 }
276
tmate_failed_cmd(int client_id,const char * cause)277 void tmate_failed_cmd(int client_id, const char *cause)
278 {
279 pack(array, 3);
280 pack(int, TMATE_OUT_FAILED_CMD);
281 pack(int, client_id);
282 pack(string, cause);
283 }
284
tmate_status(const char * left,const char * right)285 void tmate_status(const char *left, const char *right)
286 {
287 static char *old_left, *old_right;
288
289 if (old_left && !strcmp(old_left, left) &&
290 old_right && !strcmp(old_right, right))
291 return;
292
293 pack(array, 3);
294 pack(int, TMATE_OUT_STATUS);
295 pack(string, left);
296 pack(string, right);
297
298 free(old_left);
299 free(old_right);
300 old_left = xstrdup(left);
301 old_right = xstrdup(right);
302 }
303
tmate_sync_copy_mode(struct window_pane * wp)304 void tmate_sync_copy_mode(struct window_pane *wp)
305 {
306 struct window_copy_mode_data *data = wp->modedata;
307
308 pack(array, 3);
309 pack(int, TMATE_OUT_SYNC_COPY_MODE);
310
311 pack(int, wp->id);
312
313 if (wp->mode != &window_copy_mode ||
314 data->inputtype == WINDOW_COPY_PASSWORD) {
315 pack(array, 0);
316 return;
317 }
318 pack(array, 6);
319 pack(int, data->backing == &wp->base);
320
321 pack(int, data->oy);
322 pack(int, data->cx);
323 pack(int, data->cy);
324
325 if (data->screen.sel.flag) {
326 pack(array, 3);
327 pack(int, data->selx);
328 pack(int, -data->sely + screen_hsize(data->backing)
329 + screen_size_y(data->backing) - 1);
330 pack(int, data->rectflag);
331 } else
332 pack(array, 0);
333
334 if (data->inputprompt) {
335 pack(array, 3);
336 pack(int, data->inputtype);
337 pack(string, data->inputprompt);
338 pack(string, data->inputstr);
339 } else
340 pack(array, 0);
341 }
342
tmate_write_copy_mode(struct window_pane * wp,const char * str)343 void tmate_write_copy_mode(struct window_pane *wp, const char *str)
344 {
345 pack(array, 3);
346 pack(int, TMATE_OUT_WRITE_COPY_MODE);
347 pack(int, wp->id);
348 pack(string, str);
349 }
350
tmate_write_fin(void)351 void tmate_write_fin(void)
352 {
353 pack(array, 1);
354 pack(int, TMATE_OUT_FIN);
355 }
356
do_snapshot_grid(struct grid * grid,unsigned int max_history_lines)357 static void do_snapshot_grid(struct grid *grid, unsigned int max_history_lines)
358 {
359 struct grid_line *line;
360 struct grid_cell gc;
361 unsigned int line_i, i;
362 unsigned int max_lines;
363 size_t str_len;
364
365 max_lines = max_history_lines + grid->sy;
366
367 #define grid_num_lines(grid) (grid->hsize + grid->sy)
368
369 if (grid_num_lines(grid) > max_lines)
370 line_i = grid_num_lines(grid) - max_lines;
371 else
372 line_i = 0;
373
374 pack(array, grid_num_lines(grid) - line_i);
375 for (; line_i < grid_num_lines(grid); line_i++) {
376 line = &grid->linedata[line_i];
377
378 pack(array, 2);
379 str_len = 0;
380 for (i = 0; i < line->cellsize; i++) {
381 grid_get_cell(grid, i, line_i, &gc);
382 str_len += gc.data.size;
383 }
384
385 pack(str, str_len);
386 for (i = 0; i < line->cellsize; i++) {
387 grid_get_cell(grid, i, line_i, &gc);
388 pack(str_body, gc.data.data, gc.data.size);
389 }
390
391 pack(array, line->cellsize);
392 for (i = 0; i < line->cellsize; i++) {
393 grid_get_cell(grid, i, line_i, &gc);
394 pack(unsigned_int, ((gc.flags << 24) |
395 (gc.attr << 16) |
396 (gc.bg << 8) |
397 gc.fg ));
398 }
399 }
400
401 }
402
do_snapshot_pane(struct window_pane * wp,unsigned int max_history_lines)403 static void do_snapshot_pane(struct window_pane *wp, unsigned int max_history_lines)
404 {
405 struct screen *screen = &wp->base;
406
407 pack(array, 4);
408 pack(int, wp->id);
409
410 pack(unsigned_int, screen->mode);
411
412 pack(array, 3);
413 pack(int, screen->cx);
414 pack(int, screen->cy);
415 do_snapshot_grid(screen->grid, max_history_lines);
416
417 if (wp->saved_grid) {
418 pack(array, 3);
419 pack(int, wp->saved_cx);
420 pack(int, wp->saved_cy);
421 do_snapshot_grid(wp->saved_grid, max_history_lines);
422 } else {
423 pack(nil);
424 }
425 }
426
tmate_send_session_snapshot(unsigned int max_history_lines)427 static void tmate_send_session_snapshot(unsigned int max_history_lines)
428 {
429 struct session *s;
430 struct winlink *wl;
431 struct window *w;
432 struct window_pane *pane;
433 int num_panes;
434
435 pack(array, 2);
436 pack(int, TMATE_OUT_SNAPSHOT);
437
438 s = RB_MIN(sessions, &sessions);
439 if (!s)
440 tmate_fatal("no session?");
441
442 num_panes = 0;
443 RB_FOREACH(wl, winlinks, &s->windows) {
444 w = wl->window;
445 if (!w)
446 continue;
447
448 TAILQ_FOREACH(pane, &w->panes, entry)
449 num_panes++;
450 }
451
452 pack(array, num_panes);
453 RB_FOREACH(wl, winlinks, &s->windows) {
454 w = wl->window;
455 if (!w)
456 continue;
457
458 TAILQ_FOREACH(pane, &w->panes, entry)
459 do_snapshot_pane(pane, max_history_lines);
460 }
461 }
462
tmate_send_reconnection_data(struct tmate_session * session)463 static void tmate_send_reconnection_data(struct tmate_session *session)
464 {
465 if (!session->reconnection_data)
466 return;
467
468 pack(array, 2);
469 pack(int, TMATE_OUT_RECONNECT);
470 pack(string, session->reconnection_data);
471 }
472
473 #define RECONNECTION_MAX_HISTORY_LINE 300
474
tmate_send_reconnection_state(struct tmate_session * session)475 void tmate_send_reconnection_state(struct tmate_session *session)
476 {
477 /* Start with a fresh encoder */
478 tmate_encoder_destroy(&session->encoder);
479 tmate_encoder_init(&session->encoder, NULL, session);
480
481 tmate_write_header();
482 tmate_send_reconnection_data(session);
483 replay_saved_cmd(session);
484 /* TODO send all option variables */
485 tmate_write_uname();
486 tmate_write_ready();
487
488 tmate_sync_layout();
489 tmate_send_session_snapshot(RECONNECTION_MAX_HISTORY_LINE);
490 }
491