From d01e7aac89fefff31e668ac2d668c84c345b9e67 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 10 May 2020 16:52:46 +0100 Subject: [PATCH] Add a -D flag to ask tmux not to daemonize, useful both for running a debugger (lldb does not have follow-fork-mode) and for running with a managed supervisor init system. GitHub issue 2190. --- proc.c | 9 +++++++-- server-client.c | 15 +++++++++++++-- server.c | 44 +++++++++++++++++++++++++++----------------- tmux.1 | 13 ++++++++++++- tmux.c | 9 +++++++-- tmux.h | 1 + tty.c | 7 ++++++- 7 files changed, 73 insertions(+), 25 deletions(-) diff --git a/proc.c b/proc.c index 111c3730..f3d6063d 100644 --- a/proc.c +++ b/proc.c @@ -35,6 +35,7 @@ struct tmuxproc { void (*signalcb)(int); + struct event ev_sigint; struct event ev_sighup; struct event ev_sigchld; struct event ev_sigcont; @@ -219,10 +220,13 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int)) sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; - sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); + sigaction(SIGTTIN, &sa, NULL); + sigaction(SIGTTOU, &sa, NULL); + signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp); + signal_add(&tp->ev_sigint, NULL); signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp); signal_add(&tp->ev_sighup, NULL); signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp); @@ -249,10 +253,10 @@ proc_clear_signals(struct tmuxproc *tp, int defaults) sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_DFL; - sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); + signal_del(&tp->ev_sigint); signal_del(&tp->ev_sighup); signal_del(&tp->ev_sigchld); signal_del(&tp->ev_sigcont); @@ -262,6 +266,7 @@ proc_clear_signals(struct tmuxproc *tp, int defaults) signal_del(&tp->ev_sigwinch); if (defaults) { + sigaction(SIGINT, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCONT, &sa, NULL); diff --git a/server-client.c b/server-client.c index 794cb41e..c9250491 100644 --- a/server-client.c +++ b/server-client.c @@ -236,11 +236,22 @@ server_client_create(int fd) int server_client_open(struct client *c, char **cause) { + const char *ttynam = _PATH_TTY; + if (c->flags & CLIENT_CONTROL) return (0); - if (strcmp(c->ttyname, "/dev/tty") == 0) { - *cause = xstrdup("can't use /dev/tty"); + if (strcmp(c->ttyname, ttynam) || + ((isatty(STDIN_FILENO) && + (ttynam = ttyname(STDIN_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0) || + (isatty(STDOUT_FILENO) && + (ttynam = ttyname(STDOUT_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0) || + (isatty(STDERR_FILENO) && + (ttynam = ttyname(STDERR_FILENO)) != NULL && + strcmp(c->ttyname, ttynam) == 0))) { + xasprintf(cause, "can't use %s", c->ttyname); return (-1); } diff --git a/server.c b/server.c index 2f262f3c..c451989c 100644 --- a/server.c +++ b/server.c @@ -160,29 +160,35 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, struct client *c; char *cause = NULL; - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) - fatal("socketpair failed"); - server_client_flags = flags; - sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); - switch (fork()) { - case -1: - fatal("fork failed"); - case 0: - break; - default: - sigprocmask(SIG_SETMASK, &oldset, NULL); - close(pair[1]); - return (pair[0]); + + if (~flags & CLIENT_NOFORK) { + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) + fatal("socketpair failed"); + + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + sigprocmask(SIG_SETMASK, &oldset, NULL); + close(pair[1]); + return (pair[0]); + } + close(pair[0]); + if (daemon(1, 0) != 0) + fatal("daemon failed"); } - close(pair[0]); - if (daemon(1, 0) != 0) - fatal("daemon failed"); + + server_client_flags = flags; proc_clear_signals(client, 0); + if (event_reinit(base) != 0) fatalx("event_reinit failed"); server_proc = proc_start("server"); + proc_set_signals(server_proc, server_signal); sigprocmask(SIG_SETMASK, &oldset, NULL); @@ -204,7 +210,10 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, server_fd = server_create_socket(flags, &cause); if (server_fd != -1) server_update_socket(); - c = server_client_create(pair[1]); + if (~flags & CLIENT_NOFORK) + c = server_client_create(pair[1]); + else + options_set_number(global_options, "exit-empty", 0); if (lockfd >= 0) { unlink(lockfile); @@ -395,6 +404,7 @@ server_signal(int sig) log_debug("%s: %s", __func__, strsignal(sig)); switch (sig) { + case SIGINT: case SIGTERM: server_exit = 1; server_send_exit(); diff --git a/tmux.1 b/tmux.1 index 56322d85..d93f34aa 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2CluvV +.Op Fl 2CDluvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -122,6 +122,17 @@ This option is for compatibility with when .Nm is used as a login shell. +.It Fl D +Do not start the +.Nm +server as a daemon. +This also turns the +.Ic exit-empty +option off. +With +.Fl D , +.Ar command +may not be specified. .It Fl f Ar file Specify an alternative configuration file. By default, diff --git a/tmux.c b/tmux.c index 48b3b8b9..b600702c 100644 --- a/tmux.c +++ b/tmux.c @@ -54,7 +54,7 @@ static __dead void usage(void) { fprintf(stderr, - "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2CDluvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); @@ -327,7 +327,7 @@ main(int argc, char **argv) if (**argv == '-') flags = CLIENT_LOGIN; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:T:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:CDdf:lL:qS:T:uUvV")) != -1) { switch (opt) { case '2': tty_add_features(&feat, "256", ":,"); @@ -335,6 +335,9 @@ main(int argc, char **argv) case 'c': shell_command = optarg; break; + case 'D': + flags |= CLIENT_NOFORK; + break; case 'C': if (flags & CLIENT_CONTROL) flags |= CLIENT_CONTROLCONTROL; @@ -378,6 +381,8 @@ main(int argc, char **argv) if (shell_command != NULL && argc != 0) usage(); + if ((flags & CLIENT_NOFORK) && argc != 0) + usage(); if ((ptm_fd = getptmfd()) == -1) err(1, "getptmfd"); diff --git a/tmux.h b/tmux.h index 5853030c..6a05e93d 100644 --- a/tmux.h +++ b/tmux.h @@ -1586,6 +1586,7 @@ struct client { #define CLIENT_DEFAULTSOCKET 0x8000000 #define CLIENT_STARTSERVER 0x10000000 #define CLIENT_REDRAWPANES 0x20000000 +#define CLIENT_NOFORK 0x40000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ diff --git a/tty.c b/tty.c index c8efeac7..a1e2f2c6 100644 --- a/tty.c +++ b/tty.c @@ -154,16 +154,21 @@ tty_read_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; struct client *c = tty->client; + const char *name = c->name; size_t size = EVBUFFER_LENGTH(tty->in); int nread; nread = evbuffer_read(tty->in, tty->fd, -1); if (nread == 0 || nread == -1) { + if (nread == 0) + log_debug("%s: read closed", name); + else + log_debug("%s: read error: %s", name, strerror(errno)); event_del(&tty->event_in); server_client_lost(tty->client); return; } - log_debug("%s: read %d bytes (already %zu)", c->name, nread, size); + log_debug("%s: read %d bytes (already %zu)", name, nread, size); while (tty_keys_next(tty)) ;