1 /* $OpenBSD: server-fn.c,v 1.50 2011/01/23 11:03:43 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 <string.h> 22 #include <time.h> 23 #include <unistd.h> 24 25 #include "tmux.h" 26 27 struct session *server_next_session(struct session *); 28 void server_callback_identify(int, short, void *); 29 30 void 31 server_fill_environ(struct session *s, struct environ *env) 32 { 33 char var[MAXPATHLEN], *term; 34 u_int idx; 35 long pid; 36 37 if (s != NULL) { 38 term = options_get_string(&s->options, "default-terminal"); 39 environ_set(env, "TERM", term); 40 41 idx = s->idx; 42 } else 43 idx = -1; 44 pid = getpid(); 45 xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx); 46 environ_set(env, "TMUX", var); 47 } 48 49 void 50 server_write_client( 51 struct client *c, enum msgtype type, const void *buf, size_t len) 52 { 53 struct imsgbuf *ibuf = &c->ibuf; 54 55 if (c->flags & CLIENT_BAD) 56 return; 57 log_debug("writing %d to client %d", type, c->ibuf.fd); 58 imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len); 59 server_update_event(c); 60 } 61 62 void 63 server_write_session( 64 struct session *s, enum msgtype type, const void *buf, size_t len) 65 { 66 struct client *c; 67 u_int i; 68 69 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 70 c = ARRAY_ITEM(&clients, i); 71 if (c == NULL || c->session == NULL) 72 continue; 73 if (c->session == s) 74 server_write_client(c, type, buf, len); 75 } 76 } 77 78 void 79 server_redraw_client(struct client *c) 80 { 81 c->flags |= CLIENT_REDRAW; 82 } 83 84 void 85 server_status_client(struct client *c) 86 { 87 c->flags |= CLIENT_STATUS; 88 } 89 90 void 91 server_redraw_session(struct session *s) 92 { 93 struct client *c; 94 u_int i; 95 96 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 97 c = ARRAY_ITEM(&clients, i); 98 if (c == NULL || c->session == NULL) 99 continue; 100 if (c->session == s) 101 server_redraw_client(c); 102 } 103 } 104 105 void 106 server_redraw_session_group(struct session *s) 107 { 108 struct session_group *sg; 109 110 if ((sg = session_group_find(s)) == NULL) 111 server_redraw_session(s); 112 else { 113 TAILQ_FOREACH(s, &sg->sessions, gentry) 114 server_redraw_session(s); 115 } 116 } 117 118 void 119 server_status_session(struct session *s) 120 { 121 struct client *c; 122 u_int i; 123 124 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 125 c = ARRAY_ITEM(&clients, i); 126 if (c == NULL || c->session == NULL) 127 continue; 128 if (c->session == s) 129 server_status_client(c); 130 } 131 } 132 133 void 134 server_status_session_group(struct session *s) 135 { 136 struct session_group *sg; 137 138 if ((sg = session_group_find(s)) == NULL) 139 server_status_session(s); 140 else { 141 TAILQ_FOREACH(s, &sg->sessions, gentry) 142 server_status_session(s); 143 } 144 } 145 146 void 147 server_redraw_window(struct window *w) 148 { 149 struct client *c; 150 u_int i; 151 152 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 153 c = ARRAY_ITEM(&clients, i); 154 if (c == NULL || c->session == NULL) 155 continue; 156 if (c->session->curw->window == w) 157 server_redraw_client(c); 158 } 159 w->flags |= WINDOW_REDRAW; 160 } 161 162 void 163 server_redraw_window_borders(struct window *w) 164 { 165 struct client *c; 166 u_int i; 167 168 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 169 c = ARRAY_ITEM(&clients, i); 170 if (c == NULL || c->session == NULL) 171 continue; 172 if (c->session->curw->window == w) 173 c->flags |= CLIENT_BORDERS; 174 } 175 } 176 177 void 178 server_status_window(struct window *w) 179 { 180 struct session *s; 181 182 /* 183 * This is slightly different. We want to redraw the status line of any 184 * clients containing this window rather than any where it is the 185 * current window. 186 */ 187 188 RB_FOREACH(s, sessions, &sessions) { 189 if (session_has(s, w) != NULL) 190 server_status_session(s); 191 } 192 } 193 194 void 195 server_lock(void) 196 { 197 struct client *c; 198 u_int i; 199 200 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 201 c = ARRAY_ITEM(&clients, i); 202 if (c == NULL || c->session == NULL) 203 continue; 204 server_lock_client(c); 205 } 206 } 207 208 void 209 server_lock_session(struct session *s) 210 { 211 struct client *c; 212 u_int i; 213 214 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 215 c = ARRAY_ITEM(&clients, i); 216 if (c == NULL || c->session == NULL || c->session != s) 217 continue; 218 server_lock_client(c); 219 } 220 } 221 222 void 223 server_lock_client(struct client *c) 224 { 225 const char *cmd; 226 size_t cmdlen; 227 struct msg_lock_data lockdata; 228 229 if (c->flags & CLIENT_SUSPENDED) 230 return; 231 232 cmd = options_get_string(&c->session->options, "lock-command"); 233 cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd); 234 if (cmdlen >= sizeof lockdata.cmd) 235 return; 236 237 tty_stop_tty(&c->tty); 238 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP)); 239 tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR)); 240 241 c->flags |= CLIENT_SUSPENDED; 242 server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata); 243 } 244 245 void 246 server_kill_window(struct window *w) 247 { 248 struct session *s, *next_s; 249 struct winlink *wl; 250 251 next_s = RB_MIN(sessions, &sessions); 252 while (next_s != NULL) { 253 s = next_s; 254 next_s = RB_NEXT(sessions, &sessions, s); 255 256 if (session_has(s, w) == NULL) 257 continue; 258 while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { 259 if (session_detach(s, wl)) { 260 server_destroy_session_group(s); 261 break; 262 } else 263 server_redraw_session_group(s); 264 } 265 } 266 } 267 268 int 269 server_link_window(struct session *src, struct winlink *srcwl, 270 struct session *dst, int dstidx, int killflag, int selectflag, 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 dstwl->flags &= ~WINLINK_ALERTFLAGS; 296 winlink_stack_remove(&dst->lastw, dstwl); 297 winlink_remove(&dst->windows, dstwl); 298 299 /* Force select/redraw if current. */ 300 if (dstwl == dst->curw) { 301 selectflag = 1; 302 dst->curw = NULL; 303 } 304 } 305 } 306 307 if (dstidx == -1) 308 dstidx = -1 - options_get_number(&dst->options, "base-index"); 309 dstwl = session_attach(dst, srcwl->window, dstidx, cause); 310 if (dstwl == NULL) 311 return (-1); 312 313 if (selectflag) 314 session_select(dst, dstwl->idx); 315 server_redraw_session_group(dst); 316 317 return (0); 318 } 319 320 void 321 server_unlink_window(struct session *s, struct winlink *wl) 322 { 323 if (session_detach(s, wl)) 324 server_destroy_session_group(s); 325 else 326 server_redraw_session_group(s); 327 } 328 329 void 330 server_destroy_pane(struct window_pane *wp) 331 { 332 struct window *w = wp->window; 333 334 if (wp->fd != -1) { 335 close(wp->fd); 336 bufferevent_free(wp->event); 337 wp->fd = -1; 338 } 339 340 if (options_get_number(&w->options, "remain-on-exit")) 341 return; 342 343 layout_close_pane(wp); 344 window_remove_pane(w, wp); 345 346 if (TAILQ_EMPTY(&w->panes)) 347 server_kill_window(w); 348 else 349 server_redraw_window(w); 350 } 351 352 void 353 server_destroy_session_group(struct session *s) 354 { 355 struct session_group *sg; 356 357 if ((sg = session_group_find(s)) == NULL) 358 server_destroy_session(s); 359 else { 360 TAILQ_FOREACH(s, &sg->sessions, gentry) 361 server_destroy_session(s); 362 TAILQ_REMOVE(&session_groups, sg, entry); 363 xfree(sg); 364 } 365 } 366 367 struct session * 368 server_next_session(struct session *s) 369 { 370 struct session *s_loop, *s_out; 371 372 s_out = NULL; 373 RB_FOREACH(s_loop, sessions, &sessions) { 374 if (s_loop == s) 375 continue; 376 if (s_out == NULL || 377 timercmp(&s_loop->activity_time, &s_out->activity_time, <)) 378 s_out = s_loop; 379 } 380 return (s_out); 381 } 382 383 void 384 server_destroy_session(struct session *s) 385 { 386 struct client *c; 387 struct session *s_new; 388 u_int i; 389 390 if (!options_get_number(&s->options, "detach-on-destroy")) 391 s_new = server_next_session(s); 392 else 393 s_new = NULL; 394 395 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 396 c = ARRAY_ITEM(&clients, i); 397 if (c == NULL || c->session != s) 398 continue; 399 if (s_new == NULL) { 400 c->session = NULL; 401 c->flags |= CLIENT_EXIT; 402 } else { 403 c->last_session = NULL; 404 c->session = s_new; 405 session_update_activity(s_new); 406 server_redraw_client(c); 407 } 408 } 409 recalculate_sizes(); 410 } 411 412 void 413 server_check_unattached (void) 414 { 415 struct session *s; 416 417 /* 418 * If any sessions are no longer attached and have destroy-unattached 419 * set, collect them. 420 */ 421 RB_FOREACH(s, sessions, &sessions) { 422 if (!(s->flags & SESSION_UNATTACHED)) 423 continue; 424 if (options_get_number (&s->options, "destroy-unattached")) 425 session_destroy(s); 426 } 427 } 428 429 void 430 server_set_identify(struct client *c) 431 { 432 struct timeval tv; 433 int delay; 434 435 delay = options_get_number(&c->session->options, "display-panes-time"); 436 tv.tv_sec = delay / 1000; 437 tv.tv_usec = (delay % 1000) * 1000L; 438 439 evtimer_del(&c->identify_timer); 440 evtimer_set(&c->identify_timer, server_callback_identify, c); 441 evtimer_add(&c->identify_timer, &tv); 442 443 c->flags |= CLIENT_IDENTIFY; 444 c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); 445 server_redraw_client(c); 446 } 447 448 void 449 server_clear_identify(struct client *c) 450 { 451 if (c->flags & CLIENT_IDENTIFY) { 452 c->flags &= ~CLIENT_IDENTIFY; 453 c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); 454 server_redraw_client(c); 455 } 456 } 457 458 /* ARGSUSED */ 459 void 460 server_callback_identify(unused int fd, unused short events, void *data) 461 { 462 struct client *c = data; 463 464 server_clear_identify(c); 465 } 466 467 void 468 server_update_event(struct client *c) 469 { 470 short events; 471 472 events = 0; 473 if (!(c->flags & CLIENT_BAD)) 474 events |= EV_READ; 475 if (c->ibuf.w.queued > 0) 476 events |= EV_WRITE; 477 event_del(&c->event); 478 event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); 479 event_add(&c->event, NULL); 480 } 481