Nuby Q: Daemonize (other materials have been read!)

I’ve read the other references to daemonizing a process in ruby, but …
well there’s not enough to the examples to get me going properly.

I’m using Ruby 1.8 on a Linux box.

I am trying to write a function which will daemonize the current script
into one child process, which will then run isolated from the tty.
Using information found here (at the forum) and in other places, I wrote
this function:

def daemonize()

process documented in

http://blog.humlab.umu.se/samuel/archives/000107.html

begin
exit if fork
Process.setsid
exit if fork
File.umask 0774
STDIN.reopen “/dev/null” # no stdin
STDOUT.reopen $log
STDERR.reopen STDOUT # stderr & stdout to syslog
rescue
raise “could not daemonize the process.”
end
end # gvp_daemonize()

Then I read in the 2nd Ed Pragmatic Ruby that there should be a
Process.detach, if I don’t care about the parent getting feedback (which
I don’t)., so I changed my first “exit if fork” to “if fork {
Process.detach\nexit }”

The problem is that the process just exits. Nothing else gets done. I
know I’m doing something wrong, but I’m too much of a ruby-nuby (oh,
gee, and fork nuby too) to grok it from the examples I’ve seen, and the
book, and the web … HELP please.

FYI: I also need to figure out how to reset the userid and groupid, but
I have a book. :slight_smile:

Thanks for any assistance rendered.

Rogue Amateur

On Wed, 12 Jul 2006, Rogue Amateur wrote:

def daemonize()
STDOUT.reopen $log

The problem is that the process just exits. Nothing else gets done. I
know I’m doing something wrong, but I’m too much of a ruby-nuby (oh,
gee, and fork nuby too) to grok it from the examples I’ve seen, and the
book, and the web … HELP please.

here is a script which can be used on the command line to daemonize any
other
process:

harp:~ > cat daemon.rb
#! /usr//bin/env ruby
fork{
STDIN.reopen(STDOUT.reopen(STDERR.reopen(open(‘/dev/null’,‘r+’))));
fork{ system ARGV.join(’ ') ? exit : abort } and exit! }

use it like

harp:~ > daemon.rb any_other_command

FYI: I also need to figure out how to reset the userid and groupid, but
I have a book. :slight_smile:

you cannot do this from withint a script - a restriction placed by unix

  • only
    binary programs may be ‘setuid’ programs. i have a method to do this in
    ruby,
    which is often important for web and db related work where
    authentication might
    be allowed for only certain reasons. in any case setuid is really only
    appropriate for certain security situations and can be very, very bad
    for your
    system health. my inclination would be to say that if you are reading
    about it
    you shouldn’t use it! :wink: that being said this will accomplish what
    you
    seek:

    http://codeforpeople.com/lib/ruby/_ruby/

regards.

-a

First, let me say thank you for the help.

unknown wrote:

On Wed, 12 Jul 2006, Rogue Amateur wrote:

[snip]

here is a script which can be used on the command line to daemonize
any
other process:

harp:~ > cat daemon.rb
#! /usr//bin/env ruby
fork{
STDIN.reopen(STDOUT.reopen(STDERR.reopen(open(’/dev/null’,‘r+’))));
fork{ system ARGV.join(’ ') ? exit : abort } and exit! }

use it like

harp:~ > daemon.rb any_other_command

Ok, ignorance again:

  1. this means I can’t do the fork within the script itself?
  2. suppose the any_other_command isn’t in the default ruby library? do
    I supply the full path to call? Will it allow me to pass along
    arguments?
    (I’ll be trying it, as I await a response…)

FYI: I also need to figure out how to reset the userid and groupid,
but

I have a book. :slight_smile:

you cannot do this from withint a script

[snip]

my inclination would be to say that if you are reading
about it
you shouldn’t use it! :wink:

Gotcha. So the follow on question is: if I need my now isolated process
to run as a particular user/group, the process is…? What? My local
guru, who speaks about 15 levels too high for me, said “set the user and
group ids.” See, I can parrot! :slight_smile:

I’m familiar with sudo, but… would that work with the daemon.rb? (eg:
sudo daemon.rb My_Ruby_Script.rb [arg] [arg]…)

Thanks again
Rogue Amateur

Rogue Amateur wrote:
[snip, in follow up]

Ok, ignorance again:

  1. this means I can’t do the fork within the script itself?
  2. suppose the any_other_command isn’t in the default ruby library? do
    I supply the full path to call? Will it allow me to pass along
    arguments?
    (I’ll be trying it, as I await a response…)

[snip again]

I tried it:
./daemon.rb ./my_Script.rb
it dies without comment.

running ./my_Script.rb, however, works as expected. Notions?

RA

On Wed, 12 Jul 2006, Rogue Amateur wrote:

[snip again]

I tried it:
./daemon.rb ./my_Script.rb
it dies without comment.

running ./my_Script.rb, however, works as expected. Notions?

it doesn’t die - it starts the script as a daemon. check in top.

regarding the setuid/gid bit - your local guru is simply wrong - you
cannot do
that from withint a script. google around a bit. your options are to
use
sudo, which is designed to do exactly this, or my _ruby code, which is a
specialized version that accomplishs much the same thing. in either
case you
will require root privledges to setup the setuid binary or sudo
permission if
you go that route.

cheers.

-a

unknown wrote:

On Wed, 12 Jul 2006, Rogue Amateur wrote:

[snip again]

I tried it:
./daemon.rb ./my_Script.rb
it dies without comment.

running ./my_Script.rb, however, works as expected. Notions?

it doesn’t die - it starts the script as a daemon. check in top.

Ok, then I’m really confused. Because the script is NOT running.

The script in question sends data to and receives data from a socket. I
have test code written which sends data to the appropriate socket, and
receives back, and prints the result.

When I started the listener, then daemonized the script, the listener
sat there doing nothing. ps -ef returned nothing (for the script name).
When I started the script alone, suddenly the listener has something to
do, and the ps -ef returns data. I am sure I did something wrong, I
don’t doubt it a bit, but… it really doesn’t look like the script is
running as a daemon. Am I using the wrong checks? Will my sockets
suddenly work differently when run as a daemon? I haven’t found
anything that says that’s likely so… I must be confused.

Thank you for your quick responses.

RA

[long snip]

FYI: I also need to figure out how to reset the userid and groupid, but
I have a book. :slight_smile:

you cannot do this from withint a script - a restriction placed by unix -
only
binary programs may be ‘setuid’ programs.

Right but sudo might be what the doctor ordered :slight_smile:

i have a method to do this in ruby,

http://codeforpeople.com/lib/ruby/_ruby/

regards.

-a

suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.

  • h.h. the 14th dali lama

Cheers
Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

Why don’t you use the daemonize ruby library? It works great on
linux/solaris.

Check out the 4 liner over at redhanded:

http://redhanded.hobix.com/inspect/daemonize.html

Need the library?

http://grub.ath.cx/daemonize/

ARJ

unknown wrote:

On Wed, 12 Jul 2006, Rogue Amateur wrote:

[snip]

fortytwo :~ > daemon sleep 42

So, this works. So does another, innocuous script I wrote. I even
checked out the daemonize library the other person suggested, and it
works too. My pain in the butt actually need it to work as a daemon,
however, does not work.

What could cause code which works in interactive mode to fail in daemon
mode?

I’m checking the code now, and I do have “if debug” statements which
output to STDOUT. Could that be the problem?

I’m adding some blurbs to try to get the stupid thing to tell me, but it
seems to die so silently (eg: nothing in syslog, which is where most
messages are expected to go), that I can’t get enough information.

I know it manages to send the “shut down the listener” message, which is
a “rescue” action.

Hints, suggestions and other comments?

RA

On Wed, 12 Jul 2006, Rogue Amateur wrote:

When I started the listener, then daemonized the script, the listener
sat there doing nothing. ps -ef returned nothing (for the script name).
When I started the script alone, suddenly the listener has something to
do, and the ps -ef returns data. I am sure I did something wrong, I
don’t doubt it a bit, but… it really doesn’t look like the script is
running as a daemon. Am I using the wrong checks? Will my sockets
suddenly work differently when run as a daemon? I haven’t found
anything that says that’s likely so… I must be confused.

Thank you for your quick responses.

hmmm. not sure what to say. here it is spelled out a bit more:

fortytwo :~ > cat which daemon
#! /usr/bin/env ruby

fork{
fork{
null = open ‘/dev/null’, ‘r+’
[STDIN, STDOUT, STDERR].each{|io| io.reopen null}
cmd = ARGV.join(’ ')
status = system cmd
status ? exit : abort
}
exit!
}

fortytwo :~ > daemon sleep 42

fortytwo :~ > ps -elf |grep sleep
1 S ahoward 18615 1 0 79 0 - 715 wait 11:19 pts/50
00:00:00 ruby /home/ahoward/bin/daemon sleep 42
0 S ahoward 18616 18615 0 79 0 - 969 - 11:19 pts/50
00:00:00 sleep 42
0 R ahoward 18618 18118 0 77 0 - 96 - 11:19 pts/50
00:00:00 grep sleep

regards.

-a

On Fri, 14 Jul 2006, Rogue Amateur wrote:

So, this works. So does another, innocuous script I wrote. I even
checked out the daemonize library the other person suggested, and it
works too. My pain in the butt actually need it to work as a daemon,
however, does not work.

What could cause code which works in interactive mode to fail in daemon
mode?

I’m checking the code now, and I do have “if debug” statements which
output to STDOUT. Could that be the problem?

only unless you need them to go somewhere - the code i sent has them
going to
/dev/null.

I’m adding some blurbs to try to get the stupid thing to tell me, but it
seems to die so silently (eg: nothing in syslog, which is where most
messages are expected to go), that I can’t get enough information.

I know it manages to send the “shut down the listener” message, which is
a “rescue” action.

well - then it must be throwing an error right? post you code and i’m
sure
someone will spot the error.

Hints, suggestions and other comments?

wrap your script with this

begin

 #
 # your entire script
 #

rescue Exception => e
require ‘tmpdir’

 tmp = Dir.tmpdir
 this = File.basename $0
 pid = Process.pid

 deathlog = File.join tmp, "#{ this }.#{ pid }"

 m, c, b = e.message, e.class, e.backtrace.join("\n")

 msg = "%s (%s)\n%s" % [m, c, b]

 open(deathlog, "w"){|f| f.puts msg}

end

after it runs do an

ls -ltar /tmp

there should should find a record of the failure.

regards.

-a

unknown wrote:

On Fri, 14 Jul 2006, Rogue Amateur wrote:

So, this works. So does another, innocuous script I wrote. I even
checked out the daemonize library the other person suggested, and it
works too. My pain in the butt actually need it to work as a daemon,
however, does not work.

What could cause code which works in interactive mode to fail in daemon
mode?

I’m checking the code now, and I do have “if debug” statements which
output to STDOUT. Could that be the problem?

only unless you need them to go somewhere - the code i sent has them
going to
/dev/null.

I’m adding some blurbs to try to get the stupid thing to tell me, but it
seems to die so silently (eg: nothing in syslog, which is where most
messages are expected to go), that I can’t get enough information.

I know it manages to send the “shut down the listener” message, which is
a “rescue” action.

well - then it must be throwing an error right? post you code and i’m
sure
someone will spot the error.

Hints, suggestions and other comments?

wrap your script with this

begin

 #
 # your entire script
 #

rescue Exception => e
require ‘tmpdir’

 tmp = Dir.tmpdir
 this = File.basename $0
 pid = Process.pid

 deathlog = File.join tmp, "#{ this }.#{ pid }"

 m, c, b = e.message, e.class, e.backtrace.join("\n")

 msg = "%s (%s)\n%s" % [m, c, b]

 open(deathlog, "w"){|f| f.puts msg}

end

after it runs do an

ls -ltar /tmp

there should should find a record of the failure.

regards.

-a

I’d love to post the code, but I can’t - it’s proprietary. If I can
rewrite it so that I cna post it, I will do so tonight. Basically, I’ll
have to rewrite it, and verify that the rewrite has the same problem.

truely odd things are happening:
daemon.rb my_code.rb > with your Exception recue clause returned
nothing. Executes Listener stop.
ruby daemon.rb my_code.rb > with the exception clause returns
nothing. However, it doesn’t execute the Listener stop either.
(using daemonize inside the code)
my_code.rb > with exception clause returns 2 files (1503 and 1502,
both of which say:
exit (SystemExit)
/usr/lib/site_ruby/1.8/daemonize.rb:26:in exit' /usr/lib/site_ruby/1.8/daemonize.rb:26:indaemonize’
./my_code.rb:621
**** repeat the run, and new files are not created.

I’m not seeing any errors logged to syslog, or my verbose log.

ONCE, I seemed to get it to work. I ran it as a plain run in a redirect
to /dev/null, and then killed the window. It killed that process, but
didn’t kill the listener. Then, I ran it with “ruby daemon.rb
my_code.rb” (no daemonize inside the code)., and it worked, and ran
through to completion. I have not been able to do that again!

will work on rewriting tonight, and posting. apologies for the
stupidity.

RA

Rogue Amateur wrote:

checked out the daemonize library the other person suggested, and it
works too. My pain in the butt actually need it to work as a daemon,
however, does not work.

What could cause code which works in interactive mode to fail in daemon
mode?

I’m checking the code now, and I do have “if debug” statements which
output to STDOUT. Could that be the problem?

I’m guessing that the daemon library calls setsid() behind the scenes,
in which
case there’s no controlling terminal for that process. I would think
sending
anything to STDOUT would just disappear into the ether, but I’m not
positive.

Regards,

Dan

This communication is the property of Qwest and may contain confidential
or
privileged information. Unauthorized use of this communication is
strictly
prohibited and may be unlawful. If you have received this communication
in error, please immediately notify the sender by reply e-mail and
destroy
all copies of the communication and any attachments.

On Fri, 14 Jul 2006, Rogue Amateur wrote:

my_code.rb > with exception clause returns 2 files (1503 and 1502,
to /dev/null, and then killed the window. It killed that process, but
didn’t kill the listener. Then, I ran it with “ruby daemon.rb
my_code.rb” (no daemonize inside the code)., and it worked, and ran
through to completion. I have not been able to do that again!

will work on rewriting tonight, and posting. apologies for the
stupidity.

RA

can all of your code run as in

./my_code.rb

??

eg. not

ruby ./my_code.rb

if not that may be your problem. when you do

daemon.rb ./my_code.rb

it’s literally going to do

system ‘./my_code.rb’

at some point. if your code requires

system ‘ruby ./my_code.rb’

the you’d need to do

daemon.rb ruby ./my_code.rb

regards.

-a

unknown wrote:

On Fri, 14 Jul 2006, Rogue Amateur wrote:

[snip]

can all of your code run as in

./my_code.rb

??

yes. running ./my_code.rb is how I normally start the software. when
run without daemonizing of any sort, this software runs properly and to
completion. there are 2 modes: interactive and service mode. I’m
trying to make the service mode run as a daemon.

Here is the code that I’m trying to process, severely cut down because
of proprietary issues.

I can’t get anything past the daemonize statement to run.

The same seems to be true with the daemon.rb code, in that it will
output only one set of envirnment data (vs the 2 that the print
statements are set up for…)

I understand that these things are 2 different actions. One does steps,
then deamonizes. the other daemonizes the whole bloody process. both
are behaving the same way, and seem to die at the same place (only 1 set
of environment output). If I could post the whole thing, I would. I
cannot.

#!/usr/bin/ruby

FileName: StrmSrvInteract.rb

Date: 2006/05/10

require(‘SimStream’)
require(‘daemonize’)
require(‘socket’)
require(‘syslog’)
include Daemonize

check if command line options. If so, use those and “run once”

else, create listener socket thread, process on loop until

listener socket send “stop”

$mode = nil # assume service mode
$mode = 1 if ARGV.length > 0
$rtr_ip = ‘127.0.0.1’
$rtr_usr = ‘george’
$OutPort = 6001 # output port for sockets
$InPort = 6000 # input port for sockets
$host = ‘localhost’
$debug = nil
$sim = nil

use syslog to properly add log messages to the log

$log = Syslog.open(“my_code”)
$log.debug(“Running my_code.”)

use alt_log to hold debug comments if any

alt_log = “#{Dir.pwd}/my_codeDebug.log”

simple_socket = "Usage: via localhost:16000, send message as " +
“\t[actionCode]\0[inputValue(s)…]\0\n” +
“Return messages to localhost:16001”
simple_usage = “Usage: #{$0} [d[:[+]sim]] [v|h|c:cmd] [s:MCA]
[p:port]\n” +
“\tUse #{$0} v to get version\nUse #{$0} h to get extended
help.”

FunctionName: process_request

proprietary code I can’t release. It never gets a chance to execute

this anyway.

FunctionName: main

begin

parse the arguments, if any, and see if in sim mode.

my_args = Hash.new
ARGV.each do |arg|
if arg =~ /:confused:
(key, value) = arg.split(":")
my_args[key] = value
else
my_args[arg] = “”
end
end

allow for ip as an input parameter no matter what the mode.

if my_args.keys.length == 1 and my_args.has_key?(“ip”)
File.open(alt_log,“a+”) { |f| f << “New IP for Router:
#{my_args[“ip”]}”} # if $debug
$rtr_ip = my_args[“ip”]
$mode = nil
elsif my_args.has_key?(“ip”)
File.open(alt_log,“a+”) { |f| f << “New IP for Router:
#{my_args[“ip”]}”} # if $debug
$rtr_ip = my_args[“ip”]
end

for debug to force in socket mode

$sim = 1
$debug = 1

log the environment, I hope…

show_env = “\n”
ENV.each_pair { |k,v| show_env += “\t#{k}: #{v}\n” }
environment = “Dir: #{Dir.pwd}\nEff UID: #{Process.euid}\n” +
“Eff GID: #{Process.egid}\nENV: #{show_env}\n”
File.open("#{Dir.pwd}/Environment.log",“a+”) { |f| f << environment }

otherwise, daemonize and move on.

daemonize()

#####IT NEVER DOES THIS STEP. AT ALL.

log the environment, I hope…

show_env = “\n”
ENV.each_pair { |k,v| show_env += “\t#{k}: #{v}\n” }
environment = “Dir: #{Dir.pwd}\nEff UID: #{Process.euid}\n” +
“Eff GID: #{Process.egid}\nENV: #{show_env}\n”
File.open("/home/RA/Environment.log",“a+”) { |f| f << environment }

File.open(alt_log,“a+”) { |f| f << “Debug mode on.” } if $debug
File.open(alt_log,“a+”) { |f| f << “Sim mode on.” } if $debug and $sim

File.open(alt_log,“a+”) { |f| f << “Socket processing.” } if $debug

puts "Debug mode on." if $debug

puts “Sim mode on.” if $debug and $sim

puts “Socket processing.” if $debug

open the router and get stream objects

puts “Opening router <#{Time.now}>…” if $debug
if $sim
$my_rtr = SimRouter.new($rtr_ip, $rtr_usr) # for form sake
$high_Def = SimStream.new($my_rtr.open, “highdef”)
$low_Def = SimStream.new($my_rtr.shell, “lowdef”)
else
begin
$my_rtr = Router.new($rtr_ip, $rtr_usr)
my_shell = $my_rtr.open
rescue
msg = “Could not open the real router. Error: <#{$!}>\n”
msg += “Pls verify router settings: RouterIP: #{$rtr_ip} User:
#{$rtr_usr}\n”
puts(msg)
$log.err(msg)
UDPSocket.open.send(“510\000Stop\0001\000”, 0, $host, $OutPort)
exit 1
end
begin
$high_Def = Stream.new(my_shell, “highdef”)
rescue
msg = “could not determine HighDef contents. Message returned was:
<#{$!}>\n”
msg += “Pls verify router settings: RouterIP: #{$rtr_ip} User:
#{$rtr_usr}\n”
$log.err(msg)
$my_rtr.close
UDPSocket.open.send(“510\000Stop\0001\000”, 0, $host, $OutPort)
exit 1
end
begin
$low_Def = Stream.new(my_shell, “lowdef”)
rescue
msg = “could not determine LowDef contents. Message returned was:
<#{$!}>\n”
msg += “Pls verify router settings: RouterIP: #{$rtr_ip} User:
#{$rtr_usr}\n”
$log.err(msg)
$my_rtr.close
UDPSocket.open.send(“510\000Stop\0001\000”, 0, $host, $OutPort)
exit 1
end
end
puts “done opening router <#{Time.now}>” if $debug

assume socket-based service

server = UDPSocket.open
server.bind(nil, $InPort)
stop_stat = 1
last_word = “”

do this just once, to let gvpmanager know you’re up.

UDPSocket.open.send(“520\0”, 0, $host, $OutPort) if !$mode

while stop_stat
my_reply, my_from = server.recvfrom(64)
if my_reply =~ /^510/
# stop if told to stop
stop_stat = nil
last_word = my_reply
puts “Told to stop <#{Time.now}>…” if $debug
elsif my_reply =~ /^505/

the rest of the acting code has been deleted to protect the guilty.

Please understand that it never does the above noted steps.

 # process_request
 # output to socket

end

do the last clean up pieces for the socket based mode.

File.open(alt_log,“a+”) { |f| f << last_word } if !$mode and $debug
UDPSocket.open.send("#{last_word}0\0", 0, $host, $OutPort) if !$mode
msg = “Socket Service stopped. Please see system log for any error
messages.”
puts msg if !$mode
rescue RuntimeError, StandardError => boom
msg = “Runtime processing error. Software threw an error <#{boom}>.”
$log.err(msg)
puts (msg) if $mode
if defined? $my_rtr and !$my_rtr.nil?
$my_rtr.close
end
UDPSocket.open.send(“510\000Stop\0001\000”, 0, $host, $OutPort) if
!$mode
exit 1
rescue Exception => e
require ‘tmpdir’

 tmp = Dir.tmpdir
 this = File.basename $0
 pid = Process.pid

 deathlog = File.join tmp, "#{ this }.#{ pid }"

 m, c, b = e.message, e.class, e.backtrace.join("\n")

 msg = "%s (%s)\n%s" % [m, c, b]

 open(deathlog, "w"){|f| f.puts msg}

end

Rogue Amateur wrote:

unknown wrote:

On Fri, 14 Jul 2006, Rogue Amateur wrote:

[snip]

can all of your code run as in

./my_code.rb

??

yes. running ./my_code.rb is how I normally start the software. when
run without daemonizing of any sort, this software runs properly and to
completion. there are 2 modes: interactive and service mode. I’m
trying to make the service mode run as a daemon.

[snip]

problem solved, or so it appears. thank you, ALL of you, for your most
excellent help. A few of the lines of code referenced a hard-coded path
name. This was readily resolved by, prior to daemonizing, trap the
correct path, then after the daemonizing, be sure to be in the correct
path for proper processing, by deliberately changing to the correct path
in the child process. This seems to work properly.

FYI, there is another, slightly different and I’m not sure correct
Daemon type code at BigBold - Informasi Tentang Bisnis dan Marketing, for
those interested parties. I couldn’t get it to work properly, but the
site says it does.

Again, Thank you All

RA