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