Annotation of src/usr.bin/tmux/server.c, Revision 1.55
1.55 ! nicm 1: /* $OpenBSD: server.c,v 1.54 2009/10/11 08:58:05 nicm Exp $ */
1.1 nicm 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: #include <sys/ioctl.h>
21: #include <sys/socket.h>
22: #include <sys/stat.h>
23: #include <sys/un.h>
24: #include <sys/wait.h>
25:
26: #include <errno.h>
27: #include <fcntl.h>
1.5 nicm 28: #include <paths.h>
1.1 nicm 29: #include <signal.h>
30: #include <stdio.h>
31: #include <stdlib.h>
32: #include <string.h>
33: #include <syslog.h>
34: #include <termios.h>
35: #include <time.h>
36: #include <unistd.h>
37:
38: #include "tmux.h"
39:
40: /*
41: * Main server functions.
42: */
43:
44: /* Client list. */
45: struct clients clients;
1.31 nicm 46: struct clients dead_clients;
1.1 nicm 47:
1.45 nicm 48: /* Mapping of a pollfd to an fd independent of its position in the array. */
49: struct poll_item {
50: struct pollfd pfd;
51:
52: RB_ENTRY(poll_item) entry;
53: };
54: RB_HEAD(poll_items, poll_item) poll_items;
55:
56: int server_poll_cmp(struct poll_item *, struct poll_item *);
57: struct pollfd *server_poll_lookup(int);
58: void server_poll_add(int, int);
59: struct pollfd *server_poll_flatten(int *);
60: void server_poll_parse(struct pollfd *);
61: void server_poll_reset(void);
62: RB_PROTOTYPE(poll_items, poll_item, entry, server_poll_cmp);
63: RB_GENERATE(poll_items, poll_item, entry, server_poll_cmp);
64:
1.13 nicm 65: void server_create_client(int);
1.1 nicm 66: int server_create_socket(void);
67: int server_main(int);
68: void server_shutdown(void);
1.42 nicm 69: int server_should_shutdown(void);
1.1 nicm 70: void server_child_signal(void);
1.45 nicm 71: void server_fill_windows(void);
72: void server_handle_windows(void);
73: void server_fill_clients(void);
74: void server_handle_clients(void);
1.49 nicm 75: void server_fill_jobs(void);
76: void server_handle_jobs(void);
1.13 nicm 77: void server_accept_client(int);
1.1 nicm 78: void server_handle_client(struct client *);
79: void server_handle_window(struct window *, struct window_pane *);
1.10 nicm 80: int server_check_window_bell(struct session *, struct window *);
1.1 nicm 81: int server_check_window_activity(struct session *,
82: struct window *);
83: int server_check_window_content(struct session *, struct window *,
84: struct window_pane *);
1.31 nicm 85: void server_clean_dead(void);
1.1 nicm 86: void server_lost_client(struct client *);
87: void server_check_window(struct window *);
88: void server_check_redraw(struct client *);
1.38 nicm 89: void server_set_title(struct client *);
1.1 nicm 90: void server_check_timers(struct client *);
1.53 nicm 91: void server_check_jobs(void);
1.46 nicm 92: void server_lock_server(void);
93: void server_lock_sessions(void);
1.51 nicm 94: void server_check_clients(void);
1.1 nicm 95: void server_second_timers(void);
96: int server_update_socket(void);
97:
1.45 nicm 98: int
99: server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2)
100: {
101: return (pitem1->pfd.fd - pitem2->pfd.fd);
102: }
103:
104: struct pollfd *
105: server_poll_lookup(int fd)
106: {
107: struct poll_item pitem;
108:
109: pitem.pfd.fd = fd;
110: return (&RB_FIND(poll_items, &poll_items, &pitem)->pfd);
111: }
112:
113: void
114: server_poll_add(int fd, int events)
115: {
116: struct poll_item *pitem;
117:
118: pitem = xmalloc(sizeof *pitem);
119: pitem->pfd.fd = fd;
120: pitem->pfd.events = events;
121: RB_INSERT(poll_items, &poll_items, pitem);
122: }
123:
124: struct pollfd *
125: server_poll_flatten(int *nfds)
126: {
127: struct poll_item *pitem;
128: struct pollfd *pfds;
129:
130: pfds = NULL;
131: *nfds = 0;
132: RB_FOREACH(pitem, poll_items, &poll_items) {
133: pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds);
134: pfds[*nfds].fd = pitem->pfd.fd;
135: pfds[*nfds].events = pitem->pfd.events;
136: (*nfds)++;
137: }
138: return (pfds);
139: }
140:
141: void
142: server_poll_parse(struct pollfd *pfds)
143: {
144: struct poll_item *pitem;
145: int nfds;
146:
147: nfds = 0;
148: RB_FOREACH(pitem, poll_items, &poll_items) {
149: pitem->pfd.revents = pfds[nfds].revents;
150: nfds++;
151: }
152: xfree(pfds);
153: }
154:
155: void
156: server_poll_reset(void)
157: {
158: struct poll_item *pitem;
159:
160: while (!RB_EMPTY(&poll_items)) {
161: pitem = RB_ROOT(&poll_items);
162: RB_REMOVE(poll_items, &poll_items, pitem);
163: xfree(pitem);
164: }
165: }
166:
1.1 nicm 167: /* Create a new client. */
1.13 nicm 168: void
1.1 nicm 169: server_create_client(int fd)
170: {
171: struct client *c;
172: int mode;
173: u_int i;
174:
175: if ((mode = fcntl(fd, F_GETFL)) == -1)
176: fatal("fcntl failed");
177: if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
178: fatal("fcntl failed");
179: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
180: fatal("fcntl failed");
181:
182: c = xcalloc(1, sizeof *c);
1.31 nicm 183: c->references = 0;
1.18 nicm 184: imsg_init(&c->ibuf, fd);
1.44 nicm 185:
186: if (gettimeofday(&c->tv, NULL) != 0)
187: fatal("gettimeofday failed");
1.1 nicm 188:
189: ARRAY_INIT(&c->prompt_hdata);
190:
191: c->tty.fd = -1;
192: c->title = NULL;
193:
194: c->session = NULL;
195: c->tty.sx = 80;
1.37 nicm 196: c->tty.sy = 24;
1.49 nicm 197:
1.1 nicm 198: screen_init(&c->status, c->tty.sx, 1, 0);
1.49 nicm 199: job_tree_init(&c->status_jobs);
1.1 nicm 200:
201: c->message_string = NULL;
202:
203: c->prompt_string = NULL;
204: c->prompt_buffer = NULL;
205: c->prompt_index = 0;
206:
207: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
208: if (ARRAY_ITEM(&clients, i) == NULL) {
209: ARRAY_SET(&clients, i, c);
1.13 nicm 210: return;
1.1 nicm 211: }
212: }
213: ARRAY_ADD(&clients, c);
1.20 nicm 214: log_debug("new client %d", fd);
1.1 nicm 215: }
216:
217: /* Fork new server. */
218: int
219: server_start(char *path)
220: {
1.16 nicm 221: struct client *c;
222: int pair[2], srv_fd;
223: char *cause;
224: char rpathbuf[MAXPATHLEN];
1.1 nicm 225:
226: /* The first client is special and gets a socketpair; create it. */
227: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
228: fatal("socketpair failed");
229:
230: switch (fork()) {
231: case -1:
232: fatal("fork failed");
233: case 0:
234: break;
235: default:
236: close(pair[1]);
237: return (pair[0]);
238: }
239: close(pair[0]);
240:
241: /*
242: * Must daemonise before loading configuration as the PID changes so
243: * $TMUX would be wrong for sessions created in the config file.
244: */
1.16 nicm 245: if (daemon(1, 0) != 0)
1.1 nicm 246: fatal("daemon failed");
247:
1.16 nicm 248: logfile("server");
249: log_debug("server started, pid %ld", (long) getpid());
250:
1.1 nicm 251: ARRAY_INIT(&windows);
252: ARRAY_INIT(&clients);
1.31 nicm 253: ARRAY_INIT(&dead_clients);
1.1 nicm 254: ARRAY_INIT(&sessions);
1.31 nicm 255: ARRAY_INIT(&dead_sessions);
1.47 nicm 256: TAILQ_INIT(&session_groups);
1.15 nicm 257: mode_key_init_trees();
1.1 nicm 258: key_bindings_init();
259: utf8_build();
260:
261: start_time = time(NULL);
262: socket_path = path;
263:
1.16 nicm 264: if (realpath(socket_path, rpathbuf) == NULL)
265: strlcpy(rpathbuf, socket_path, sizeof rpathbuf);
266: log_debug("socket path %s", socket_path);
267: setproctitle("server (%s)", rpathbuf);
268:
269: srv_fd = server_create_socket();
270: server_create_client(pair[1]);
271:
1.7 nicm 272: if (access(SYSTEM_CFG, R_OK) != 0) {
273: if (errno != ENOENT) {
1.16 nicm 274: xasprintf(
275: &cause, "%s: %s", strerror(errno), SYSTEM_CFG);
276: goto error;
277: }
1.24 nicm 278: } else if (load_cfg(SYSTEM_CFG, NULL, &cause) != 0)
1.16 nicm 279: goto error;
1.24 nicm 280: if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0)
1.16 nicm 281: goto error;
1.5 nicm 282:
1.16 nicm 283: exit(server_main(srv_fd));
1.1 nicm 284:
1.16 nicm 285: error:
286: /* Write the error and shutdown the server. */
287: c = ARRAY_FIRST(&clients);
1.1 nicm 288:
1.16 nicm 289: server_write_error(c, cause);
290: xfree(cause);
1.1 nicm 291:
1.27 nicm 292: sigterm = 1;
1.16 nicm 293: server_shutdown();
1.1 nicm 294:
295: exit(server_main(srv_fd));
296: }
297:
298: /* Create server socket. */
299: int
300: server_create_socket(void)
301: {
302: struct sockaddr_un sa;
303: size_t size;
304: mode_t mask;
305: int fd, mode;
306:
307: memset(&sa, 0, sizeof sa);
308: sa.sun_family = AF_UNIX;
309: size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
310: if (size >= sizeof sa.sun_path) {
311: errno = ENAMETOOLONG;
312: fatal("socket failed");
313: }
314: unlink(sa.sun_path);
315:
316: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
317: fatal("socket failed");
318:
319: mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
320: if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
321: fatal("bind failed");
322: umask(mask);
323:
324: if (listen(fd, 16) == -1)
325: fatal("listen failed");
326:
327: if ((mode = fcntl(fd, F_GETFL)) == -1)
328: fatal("fcntl failed");
329: if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
330: fatal("fcntl failed");
331: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
332: fatal("fcntl failed");
333:
334: return (fd);
335: }
336:
337: /* Main server loop. */
338: int
339: server_main(int srv_fd)
340: {
341: struct pollfd *pfds, *pfd;
342: int nfds, xtimeout;
1.42 nicm 343: u_int i;
1.1 nicm 344: time_t now, last;
345:
346: siginit();
1.20 nicm 347: log_debug("server socket is %d", srv_fd);
1.1 nicm 348:
349: last = time(NULL);
350:
351: pfds = NULL;
352: for (;;) {
353: /* If sigterm, kill all windows and clients. */
354: if (sigterm)
355: server_shutdown();
356:
1.42 nicm 357: /* Stop if no sessions or clients left. */
358: if (server_should_shutdown())
359: break;
360:
1.1 nicm 361: /* Handle child exit. */
362: if (sigchld) {
363: server_child_signal();
364: sigchld = 0;
365: }
366:
367: /* Recreate socket on SIGUSR1. */
368: if (sigusr1) {
369: close(srv_fd);
370: srv_fd = server_create_socket();
371: sigusr1 = 0;
372: }
373:
1.53 nicm 374: /* Collect any jobs that have died and process clients. */
375: server_check_jobs();
1.51 nicm 376: server_check_clients();
377:
1.45 nicm 378: /* Initialise pollfd array and add server socket. */
379: server_poll_reset();
380: server_poll_add(srv_fd, POLLIN);
1.1 nicm 381:
382: /* Fill window and client sockets. */
1.49 nicm 383: server_fill_jobs();
1.45 nicm 384: server_fill_windows();
385: server_fill_clients();
1.1 nicm 386:
387: /* Update socket permissions. */
388: xtimeout = INFTIM;
1.27 nicm 389: if (server_update_socket() != 0)
1.1 nicm 390: xtimeout = POLL_TIMEOUT;
391:
392: /* Do the poll. */
1.45 nicm 393: pfds = server_poll_flatten(&nfds);
1.4 nicm 394: if (poll(pfds, nfds, xtimeout) == -1) {
1.1 nicm 395: if (errno == EAGAIN || errno == EINTR)
396: continue;
397: fatal("poll failed");
398: }
1.45 nicm 399: server_poll_parse(pfds);
1.1 nicm 400:
401: /* Handle server socket. */
1.45 nicm 402: pfd = server_poll_lookup(srv_fd);
1.1 nicm 403: if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
404: fatalx("lost server socket");
405: if (pfd->revents & POLLIN) {
406: server_accept_client(srv_fd);
407: continue;
408: }
409:
410: /* Call second-based timers. */
411: now = time(NULL);
412: if (now != last) {
413: last = now;
414: server_second_timers();
415: }
416:
417: /* Set window names. */
418: set_window_names();
419:
1.50 nicm 420: /* Handle window and client sockets. */
1.49 nicm 421: server_handle_jobs();
1.45 nicm 422: server_handle_windows();
423: server_handle_clients();
1.1 nicm 424:
1.8 nicm 425: /* Collect any unset key bindings. */
426: key_bindings_clean();
1.31 nicm 427:
428: /* Collect dead clients and sessions. */
429: server_clean_dead();
1.1 nicm 430: }
1.45 nicm 431: server_poll_reset();
1.1 nicm 432:
433: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
434: if (ARRAY_ITEM(&sessions, i) != NULL)
435: session_destroy(ARRAY_ITEM(&sessions, i));
436: }
437: ARRAY_FREE(&sessions);
438:
439: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
440: if (ARRAY_ITEM(&clients, i) != NULL)
441: server_lost_client(ARRAY_ITEM(&clients, i));
442: }
443: ARRAY_FREE(&clients);
444:
1.15 nicm 445: mode_key_free_trees();
1.1 nicm 446: key_bindings_free();
447:
448: close(srv_fd);
449:
450: unlink(socket_path);
451: xfree(socket_path);
452:
1.6 nicm 453: options_free(&global_s_options);
454: options_free(&global_w_options);
1.1 nicm 455:
456: return (0);
457: }
458:
459: /* Kill all clients. */
460: void
461: server_shutdown(void)
462: {
463: struct session *s;
464: struct client *c;
465: u_int i, j;
466:
1.42 nicm 467: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
468: c = ARRAY_ITEM(&clients, i);
469: if (c != NULL) {
470: if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
471: server_lost_client(c);
472: else
473: server_write_client(c, MSG_SHUTDOWN, NULL, 0);
474: }
475: }
476:
1.1 nicm 477: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
478: s = ARRAY_ITEM(&sessions, i);
479: for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
480: c = ARRAY_ITEM(&clients, j);
481: if (c != NULL && c->session == s) {
482: s = NULL;
483: break;
484: }
485: }
486: if (s != NULL)
487: session_destroy(s);
488: }
1.42 nicm 489: }
490:
491: /* Check if the server should be shutting down (no more clients or windows). */
492: int
493: server_should_shutdown(void)
494: {
495: u_int i;
1.1 nicm 496:
1.42 nicm 497: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
498: if (ARRAY_ITEM(&sessions, i) != NULL)
499: return (0);
500: }
1.1 nicm 501: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1.42 nicm 502: if (ARRAY_ITEM(&clients, i) != NULL)
503: return (0);
1.1 nicm 504: }
1.42 nicm 505: return (1);
1.1 nicm 506: }
507:
508: /* Handle SIGCHLD. */
509: void
510: server_child_signal(void)
511: {
512: struct window *w;
513: struct window_pane *wp;
1.53 nicm 514: struct job *job;
1.1 nicm 515: int status;
516: pid_t pid;
517: u_int i;
518:
519: for (;;) {
520: switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
521: case -1:
522: if (errno == ECHILD)
523: return;
1.39 nicm 524: fatal("waitpid failed");
1.1 nicm 525: case 0:
526: return;
527: }
1.53 nicm 528: if (!WIFSTOPPED(status)) {
529: SLIST_FOREACH(job, &all_jobs, lentry) {
530: if (pid == job->pid) {
531: job->pid = -1;
532: job->status = status;
533: }
534: }
1.1 nicm 535: continue;
1.53 nicm 536: }
1.1 nicm 537: if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
538: continue;
539:
540: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
541: w = ARRAY_ITEM(&windows, i);
542: if (w == NULL)
543: continue;
544: TAILQ_FOREACH(wp, &w->panes, entry) {
545: if (wp->pid == pid) {
546: if (killpg(pid, SIGCONT) != 0)
547: kill(pid, SIGCONT);
548: }
549: }
550: }
551: }
552: }
553:
554: /* Fill window pollfds. */
555: void
1.45 nicm 556: server_fill_windows(void)
1.1 nicm 557: {
558: struct window *w;
559: struct window_pane *wp;
560: u_int i;
1.45 nicm 561: int events;
1.1 nicm 562:
563: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
564: w = ARRAY_ITEM(&windows, i);
565: if (w == NULL)
566: continue;
567:
1.45 nicm 568: TAILQ_FOREACH(wp, &w->panes, entry) {
569: if (wp->fd == -1)
570: continue;
571: events = POLLIN;
572: if (BUFFER_USED(wp->out) > 0)
573: events |= POLLOUT;
574: server_poll_add(wp->fd, events);
1.55 ! nicm 575:
! 576: if (wp->pipe_fd == -1)
! 577: continue;
! 578: events = 0;
! 579: if (BUFFER_USED(wp->pipe_buf) > 0)
! 580: events |= POLLOUT;
! 581: server_poll_add(wp->pipe_fd, events);
1.1 nicm 582: }
583: }
584: }
585:
586: /* Handle window pollfds. */
587: void
1.45 nicm 588: server_handle_windows(void)
1.1 nicm 589: {
590: struct window *w;
591: struct window_pane *wp;
1.45 nicm 592: struct pollfd *pfd;
1.1 nicm 593: u_int i;
594:
595: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
596: w = ARRAY_ITEM(&windows, i);
597: if (w == NULL)
598: continue;
599:
600: TAILQ_FOREACH(wp, &w->panes, entry) {
1.45 nicm 601: if (wp->fd == -1)
602: continue;
603: if ((pfd = server_poll_lookup(wp->fd)) == NULL)
604: continue;
605: if (buffer_poll(pfd, wp->in, wp->out) != 0) {
606: close(wp->fd);
607: wp->fd = -1;
608: } else
609: server_handle_window(w, wp);
1.55 ! nicm 610:
! 611: if (wp->pipe_fd == -1)
! 612: continue;
! 613: if ((pfd = server_poll_lookup(wp->pipe_fd)) == NULL)
! 614: continue;
! 615: if (buffer_poll(pfd, NULL, wp->pipe_buf) != 0) {
! 616: buffer_destroy(wp->pipe_buf);
! 617: close(wp->pipe_fd);
! 618: wp->pipe_fd = -1;
! 619: }
1.1 nicm 620: }
621:
622: server_check_window(w);
623: }
624: }
625:
1.51 nicm 626: /* Check clients for redraw and timers. */
627: void
628: server_check_clients(void)
629: {
630: struct client *c;
631: struct window *w;
632: struct window_pane *wp;
633: u_int i;
634:
635: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
636: c = ARRAY_ITEM(&clients, i);
637: if (c == NULL || c->session == NULL)
638: continue;
639:
640: server_check_timers(c);
641: server_check_redraw(c);
642: }
643:
644: /*
645: * Clear any window redraw flags (will have been redrawn as part of
646: * client).
647: */
648: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
649: w = ARRAY_ITEM(&windows, i);
650: if (w == NULL)
651: continue;
652:
653: w->flags &= ~WINDOW_REDRAW;
654: TAILQ_FOREACH(wp, &w->panes, entry)
655: wp->flags &= ~PANE_REDRAW;
656: }
657: }
658:
1.1 nicm 659: /* Check for general redraw on client. */
660: void
661: server_check_redraw(struct client *c)
662: {
1.51 nicm 663: struct session *s = c->session;
1.1 nicm 664: struct window_pane *wp;
665: int flags, redraw;
666:
667: flags = c->tty.flags & TTY_FREEZE;
668: c->tty.flags &= ~TTY_FREEZE;
669:
670: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
1.38 nicm 671: if (options_get_number(&s->options, "set-titles"))
672: server_set_title(c);
673:
1.1 nicm 674: if (c->message_string != NULL)
675: redraw = status_message_redraw(c);
676: else if (c->prompt_string != NULL)
677: redraw = status_prompt_redraw(c);
678: else
679: redraw = status_redraw(c);
680: if (!redraw)
681: c->flags &= ~CLIENT_STATUS;
682: }
683:
684: if (c->flags & CLIENT_REDRAW) {
1.41 nicm 685: screen_redraw_screen(c, 0);
1.1 nicm 686: c->flags &= ~CLIENT_STATUS;
687: } else {
688: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
689: if (wp->flags & PANE_REDRAW)
690: screen_redraw_pane(c, wp);
691: }
692: }
693:
694: if (c->flags & CLIENT_STATUS)
1.9 nicm 695: screen_redraw_screen(c, 1);
1.1 nicm 696:
697: c->tty.flags |= flags;
698:
699: c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
1.38 nicm 700: }
701:
702: /* Set client title. */
703: void
704: server_set_title(struct client *c)
705: {
706: struct session *s = c->session;
707: const char *template;
708: char *title;
709:
710: template = options_get_string(&s->options, "set-titles-string");
711:
1.49 nicm 712: title = status_replace(c, template, time(NULL));
1.38 nicm 713: if (c->title == NULL || strcmp(title, c->title) != 0) {
714: if (c->title != NULL)
715: xfree(c->title);
716: c->title = xstrdup(title);
717: tty_set_title(&c->tty, c->title);
718: }
719: xfree(title);
1.1 nicm 720: }
721:
722: /* Check for timers on client. */
723: void
724: server_check_timers(struct client *c)
725: {
1.51 nicm 726: struct session *s = c->session;
1.49 nicm 727: struct job *job;
1.1 nicm 728: struct timeval tv;
729: u_int interval;
730:
731: if (gettimeofday(&tv, NULL) != 0)
1.39 nicm 732: fatal("gettimeofday failed");
1.1 nicm 733:
1.26 nicm 734: if (c->flags & CLIENT_IDENTIFY && timercmp(&tv, &c->identify_timer, >))
735: server_clear_identify(c);
736:
1.1 nicm 737: if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
738: status_message_clear(c);
739:
740: if (c->message_string != NULL || c->prompt_string != NULL) {
741: /*
742: * Don't need timed redraw for messages/prompts so bail now.
743: * The status timer isn't reset when they are redrawn anyway.
744: */
745: return;
746: }
747: if (!options_get_number(&s->options, "status"))
748: return;
749:
750: /* Check timer; resolution is only a second so don't be too clever. */
751: interval = options_get_number(&s->options, "status-interval");
752: if (interval == 0)
753: return;
754: if (tv.tv_sec < c->status_timer.tv_sec ||
1.49 nicm 755: ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) {
756: /* Run the jobs for this client and schedule for redraw. */
757: RB_FOREACH(job, jobs, &c->status_jobs)
758: job_run(job);
1.1 nicm 759: c->flags |= CLIENT_STATUS;
1.49 nicm 760: }
1.1 nicm 761: }
762:
763: /* Fill client pollfds. */
764: void
1.45 nicm 765: server_fill_clients(void)
1.1 nicm 766: {
1.51 nicm 767: struct client *c;
768: u_int i;
769: int events;
1.1 nicm 770:
771: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
772: c = ARRAY_ITEM(&clients, i);
773:
1.45 nicm 774: if (c != NULL) {
775: events = 0;
1.16 nicm 776: if (!(c->flags & CLIENT_BAD))
1.45 nicm 777: events |= POLLIN;
1.18 nicm 778: if (c->ibuf.w.queued > 0)
1.45 nicm 779: events |= POLLOUT;
780: server_poll_add(c->ibuf.fd, events);
1.1 nicm 781: }
782:
1.45 nicm 783: if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
784: c->tty.fd != -1 && c->session != NULL) {
785: events = POLLIN;
1.1 nicm 786: if (BUFFER_USED(c->tty.out) > 0)
1.45 nicm 787: events |= POLLOUT;
788: server_poll_add(c->tty.fd, events);
1.1 nicm 789: }
790: }
791: }
792:
1.49 nicm 793: /* Fill in job fds. */
794: void
795: server_fill_jobs(void)
796: {
797: struct job *job;
798:
1.50 nicm 799: SLIST_FOREACH(job, &all_jobs, lentry) {
1.49 nicm 800: if (job->fd == -1)
801: continue;
802: server_poll_add(job->fd, POLLIN);
803: }
804: }
805:
806: /* Handle job fds. */
807: void
808: server_handle_jobs(void)
809: {
810: struct job *job;
811: struct pollfd *pfd;
812:
1.50 nicm 813: SLIST_FOREACH(job, &all_jobs, lentry) {
1.49 nicm 814: if (job->fd == -1)
815: continue;
816: if ((pfd = server_poll_lookup(job->fd)) == NULL)
817: continue;
818: if (buffer_poll(pfd, job->out, NULL) != 0) {
819: close(job->fd);
820: job->fd = -1;
821: }
1.53 nicm 822: }
823: }
824:
825: /* Handle job fds. */
826: void
827: server_check_jobs(void)
828: {
829: struct job *job;
1.54 nicm 830:
831: restart:
1.53 nicm 832: SLIST_FOREACH(job, &all_jobs, lentry) {
833: if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1)
834: continue;
1.54 nicm 835: job->flags |= JOB_DONE;
836:
837: if (job->callbackfn != NULL) {
1.53 nicm 838: job->callbackfn(job);
1.54 nicm 839: goto restart; /* could be freed by callback */
840: }
1.49 nicm 841: }
842: }
843:
1.1 nicm 844: /* Handle client pollfds. */
845: void
1.45 nicm 846: server_handle_clients(void)
1.1 nicm 847: {
848: struct client *c;
1.45 nicm 849: struct pollfd *pfd;
1.1 nicm 850: u_int i;
851:
852: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
853: c = ARRAY_ITEM(&clients, i);
854:
855: if (c != NULL) {
1.45 nicm 856: if ((pfd = server_poll_lookup(c->ibuf.fd)) == NULL)
857: continue;
858: if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) {
1.1 nicm 859: server_lost_client(c);
860: continue;
1.18 nicm 861: }
862:
1.45 nicm 863: if (pfd->revents & POLLOUT) {
1.18 nicm 864: if (msgbuf_write(&c->ibuf.w) < 0) {
865: server_lost_client(c);
866: continue;
867: }
868: }
869:
870: if (c->flags & CLIENT_BAD) {
871: if (c->ibuf.w.queued == 0)
1.16 nicm 872: server_lost_client(c);
1.18 nicm 873: continue;
1.45 nicm 874: } else if (pfd->revents & POLLIN) {
1.18 nicm 875: if (server_msg_dispatch(c) != 0) {
876: server_lost_client(c);
877: continue;
878: }
879: }
1.1 nicm 880: }
881:
882: if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
883: c->tty.fd != -1 && c->session != NULL) {
1.45 nicm 884: if ((pfd = server_poll_lookup(c->tty.fd)) == NULL)
885: continue;
886: if (buffer_poll(pfd, c->tty.in, c->tty.out) != 0)
1.1 nicm 887: server_lost_client(c);
888: else
889: server_handle_client(c);
890: }
891: }
892: }
893:
894: /* accept(2) and create new client. */
1.13 nicm 895: void
1.1 nicm 896: server_accept_client(int srv_fd)
897: {
898: struct sockaddr_storage sa;
899: socklen_t slen = sizeof sa;
900: int fd;
901:
902: fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
903: if (fd == -1) {
904: if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
1.13 nicm 905: return;
1.1 nicm 906: fatal("accept failed");
907: }
908: if (sigterm) {
909: close(fd);
1.13 nicm 910: return;
1.1 nicm 911: }
1.13 nicm 912: server_create_client(fd);
1.1 nicm 913: }
914:
915: /* Input data from client. */
916: void
917: server_handle_client(struct client *c)
918: {
1.32 nicm 919: struct window *w;
1.1 nicm 920: struct window_pane *wp;
921: struct screen *s;
1.48 nicm 922: struct options *oo;
1.1 nicm 923: struct timeval tv;
924: struct key_binding *bd;
1.40 nicm 925: struct keylist *keylist;
1.52 nicm 926: struct mouse_event mouse;
1.40 nicm 927: int key, status, xtimeout, mode, isprefix;
928: u_int i;
1.1 nicm 929:
930: xtimeout = options_get_number(&c->session->options, "repeat-time");
931: if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
932: if (gettimeofday(&tv, NULL) != 0)
1.39 nicm 933: fatal("gettimeofday failed");
1.1 nicm 934: if (timercmp(&tv, &c->repeat_timer, >))
935: c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
936: }
937:
938: /* Process keys. */
1.40 nicm 939: keylist = options_get_data(&c->session->options, "prefix");
1.52 nicm 940: while (tty_keys_next(&c->tty, &key, &mouse) == 0) {
1.1 nicm 941: if (c->session == NULL)
942: return;
1.46 nicm 943:
944: c->session->activity = time(NULL);
1.32 nicm 945: w = c->session->curw->window;
946: wp = w->active; /* could die */
1.48 nicm 947: oo = &c->session->options;
1.1 nicm 948:
1.32 nicm 949: /* Special case: number keys jump to pane in identify mode. */
950: if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
951: wp = window_pane_at_index(w, key - '0');
952: if (wp != NULL && window_pane_visible(wp))
953: window_set_active_pane(w, wp);
954: server_clear_identify(c);
955: continue;
956: }
957:
1.1 nicm 958: status_message_clear(c);
1.26 nicm 959: server_clear_identify(c);
1.1 nicm 960: if (c->prompt_string != NULL) {
961: status_prompt_key(c, key);
962: continue;
963: }
964:
965: /* Check for mouse keys. */
966: if (key == KEYC_MOUSE) {
1.48 nicm 967: if (options_get_number(oo, "mouse-select-pane")) {
1.52 nicm 968: window_set_active_at(w, mouse.x, mouse.y);
1.48 nicm 969: wp = w->active;
970: }
1.52 nicm 971: window_pane_mouse(wp, c, &mouse);
1.1 nicm 972: continue;
973: }
974:
1.40 nicm 975: /* Is this a prefix key? */
976: isprefix = 0;
977: for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
978: if (key == ARRAY_ITEM(keylist, i)) {
979: isprefix = 1;
980: break;
981: }
982: }
983:
1.1 nicm 984: /* No previous prefix key. */
985: if (!(c->flags & CLIENT_PREFIX)) {
1.40 nicm 986: if (isprefix)
1.1 nicm 987: c->flags |= CLIENT_PREFIX;
1.14 nicm 988: else {
989: /* Try as a non-prefix key binding. */
990: if ((bd = key_bindings_lookup(key)) == NULL)
991: window_pane_key(wp, c, key);
992: else
993: key_bindings_dispatch(bd, c);
994: }
1.1 nicm 995: continue;
996: }
997:
998: /* Prefix key already pressed. Reset prefix and lookup key. */
999: c->flags &= ~CLIENT_PREFIX;
1.14 nicm 1000: if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
1.1 nicm 1001: /* If repeating, treat this as a key, else ignore. */
1002: if (c->flags & CLIENT_REPEAT) {
1003: c->flags &= ~CLIENT_REPEAT;
1.40 nicm 1004: if (isprefix)
1.1 nicm 1005: c->flags |= CLIENT_PREFIX;
1006: else
1007: window_pane_key(wp, c, key);
1008: }
1009: continue;
1010: }
1011:
1012: /* If already repeating, but this key can't repeat, skip it. */
1013: if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
1014: c->flags &= ~CLIENT_REPEAT;
1.40 nicm 1015: if (isprefix)
1.1 nicm 1016: c->flags |= CLIENT_PREFIX;
1017: else
1018: window_pane_key(wp, c, key);
1019: continue;
1020: }
1021:
1022: /* If this key can repeat, reset the repeat flags and timer. */
1023: if (xtimeout != 0 && bd->can_repeat) {
1024: c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
1025:
1026: tv.tv_sec = xtimeout / 1000;
1027: tv.tv_usec = (xtimeout % 1000) * 1000L;
1028: if (gettimeofday(&c->repeat_timer, NULL) != 0)
1.39 nicm 1029: fatal("gettimeofday failed");
1.1 nicm 1030: timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
1031: }
1032:
1033: /* Dispatch the command. */
1034: key_bindings_dispatch(bd, c);
1035: }
1036: if (c->session == NULL)
1037: return;
1.48 nicm 1038: w = c->session->curw->window;
1039: wp = w->active;
1040: oo = &c->session->options;
1.1 nicm 1041: s = wp->screen;
1042:
1.21 nicm 1043: /*
1044: * Update cursor position and mode settings. The scroll region and
1045: * attributes are cleared across poll(2) as this is the most likely
1046: * time a user may interrupt tmux, for example with ~^Z in ssh(1). This
1047: * is a compromise between excessive resets and likelihood of an
1048: * interrupt.
1049: *
1050: * tty_region/tty_reset/tty_update_mode already take care of not
1051: * resetting things that are already in their default state.
1052: */
1.48 nicm 1053: status = options_get_number(oo, "status");
1.17 nicm 1054: tty_region(&c->tty, 0, c->tty.sy - 1, 0);
1.11 nicm 1055: if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
1056: tty_cursor(&c->tty, 0, 0, 0, 0);
1057: else
1.1 nicm 1058: tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff);
1059:
1060: mode = s->mode;
1.48 nicm 1061: if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
1062: options_get_number(oo, "mouse-select-pane"))
1063: mode |= MODE_MOUSE;
1.1 nicm 1064: tty_update_mode(&c->tty, mode);
1.21 nicm 1065: tty_reset(&c->tty);
1.1 nicm 1066: }
1067:
1068: /* Lost a client. */
1069: void
1070: server_lost_client(struct client *c)
1071: {
1072: u_int i;
1073:
1074: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1075: if (ARRAY_ITEM(&clients, i) == c)
1076: ARRAY_SET(&clients, i, NULL);
1077: }
1.20 nicm 1078: log_debug("lost client %d", c->ibuf.fd);
1.1 nicm 1079:
1.25 nicm 1080: /*
1081: * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
1082: * and tty_free might close an unrelated fd.
1083: */
1084: if (c->flags & CLIENT_TERMINAL)
1085: tty_free(&c->tty);
1.1 nicm 1086:
1087: screen_free(&c->status);
1.49 nicm 1088: job_tree_free(&c->status_jobs);
1.1 nicm 1089:
1090: if (c->title != NULL)
1091: xfree(c->title);
1092:
1093: if (c->message_string != NULL)
1094: xfree(c->message_string);
1095:
1096: if (c->prompt_string != NULL)
1097: xfree(c->prompt_string);
1098: if (c->prompt_buffer != NULL)
1099: xfree(c->prompt_buffer);
1100: for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
1101: xfree(ARRAY_ITEM(&c->prompt_hdata, i));
1102: ARRAY_FREE(&c->prompt_hdata);
1103:
1104: if (c->cwd != NULL)
1105: xfree(c->cwd);
1106:
1.18 nicm 1107: close(c->ibuf.fd);
1108: imsg_clear(&c->ibuf);
1.31 nicm 1109:
1110: for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
1111: if (ARRAY_ITEM(&dead_clients, i) == NULL) {
1112: ARRAY_SET(&dead_clients, i, c);
1113: break;
1114: }
1115: }
1116: if (i == ARRAY_LENGTH(&dead_clients))
1117: ARRAY_ADD(&dead_clients, c);
1118: c->flags |= CLIENT_DEAD;
1.1 nicm 1119:
1120: recalculate_sizes();
1.31 nicm 1121: }
1122:
1123: /* Free dead, unreferenced clients and sessions. */
1124: void
1125: server_clean_dead(void)
1126: {
1127: struct session *s;
1128: struct client *c;
1129: u_int i;
1130:
1131: for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) {
1132: s = ARRAY_ITEM(&dead_sessions, i);
1133: if (s == NULL || s->references != 0)
1134: continue;
1135: ARRAY_SET(&dead_sessions, i, NULL);
1136: xfree(s);
1137: }
1138:
1139: for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
1140: c = ARRAY_ITEM(&dead_clients, i);
1141: if (c == NULL || c->references != 0)
1142: continue;
1143: ARRAY_SET(&dead_clients, i, NULL);
1144: xfree(c);
1145: }
1.1 nicm 1146: }
1147:
1148: /* Handle window data. */
1149: void
1150: server_handle_window(struct window *w, struct window_pane *wp)
1151: {
1152: struct session *s;
1153: u_int i;
1154: int update;
1155:
1156: window_pane_parse(wp);
1157:
1158: if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0)
1159: return;
1160:
1161: update = 0;
1162: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1163: s = ARRAY_ITEM(&sessions, i);
1164: if (s == NULL || !session_has(s, w))
1165: continue;
1166:
1.10 nicm 1167: update += server_check_window_bell(s, w);
1.1 nicm 1168: update += server_check_window_activity(s, w);
1169: update += server_check_window_content(s, w, wp);
1170: }
1171: if (update)
1172: server_status_window(w);
1173:
1174: w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
1175: }
1176:
1177: int
1.10 nicm 1178: server_check_window_bell(struct session *s, struct window *w)
1.1 nicm 1179: {
1180: struct client *c;
1181: u_int i;
1.10 nicm 1182: int action, visual;
1.1 nicm 1183:
1184: if (!(w->flags & WINDOW_BELL))
1185: return (0);
1186: if (session_alert_has_window(s, w, WINDOW_BELL))
1187: return (0);
1188: session_alert_add(s, w, WINDOW_BELL);
1189:
1190: action = options_get_number(&s->options, "bell-action");
1191: switch (action) {
1192: case BELL_ANY:
1193: if (s->flags & SESSION_UNATTACHED)
1194: break;
1.10 nicm 1195: visual = options_get_number(&s->options, "visual-bell");
1.1 nicm 1196: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1197: c = ARRAY_ITEM(&clients, i);
1.10 nicm 1198: if (c == NULL || c->session != s)
1199: continue;
1200: if (!visual) {
1.1 nicm 1201: tty_putcode(&c->tty, TTYC_BEL);
1.10 nicm 1202: continue;
1203: }
1204: if (c->session->curw->window == w) {
1205: status_message_set(c, "Bell in current window");
1206: continue;
1207: }
1208: status_message_set(c, "Bell in window %u",
1209: winlink_find_by_window(&s->windows, w)->idx);
1.1 nicm 1210: }
1211: break;
1212: case BELL_CURRENT:
1.10 nicm 1213: if (s->flags & SESSION_UNATTACHED)
1.1 nicm 1214: break;
1.10 nicm 1215: visual = options_get_number(&s->options, "visual-bell");
1.1 nicm 1216: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1217: c = ARRAY_ITEM(&clients, i);
1.10 nicm 1218: if (c == NULL || c->session != s)
1219: continue;
1220: if (c->session->curw->window != w)
1221: continue;
1222: if (!visual) {
1.1 nicm 1223: tty_putcode(&c->tty, TTYC_BEL);
1.10 nicm 1224: continue;
1225: }
1226: status_message_set(c, "Bell in current window");
1.1 nicm 1227: }
1228: break;
1229: }
1230: return (1);
1231: }
1232:
1233: int
1234: server_check_window_activity(struct session *s, struct window *w)
1235: {
1.10 nicm 1236: struct client *c;
1237: u_int i;
1238:
1.1 nicm 1239: if (!(w->flags & WINDOW_ACTIVITY))
1240: return (0);
1.10 nicm 1241:
1.1 nicm 1242: if (!options_get_number(&w->options, "monitor-activity"))
1243: return (0);
1.10 nicm 1244:
1.1 nicm 1245: if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
1246: return (0);
1.10 nicm 1247: if (s->curw->window == w)
1248: return (0);
1249:
1.1 nicm 1250: session_alert_add(s, w, WINDOW_ACTIVITY);
1.10 nicm 1251: if (s->flags & SESSION_UNATTACHED)
1252: return (0);
1253: if (options_get_number(&s->options, "visual-activity")) {
1254: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1255: c = ARRAY_ITEM(&clients, i);
1256: if (c == NULL || c->session != s)
1257: continue;
1258: status_message_set(c, "Activity in window %u",
1259: winlink_find_by_window(&s->windows, w)->idx);
1260: }
1261: }
1262:
1.1 nicm 1263: return (1);
1264: }
1265:
1266: int
1267: server_check_window_content(
1268: struct session *s, struct window *w, struct window_pane *wp)
1269: {
1.10 nicm 1270: struct client *c;
1271: u_int i;
1272: char *found, *ptr;
1273:
1274: if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */
1275: return (0);
1.1 nicm 1276:
1.10 nicm 1277: ptr = options_get_string(&w->options, "monitor-content");
1278: if (ptr == NULL || *ptr == '\0')
1.1 nicm 1279: return (0);
1.10 nicm 1280:
1281: if (session_alert_has_window(s, w, WINDOW_CONTENT))
1.1 nicm 1282: return (0);
1.10 nicm 1283: if (s->curw->window == w)
1.1 nicm 1284: return (0);
1.10 nicm 1285:
1.3 nicm 1286: if ((found = window_pane_search(wp, ptr, NULL)) == NULL)
1.1 nicm 1287: return (0);
1.10 nicm 1288: xfree(found);
1289:
1.1 nicm 1290: session_alert_add(s, w, WINDOW_CONTENT);
1.10 nicm 1291: if (s->flags & SESSION_UNATTACHED)
1292: return (0);
1293: if (options_get_number(&s->options, "visual-content")) {
1294: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1295: c = ARRAY_ITEM(&clients, i);
1296: if (c == NULL || c->session != s)
1297: continue;
1298: status_message_set(c, "Content in window %u",
1299: winlink_find_by_window(&s->windows, w)->idx);
1300: }
1301: }
1302:
1.1 nicm 1303: return (1);
1304: }
1305:
1.2 nicm 1306: /* Check if window still exists. */
1.1 nicm 1307: void
1308: server_check_window(struct window *w)
1309: {
1310: struct window_pane *wp, *wq;
1.22 nicm 1311: struct options *oo = &w->options;
1.1 nicm 1312: struct session *s;
1313: struct winlink *wl;
1.35 nicm 1314: u_int i;
1.22 nicm 1315: int destroyed;
1.1 nicm 1316:
1317: destroyed = 1;
1318:
1319: wp = TAILQ_FIRST(&w->panes);
1320: while (wp != NULL) {
1321: wq = TAILQ_NEXT(wp, entry);
1.2 nicm 1322: /*
1323: * If the pane has died and the remain-on-exit flag is not set,
1324: * remove the pane; otherwise, if the flag is set, don't allow
1325: * the window to be destroyed (or it'll close when the last
1326: * pane dies).
1327: */
1.23 nicm 1328: if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) {
1.11 nicm 1329: layout_close_pane(wp);
1.1 nicm 1330: window_remove_pane(w, wp);
1331: server_redraw_window(w);
1.2 nicm 1332: } else
1333: destroyed = 0;
1.1 nicm 1334: wp = wq;
1335: }
1336:
1337: if (!destroyed)
1338: return;
1339:
1340: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1341: s = ARRAY_ITEM(&sessions, i);
1342: if (s == NULL)
1343: continue;
1344: if (!session_has(s, w))
1345: continue;
1346:
1347: restart:
1348: /* Detach window and either redraw or kill clients. */
1349: RB_FOREACH(wl, winlinks, &s->windows) {
1350: if (wl->window != w)
1351: continue;
1.34 nicm 1352: if (session_detach(s, wl)) {
1.47 nicm 1353: server_destroy_session_group(s);
1.34 nicm 1354: break;
1.1 nicm 1355: }
1.34 nicm 1356: server_redraw_session(s);
1.47 nicm 1357: server_status_session_group(s);
1.1 nicm 1358: goto restart;
1359: }
1360: }
1361:
1362: recalculate_sizes();
1363: }
1364:
1.46 nicm 1365: /* Lock the server if ALL sessions have hit the time limit. */
1366: void
1367: server_lock_server(void)
1368: {
1369: struct session *s;
1370: u_int i;
1371: int timeout;
1372: time_t t;
1373:
1374: t = time(NULL);
1375: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1376: if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
1377: continue;
1378:
1379: timeout = options_get_number(&s->options, "lock-after-time");
1380: if (timeout <= 0 || t <= s->activity + timeout)
1381: return; /* not timed out */
1382: }
1383:
1384: server_lock();
1385: recalculate_sizes();
1386: }
1387:
1388: /* Lock any sessions which have timed out. */
1389: void
1390: server_lock_sessions(void)
1391: {
1392: struct session *s;
1393: u_int i;
1394: int timeout;
1395: time_t t;
1396:
1397: t = time(NULL);
1398: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1399: if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
1400: continue;
1401:
1402: timeout = options_get_number(&s->options, "lock-after-time");
1403: if (timeout > 0 && t > s->activity + timeout) {
1404: server_lock_session(s);
1405: recalculate_sizes();
1406: }
1407: }
1408: }
1409:
1.1 nicm 1410: /* Call any once-per-second timers. */
1411: void
1412: server_second_timers(void)
1413: {
1414: struct window *w;
1415: struct window_pane *wp;
1416: u_int i;
1417:
1.46 nicm 1418: if (options_get_number(&global_s_options, "lock-server"))
1419: server_lock_server();
1420: else
1421: server_lock_sessions();
1.1 nicm 1422:
1423: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
1424: w = ARRAY_ITEM(&windows, i);
1425: if (w == NULL)
1426: continue;
1427:
1428: TAILQ_FOREACH(wp, &w->panes, entry) {
1429: if (wp->mode != NULL && wp->mode->timer != NULL)
1430: wp->mode->timer(wp);
1431: }
1432: }
1433: }
1434:
1435: /* Update socket execute permissions based on whether sessions are attached. */
1436: int
1437: server_update_socket(void)
1438: {
1439: struct session *s;
1440: u_int i;
1441: static int last = -1;
1442: int n;
1443:
1444: n = 0;
1445: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1446: s = ARRAY_ITEM(&sessions, i);
1447: if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
1448: n++;
1449: break;
1450: }
1451: }
1452:
1453: if (n != last) {
1454: last = n;
1455: if (n != 0)
1456: chmod(socket_path, S_IRWXU);
1457: else
1458: chmod(socket_path, S_IRUSR|S_IWUSR);
1459: }
1460:
1461: return (n);
1462: }