November 19th, 2009 · Linux, OS X
This is the promised launchd script to run the service in an earlier post.
The name of this file is /Library/LaunchDaemons/com.{domain_name}.{service_name}.plist
Replace any text in curly braces {} with some appropriate information for your script.
When you are finished creating the file, load it with:
launchctl load -w /Library/LaunchDaemons/com.{domain_name}.{service_name}.plist
It should crank right up and start doing its’ thang.
Here’s the content of the .plist file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Enabled</key>
<true/>
<key>GroupName</key>
<string>staff</string>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.{domain_name}.{service_name}</string>
<key>Program</key>
<string>/usr/local/bin/{script_name}</string>
<key>ProgramArguments</key>
<array>
<string>{script_name}</string>
<string>start</string>
<string>--nofork</string>
</array>
<key>QueueDirectories</key>
<array/>
<key>RunAtLoad</key>
<true/>
<key>ServiceDescription</key>
<string>Some creative description of your service.</string>
<key>UserName</key>
<string>{user_name}</string>
<key>WatchPaths</key>
<array/>
</dict>
</plist> |
Tags:
November 9th, 2009 · Linux, OS X
This is a daemon script written for OS/X in bash. It can be run either as a daemon or a launchd service. The launchd service file will follow in a later post.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
| #!/bin/bash
# put these lines in the file header comment
# FILE: |FILENAME|
# USAGE: ./|FILENAME| start [ stop | restart ] [ --nofork ] [ --debug ]
#-----------------------------------------------------------------------
#Uncomment the following line to echo all commands to the terminal
#-----------------------------------------------------------------------
#set -x
# better yet, set the --debug command line flag and syslog it.
#-----------------------------------------------------------------------
# Check number of command line arguments
#-----------------------------------------------------------------------
[[ $# -lt 1 ]] || [[ $# -gt 3 ]] && {
echo -e "\tUsage: ${0##/*/} start [ | stop | restart ] [ --nofork ] [ --debug ]"
exit 1
}
#A launchd script in OSX is not supposed to fork
nofork=`echo "$*" | grep "\-\-nofork"`
[[ ! -z $nofork ]] && nofork=true
[[ -z $nofork ]] && nofork=false
#check to send everything to syslog
debug=`echo "$*" | grep "\-\-debug"`
[[ ! -z $debug ]] && debug=true
[[ -z $debug ]] && debug=false
#configuration
basename="`basename $0 .sh`"
# look for the conf file in the same folder as the script
conf="`dirname $(which $0)`/${basename}.conf" #<-- which handles "bash -x $0 start"
[[ -f "$conf" ]] && source "$conf"
whoiam=`whoami`
# create a pid file with the pid of the process on the first line
pidfile="/tmp/${basename}.pid"
[[ $whoiam == 'root' ]] && pidfile="/var/run/${basename}.pid"
#find the temp directory
tempfile=`mktemp -t j`
#put all of your incidentals in the tempdir
tempdir=`dirname $tempfile`
rm $tempfile
#Do all further processing from the root
cd /
# Load the daemon template for OSX
. /etc/rc.common
#traps trap 'source $conf' 1
# response to kill -s HUP $$ should be to read the config file.
#trap 'echo trap 0; rm -f "$pidfile"; exit' 0 #can't trap this, daemon will exit when parent dies.
trap 'echo trap 3; rm -f "$pidfile"; exit' 3 15 #clean up the pidfile
[[ "$debug" == "true" ]] && trap 'logger -t $0 -i -- $USER : $BASH_COMMAND' DEBUG #syslog everything if we're debugging
trap 'logger -t $0 -i -- $USER : $BASH_COMMAND' ERR #log errors regardless
#=== FUNCTION ================================================================
# NAME: main
# DESCRIPTION: Allows daemon to be run in background
# PARAMETERS: none
# RETURNS: void
#===============================================================================
function main() {
# Put your service code here
while : # Loop forever
do
sleep 10
echo $RANDOM
done
}
#=== FUNCTION ================================================================
# NAME: StartService
# DESCRIPTION: implements function from /etc/rc.common
# PARAMETERS: none
# RETURNS: void
#===============================================================================
StartService () {
CheckForNetwork
[[ -f $pidfile ]] && {
pid=`head -n 1 $pidfile`
procname=`ps awx | awk "\\\$1 == $pid { print \\\$7 }"`
[[ ! -z $procname ]] && [[ "$procname" == "$0" ]] && {
echo "$basename is already running"
exit 1
}
rm $pidfile
}
# do the service
# no fork method for launchd service
[[ "$nofork" == "true" ]] && {
#pid file with this process id
echo $$ > $pidfile
main
exit
}
# fork and exit
main &
# create a pid file with the pid of the child
# the parent will go away and leave the child owned by init
echo $! > $pidfile
}
#=== FUNCTION ================================================================
# NAME: StopService
# DESCRIPTION: implements the function from /etc/rc.common
# PARAMETERS: none
# RETURNS: void
#===============================================================================
StopService ()
{
[[ -f $pidfile ]] && {
kill `head -n 1 $pidfile`
rm $pidfile
}
#touch "$quit"
}
#=== FUNCTION ================================================================
# NAME: RestartService
# DESCRIPTION: implements the function from /etc/rc.common
# PARAMETERS: none
# RETURNS: void
#===============================================================================
RestartService ()
{
StopService
StartService
}
#-------------------------------------------------------------------------------
# Call the function declared in /etc/rc.common to daemonize process
#-------------------------------------------------------------------------------
RunService "$1" #First parameter is start/stop/restart |
Tags: os/x mac bash daemon script
October 30th, 2009 · Linux, OS X
Problem: You want your script to execute without overlapping another execution.
Solution: Make a flag file so you know that your script is already executing.
Extended problem: What if your script dies and leaves the flag file behind?
Extended solution: Check the process list to see if the old instance is still alive.
Ext. Ext. Problem: What if somebody kills my script?
Ext. Ext. Solution: Trap the abnormal exit and clean up properly.
Ext. Ext. Ext. Problem: What if someone else is invoking a script with exactly the same name as mine?
Ext. Ext. Ext. Solution: You’re screwed. Give your script a really long descriptive name so this won’t happen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #figure out where to put the pid file
myfile=`basename "$0" .sh`
whoiam=`whoami`
pidfile=/tmp/$myfile.pid
[[ "$whoiam" == "root" ]] && pidfile=/var/run/$myfile.pid
#remove the pid file cleanly on exit
function cleanup () {
[[ -f "$pidfile" ]] && rm "$pidfile"
#add other post processing cleanup here
}
# trap all the exit signals for cleanup
trap "cleanup; exit" 0 SIGINT SIGQUIT SIGTERM # ON OS/X this is 0 2 3 15
# Am I already running?
[[ -f "$pidfile" ]] && {
#pidfile exists, check if previous process exists.
pid=`head -n 1 $pidfile`
pidtest="\$1 == $pid { print \$7 }"
procname=`ps awx | grep $0 | awk "$pidtest"`
[[ "$procname" == "$0" ]] && {
echo "Running"
exit 1
}
}
#put this pid in the file
echo $$ > "$pidfile" |
Tags: bash shell script pid