1 /* $OpenBSD$ */ 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 __UNCONST(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 #ifdef HAVE_UTEMPTER 341 utempter_remove_record(wp->fd); 342 #endif 343 bufferevent_free(wp->event); 344 close(wp->fd); 345 wp->fd = -1; 346 } 347 348 if (options_get_number(&w->options, "remain-on-exit")) { 349 if (old_fd == -1) 350 return; 351 screen_write_start(&ctx, wp, &wp->base); 352 screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); 353 screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1); 354 screen_write_linefeed(&ctx, 1); 355 memcpy(&gc, &grid_default_cell, sizeof gc); 356 gc.attr |= GRID_ATTR_BRIGHT; 357 screen_write_puts(&ctx, &gc, "Pane is dead"); 358 screen_write_stop(&ctx); 359 wp->flags |= PANE_REDRAW; 360 return; 361 } 362 363 server_unzoom_window(w); 364 layout_close_pane(wp); 365 window_remove_pane(w, wp); 366 367 if (TAILQ_EMPTY(&w->panes)) 368 server_kill_window(w); 369 else 370 server_redraw_window(w); 371 } 372 373 void 374 server_destroy_session_group(struct session *s) 375 { 376 struct session_group *sg; 377 struct session *s1; 378 379 if ((sg = session_group_find(s)) == NULL) 380 server_destroy_session(s); 381 else { 382 TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { 383 server_destroy_session(s); 384 session_destroy(s); 385 } 386 } 387 } 388 389 struct session * 390 server_next_session(struct session *s) 391 { 392 struct session *s_loop, *s_out; 393 394 s_out = NULL; 395 RB_FOREACH(s_loop, sessions, &sessions) { 396 if (s_loop == s) 397 continue; 398 if (s_out == NULL || 399 timercmp(&s_loop->activity_time, &s_out->activity_time, <)) 400 s_out = s_loop; 401 } 402 return (s_out); 403 } 404 405 void 406 server_destroy_session(struct session *s) 407 { 408 struct client *c; 409 struct session *s_new; 410 411 if (!options_get_number(&s->options, "detach-on-destroy")) 412 s_new = server_next_session(s); 413 else 414 s_new = NULL; 415 416 TAILQ_FOREACH(c, &clients, entry) { 417 if (c->session != s) 418 continue; 419 if (s_new == NULL) { 420 c->session = NULL; 421 c->flags |= CLIENT_EXIT; 422 } else { 423 c->last_session = NULL; 424 c->session = s_new; 425 status_timer_start(c); 426 notify_attached_session_changed(c); 427 session_update_activity(s_new, NULL); 428 gettimeofday(&s_new->last_attached_time, NULL); 429 server_redraw_client(c); 430 } 431 } 432 recalculate_sizes(); 433 } 434 435 void 436 server_check_unattached(void) 437 { 438 struct session *s; 439 440 /* 441 * If any sessions are no longer attached and have destroy-unattached 442 * set, collect them. 443 */ 444 RB_FOREACH(s, sessions, &sessions) { 445 if (!(s->flags & SESSION_UNATTACHED)) 446 continue; 447 if (options_get_number (&s->options, "destroy-unattached")) 448 session_destroy(s); 449 } 450 } 451 452 void 453 server_set_identify(struct client *c) 454 { 455 struct timeval tv; 456 int delay; 457 458 delay = options_get_number(&c->session->options, "display-panes-time"); 459 tv.tv_sec = delay / 1000; 460 tv.tv_usec = (delay % 1000) * 1000L; 461 462 if (event_initialized(&c->identify_timer)) 463 evtimer_del(&c->identify_timer); 464 evtimer_set(&c->identify_timer, server_callback_identify, c); 465 evtimer_add(&c->identify_timer, &tv); 466 467 c->flags |= CLIENT_IDENTIFY; 468 c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); 469 server_redraw_client(c); 470 } 471 472 void 473 server_clear_identify(struct client *c) 474 { 475 if (c->flags & CLIENT_IDENTIFY) { 476 c->flags &= ~CLIENT_IDENTIFY; 477 c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); 478 server_redraw_client(c); 479 } 480 } 481 482 void 483 server_callback_identify(unused int fd, unused short events, void *data) 484 { 485 struct client *c = data; 486 487 server_clear_identify(c); 488 } 489 490 void 491 server_update_event(struct client *c) 492 { 493 short events; 494 495 events = 0; 496 if (!(c->flags & CLIENT_BAD)) 497 events |= EV_READ; 498 if (c->ibuf.w.queued > 0) 499 events |= EV_WRITE; 500 if (event_initialized(&c->event)) 501 event_del(&c->event); 502 event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); 503 event_add(&c->event, NULL); 504 } 505 506 /* Push stdout to client if possible. */ 507 void 508 server_push_stdout(struct client *c) 509 { 510 struct msg_stdout_data data; 511 size_t size; 512 513 size = EVBUFFER_LENGTH(c->stdout_data); 514 if (size == 0) 515 return; 516 if (size > sizeof data.data) 517 size = sizeof data.data; 518 519 memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); 520 data.size = size; 521 522 if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) 523 evbuffer_drain(c->stdout_data, size); 524 } 525 526 /* Push stderr to client if possible. */ 527 void 528 server_push_stderr(struct client *c) 529 { 530 struct msg_stderr_data data; 531 size_t size; 532 533 if (c->stderr_data == c->stdout_data) { 534 server_push_stdout(c); 535 return; 536 } 537 size = EVBUFFER_LENGTH(c->stderr_data); 538 if (size == 0) 539 return; 540 if (size > sizeof data.data) 541 size = sizeof data.data; 542 543 memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); 544 data.size = size; 545 546 if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) 547 evbuffer_drain(c->stderr_data, size); 548 } 549 550 /* Set stdin callback. */ 551 int 552 server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, 553 void *), void *cb_data, char **cause) 554 { 555 if (c == NULL || c->session != NULL) { 556 *cause = xstrdup("no client with stdin"); 557 return (-1); 558 } 559 if (c->flags & CLIENT_TERMINAL) { 560 *cause = xstrdup("stdin is a tty"); 561 return (-1); 562 } 563 if (c->stdin_callback != NULL) { 564 *cause = xstrdup("stdin in use"); 565 return (-1); 566 } 567 568 c->stdin_callback_data = cb_data; 569 c->stdin_callback = cb; 570 571 c->references++; 572 573 if (c->stdin_closed) 574 c->stdin_callback(c, 1, c->stdin_callback_data); 575 576 server_write_client(c, MSG_STDIN, NULL, 0); 577 578 return (0); 579 } 580 581 void 582 server_unzoom_window(struct window *w) 583 { 584 if (window_unzoom(w) == 0) { 585 server_redraw_window(w); 586 server_status_window(w); 587 } 588 } 589