I'd like to schedule a command to run after reboot on a Linux box. I know how to do this so the command consistently runs after every reboot with a @reboot crontab entry, however I only want the command to run once. After it runs, it should be removed from the queue of commands to run. I'm essentially looking for a Linux equivalent to RunOnce in the Windows world.
In case it matters:
$ uname -a
Linux devbox 2.6.27.19-5-default #1 SMP 2009-02-28 04:40:21 +0100 x86_64 x86_64 x86_64 GNU/Linux
$ bash --version
GNU bash, version 3.2.48(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat /etc/SuSE-release
SUSE Linux Enterprise Server 11 (x86_64)
VERSION = 11
PATCHLEVEL = 0
Is there an easy, scriptable way to do this?
-
Create an
@rebootentry in your crontab to run a script called/usr/local/bin/runonce.Create a directory structure called
/etc/local/runonce.d/ranusingmkdir -p.Create the script
/usr/local/bin/runonceas follows:#!/bin/sh for file in /etc/local/runonce.d/* do if [[ ! -f "$file" ]] then continue fi "$file" mv "$file" "/usr/local/runonce.d/ran/$file.$(date +%Y%m%dT%H%M%S)" logger -t runonce -p local3.info "$file" doneNow place any script you want run at the next reboot (once only) in the directory
/etc/local/runonce.dandchownandchmod +xit appropriately. Once it's been run, you'll find it moved to theransubdirectory and the date and time appended to its name. There will also be an entry in yoursyslog.pra : for file in /etc/local/runonce.d/*, no?Dennis Williamson : @pra: Thanks for spotting that.Christopher Parker : Thanks for your answer. This solution is great. It technically solves my problem, however it seems like there's a lot of preparation of infrastructure required to make this work. It's not portable. I think your solution would ideally be baked into a Linux distribution (I'm not sure why it isn't!). Your answer inspired my ultimate solution, which I've also posted as an answer. Thanks again!Christopher Parker : What made you choose local3, versus any of the other facilities between 0 and 7?Dennis Williamson : @Christopher: A dice roll is always the best method. Seriously, though, for an example it didn't matter and that's the key my finger landed on. Besides, I don't own any eight-sided die.Christopher Parker : @Dennis: Got it, thanks. Coincidentally, local3 is the local facility that appears in `man logger`.From Dennis Williamson -
RunOnce is an artifact of Windows resulting from problems completing configuration before a reboot. Is there any reason you can't run your script before reboot? The above solution appears to be a reasonable clone of RunOnce.
From BillThor -
in redhat and debian systems you can do that from /etc/rc.local, it's a kind of autoexec.bat.
Dennis Williamson : That's going to get executed at *each* boot not just the next one only.natxo asenjo : my bad, I misread the question. Thanks for the correctionFrom natxo asenjo -
I really appreciate the effort put into Dennis Williamson's answer. I wanted to accept it as the answer to this question, as it is elegant and simple, however:
- I ultimately felt that it required too many steps to set up.
- It requires root access.
I think his solution would be great as an out-of-the-box feature of a Linux distribution.
That being said, I wrote my own script to accomplish more or less the same thing as Dennis's solution. It doesn't require any extra setup steps and it doesn't require root access.
#!/bin/bash if [[ $# -eq 0 ]]; then echo "Schedules a command to be run after the next reboot." echo "Usage: $(basename $0) <command>" echo " $(basename $0) -p <path> <command>" echo " $(basename $0) -r <command>" else REMOVE=0 COMMAND=${!#} SCRIPTPATH=$PATH while getopts ":r:p:" optionName; do case "$optionName" in r) REMOVE=1; COMMAND=$OPTARG;; p) SCRIPTPATH=$OPTARG;; esac done SCRIPT="${HOME}/.$(basename $0)_$(echo $COMMAND | sed 's/[^a-zA-Z0-9_]/_/g')" if [[ ! -f $SCRIPT ]]; then echo "PATH=$SCRIPTPATH" >> $SCRIPT echo "cd $(pwd)" >> $SCRIPT echo "logger -t $(basename $0) -p local3.info \"COMMAND=$COMMAND ; USER=\$(whoami) ($(logname)) ; PWD=$(pwd) ; PATH=\$PATH\"" >> $SCRIPT echo "$COMMAND | logger -t $(basename $0) -p local3.info" >> $SCRIPT echo "$0 -r \"$(echo $COMMAND | sed 's/\"/\\\"/g')\"" >> $SCRIPT chmod +x $SCRIPT fi CRONTAB="${HOME}/.$(basename $0)_temp_crontab_$RANDOM" ENTRY="@reboot $SCRIPT" echo "$(crontab -l 2>/dev/null)" | grep -v "$ENTRY" | grep -v "^# DO NOT EDIT THIS FILE - edit the master and reinstall.$" | grep -v "^# ([^ ]* installed on [^)]*)$" | grep -v "^# (Cron version [^$]*\$[^$]*\$)$" > $CRONTAB if [[ $REMOVE -eq 0 ]]; then echo "$ENTRY" >> $CRONTAB fi crontab $CRONTAB rm $CRONTAB if [[ $REMOVE -ne 0 ]]; then rm $SCRIPT fi fiSave this script (e.g.:
runonce),chmod +x, and run:$ runonce foo $ runonce "echo \"I'm up. I swear I'll never email you again.\" | mail -s \"Server's Up\" $(whoami)"In the event of a typo, you can remove a command from the runonce queue with the -r flag:
$ runonce fop $ runonce -r fop $ runonce fooUsing sudo works the way you'd expect it to work. Useful for starting a server just once after the next reboot.
myuser@myhost:/home/myuser$ sudo runonce foo myuser@myhost:/home/myuser$ sudo crontab -l # DO NOT EDIT THIS FILE - edit the master and reinstall. # (/root/.runonce_temp_crontab_10478 installed on Wed Jun 9 16:56:00 2010) # (Cron version V5.0 -- $Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp $) @reboot /root/.runonce_foo myuser@myhost:/home/myuser$ sudo cat /root/.runonce_foo PATH=/usr/sbin:/bin:/usr/bin:/sbin cd /home/myuser foo /home/myuser/bin/runonce -r "foo"Some notes:
- This script replicates the environment (PATH, working directory, user) it was invoked in.
- It's designed to basically defer execution of a command as it would be executed "right here, right now" until after the next boot sequence.
Dennis Williamson : Your script looks really handy. One thing to note is that it destructively strips comments out of the crontab.Christopher Parker : @Dennis: Thanks. I originally didn't have that extra grep call in there, but all of the comments were piling up; three for every time I ran the script. I think I'll change the script to just always remove comment lines that look like those three auto-generated comments.Christopher Parker : @Dennis: Done. The patterns could probably be better, but it works for me.Christopher Parker : @Dennis: Actually, based on crontab.c, I think my patterns are just fine. (Search for "DO NOT EDIT THIS FILE" at http://www.opensource.apple.com/source/cron/cron-35/crontab/crontab.c.)From Christopher Parker -
Set up the script in /etc/rc5.d with an S99 and have it delete itself after running.
From mpez0
0 comments:
Post a Comment