This was my original answer (quoted because SO doesn't support strikethrough):
Proper answer is that you simply can't. At least not reliably and easily.
Proper answer is that you can. Very very carefully. See addenum on the bottom.
Despite what manual page says in section referenced by other commenters, the default sudo process model is to
fork() on great majority (99.9%) of systems most of the time.
You can verify the truth easily yourself with process monitor like
Even suggested answer:
exec("exec sudo -u www-data php -r 'sleep(2); echo 5;' < /dev/null > /dev/null 2>&1");
will leave lingering sudo process around:
└─ sudo -u www-data php -r 'sleep(2); echo 5;'
└─ php -r 'sleep(2); echo 5;'
as long as php invoking the sudo driving script is somehow tied to some controlling terminal (edited: or script child process to a pty).
The reason for this is probably 'modern'
sudo uses tty identification for ticketing and
pam plugins that can do anything they want with your session as well. In ~some~ most software often simply compiling-in pam disables
execve() pathways. Then you have 'modern' stuff interacting like systemd-logind etc.
sudo is very complex piece of software, and making it do what you want is not at all easy,
and in your situation, it is essentially impossible.
and depending on your situation, might be still possible.
However having multiple
sudo instances wait for their children is not a problem in general. On *nix systems processes are very cheap entities, even these days, when they are dynamically linked with miliards of .so libraries.
You can also be assured that
sudo lingers around "properly", that means it is consuming minimum computation resources (almost zero) and os is smart enough to share as much memory as possible among all the running
sudo instances. So you should not see it as problem at all.
Now the question becomes, why do you want get rid of the
sudo processes in-between?
If reason is that you don't "like" sudo processes there, without proper justification, that's the wrong reason.
Still, it can be done, of course, by means of SUID root and dropping privileges respectively, but it is an approach which is not beginner friendly, it is hard to get right, and all that makes it extremely dangerous.
So much, it is generally frowned upon even by experts, so we won't go into how to achieve that here.
Still if you are persistent enough, there are answers even here, on stackoverflow network, how to switch back to
root by the means of SUID.
Just keep in mind, that you cannot trust anything in that process state (not even env vars) and must drop privileges properly into your target user as soon as possible (and do that correctly as well).
More over, depending on your deployment platform, you might need to take into account things like selinux or other security frameworks too.
Finally once your child is of different user than it's driving parent process, it becomes impossible to send it a signal to change it's state, without jumping through further hoops.
Because it's so hard and error prone, it is the sole reason
sudo even exists, but I agree with you
sudo is not always fit for every use case.
See user11658273's answer on how to disable all the advanced features: pam integration, pty creation and log servers - which all must be disabled, it seems, for this to work. It should make sudo go into forkless mode, even when you have PAM locally configured.
Be sufficiently careful.
pam_session will probably break things depending on PAM session being setup, while I (only imagine)
pam_setcred could break IPA, Kerberos or something else that relies on credentials tickets being issued through PAM.
log_servers are not that often used and with
!use_pty your target script will inherit first 3 file descriptors that parent setup for it. I cannot test in this environment, whether this actually prevents sudo from authenticating "remote" accounts or not.
Better, to minimize potential breakage for all other users of the machine, you should give these special permissions only to user in question:
Defaults: yourscriptuser !pam_session,!pam_setcred,!use_pty,!log_servers
Understand what you are doing and that sending signals from the parent script (to terminate child for example) might not work:
$ sudo cat /etc/sudoers | grep \!use_pty
Defaults: testuser !pam_session,!pam_setcred,!use_pty,!log_servers
# in one terminal:
$ sudo sh -c 'echo $$ && exec sleep 88888'
# in other terminal:
$ kill -TERM 27749
kill: kill 27749 failed: operation not permitted
Make sure your child script always exits after it does it's work or when it reads zero bytes from stdin handle (ie read it from time to time and exit on empty read) - which is standard unix indication the file was closed (in this case pipe from the parent), this should make all the lingering children exit properly should they still remain running, when parent terminates. Otherwise they might end up lingering around indefinitely depending on their code (forking sudo handles of that for you).