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.
This commit is contained in:
Nicholas Marriott 2020-05-10 16:52:46 +01:00
parent 5fa377d927
commit d01e7aac89
7 changed files with 73 additions and 25 deletions

9
proc.c
View File

@ -35,6 +35,7 @@ struct tmuxproc {
void (*signalcb)(int); void (*signalcb)(int);
struct event ev_sigint;
struct event ev_sighup; struct event ev_sighup;
struct event ev_sigchld; struct event ev_sigchld;
struct event ev_sigcont; struct event ev_sigcont;
@ -219,10 +220,13 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int))
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN; sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL); sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGTSTP, &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_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp);
signal_add(&tp->ev_sighup, NULL); signal_add(&tp->ev_sighup, NULL);
signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp); 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_flags = SA_RESTART;
sa.sa_handler = SIG_DFL; sa.sa_handler = SIG_DFL;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL); sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL); sigaction(SIGTSTP, &sa, NULL);
signal_del(&tp->ev_sigint);
signal_del(&tp->ev_sighup); signal_del(&tp->ev_sighup);
signal_del(&tp->ev_sigchld); signal_del(&tp->ev_sigchld);
signal_del(&tp->ev_sigcont); signal_del(&tp->ev_sigcont);
@ -262,6 +266,7 @@ proc_clear_signals(struct tmuxproc *tp, int defaults)
signal_del(&tp->ev_sigwinch); signal_del(&tp->ev_sigwinch);
if (defaults) { if (defaults) {
sigaction(SIGINT, &sa, NULL);
sigaction(SIGHUP, &sa, NULL); sigaction(SIGHUP, &sa, NULL);
sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGCONT, &sa, NULL); sigaction(SIGCONT, &sa, NULL);

View File

@ -236,11 +236,22 @@ server_client_create(int fd)
int int
server_client_open(struct client *c, char **cause) server_client_open(struct client *c, char **cause)
{ {
const char *ttynam = _PATH_TTY;
if (c->flags & CLIENT_CONTROL) if (c->flags & CLIENT_CONTROL)
return (0); return (0);
if (strcmp(c->ttyname, "/dev/tty") == 0) { if (strcmp(c->ttyname, ttynam) ||
*cause = xstrdup("can't use /dev/tty"); ((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); return (-1);
} }

View File

@ -160,29 +160,35 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
struct client *c; struct client *c;
char *cause = NULL; char *cause = NULL;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
fatal("socketpair failed");
server_client_flags = flags;
sigfillset(&set); sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset); sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) {
case -1: if (~flags & CLIENT_NOFORK) {
fatal("fork failed"); if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
case 0: fatal("socketpair failed");
break;
default: switch (fork()) {
sigprocmask(SIG_SETMASK, &oldset, NULL); case -1:
close(pair[1]); fatal("fork failed");
return (pair[0]); 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) server_client_flags = flags;
fatal("daemon failed");
proc_clear_signals(client, 0); proc_clear_signals(client, 0);
if (event_reinit(base) != 0) if (event_reinit(base) != 0)
fatalx("event_reinit failed"); fatalx("event_reinit failed");
server_proc = proc_start("server"); server_proc = proc_start("server");
proc_set_signals(server_proc, server_signal); proc_set_signals(server_proc, server_signal);
sigprocmask(SIG_SETMASK, &oldset, NULL); 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); server_fd = server_create_socket(flags, &cause);
if (server_fd != -1) if (server_fd != -1)
server_update_socket(); 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) { if (lockfd >= 0) {
unlink(lockfile); unlink(lockfile);
@ -395,6 +404,7 @@ server_signal(int sig)
log_debug("%s: %s", __func__, strsignal(sig)); log_debug("%s: %s", __func__, strsignal(sig));
switch (sig) { switch (sig) {
case SIGINT:
case SIGTERM: case SIGTERM:
server_exit = 1; server_exit = 1;
server_send_exit(); server_send_exit();

13
tmux.1
View File

@ -23,7 +23,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm tmux .Nm tmux
.Bk -words .Bk -words
.Op Fl 2CluvV .Op Fl 2CDluvV
.Op Fl c Ar shell-command .Op Fl c Ar shell-command
.Op Fl f Ar file .Op Fl f Ar file
.Op Fl L Ar socket-name .Op Fl L Ar socket-name
@ -122,6 +122,17 @@ This option is for compatibility with
when when
.Nm .Nm
is used as a login shell. 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 .It Fl f Ar file
Specify an alternative configuration file. Specify an alternative configuration file.
By default, By default,

9
tmux.c
View File

@ -54,7 +54,7 @@ static __dead void
usage(void) usage(void)
{ {
fprintf(stderr, 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", " [-S socket-path] [-T features] [command [flags]]\n",
getprogname()); getprogname());
exit(1); exit(1);
@ -327,7 +327,7 @@ main(int argc, char **argv)
if (**argv == '-') if (**argv == '-')
flags = CLIENT_LOGIN; 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) { switch (opt) {
case '2': case '2':
tty_add_features(&feat, "256", ":,"); tty_add_features(&feat, "256", ":,");
@ -335,6 +335,9 @@ main(int argc, char **argv)
case 'c': case 'c':
shell_command = optarg; shell_command = optarg;
break; break;
case 'D':
flags |= CLIENT_NOFORK;
break;
case 'C': case 'C':
if (flags & CLIENT_CONTROL) if (flags & CLIENT_CONTROL)
flags |= CLIENT_CONTROLCONTROL; flags |= CLIENT_CONTROLCONTROL;
@ -378,6 +381,8 @@ main(int argc, char **argv)
if (shell_command != NULL && argc != 0) if (shell_command != NULL && argc != 0)
usage(); usage();
if ((flags & CLIENT_NOFORK) && argc != 0)
usage();
if ((ptm_fd = getptmfd()) == -1) if ((ptm_fd = getptmfd()) == -1)
err(1, "getptmfd"); err(1, "getptmfd");

1
tmux.h
View File

@ -1586,6 +1586,7 @@ struct client {
#define CLIENT_DEFAULTSOCKET 0x8000000 #define CLIENT_DEFAULTSOCKET 0x8000000
#define CLIENT_STARTSERVER 0x10000000 #define CLIENT_STARTSERVER 0x10000000
#define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_REDRAWPANES 0x20000000
#define CLIENT_NOFORK 0x40000000
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \

7
tty.c
View File

@ -154,16 +154,21 @@ tty_read_callback(__unused int fd, __unused short events, void *data)
{ {
struct tty *tty = data; struct tty *tty = data;
struct client *c = tty->client; struct client *c = tty->client;
const char *name = c->name;
size_t size = EVBUFFER_LENGTH(tty->in); size_t size = EVBUFFER_LENGTH(tty->in);
int nread; int nread;
nread = evbuffer_read(tty->in, tty->fd, -1); nread = evbuffer_read(tty->in, tty->fd, -1);
if (nread == 0 || nread == -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); event_del(&tty->event_in);
server_client_lost(tty->client); server_client_lost(tty->client);
return; 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)) while (tty_keys_next(tty))
; ;