# How to write a Ruby service(?)?

**URL:** <https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245>\
**Category:** Ruby\
**Created:** [March 18, 2006, 2:22pm UTC](https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245 "2006-03-18T14:22:41Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![randy\_k](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/258eb7/32.png) [@randy\_k](https://www.ruby-forum.com/u/randy_k)\
**Post date:** [March 18, 2006, 2:22pm UTC](https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245/1 "2006-03-18T14:22:41Z")

</div>

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

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 K.

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).

---

<div class="post-metadata">

**Author:** ![randy\_k](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/258eb7/32.png) [@randy\_k](https://www.ruby-forum.com/u/randy_k)\
**Post date:** [March 18, 2006, 7:06pm UTC](https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245/2 "2006-03-18T19:06:21Z")

</div>

On Saturday 18 March 2006 08:19 am, Randy K. 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 K.

---

<div class="post-metadata">

**Author:** ![randy\_k](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/258eb7/32.png) [@randy\_k](https://www.ruby-forum.com/u/randy_k)\
**Post date:** [March 18, 2006, 7:12pm UTC](https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245/3 "2006-03-18T19:12:25Z")

</div>

On 3/18/06, Randy K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> But, any hints appreciated!
> 
> Randy K.

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

---

<div class="post-metadata">

**Author:** ![randy\_k](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/258eb7/32.png) [@randy\_k](https://www.ruby-forum.com/u/randy_k)\
**Post date:** [March 18, 2006, 8:08pm UTC](https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245/4 "2006-03-18T20:08:09Z")

</div>

Pat M. wrote:

> > But, any hints appreciated!
> > 
> > Randy K.
> 
> 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 B.'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/files/doc/cli\_txt.html](http://path.berkeley.edu/vii/viicatl/doc/ruby-api/files/doc/cli_txt.html)).

---

<div class="post-metadata">

**Author:** ![randy\_k](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/258eb7/32.png) [@randy\_k](https://www.ruby-forum.com/u/randy_k)\
**Post date:** [March 20, 2006, 2:11pm UTC](https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245/5 "2006-03-20T14:11:32Z")

</div>

On Saturday 18 March 2006 03:21 pm, Guillaume M. 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/](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 K.

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.

---

<div class="post-metadata">

**Author:** ![randy\_k](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/258eb7/32.png) [@randy\_k](https://www.ruby-forum.com/u/randy_k)\
**Post date:** [March 18, 2006, 9:24pm UTC](https://www.ruby-forum.com/t/how-to-write-a-ruby-service/55245/6 "2006-03-18T21:24:08Z")

</div>

Le 18 mars 06, à 08:19, Randy K. a écrit :

> program   
> 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/](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.
