switch desktops after 5 minutes idle (xprintidle): crontab or daemon? - linux

on my raspberry pi (raspbian running) I would like to have the current desktop switched to desktop n#0 after 5 minutes of idle system (no mouse or keyboard action), through wmctrl -s 0 and xprintidle for idle time checking.
Please keep in mind I'm no expert...
I tried 2 different ways, none of them working and I was wondering which one of them is the best way to do have the job done:
bash script and crontab
I wrote a simple script which checks if xprintidle is greater than a previously set $IDLE_TIME, than it switches desktops (saved in /usr/local/bin/switchDesktop0OnIdle):
#!/bin/bash
# 5 minutes in ms
IDLE_TIME=$((5*60*1000))
# Sequence to execute when timeout triggers.
trigger_cmd() {
wmctrl -s 0
}
sleep_time=$IDLE_TIME
triggered=false
while sleep $(((sleep_time+999)/1000)); do
idle=$(xprintidle)
if [ $idle -ge $IDLE_TIME ]; then
if ! $triggered; then
trigger_cmd
triggered=true
sleep_time=$IDLE_TIME
fi
else
triggered=false
# Give 100 ms buffer to avoid frantic loops shortly before triggers.
sleep_time=$((IDLE_TIME-idle+100))
fi
done
script itself works.
Then I added it to crontab (crontab -e) for have it run every 6 minutes
*/6 * * * * * sudo /usr/local/bin/switchDesktop0OnIdle
not sure sudo is necessary or not.
Anyway It doesn't work: googling around I understood that crontab runs in its own environment with its own variables. Even though I don't remember how to access this environment (oops) I do remember that I get these 2 errors running the script in it (which correctly works in "normal" shell)
could not open display (is it important ?)
bla bla -ge error, unary operator expected or similar: basically xprintidle doesn't work in this environment a gives back an empty value
What am I missing ?
infinite-while bash script running as daemon
second method I tried to set up a script with an internal infinite-while checking if xprintidle is greater then 5 minutes. In this case desktop is switched (less elegant?). Saved also in /usr/local/bin/switchDesktop0OnIdle
#!/bin/bash
triggered=false
while :
do
if [ `xprintidle` -ge 300000 ]; then
if [ triggered == false ]
wmctrl -s 0
triggered = true
fi
else
triggered = false
fi
fi
done
again the script itself works.
I tried to create a daemon in /etc/init.d/switchDesktop0OnIdle (really not an expert here, modified an existing one)
#! /bin/sh
# /etc/init.d/switchDesktop0OnIdle
### BEGIN INIT INFO
# Provides: switchDesktop0OnIdle
# Required-Start: $all
# Required-Stop: $all
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description:
# Description:
### END INIT INFO
DAEMON=/usr/local/bin/switchDesktop0OnIdle
NAME=switchDesktop0OnIdle
test -x $DAEMON || exit 0
case "$1" in
start)
echo -n "Starting daemon: "
start-stop-daemon --start --exec $DAEMON
echo "switchDesktop0OnIdle."
;;
stop)
echo -n "Shutting down daemon:"
start-stop-daemon --stop --oknodo --retry 30 --exec $DAEMON
echo "switchDesktop0OnIdle."
;;
restart)
echo -n "Restarting daemon: "
start-stop-daemon --stop --oknodo --retry 30 --exec $DAEMON
start-stop-daemon --start --exec $DAEMON
echo "switchDesktop0OnIdle."
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
I set it up
sudo update-rc.d switchDesktop0OnIdle defaults
and
sudo service switchDesktop0OnIdle start
(necessary?)
...and nothing happens...
also I don't find the process with ps -ef | grep switchDesktop0OnIdle but it seems running with sudo service switchDesktop0OnIdle status
can anyone please help?
thank you
Giuseppe

As you suspected, the issue is that when you run your scripts from init or from cron, they are not running within the GUI environment you want them to control. In principle, a Linux system can have multiple X environments running. When you are using one, there are environment variables that direct the executables you are using to the environment you are in.
There are two parts to the solution: your scripts have to know which environment they are acting on, and they have to have authorization to interact with that environment.
You almost certainly are using a DISPLAY value of ":0", so export DISPLAY=:0 at the beginning of your script will handle the first part of the problem. (It might be ":0.0", which is effectively equivalent).
Authorization is a bit more complex. X can be set up to do authorization in different ways, but the most common is to have a file .Xauthority in your home directory which contains a token that is checked by the X server. If you install a script in your own crontab, it will run under your own user id (you probabl shouldn't use sudo), so it will read the right .Xauthority file. If you run from the root crontab, or from an init script, it will run as the root user, so it will have access to everything but will still need to know where to take the token from. I think that adding export XAUTHORITY=/home/joe/.Xauthority to the script will work. (Assuming your user id is joe.)

Related

My application speed is low when run at linux startup

I have an embedded console application created with Qt,C++. I use nanopi fire (FriendlyARM) for my device with Linux arch. I connect it's gpio as relay for turning on and off the lamps of my room. Also I wrote mobile app to connect the device with socket. When I run my program from putty ./SmartKeyC it runs and I can switch the lamps with my mobile app buttons and all of them are ok.
But when I put my program on start up for auto run, all of functions do their tasks with 2-3 seconds delay.
I used this link to create auto run app. this is my script:
#! /bin/sh
### BEGIN INIT INFO
# Provides: <your script name>
# Required-Start: $all
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Manage my cool stuff
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin:/root
. /lib/init/vars.sh
. /lib/lsb/init-functions
. /root/
# If you need to source some other scripts, do it here
case "$1" in
start)
log_begin_msg "Starting my super cool service"
# do something
/root/SmartKeyC
log_end_msg $?
exit 0
;;
stop)
log_begin_msg "Stopping the coolest service ever unfortunately"
# do something to kill the service or cleanup or nothing
log_end_msg $?
exit 0
;;
*)
echo "Usage: /etc/init.d/<your script> {start|stop}"
exit 1
;;
esac
and after update rc I have:
root#NanoPi2-Fire:/etc# find -iname "*SmartKeyScript"
./init.d/SmartKeyScript
./rc0.d/K01SmartKeyScript
./rc1.d/K01SmartKeyScript
./rc2.d/S03SmartKeyScript
./rc3.d/S03SmartKeyScript
./rc4.d/S03SmartKeyScript
./rc5.d/S03SmartKeyScript
./rc6.d/K01SmartKeyScript
Where is the problem?
Why when I run my app in putty , everything works good , but when my app starts from auto run it has delay for each function calling?
I remove my script from rcX and put file in my rc.local like below:
Vi /etc/rc.local
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
/usr/local/bin/gen-friendlyelec-release
. /etc/friendlyelec-release
if [ ! -f /etc/firstuse ]; then
/bin/echo ${BOARD} > /etc/hostname
/bin/sed -i "s/\(127.0.1.1\s*\).*/\1${BOARD}/g" /etc/hosts
/bin/hostname ${BOARD}
/bin/echo "0" > /etc/firstuse
fi
. /usr/bin/setqt5env
/usr/bin/lcd2usb_print "CPU: {{CPU}}" "Mem: {{MEM}}" "IP: {{IP}}" "LoadAvg: {{LOADAVG}}" 2>&1 > /dev/null&
#/opt/QtE-Demo/run.sh&
/root/SmartKeyC & > /dev/tty0
exit 0

How to run my looping Bash script as a service?

I have 2 Amazon Linux EC2 instances that are running HAProxy. I want to monitor each instance from the other instance and if a instance becomes unavailable, the other instance will issue a API command to move the elastic IP to the active server.
I created a Bash script to do the monitoring every XX seconds. I need to set the script to run as a service so I created a service wrapper and placed in /etc/init.d based on a template that I found and registered as a service.
The problem is when I issue command #service hamonitor start, it says "Starting hamonitor...", but I never see the OK message and if I issue the stop command, it fails and if I issue the status command, it says it is not running. But, if I check the logs, it shows that the script is in fact running. I assume that I need a proper PID file and/or since the script runs in a infinite loop, it never completes so the OK does not get issued.
Service Wrapper:
#!/bin/sh
#
# /etc/init.d/hamonitor
# Subsystem file for "hamonitor" server
#
# chkconfig: 2345 95 05 (1)
# description: hamonitor server daemon
#
# processname: hamonitor
### BEGIN INIT INFO
# Provides:
# Required-Start:
# Required-Stop:
# Should-Start:
# Should-Stop:
# Default-Start:
# Default-Stop:
# Short-Description:
# Description:
### END INIT INFO
# source function library
. /etc/rc.d/init.d/functions
PROG=hamonitor
EXEC=/etc/haproxy/hamonitor
LOCKFILE=/var/lock/subsys/$prog
PIDFILE=/var/run/$prog.pid
RETVAL=0
start() {
echo -n $"Starting $PROG:"
echo
#daemon $EXEC &
/etc/haproxy/hamonitor &
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
touch LOCKFILE
touch PIDFILE
echo "[ OK ]"
else
echo "[ FAIL: ${retval} ]"
fi
return $RETVAL
}
stop() {
echo -n $"Stopping $PROG:"
echo
killproc $PROG -TERM
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
rm -f LOCKFILE
rm -f PIDFILE
echo "[ OK ]"
else
echo "[ FAIL: ${RETVAL} ]"
fi
return $RETVAL
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $PROG
RETVAL=$?
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
RETVAL=1
esac
exit $RETVAL
App:
#!/usr/bin/env bash
export EC2_HOME=/opt/aws/apitools/ec2
export JAVA_HOME=/usr/lib/jvm/jre
AWS_ACCESS_KEY="XXXXXXXXXXXXXXXXXXXXXXXXX"
AWS_SECRET_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
VIP1="1.2.3.4"
VIP1_ALLOCATIONID="eipalloc-XXXXXXX"
THIS_NODE_EC2_ID="i-XXXXXXX"
THIS_NODE_PRIVATE_IPADDRESS1="10.60.0.11"
THIS_NODE_HEALTHCHECK_URL="http://10.60.0.10/haproxy?monitor"
OTHER_NODE_HEALTHCHECK_URL="http://10.60.49.50/haproxy?monitor"
CHECK_OTHER_INTERVAL=5
CHECK_OTHER_FAIL_COUNT=0
CHECK_OTHER_RUN_COUNT=0
AFTER_TAKEOVER_WAIT=30
function takeover_vips {
/opt/aws/bin/ec2-associate-address -aws-access-key ${AWS_ACCESS_KEY} -aws-secret-key ${AWS_SECRET_KEY} -a ${VIP1_ALLOCATIONID} -i ${THIS_NODE_EC2_ID} -private-ip-address ${THIS_NODE_PRIVATE_IPADDRESS1} -allow-reassociation > /dev/null
}
function does_this_node_have_ips {
is_active=$(/opt/aws/bin/ec2-describe-addresses -aws-access-key ${AWS_ACCESS_KEY} -aws-secret-key ${AWS_SECRET_KEY} | grep ${VIP1} | grep ${THIS_NODE_EC2_ID})
if [ "$is_active" = "" ]; then
echo "no"
else
echo "yes"
fi
}
function log_msg {
msg=$1
msg="$(date) -- ${msg}"
echo ${msg} >> /var/log/hamonitorlog
}
while [ . ]; do
healthcheck_response=$(curl -sL -w "%{http_code}" ${OTHER_NODE_HEALTHCHECK_URL} -o /dev/null)
if [ "$healthcheck_response" != "200" ]; then
CHECK_OTHER_FAIL_COUNT=$((CHECK_OTHER_FAIL_COUNT+1))
if [ "$CHECK_OTHER_FAIL_COUNT" -gt 2 ]; then
takeover_vips
CHECK_OTHER_FAIL_COUNT=0
sleep ${AFTER_TAKEOVER_WAIT}
fi
sleep ${CHECK_OTHER_INTERVAL}
done
Some Linux distribution have up-start and other init; I assume you have init. The chkconfig is being used to maintain symlinks. You should confirm the comment,
# chkconfig: 2345 95 05 (1)
is correct for your system.
As a guess, you need daemon to be invoked via a script. This may have been a script function in some init script library, like /etc/rc.d/init.d/functions. I would suggest that you use the daemon() function if it exists. Either,
daemon $EXEC & #option1
nohup /etc/haproxy/hamonitor < /dev/null > /dev/null 2>&1 & #option2
/etc/haproxy/hamonitor& #option3, 2 lines.
disown $! #...
This is related to SIGCHLD and process return status (see man wait for more). As well, you may need to detach hamonitor from the controlling terminal. You can use logger to send information to the system logs in this case; I guess the App script is the hamonitor code? Just change echo to logger.
If the hamonitor needs stdout, stdin, and/or stderr, you may need to redirect to some other file if it requires it. You might also consider running it via screen if this is the case.
Edit: The last option can be used to create a proper PIDFILE. For instance,
# !!! optional grabbing of lock here...
/etc/haproxy/hamonitor & # spawn in bg
HA_PID=$! # record spawn pid
echo $HA_PID > $PIDFILE # record the PID to a file for `stop`.
# !!! optional release of lock here...
disown $HA_PID # detach script from terminal.
Services should never use echo and the like; logger is the better option. This is probably not your issue unless hamonitor tries to read from something. Mainly the issue is that start() will wait for the hamonitor to finish if you don't disown, so the rc script's start will never finish.
Generically, you can look at /etc/rc.d/init.d/functions, provide a link to your file, or provide your distribution and version (or at least linux standard base conformance which seems to define how this should work in its different versions). The file can be different on each and every Linux. You can look at this file yourself if you understand scripting to see what environment variables, files, etc are expected and what functions you use in this file. For instance, killproc is most likely defined there.

How to get this init.d script to start at server restart?

I'm following the directions on installing Redis on a production machine (CentOS using chkconfig).
The example script I was given requires the argument start to actually start it, which it seems init.d does not do (pass arguments).
The real command that must be run is /etc/init.d/redis_6379 start, but what its actually calling is /etc/inti.d/redis_6379, which simply says use start or stop as an argument
Therefor, when my server reboots it doesnt actually start redis. What should I do here?
Here is the initial config
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
#
# chkconfig: - 85 15
# description: Redis is a persistent key-value database
# processname: redis_6379
REDISPORT=6379
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli
PIDFILE=/var/run/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
Make sure your script is added for service management by chkconfig. Use chkconfig --list to see the list and use chkconfig --add scriptname if it's not there. After that configure the runlevels you want it to be called into. I would guess it's 3, 4 and 5 so: chkconfig --level 345 scriptname on.
You should tell us how exactly you are running the script from init.d
But here is a dirty workaround:
Change the line
start)
to
start|'')
This will make it start if there are no parameters passed.
If you want to start a service through command line you can just add in /etc/rc.d/rc.local too for the same instead of creating a service file in init.d.
Centos redis has an init script with a chkconfig header line stating that it will start in all runlevels, which is very bad. chkconfig is used to manage the symlinks in /etc/rc.d
# chkconfig: - 85 15
I suggest that redis is a service to run in level 3 after critical services have launched (sshd for instance). In your test scenario's reboot your server before going to production. If redis cannot launch (just happened here) you cannot boot it in another runlevel to fix it.
If you implement the proper headers you can use init and also systemd (Fedora)
Your should add code below to the script /etc/inti.d/redis_6379. The status argument is used by command service --status-all.
# processname: redis_6379
# Source function library.
. /etc/init.d/functions
...
case "$1" in
status)
status -p $PIDFILE redis
script_result=$?
;;
Init.d's days are numbered, wtf are you still reading this for? There's no more sudo service, all the new kids are slapping down syscrtl
Nowadays like of course on my ubuntu 17.04 server at work, /etc/rc.local didn't even exist
Just write a new one!
rc.local is awesome, especially combined with the unix style daemonize program... those two alone, I can pretty much call it a day.
However, if you want to take rc.local to the next level, I'll cover basic ideas behind my own personal redis init.d script--same one we use on production servers across my company:
pre-empt redis complaint about system socket/file limits
slap in some linux perf and mess around with sysconf in persistent fashion
autopilot redis while i go take a nap
#!/bin/sh
### BEGIN INIT INFO
# Provides: redis
# Required-Start: $syslog
# Required-Stop: $syslog
# Should-Start: $all
# Should-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# X-Interactive: true
# Short-Description: start and stop redis
# Description: persistent key-value db
### END INIT INFO
NAME=redis
PATH=/opt/bin:/opt/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
EXEC=/opt/sbin/redis-server
CLIEXEC=/opt/sbin/redis-cli
CONF=/etc/$NAME/$NAME.conf
PIDFILE=/var/run/$NAME.pid
SOCKET=/var/run/$NAME.sock
PERF=/tmp/redis.sysctl
KERNELPG=/sys/kernel/mm/transparent_hugepage/enabled
[ -x /opt/sbin/redis-server ] || exit 0
set -e
# tune system for better redis performance
if [ ! -f $PERF ]; then
echo "tunning redis..." &>> $PERF
echo never > $KERNELPG && cat $KERNELPG &>> $PERF
sysctl -w net.core.somaxconn=65535 &>> $PERF
sysctl -w vm.overcommit_memory=1 &>> $PERF
echo "tuned." &>> $PERF && cat $PERF
fi
next, if we're doin' it right:
let's have nice idiomatic case $money numbers, focused on starting and stopping without sorting through excessive PID tracking shenanigans
take advantage of the start-stop-daemon (i.e. can't get cut short by parent process death if there is no parent process)
case $1 in
start)
if [ ! -f $PIDFILE ]; then
echo -n "Starting $NAME: "
start-stop-daemon --start --pidfile $PIDFILE --exec $EXEC -- $CONF
echo "waiting for redis db to start..."
while [ ! -f $PIDFILE ]; do
sleep 0.1;
done
fi
PID=$(cat $PIDFILE)
echo "running with pid: $PID"
;;
stop)
if [ ! -f $PIDFILE ]; then
echo "redis is already stopped"
else
PID=$(cat $PIDFILE)
echo -n "Stopping $NAME: "
$CLIEXEC -s $SOCKET shutdown
echo "waiting for shutdown..."
while [ -x /proc/${PID} ]; do
sleep 0.1
done
echo "db stopped."
fi
;;
status)
if [ -f $PIDFILE ]; then
PID=$(cat $PIDFILE)
echo "running with pid: $PID"
else
echo "stopped."
fi
;;
restart|force-reload)
$0 stop && $0 start
;;
*)
echo "Argument \"$1\" not implemented."
exit 2
;;
esac
exit 0
edit redis.conf to designate daemonize yes. Make redis the primary responsible party for administrative PID file state (in case you were wondering why we didn't have to do anything with it in the script, except read from it if it's around)
mkdir /etc/redis
echo 'daemonize yes' >> /etc/redis/redis.conf
echo 'pidfile /var/run/redis.pid' >> /etc/redis/redis.conf
update your rc entry by name after copying and setting execution bits:
mkdir /etc/redis
vim /etc/redis/redis # keep it traditional, no .sh extensions here
# saving buffers from root all damn day...
chmod a+x /etc/init.d/redis
update-rc.d redis defaults
Here's the full example link w/ service installer. Again, be sure to edit conf and install to suit you. Most people would probably want to remove the listening file path in favor of TCP stack w/ redis port number open for client(s),

Adding a service startup script for Amazon linux AMI

I am using an Amazon Linux AMI and doing some custom modifications(added an axis2server, etc) on it and saving it as a new AMI. Now what I want to do is when the AMI boots up, start up axis2server(ie.axis2server should automatically start when the instance boots up). For that I used a init script like below and ran the following command:
chkconfig --add axisservice
But when I launch a new instance from my image, the axis2server is not getting started.
I just only need to execute the script /home/ec2-user/axis2-1.6.1/bin/axis2server.sh at startup. Am I missing anything here?
#! /bin/sh
# Basic support for IRIX style chkconfig
###
# chkconfig: 235 98 55
# description: Manages the services you are controlling with the chkconfig command
###
case "$1" in
start)
echo -n "Starting axisservice"
touch ~/temp.txt
cd /home/ec2-user/axis2-1.6.1/bin
./axis2server.sh &
echo "."
;;
stop)
echo -n "Stopping axisservice"
echo "."
;;
*)
echo "Usage: /sbin/service axisservice {start|stop}"
exit 1
esac
exit 0
I went through https://help.ubuntu.com/community/CloudInit as well and it provides a mechanism called User-Data Scripts, where a user can execute a script when launching the script.
$ euca-run-instances --key mykey --user-data-file myscript.sh ami-axxxx
This is a command line option and what I want is something like when I launch the instance through the UI, the script should be started.Therefore, I think the above option can not be used in my case. Please correct me if I am wrong.
Thanks,
H.
I bet the environment is not set(up correctly). This means that I am guessing that your shell script tries to start another program and it's not to be found.
So at first, I'd adjust the start part of your script (current):
echo -n "Starting axisservice"
touch ~/temp.txt
cd /home/ec2-user/axis2-1.6.1/bin
./axis2server.sh &
echo "."
Edited:
echo -n "Starting axisservice"
touch ~/temp.txt
cd /home/ec2-user/axis2-1.6.1/bin
./axis2server.sh
RETVAL=$?
[ $RETVAL -eq 0 ] && echo Success
[ $RETVAL -ne 0 ] && echo Failure
echo "."
So what did I do?
removed & so script waits for your shell script (axis2server.sh) to complete
checked the return status ($?) of your shell script
Further debugging:
Add set -x to your scripts to enable tracing and log both stderr and stdout.
Questions:
Are you are aware that stop (in your service script) doesn't do anything?
touch ~/temp.txt is that supposed to create /root/temp.txt? (I'm guessing root runs this script.)
If none of my suggestions work, can you share axis2server.sh and paste stderr and stdout?

Test a weekly cron job [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 4 years ago.
Improve this question
I have a #!/bin/bash file in cron.week directory.
Is there a way to test if it works? Can't wait 1 week
I am on Debian 6 with root
Just do what cron does, run the following as root:
run-parts -v /etc/cron.weekly
... or the next one if you receive the "Not a directory: -v" error:
run-parts /etc/cron.weekly -v
Option -v prints the script names before they are run.
A wee bit beyond the scope of your question... but here's what I do.
The "how do I test a cron job?" question is closely connected to "how do I test scripts that run in non-interactive contexts launched by other programs?" In cron, the trigger is some time condition, but lots of other *nix facilities launch scripts or script fragments in non-interactive ways, and often the conditions in which those scripts run contain something unexpected and cause breakage until the bugs are sorted out. (See also: https://stackoverflow.com/a/17805088/237059 )
A general approach to this problem is helpful to have.
One of my favorite techniques is to use a script I wrote called 'crontest'. It launches the target command inside a GNU screen session from within cron, so that you can attach with a separate terminal to see what's going on, interact with the script, even use a debugger.
To set this up, you would use "all stars" in your crontab entry, and specify crontest as the first command on the command line, e.g.:
* * * * * crontest /command/to/be/tested --param1 --param2
So now cron will run your command every minute, but crontest will ensure that only one instance runs at a time. If the command takes time to run, you can do a "screen -x" to attach and watch it run. If the command is a script, you can put a "read" command at the top to make it stop and wait for the screen attachment to complete (hit enter after attaching)
If your command is a bash script, you can do this instead:
* * * * * crontest --bashdb /command/to/be/tested --param1 --param2
Now, if you attach with "screen -x", you'll be facing an interactive bashdb session, and you can step through the code, examine variables, etc.
#!/bin/bash
# crontest
# See https://github.com/Stabledog/crontest for canonical source.
# Test wrapper for cron tasks. The suggested use is:
#
# 1. When adding your cron job, use all 5 stars to make it run every minute
# 2. Wrap the command in crontest
#
#
# Example:
#
# $ crontab -e
# * * * * * /usr/local/bin/crontest $HOME/bin/my-new-script --myparams
#
# Now, cron will run your job every minute, but crontest will only allow one
# instance to run at a time.
#
# crontest always wraps the command in "screen -d -m" if possible, so you can
# use "screen -x" to attach and interact with the job.
#
# If --bashdb is used, the command line will be passed to bashdb. Thus you
# can attach with "screen -x" and debug the remaining command in context.
#
# NOTES:
# - crontest can be used in other contexts, it doesn't have to be a cron job.
# Any place where commands are invoked without an interactive terminal and
# may need to be debugged.
#
# - crontest writes its own stuff to /tmp/crontest.log
#
# - If GNU screen isn't available, neither is --bashdb
#
crontestLog=/tmp/crontest.log
lockfile=$(if [[ -d /var/lock ]]; then echo /var/lock/crontest.lock; else echo /tmp/crontest.lock; fi )
useBashdb=false
useScreen=$( if which screen &>/dev/null; then echo true; else echo false; fi )
innerArgs="$#"
screenBin=$(which screen 2>/dev/null)
function errExit {
echo "[-err-] $#" | tee -a $crontestLog >&2
}
function log {
echo "[-stat-] $#" >> $crontestLog
}
function parseArgs {
while [[ ! -z $1 ]]; do
case $1 in
--bashdb)
if ! $useScreen; then
errExit "--bashdb invalid in crontest because GNU screen not installed"
fi
if ! which bashdb &>/dev/null; then
errExit "--bashdb invalid in crontest: no bashdb on the PATH"
fi
useBashdb=true
;;
--)
shift
innerArgs="$#"
return 0
;;
*)
innerArgs="$#"
return 0
;;
esac
shift
done
}
if [[ -z $sourceMe ]]; then
# Lock the lockfile (no, we do not wish to follow the standard
# advice of wrapping this in a subshell!)
exec 9>$lockfile
flock -n 9 || exit 1
# Zap any old log data:
[[ -f $crontestLog ]] && rm -f $crontestLog
parseArgs "$#"
log "crontest starting at $(date)"
log "Raw command line: $#"
log "Inner args: $#"
log "screenBin: $screenBin"
log "useBashdb: $( if $useBashdb; then echo YES; else echo no; fi )"
log "useScreen: $( if $useScreen; then echo YES; else echo no; fi )"
# Were building a command line.
cmdline=""
# If screen is available, put the task inside a pseudo-terminal
# owned by screen. That allows the developer to do a "screen -x" to
# interact with the running command:
if $useScreen; then
cmdline="$screenBin -D -m "
fi
# If bashdb is installed and --bashdb is specified on the command line,
# pass the command to bashdb. This allows the developer to do a "screen -x" to
# interactively debug a bash shell script:
if $useBashdb; then
cmdline="$cmdline $(which bashdb) "
fi
# Finally, append the target command and params:
cmdline="$cmdline $innerArgs"
log "cmdline: $cmdline"
# And run the whole schlock:
$cmdline
res=$?
log "Command result: $res"
echo "[-result-] $(if [[ $res -eq 0 ]]; then echo ok; else echo fail; fi)" >> $crontestLog
# Release the lock:
9<&-
fi
After messing about with some stuff in cron which wasn't instantly compatible I found that the following approach was nice for debugging:
crontab -e
* * * * * /path/to/prog var1 var2 &>>/tmp/cron_debug_log.log
This will run the task once a minute and you can simply look in the /tmp/cron_debug_log.log file to figure out what is going on.
It is not exactly the "fire job" you might be looking for, but this helped me a lot when debugging a script that didn't work in cron at first.
I'd use a lock file and then set the cron job to run every minute. (use crontab -e and * * * * * /path/to/job) That way you can just keep editing the files and each minute they'll be tested out. Additionally, you can stop the cronjob by just touching the lock file.
#!/bin/sh
if [ -e /tmp/cronlock ]
then
echo "cronjob locked"
exit 1
fi
touch /tmp/cronlock
<...do your regular cron here ....>
rm -f /tmp/cronlock
What about putting it into cron.hourly, waiting until the next run of hourly cron jobs, then removing it? That would run it once within an hour, and in the cron environment. You can also run ./your_script, but that won't have the same environment as under cron.
Aside from that you can also use:
http://pypi.python.org/pypi/cronwrap
to wrap up your cron to send you an email upon success or failure.
None of these answers fit my specific situation, which was that I wanted to run one specific cron job, just once, and run it immediately.
I'm on a Ubuntu server, and I use cPanel to setup my cron jobs.
I simply wrote down my current settings, and then edited them to be one minute from now. When I fixed another bug, I just edited it again to one minute from now. And when I was all done, I just reset the settings back to how they were before.
Example: It's 4:34pm right now, so I put 35 16 * * *, for it to run at 16:35.
It worked like a charm, and the most I ever had to wait was a little less than one minute.
I thought this was a better option than some of the other answers because I didn't want to run all of my weekly crons, and I didn't want the job to run every minute. It takes me a few minutes to fix whatever the issues were before I'm ready to test it again. Hopefully this helps someone.
The solution I am using is as follows:
Edit crontab(use command :crontab -e) to run the job as frequently
as needed (every 1 minute or 5 minutes)
Modify the shell script which should be executed using cron to prints the output into some file (e.g: echo "Working fine" >>
output.txt)
Check the output.txt file using the command : tail -f output.txt, which will print the latest additions into this file, and thus you can track the execution of the script
I normally test by running the job i created like this:
It is easier to use two terminals to do this.
run job:
#./jobname.sh
go to:
#/var/log and run
run the following:
#tailf /var/log/cron
This allows me to see the cron logs update in real time. You can also review the log after you run it, I prefer watching in real time.
Here is an example of a simple cron job. Running a yum update...
#!/bin/bash
YUM=/usr/bin/yum
$YUM -y -R 120 -d 0 -e 0 update yum
$YUM -y -R 10 -e 0 -d 0 update
Here is the breakdown:
First command will update yum itself and next will apply system updates.
-R 120 : Sets the maximum amount of time yum will wait before performing a command
-e 0 : Sets the error level to 0 (range 0 - 10). 0 means print only critical errors about which you must be told.
-d 0 : Sets the debugging level to 0 - turns up or down the amount of things that are printed. (range: 0 - 10).
-y : Assume yes; assume that the answer to any question which would be asked is yes
After I built the cron job I ran the below command to make my job executable.
#chmod +x /etc/cron.daily/jobname.sh
Hope this helps,
Dorlack
sudo run-parts --test /var/spool/cron/crontabs/
files in that crontabs/ directory needs to be executable by owner - octal 700
source: man cron and NNRooth's
I'm using Webmin because its a productivity gem for someone who finds command line administration a bit daunting and impenetrable.
There is a "Save and Run Now" button in the "System > Scheduled Cron Jobs > Edit Cron Job" web interface.
It displays the output of the command and is exactly what I needed.

Resources