Create own startup script for MacOSX Sierra
A small description how to create your own Bash script which got’s loaded after login.
I want to automate the start of a Virtual Machine in VirtualBox after I login and additionally to mount a NFS share which got’s exported within my Virtual Machine. Also the opposite when I shutdown/restart my Laptop.
I use my Virtual Machine for several reasons but mainly to develop Software as I don’t like to install all necessary tools and dependencies on my Laptop OS. So, to run all make commands I connect to my VM via SSH and use the NFS Export to manage and edit all files and repos on my Laptop OS.
Since MacOSX Sierra use launchd for starting/stopping daemons, applications, processes etc. we need to create a launchd Property List File. More information about creating Launchd jobs in MacOSX can be taken from the the Apple Developer Documentation here(link).
launchd Property List File
The Property List file contains all information about our script we want to run at startup/shutdown in XML Syntax. As taken from the documentation, following property list keys are required:
Key | Description |
---|---|
Label | Contains a unique string to identify your daemon to launchd. |
ProgramArguments | The arguments used to launch your daemon. |
For a complete list of all available keys have a look into the man page man launchd.plist
.
My launchd Property List File com.user.virtualbox.startvm.plist
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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>Label</key>
<string>com.user.virtualbox.startvm</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/start_vm.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>LaunchOnlyOnce</keyu>
<true/>
<key>StandardOutPath</key>
<string>/Users/mbloch/log/startvm_stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/mbloch/log/startvm_errout.log</string>
</dict>
</plist>
Now that Property List file exist we need to consider when we want to run the script.
When | From where (Path) | Type |
---|---|---|
Sytem Startup/ Shutdown | /System/Library/LaunchDaemons | System-wide daemons provided by Mac OS X. |
/Library/LaunchDaemons | System-wide daemons provided by the administrator. | |
Login / Logout | /System/Library/LaunchAgents | Per-user agents provided by Mac OS X. |
/Library/LaunchAgents | Per-user agents provided by the administrator. | |
~/Library/LaunchAgents | Per-user agents provided by the user | |
Since I want to start my script after login and before logout I placed my Property List file under ~/Library/LaunchAgents/com.user.virtualbox.startvm.plist
.
Now that we have our Property List file we need to add it to launchd. To avoid errors while adding it to launchd like. If you place it under the User folders ~/Library/LaunchAgents
you have to make sure the permissions are set to the user. For the other folders make sure you use root:wheel
.
Run
The -w
option will enable our service directly.
Startup/Shutdown script
Now that we told our OS about our new Startup/Shutdown script we need to create it.
Basically you can use a simple Hello world script as a startup script. But if you want to make sure it runs also during shutdown/logout you need to run this script continuously, till shutdown/logout.
I realised my script to start/stop the VMs and to mount/umount the NFS share with 6 functions:
- Start VM
- Stop VM
- Mount NFS
- UMOUNT NFS
- Handler
- Run in Background
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
#! /bin/bash
#
# Startup script for MacOSX Sierra to start and stop a Virtual Machine
# everytime this scripts gots executed or receives a SIGTERM SIGKILL SIGINT SIGHUP Signal
#
# Variables
VM_ID=d9782aed-e997-42b1-96f8-1cdb1ee3d55c
Mount_PATH=/Users/mbloch/projects
NFS_IP=192.168.98.101
NFS_PATH=/data/projects
NFS_OPTS=noowners,nolockd,resvport,hard,intr,rw,tcp,nfc
StartVM()
{
echo "Start VM"
/usr/local/bin/VBoxManage startvm ${VM_ID} --type headless
echo ${?}
sleep 10
MountNFS
echo "All done"
Background
}
StopVM()
{
UmountNFS
echo "shutdown VM"
/usr/local/bin/VBoxManage controlvm ${VM_ID} acpipowerbutton
echo ${?}
}
MountNFS()
{
i=0
err_nfs=1
if mount |grep projects > /dev/null; then
echo "Already mounted"
else
while [ ${i} -lt 120 ]; do
if [ ${err_nfs} -gt 0 ]; then
echo "Mount NFS"
sudo /sbin/mount -t nfs -o ${NFS_OPTS} ${NFS_IP}:${NFS_PATH} ${Mount_PATH}
err_nfs=$(echo $?)
echo ${err_nfs}
sleep 1
i=$[$i+1]
else
echo "Mount done"
i=256
fi
done
fi
}
UmountNFS()
{
if mount |grep projects > /dev/null; then
echo "Unmount NFS"
sudo /sbin/umount ${Mount_PATH}
fi
}
Handler()
{
echo $(date)
#
# Condition check if a VM with given VM ID is already running
# Yes stop it, no start it
#
/bin/ps -Af|grep ${VM_ID}|grep -v grep
err=$(echo $?)
if [ ${err} -gt 0 ]; then
StartVM
else
StopVM
fi
}
Background()
{
tail -f /dev/null &
wait $!
}
trap StopVM SIGTERM SIGKILL SIGINT SIGHUP
Handler;
The most important part of this script is the Background function
and the trap
line. With trap
we say call function StopVM
when a System signals like a shutdown/logout event SIGTERM
or a kill event SIGKILL
occurs. But our script can only listen to those events when it’s running. That’s why we run a endless loop in the background function.
Now after every login my VM get started, the NFS share gets mounted and before logout my NFS Share gets umounted and my VM gets shutdown.
My examples can be found here(link).