1 /* $OpenBSD: server-fn.c,v 1.85 2015/04/29 15:59:08 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 notify_attached_session_changed(c); 423 session_update_activity(s_new); 424 server_redraw_client(c); 425 } 426 } 427 recalculate_sizes(); 428 } 429 430 void 431 server_check_unattached(void) 432 { 433 struct session *s; 434 435 /* 436 * If any sessions are no longer attached and have destroy-unattached 437 * set, collect them. 438 */ 439 RB_FOREACH(s, sessions, &sessions) { 440 if (!(s->flags & SESSION_UNATTACHED)) 441 continue; 442 if (options_get_number (&s->options, "destroy-unattached")) 443 session_destroy(s); 444 } 445 } 446 447 void 448 server_set_identify(struct client *c) 449 { 450 struct timeval tv; 451 int delay; 452 453 delay = options_get_number(&c->session->options, "display-panes-time"); 454 tv.tv_sec = delay / 1000; 455 tv.tv_usec = (delay % 1000) * 1000L; 456 457 if (event_initialized(&c->identify_timer)) 458 evtimer_del(&c->identify_timer); 459 evtimer_set(&c->identify_timer, server_callback_identify, c); 460 evtimer_add(&c->identify_timer, &tv); 461 462 c->flags |= CLIENT_IDENTIFY; 463 c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); 464 server_redraw_client(c); 465 } 466 467 void 468 server_clear_identify(struct client *c) 469 { 470 if (c->flags & CLIENT_IDENTIFY) { 471 c->flags &= ~CLIENT_IDENTIFY; 472 c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); 473 server_redraw_client(c); 474 } 475 } 476 477 void 478 server_callback_identify(unused int fd, unused short events, void *data) 479 { 480 struct client *c = data; 481 482 server_clear_identify(c); 483 } 484 485 void 486 server_update_event(struct client *c) 487 { 488 short events; 489 490 events = 0; 491 if (!(c->flags & CLIENT_BAD)) 492 events |= EV_READ; 493 if (c->ibuf.w.queued > 0) 494 events |= EV_WRITE; 495 if (event_initialized(&c->event)) 496 event_del(&c->event); 497 event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); 498 event_add(&c->event, NULL); 499 } 500 501 /* Push stdout to client if possible. */ 502 void 503 server_push_stdout(struct client *c) 504 { 505 struct msg_stdout_data data; 506 size_t size; 507 508 size = EVBUFFER_LENGTH(c->stdout_data); 509 if (size == 0) 510 return; 511 if (size > sizeof data.data) 512 size = sizeof data.data; 513 514 memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); 515 data.size = size; 516 517 if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) 518 evbuffer_drain(c->stdout_data, size); 519 } 520 521 /* Push stderr to client if possible. */ 522 void 523 server_push_stderr(struct client *c) 524 { 525 struct msg_stderr_data data; 526 size_t size; 527 528 if (c->stderr_data == c->stdout_data) { 529 server_push_stdout(c); 530 return; 531 } 532 size = EVBUFFER_LENGTH(c->stderr_data); 533 if (size == 0) 534 return; 535 if (size > sizeof data.data) 536 size = sizeof data.data; 537 538 memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); 539 data.size = size; 540 541 if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) 542 evbuffer_drain(c->stderr_data, size); 543 } 544 545 /* Set stdin callback. */ 546 int 547 server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, 548 void *), void *cb_data, char **cause) 549 { 550 if (c == NULL || c->session != NULL) { 551 *cause = xstrdup("no client with stdin"); 552 return (-1); 553 } 554 if (c->flags & CLIENT_TERMINAL) { 555 *cause = xstrdup("stdin is a tty"); 556 return (-1); 557 } 558 if (c->stdin_callback != NULL) { 559 *cause = xstrdup("stdin in use"); 560 return (-1); 561 } 562 563 c->stdin_callback_data = cb_data; 564 c->stdin_callback = cb; 565 566 c->references++; 567 568 if (c->stdin_closed) 569 c->stdin_callback(c, 1, c->stdin_callback_data); 570 571 server_write_client(c, MSG_STDIN, NULL, 0); 572 573 return (0); 574 } 575 576 void 577 server_unzoom_window(struct window *w) 578 { 579 if (window_unzoom(w) == 0) { 580 server_redraw_window(w); 581 server_status_window(w); 582 } 583 } 584