Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Daemon with Net::Daemon

by tja_ariani (Acolyte)
on Sep 02, 2003 at 04:48 UTC ( [id://288240]=perlquestion: print w/replies, xml ) Need Help??

tja_ariani has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,
I need your wise advice from you all.

I need to create a daemon in a server(A), to be ready in accepting commands from a webserver(B).
Upon receiving this command, I need to execute a series of command lines in the server(A) to run an application, and get output of command execution.
The daemon needs to fork a new child when accepting a command such that it can be parallel.

I was reading Net::Daemon for this, but I have no idea of how to create the connection from A to B and passing the command.

Replies are listed 'Best First'.
Re: Daemon with Net::Daemon
by Preceptor (Deacon) on Sep 02, 2003 at 08:25 UTC
    This was discussed quite recently in node 286340. You may find some good answer's there.

    Looking at what you are saying, server B needs to be able to run commands on server A. You may find that something like rsh, ssh is the tool for the job. However, if it's something like 'server B connects, get's result of a defined commmand' then you're fairly safe in perl.
    If you have some way for the webserver to send the command it requires to server A, then consider very carefully security implications - a daemon that just accepts remote commands and runs them can be an extremely dangerous thing.
    In order to create the connection, then the module you want may be Net::Telnet. Or you could do it the slightly harder way by using IO::Socket (but you'll learn more)

    IMHO forking 'just because it needs to be parallel' is ugly, and the wrong answer. A 'fork', should be used in situations that you need to create an identical copy of the process. In most daemon examples I've seen, you don't.

    I know the perl cookbook recommends doing it this way, but I don't agree. I think the right way to do it (at least with a simple sort of 'do stuff, return result, disconnect' type daemon) is to use IO::Select, thus:

    #!/bin/perl use strict; use warnings; use IO::Select; use IO::Socket; #initialise our socket my $listener = IO::Socket::INET -> new( LocalPort => 5001, Proto => "tcp", Reuse => 1, Listen => 20, Blocking => 0, ); $listener -> autoflush(1); #create our 'select' groups my $listen_select = new IO::Select( $listener ); my $sockets = new IO::Select ( ); #A timeout of 0 can be workable, but is inefficient unless #you have _lots_ of IO my $timeout = 1; #seconds; while () { if ( $sockets -> count ) { #check for an incoming socket. Could probably merge #the two can_read statements. if ( $listen_select -> can_read($timeout) ) { my $client; my $raddr = accept($client, $listener); $sockets -> add ( $client ); $client -> autoflush(1); $timeout = 0; print "Connection accepted from ", print unpack ( "C*", $raddr ) +, "\n"; } my @ready = $sockets -> can_read($timeout); #if any socket has pending data, read it and act on it. foreach my $handle ( @ready ) { my $read_tmp = <$handle>; if ( !defined ( $read_tmp ) ) { print "Filehandle closed.\n"; $sockets -> remove ( $handle ); $handle -> close; } else { #do processing here. #imagine doing something good with read_tmp print "Got data: $read_tmp\n"; #and sending something cool to the client print $handle "Fwatoom\n"; } } } #if count else { #we have no handles in our thingy, so we sit idle waiting for #a connection. print "No pending data. Idling for connect()\n"; my $client; my $raddr = accept($client, $listener); $sockets -> add ( $client ); $client -> autoflush(1); $timeout = 0; print "Connection accepted from ", print unpack ( "C*", $raddr ), +"\n"; } }
    For the client side, you can do something like:
    Note that the two won't _quite_ work together - you'll have to modify one or the other. The server expects an input, and then sends an output, this snippet will simply get all the data it can from the server, and then exit when the connection closes.
    #!/bin/perl use strict; use warnings; use IO::Socket; my $destination_port = 5001; #change these how you will my $destination_address = "localhost"; my $socket = new IO::Socket::INET ( proto => "tcp", PeerAddr => $destination_address, PeerPort => $destination_port, ) or die "$!"; #send our "command" to the server print $socket "hello\n"; #See what the response is. In this case, we'll just keep #reading until the socket is closed remotely. while ( <$socket> ) { print; }
      Why do you not like fork for situations like this? To me it seems like the perfect tool to create little child processes. Also although I hate to point out the obvious the 'server' I posted above has maybe five times less code, making it substanially easier to read and write.
        I dislike the idea of duplicating the entire process for something simple like printing a response to the socket. Fork is know as a heavyweight process for a reason. (yes, I know that copy-on-demand alleviates this)

        I also rather like showing off an example of one of the other ways to do it, which becomes useful in some of the situations where a fork is not.

        As ever, there's more than one way to do it. Fork can be effective and simple, but IO::Select allows one some better control over child processes.

        I've never actually profiled the code, but I'd imagine that fork works pretty good on Unix, where it's integral to the OS. (Ish, because it becomes a little fuzzier when you're talking about forking perl, and the interpreted code) I'd not be so sure about doing so on Windows.

        Has anyone tried a benchmark of the relative methods?
Re: Daemon with Net::Daemon
by BUU (Prior) on Sep 02, 2003 at 08:02 UTC
    I would just use a simple forking tcp server to accept connections, do stuff, and return the values, but I may be slightly biased in their favor as I just did a bunch of stuff using them. Anyways, the code would look something like:
    $SIG{CHLD}='IGNORE'; # no zombies for me! use IO::Socket; my $server = new IO::Socket::INET ( LocalPort=>2000, LocalAddr=>'127.0.0.1', Type=>SOCK_STREAM, Reuse=>1, Listen=>10, ); while(my $client = $server->accept) { my $pid = fork; next if $pid; # # This is the body of the forked child, # simply read from the client using <$client> # and write using print $client foo; # then exit the child (DO NOT FORGET); exit; }
    Then you could simply use IO::Socket::INET to create a socket that connects to the above server and reads/writes commands to it.
Re: Daemon with Net::Daemon
by bm (Hermit) on Sep 02, 2003 at 10:31 UTC
    Here is an explanation of Net::Daemon.

    Define a new class that inherits from Net::Daemon in daemon.pl

    require Net::Daemon; package Contdmon; use vars qw(@ISA); @ISA = qw(Net::Daemon); # to inherit from Net::Daem
    In your class's constructor, call the Net::Daemon constructor and then do any other initialisation you need.
    sub new ($$;$) { my($class, $attr, $args) = @_; my($self) = $class->SUPER::new($attr, $args); # set up the logfile my $file = IO::File->new ( '/path', "a" ); $file->autoflush ( 1 ) ; $self->{'logfile'} = $file; $self; }
    Now write your Run method. This method overrides the Run method in Net::Daemon. It is executed whenever a client connects to the daemon
    sub Run ($) { my($self) = @_; my($line, $result, $sock); $sock = $self->{'socket'}; while (1) { if (!defined($line = $sock->getline())) { if ($sock->error()) { $self->Error("Client connection error %s", $sock->error()); } $sock->close(); return; } } print "Server received request: $line\n"; }
    Now go ahead and create your server
    package main; my $args = { 'facility' => 'daemon', 'pidfile' => '/users/ccm_root/logs/contdaem.pid', 'logfile' => '/users/ccm_root/logs/contdaem.log', 'user' => 'ccm_root', 'group' => 'ccm_root', 'localport' => '5440', 'mode' => 'single', }; my $server = Contdmon->new ({}, $args); $server->Bind();
    So at this point you have a daemon sitting there waiting for a request. Run this script on server A.
    Now you need a client to submit a request from server B. In client.pl:
    use strict; use IO::Socket; $| = 1; # flush output buffers immediately my ( $host ) = 'server'; my ( $port ) = 1234; # create the socket my $sock = IO::Socket::INET->new ( Proto => 'tcp', Type => SOCK_STREAM, PeerAddr => $host, PeerPort => $port ) or die "Cannot create socket : $!"; # so output goes to daemon immediately $sock->autoflush (1); print $sock "What is the time, Mr Wolf?\n";
    Run client.pl on your client machine, webserver(B), and it will send a request via the TCP protocol to your daemon waiting on server A.
    Note that there are some security concerns with this. Don't let your daemon accept just any list of commands to run on server A. Perhaps sends 'RunA' from client.pl, and your daemon will then go ahead and execute a hardcoded set of commands before sending back the response.
    Hope this helps
    --
    bm

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://288240]
Approved by Corion
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2024-04-24 02:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found