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