FastCGI application in Perl. Part two.

    The FastCGI application written in the previous article is chained together with the 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, is the parent of this program. The existence of descendants without parents is not allowed. Accordingly, when a parent-terminal is closed, all descendants dependent on it and, in particular, our FastCGI application, are immediately closed.

    In order for the FastCGI application to cease to depend on the parent terminal, it must be converted from a simple script to 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 up and starts all other programs. init works from boot to shutdown, so daemons (unless, of course, kill intervenes) also work without sleep or rest.

    In order to become a demon, the 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 a block of code to it that performs demonization:

    #! / usr / bin / perl
    # To put things in order
    use strict;
    use warnings;
    # This module implements FastCGI protocol
    use FCGI;
    # Demonization {
        # This module is for talking with the OSes on concepts :)
        use POSIX;
        # Fork
        # get rid of the parent
        fork_proc () && exit 0;
        # Start a new session
        # our demon will be the founder of a new session
        POSIX :: setsid () or die "Can't set sid: $!";
        # Go to the root directory
        # so as not to interfere with unmounting the 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 descriptors on / dev / null
        # no longer talking to user
        reopen_std ();
    #}
    # Open socket
    # our demon will listen on port 9000
    # request queue length - 5 pieces
    my $ socket = FCGI :: OpenSocket (": 9000", 5);
    # Start to listen
    # daemon will intercept standard descriptors
    my $ request = FCGI :: Request (\ * STDIN, \ * STDOUT, \ * STDERR, \% ENV, $ socket);
    my $ count = 1;
    # Endless cycle
    # at each received request, one “revolution” of the 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 descriptors 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: $!";
    };
    

    For those in the topic, the question may arise - why did I do the demonization manually, instead of using the ready-made Proc :: Daemon module ?

    The fact is that the sequence of commands required for demonization is collapsed in the Proc :: Daemon module into one function. In the future, when we fasten parallelization to the demon, this can play a trick on us. We will need to divide the demonization process into two stages and it will only be possible to do this manually.

    The demonization itself, in general, does not contain any special differences from the cookbook example, and the implementation of the fork_proc function is taken almost one in one of the camelbook.

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

    User nobody is a special user that does not have any privileges on the system. The work of the daemon on behalf of the user nobody provides additional protection in the event that an attacker can gain control of the daemon. In this case, the work of the daemon on behalf of nobody will prevent the attacker from gaining access to other system resources.

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

    $ id nobody

    Yes, note that only root can change uid, so root must also run the daemon.

    Especially corrosive, one more question may arise - why didn’t I provide signal handlers for correct completion?

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

    We start the demon. If there are no errors, the daemon will start silently, will not display anything, and will detach from the console. Now you can close the terminal, the daemon will not pay any attention to this and will continue to work.

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

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

    ( original article )

    Also popular now: