forked from github/abduco
Fix race condition on command execution
There are now two different pipes used for synchronization purposes.
This commit is contained in:
parent
c81c3c3214
commit
9e29a968d8
79
abduco.c
79
abduco.c
|
|
@ -22,7 +22,6 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <poll.h>
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
@ -266,12 +265,21 @@ static int create_socket(const char *name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool create_session(const char *name, char * const argv[]) {
|
static bool create_session(const char *name, char * const argv[]) {
|
||||||
int pipefds[2];
|
/* this uses the well known double fork strategy as described in section 1.7 of
|
||||||
|
*
|
||||||
|
* http://www.faqs.org/faqs/unix-faq/programmer/faq/
|
||||||
|
*
|
||||||
|
* pipes are used for synchronization and error reporting i.e. the child sets
|
||||||
|
* the close on exec flag before calling execvp(3) the parent blocks on a read(2)
|
||||||
|
* in case of failure the error message is written to the pipe, success is
|
||||||
|
* indicated by EOF on the pipe.
|
||||||
|
*/
|
||||||
|
int client_pipe[2], server_pipe[2];
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
char errormsg[255];
|
char errormsg[255];
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
if (pipe(pipefds) == -1)
|
if (pipe(client_pipe) == -1)
|
||||||
return false;
|
return false;
|
||||||
if ((server.socket = server_create_socket(name)) == -1)
|
if ((server.socket = server_create_socket(name)) == -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -279,28 +287,42 @@ static bool create_session(const char *name, char * const argv[]) {
|
||||||
switch ((pid = fork())) {
|
switch ((pid = fork())) {
|
||||||
case 0: /* child process */
|
case 0: /* child process */
|
||||||
setsid();
|
setsid();
|
||||||
close(pipefds[0]);
|
close(client_pipe[0]);
|
||||||
switch ((pid = fork())) {
|
switch ((pid = fork())) {
|
||||||
case 0: /* child process */
|
case 0: /* child process */
|
||||||
|
if (pipe(server_pipe) == -1) {
|
||||||
|
snprintf(errormsg, sizeof(errormsg), "server-pipe: %s\n", strerror(errno));
|
||||||
|
write_all(client_pipe[1], errormsg, strlen(errormsg));
|
||||||
|
close(client_pipe[1]);
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
sa.sa_flags = 0;
|
sa.sa_flags = 0;
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_handler = server_pty_died_handler;
|
sa.sa_handler = server_pty_died_handler;
|
||||||
sigaction(SIGCHLD, &sa, NULL);
|
sigaction(SIGCHLD, &sa, NULL);
|
||||||
switch (server.pid = forkpty(&server.pty, NULL, has_term ? &server.term : NULL, &server.winsize)) {
|
switch (server.pid = forkpty(&server.pty, NULL, has_term ? &server.term : NULL, &server.winsize)) {
|
||||||
case 0: /* child process */
|
case 0: /* child = user application process */
|
||||||
fcntl(pipefds[1], F_SETFD, FD_CLOEXEC);
|
|
||||||
close(server.socket);
|
close(server.socket);
|
||||||
|
close(server_pipe[0]);
|
||||||
|
fcntl(client_pipe[1], F_SETFD, FD_CLOEXEC);
|
||||||
|
fcntl(server_pipe[1], F_SETFD, FD_CLOEXEC);
|
||||||
execvp(argv[0], argv);
|
execvp(argv[0], argv);
|
||||||
snprintf(errormsg, sizeof(errormsg), "server-execvp: %s\n", strerror(errno));
|
snprintf(errormsg, sizeof(errormsg), "server-execvp: %s\n", strerror(errno));
|
||||||
write_all(pipefds[1], errormsg, strlen(errormsg));
|
write_all(client_pipe[1], errormsg, strlen(errormsg));
|
||||||
close(pipefds[1]);
|
write_all(server_pipe[1], errormsg, strlen(errormsg));
|
||||||
exit(EXIT_FAILURE);
|
close(client_pipe[1]);
|
||||||
|
close(server_pipe[1]);
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
break;
|
break;
|
||||||
case -1:
|
case -1: /* forkpty failed */
|
||||||
die("server-forkpty");
|
snprintf(errormsg, sizeof(errormsg), "server-forkpty: %s\n", strerror(errno));
|
||||||
|
write_all(client_pipe[1], errormsg, strlen(errormsg));
|
||||||
|
close(client_pipe[1]);
|
||||||
|
close(server_pipe[0]);
|
||||||
|
close(server_pipe[1]);
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
break;
|
break;
|
||||||
default:
|
default: /* parent = server process */
|
||||||
/* SIGTTIN, SIGTTU */
|
|
||||||
sa.sa_handler = server_sigterm_handler;
|
sa.sa_handler = server_sigterm_handler;
|
||||||
sigaction(SIGTERM, &sa, NULL);
|
sigaction(SIGTERM, &sa, NULL);
|
||||||
sigaction(SIGINT, &sa, NULL);
|
sigaction(SIGINT, &sa, NULL);
|
||||||
|
|
@ -316,33 +338,42 @@ static bool create_session(const char *name, char * const argv[]) {
|
||||||
dup2(fd, 1);
|
dup2(fd, 1);
|
||||||
dup2(fd, 2);
|
dup2(fd, 2);
|
||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
close(pipefds[1]);
|
close(client_pipe[1]);
|
||||||
struct pollfd pipe_status = { .fd = pipefds[0], .events = POLLIN };
|
close(server_pipe[1]);
|
||||||
if (poll(&pipe_status, 1, -1) == 1 && pipe_status.revents == POLLHUP)
|
if (read_all(server_pipe[0], errormsg, sizeof(errormsg)) > 0)
|
||||||
exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
|
close(server_pipe[0]);
|
||||||
server_mainloop();
|
server_mainloop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
case -1: /* fork failed */
|
||||||
close(pipefds[1]);
|
snprintf(errormsg, sizeof(errormsg), "server-fork: %s\n", strerror(errno));
|
||||||
exit(EXIT_SUCCESS);
|
write_all(client_pipe[1], errormsg, strlen(errormsg));
|
||||||
|
close(client_pipe[1]);
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
default: /* parent = intermediate process */
|
||||||
|
close(client_pipe[1]);
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case -1: /* fork failed */
|
case -1: /* fork failed */
|
||||||
|
close(client_pipe[0]);
|
||||||
|
close(client_pipe[1]);
|
||||||
return false;
|
return false;
|
||||||
default: /* parent */
|
default: /* parent = client process */
|
||||||
close(pipefds[1]);
|
close(client_pipe[1]);
|
||||||
int status;
|
int status;
|
||||||
wait(&status); /* wait for first fork */
|
wait(&status); /* wait for first fork */
|
||||||
ssize_t len = read_all(pipefds[0], errormsg, sizeof(errormsg));
|
ssize_t len = read_all(client_pipe[0], errormsg, sizeof(errormsg));
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
write_all(STDERR_FILENO, errormsg, len);
|
write_all(STDERR_FILENO, errormsg, len);
|
||||||
unlink(sockaddr.sun_path);
|
unlink(sockaddr.sun_path);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
close(pipefds[0]);
|
close(client_pipe[0]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue