server-fn.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <sys/uio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <time.h>
  22. #include <unistd.h>
  23. #include "tmux.h"
  24. struct session *server_next_session(struct session *);
  25. void server_callback_identify(int, short, void *);
  26. void
  27. server_fill_environ(struct session *s, struct environ *env)
  28. {
  29. char *term;
  30. u_int idx;
  31. long pid;
  32. if (s != NULL) {
  33. term = options_get_string(global_options, "default-terminal");
  34. environ_set(env, "TERM", "%s", term);
  35. idx = s->id;
  36. } else
  37. idx = (u_int)-1;
  38. pid = getpid();
  39. environ_set(env, "TMUX", "%s,%ld,%u", socket_path, pid, idx);
  40. }
  41. void
  42. server_redraw_client(struct client *c)
  43. {
  44. c->flags |= CLIENT_REDRAW;
  45. }
  46. void
  47. server_status_client(struct client *c)
  48. {
  49. c->flags |= CLIENT_STATUS;
  50. }
  51. void
  52. server_redraw_session(struct session *s)
  53. {
  54. struct client *c;
  55. TAILQ_FOREACH(c, &clients, entry) {
  56. if (c->session == s)
  57. server_redraw_client(c);
  58. }
  59. }
  60. void
  61. server_redraw_session_group(struct session *s)
  62. {
  63. struct session_group *sg;
  64. if ((sg = session_group_find(s)) == NULL)
  65. server_redraw_session(s);
  66. else {
  67. TAILQ_FOREACH(s, &sg->sessions, gentry)
  68. server_redraw_session(s);
  69. }
  70. }
  71. void
  72. server_status_session(struct session *s)
  73. {
  74. struct client *c;
  75. TAILQ_FOREACH(c, &clients, entry) {
  76. if (c->session == s)
  77. server_status_client(c);
  78. }
  79. }
  80. void
  81. server_status_session_group(struct session *s)
  82. {
  83. struct session_group *sg;
  84. if ((sg = session_group_find(s)) == NULL)
  85. server_status_session(s);
  86. else {
  87. TAILQ_FOREACH(s, &sg->sessions, gentry)
  88. server_status_session(s);
  89. }
  90. }
  91. void
  92. server_redraw_window(struct window *w)
  93. {
  94. struct client *c;
  95. TAILQ_FOREACH(c, &clients, entry) {
  96. if (c->session != NULL && c->session->curw->window == w)
  97. server_redraw_client(c);
  98. }
  99. w->flags |= WINDOW_REDRAW;
  100. }
  101. void
  102. server_redraw_window_borders(struct window *w)
  103. {
  104. struct client *c;
  105. TAILQ_FOREACH(c, &clients, entry) {
  106. if (c->session != NULL && c->session->curw->window == w)
  107. c->flags |= CLIENT_BORDERS;
  108. }
  109. }
  110. void
  111. server_status_window(struct window *w)
  112. {
  113. struct session *s;
  114. /*
  115. * This is slightly different. We want to redraw the status line of any
  116. * clients containing this window rather than anywhere it is the
  117. * current window.
  118. */
  119. RB_FOREACH(s, sessions, &sessions) {
  120. if (session_has(s, w))
  121. server_status_session(s);
  122. }
  123. }
  124. void
  125. server_lock(void)
  126. {
  127. struct client *c;
  128. TAILQ_FOREACH(c, &clients, entry) {
  129. if (c->session != NULL)
  130. server_lock_client(c);
  131. }
  132. }
  133. void
  134. server_lock_session(struct session *s)
  135. {
  136. struct client *c;
  137. TAILQ_FOREACH(c, &clients, entry) {
  138. if (c->session == s)
  139. server_lock_client(c);
  140. }
  141. }
  142. void
  143. server_lock_client(struct client *c)
  144. {
  145. const char *cmd;
  146. if (c->flags & CLIENT_CONTROL)
  147. return;
  148. if (c->flags & CLIENT_SUSPENDED)
  149. return;
  150. cmd = options_get_string(c->session->options, "lock-command");
  151. if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
  152. return;
  153. tty_stop_tty(&c->tty);
  154. tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
  155. tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
  156. tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
  157. c->flags |= CLIENT_SUSPENDED;
  158. proc_send_s(c->peer, MSG_LOCK, cmd);
  159. }
  160. void
  161. server_kill_window(struct window *w)
  162. {
  163. struct session *s, *next_s, *target_s;
  164. struct session_group *sg;
  165. struct winlink *wl;
  166. next_s = RB_MIN(sessions, &sessions);
  167. while (next_s != NULL) {
  168. s = next_s;
  169. next_s = RB_NEXT(sessions, &sessions, s);
  170. if (!session_has(s, w))
  171. continue;
  172. server_unzoom_window(w);
  173. while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
  174. if (session_detach(s, wl)) {
  175. server_destroy_session_group(s);
  176. break;
  177. } else
  178. server_redraw_session_group(s);
  179. }
  180. if (options_get_number(s->options, "renumber-windows")) {
  181. if ((sg = session_group_find(s)) != NULL) {
  182. TAILQ_FOREACH(target_s, &sg->sessions, gentry)
  183. session_renumber_windows(target_s);
  184. } else
  185. session_renumber_windows(s);
  186. }
  187. }
  188. recalculate_sizes();
  189. }
  190. int
  191. server_link_window(struct session *src, struct winlink *srcwl,
  192. struct session *dst, int dstidx, int killflag, int selectflag,
  193. char **cause)
  194. {
  195. struct winlink *dstwl;
  196. struct session_group *srcsg, *dstsg;
  197. srcsg = session_group_find(src);
  198. dstsg = session_group_find(dst);
  199. if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
  200. xasprintf(cause, "sessions are grouped");
  201. return (-1);
  202. }
  203. dstwl = NULL;
  204. if (dstidx != -1)
  205. dstwl = winlink_find_by_index(&dst->windows, dstidx);
  206. if (dstwl != NULL) {
  207. if (dstwl->window == srcwl->window) {
  208. xasprintf(cause, "same index: %d", dstidx);
  209. return (-1);
  210. }
  211. if (killflag) {
  212. /*
  213. * Can't use session_detach as it will destroy session
  214. * if this makes it empty.
  215. */
  216. notify_window_unlinked(dst, dstwl->window);
  217. dstwl->flags &= ~WINLINK_ALERTFLAGS;
  218. winlink_stack_remove(&dst->lastw, dstwl);
  219. winlink_remove(&dst->windows, dstwl);
  220. /* Force select/redraw if current. */
  221. if (dstwl == dst->curw) {
  222. selectflag = 1;
  223. dst->curw = NULL;
  224. }
  225. }
  226. }
  227. if (dstidx == -1)
  228. dstidx = -1 - options_get_number(dst->options, "base-index");
  229. dstwl = session_attach(dst, srcwl->window, dstidx, cause);
  230. if (dstwl == NULL)
  231. return (-1);
  232. if (selectflag)
  233. session_select(dst, dstwl->idx);
  234. server_redraw_session_group(dst);
  235. return (0);
  236. }
  237. void
  238. server_unlink_window(struct session *s, struct winlink *wl)
  239. {
  240. if (session_detach(s, wl))
  241. server_destroy_session_group(s);
  242. else
  243. server_redraw_session_group(s);
  244. }
  245. void
  246. server_destroy_pane(struct window_pane *wp, int hooks)
  247. {
  248. struct window *w = wp->window;
  249. int old_fd;
  250. struct screen_write_ctx ctx;
  251. struct grid_cell gc;
  252. struct cmd_find_state fs;
  253. old_fd = wp->fd;
  254. if (wp->fd != -1) {
  255. #ifdef HAVE_UTEMPTER
  256. utempter_remove_record(wp->fd);
  257. #endif
  258. bufferevent_free(wp->event);
  259. close(wp->fd);
  260. wp->fd = -1;
  261. }
  262. if (options_get_number(w->options, "remain-on-exit")) {
  263. if (old_fd == -1)
  264. return;
  265. screen_write_start(&ctx, wp, &wp->base);
  266. screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
  267. screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
  268. screen_write_linefeed(&ctx, 1);
  269. memcpy(&gc, &grid_default_cell, sizeof gc);
  270. gc.attr |= GRID_ATTR_BRIGHT;
  271. screen_write_puts(&ctx, &gc, "Pane is dead");
  272. screen_write_stop(&ctx);
  273. wp->flags |= PANE_REDRAW;
  274. if (hooks && cmd_find_from_pane(&fs, wp) == 0)
  275. hooks_run(hooks_get(fs.s), NULL, &fs, "pane-died");
  276. return;
  277. }
  278. server_unzoom_window(w);
  279. layout_close_pane(wp);
  280. window_remove_pane(w, wp);
  281. if (hooks && cmd_find_from_window(&fs, w) == 0)
  282. hooks_run(hooks_get(fs.s), NULL, &fs, "pane-exited");
  283. if (TAILQ_EMPTY(&w->panes))
  284. server_kill_window(w);
  285. else
  286. server_redraw_window(w);
  287. }
  288. void
  289. server_destroy_session_group(struct session *s)
  290. {
  291. struct session_group *sg;
  292. struct session *s1;
  293. if ((sg = session_group_find(s)) == NULL)
  294. server_destroy_session(s);
  295. else {
  296. TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
  297. server_destroy_session(s);
  298. session_destroy(s);
  299. }
  300. }
  301. }
  302. struct session *
  303. server_next_session(struct session *s)
  304. {
  305. struct session *s_loop, *s_out;
  306. s_out = NULL;
  307. RB_FOREACH(s_loop, sessions, &sessions) {
  308. if (s_loop == s)
  309. continue;
  310. if (s_out == NULL ||
  311. timercmp(&s_loop->activity_time, &s_out->activity_time, <))
  312. s_out = s_loop;
  313. }
  314. return (s_out);
  315. }
  316. void
  317. server_destroy_session(struct session *s)
  318. {
  319. struct client *c;
  320. struct session *s_new;
  321. if (!options_get_number(s->options, "detach-on-destroy"))
  322. s_new = server_next_session(s);
  323. else
  324. s_new = NULL;
  325. TAILQ_FOREACH(c, &clients, entry) {
  326. if (c->session != s)
  327. continue;
  328. if (s_new == NULL) {
  329. c->session = NULL;
  330. c->flags |= CLIENT_EXIT;
  331. } else {
  332. c->last_session = NULL;
  333. c->session = s_new;
  334. server_client_set_key_table(c, NULL);
  335. status_timer_start(c);
  336. notify_attached_session_changed(c);
  337. session_update_activity(s_new, NULL);
  338. gettimeofday(&s_new->last_attached_time, NULL);
  339. server_redraw_client(c);
  340. alerts_check_session(s_new);
  341. }
  342. }
  343. recalculate_sizes();
  344. }
  345. void
  346. server_check_unattached(void)
  347. {
  348. struct session *s;
  349. /*
  350. * If any sessions are no longer attached and have destroy-unattached
  351. * set, collect them.
  352. */
  353. RB_FOREACH(s, sessions, &sessions) {
  354. if (!(s->flags & SESSION_UNATTACHED))
  355. continue;
  356. if (options_get_number (s->options, "destroy-unattached"))
  357. session_destroy(s);
  358. }
  359. }
  360. void
  361. server_set_identify(struct client *c)
  362. {
  363. struct timeval tv;
  364. int delay;
  365. delay = options_get_number(c->session->options, "display-panes-time");
  366. tv.tv_sec = delay / 1000;
  367. tv.tv_usec = (delay % 1000) * 1000L;
  368. if (event_initialized(&c->identify_timer))
  369. evtimer_del(&c->identify_timer);
  370. evtimer_set(&c->identify_timer, server_callback_identify, c);
  371. evtimer_add(&c->identify_timer, &tv);
  372. c->flags |= CLIENT_IDENTIFY;
  373. c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
  374. server_redraw_client(c);
  375. }
  376. void
  377. server_clear_identify(struct client *c)
  378. {
  379. if (c->flags & CLIENT_IDENTIFY) {
  380. c->flags &= ~CLIENT_IDENTIFY;
  381. c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
  382. server_redraw_client(c);
  383. }
  384. }
  385. void
  386. server_callback_identify(__unused int fd, __unused short events, void *data)
  387. {
  388. struct client *c = data;
  389. server_clear_identify(c);
  390. }
  391. /* Set stdin callback. */
  392. int
  393. server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
  394. void *), void *cb_data, char **cause)
  395. {
  396. if (c == NULL || c->session != NULL) {
  397. *cause = xstrdup("no client with stdin");
  398. return (-1);
  399. }
  400. if (c->flags & CLIENT_TERMINAL) {
  401. *cause = xstrdup("stdin is a tty");
  402. return (-1);
  403. }
  404. if (c->stdin_callback != NULL) {
  405. *cause = xstrdup("stdin in use");
  406. return (-1);
  407. }
  408. c->stdin_callback_data = cb_data;
  409. c->stdin_callback = cb;
  410. c->references++;
  411. if (c->stdin_closed)
  412. c->stdin_callback(c, 1, c->stdin_callback_data);
  413. proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
  414. return (0);
  415. }
  416. void
  417. server_unzoom_window(struct window *w)
  418. {
  419. if (window_unzoom(w) == 0) {
  420. server_redraw_window(w);
  421. server_status_window(w);
  422. }
  423. }