initng technical overview

initng is all about lauching and managing processes, daemons and services. The chief goals for this daemon are:

So what kind of reasons do we have to lauch stuff on demand? Well, for background, there is my rambling inventigation of what kind of daemons we have in the system.

So I think the fastest way to look at this is the number one way developers will interface with initng is the plist they'll use to describe a service/daemon to the system. With the exception of the "Program" variable and at least one "trigger" being specified, the rest of the values are optional:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Program</key>
	<string></string>
	<key>ProgramArguments</key>
	<array/>
	<key>EnvironmentVariables</key>
	<dict/>
	<key>ServiceDescription</key>
	<string></string>
	<key>Disabled</key>
	<false/>
	<key>OnDemand</key>
	<true/>
	<key>LaunchOnce</key>
	<false/>
	<key>ServiceIPC</key>
	<false/>
	<key>UserName</key>
	<string></string>
	<key>GroupName</key>
	<string></string>
	<key>HardwareDevices</key>
	<dict/>
	<key>MachServiceNames</key>
	<array/>
	<key>WatchPaths</key>
	<array/>
	<key>Sockets</key>
	<array>
		<dict>
			<key>addrinfo_canonname</key>
			<false/>
			<key>addrinfo_family</key>
			<string></string>
			<key>addrinfo_nodename</key>
			<string></string>
			<key>addrinfo_numerichost</key>
			<false/>
			<key>addrinfo_passive</key>
			<false/>
			<key>addrinfo_protocol</key>
			<string></string>
			<key>addrinfo_servname</key>
			<string></string>
			<key>addrinfo_socktype</key>
			<string></string>
			<key>listen_depth</key>
			<integer>5</integer>
		</dict>
	</array>
	<key>SpecificTimeval</key>
	<integer>0</integer>
	<key>PeriodicSeconds</key>
	<integer>0</integer>
	<key>Cron</key>
	<dict>
		<key>DayOfTheMonth</key>
		<string></string>
		<key>DayOfTheWeek</key>
		<string></string>
		<key>Hour</key>
		<string></string>
		<key>Minute</key>
		<string></string>
		<key>Month</key>
		<string></string>
	</dict>
	<key>Limits</key>
	<dict>
		<key>CPUTime</key>
		<integer>0</integer>
		<key>CoreFileSize</key>
		<integer>0</integer>
		<key>DataSegSize</key>
		<integer>0</integer>
		<key>FileSize</key>
		<integer>0</integer>
		<key>MaxLockedMemory</key>
		<integer>0</integer>
		<key>MaxMemorySize</key>
		<integer>0</integer>
		<key>MaxUserProcesses</key>
		<integer>0</integer>
		<key>OpenFiles</key>
		<integer>0</integer>
		<key>PipeSize</key>
		<integer>0</integer>
		<key>StackSize</key>
		<integer>0</integer>
		<key>VirtualMemory</key>
		<integer>0</integer>
	</dict>
</dict>
</plist>

So what do all these variables do?

ProgramThe path to the daemon/service/command.
ProgramArgumentsAn array of strings to pass as arguments to the program.
EnvironmentVariablesAn array of extra environmental variables to pass to the program.
ServiceDescriptionA description of what this service does.
DisabledIs this service disabled?
OnDemandIs this service capable of running on demand?
LauchOnceDoes this service need to be launched once (for whatever reason).
ServiceIPCDoes this service participate in the Service API? (Can we query it, ask it to shutdown, etc).
UserNameWho should this service run as?
GroupNameWhich group should this service run as?
MachServiceNamesAn array of services to publish into the mach namespace that this program is expected to service.
HardwareDevicesA dictionary of keys and values to watch for in the IOKit namespace, should they show up, launch this service (for blued, the USB printer team, etc).
WatchPathsAn array of strings that are file system paths to watch for change and then launch this job.
SocketsAn array of dictionaries that correspond to the addrinfo structure (see getaddrinfo(3)).
SpecificTimevalMeant for any service that needs to be lauched at a specific time (see at(1) and batch(1)).
PeriodicSecondsMeant for any true periodic job.
CronSupport for crontab(5) style time specification.
LimitsFinally, since this is being lauched by pid 1, people want an easy way to specify limits.

So given the above job/daemon/service description capabilities, what will the internals of initngd look like?

Fundamentally, a kqueue() and Libc plus system calls based state engine. Internally, we'll maintain two linked list, one of jobs that could run, and one of jobs that are running. We will use an IPC mechanism to accept binary job descriptions from tools that parse configuration files, be they the plist outlined above or legacy configuration files. This will simplify and insulate the daemon from failure. I don't see this as being very difficult. The only difficulty is just in the details and matters of risk/reward such as what API libraries we wish to use or not use and in defining how to effectively communicate to the daemon why they were launched (I still haven't found something I like yet, Mach has a solution for the Mach APIs, be we need something for the rest of the problem space.)

Eventually the IPC used to submit jobs will be public, but we can flush it out throughtout Merlot, we don't need to do it up front.

Long term work needs to be done on the IPC protocol between initng and the respective daemons for job control.

So what has been done so far?

I've written a simple daemon that takes job requests from an external agent and launches them based on time related events. I just need to flush out the rest of the features and design now.


Links:

My earlier daemon investigation.
Dave Zarzycki