rc.d script to start a sh script

barbish

New Member


Messages: 6

#1
Here is the test setup. In /usr/local/sbin is this script called testloop.
Code:
>cat /usr/local/sbin/testloop

#!/bin/sh
   while true; do
     echo -e "Success:"
      sleep 10
   done
And here is the rc.d script
Code:
 >cat /usr/local/etc/rc.d/testloop 

#!/bin/sh 
 # 
 # 
 # PROVIDE: testloop 
 # REQUIRE: LOGIN DAEMON NETWORKING 
 # KEYWORD: nojail shutdown 
 # 
 # Add the following line to /etc/rc.conf to enable testloop: 
 # 
 # testloop_enable="YES" 
 # 

 . /etc/rc.subr 
 name="testloop" 
 rcvar=testloop_enable 
 pidfile="[I]/var/run/[/I]${name}.pid" 
 command="[I]/usr/local/sbin/[/I]${name}" 
 load_rc_config ${name} 
 run_rc_command "$1"
issuing the service testloop start command does start the testloop script with the "while" command in it. But it does not auto create the PID file and the service testloop stop command complains about there being no PID file.

This rc.d script is using the rc.d default daemon script method. There is another rc.d script method based on the use of the daemon command and I have that working.

I want to get this rc.d default daemon script method working.
What is missing from the rc.d script to get this working?
 
Last edited by a moderator:

ShelLuser

Son of Beastie

Thanks: 1,429
Messages: 3,144

#2
issuing the "service testloop start" command does start the testloop script with the "while" command in it. But it does not auto create the pid file and the "service testloop stop" command complains about there being no pid file.
Of course it doesn't, because that's not its job. The pid file is there to denote the actual PID of the running process you're controlling. And that process needs to be started by the script, and it is that process which will eventually create the pid file. If there's no process yet there's no PID and therefor nothing to be placed in the pid file.

I want to get this rc.d default daemon script method working.
What is missing from the rc.d script to get this working?
A pid file :p

Actually it's not so much about something missing, but more so about something unnecessary being in there. Why specify a pid file when you don't use one? If you don't use one then don't specify one, problem solved. sysutils/bsdstats might provide you with a good example here.
 
OP
OP
B

barbish

New Member


Messages: 6

#3
According to the rc.d framework the rc.d script uses that "pidfile="/var/run/${name}.pid"" path/name to auto allocate and populate it when the rc.d script is executed. I am at a loss to understand what point your trying to make with your post. I ran test with the pidfile= statement commented out and got the same results as before. The posted example is so very simple that you can run it and demonstrate what it takes to get it to work. One example is worth a thousand words.
 

ShelLuser

Son of Beastie

Thanks: 1,429
Messages: 3,144

#4
According to the rc.d framework the rc.d script uses that "pidfile="/var/run/${name}.pid"" path/name to auto allocate and populate it when the rc.d script is executed.
Not exactly. See also rc.subr(8). The term pidfile is used to (I quote): "determine the PID(s) of the running command".

But if your command is not a daemon and something which only runs once (which seems to be the case in your example considering the lack of any stop routines) then you don't need a PID file because there's nothing to keep track of. Also note the existence of check_pidfile and check_process which are deemed necessary the very moment you start using pidfiles to keep track of daemons.

I ran test with the pidfile= statement commented out and got the same results as before.
Then you didn't comment it out completely, or overlooked something else. See also rc(8) which provides some examples of very minimalistic rc scripts.

Code:
#!/bin/sh

# PROVIDE: testrc
# REQUIRE: LOGIN DAEMON NETWORKING
# KEYWORD: jail java test

# Enable this script by adding:
# testrc_enable="YES"
# ... to /etc/rc.conf

. /etc/rc.subr

name="testrc"
rcvar="testrc_enable"

command="/usr/local/bin/hello";

load_rc_config $name
run_rc_command "$1"
Here's something I cooked up myself. /usr/bin/hello is merely another shell script. Well, as I hinted at above, if you use this (so without mention of any pidfiles) then this is what happens:

Code:
# service testrc onestart
Starting testrc.
Hello world!
# service testrc onestop
testrc not running?
#
And I think this is exactly the behavior you were looking for. As such my suggestion: don't use any pidfile references if you're not going to use any daemons.

(edit)

Also:

If you do use daemon processes be sure to use check_pidfile and check_process. If the daemon doesn't generate its own pidfile then this will be up to your rc script.
 

Eric A. Borisch

Well-Known Member

Thanks: 212
Messages: 338

#5
Take a look at the daemon(8) utility to wrap your (persistent, ie doesn't just run and exit) shell script. It can also handle creating a pidfile, running the command as a specified user, and syslog redirection; it's essentially tailor made for taking an existing executable and controlling it from init.
 
OP
OP
B

barbish

New Member


Messages: 6

#6
I have read and re-read all those references you pointed to without getting a working solution. Also the posts here don't shed any new light on the problem. But I think I see the reason. No one has really taken a close look at the example script I posted. It greatly differences from your example. Your example is do something one time and fall through and exit. My example is a ENDLESS LOOP. After starting, it cycles through the logic every 10 seconds.
Code:
>cat /usr/local/sbin/testloop

#!/bin/sh
while true; do
echo "Success:"
sleep 10
done
In this example the echo command is just a position holder. In the real script many commands happen in place of the echo command.

As stated previously I do have a working rc.d version that uses the "daemon" command. That is not the point of my post here.

If you look at the rc.d script I posted here, It has every thing that your rc.d script has. When I issue "service testloop start" it does start. The ps ax commands shows its running. But it also shows the terminal the service command was issued from as running, have to use kill command to terminate them.

I have tried changing the base script "done" to "done &". This frees up the terminal the "service" command was issued from and but still complains about no pid file.

So here we are back at the starting point again. What is needed in the testloop rc.d script or in the base testloop script to enable the service testloop start/stop command to function with a auto pid file?
 
Last edited by a moderator:

ShelLuser

Son of Beastie

Thanks: 1,429
Messages: 3,144

#7
No one has really taken a close look at the example script I posted. It greatly differences from your example. Your example is do something one time and fall through and exit. My example is a ENDLESS LOOP.
That's what I was getting at when I mentioned a daemon process. There's no difference. Get the PID of the running process and then use that for your PID file using the tools mentioned above.

(edit)

And yes, if you need to start a process in the background you'd use &, get the PID from that and then use that.

(edit2)

I suppose I can elaborate a bit, but I do need to stress out that I also think you didn't fully read rc(8) and rc.subr(8).

Every rc script needs to be able to perform a minimum set of tasks, in specific start, stop, restart, status and a few more. If you use the run_rc_command() function then most of these will be automatically supported.

However, that only uses bare defaults, you'll have to add the extra functionality yourself. This is also why several scripts do not use run_rc_command at all but implement their own routines.

Alas, one quick way to solve your "stall" problem is to use command_args, for example:

Code:
command="/usr/local/bin/testloop";
command_args="&";
Of course you'd still need to check for the PID and ensure that this gets placed in the PID file, otherwise the script won't be able to monitor the process (so: check that it's actually running or has already stopped).
 

ShelLuser

Son of Beastie

Thanks: 1,429
Messages: 3,144

#8
Right. Took me a while because I don't really use this very often myself, but from what I can make from the documentation (and a few existing examples) I'd probably solve the puzzle this way:

Code:
#!/bin/sh

# PROVIDE: testrc
# REQUIRE: LOGIN DAEMON NETWORKING
# KEYWORD: jail java test

# Enable this script by adding:
# testrc_enable="YES"
# ... to /etc/rc.conf

. /etc/rc.subr

name="testrc"
rcvar="testrc_enable"
command="/usr/local/bin/testloop"
pidfile="/var/run/${name}.pid"

start_cmd="test_start"
stop_cmd="test_stop"
status_cmd="test_status"

test_start() {
        /usr/sbin/daemon -p ${pidfile} ${command}
}

test_status() {
        if [ -e ${pidfile} ]; then
                echo ${name} is running...
        else
                echo ${name} is not running.
        fi
}

test_stop() {
        if [ -e ${pidfile} ]; then
                kill `cat ${pidfile}`;
        else
                echo ${name} is not running?
        fi
}

load_rc_config $name
run_rc_command "$1"
 

Eric A. Borisch

Well-Known Member

Thanks: 212
Messages: 338

#9
This can be simplified.

Code:
#!/bin/sh

# PROVIDE: testrc
# REQUIRE: LOGIN DAEMON NETWORKING
# KEYWORD: jail java test

# Enable this script by adding:
# testrc_enable="YES"
# ... to /etc/rc.conf

. /etc/rc.subr

# Set the default
: ${testrc_enable:="NO"}

name="testrc"
rcvar="testrc_enable"

# This is the tool init launches
command="/usr/sbin/daemon"
pidfile="/var/run/${name}.pid"

# This is the tool daemon launches
task="/usr/local/bin/testloop"
procname="/bin/sh" 

command_args="-p ${pidfile} ${task}"

load_rc_config $name
run_rc_command "$1"
See the rc.subr(8) section on what all run_rc_command handles for you. (And requires in terms of variables.) To get status to work with daemon, you need to have procname set to ${task}'s interpreter such that check_pidfile works. (Since the pidfile points to the managed task, not the managing (daemon) ${command}.)

Also consider adding (to beginning of command_args) "-u some_other_user" to not run the task as root.
 
OP
OP
B

barbish

New Member


Messages: 6

#10
Let me summarize what I think is the true out come I have learned from this thread.

1. Any binary executable would use the standard rc.d script format which would automatically create and populate the pid file as shown below.

Code:
. /etc/rc.subr
name="binarypgm"
rcvar=binarypgm_enable
pidfile="/var/run/${name}.pid"
command="/usr/local/sbin/${name}"
load_rc_config ${name}
run_rc_command "$1"
2. Now a sh or bash or any other type of script. [IE: the key word here being "script"] all need an shell interpreter to start first. The rc.d framework is not designed to automatically handle this situation using the standard rc.d format shown above. The ONLY way to get any script to work is to add the "daemon" program into the standard rc.d format shown above. This is what that looks like.

Code:
. /etc/rc.subr
name="testloop"
rcvar=${name}_enable
pidfile="/var/run/${name}.pid"
command="/usr/sbin/daemon"
task="/usr/local/sbin/${name}"
command_args="-p ${pidfile} ${task}"
load_rc_config ${name}
run_rc_command "$1"
I have really tested this rc.d script and my sh testloop script does run as a daemon and all the service commands work.

No where in any of the different manuals is the handling difference between a binary executable and a script pointed out. This was the information I needed to understand the rc.d framework. You would THINK something this simple would have been documented as a major fundamental handling difference in the rc.d framework manuals. This a an example of the documentation writers being to close to the forest to see the trees. This fundamental difference was not even pointed out by those who replied.

Thanks to all who replied
 

ShelLuser

Son of Beastie

Thanks: 1,429
Messages: 3,144

#11
2. Now a sh or bash or any other type of script. [IE: the key word here being "script"] all need an shell interpreter to start first. The rc.d framework is not designed to automatically handle this situation using the standard rc.d format shown above.
That conclusion is incorrect.

This isn't about running a script but more so about running a command which doesn't automatically place itself in the background. Which is also why I suggested that you'd look into existing examples. If you look at the rc scripts for, say, Apache and Samba you'll come across pretty much the same structures as the one I used above.

In fact, I think it's safe to conclude that almost none of the third party rc scripts (so: those located in /usr/local/etc/rc.d) follow the "standard" format. There really isn't a set out standard here, the only standard there is is /etc/rc.subr. But what the scripts do with its definitions is pretty much a free for all.

But to prove my point... mail/spamsassasin:

Code:
name=spamd
rcvar=${name}_enable

extra_commands="reload"
load_rc_config $name

start_precmd="precmd"
restart_precmd="precmd"

# Set defaults
: ${spamd_enable:="NO"}
: ${spamd_flags="-c -Q -u spamd -H /var/spool/spamd"}

pidfile=${spamd_pidfile:-"/var/run/${name}/${name}.pid"}
command=/usr/local/bin/${name}
command_args="-d -r ${pidfile}"
#command_interpreter="/usr/local/bin/perl"
required_dirs="/usr/local/share/spamassassin"

precmd()
{
        if [ ! -d /var/run/${name} ]; then
                mkdir -p /var/run/${name}
                chown spamd:spamd /var/run/${name}
        fi
}

run_rc_command "$1"
This is its rc script, and guess what /usr/local/bin/spamd is?

Code:
omicron:/home/peter $ file /usr/local/bin/spamd
/usr/local/bin/spamd: Perl script text executable
 
OP
OP
B

barbish

New Member


Messages: 6

#12
First of all spamd is not a good example to use to make any point with. I only have a very limited exposure to perl and the
/usr/local/bin/spamd script is very very complicated.

About the posted spamd rc.d script, the "precmd" routine is only there because spamd wants to use a sub-directory to hold all its many pid files. And the command_args="-d -r ${pidfile}" statement is showing the path to the directory location of the pid files is being passed directly to the spamd perl script at execution.

Now this would seem to indicate that the executed spamd perl script is going to
  1. somehow determine it has been executed,
  2. figure out what its task number is, and
  3. populate the pid file with that value.
I tried to follow the logic in the /usr/local/bin/spamd script to find where this took place with no luck and even if I had been able to it would not help me in my sh script.

Now if the point your trying to make with this spamd post is the fact the the executed script is responsible to handle the management of it's own pid file, then I got the message.

So now the question is how do I code my looptest executable sh script to perform those functions. I am thinking that at the start of the my testloop script I do a ps ax | grep testloopand if I find a task number it must be running and then echo that task number to my pid file everything will then be in-place that the service command needs.

Am I headed down the correct road with this now?
 

Eric A. Borisch

Well-Known Member

Thanks: 212
Messages: 338

#13
I still think a simple rc.d script (like the one I posted above) with daemon(8) is the right way to go for this task (demonizing / managing an existing script as a service).

That said, sh provides '$$' as a variable filled with its own PID. So echo $$ > /var/run/testloop.pid would suffice to create a pid file.

Note that you should also remove the pid file when you are done, and when you want to use it to kill a task (with a service foo stop), you will want to check that the running process with that PID is actually the command you expected it to be before killing it. These steps are all handled by the minimal rc.d script I provided via run_rc_command.
 
OP
OP
B

barbish

New Member


Messages: 6

#14
There are 2 different ideas being purposed in this thread.
1. Using a rc.d script that employs the use of daemon(8) as the way to demonizing and managing any existing script as a service.
This is the solution I am using.

2. The second idea that the same thing can be accomplished without using daemon(8) in the rc.d script. We have gone around and around with posts that state information that just does not work when tested on a real system. I feel like we are beating a dead dog and nothing is being achieved. So enough of this non-tested information. If your going to post a reply, it has to contain a working example based on the testloop rc.d script and base script pair posted at the start of this thread. Show me the solution, not more non-tested verbiage.

I still believe that rc.d solutions are separated into 2 groups
1. Any binary executable would use the standard rc.d script format which would automatically create and populate the pid file.
2. A sh or bash or any other type of script. [IE: the key word here being "script"] all need an shell interpreter to start first. The rc.d framework is not designed to automatically handle this situation using the standard rc.d format. The ONLY way to get any script to work is to add the "daemon" program into the standard rc.d format.

I am just waiting for someone to prove this wrong by posting the solution code based on the testloop example previous posted.
 

ShelLuser

Son of Beastie

Thanks: 1,429
Messages: 3,144

#15
Just so we're clear: I have tested the stuff I mentioned above and quite frankly I'm not quite convinced that you did the same.

There is absolutely no difference at all between executing a binary or a shell script from an rc script. The main difference here is that your example shell script didn't go into the background on its own but went into an endless loop. As such you need to compensate for that. But this is absolutely no different from a binary which goes into an endless loop. Something you could have determined yourself by looking into existing examples, as I suggested above. Instead you claimed that the example I mentioned wasn't comparable, which makes me seriously wonder if you actually looked into the matter at all.

Because using daemon is also often used when executing binaries, especially when they.... as mentioned: don't go into the background on their own.

I think you're searching for things which simply aren't there. If you want to make this work then well.. the solutions to that have been given above.
 

Eric A. Borisch

Well-Known Member

Thanks: 212
Messages: 338

#16
I still believe that rc.d solutions are separated into 2 groups
1. Any binary executable would use the standard rc.d script format which would automatically create and populate the pid file.
2. A sh or bash or any other type of script. [IE: the key word here being "script"] all need an shell interpreter to start first. The rc.d framework is not designed to automatically handle this situation using the standard rc.d format. The ONLY way to get any script to work is to add the "daemon" program into the standard rc.d format.
There's some confusion here. The only thing special about the shell script in this context is that the called command name (/usr/local/bin/my_script.sh) and the executing binary process name (/bin/sh) are different from each other, while for a binary (/usr/sbin/sshd for example) they will typically be the same.

This necessitates -- if you're using run_rc_command -- some additional information such that run_rc_command knows what executing binary name to check for when deciding if the process running with the recorded PID is a "match" that can then be checked/killed as appropriate. (PIDs are recycled, so even this isn't a foolproof check...)

That said, scripts and binaries can both certainly create pid-files, ( echo $$ > /var/run/myscript.pid) and also be run via appropriate rc.d scripts without daemon(8). The daemon utility -- which is not explicitly part of the rc.d framework -- provides a convenient method to wrap a script or a binary that doesn't natively generate pid-files. (It can also do output redirection, process monitoring (restart if it exits), effective user changes, etc.) As I've shown above, it can be used to create a very small rc.d script that typically "does what you want." Again, daemon is a convenience wrapper that you can choose to use if you don't want to (or can't if you don't have the source) reimplement those features in your script or binary.

Hope that clears things up.
 
Top