1 /* $OpenBSD: server-fn.c,v 1.87 2015/08/28 13:01:03 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 <stdlib.h> 22 #include <string.h> 23 #include <time.h> 24 #include <unistd.h> 25 26 #include "tmux.h" 27 28 struct session *server_next_session(struct session *); 29 void server_callback_identify(int, short, void *); 30 31 void 32 server_fill_environ(struct session *s, struct environ *env) 33 { 34 char var[PATH_MAX], *term; 35 u_int idx; 36 long pid; 37 38 if (s != NULL) { 39 term = options_get_string(&global_options, "default-terminal"); 40 environ_set(env, "TERM", term); 41 42 idx = s->id; 43 } else 44 idx = (u_int)-1; 45 pid = getpid(); 46 xsnprintf(var, sizeof var, "%s,%ld,%u", socket_path, pid, idx); 47 environ_set(env, "TMUX", var); 48 } 49 50 void 51 server_write_ready(struct client *c) 52 { 53 if (c->flags & CLIENT_CONTROL) 54 return; 55 server_write_client(c, MSG_READY, NULL, 0); 56 } 57 58 int 59 server_write_client(struct client *c, enum msgtype type, const void *buf, 60 size_t len) 61 { 62 struct imsgbuf *ibuf = &c->ibuf; 63 int error; 64 65 if (c->flags & CLIENT_BAD) 66 return (-1); 67 log_debug("writing %d to client %d", type, c->ibuf.fd); 68 error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, 69 (void *) buf, len); 70 if (error == 1) 71 server_update_event(c); 72 return (error == 1 ? 0 : -1); 73 } 74 75 void 76 server_write_session(struct session *s, enum msgtype type, const void *buf, 77 size_t len) 78 { 79 struct client *c; 80 81 TAILQ_FOREACH(c, &clients, entry) { 82 if (c->session == s) 83 server_write_client(c, type, buf, len); 84 } 85 } 86 87 void 88 server_redraw_client(struct client *c) 89 { 90 c->flags |= CLIENT_REDRAW; 91 } 92 93 void 94 server_status_client(struct client *c) 95 { 96 c->flags |= CLIENT_STATUS; 97 } 98 99 void 100 server_redraw_session(struct session *s) 101 { 102 struct client *c; 103 104 TAILQ_FOREACH(c, &clients, entry) { 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 128 TAILQ_FOREACH(c, &clients, entry) { 129 if (c->session == s) 130 server_status_client(c); 131 } 132 } 133 134 void 135 server_status_session_group(struct session *s) 136 { 137 struct session_group *sg; 138 139 if ((sg = session_group_find(s)) == NULL) 140 server_status_session(s); 141 else { 142 TAILQ_FOREACH(s, &sg->sessions, gentry) 143 server_status_session(s); 144 } 145 } 146 147 void 148 server_redraw_window(struct window *w) 149 { 150 struct client *c; 151 152 TAILQ_FOREACH(c, &clients, entry) { 153 if (c->session != NULL && c->session->curw->window == w) 154 server_redraw_client(c); 155 } 156 w->flags |= WINDOW_REDRAW; 157 } 158 159 void 160 server_redraw_window_borders(struct window *w) 161 { 162 struct client *c; 163 164 TAILQ_FOREACH(c, &clients, entry) { 165 if (c->session != NULL && c->session->curw->window == w) 166 c->flags |= CLIENT_BORDERS; 167 } 168 } 169 170 void 171 server_status_window(struct window *w) 172 { 173 struct session *s; 174 175 /* 176 * This is slightly different. We want to redraw the status line of any 177 * clients containing this window rather than anywhere it is the 178 * current window. 179 */ 180 181 RB_FOREACH(s, sessions, &sessions) { 182 if (session_has(s, w)) 183 server_status_session(s); 184 } 185 } 186 187 void 188 server_lock(void) 189 { 190 struct client *c; 191 192 TAILQ_FOREACH(c, &clients, entry) { 193 if (c->session != NULL) 194 server_lock_client(c); 195 } 196 } 197 198 void 199 server_lock_session(struct session *s) 200 { 201 struct client *c; 202 203 TAILQ_FOREACH(c, &clients, entry) { 204 if (c->session == s) 205 server_lock_client(c); 206 } 207 } 208 209 void 210 server_lock_client(struct client *c) 211 { 212 const char *cmd; 213 214 if (c->flags & CLIENT_CONTROL) 215 return; 216 217 if (c->flags & CLIENT_SUSPENDED) 218 return; 219 220 cmd = options_get_string(&c->session->options, "lock-command"); 221 if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) 222 return; 223 224 tty_stop_tty(&c->tty); 225 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP)); 226 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR)); 227 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); 228 229 c->flags |= CLIENT_SUSPENDED; 230 server_write_client(c, MSG_LOCK, cmd, strlen(cmd) + 1); 231 } 232 233 void 234 server_kill_window(struct window *w) 235 { 236 struct session *s, *next_s, *target_s; 237 struct session_group *sg; 238 struct winlink *wl; 239 240 next_s = RB_MIN(sessions, &sessions); 241 while (next_s != NULL) { 242 s = next_s; 243 next_s = RB_NEXT(sessions, &sessions, s); 244 245 if (!session_has(s, w)) 246 continue; 247 server_unzoom_window(w); 248 while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { 249 if (session_detach(s, wl)) { 250 server_destroy_session_group(s); 251 break; 252 } else 253 server_redraw_session_group(s); 254 } 255 256 if (options_get_number(&s->options, "renumber-windows")) { 257 if ((sg = session_group_find(s)) != NULL) { 258 TAILQ_FOREACH(target_s, &sg->sessions, gentry) 259 session_renumber_windows(target_s); 260 } else 261 session_renumber_windows(s); 262 } 263 } 264 recalculate_sizes(); 265 } 266 267 int 268 server_link_window(struct session *src, struct winlink *srcwl, 269 struct session *dst, int dstidx, int killflag, int selectflag, 270 char **cause) 271 { 272 struct winlink *dstwl; 273 struct session_group *srcsg, *dstsg; 274 275 srcsg = session_group_find(src); 276 dstsg = session_group_find(dst); 277 if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) { 278 xasprintf(cause, "sessions are grouped"); 279 return (-1); 280 } 281 282 dstwl = NULL; 283 if (dstidx != -1) 284 dstwl = winlink_find_by_index(&dst->windows, dstidx); 285 if (dstwl != NULL) { 286 if (dstwl->window == srcwl->window) { 287 xasprintf(cause, "same index: %d", dstidx); 288 return (-1); 289 } 290 if (killflag) { 291 /* 292 * Can't use session_detach as it will destroy session 293 * if this makes it empty. 294 */ 295 notify_window_unlinked(dst, dstwl->window); 296 dstwl->flags &= ~WINLINK_ALERTFLAGS; 297 winlink_stack_remove(&dst->lastw, dstwl); 298 winlink_remove(&dst->windows, dstwl); 299 300 /* Force select/redraw if current. */ 301 if (dstwl == dst->curw) { 302 selectflag = 1; 303 dst->curw = NULL; 304 } 305 } 306 } 307 308 if (dstidx == -1) 309 dstidx = -1 - options_get_number(&dst->options, "base-index"); 310 dstwl = session_attach(dst, srcwl->window, dstidx, cause); 311 if (dstwl == NULL) 312 return (-1); 313 314 if (selectflag) 315 session_select(dst, dstwl->idx); 316 server_redraw_session_group(dst); 317 318 return (0); 319 } 320 321 void 322 server_unlink_window(struct session *s, struct winlink *wl) 323 { 324 if (session_detach(s, wl)) 325 server_destroy_session_group(s); 326 else 327 server_redraw_session_group(s); 328 } 329 330 void 331 server_destroy_pane(struct window_pane *wp) 332 { 333 struct window *w = wp->window; 334 int old_fd; 335 struct screen_write_ctx ctx; 336 struct grid_cell gc; 337 338 old_fd = wp->fd; 339 if (wp->fd != -1) { 340 bufferevent_free(wp->event); 341 close(wp->fd); 342 wp->fd = -1; 343 } 344 345 if (options_get_number(&w->options, "remain-on-exit")) { 346 if (old_fd == -1) 347 return; 348 screen_write_start(&ctx, wp, &wp->base); 349 screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); 350 screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1); 351 screen_write_linefeed(&ctx, 1); 352 memcpy(&gc, &grid_default_cell, sizeof gc); 353 gc.attr |= GRID_ATTR_BRIGHT; 354 screen_write_puts(&ctx, &gc, "Pane is dead"); 355 screen_write_stop(&ctx); 356 wp->flags |= PANE_REDRAW; 357 return; 358 } 359 360 server_unzoom_window(w); 361 layout_close_pane(wp); 362 window_remove_pane(w, wp); 363 364 if (TAILQ_EMPTY(&w->panes)) 365 server_kill_window(w); 366 else 367 server_redraw_window(w); 368 } 369 370 void 371 server_destroy_session_group(struct session *s) 372 { 373 struct session_group *sg; 374 struct session *s1; 375 376 if ((sg = session_group_find(s)) == NULL) 377 server_destroy_session(s); 378 else { 379 TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { 380 server_destroy_session(s); 381 session_destroy(s); 382 } 383 } 384 } 385 386 struct session * 387 server_next_session(struct session *s) 388 { 389 struct session *s_loop, *s_out; 390 391 s_out = NULL; 392 RB_FOREACH(s_loop, sessions, &sessions) { 393 if (s_loop == s) 394 continue; 395 if (s_out == NULL || 396 timercmp(&s_loop->activity_time, &s_out->activity_time, <)) 397 s_out = s_loop; 398 } 399 return (s_out); 400 } 401 402 void 403 server_destroy_session(struct session *s) 404 { 405 struct client *c; 406 struct session *s_new; 407 408 if (!options_get_number(&s->options, "detach-on-destroy")) 409 s_new = server_next_session(s); 410 else 411 s_new = NULL; 412 413 TAILQ_FOREACH(c, &clients, entry) { 414 if (c->session != s) 415 continue; 416 if (s_new == NULL) { 417 c->session = NULL; 418 c->flags |= CLIENT_EXIT; 419 } else { 420 c->last_session = NULL; 421 c->session = s_new; 422 status_timer_start(c); 423 notify_attached_session_changed(c); 424 session_update_activity(s_new, NULL); 425 server_redraw_client(c); 426 } 427 } 428 recalculate_sizes(); 429 } 430 431 void 432 server_check_unattached(void) 433 { 434 struct session *s; 435 436 /* 437 * If any sessions are no longer attached and have destroy-unattached 438 * set, collect them. 439 */ 440 RB_FOREACH(s, sessions, &sessions) { 441 if (!(s->flags & SESSION_UNATTACHED)) 442 continue; 443 if (options_get_number (&s->options, "destroy-unattached")) 444 session_destroy(s); 445 } 446 } 447 448 void 449 server_set_identify(struct client *c) 450 { 451 struct timeval tv; 452 int delay; 453 454 delay = options_get_number(&c->session->options, "display-panes-time"); 455 tv.tv_sec = delay / 1000; 456 tv.tv_usec = (delay % 1000) * 1000L; 457 458 if (event_initialized(&c->identify_timer)) 459 evtimer_del(&c->identify_timer); 460 evtimer_set(&c->identify_timer, server_callback_identify, c); 461 evtimer_add(&c->identify_timer, &tv); 462 463 c->flags |= CLIENT_IDENTIFY; 464 c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); 465 server_redraw_client(c); 466 } 467 468 void 469 server_clear_identify(struct client *c) 470 { 471 if (c->flags & CLIENT_IDENTIFY) { 472 c->flags &= ~CLIENT_IDENTIFY; 473 c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); 474 server_redraw_client(c); 475 } 476 } 477 478 void 479 server_callback_identify(unused int fd, unused short events, void *data) 480 { 481 struct client *c = data; 482 483 server_clear_identify(c); 484 } 485 486 void 487 server_update_event(struct client *c) 488 { 489 short events; 490 491 events = 0; 492 if (!(c->flags & CLIENT_BAD)) 493 events |= EV_READ; 494 if (c->ibuf.w.queued > 0) 495 events |= EV_WRITE; 496 if (event_initialized(&c->event)) 497 event_del(&c->event); 498 event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); 499 event_add(&c->event, NULL); 500 } 501 502 /* Push stdout to client if possible. */ 503 void 504 server_push_stdout(struct client *c) 505 { 506 struct msg_stdout_data data; 507 size_t size; 508 509 size = EVBUFFER_LENGTH(c->stdout_data); 510 if (size == 0) 511 return; 512 if (size > sizeof data.data) 513 size = sizeof data.data; 514 515 memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); 516 data.size = size; 517 518 if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) 519 evbuffer_drain(c->stdout_data, size); 520 } 521 522 /* Push stderr to client if possible. */ 523 void 524 server_push_stderr(struct client *c) 525 { 526 struct msg_stderr_data data; 527 size_t size; 528 529 if (c->stderr_data == c->stdout_data) { 530 server_push_stdout(c); 531 return; 532 } 533 size = EVBUFFER_LENGTH(c->stderr_data); 534 if (size == 0) 535 return; 536 if (size > sizeof data.data) 537 size = sizeof data.data; 538 539 memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); 540 data.size = size; 541 542 if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) 543 evbuffer_drain(c->stderr_data, size); 544 } 545 546 /* Set stdin callback. */ 547 int 548 server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, 549 void *), void *cb_data, char **cause) 550 { 551 if (c == NULL || c->session != NULL) { 552 *cause = xstrdup("no client with stdin"); 553 return (-1); 554 } 555 if (c->flags & CLIENT_TERMINAL) { 556 *cause = xstrdup("stdin is a tty"); 557 return (-1); 558 } 559 if (c->stdin_callback != NULL) { 560 *cause = xstrdup("stdin in use"); 561 return (-1); 562 } 563 564 c->stdin_callback_data = cb_data; 565 c->stdin_callback = cb; 566 567 c->references++; 568 569 if (c->stdin_closed) 570 c->stdin_callback(c, 1, c->stdin_callback_data); 571 572 server_write_client(c, MSG_STDIN, NULL, 0); 573 574 return (0); 575 } 576 577 void 578 server_unzoom_window(struct window *w) 579 { 580 if (window_unzoom(w) == 0) { 581 server_redraw_window(w); 582 server_status_window(w); 583 } 584 } 585