diff options
| author | Philipp Stephani | 2020-12-30 14:42:01 +0100 |
|---|---|---|
| committer | Philipp Stephani | 2021-11-13 16:45:19 +0100 |
| commit | cc4edea872ca653f3e0631ce50e47b5260c6773a (patch) | |
| tree | 1687f6dc24ee03aade146c749504d3b8d7b9e73f /src/callproc.c | |
| parent | a56dd60d2fba9d873748ca3831ba61711628f698 (diff) | |
| download | emacs-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.c | 190 |
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 | |||
| 1270 | static int | ||
| 1271 | emacs_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 | |||
| 1312 | static int | ||
| 1313 | emacs_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 | |||
| 1370 | static int | ||
| 1371 | emacs_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); |