123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- /* $OpenBSD$ */
- /*
- * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <sys/un.h>
- #include <sys/wait.h>
- #include <errno.h>
- #include <event.h>
- #include <fcntl.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <termios.h>
- #include <time.h>
- #include <unistd.h>
- #include "tmux.h"
- #include "tmate.h"
- /*
- * Main server functions.
- */
- struct clients clients;
- struct tmuxproc *server_proc;
- int server_fd;
- int server_exit;
- struct event server_ev_accept;
- struct cmd_find_state marked_pane;
- int server_create_socket(void);
- int server_loop(void);
- int server_should_exit(void);
- void server_send_exit(void);
- void server_accept(int, short, void *);
- void server_signal(int);
- void server_child_signal(void);
- void server_child_exited(pid_t, int);
- void server_child_stopped(pid_t, int);
- /* Set marked pane. */
- void
- server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
- {
- cmd_find_clear_state(&marked_pane, NULL, 0);
- marked_pane.s = s;
- marked_pane.wl = wl;
- marked_pane.w = wl->window;
- marked_pane.wp = wp;
- }
- /* Clear marked pane. */
- void
- server_clear_marked(void)
- {
- cmd_find_clear_state(&marked_pane, NULL, 0);
- }
- /* Is this the marked pane? */
- int
- server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
- {
- if (s == NULL || wl == NULL || wp == NULL)
- return (0);
- if (marked_pane.s != s || marked_pane.wl != wl)
- return (0);
- if (marked_pane.wp != wp)
- return (0);
- return (server_check_marked());
- }
- /* Check if the marked pane is still valid. */
- int
- server_check_marked(void)
- {
- return (cmd_find_valid_state(&marked_pane));
- }
- /* Create server socket. */
- int
- server_create_socket(void)
- {
- struct sockaddr_un sa;
- size_t size;
- mode_t mask;
- int fd;
- memset(&sa, 0, sizeof sa);
- sa.sun_family = AF_UNIX;
- size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
- if (size >= sizeof sa.sun_path) {
- errno = ENAMETOOLONG;
- return (-1);
- }
- unlink(sa.sun_path);
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- return (-1);
- mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
- if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1)
- return (-1);
- umask(mask);
- if (listen(fd, 16) == -1)
- return (-1);
- setblocking(fd, 0);
- return (fd);
- }
- #ifdef TMATE
- static void tmate_set_editor_mode(void)
- {
- switch (options_get_number(global_s_options, "status-keys")) {
- case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "emacs"}); break;
- case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "vi"}); break;
- }
- switch (options_get_number(global_w_options, "mode-keys")) {
- case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "emacs"}); break;
- case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "vi"}); break;
- }
- }
- #endif
- /* Fork new server. */
- int
- server_start(struct event_base *base, int lockfd, char *lockfile)
- {
- int pair[2];
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
- fatal("socketpair failed");
- server_proc = proc_start("server", base, 1, server_signal);
- if (server_proc == NULL) {
- close(pair[1]);
- return (pair[0]);
- }
- close(pair[0]);
- if (log_get_level() > 3)
- tty_create_log();
- #ifdef __OpenBSD__
- if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
- "tty ps", NULL) != 0)
- fatal("pledge failed");
- #endif
- RB_INIT(&windows);
- RB_INIT(&all_window_panes);
- TAILQ_INIT(&clients);
- RB_INIT(&sessions);
- TAILQ_INIT(&session_groups);
- mode_key_init_trees();
- #ifdef TMATE
- tmate_session_init(base);
- #endif
- key_bindings_init();
- gettimeofday(&start_time, NULL);
- server_fd = server_create_socket();
- if (server_fd == -1)
- fatal("couldn't create socket");
- server_update_socket();
- server_client_create(pair[1]);
- if (lockfd >= 0) {
- unlink(lockfile);
- free(lockfile);
- close(lockfd);
- }
- #ifdef TMATE
- tmate_set_editor_mode();
- #endif
- start_cfg();
- status_prompt_load_history();
- #ifdef TMATE
- tmate_write_ready();
- #endif
- server_add_accept(0);
- proc_loop(server_proc, server_loop);
- status_prompt_save_history();
- #ifdef TMATE
- unlink(socket_path);
- #endif
- exit(0);
- }
- /* Server loop callback. */
- int
- server_loop(void)
- {
- struct client *c;
- server_client_loop();
- if (!options_get_number(global_options, "exit-unattached")) {
- if (!RB_EMPTY(&sessions))
- return (0);
- }
- TAILQ_FOREACH(c, &clients, entry) {
- if (c->session != NULL)
- return (0);
- }
- /*
- * No attached clients therefore want to exit - flush any waiting
- * clients but don't actually exit until they've gone.
- */
- cmd_wait_for_flush();
- if (!TAILQ_EMPTY(&clients))
- return (0);
- return (1);
- }
- /* Exit the server by killing all clients and windows. */
- void
- server_send_exit(void)
- {
- struct client *c, *c1;
- struct session *s, *s1;
- cmd_wait_for_flush();
- TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
- if (c->flags & CLIENT_SUSPENDED)
- server_client_lost(c);
- else
- proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
- c->session = NULL;
- }
- RB_FOREACH_SAFE(s, sessions, &sessions, s1)
- session_destroy(s);
- }
- /* Update socket execute permissions based on whether sessions are attached. */
- void
- server_update_socket(void)
- {
- struct session *s;
- static int last = -1;
- int n, mode;
- struct stat sb;
- n = 0;
- RB_FOREACH(s, sessions, &sessions) {
- if (!(s->flags & SESSION_UNATTACHED)) {
- n++;
- break;
- }
- }
- if (n != last) {
- last = n;
- if (stat(socket_path, &sb) != 0)
- return;
- mode = sb.st_mode;
- if (n != 0) {
- if (mode & S_IRUSR)
- mode |= S_IXUSR;
- if (mode & S_IRGRP)
- mode |= S_IXGRP;
- if (mode & S_IROTH)
- mode |= S_IXOTH;
- } else
- mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
- chmod(socket_path, mode);
- }
- }
- /* Callback for server socket. */
- void
- server_accept(int fd, short events, __unused void *data)
- {
- struct sockaddr_storage sa;
- socklen_t slen = sizeof sa;
- int newfd;
- server_add_accept(0);
- if (!(events & EV_READ))
- return;
- newfd = accept(fd, (struct sockaddr *) &sa, &slen);
- if (newfd == -1) {
- if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
- return;
- if (errno == ENFILE || errno == EMFILE) {
- /* Delete and don't try again for 1 second. */
- server_add_accept(1);
- return;
- }
- fatal("accept failed");
- }
- if (server_exit) {
- close(newfd);
- return;
- }
- server_client_create(newfd);
- }
- /*
- * Add accept event. If timeout is nonzero, add as a timeout instead of a read
- * event - used to backoff when running out of file descriptors.
- */
- void
- server_add_accept(int timeout)
- {
- struct timeval tv = { timeout, 0 };
- if (event_initialized(&server_ev_accept))
- event_del(&server_ev_accept);
- if (timeout == 0) {
- event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
- NULL);
- event_add(&server_ev_accept, NULL);
- } else {
- event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
- server_accept, NULL);
- event_add(&server_ev_accept, &tv);
- }
- }
- /* Signal handler. */
- void
- server_signal(int sig)
- {
- int fd;
- switch (sig) {
- case SIGTERM:
- server_exit = 1;
- server_send_exit();
- break;
- case SIGCHLD:
- server_child_signal();
- break;
- case SIGUSR1:
- event_del(&server_ev_accept);
- fd = server_create_socket();
- if (fd != -1) {
- close(server_fd);
- server_fd = fd;
- server_update_socket();
- }
- server_add_accept(0);
- break;
- }
- }
- /* Handle SIGCHLD. */
- void
- server_child_signal(void)
- {
- int status;
- pid_t pid;
- for (;;) {
- switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
- case -1:
- if (errno == ECHILD)
- return;
- fatal("waitpid failed");
- case 0:
- return;
- }
- if (WIFSTOPPED(status))
- server_child_stopped(pid, status);
- else if (WIFEXITED(status) || WIFSIGNALED(status))
- server_child_exited(pid, status);
- }
- }
- /* Handle exited children. */
- void
- server_child_exited(pid_t pid, int status)
- {
- struct window *w, *w1;
- struct window_pane *wp;
- struct job *job;
- RB_FOREACH_SAFE(w, windows, &windows, w1) {
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->pid == pid) {
- wp->status = status;
- server_destroy_pane(wp, 1);
- break;
- }
- }
- }
- LIST_FOREACH(job, &all_jobs, lentry) {
- if (pid == job->pid) {
- job_died(job, status); /* might free job */
- break;
- }
- }
- }
- /* Handle stopped children. */
- void
- server_child_stopped(pid_t pid, int status)
- {
- struct window *w;
- struct window_pane *wp;
- if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
- return;
- RB_FOREACH(w, windows, &windows) {
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->pid == pid) {
- if (killpg(pid, SIGCONT) != 0)
- kill(pid, SIGCONT);
- }
- }
- }
- }
|