Tutorials/Create a Mac OS X startup daemon

= Introduction = ''Creating a proper and safe Mac OS X daemon a relatively hard task, and this document is a work in progress. It works for me, it might not for you. Please refrain from doing so unless you know your way around Terminal and Console, and you have a good analytical mind that will tell you whenever you are doing mistakes. Take this text as a helping hand, not as a full "run this package and it works" text. You know about Creepers? Imagine one blowing up in the innards of your beautiful Mac! You don't want that, don't you? [Insert MythBusters warning here] ''

This page will help you make your Minecraft server securely run without having to log in as a user. It has been tested on Mac OS X Server Snow Leopard and Mac OS X Snow Leopard. You mileage will vary elsewhere, as the commands have changed a little bit.

Before even attempting this, you should run a server instance by hand, to make sure everything works. If you are able to run your server and actually connect to it from a remote computer, you can start thinking about creating a daemon.

Word of interest about sudo
I use  every single command I need to do as root. Many people will tell you to do  and be done with it. That's a valid argument, but it's also much more dangerous to do so. Forgetting you're as root and doing something awkward gives you more chances to destroy your computer.

It's also the reason why I first move to the proper folder and then do the action for a precise file or folder; I've had too many people in my life telling me they mistakenly pressed return while doing a nasty command and applied it to their whole computer... me included! I learned the hard way, and on that note, I'd like to thank my first Internet server provider for not giving me root access or else their computer would've been 'ed at least once.

Final reason, every sudo command is logged in your Console, meaning you know what happens, and if you made something incorrect, you can review your log and know what you did to try to repair it. Doing a  will only show that. But then, it's your computer, it's your habits, do whatever you want with it!

= Creating a _minecraft daemon user and group = This section is technically optional, but for security considerations, you should do it the hard way.

The easiest (and evil) way
And you are done! Congratulations! That will make your Minecraft application run as root, meaning if someone hacks your computer through Minecraft, it'll be able to do anything he wants, including viewing and modifying all your files, or simply deleting your computer into oblivion. Seriously, you do not want that! Please go to the Hard way and sweat your way. I'm a completionist, so I tell you the possibility to run it like that, but please don't ... please ...
 * 1) Don't do anything (Please make sure to modify the launchd plist file to remove the RunAsUser part)

The easy (and incorrect) way
And you are done! Congratulations! However, this creates a full fledged user, whilst Mac OS X expects a daemon user. As a side bonus, you can run this as your own personal user, or any other possibilities for users. But then, if your Minecraft user gets hacked, the hacker will have a full account to have fun, accessing all your software, and be able to modify everything that's public. It's not the best.
 * 1) Click on Apple menu item
 * 2) Open System Preferences
 * 3) Click on System: Accounts
 * 4) Click on the lock in order to unlock the page
 * 5) Enter your administrator password
 * 6) Click on the Plus button
 * 7) Create a new Standard user, named Minecraft (Please make sure to modify the launchd plist file saying you are using a different user) with a password
 * 8) Click Ok

The hard (and correct) way
The user will be created with an underscore first, denoting it's a daemon and should be hidden from user view. It will also be created with a daemon UID. We will use dscl to create a user and group. Obviously, it needs to be done with privileges. Open Terminal and type MyMac:~ myuser$ sudo dscl Password: (your password) Entering interactive mode... (type "help" for commands) Let's move to the base folder for what we need to do (less typing) > cd /Local/Default/ /Local/Default > If you are running a Mac OS X Server or if you have other directory authentication, you might have to move to an other folder instead of Local. Use the  command to view the different folders. To know if you are in the good place, you should have a Users folder with different underscore-prefixed names (like _coreaudiod, _softwareupdate and _www).

Creating the group (GID)
/Local/Default > ls Groups gid This will show up a list of all the created groups on your computer, along with their GIDs. We don't care what they are, we just want an empty GID, potentially in the daemon range (less than 500). Verify if 300 is unused. /Local/Default > create Groups/_minecraft /Local/Default > create Groups/_minecraft PrimaryGroupID 300 At this point, your group should be created, and assigned the 300 (or any number you chose) GID. You can verify it with the  command and you can compare it to others with the   command.

Creating the user (UID)
/Local/Default > ls Users uid This will show up a list of all the created users on your computer, along with their UIDs. Again, we don't care what they are, we just want an empty UID, again in the daemon range (less than 500). Verify if 300 is unused. You can select a different UID and GID, they need not to be identical, but it's certainly tidier if they are. /Local/Default > create Users/_minecraft UserShell /bin/bash /Local/Default > create Users/_minecraft UniqueID 300 /Local/Default > create Users/_minecraft PrimaryGroupID 300 /Local/Default > create Users/_minecraft NFSHomeDirectory /Users/_minecraft This created your user. Obviously you need to modify the UniqueID (UID) for the one you chose in this step, and the PrimaryGroupID for the one you chose in the previous section. You can also choose a different home directory. I put my Minecraft folder with the other users, but you can put this anywhere you want, really.

Like for the GID, you can use  and   to make sure everything is all right.

Now, we said to the user we have a group (PrimaryGroupID) but we need to tell the group it has users: /Local/Default > append /Groups/_minecraft GroupMembership _minecraft

And we're done! /Local/Default > exit Goodbye

Creating the user home
We now need to create the home folder. Assuming you previously described it as, please type: MyMac:~ myuser$ cd /Users MyMac:Users myuser$ sudo mkdir _minecraft MyMac:Users myuser$ sudo chown _minecraft:_minecraft _minecraft MyMac:Users myuser$ ls -la Whoa, there are many  here! Here is a version where you can understand something (don't go run this, that said!) cd /Users sudo mkdir UserFolder sudo chown UserName:GroupName UserFolder ls -la Here ... better! If these commands go through, it means your user and group were properly created in the previous steps. At this point, the ls -la should give you something like total 0 drwxr-xr-x  5 root        admin        170 15 jui  2010. drwxrwxr-x@ 34 root       admin       1224  6 jan 23:33 .. -rw-r--r--  1 root        wheel          0  1 jul  2009 .localized drwxrwxrwt  5 root        wheel        170 30 sep 20:18 Shared drwxr-xr-x+ 27 _minecraft _minecraft     0 26 feb 12:54 _minecraft drwxr-xr-x+ 27 sakamura   staff        918 10 nov 21:48 sakamura

Moving the server files
That's a walk in the park compared to what we went through. First, make sure your Minecraft server is not actually running, or you'll definitely lose data! Then... MyMac:~ myuser$ cd /Users/_minecraft MyMac:_minecraft myuser$ sudo mv /Users/myuser/Desktop/Minecraft\ Server/*. MyMac:_minecraft myuser$ sudo chown -R _minecraft:_minecraft * Of course, you want to adjust as needed, with the starting path being where you actually installed your Minecraft server in the first place, and please look-up the previous section for an explanation of the chown command. ''And please don't forget the dot at the end of the mv command, it's barely visible but it's there, it means the destination is our current folder! Otherwise, command will fail''

Tip #1: If you press Tab once, it will auto-complete the name of the file/folder for you. That's very useful for quickly writing special characters like the evil space bar that needs to be written with a backslash first.

Tip #2: If you press Tab twice, it will show you all possible auto-completion possibilities, to give you a quick choice if you forgot how it's actually written.

Tip for the astute reader: Instead of doing a chown once in the previous step, and a chown at this step, it's quicker to simply move the folder, and chown -R the resulting folder. One less command. The reason why I do it twice is to make sure your User and Group are properly created in the previous step. But no reason.

= Support files = You need a few files in order to make this work adequately. It could be done with less files, but it's more readable that way. Please copy them carefully and modify what has to be modified accordingly. I personally use vim as a text editor since it keeps my carriage returns at their proper places and doesn't try to be intelligent with me. However, it's also (with emacs) one of the hardest editor to use, from another era). GUI-wise, Apple TextEdit should be fine for this, as long as you follow the instructions from the Mac OS X Setting up a server page (IE: Make as Plain Text).

minecraft.command
trap 'echo "$(date) Killing Minecraft."; ./stop.sh; exit' TERM ipconfig waitall echo "$(date) Starting minecraft." ./start.sh & wait echo "$(date) Minecraft done."
 * 1) !/bin/bash

Startup
This script must be copied inside your  folder.

You must set the working folder to  before attempting to use it as it doesn't like being sent elsewhere. Yes, I know, this can be done and it's even explained in the non-daemon version, however, Mac OS X launchd guidelines says you should never change your working folder for your application.

There is also a need to wait for the network stack to be properly initialized before continuing. does just that. Yes, it's really  and not , both these tools actually exist on Mac OS X, and are different. Beware, this tool is deprecated, and might disappear on Mac OS X Lion, we'll see at that point.

Termination
Mac OS X Launchd sends a SIGTERM (control-c or signal 15) to kill a daemon. Java plain returns when it receives one. Hence you need to  it before it reaches java. The whole  code could be added to the trap line (with many   and , but it'd be messy.)

The  script needs to be asynchronously started and   upon, as trap needs to exit   to execute properly See trap examples and explanation here.

start.sh
touch stdin.commandlist tail -n 0 -f stdin.commandlist | java -Xmx1G -Xms1G -jar minecraft_server.jar nogui
 * 1) !/bin/bash

stdin redirection
In the other Linux systems, the  command is used to create a separate screen TTY to execute the java system in. The main advantage being to be able to switch to that screen if someone wants to directly interact with the server. That would work if you were to put the launchd plist in your personal user folder, but it will not work in the startup daemon, and it's specifically not supported my launchd, although you can get an up-to-date screen that won't cause problems.

There are multiple ways to send out commands, including a mkfifo that will work well, but will cause problems with carriage returns. So I prefer to use a file, and append the commands I want to send to the java through a pipe.

Termination
The tail command will automatically terminate once java terminates.

Bug
The java environment doesn't seem to quit correctly when shutting down. That's weird .... and must be investigated. Yet another signal maybe? TODO.

stop.sh
echo stop >> stdin.commandlist while [ 1 ]; do	sleep 1 ps -u _minecraft | awk 'minecraft_server.jar/{print $2}' > /dev/null 2> /dev/null if [ $? != 0 ]; then exit fi done
 * 1) !/bin/bash

Command passing
To pass a command to the environment, you simply  a command, appending it to the command list file.

Waiting for the java environment to stop
The system waits for the  user's command containing   to be done before exiting. If unloading through, this one will give up after 20 seconds.

Launchd plist
= Tying it up =