1 /* $OpenBSD: server-fn.c,v 1.99 2016/06/16 10:55:47 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 struct session *server_next_session(struct session *); 32 void server_callback_identify(int, short, void *); 33 34 void 35 server_fill_environ(struct session *s, struct environ *env) 36 { 37 char *term; 38 u_int idx; 39 long pid; 40 41 if (s != NULL) { 42 term = options_get_string(global_options, "default-terminal"); 43 environ_set(env, "TERM", "%s", term); 44 45 idx = s->id; 46 } else 47 idx = (u_int)-1; 48 pid = getpid(); 49 environ_set(env, "TMUX", "%s,%ld,%u", socket_path, pid, idx); 50 } 51 52 void 53 server_redraw_client(struct client *c) 54 { 55 c->flags |= CLIENT_REDRAW; 56 } 57 58 void 59 server_status_client(struct client *c) 60 { 61 c->flags |= CLIENT_STATUS; 62 } 63 64 void 65 server_redraw_session(struct session *s) 66 { 67 struct client *c; 68 69 TAILQ_FOREACH(c, &clients, entry) { 70 if (c->session == s) 71 server_redraw_client(c); 72 } 73 } 74 75 void 76 server_redraw_session_group(struct session *s) 77 { 78 struct session_group *sg; 79 80 if ((sg = session_group_find(s)) == NULL) 81 server_redraw_session(s); 82 else { 83 TAILQ_FOREACH(s, &sg->sessions, gentry) 84 server_redraw_session(s); 85 } 86 } 87 88 void 89 server_status_session(struct session *s) 90 { 91 struct client *c; 92 93 TAILQ_FOREACH(c, &clients, entry) { 94 if (c->session == s) 95 server_status_client(c); 96 } 97 } 98 99 void 100 server_status_session_group(struct session *s) 101 { 102 struct session_group *sg; 103 104 if ((sg = session_group_find(s)) == NULL) 105 server_status_session(s); 106 else { 107 TAILQ_FOREACH(s, &sg->sessions, gentry) 108 server_status_session(s); 109 } 110 } 111 112 void 113 server_redraw_window(struct window *w) 114 { 115 struct client *c; 116 117 TAILQ_FOREACH(c, &clients, entry) { 118 if (c->session != NULL && c->session->curw->window == w) 119 server_redraw_client(c); 120 } 121 w->flags |= WINDOW_REDRAW; 122 } 123 124 void 125 server_redraw_window_borders(struct window *w) 126 { 127 struct client *c; 128 129 TAILQ_FOREACH(c, &clients, entry) { 130 if (c->session != NULL && c->session->curw->window == w) 131 c->flags |= CLIENT_BORDERS; 132 } 133 } 134 135 void 136 server_status_window(struct window *w) 137 { 138 struct session *s; 139 140 /* 141 * This is slightly different. We want to redraw the status line of any 142 * clients containing this window rather than anywhere it is the 143 * current window. 144 */ 145 146 RB_FOREACH(s, sessions, &sessions) { 147 if (session_has(s, w)) 148 server_status_session(s); 149 } 150 } 151 152 void 153 server_lock(void) 154 { 155 struct client *c; 156 157 TAILQ_FOREACH(c, &clients, entry) { 158 if (c->session != NULL) 159 server_lock_client(c); 160 } 161 } 162 163 void 164 server_lock_session(struct session *s) 165 { 166 struct client *c; 167 168 TAILQ_FOREACH(c, &clients, entry) { 169 if (c->session == s) 170 server_lock_client(c); 171 } 172 } 173 174 void 175 server_lock_client(struct client *c) 176 { 177 const char *cmd; 178 179 if (c->flags & CLIENT_CONTROL) 180 return; 181 182 if (c->flags & CLIENT_SUSPENDED) 183 return; 184 185 cmd = options_get_string(c->session->options, "lock-command"); 186 if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) 187 return; 188 189 tty_stop_tty(&c->tty); 190 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP)); 191 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR)); 192 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); 193 194 c->flags |= CLIENT_SUSPENDED; 195 proc_send_s(c->peer, MSG_LOCK, cmd); 196 } 197 198 void 199 server_kill_window(struct window *w) 200 { 201 struct session *s, *next_s, *target_s; 202 struct session_group *sg; 203 struct winlink *wl; 204 205 next_s = RB_MIN(sessions, &sessions); 206 while (next_s != NULL) { 207 s = next_s; 208 next_s = RB_NEXT(sessions, &sessions, s); 209 210 if (!session_has(s, w)) 211 continue; 212 server_unzoom_window(w); 213 while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { 214 if (session_detach(s, wl)) { 215 server_destroy_session_group(s); 216 break; 217 } else 218 server_redraw_session_group(s); 219 } 220 221 if (options_get_number(s->options, "renumber-windows")) { 222 if ((sg = session_group_find(s)) != NULL) { 223 TAILQ_FOREACH(target_s, &sg->sessions, gentry) 224 session_renumber_windows(target_s); 225 } else 226 session_renumber_windows(s); 227 } 228 } 229 recalculate_sizes(); 230 } 231 232 int 233 server_link_window(struct session *src, struct winlink *srcwl, 234 struct session *dst, int dstidx, int killflag, int selectflag, 235 char **cause) 236 { 237 struct winlink *dstwl; 238 struct session_group *srcsg, *dstsg; 239 240 srcsg = session_group_find(src); 241 dstsg = session_group_find(dst); 242 if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) { 243 xasprintf(cause, "sessions are grouped"); 244 return (-1); 245 } 246 247 dstwl = NULL; 248 if (dstidx != -1) 249 dstwl = winlink_find_by_index(&dst->windows, dstidx); 250 if (dstwl != NULL) { 251 if (dstwl->window == srcwl->window) { 252 xasprintf(cause, "same index: %d", dstidx); 253 return (-1); 254 } 255 if (killflag) { 256 /* 257 * Can't use session_detach as it will destroy session 258 * if this makes it empty. 259 */ 260 notify_window_unlinked(dst, dstwl->window); 261 dstwl->flags &= ~WINLINK_ALERTFLAGS; 262 winlink_stack_remove(&dst->lastw, dstwl); 263 winlink_remove(&dst->windows, dstwl); 264 265 /* Force select/redraw if current. */ 266 if (dstwl == dst->curw) { 267 selectflag = 1; 268 dst->curw = NULL; 269 } 270 } 271 } 272 273 if (dstidx == -1) 274 dstidx = -1 - options_get_number(dst->options, "base-index"); 275 dstwl = session_attach(dst, srcwl->window, dstidx, cause); 276 if (dstwl == NULL) 277 return (-1); 278 279 if (selectflag) 280 session_select(dst, dstwl->idx); 281 server_redraw_session_group(dst); 282 283 return (0); 284 } 285 286 void 287 server_unlink_window(struct session *s, struct winlink *wl) 288 { 289 if (session_detach(s, wl)) 290 server_destroy_session_group(s); 291 else 292 server_redraw_session_group(s); 293 } 294 295 void 296 server_destroy_pane(struct window_pane *wp, int hooks) 297 { 298 struct window *w = wp->window; 299 int old_fd; 300 struct screen_write_ctx ctx; 301 struct grid_cell gc; 302 struct cmd_find_state fs; 303 304 old_fd = wp->fd; 305 if (wp->fd != -1) { 306 bufferevent_free(wp->event); 307 close(wp->fd); 308 wp->fd = -1; 309 } 310 311 if (options_get_number(w->options, "remain-on-exit")) { 312 if (old_fd == -1) 313 return; 314 screen_write_start(&ctx, wp, &wp->base); 315 screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); 316 screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1); 317 screen_write_linefeed(&ctx, 1); 318 memcpy(&gc, &grid_default_cell, sizeof gc); 319 gc.attr |= GRID_ATTR_BRIGHT; 320 screen_write_puts(&ctx, &gc, "Pane is dead"); 321 screen_write_stop(&ctx); 322 wp->flags |= PANE_REDRAW; 323 324 if (hooks && cmd_find_from_pane(&fs, wp) == 0) 325 hooks_run(hooks_get(fs.s), NULL, &fs, "pane-died"); 326 return; 327 } 328 329 server_unzoom_window(w); 330 layout_close_pane(wp); 331 window_remove_pane(w, wp); 332 333 if (hooks && cmd_find_from_window(&fs, w) == 0) 334 hooks_run(hooks_get(fs.s), NULL, &fs, "pane-exited"); 335 336 if (TAILQ_EMPTY(&w->panes)) 337 server_kill_window(w); 338 else 339 server_redraw_window(w); 340 } 341 342 void 343 server_destroy_session_group(struct session *s) 344 { 345 struct session_group *sg; 346 struct session *s1; 347 348 if ((sg = session_group_find(s)) == NULL) 349 server_destroy_session(s); 350 else { 351 TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { 352 server_destroy_session(s); 353 session_destroy(s); 354 } 355 } 356 } 357 358 struct session * 359 server_next_session(struct session *s) 360 { 361 struct session *s_loop, *s_out; 362 363 s_out = NULL; 364 RB_FOREACH(s_loop, sessions, &sessions) { 365 if (s_loop == s) 366 continue; 367 if (s_out == NULL || 368 timercmp(&s_loop->activity_time, &s_out->activity_time, <)) 369 s_out = s_loop; 370 } 371 return (s_out); 372 } 373 374 void 375 server_destroy_session(struct session *s) 376 { 377 struct client *c; 378 struct session *s_new; 379 380 if (!options_get_number(s->options, "detach-on-destroy")) 381 s_new = server_next_session(s); 382 else 383 s_new = NULL; 384 385 TAILQ_FOREACH(c, &clients, entry) { 386 if (c->session != s) 387 continue; 388 if (s_new == NULL) { 389 c->session = NULL; 390 c->flags |= CLIENT_EXIT; 391 } else { 392 c->last_session = NULL; 393 c->session = s_new; 394 server_client_set_key_table(c, NULL); 395 status_timer_start(c); 396 notify_attached_session_changed(c); 397 session_update_activity(s_new, NULL); 398 gettimeofday(&s_new->last_attached_time, NULL); 399 server_redraw_client(c); 400 alerts_check_session(s_new); 401 } 402 } 403 recalculate_sizes(); 404 } 405 406 void 407 server_check_unattached(void) 408 { 409 struct session *s; 410 411 /* 412 * If any sessions are no longer attached and have destroy-unattached 413 * set, collect them. 414 */ 415 RB_FOREACH(s, sessions, &sessions) { 416 if (!(s->flags & SESSION_UNATTACHED)) 417 continue; 418 if (options_get_number (s->options, "destroy-unattached")) 419 session_destroy(s); 420 } 421 } 422 423 void 424 server_set_identify(struct client *c) 425 { 426 struct timeval tv; 427 int delay; 428 429 delay = options_get_number(c->session->options, "display-panes-time"); 430 tv.tv_sec = delay / 1000; 431 tv.tv_usec = (delay % 1000) * 1000L; 432 433 if (event_initialized(&c->identify_timer)) 434 evtimer_del(&c->identify_timer); 435 evtimer_set(&c->identify_timer, server_callback_identify, c); 436 evtimer_add(&c->identify_timer, &tv); 437 438 c->flags |= CLIENT_IDENTIFY; 439 c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); 440 server_redraw_client(c); 441 } 442 443 void 444 server_clear_identify(struct client *c, struct window_pane *wp) 445 { 446 if (~c->flags & CLIENT_IDENTIFY) 447 return; 448 c->flags &= ~CLIENT_IDENTIFY; 449 450 if (c->identify_callback != NULL) 451 c->identify_callback(c, wp); 452 453 c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); 454 server_redraw_client(c); 455 } 456 457 void 458 server_callback_identify(__unused int fd, __unused short events, void *data) 459 { 460 server_clear_identify(data, NULL); 461 } 462 463 /* Set stdin callback. */ 464 int 465 server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, 466 void *), void *cb_data, char **cause) 467 { 468 if (c == NULL || c->session != NULL) { 469 *cause = xstrdup("no client with stdin"); 470 return (-1); 471 } 472 if (c->flags & CLIENT_TERMINAL) { 473 *cause = xstrdup("stdin is a tty"); 474 return (-1); 475 } 476 if (c->stdin_callback != NULL) { 477 *cause = xstrdup("stdin in use"); 478 return (-1); 479 } 480 481 c->stdin_callback_data = cb_data; 482 c->stdin_callback = cb; 483 484 c->references++; 485 486 if (c->stdin_closed) 487 c->stdin_callback(c, 1, c->stdin_callback_data); 488 489 proc_send(c->peer, MSG_STDIN, -1, NULL, 0); 490 491 return (0); 492 } 493 494 void 495 server_unzoom_window(struct window *w) 496 { 497 if (window_unzoom(w) == 0) { 498 server_redraw_window(w); 499 server_status_window(w); 500 } 501 } 502