npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

smfgen

v1.2.0

Published

Generate an SMF manifest from a JSON or CLI description of the service

Downloads

21

Readme

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.