smfgen

1.2.0 • Public • Published

smfgen

This tool generates an SMF manifest from a JSON description of the service. It's only intended to generate simple manifests. For more details, see smf(5).

This tool is still experimental.

[sudo] npm install -g smfgen

JSON Input

Emits an SMF manifest for the service described by the given JSON:

Property Description
ident The SMF identifier for the service. The full SMF identifier (FMRI) will be constructed from the category and identifier.
[category] Service category (default: "application").
label The human-readable name of the service.
[dependencies] Array of service FMRIs that must be online before this service is started.
start The method object that describes how to start the service. (See below.)
[stop] The method object that describes how to stop the service. (See below.)
[refresh] The method object that describes how to refresh the service. (See below.)
[enabled] Whether to start the service automatically (default: true)

Both the start and stop properties in the JSON should have object values that describe the context of the method. A method context consists of these properties:

Property Description
exec The script to run for this method. Defaults to :kill for the stop method. This invocation may run the service synchronously or in the background. Note that this mechanism does not allow you to use SMF's built in timeout for service startup, since it doesn't know when the service has actually started.
[user] Run the script for this method as this user.
[group] Run the script for this method as this group.
[environment] A hash of environment variables for this method script.
[privileges] An array of RBAC privilege names. This method will be run with this privilege set. See also: privileges(5).
[working_directory] Use this working directory when invoking the method script.
[timeout] The number of seconds the method may run before it is considered timed out and aborted. Defaults to 10 for start and refresh and 30 for stop.

A set of example methods might look like this:

{
    "start": {
        "user": "webservd", "group": "webservd",
        "exec": "/opt/pkg/sbin/apachectl start"
    },
    "stop": {
        "timeout": 120
    }
}

Note that you may also specify the properties that describe the context of a method at the top level of the JSON. Top-level properties apply to all methods, but may also be overridden in a specific method. For example:

{
    "user": "webservd", "group": "webservd",
    "exec": "/opt/pkg/sbin/apachectl %m",
    "timeout": 10,
    "stop": {
        "timeout": 120
    }
}

For compatibility with earlier versions of smfgen, the program will accept a string in place of an object for the start, stop, and refresh methods. This string will be assumed to be the exec property of that method.

Example

$ cat bapi.json
{
    "ident": "bapi",
    "label": "Boilerplate API",
    "start": "node bapi.js"
}
$ ./smfgen < bapi.json
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!-- 
    Manifest automatically generated by smfgen.
 -->
<service_bundle type="manifest" name="application-bapi" >
    <service name="application/bapi" type="service" version="1" >
        <create_default_instance enabled="true" />
        <dependency name="dep1" grouping="require_all" restart_on="error" type="service" >
            <service_fmri value="svc:/milestone/multi-user:default" />
        </dependency>
        <exec_method type="method" name="start" exec="node bapi.js &amp;" timeout_seconds="10" />
        <exec_method type="method" name="stop" exec=":kill" timeout_seconds="30" />
        <template >
            <common_name >
                <loctext xml:lang="C" >Boilerplate API</loctext>
            </common_name>
        </template>
    </service>
</service_bundle>

CLI Input

The following options can be specified over the CLI:

  • -c, --category becomes data.category
  • -d, --cwd becomes data.working_directory
  • -D, --dependency appends to data.dependencies
  • -e, --env appends to data.environment
  • -g, --group becomes data.group
  • -i, --ident becomes data.ident
  • -l, --label becomes data.label
  • -p, --privilege appends to data.privileges
  • -r, --refresh becomes data.refresh.exec
  • -s, --start becomes data.start.exec
  • -S, --stop becomes data.stop.exec
  • -t, --timeout becomes data.timeout
  • -u, --user becomes data.user

Example

$ ./smfgen -i nginx -l 'NGINX Web Server' \
    -s 'nginx -d' -d /var/www \
    -u nobody -g other \
    -p basic -p net_privaddr \
    -eHOME=/var/tmp -ePATH=/bin:/usr/bin
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!-- 
    Manifest automatically generated by smfgen.
 -->
<service_bundle type="manifest" name="application-nginx" >
    <service name="application/nginx" type="service" version="1" >
        <create_default_instance enabled="true" />
        <dependency name="dep0" grouping="require_all" restart_on="error" type="service" >
            <service_fmri value="svc:/milestone/multi-user:default" />
        </dependency>
        <exec_method type="method" name="start" exec="nginx -d &amp;" timeout_seconds="10" >
            <method_context working_directory="/var/www" >
                <method_credential user="nobody" group="other" privileges="basic,net_privaddr" />
                <method_environment >
                    <envvar name="HOME" value="/var/tmp" />
                    <envvar name="PATH" value="/bin:/usr/bin" />
                </method_environment>
            </method_context>
        </exec_method>
        <exec_method type="method" name="stop" exec=":kill" timeout_seconds="30" >
            <method_context working_directory="/var/www" >
                <method_credential user="nobody" group="other" privileges="basic,net_privaddr" />
                <method_environment >
                    <envvar name="HOME" value="/var/tmp" />
                    <envvar name="PATH" value="/bin:/usr/bin" />
                </method_environment>
            </method_context>
        </exec_method>
        <exec_method type="method" name="refresh" exec=":true" timeout_seconds="10" >
            <method_context working_directory="/var/www" >
                <method_credential user="nobody" group="other" privileges="basic,net_privaddr" />
                <method_environment >
                    <envvar name="HOME" value="/var/tmp" />
                    <envvar name="PATH" value="/bin:/usr/bin" />
                </method_environment>
            </method_context>
        </exec_method>
        <template >
            <common_name >
                <loctext xml:lang="C" >NGINX Web Server</loctext>
            </common_name>
        </template>
    </service>
</service_bundle>

Assumptions

This tool makes a ton of assumptions about your service:

  • Your service is a contract-model service in SMF. That means SMF should consider the service failed (and potentially restart it) if:
    • all processes in the service exit, OR
    • any process in the service produces a core dump, OR
    • any process outside the service sends any service process a fatal signal See svc.startd(1M) for details.
  • Your service is an application which depends on system services like the filesystem and network. This tool wouldn't work for system services implementing any of that functionality.
  • If you specify any additional dependencies (like other services of yours), that means your service should not be started until those other services are online. However, if those services restart, your service will not be restarted.
  • You only intend to have one instance of your service.
  • SMF provides a mechanism for timing out the "start" operation. But for simplicity, this tool always runs your start script in the background, so as far as SMF sees it starts almost instantly. If you want to detect "start" timeout, you must implement a start method that returns exactly when your program has started providing service (e.g., opened its server socket), and you'll have to write your own manifest rather than use this tool.
  • By default, the "stop" method just kills all processes in this service, which includes all processes forked by the initial "start" script. You can override this with a "stop" script, but you should use the default if that script is only going to kill processes. There's a default 30 second timeout on the stop script, so the processes must exit within about 30 seconds of receiving the signal.
  • The service does not use SMF to store configuration properties.

Package Sidebar

Install

npm i smfgen

Weekly Downloads

3

Version

1.2.0

License

MIT

Unpacked Size

27.6 kB

Total Files

9

Last publish

Collaborators

  • bahamas10
  • dap