I am trying to create a file in rc.d/ that will start up a /bin/sh script that I have written. I am following some examples found here:
http://www.freebsd.org/doc/en/articles/rc-scripting/article.html#rc-flags
#!/bin/sh -x
# PROVIDE: copyfiles
. /etc/rc.subr
name=copyfiles
rcvar=copyfiles_enable
pidfile="/var/run/${name}.pid"
command="/var/etc/copy_dat_files.sh -f /var/etc/copydatafiles.conf"
command_args="&"
load_rc_config $name
run_rc_command "$1"
It seems like I am having a problem with the pidfile. Does my script need to be the one that creates the pid file, or does it automatically get created? I have tried both ways, and whether or not i make my script create a pid file, I get an error that the pid file is not readable.
If my script is supposed to make it, what is the proper way to make the pid file?
Thanks
Look at the existing daemons for example (such as /etc/rc.d/mountd). Then look at the subroutines in /etc/rc.subr -- there is code in there to check the PID-file, but nothing creates it.
In other words, you can declare in the daemon-starting script, what the PID-file is, but creating it is up to the daemon. Speaking of the daemons, you may wish to use the daemon(8) utility, if your daemon is, in fact, a shell script. The utility will take care of the PID-file creation for you. (If the daemon is written in C, you can/should use daemon(3) function.)
BTW, in my own opinion, daemons, when opening up the PID-files for creation, should also lock them (with flock(3) or fcntl(2) or lockf(3)). This way, if an instance crashes (or is killed) without removing the PID-file, the next instance will have no problem determining, the file is stale.
In general, a daemon is supposed to create and clean up its own PID file.
From a shell-script you can give the following command to create it;
echo $$ >/var/run/${name}.pid
Do not forget to remove the file before exiting the script. Write a cleanup() function that does that and let trap call that function when certain signals occur. Also call cleanup just before exiting the script.
Related
Suppose I have a bash script that I want at most one instance of at a given time. Suppose the intended behavior for multiple calls to the same bash script is to queue their executions.
Within a single program where the script is analogous to a function, this can be achieved with mutex locks.
How would I approach such a design?
Ok, just off the cuff...
Implement a queueing system inside the script.
Have the script attempt to mkdir a known and standard directory name.
if mkdir /tmp/${0##*/}.Q
then : .. proceed normally
mkdir is atomic, so only one instance can ever succeed.
If it fails, test to see if it's because the directory already exists.
If it does not, there's a problem, report and abort.
If it succeeds, open a FIFO in that directory named with the PID of the script, and write its command line arguments there. Maybe add the PID.
Then clear the args, fork a child, and wait. On SIGCHLD go read the FIFO again. As long as you find stuff, lather/rinse/repeat. When there's no more, shut down.
If runs with args but the dir exists then confirm a copy is already running on another PID, find the FIFO in the dir, write args (& maybe PID) to that, and exit.
If it has no args (likely because it's a forked child), read the pipe, set the args, and process them. Confirm that there is a copy running with the PID that matches the FIFO. Delete the FIFO and exit normally when all records are processed.
Haven't tested this at all. Expect to find some flaws.
OR just look for the "flagfile" and sleep a bit in a loop until it does not exist, then create it, run normally, and remove it again when done. That doesn't assure order, but it does keep more than one copy from running.
I've been trying to generate and infinite loop in OpenWRT, and I've succeeded:
#!/bin/sh /etc/rc.common
while [ true ]
do
# Code to run
sleep 15
done
This code works as a charm if I execute it as ./script. However, I want this to start on its own when I turn on my router. I've placed the script in /etc/init.dand enabled it with chmod +x script.
Regardless, the program doesn't start running at all. My guess is that I shouldn't execute this script on boot up but have a script that calls this other script. I haven't been able to work this out.
Any help would be appreciated.
As I have messed with init scripts of OpenWRT in my previous projects. I would like contribute to Rich Alloway's answer (for the ones who will likely to drop here from google search). His answer only covers for "traditional SysV style init scripts" as it is mentioned in the page that he gave link Init Scripts.
There is new process management daemon, Procd that you might find in your OpenWRT version. Sadly documentation of it has not been completed yet; Procd Init Scripts.
There are minor differences like they have pointed out in their documentation :
procd expects services to run in the foreground,
Different shebang,
line: #!/bin/sh /etc/rc.common Explicitly use procd USE_PROCD=1
start_service() instead of start()
A simple init script for procd would look like :
#!/bin/sh /etc/rc.common
# it is run order of your script, make it high to not mess up with other init scripts
START=100
USE_PROCD=1
start_service() {
procd_open_instance
procd_set_param command /target/to/your/useless/command -some -useless -shit -here
}
I have posted some blog post about it while ago that might help.
You need to have a file in /etc/rc.d/ with an Sxx prefix in order for the system to execute the script at boot time. This is usually accomplished by having the script in /etc/init.d and a symlink in /etc/rc.d pointing to the script.
The S indicates that the script should run at startup while the xx dictates the order that the script will run. Scripts are executed in naturally increasing order: S10boot runs before S40network and S50cron runs before S50dropbear.
Keep in mind that the system may not continue to boot with the script that you have shown here!
/etc/init.d/rcS calls each script sequentially and waits for the current one to exit before calling the next script. Since your script is an infinite loop, it will never exit and rcS may not complete the boot process.
Including /etc/rc.common will be more useful if you use functions in your script like start(), stop(), restart(), etc and add START and STOP variables which describe when the script should be executed during boot/shutdown.
Your script can then be used to enable and disable itself at boot time by creating or removing the symlink: /etc/init.d/myscript enable
See also OpenWRT Boot Process and Init Scripts
-Rich Alloway (RogueWave)
So, I need to run a program, not keep track of its PID (in memory, at least), and later kill that program. Any ideas? My immediate thought was to tag the process with something I could find later, but that seems a bust. My next thought was to store on disk the PID, but I've no idea what the convention for that sort of thing is. Any ideas? Thanks!
Your program can create a directory under /var/run/ to store such files. For instance, if your program is myprog it might store its PID on startup in
/var/run/myprog/PID
If your program could have multiple instances running at the same time, you might use the PID itself in the file name, along with its startup time, to ensure a unique file name for each instance.
/var/run/myprog/201410302306.1283.pid
(Note that if you use the PID in the file name, it's up to you if you actually write the PID in the file itself; an empty file would suffice.)
The most straight forward (and common case) is, as you and chepner already mentioned, to store it in a pid file. For this:
If it's a system wide programm:
/var/run/prog.pid
If it's a system installed program that might be run multiple times:
/var/run/prog/prog_instance.pid
If it's a user local program:
$HOME/.prog/prog.pid
Other options might be to use pgrep for finding it as long as you can uniquely define the calling command. For example pass a dummy argument not used and use it for retrieval:
$ prog.sg --instance_1234 &
$ pgrep -f -- '--instance_1234'
3523
you can use start-stop-daemon to start a program and kill it later. It has a lot of options to find the programm, the most usefull is the --pid that stores and retrive the pid from a file in filesystem, usually in /var/run (pay attention to file system privileges to write in it).
See the man pages for more tips.
I ask because I recently made a change to a KornShell (ksh) script that was executing. A short while after I saved my changes, the executing process failed. Judging from the error message, it looked as though the running process had seen some -- but not all -- of my changes. This strongly suggests that when a shell script is invoked, the entire script is not read into memory.
If this conclusion is correct, it suggests that one should avoid making changes to scripts that are running.
$ uname -a
SunOS blahblah 5.9 Generic_122300-61 sun4u sparc SUNW,Sun-Fire-15000
No. Shell scripts are read either line-by-line, or command-by-command followed by ;s, with the exception of blocks such as if ... fi blocks which are interpreted as a chunk:
A shell script is a text file containing shell commands. When such a
file is used as the first non-option argument when invoking Bash, and
neither the -c nor -s option is supplied (see Invoking Bash), Bash
reads and executes commands from the file, then exits. This mode of
operation creates a non-interactive shell.
You can demonstrate that the shell waits for the fi of an if block to execute commands by typing them manually on the command line.
http://www.gnu.org/software/bash/manual/bashref.html#Executing-Commands
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Scripts
It's funny that most OS'es I know, do NOT read the entire content of any script in memory, and run it from disk. Doing otherwise would allow making changes to the script, while running. I don't understand why that is done, given the fact :
scripts are usually very small (and don't take many memory anyway)
at some point, and shown in this thread, people would start making changes to a script that is already running anyway
But, acknowledging this, here's something to think about: If you decided that a script is not running OK (because you are writing/changing/debugging), do you care on the rest of the running of that script ? you can go ahead making the changes, save them, and ignore all output and actions, done by the current run.
But .. Sometimes, and that depends on the script in question, a subsequent run of the same script (modified or not), can become a problem since the current/previous run is doing an abnormal run. It would typically skip some stuff, or sudenly jump to parts in the script, it shouldn't. And THAT may be a problem. It may leave "things" in a bad state; particularly if file manipulation/creation is involved.
So, as a general rule : even if the OS supports the feature or not, it's best to let the current run finish, and THEN save the updated script. You can change it already, but don't save it.
It's not like in the old days of DOS, where you actually have only one screen in front of you (one DOS screen), so you can't say you need to wait on run completion, before you can open a file again.
No they are not and there are many good reasons for that.
One of the things you should keep in mind is that a shell is not an interpreter even if there are some similarities. Shells are designed to work with a stream of commands. Either from the TTY ,a PIPE, FIFO or even a socket.
The shell reads from its resource line by line until a EOF is returned by the kernel.
The most shells have no extra support for interpreting files. they work with a file as they would work with a terminal.
In fact this is considered to be a nice feature because you can do interesting stuff like this How do Linux binary installers (.bin, .sh) work?
You can use a binary file and prepend shell scripts. You can't do this with an interpreter. because it parses the whole file or at least it would try it and fail. A shell would just interpret it line by line and doesnt care about the garbage at the end of the file. You just have to make sure the execution of the script gets terminated before it reaches the binary part.
UPDATE: this is a repost of How to make shell scripts robust to source being changed as they run
This is a little thing that bothers me every now and then:
I write a shell script (bash) for a quick and dirty job
I run the script, and it runs for quite a while
While it's running, I edit a few lines in the script, configuring it for a different job
But the first process is still reading the same script file and gets all screwed up.
Apparently, the script is interpreted by loading each line from the file as it is needed. Is there some way that I can have the script indicate to the shell that the entire script file should be read into memory all at once? For example, Perl scripts seem to do this: editing the code file does not affect a process that's currently interpreting it (because it's initially parsed/compiled?).
I understand that there are many ways I could get around this problem. For example, I could try something like:
cat script.sh | sh
or
sh -c "`cat script.sh`"
... although those might not work correctly if the script file is large and there are limits on the size of stream buffers and command-line arguments. I could also write an auxiliary wrapper that copies a script file to a locked temporary file and then executes it, but that doesn't seem very portable.
So I was hoping for the simplest solution that would involve modifications only to the script, not the way in which it is invoked. Can I just add a line or two at the start of the script? I don't know if such a solution exists, but I'm guessing it might make use of the $0 variable...
The best answer I've found is a very slight variation on the solutions offered to How to make shell scripts robust to source being changed as they run. Thanks to camh for noting the repost!
#!/bin/sh
{
# Your stuff goes here
exit
}
This ensures that all of your code is parsed initially; note that the 'exit' is critical to ensuring that the file isn't accessed later to see if there are additional lines to interpret. Also, as noted on the previous post, this isn't a guarantee that other scripts called by your script will be safe.
Thanks everyone for the help!
Use an editor that doesn't modify the existing file, and instead creates a new file then replaces the old file. For example, using :set writebackup backupcopy=no in Vim.
How about a solution to how you edit it.
If the script is running, before editing it, do this:
mv script script-old
cp script-old script
rm script-old
Since the shell keep's the file open as long as you don't change the contents of the open inode everything will work okay.
The above works because mv will preserve the old inode while cp will create a new one. Since a file's contents will not actually be removed if it is opened, you can remove it right away and it will be cleaned up once the shell closes the file.
According to the bash documentation if instead of
#!/bin/bash
body of script
you try
#!/bin/bash
script=$(cat <<'SETVAR'
body of script
SETVAR)
eval "$script"
then I think you will be in business.
Consider creating a new bang path for your quick-and-dirty jobs. If you start your scripts with:
#!/usr/local/fastbash
or something, then you can write a fastbash wrapper that uses one of the methods you mentioned. For portability, one can just create a symlink from fastbash to bash, or have a comment in the script saying one can replace fastbash with bash.
If you use Emacs, try M-x customize-variable break-hardlink-on-save. Setting this variable will tell Emacs to write to a temp file and then rename the temp file over the original instead of editing the original file directly. This should allow the running instance to keep its unmodified version while you save the new version.
Presumably, other semi-intelligent editors would have similar options.
A self contained way to make a script resistant to this problem is to have the script copy and re-execute itself like this:
#!/bin/bash
if [[ $0 != /tmp/copy-* ]] ; then
rm -f /tmp/copy-$$
cp $0 /tmp/copy-$$
exec /tmp/copy-$$ "$#"
echo "error copying and execing script"
exit 1
fi
rm $0
# rest of script...
(This will not work if the original script begins with the characters /tmp/copy-)
(This is inspired by R Samuel Klatchko's answer)