Forum: Ruby How to write a Ruby service(?)?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
F5b3c1ebfb2e9fc5f67bb48b119f6054?d=identicon&s=25 Randy Kramer (Guest)
on 2006-03-18 14:22
(Received via mailing list)
I need to write what might be called a Ruby service or server(?)--a Ruby
program that:
   * runs only one instance even if "invoked" multiple times
   * can be sent additional "commands" via CLI commands, e.g., if the
name of
the program is, for example, "program" I can issue multiple CLI commands
like:

program <parameters>

If program is not running, it will start and perform appropriate actions
based
on the parameters.

If program is running, the existing instance of the program will receive
those
parameters and perform appropriate actions.

At the moment (and maybe permanently ;-) I'm drawing a blank as to what
a
program like that is called (service, server, ...?), and where to dig to
learn how to implement such a thing (preferably in Ruby).

Hints?

Initially I plan to implement this under Linux--some day it may get
ported to
Windows, etc.

Thanks!
Randy Kramer

Incidentally, if the program crashes, the next invocation of the program
will
restart it and recover as much of the previous "context" as possible
(which
is, of course, only what has been recorded to disk).
F5b3c1ebfb2e9fc5f67bb48b119f6054?d=identicon&s=25 Randy Kramer (Guest)
on 2006-03-18 19:06
(Received via mailing list)
On Saturday 18 March 2006 08:19 am, Randy Kramer wrote:
> I need to write what might be called a Ruby service or server(?)--a Ruby
> program that:
>    * runs only one instance even if "invoked" multiple times
>    * can be sent additional "commands" via CLI commands, e.g., if the name
> of the program is, for example, "program" I can issue multiple CLI commands
> like:

Oops, wait--I'm starting to wake up now--that would be a daemon,
wouldn't it?
Off to Google [Ruby daemon].

But, any hints appreciated!

Randy Kramer
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2006-03-18 19:12
(Received via mailing list)
On 3/18/06, Randy Kramer <rhkramer@gmail.com> wrote:
>
> But, any hints appreciated!
>
> Randy Kramer
>
>

Well fwiw, usually you would write two apps - a client and a server.
The server will be invoked once and daemonized, and you use the client
to issue commands.  You can connect to the client either via TCP or
unix domain sockets.

Pat
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-03-18 20:08
(Received via mailing list)
Pat Maddox wrote:
>>
>> But, any hints appreciated!
>>
>> Randy Kramer
>>
>>
>
> Well fwiw, usually you would write two apps - a client and a server.
> The server will be invoked once and daemonized, and you use the client
> to issue commands.  You can connect to the client either via TCP or
> unix domain sockets.

That approach has been working really nicely for me. Some suggestions:

- look on raa for daemon or daemonize, it's got the 5 or 6 basic steps
you need to do to make a process a daemon on unix/linux (maybe use
Daniel Berger's win32service on windows--I haven't tried that).

- when the server starts, it reads its URI from a config file, and sets
up drb (or other protocol) at that URI. If the service is already
running there, bail out (so the server is unique). (I like drbunix for
this, since you can chmod the socket.)

- the client has a command line option to read from a server config
file, so you just have to know the filename, not URI (assuming you are
on the same host).

- the client uses the return code to indicate server up/down, to make
shell scripting easy ("client --remote server.config || server --start
--file server.config")

- the client can have an additional command line option (or set of them)
to send drb requests to the server

I can send you a link to the code that I use for this, but it's mixed in
with a lot of other stuff... (the command line doc is at
http://path.berkeley.edu/vii/viicatl/doc/ruby-api/...).
D8831c4665a164c6ce484003deb1afd6?d=identicon&s=25 Guillaume Marcais (Guest)
on 2006-03-18 21:24
(Received via mailing list)
Le 18 mars 06, à 08:19, Randy Kramer a écrit :

> program <parameters>
> what a
> program like that is called (service, server, ...?), and where to dig
> to
> learn how to implement such a thing (preferably in Ruby).
>
> Hints?
>
> Initially I plan to implement this under Linux--some day it may get
> ported to
> Windows, etc.

I have some code that do kind of what you are describing. But I have to
admit that lately, when I run a daemon/service on my Linux box, I am
incline to manage it with something like runit
(http://smarden.sunsite.dk/runit/). I find it easier than to manage
than the SysV services.

On to some code. The server says:

module TelnetShellServer
   def self.daemonize
     return true if fork             # Parent exits, child continues.
     Process.setsid                  # Become session leader.
     exit if fork                    # Zap session leader. See [1].
     Dir.chdir "/"                   # Release old working directory.
     File.umask 0000                 # Ensure sensible umask. Adjust as
needed.
     STDIN.reopen("/dev/null", "r")  # Free file descriptors and
     stdout_r, stdout_w = IO.pipe
     stderr_r, stderr_w = IO.pipe
     Thread.new { loop { $logger.info(stdout_r.gets.chomp) } }
     Thread.new { loop { $logger.error(stderr_r.gets.chomp) } }
     STDOUT.reopen(stdout_w)
     STDERR.reopen(stderr_w)
     nil
   end

   class Broker
     [... actual useful service code ...]
   end
end

[ ... read options and configuration file ...]

unless foreground
   TelnetShellServer.daemonize and exit 0
end

$SAFE = 1   # disable eval() and friends
BROKER = Broker.new(login_files, auto_kill)
$logger.info("Starting DRb")
DRb.start_service(uri.to_s, BROKER)
$logger.info("Broker listening at #{uri}")
trap("TERM") { $logger.debug("TERM"); BROKER.stop }
trap("QUIT") { $logger.debug("QUIT"); BROKER.stop }
trap("INT") { $logger.debug("INT"); BROKER.stop }
trap("HUP") { $logger.debug("HUP"); BROKER.reread_logins }

DRb.thread.join


The client says:

[ ... read options and configuration file ...]
uri = URI.parse(uri)
DRb.start_service
broker = DRbObject.new_with_uri(uri.to_s)
tries = 2
host, cmd = ARGV.shift, ARGV.join(' ')
begin
   res = broker.connection_do(host, cmd)
   puts(res) if res
rescue DRb::DRbConnError
   if auto_vivify && ((tries -= 1) > 0)
     system("telnet_shell_serverd")  # server not present, start it!
     sleep(2)
     retry
   end
   raise
end

HTH,
Guillaume.
F5b3c1ebfb2e9fc5f67bb48b119f6054?d=identicon&s=25 Randy Kramer (Guest)
on 2006-03-20 14:11
(Received via mailing list)
On Saturday 18 March 2006 03:21 pm, Guillaume Marcais wrote:
> I have some code that do kind of what you are describing. But I have to
> admit that lately, when I run a daemon/service on my Linux box, I am
> incline to manage it with something like runit
> (http://smarden.sunsite.dk/runit/). I find it easier than to manage
> than the SysV services.

Thanks to all who replied!

I'm enough of a newbie that it will take me a little while to sort
through
these responses--when I'm ready, I'll ask the next question (because I'm
sure
there will be one ;-)

Randy Kramer

Aside: Someone else suggested using a fifo (starting with mkfifo)--I'll
need
to do some experimenting with that as well.  I suspect it won't give all
the
functionality I thought I wanted, but it may be a simple approach to get
started.
This topic is locked and can not be replied to.