forking a new process in perl on linux - linux

I want to fork a background process from a cgi call. So that the httpd call returns immediately and rest of the stuff keeps running
This used to work all this while until we migrated to a new machine
............
## Close the http connection so that the remote client returns
close STDOUT;
close STDERR;
POSIX::setsid();
fork() && exit;
do_job();
.........
Now on the new machine , same code never executes the do_job()
Perl , httpd versions are the same ( there is a minor kernel upgrade )
Now I changed the code to
..........
open(STDOUT,">/dev/null");
open(STDERR,">/dev/null");
POSIX::setsid();
fork() && exit;
do_job();
.........
This works, but I am not sure why

I'm not quite sure about why the first code will work. But you should always put setsid after the fork(). Pls let me know if it doesn't work after you doing this.
Every process group is in a unique session. (When the process is created, it becomes a member of the session of its parent.) By convention, the session ID of a session equals the process ID of the first member of the session, called the session leader. A process finds the ID of its session using the system call getsid().
A new session is created by
pid = setsid();
This is allowed only when the current process is not a process group leader.
Let me explain why. Assume if you are process group leader or session leader you have to understand that process group and session ids are initialized from the process id of the process creating them (and then leading them, i.e. for a session leader pid == sid and for a process group leader pid == pgid). Moreover, process groups cannot move between sessions.
That means if you are a process group leader, and creating a new session would be allowed then the sid and the pgid would get set to your pid, leaving the other processes in your old process group in a weird state: their process group leader suddenly is in a different session then they themselves might be. And that cannot be allowed, hence the EPERM returned by the kernel.
Now if you fork() once you are neither session nor process group leader anymore and hence setting your sid and pgid to your pid is safe, because there are no other processes in such group.
The following links might be helpful:
Sessions and Process Groups
The Linux Kernel: Process

I recommend you the use of an external queue that handles this kind of work. You can use some CPAN modules to get this funcionallity, like Queue::DBI or others, like Gearman

Related

File descriptor for ioctl call to make a controlling terminal

On linux to be able to control lifetime of processes forked off of my main process I'm making the main process be the session and group leader by calling setsid(). Then it looks like I need to have the main process make a controlling terminal for the process group, and then, once the main process terminates, all other processes in the process group will receive a SIGHUP. I tried calling open() for a regular file on the filesystem, but ioctl() refuses to accept this fd with 'Inappropriate file descriptor'. Is posix_openpt() what I should be using instead? The man page says that it'll create a pseudo-terminal and return a file descriptor for it. Do I even need an ioctl(fd, TIOCSCTTY, 0) call after posix_openpt(), or not using O_NOCTTY is all I really need? Thanks!
Do I even need an ioctl(fd, TIOCSCTTY, 0) call after posix_openpt(), or not using O_NOCTTY is all I really need?
I just tried on Ubuntu 18.04.5:
If you don't do that and the controlling process is closed, the systemd process becomes the new controlling process of the child process and the child process does not receive SIGHUP.
I'm not sure if this behavior is the same for other Linux distributions, too.
Is posix_openpt() what I should be using instead?
Try the following code:
int master, tty;
master = posix_openpty(O_RDWR);
grantpt(master);
unlockpt(master);
tty = open(ptsname(master), O_RDWR);
ioctl(tty, TIOCSCTTY, 0);
This must be done in the same process that called setsid().
Note: As soon as you completely close the master file, the processes will receive a SIGHUP.
("Completely" means: When you close all copies created by dup() or by creating a child process inheriting the handle.)
If you really want to use the pseudo-TTY, you should not inherit the master handle to child processes (or close() the handle in a child process. However, in your case you only want to use the pseudo-TTY as "workaround", so this is not that important.

How to run os.system to start a process through a different pid?

import os
from kazoo.client import KazooClient
signal.signal(signal.SIGINT, signal_handler)
def signal_handler(signal,frame):
print('\nYou pressed Ctrl+C!')
print("Stopping...Process "+process_id)
print()
children = zk.get_children("/assign2/root")
l = [int(i[3:]) for i in children]
l.remove(int(process_id))
print("Min process id : ",min(l))
zk.delete("/assign2/root/pid"+process_id, recursive=True)
#To run a command on terminal --> os.system("python3 zook.py")
if(int(process_id)==min(l)):
print("Starting new process through :" , min(l))
os.system("python3 zook.py")
os.kill(int(process_id),0)
zk.stop()
sys.exit(0)
zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()
zk.ensure_path("/assign2/root")
zk.create("/assign2/root/pid"+process_id, bytes(process_id, encoding='utf-8'),ephemeral=True)
On killing a process, I want to find the smallest of the remaining pids and start a process through the smallest pid.
I am using zookeeper to store pids as ephemeral znodes and upon terminating the python script in terminal, I'm trying to create another process.
I am able to create a process through the pid of the process I am terminating, but not from another pid of my choice.
Please let me know if there is a way to do this.
So, what you need here is monitoring all your different processes (each process monitoring all other), and wait for the failure of a process. In zookeeper world, this can be achieved via:
1) Zookeeper watches: Each zookeeper client (process) can set up a watch on the parent znode (In your case /assign2/root/) and watch for CHILD_DELETE events. You can have a look at the zookeeper documentation and the documentation of your specify library (kazoo) to see how you can do that. On setting up the watch, you can specify a callback which would be executed asynchronously, each time a znode under your parent znode disappears. This could happen because:
The child znode is ephemeral and the zk client which create that
znode died.
The client explicitly deleted the child node. For example in your case you can delete the child node in the signal handler.
In this callback, each of the alive zookeeper clients/processes, will determine if its the lowest ranked process id (by fetching the list of all existing children under the parent znode), and if it is indeed the smallest pid, it will spawn the new process (python3 zook.py).
Note, that zookeeper watches are one-time fire concept. This means that after the watch has fired (i.e. the callback has executed), you would want to reset the watch at the very end of the callback execution, so that the next child delete event will also result in the callback firing.
2) The alternative approach is polling. In each of your process, you can have a dedicated thread which can periodically monitor the children of your parent znode, and each time a process detects that a node has disappeared, it can again determine if its the lowest alive pid and spawn a new process, if it indeed is.
Hope this helps. Let me know if you have any doubts. Would have liked to post the code you needed, but have to run for an errand.

How a session leader can take the control of tty in Linux?

I was trying to daemon a process, and have found that we should fork() it twice. The reason is that if the process is a session-leader then it can take a control of tty. I was wondering, what is the rationale behind this theory.
The first fork is to run the daemon in a separate session. What you are expected to do for daemonizing is (pseudo-code):
fork()
if (parent) {
return
else if (child) {
setsid();
closefds();
fork();
if (child) {
childwork();
} else {
exit(0);
}
}
The setsid call puts the daemon in it's own session and disassociates it from the tty of the parent process. If you don't do the setsid call, then you don't get the new session, and the daemon could be interfered with by the process group that it still belongs to - i.e. it could be interfered by a HUP sent to the entire process group.
The second fork() call causes the process to no longer be the session leader. This means that process will not be able to acquire a controlling terminal if it opens an unused terminal device (either by accident, or intentionally) because it's not the session leader. If it was still the session leader, then you would have to make sure that all open calls that open terminal devices would have to be invoked with O_NOCTTY to be on the safe side.

Signals shortcut keys and process groups

I have one simple question about signals in Linux systems. As I understand every process has it's PID and PGID. When I create a process it gets it's unique PID, now if I would fork a new process with fork() function I would get child process with different PID but the same PGID.
Now, the code
#include<stdio.h>
#include<unistd.h>
int main()
{
int i=3;
int j;
for(j=0;j<i;++j)
{
if (fork() == 0)
{
while(1)
{
}
}
}
printf("created\n");
while(1)
{
}
return 0;
}
when I compile this program and run it with the command
./foo
and wait a sec so he creates his children and I do CTRL-C and then ps aux I can see that the parent and the children are gone, but if I do
./foo
wait for forking to complete and in other terminal do
kill -INT <pid_of_foo>
and ps aux I can see that the parent is gone but children are still alive and eating my CPU.
I am not sure, but it seems that CTRL-C sends the signal to every process that is in some process group and the KILL -SIGNAL pid command sends the signal to the process with PID=pid not PGID=pid.
Am I on the right track? If yes, why the key combination kills processes with PGID and not PID?
Signal delivery, process groups, and sessions
Yes, you are on the right track.
Modern Unix variants since the BSD releases implement sessions and process groups.
You can look at sessions as groups of process groups. The idea was that everything resulting from a single login on a tty or pseudo-tty line is part of a session, and things relating to a single shell pipeline or other logical grouping of processes would be organized into a single process group.
This makes moving "jobs" between the foreground and background and delivering signals more convenient. The shell users mostly doesn't need to worry about individual processes but can control-C a group of related commands in an intuitive manner.
Signals generated by the keyboard are sent to the foreground process group in a session. The CLI kill command you are using delivers signals to individual processes. If you want to try to duplicate the ^C delivery mechanism you can use kill 0; that will send the signal to every member of the same process group, and if executed from a script it may do what you want.
Note: I edited your question to change GPID to PGID.

How can a process kill itself?

#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int main(){
pid_t pid = fork();
if(pid==0){
system("watch ls");
}
else{
sleep(5);
killpg(getpid(),SIGTERM); //to kill the complete process tree.
}
return 0;
}
Terminal:
anirudh#anirudh-Aspire-5920:~/Desktop/testing$ gcc test.c
anirudh#anirudh-Aspire-5920:~/Desktop/testing$ ./a.out
Terminated
for the first 5 secs the output of the "watch ls" is shown and then it terminates because I send a SIGTERM.
Question: How can a process kills itself ? I have done kill(getpid(),SIGTERM);
My hypothesis:
so during the kill() call the process switches to kernel mode. The kill call sends the SIGTERM to the process and copies it in the process's process table. when the process comes back to user mode it sees the signal in its table and it terminates itself (HOW ? I REALLY DO NOT KNOW )
(I think I am going wrong (may be a blunder) somewhere in my hypothesis ... so Please enlighten me)
This code is actually a stub which I am using to test my other modules of the Project.
Its doing the job for me and I am happy with it but there lies a question in my mind how actually a process kills itself. I want to know the step by step hypothesis.
Thanks in advance
Anirudh Tomer
Your process dies because you are using killpg(), that sends a signal to a process group, not to a process.
When you fork(), the children inherits from the father, among the other things, the process group. From man fork:
* The child's parent process ID is the same as the parent's process ID.
So you kill the parent along with the child.
If you do a simple kill(getpid(), SIGTERM) then the father will kill the child (that is watching ls) and then will peacefully exit.
so during the kill() call the process switches to kernel mode. The kill call sends the SIGTERM to the process and copies it in the process's process table. when the process comes back to user mode it sees the signal in its table and it terminates itself (HOW ? I REALLY DO NOT KNOW )
In Linux, when returning from the kernel mode to the user-space mode the kernel checks if there are any pending signals that can be delivered. If there are some it delivers the signals just before returning to the user-space mode. It can also deliver signals at other times, for example, if a process was blocked on select() and then killed, or when a thread accesses an unmapped memory location.
I think it when it sees the SIGTERM signal in its process tables it first kills its child processes( complete tree since I have called killpg() ) and then it calls exit().
I am still looking for a better answer to this question.
kill(getpid(), SIGKILL); // itself I think
I tested it after a fork with case 0: and it quit regular from separate parent process.
I don't know if this is a standard certification method ....
(I can see from my psensor tool that CPU usage return in 34% like a normal program code with
a counter stopped ) .
This is super-easy in Perl:
{
local $SIG{TERM} = "IGNORE";
kill TERM => -$$;
}
Conversion into C is left as an exercise for the reader.

Resources