aboutsummaryrefslogtreecommitdiffstats
path: root/src/callproc.c
diff options
context:
space:
mode:
authorPhilipp Stephani2020-12-30 14:42:01 +0100
committerPhilipp Stephani2021-11-13 16:45:19 +0100
commitcc4edea872ca653f3e0631ce50e47b5260c6773a (patch)
tree1687f6dc24ee03aade146c749504d3b8d7b9e73f /src/callproc.c
parenta56dd60d2fba9d873748ca3831ba61711628f698 (diff)
downloademacs-cc4edea872ca653f3e0631ce50e47b5260c6773a.tar.gz
emacs-cc4edea872ca653f3e0631ce50e47b5260c6773a.zip
Use posix_spawn if possible.
posix_spawn is less error-prone than vfork + execve, and can make better use of system-specific enhancements like 'clone' on Linux. Use it if we don't need to configure a pseudoterminal. Backported from commit a60053f8368e058229721f1bf1567c2b1676b239. Unlike that commit, only define USABLE_POSIX_SPAWN on macOS, because there posix_spawn is much faster than vfork. Don't merge to master. * configure.ac (HAVE_SPAWN_H, HAVE_POSIX_SPAWN) (HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR) (HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) (HAVE_POSIX_SPAWNATTR_SETFLAGS, HAVE_DECL_POSIX_SPAWN_SETSID): New configuration variables. * src/callproc.c (USABLE_POSIX_SPAWN): New configuration macro. (emacs_posix_spawn_init_actions) (emacs_posix_spawn_init_attributes, emacs_posix_spawn_init): New helper functions. (emacs_spawn): Use posix_spawn if possible. (cherry picked from commit a60053f8368e058229721f1bf1567c2b1676b239)
Diffstat (limited to 'src/callproc.c')
-rw-r--r--src/callproc.c190
1 files changed, 189 insertions, 1 deletions
diff --git a/src/callproc.c b/src/callproc.c
index fa43f973844..4aa24636c35 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -28,6 +28,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
28#include <sys/file.h> 28#include <sys/file.h>
29#include <fcntl.h> 29#include <fcntl.h>
30 30
31/* In order to be able to use `posix_spawn', it needs to support some
32 variant of `chdir' as well as `setsid'. */
33#if defined DARWIN_OS \
34 && defined HAVE_SPAWN_H && defined HAVE_POSIX_SPAWN \
35 && defined HAVE_POSIX_SPAWNATTR_SETFLAGS \
36 && (defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR \
37 || defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) \
38 && defined HAVE_DECL_POSIX_SPAWN_SETSID \
39 && HAVE_DECL_POSIX_SPAWN_SETSID == 1
40# include <spawn.h>
41# define USABLE_POSIX_SPAWN 1
42#else
43# define USABLE_POSIX_SPAWN 0
44#endif
45
31#include "lisp.h" 46#include "lisp.h"
32 47
33#ifdef SETUP_SLAVE_PTY 48#ifdef SETUP_SLAVE_PTY
@@ -1247,6 +1262,130 @@ child_setup (int in, int out, int err, char **new_argv, char **env,
1247#endif /* not WINDOWSNT */ 1262#endif /* not WINDOWSNT */
1248} 1263}
1249 1264
1265#if USABLE_POSIX_SPAWN
1266
1267/* Set up ACTIONS and ATTRIBUTES for `posix_spawn'. Return an error
1268 number. */
1269
1270static int
1271emacs_posix_spawn_init_actions (posix_spawn_file_actions_t *actions,
1272 int std_in, int std_out, int std_err,
1273 const char *cwd)
1274{
1275 int error = posix_spawn_file_actions_init (actions);
1276 if (error != 0)
1277 return error;
1278
1279 error = posix_spawn_file_actions_adddup2 (actions, std_in,
1280 STDIN_FILENO);
1281 if (error != 0)
1282 goto out;
1283
1284 error = posix_spawn_file_actions_adddup2 (actions, std_out,
1285 STDOUT_FILENO);
1286 if (error != 0)
1287 goto out;
1288
1289 error = posix_spawn_file_actions_adddup2 (actions,
1290 std_err < 0 ? std_out
1291 : std_err,
1292 STDERR_FILENO);
1293 if (error != 0)
1294 goto out;
1295
1296 error =
1297#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR
1298 posix_spawn_file_actions_addchdir
1299#else
1300 posix_spawn_file_actions_addchdir_np
1301#endif
1302 (actions, cwd);
1303 if (error != 0)
1304 goto out;
1305
1306 out:
1307 if (error != 0)
1308 posix_spawn_file_actions_destroy (actions);
1309 return error;
1310}
1311
1312static int
1313emacs_posix_spawn_init_attributes (posix_spawnattr_t *attributes)
1314{
1315 int error = posix_spawnattr_init (attributes);
1316 if (error != 0)
1317 return error;
1318
1319 error = posix_spawnattr_setflags (attributes,
1320 POSIX_SPAWN_SETSID
1321 | POSIX_SPAWN_SETSIGDEF
1322 | POSIX_SPAWN_SETSIGMASK);
1323 if (error != 0)
1324 goto out;
1325
1326 sigset_t sigdefault;
1327 sigemptyset (&sigdefault);
1328
1329#ifdef DARWIN_OS
1330 /* Work around a macOS bug, where SIGCHLD is apparently
1331 delivered to a vforked child instead of to its parent. See:
1332 https://lists.gnu.org/r/emacs-devel/2017-05/msg00342.html
1333 */
1334 sigaddset (&sigdefault, SIGCHLD);
1335#endif
1336
1337 sigaddset (&sigdefault, SIGINT);
1338 sigaddset (&sigdefault, SIGQUIT);
1339#ifdef SIGPROF
1340 sigaddset (&sigdefault, SIGPROF);
1341#endif
1342
1343 /* Emacs ignores SIGPIPE, but the child should not. */
1344 sigaddset (&sigdefault, SIGPIPE);
1345 /* Likewise for SIGPROF. */
1346#ifdef SIGPROF
1347 sigaddset (&sigdefault, SIGPROF);
1348#endif
1349
1350 error = posix_spawnattr_setsigdefault (attributes, &sigdefault);
1351 if (error != 0)
1352 goto out;
1353
1354 /* Stop blocking SIGCHLD in the child. */
1355 sigset_t oldset;
1356 error = pthread_sigmask (SIG_SETMASK, NULL, &oldset);
1357 if (error != 0)
1358 goto out;
1359 error = posix_spawnattr_setsigmask (attributes, &oldset);
1360 if (error != 0)
1361 goto out;
1362
1363 out:
1364 if (error != 0)
1365 posix_spawnattr_destroy (attributes);
1366
1367 return error;
1368}
1369
1370static int
1371emacs_posix_spawn_init (posix_spawn_file_actions_t *actions,
1372 posix_spawnattr_t *attributes, int std_in,
1373 int std_out, int std_err, const char *cwd)
1374{
1375 int error = emacs_posix_spawn_init_actions (actions, std_in,
1376 std_out, std_err, cwd);
1377 if (error != 0)
1378 return error;
1379
1380 error = emacs_posix_spawn_init_attributes (attributes);
1381 if (error != 0)
1382 return error;
1383
1384 return 0;
1385}
1386
1387#endif
1388
1250/* Start a new asynchronous subprocess. If successful, return zero 1389/* Start a new asynchronous subprocess. If successful, return zero
1251 and store the process identifier of the new process in *NEWPID. 1390 and store the process identifier of the new process in *NEWPID.
1252 Use STDIN, STDOUT, and STDERR as standard streams for the new 1391 Use STDIN, STDOUT, and STDERR as standard streams for the new
@@ -1266,10 +1405,58 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err,
1266 char **argv, char **envp, const char *cwd, 1405 char **argv, char **envp, const char *cwd,
1267 const char *pty, const sigset_t *oldset) 1406 const char *pty, const sigset_t *oldset)
1268{ 1407{
1408#if USABLE_POSIX_SPAWN
1409 /* Prefer the simpler `posix_spawn' if available. `posix_spawn'
1410 doesn't yet support setting up pseudoterminals, so we fall back
1411 to `vfork' if we're supposed to use a pseudoterminal. */
1412
1413 bool use_posix_spawn = pty == NULL;
1414
1415 posix_spawn_file_actions_t actions;
1416 posix_spawnattr_t attributes;
1417
1418 if (use_posix_spawn)
1419 {
1420 /* Initialize optional attributes before blocking. */
1421 int error
1422 = emacs_posix_spawn_init (&actions, &attributes, std_in,
1423 std_out, std_err, cwd);
1424 if (error != 0)
1425 return error;
1426 }
1427#endif
1428
1269 int pid; 1429 int pid;
1430 int vfork_error;
1270 1431
1271 eassert (input_blocked_p ()); 1432 eassert (input_blocked_p ());
1272 1433
1434#if USABLE_POSIX_SPAWN
1435 if (use_posix_spawn)
1436 {
1437 vfork_error = posix_spawn (&pid, argv[0], &actions, &attributes,
1438 argv, envp);
1439 if (vfork_error != 0)
1440 pid = -1;
1441
1442 int error = posix_spawn_file_actions_destroy (&actions);
1443 if (error != 0)
1444 {
1445 errno = error;
1446 emacs_perror ("posix_spawn_file_actions_destroy");
1447 }
1448
1449 error = posix_spawnattr_destroy (&attributes);
1450 if (error != 0)
1451 {
1452 errno = error;
1453 emacs_perror ("posix_spawnattr_destroy");
1454 }
1455
1456 goto fork_done;
1457 }
1458#endif
1459
1273#ifndef WINDOWSNT 1460#ifndef WINDOWSNT
1274 /* vfork, and prevent local vars from being clobbered by the vfork. */ 1461 /* vfork, and prevent local vars from being clobbered by the vfork. */
1275 pid_t *volatile newpid_volatile = newpid; 1462 pid_t *volatile newpid_volatile = newpid;
@@ -1413,8 +1600,9 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err,
1413 1600
1414 /* Back in the parent process. */ 1601 /* Back in the parent process. */
1415 1602
1416 int vfork_error = pid < 0 ? errno : 0; 1603 vfork_error = pid < 0 ? errno : 0;
1417 1604
1605 fork_done:
1418 if (pid < 0) 1606 if (pid < 0)
1419 { 1607 {
1420 eassert (0 < vfork_error); 1608 eassert (0 < vfork_error);