Call pcntl_fork twice cause PHP CGI dead loop - fork

function spawn($exec, $args = array()) {
$pid = pcntl_fork();
if ($pid < 0)
return false;
else if ($pid == 0) {
$ppid = getmypid();
$pid = pcntl_fork();
if ($pid < 0)
file_put_contents('/tmp/error.log', "fork failed: ${cmd} ". implode(' ', $args). "\n");
else if ($pid == 0) {
pcntl_waitpid($ppid, $status);
pcntl_exec($exec, $args);
}
else
exit(0);
}
}
This works well in CLI mode.
But for php-fpm it causes the caller dead loop and then a timeout.
Why this happens?

It doesn't work because calling exit() under FPM doesn't cause the parent process to exit -- it just makes it clean up the request, then return to the pool of available worker processes. Since it never actually exits, the pcntl_waitpid ends up waiting forever.
As Roman Newaza notes, you should really avoid the pcntl functions under FPM (and, in general, outside CLI).

Process Control should not be enabled within a web server environment and unexpected results may happen if any Process Control functions are used within a web server environment: PCNTL/Introduction

Related

Background process in my own shell program to ignore stdin

I'm working on my own little shell program in C. When I run the child process as a background process, I would like to ignore the input from the user coming from the parent process. I am currently trying to pipe it then close stdin for the child, but the input still goes to the child.
else // A process
{
pid_t child_pid;
char lastArgument = args[currArgsIndex-1][0];
if (lastArgument != '&'){ //Normal process
if((child_pid = fork()) == 0) {
execvp(filepath, args);
exit(0);
}
else
{
while(wait(NULL) != child_pid);
}
}
else { // Background
args[currArgsIndex-1] = NULL;
int process_pipe[2];
pipe(process_pipe); // Piping
if((child_pid = fork()) == 0) {
close(process_pipe[0]); // Ignore stdin for child
execvp(filepath, args);
exit(0);
}
}
}
You create a pipe and close the read end, but you never say that the pipe should be stdin.
It sounds like your intention was instead to 1. open the pipe only in the child, 2. close the write end so that no data can be read, 3. set the read end as stdin:
else { // Background
args[currArgsIndex-1] = NULL;
if((child_pid = fork()) == 0) {
int process_pipe[2];
pipe(process_pipe); // Piping
dup2(process_pipe[0], 0); // Copy read end as stdin
close(process_pipe[0]); // Close FD that is now unused
close(process_pipe[1]); // Close write end so no data can be read
execvp(filepath, args);
perror("execvp failed");
exit(1); // exit with error
}
}
There's no point having a pipe though. You can more easily open /dev/null for reading and setting that as stdin. Alternatively, simply close stdin entirely (some programs will complain):
else { // Background
args[currArgsIndex-1] = NULL;
if((child_pid = fork()) == 0) {
close(0); // Close stdin
execvp(filepath, args);
/* error handling */
}
Be aware that real shells allow redirecting to backgrounded processes, in which case none of the above will work:
wc -l < myfile &
Real shells will in fact not close or redirect stdin at all, but will put the command in its own process group that's not controlling the terminal. The process will then receive a SIGTSTP when it tries to read from stdin, and you can then use fg to bring it to the foreground to start typing data.

Automatically Resume a Suspended Windows Process

I'm trying to write a windows batch file in order to resume a windows process that gets Suspended. I'm using pssuspend (from pstools) to resume the process. However, I'm trying to write windows batch file script that will continually get the status of a process (e.g. myExe.exe). If the script is not suspended, I would like for it to keep checking if it is suspended. If it is suspended, I would like it to run the pssuspend code. I'm unsure how to obtain the Suspend status. So far I have this:
if myExe.exe == "Suspend" (
pssuspend -r myExe.exe
suspend_fix.bat
) else (
suspend_fix.bat
)
Thanks for your help!
Windows services (that are created with the right attributes) can be suspended, but I am not sure how an executable can be suspended, or what exactly you mean by that.
If you mean that the program has been stopped, and when it does, you want to restart it, then here are a couple of code blocks that I have used to determine if a program is running:
1) by checking to see if the exe name exists, i.e., is running.
By the way, I recommend this one from my interpretation of your post:
BOOL ExeExists(char *exe)
{
HANDLE pss = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof(pe);
if (Process32First(pss, &pe))
{
do
{
if (strstr(pe.szExeFile,exe))
{
CloseHandle(pss);
return TRUE;
}
}
while(Process32Next(pss, &pe));
}
CloseHandle(pss);
return FALSE;
}
2) by checking to see if the PID exists
BOOL PidExists(int pid)
{
HANDLE pss = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof(pe);
if (Process32First(pss, &pe))
{
do
{
if (pe.th32ProcessID == pid)
{
CloseHandle(pss);
return TRUE;
}
}
while(Process32Next(pss, &pe));
}
CloseHandle(pss);
return FALSE;
}
By the way this is used to get the process ID (it is defined in winbase.h)
of the application making the call.
int GetProcessIdApp(void)
{
return GetProcessId(GetCurrentProcess());//defined in WinBase.h
}
Inside WinBase.h
WINBASEAPI
DWORD
WINAPI
GetProcessId(
__in HANDLE Process
);
In my scenario, An application broadcasts its PID at start up, such that
my monitoring program (the Windows service) can read it, then use it to make an ongoing determination of the application's status. If the app is discovered to be dead, and if other criteria indicate it should still be running, my service will start it back up.

running a background process using a double fork?

pid = fork();
if(pid == 0 && background == 1){
// performs background process
pid2 = fork();
if(pid2 == 0){
// grandchild process
} else {
// child process
exit(0);
}
} else if(pid == 0){
// child process
} else {
//parent process
waitpid(pid, NULL, 0);
}
My question is I'm trying to perform a background process using double forks but I'm not 100% sure it works or not with the way I set it up, another question is after I double fork what is a good way to handle the orpaned children, can I just leave them or will that cause an issue? this is for a unix shell.

breaking 'sleep' in perl abruptly

I want to do the following -
#starting code
sleep(1000);
#remaining code
The starting code would run and get stuck at 'sleep'. After some time (<<1000), some other process would wake up this process by breaking this sleep(probably by sending a signal) and the rest of the program would run.
I have to use perl 5.6.1 on Windows, and it doesn't support alarm. I tried some signals like SIGINT, SIGFPE etc. but failed. Please suggest some alternative.
Are you using a signal handler? If not, SIGINT and its ilk will terminate your program.
my $parent_pid = $$;
# schedule interruption
if (fork() == 0) {
sleep 5;
kill 'INT', $parent_pid;
exit;
}
# trivial signal handler so SIGINT doesn't terminate the program
$SIG{INT} = sub { };
my $n = sleep 1_000_000;
print "Slept for $n seconds.\n";
On Linux, perl 5.6.2, this gives the output:
Slept for 5 seconds
For that matter, I don't know why you say alarm isn't supported on Perl 5.6 (unless you're on Windows maybe?) Again, set a signal handler or your program will terminate.
$SIG{ALRM} = sub {};
alarm 5;
$n = sleep 1_000_000;
print "slept for $n seconds\n";
works fine on my Perl 5.6.2.
Well, if I wanted to emulate
$SIG{ALRM} = \&handle_alarm;
alarm(5);
...
sleep(10);
I'd start with
use Time::HiRes qw( time sleep ); # Optional
my $alarm = time + 5;
...
my $timeout = $alarm - time;
if ($timeout <= 0) {
handle_alarm();
} else {
my $to_sleep = 10;
$to_sleep = $timeout if $timeout < $to_sleep;
sleep($to_sleep);
}

C - passing an unknown command into execvp()

I'm writing a fake shell, where I create a child process and then call execvp(). In the normal shell, when I enter an unknown command such as 'hello' it returns 'hello: Command not found.' However, when I pass hello into execvp(), it doesn't return any error by default and just continues running the rest of my program like nothing happened. What's the easiest way to find out if nothing was actually run? here's my code:
if(fork() == 0)
{
execvp(cmd, args);
}
else
{
int status = 0;
int corpse = wait(&status);
printf(Child %d exited with a status of %d\n", corpse, status);
}
I know that if corpse < 0, then it's an unknown command, but there are other conditions in my code not listed where I don't want to wait (such as if & is entered at the end of a command). Any suggestions?
All of the exec methods can return -1 if there was an error (errno is set appropriately). You aren't checking the result of execvp so if it fails, the rest of your program will continue executing. You could have something like this to prevent the rest of your program from executing:
if (execvp(cmd, args) == -1)
exit(EXIT_FAILURE);
You also want to check the result of fork() for <0.
You have two independent concerns.
1) is the return value of execvp. It shouldn't return. If it does there is a problem. Here's what I get execvp'ing a bad command. You don't want to wait if execvp fails. Always check the return values.
int res = execvp(argv[1], argv);
printf ("res is %i %s\n", res, strerror(errno));
// => res is -1 No such file or directory
2) The other concern is background processes and such. That's the job of a shell and you're going to need to figure out when your program should wait immediately and when you want to save the pid from fork and wait on it later.

Resources