Products for USB Sensing and Control
Products for USB Sensing and Control

A Daemon on the SBC

How to write and deploy a daemon on the Phidget SBC


by Chad

Introduction

This article introduces the concept of a daemon process in the UNIX environment, and teaches how to create, install and manage a daemon on the Phidget SBC.

A daemon is a process that runs, typically for a long period of time, in the background. The process is detached from a controlling terminal and no longer communicates directly with a user. In many cases, the daemon is a server and handles requests from users. A webserver is an example of a server daemon.

You want a daemon process if you need a long running process to perform tasks such as gathering, processing and serving sensor data, and you do not want the process to exit when you log out of your SBC, and in most cases, you want the process to start automatically when the SBC boots.

Hardware

Becoming a Daemon

In the UNIX environment a process becomes a daemon by forking a new child process that will continue on, and the parent (that was started by a user) exits. From the user perspective at the command line, the process is started, and appears to exit immediately. The daemon process is a new process that runs in the background.

			
	int
	init_daemon(int flags) {
		pid_t pid;

		pid = fork();	/* create a new process */
		if (pid < 0)	/* error */
			return (1);
		if (pid != 0)	/* in the parent the pid of the new child is returned */
			exit(0);

		return (0);
	}
			
			

Now that the child process is running, and no longer attached to the terminal of the user that created it, there are a few things to consider.

  • In what directory is the daemon running?
  • How are the actions of the daemon configured?
  • How will the daemon communicate its progress and status?

A well behaved daemon does not expect a specific working directory, and should use absolute paths to access files and directories. Since our daemon is well behaved, we should change the working directory of the daemon to the root directory. This allows the filesystem where the daemon was running to be unmounted if ever necessary (without having to restart the daemon), and allows the user to delete that directory without causing the daemon to potentially fail.

Changing the working directory is not absolutely necessary, but is considered "the right thing to do". Changing the working directory to the root directory may not always be the most convenient. In some cases, the daemon could change to a well known directory where it can assume its configuration files and other resources are located. This is more common in large programs where there may be multiple configuration files, log files and resources required by the program.

			
	int
	init_daemon(int flags) {
		pid_t pid;

		pid = fork();	/* create a new process */
		if (pid < 0)	/* error */
			return (1);
		if (pid != 0)	/* in the parent the pid of the new child is returned */
			exit(0);

		/* Unless we are told not to, change directory to '/' */
		if ((flags & NOCHDIR) == 0) {
			if (chdir("/") != 0)
				return (1);
		}

		return (0);
	}
			
			

Since the daemon is no longer attached to a controlling terminal, it must be configured with command line arguments to the parent, or from a configuration file. Typically a configuration file is used, unless the daemon is very simple, and often a combination of the two is best. Regardless, the daemon should not expect to be able to read from standard input. In addition, output from the daemon should no longer be written to standard output and standard error, since the terminal the daemon was started in probably doesn't exist any more (and could be the console of the machine). Output to the console is annoying, and output to a closed terminal is pointless.

Our well behaved daemon will expect a configuration file, and will use proper logging techniques, so we do not need to keep standard input, output and error open. We could just close those descriptors, but a lot of code uses them blindly and could causes issues for us, so instead we can simply route all 'bad' IO to /dev/null.

			
	int
	init_daemon(int flags) {
		pid_t pid;

		pid = fork();	/* create a new process */
		if (pid < 0)	/* error */
			return (1);
		if (pid != 0)	/* in the parent the pid of the new child is returned */
			exit(0);

		/* Unless we are told not to, change directory to '/' */
		if ((flags & NOCHDIR) == 0) {
			if (chdir("/") != 0)
				return (1);
		}

		/* Map stdin, stdout and stderr to /dev/null */
		if ((flags & NOCLOSEFD) == 0) {
			if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
				(void)dup2(fd, STDIN_FILENO);
				(void)dup2(fd, STDOUT_FILENO);
				(void)dup2(fd, STDERR_FILENO);
			}
		}

		return (0);
	}
			
			

The above code routes IO destined to stdin, stdout and stderr to /dev/null, and while this is better than simply closing those descriptors, we could also consider sending everything from stdout and stderr to a log file. In the case where we are using existing code that uses fprintf() to log, redirecting that IO might be easier than rewriting all of that existing code.

Managing a Daemon

Now that we have learned how to create a daemon process, we have to figure out how to start and stop that process, both manually, and when the system boots.

Different UNIX environements handle system initialization and the starting of daemon processes differently, but for the most part the basics are the same. When the operating systems boots, a list of services to start is created (ordered by dependency and priority somehow), and then the services are started. Traditionally each service has its own init script (normally just a shell script) that is read from a well known directory, and that script contains the instructions for how to start and stop the daemon, as well as indicate any prerequisites or dependencies.

Phidget SBCs run Debian Linux, and support both init.d style scripts as well as systemd in later versions. As systemd is still fairly new, and not all systems support it, this article focuses on init.d scripts. Check your specific operating system documentation for details.

			
	#!/bin/sh	

	### BEGIN INIT INFO
	# Provides: daemonname
	# Required-Start: $network $remote_fs
	# Required-Stop: $network $remote_fs
	# Description: A description
	# Short-Description: A short description
	### END INIT INFO

	NAME=daemonname
	BIN=/path/to/daemon/daemond
	PIDFILE=/var/run/${NAME}.pid
	CFG=/path/to/config

	start() {
			echo "Starting ${NAME}
			${BIN} -c ${CFG}
	}

	stop() {
		echo "Stopping ${NAME}
		if [ -f ${PIDFILE} ] ; then
			PID=`cat ${PIDFILE}`
			kill ${PID}
			rm -f ${PIDFILE}
		else
			echo "${PIDFILE} not found"
		fi
	}

	case "$1" in
		start)
			start
			;;
		stop)
			stop
			;;
		*)
			echo "Usage $0 {start|stop}
	esac

	exit 0
			
			

As this is a shell script, we indicate that /bin/sh should be the interpreter by starting the script file with #!/bin/sh. Following that, we list the dependency and service identification information. On Debian, the control information must be between the BEGIN and END lines, and the format is strict. There are additional control keys that have not covered. Please see the Debian LSBInitScripts documentation for more information.

  • Provides: the boot facility provided when this daemon is started
  • Required-Start: facilities that are required by this daemon
  • Required-Stop: facilities that should not be stopped before this daemon
  • Description: A description of this daemon
  • Short-Description: A short (one line) description of this daemon
In the case of most simple daemons, Required-Start: is all that is normally required. $network indicates that we require networking to have been started, and $remote_fs indicates that we require all filesystems to have been mounted. The following are also common facilities:
  • $local_fs
  • $named
  • $portmap
  • $syslog
  • $time
  • $all
$all can be used to cause the daemon to be started at or near the end of system initialization.

Note that the init script always exits with a value of 0. Unless you know for sure that your system will handle (and expects) a non-zero exit code from init scripts, you should always exit 0.

Once start and stop are working, it is a good idea to add restart and status targets to the script. There may be additional targets expected or supported by your version of UNIX. Please check your documentation for details.

After your have tested your init script, copy it into the /etc/init.d directory, and run update-rc.d to create the necessary links to allow the system to automatically start and stop your daemon.

Conclusion

Now that we have covered that details of how to create and configure your own daemon process on a Phidget SBC, it is only fair to point out that the web interface of the SBC supports the creation of User Projects, and that system will handle a lot of these details for you, including running a simple process as a daemon. If the requirements of your project do not require the type of control we have outlined in this article, User Projects may be the best option. For details on how to use User Projects on a Phidget SBC please see the user manual for your SBC.