1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

Support glibc 2.35's posix_spawn_file_actions_addtcsetpgrp_np(3)

This commit implements support for the glibc 2.35
posix_spawn_file_actions_addtcsetpgrp_np(3) extension[2][3],
updating spawnveg(3) to use the new function for setting the
terminal group. This was done with the intention of improving
performance in interactive shells without reintroducing previous
race conditions[4][5].

[1]: https://sourceware.org/pipermail/libc-alpha/2022-February/136040.html
[2]: https://sourceware.org/git/?p=glibc.git;a=commit;h=342cc934
[3]: https://sourceware.org/git/?p=glibc.git;a=commit;h=6289d28d
[4]: https://github.com/ksh93/ksh/issues/79
[5]: https://www.mail-archive.com/ast-developers@research.att.com/msg00717.html

src/cmd/ksh93/sh/path.c:
- Tell spawnveg(3) to set the terminal process group when launching
  a child process in an interactive shell.

src/cmd/ksh93/sh/xec.c:
- If posix_spawn_file_actions_addtcsetpgrp_np(3) is available,
  allow use of spawnveg(3) (via sh_ntfork()) even with job control
  active.
- sh_ntfork(): Reimplement most of the SIGTSTP handling code
  removed in commit 66c37202.

src/lib/libast/comp/spawnveg.c,
src/lib/libast/misc/procopen.c,
src/lib/libast/features/sys:
- Add support for posix_spawn_file_actions_addtcsetpgrp_np(3).
- Allow spawnveg to set the terminal process group when pgid == 0.
  This was necessary to avoid race conditions when using the new
  function.

src/lib/libast/features/lib:
- Detect posix_spawn_file_actions_addtcsetpgrp_np(3).
- Do not detect an OS spawnveg(3). With the API changes to spawnveg
  in this pull request ksh probably can't use the OS's spawnveg
  function anymore. (That's assuming anything else even provides a
  spawnveg function to begin with, which is unlikely.)

src/lib/libast/features/api,
src/cmd/ksh93/include/defs.h:
- Bump libast version (20220101 => 20220201) due to the spawnveg(3)
  API change.

src/lib/libast/man/spawnveg.3:
- Document the changes to spawnveg(3) in the corresponding man
  page. Currently, it will only use the new tcfd argument if
  posix_spawn_file_actions_addtcsetpgrp_np(3) is supported. This
  could also be implemented for the fork(2) fallback, but for now
  I've avoided changing that since actually using it in the fork
  code would likely require a lot of hackery to avoid attempting
  tcsetpgrp with vfork (the behavior of tcsetpgrp after vfork is
  not portable) and would only benefit systems that don't have
  posix_spawn and vfork (I can't recall any off the top of my head
  that would fall under that category).
- Updated the man page to account for spawnveg's change in
  behavior.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
This commit is contained in:
Johnothan King 2022-02-05 13:14:39 +00:00 committed by Martijn Dekker
parent c7a774d9df
commit 0863a8eb29
9 changed files with 101 additions and 35 deletions

View file

@ -29,8 +29,8 @@
#define defs_h_defined #define defs_h_defined
#include <ast.h> #include <ast.h>
#if !defined(AST_VERSION) || AST_VERSION < 20220101 #if !defined(AST_VERSION) || AST_VERSION < 20220201
#error libast version 20220101 or later is required #error libast version 20220201 or later is required
#endif #endif
#if !_lib_fork #if !_lib_fork
#error In 2021, ksh joined the 21st century and started requiring fork(2). #error In 2021, ksh joined the 21st century and started requiring fork(2).

View file

@ -147,7 +147,7 @@ static pid_t _spawnveg(const char *path, char* const argv[], char* const envp[],
while(1) while(1)
{ {
sh_stats(STAT_SPAWN); sh_stats(STAT_SPAWN);
pid = spawnveg(path,argv,envp,pgid); pid = spawnveg(path,argv,envp,pgid,job.jobcontrol?job.fd:-1);
if(pid>=0 || errno!=EAGAIN) if(pid>=0 || errno!=EAGAIN)
break; break;
} }

View file

@ -55,6 +55,10 @@
# include <sys/resource.h> # include <sys/resource.h>
#endif #endif
#if _lib_posix_spawn > 1 && _lib_posix_spawn_file_actions_addtcsetpgrp_np
#define _use_ntfork_tcpgrp 1
#endif
#define SH_NTFORK SH_TIMING #define SH_NTFORK SH_TIMING
#define NV_BLTPFSH NV_ARRAY #define NV_BLTPFSH NV_ARRAY
@ -1613,7 +1617,11 @@ int sh_exec(register const Shnode_t *t, int flags)
fifo_save_ppid = sh.current_pid; fifo_save_ppid = sh.current_pid;
#endif #endif
#if SHOPT_SPAWN #if SHOPT_SPAWN
#if _use_ntfork_tcpgrp
if(com)
#else
if(com && !job.jobcontrol) if(com && !job.jobcontrol)
#endif /* _use_ntfork_tcpgrp */
{ {
parent = sh_ntfork(t,com,&jobid,ntflag); parent = sh_ntfork(t,com,&jobid,ntflag);
if(parent<0) if(parent<0)
@ -3449,7 +3457,8 @@ static void sigreset(int mode)
/* /*
* A combined fork/exec for systems with slow fork(). * A combined fork/exec for systems with slow fork().
* Incompatible with job control on interactive shells (job.jobcontrol). * Incompatible with job control on interactive shells (job.jobcontrol) if
* the system does not support posix_spawn_file_actions_addtcsetpgrp_np().
*/ */
static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag)
{ {
@ -3461,6 +3470,9 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag)
char **arge, *path; char **arge, *path;
volatile pid_t grp = 0; volatile pid_t grp = 0;
Pathcomp_t *pp; Pathcomp_t *pp;
#if _use_ntfork_tcpgrp
volatile int jobwasset=0;
#endif /* _use_ntfork_tcpgrp */
if(flag) if(flag)
{ {
otype = savetype; otype = savetype;
@ -3525,8 +3537,21 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag)
} }
arge = sh_envgen(); arge = sh_envgen();
sh.exitval = 0; sh.exitval = 0;
#if _use_ntfork_tcpgrp
if(job.jobcontrol)
{
signal(SIGTTIN,SIG_DFL);
signal(SIGTTOU,SIG_DFL);
signal(SIGTSTP,SIG_DFL);
jobwasset++;
}
#endif /* _use_ntfork_tcpgrp */
#ifdef JOBS #ifdef JOBS
#if _use_ntfork_tcpgrp
if(sh_isstate(SH_MONITOR) && (job.jobcontrol || (otype&FAMP)))
#else
if(sh_isstate(SH_MONITOR) && (otype&FAMP)) if(sh_isstate(SH_MONITOR) && (otype&FAMP))
#endif /* _use_ntfork_tcpgrp */
{ {
if((otype&FAMP) || job.curpgid==0) if((otype&FAMP) || job.curpgid==0)
grp = 1; grp = 1;
@ -3587,6 +3612,17 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag)
sh_popcontext(buffp); sh_popcontext(buffp);
if(buffp->olist) if(buffp->olist)
free_list(buffp->olist); free_list(buffp->olist);
#if _use_ntfork_tcpgrp
if(jobwasset)
{
signal(SIGTTIN,SIG_IGN);
signal(SIGTTOU,SIG_IGN);
if(sh_isstate(SH_INTERACTIVE))
signal(SIGTSTP,SIG_IGN);
else
signal(SIGTSTP,SIG_DFL);
}
#endif /* _use_ntfork_tcpgrp */
if(sigwasset) if(sigwasset)
sigreset(1); /* restore ignored signals */ sigreset(1); /* restore ignored signals */
if(scope) if(scope)

View file

@ -32,12 +32,6 @@
#include <ast.h> #include <ast.h>
#if _lib_spawnveg
NoN(spawnveg)
#else
#if _lib_posix_spawn > 1 /* reports underlying exec() errors */ #if _lib_posix_spawn > 1 /* reports underlying exec() errors */
#include <spawn.h> #include <spawn.h>
@ -45,20 +39,22 @@ NoN(spawnveg)
#include <wait.h> #include <wait.h>
pid_t pid_t
spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid) spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid, int tcfd)
{ {
int err, flags = 0; int err, flags = 0;
pid_t pid; pid_t pid;
posix_spawnattr_t attr; posix_spawnattr_t attr;
#if _lib_posix_spawn_file_actions_addtcsetpgrp_np
posix_spawn_file_actions_t actions;
#endif
if (err = posix_spawnattr_init(&attr)) if (err = posix_spawnattr_init(&attr))
goto nope; goto nope;
#if POSIX_SPAWN_SETSID #if POSIX_SPAWN_SETSID
if (pgid == -1) if (pgid == -1)
flags |= POSIX_SPAWN_SETSID; flags |= POSIX_SPAWN_SETSID;
else
#endif #endif
if (pgid) if (pgid && pgid != -1)
flags |= POSIX_SPAWN_SETPGROUP; flags |= POSIX_SPAWN_SETPGROUP;
if (flags && (err = posix_spawnattr_setflags(&attr, flags))) if (flags && (err = posix_spawnattr_setflags(&attr, flags)))
goto bad; goto bad;
@ -69,13 +65,33 @@ spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
if (err = posix_spawnattr_setpgroup(&attr, pgid)) if (err = posix_spawnattr_setpgroup(&attr, pgid))
goto bad; goto bad;
} }
#if _lib_posix_spawn_file_actions_addtcsetpgrp_np
if (tcfd >= 0)
{
if (err = posix_spawn_file_actions_init(&actions))
goto bad;
if (err = posix_spawn_file_actions_addtcsetpgrp_np(&actions, tcfd))
goto fail;
}
if (err = posix_spawn(&pid, path, (tcfd >= 0) ? &actions : NiL, &attr, argv, envv ? envv : environ))
#else
if (err = posix_spawn(&pid, path, NiL, &attr, argv, envv ? envv : environ)) if (err = posix_spawn(&pid, path, NiL, &attr, argv, envv ? envv : environ))
#endif
{ {
if ((err != EPERM) || (err = posix_spawn(&pid, path, NiL, NiL, argv, envv ? envv : environ))) if ((err != EPERM) || (err = posix_spawn(&pid, path, NiL, NiL, argv, envv ? envv : environ)))
goto bad; goto fail;
} }
#if _lib_posix_spawn_file_actions_addtcsetpgrp_np
if (tcfd >= 0)
posix_spawn_file_actions_destroy(&actions);
#endif
posix_spawnattr_destroy(&attr); posix_spawnattr_destroy(&attr);
return pid; return pid;
fail:
#if _lib_posix_spawn_file_actions_addtcsetpgrp_np
if (tcfd >= 0)
posix_spawn_file_actions_destroy(&actions);
#endif
bad: bad:
posix_spawnattr_destroy(&attr); posix_spawnattr_destroy(&attr);
nope: nope:
@ -97,8 +113,9 @@ spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
#endif #endif
pid_t pid_t
spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid) spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid, int tcfd)
{ {
NOT_USED(tcfd);
#if defined(P_DETACH) #if defined(P_DETACH)
return spawnve(pgid ? P_DETACH : P_NOWAIT, path, argv, envv ? envv : environ); return spawnve(pgid ? P_DETACH : P_NOWAIT, path, argv, envv ? envv : environ);
#else #else
@ -117,10 +134,11 @@ spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
*/ */
pid_t pid_t
spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid) spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid, int tcfd)
{ {
struct inheritance inherit; struct inheritance inherit;
NOT_USED(tcfd);
inherit.flags = 0; inherit.flags = 0;
if (pgid) if (pgid)
{ {
@ -154,7 +172,7 @@ spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
*/ */
pid_t pid_t
spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid) spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid, int tcfd)
{ {
#if _lib_fork || _lib_vfork #if _lib_fork || _lib_vfork
int n; int n;
@ -169,6 +187,7 @@ spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
#endif #endif
#endif #endif
NOT_USED(tcfd);
if (!envv) if (!envv)
envv = environ; envv = environ;
#if _lib_spawnve #if _lib_spawnve
@ -214,11 +233,9 @@ spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
#if _lib_tcgetpgrp #if _lib_tcgetpgrp
if (m) if (m)
tcsetpgrp(2, pgid); tcsetpgrp(2, pgid);
#else #elif defined(TIOCSPGRP)
#ifdef TIOCSPGRP
if (m) if (m)
ioctl(2, TIOCSPGRP, &pgid); ioctl(2, TIOCSPGRP, &pgid);
#endif
#endif #endif
} }
execve(path, argv, envv); execve(path, argv, envv);
@ -289,5 +306,3 @@ spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
#endif #endif
#endif #endif
#endif

View file

@ -1,6 +1,6 @@
iff AST_API iff AST_API
ver ast 20220101 ver ast 20220201
api ast 20120528 regexec regnexec regrexec regsubexec strgrpmatch api ast 20120528 regexec regnexec regrexec regsubexec strgrpmatch

View file

@ -36,7 +36,7 @@ lib mount,onexit,opendir,pathconf
lib readlink,remove,rename,rewinddir,rindex,rmdir,setlocale lib readlink,remove,rename,rewinddir,rindex,rmdir,setlocale
lib setpgid,setpgrp,setpgrp2,setreuid,setsid,setuid,sigaction lib setpgid,setpgrp,setpgrp2,setreuid,setsid,setuid,sigaction
lib sigprocmask,sigsetmask,sigunblock,sigvec,socketpair lib sigprocmask,sigsetmask,sigunblock,sigvec,socketpair
lib spawn,spawnve,spawnveg lib spawn,spawnve
lib strchr,strcoll,strdup,strerror,strcasecmp,strncasecmp,strrchr,strstr lib strchr,strcoll,strdup,strerror,strcasecmp,strncasecmp,strrchr,strstr
lib strmode,strxfrm,strftime,swab,symlink,sysconf,sysinfo,syslog lib strmode,strxfrm,strftime,swab,symlink,sysconf,sysinfo,syslog
lib telldir,tmpnam,tzset,universe,unlink,utime,wctype lib telldir,tmpnam,tzset,universe,unlink,utime,wctype
@ -343,6 +343,7 @@ tst lib_posix_spawn unistd.h stdlib.h spawn.h -Dfork=______fork note{ posix_spaw
_exit(n); _exit(n);
} }
}end }end
lib posix_spawn_file_actions_addtcsetpgrp_np
tst lib_spawn_mode unistd.h stdlib.h note{ first spawn arg is mode and it works }end execute{ tst lib_spawn_mode unistd.h stdlib.h note{ first spawn arg is mode and it works }end execute{
#include <signal.h> #include <signal.h>
@ -642,7 +643,7 @@ tst - output{
printf("\n"); printf("\n");
#endif #endif
#if _lib_spawnveg || _lib_posix_spawn || _lib_spawn_mode || _lib_spawn && _hdr_spawn && _mem_pgroup_inheritance || _lib_vfork && _real_vfork #if _lib_posix_spawn || _lib_spawn_mode || _lib_spawn && _hdr_spawn && _mem_pgroup_inheritance || _lib_vfork && _real_vfork
printf("#if !_AST_no_spawnveg\n"); printf("#if !_AST_no_spawnveg\n");
printf("#define _use_spawnveg 1\n"); printf("#define _use_spawnveg 1\n");
printf("#endif\n"); printf("#endif\n");

View file

@ -195,7 +195,7 @@ extern setpgid int (pid_t, pid_t)
extern setsid pid_t (void) extern setsid pid_t (void)
extern setuid int (uid_t) extern setuid int (uid_t)
extern sleep unsigned (unsigned int) extern sleep unsigned (unsigned int)
extern spawnveg pid_t (const char*, char* const[], char* const[], pid_t) extern spawnveg pid_t (const char*, char* const[], char* const[], pid_t, int)
extern srand void (unsigned int) extern srand void (unsigned int)
extern strcasecmp int (const char*, const char*) extern strcasecmp int (const char*, const char*)
extern strcat char* (char*, const char*) extern strcat char* (char*, const char*)

View file

@ -42,7 +42,7 @@ spawnveg \- process spawn with process group and session control
.SH SYNOPSIS .SH SYNOPSIS
.L "#include <ast.h>" .L "#include <ast.h>"
.sp .sp
.L "int spawnveg(const char* command, char** argv, char** envv, pid_t pgid);" .L "int spawnveg(const char* command, char** argv, char** envv, pid_t pgid, int tcfd);"
.SH DESCRIPTION .SH DESCRIPTION
.L spawnveg .L spawnveg
combines combines
@ -81,10 +81,24 @@ The new process becomes a process group leader.
.L >1 .L >1
The new process joins the process group The new process joins the process group
.IR pgid . .IR pgid .
.SH CAVEATS .PP
The The
.L tcfd
argument is currently only used if the operating system supports the
.I posix_spawn_file_actions_addtcsetpgrp_np
function.
When
.L tcfd
is
.LR >=0 ,
spawnveg will set the controlling terminal for the new process to
.IR tcfd .
.SH CAVEATS
If the
.I posix_spawn_file_actions_addtcsetpgrp_np
function is not available, then
.L spawnveg .L spawnveg
function cannot set the terminal process group. cannot reliably set the terminal process group.
As a result, it is incompatible with job control when used with terminals. As a result, it is incompatible with job control when used with terminals.
Additionally, if the Additionally, if the
.L POSIX_SPAWN_SETSID .L POSIX_SPAWN_SETSID

View file

@ -2,7 +2,7 @@
* * * *
* This software is part of the ast package * * This software is part of the ast package *
* Copyright (c) 1985-2012 AT&T Intellectual Property * * Copyright (c) 1985-2012 AT&T Intellectual Property *
* Copyright (c) 2020-2021 Contributors to ksh 93u+m * * Copyright (c) 2020-2022 Contributors to ksh 93u+m *
* and is licensed under the * * and is licensed under the *
* Eclipse Public License, Version 1.0 * * Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property * * by AT&T Intellectual Property *
@ -744,7 +744,7 @@ sfsync(sfstderr);
if (forked || (flags & PROC_OVERLAY)) if (forked || (flags & PROC_OVERLAY))
execve(path, p, environ); execve(path, p, environ);
#if _use_spawnveg #if _use_spawnveg
else if ((proc->pid = spawnveg(path, p, environ, proc->pgrp)) != -1) else if ((proc->pid = spawnveg(path, p, environ, proc->pgrp, -1)) != -1)
goto cleanup; goto cleanup;
#endif #endif
if (errno != ENOEXEC) if (errno != ENOEXEC)
@ -773,7 +773,7 @@ sfsync(sfstderr);
execve(env + 2, p, environ); execve(env + 2, p, environ);
#if _use_spawnveg #if _use_spawnveg
else else
proc->pid = spawnveg(env + 2, p, environ, proc->pgrp); proc->pid = spawnveg(env + 2, p, environ, proc->pgrp, -1);
#endif #endif
cleanup: cleanup:
if (forked) if (forked)