📜 ⬆️ ⬇️

FastCGI application in Perl. Part two.

The FastCGI application written in the previous article is chained to the same terminal from which it was launched. The application will run smoothly as long as the terminal is open. As soon as you close the terminal, the application will be killed immediately.

The reason is that any program launched from the terminal becomes a descendant of this terminal, and the terminal, respectively, the parent of this program. The existence of descendants without parents is not allowed. Accordingly, when a parent terminal is closed, all its descendants dependent on it, and in particular our FastCGI application, are immediately closed.

In order for the FastCGI application to no longer depend on the parent terminal, it must be converted from a simple script into a daemon.
')
What the hell is this?

A daemon is a program that runs in the background without direct user interaction. The parent of any daemon is init - the very first program that starts when the operating system boots and starts all other programs. init works from boot up to shutter, so the demons (unless kill intervenes, of course) also work without sleep and rest.

In order to become a demon, an application must perform a series of actions, the main of which is to get rid of parental care at all costs.

Take the script from the previous article and add to it a block of code that executes daemonization:

 #! / usr / bin / perl

 # To heighten the order
 use strict;
 use warnings;

 # This module implements the FastCGI protocol
 use FCGI;

 # Demonization {
     # This module is for talking with concepts on the operating system :)
     use POSIX;
   
     # Fork
     # getting rid of the parent
     fork_proc () && exit 0;
   
     # Start a new session
     # our demon will be the ancestor of the new session
     POSIX :: setsid () or die "Can't set sid: $!";
   
     # Go to root directory
     # so as not to interfere with unmounting a file system
     chdir '/' or die "Can't chdir: $!";
   
     # Change user to nobody
     # we are paranoid, huh?
     POSIX :: setuid (65534) or die "Can't set uid: $!";

     # Reopen standard handles on / dev / null
     # no longer talk to user
     reopen_std ();
 #}

 # Open the socket
 # our demon will listen to port 9000
 # query queue length - 5 pieces
 my $ socket = FCGI :: OpenSocket (": 9000", 5);

 # Getting to listen
 # daemon will intercept standard handles
 my $ request = FCGI :: Request (\ * STDIN, \ * STDOUT, \ * STDERR, \% ENV, $ socket);

 my $ count = 1;

 # Endless cycle
 # for each accepted request, one cycle is performed.
 while ($ request-> Accept ()> = 0) {
     # Inside the loop, all the required actions are performed
     print "Content-Type: text / plain \ r \ n \ r \ n";
     print $ count ++;
 };

 # Fork
 sub fork_proc {
     my $ pid;
   
     FORK: {
         if (defined ($ pid = fork)) {
             return $ pid;
         }
         elsif ($! = ~ / No more process /) {
             sleep 5;
             redo FORK;
         }
         else {
             die "Can't fork: $!";
         };
     };
 };

 # Reopen standard handles on / dev / null
 sub reopen_std {   
     open (STDIN, "+> / dev / null") or die "Can't open STDIN: $!";
     open (STDOUT, "+> & STDIN") or die "Can't open STDOUT: $!";
     open (STDERR, "+> & STDIN") or die "Can't open STDERR: $!";
 };

Those who are in the subject may have a question - why did I manually demonize instead of using the ready-made module Proc :: Daemon ?

The fact is that the sequence of commands required for demonization is minimized in the Proc :: Daemon module into one function. Later, when we are going to thread paralleling to the daemon, this can play a cruel joke with us. We will need to divide the process of demonization into two stages and it will be done only manually.

The demonization itself, in general, does not contain any particular differences from the example from the kookbook, and the implementation of the fork_proc function is taken almost one to one from the kemebook.

Separately, it is worth noting the change of user to nobody.

The user nobody is a special user who does not have any privileges in the system. Running the daemon on behalf of the user nobody provides additional protection in case some attacker can gain control of the daemon. In this case, running the daemon on behalf of nobody will not allow the attacker to gain access to the rest of the system’s resources.

The number 65534 in the setuid function is the uid of the user nobody. You can specify the uid using the special command:

$ id nobody

Yes, pay attention - only root can change uid, therefore it must also be root to start the daemon.

Particularly corrosive may have another question - why did I not provide signal handlers for correct completion?

The fact is that in our particular case, signal handlers are not required. Rather, handlers will be implemented by themselves, so to speak, in a magical way, when screwing parallelization.

Run the demon. If there are no errors, the daemon will start silently, output nothing and unhook from the console. Now you can close the terminal, the demon will not pay any attention to this and continue to work.

Open the browser test.host/fcgi-bin/ - the daemon responds as before. Now you can shut it up only with the kill command.

In the next part - parallelization, we teach the daemon to respond to several requests simultaneously.

( original article )

Source: https://habr.com/ru/post/53206/


All Articles