Handle fork() failures more gracefully

This commit is contained in:
Kovid Goyal 2018-01-05 13:57:49 +05:30
parent e03c713294
commit f06f871dfc
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -51,45 +51,52 @@ spawn(PyObject *self UNUSED, PyObject *args) {
#define exit_on_err(m) { write_to_stderr(m); write_to_stderr(": "); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); } #define exit_on_err(m) { write_to_stderr(m); write_to_stderr(": "); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); }
pid_t pid = fork(); pid_t pid = fork();
if (pid == 0) { switch(pid) {
// child case 0:
// Use only signal-safe functions (man 7 signal-safety) // child
if (chdir(cwd) != 0) { if (chdir("/") != 0) {} }; // ignore failure to chdir to / // Use only signal-safe functions (man 7 signal-safety)
if (setsid() == -1) exit_on_err("setsid() in child process failed"); if (chdir(cwd) != 0) { if (chdir("/") != 0) {} }; // ignore failure to chdir to /
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1"); if (setsid() == -1) exit_on_err("setsid() in child process failed");
if (dup2(slave, 2) == -1) exit_on_err("dup2() failed for fd number 2"); if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");
if (stdin_read_fd > -1) { if (dup2(slave, 2) == -1) exit_on_err("dup2() failed for fd number 2");
if (dup2(stdin_read_fd, 0) == -1) exit_on_err("dup2() failed for fd number 0"); if (stdin_read_fd > -1) {
close(stdin_read_fd); if (dup2(stdin_read_fd, 0) == -1) exit_on_err("dup2() failed for fd number 0");
close(stdin_write_fd); close(stdin_read_fd);
} else { close(stdin_write_fd);
if (dup2(slave, 0) == -1) exit_on_err("dup2() failed for fd number 0"); } else {
} if (dup2(slave, 0) == -1) exit_on_err("dup2() failed for fd number 0");
close(slave); }
close(master); close(slave);
for (int c = 3; c < 201; c++) close(c); close(master);
for (int c = 3; c < 201; c++) close(c);
// Establish the controlling terminal (see man 7 credentials) // Establish the controlling terminal (see man 7 credentials)
int tfd = open(name, O_RDWR); int tfd = open(name, O_RDWR);
if (tfd == -1) exit_on_err("Failed to open controlling terminal"); if (tfd == -1) exit_on_err("Failed to open controlling terminal");
close(tfd); close(tfd);
environ = env; environ = env;
execvp(argv[0], argv); execvp(argv[0], argv);
// Report the failure and exec a shell instead, so that we are not left // Report the failure and exec a shell instead, so that we are not left
// with a forked but not exec'ed process // with a forked but not exec'ed process
write_to_stderr("Failed to launch child: "); write_to_stderr("Failed to launch child: ");
write_to_stderr(argv[0]); write_to_stderr(argv[0]);
write_to_stderr("\nWith error: "); write_to_stderr("\nWith error: ");
write_to_stderr(strerror(errno)); write_to_stderr(strerror(errno));
write_to_stderr("\nPress Enter to exit.\n"); write_to_stderr("\nPress Enter to exit.\n");
execlp("sh", "sh", "-c", "read w", NULL); execlp("sh", "sh", "-c", "read w", NULL);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else { break;
free(argv); case -1:
free(env); PyErr_SetFromErrno(PyExc_OSError);
break;
default:
break;
} }
#undef exit_on_err #undef exit_on_err
free(argv);
free(env);
if (pid == -1) return NULL;
return PyLong_FromLong(pid); return PyLong_FromLong(pid);
} }