Package integration
An optional package is more than the files it installs. When pkg(8) installs a
FreeSense-pkg-* port, the FreeSense package framework reads the package’s manifest and registers
its WebUI menus, services, tabs, plugin hooks, and configuration with the appliance. This page
follows that lifecycle and describes the manifest fields the framework uses. To produce the package
itself, see building optional packages.
Repositories and the Package Manager
Section titled “Repositories and the Package Manager”FreeSense-repoc writes the appliance’s pkg(8) repository definitions from the signed channel
manifest on pkg.freesense.org, verified with keys shipped in the base system:
| Repository | Contents | Priority |
|---|---|---|
FreeSense |
The System repository: base OS, WebUI, and System ports | 100 |
FreeSense-packages |
Optional packages for the same release train | 50 |
FreeSense-mirror |
A frozen upstream FreeBSD package layer, when the channel publishes one | 10 |
The priorities make a package that FreeSense builds always win over a stock package of the same name. The stock FreeBSD repositories are disabled.
Operators can add their own repositories under System → Package Manager → Repositories. Custom repositories are kept separate: the system upgrader and the WebUI’s own package operations use only the FreeSense repositories, and the Package Manager lists packages from other sources only when the operator selects that source. See custom repositories.
The Package Manager combines two sources:
pkg(8)supplies package names, versions, and installation state.- The product catalog (
/etc/freesense-package-catalog.json, read bysrc/etc/inc/package_catalog.inc) supplies the display name, category, support state, resource profile, capability badges, services, and the Configure and Status destinations.
The catalog ships with the base system, not with the package, so product metadata is reviewed and
versioned with the System release. The Available and Installed lists mark FreeSense packages as
Official. The per-package control page pkg_control.php offers start, stop, and
restart only for the services the catalog lists for that package.
Install lifecycle
Section titled “Install lifecycle”- System → Package Manager runs
FreeSense-upgradeto install, remove, or reinstall the package throughpkg(8). - After
pkg(8)places the files, the package’spkg-installscript runsphp -f /etc/rc.packages <PORTNAME> POST-INSTALL. rc.packagesstrips theFreeSense-pkg-prefix and callsinstall_package_xml()insrc/etc/inc/pkg-utils.inc, which:- reads the registration file
/usr/local/share/FreeSense-pkg-<name>/info.xmland records it ininstalledpackages/packageinconfig.xml; - parses the manifest named by its
<configurationfile>from/usr/local/pkg/; - loads the manifest’s
<include_file>(installation stops and is rolled back if it is missing); - runs
custom_php_global_functions,custom_php_install_command, thencustom_php_resync_config_command; - writes the package’s menu and service entries, each tagged with the owning package; and
- saves the configuration and, if the package declares
<logging>, restarts the system logger.
- reads the registration file
- The WebUI picks up the new menu entries on the next page load.
Removal runs the same script with DEINSTALL and POST-DEINSTALL. delete_package_xml() stops the
package’s services, removes its rc scripts, runs custom_php_pre_deinstall_command and
custom_php_deinstall_command, removes the menu and service entries that the package owns, and
finally removes its installedpackages/package record.
At boot, /etc/rc.start_packages calls sync_package() (which runs the resync hook unless the
manifest sets <nosync>) and starts each registered service. After an upgrade or configuration
restore, the framework refreshes the generated menu and service metadata for every installed
package instead of re-running install hooks.
The registration file: info.xml
Section titled “The registration file: info.xml”<?xml version="1.0"?><freesensepkgs> <package> <name>tftpd</name> <descr><![CDATA[Installs a TFTP server, used for thin client booting and more.]]></descr> <website>http://freecode.com/projects/tftp-hpa/</website> <version>%%PKGVERSION%%</version> <configurationfile>tftpd.xml</configurationfile> </package></freesensepkgs><configurationfile> names the manifest in /usr/local/pkg/. Optional elements include
<internal_name>, <after_install_info>, and <logging>.
New packages use the <freesensepkgs> root element. If a file doesn’t parse with that root,
pkg-utils.inc falls back to the upstream <pfsensepkgs> root, so third-party and rebuilt upstream
packages can still register their menus, services, and tabs.
The package manifest
Section titled “The package manifest”The manifest (/usr/local/pkg/<name>.xml, root element <packagegui>) declares everything the
framework needs. A package can describe its whole settings form in XML, or point its menu at custom
PHP pages and keep only the registration parts.
<packagegui> <name>tftpd</name> <!-- settings live in installedpackages/tftpd/config --> <version>%%PKGVERSION%%</version> <title>Services/TFTP Server</title> <include_file>/usr/local/pkg/tftpd.inc</include_file> <menu> <name>TFTP Server</name> <section>Services</section> <group>network</group> <url>/pkg_edit.php?xml=tftpd.xml</url> </menu> <service> <name>tftpd</name> <rcfile>tftpd.sh</rcfile> <executable>in.tftpd</executable> <description>TFTP Daemon</description> </service> <tabs> <tab><text>Settings</text><url>/pkg_edit.php?xml=tftpd.xml</url><active/></tab> <tab><text>Files</text><url>/tftp_files.php</url></tab> </tabs> <fields> <field> <fielddescr>Enable TFTP service</fielddescr> <fieldname>enable</fieldname> <type>checkbox</type> </field> <!-- ... --> </fields> <custom_php_install_command>install_package_tftpd();</custom_php_install_command> <custom_php_deinstall_command>deinstall_package_tftpd();</custom_php_deinstall_command> <custom_php_resync_config_command>sync_package_tftpd();</custom_php_resync_config_command> <custom_php_validation_command>validate_form_tftpd($_POST, $input_errors);</custom_php_validation_command></packagegui>| Element | Purpose |
|---|---|
name |
Configuration key. Settings saved by generated forms live under installedpackages/<name>/config. |
include_file |
PHP file loaded before any hook runs; holds the hook functions. |
menu |
One or more WebUI menu entries, each with name, section, an optional group, and url. See the WebUI menu system. |
tabs |
Tab bar for generated pages (text, url or xml, active, tab_level). |
service |
Service registration for Status → Services and service controls. |
fields, adddeleteeditpagefields |
Generated settings form rendered by pkg_edit.php, or list view rendered by pkg.php. |
plugins |
System events the package subscribes to (see below). |
custom_php_install_command |
Runs once at install time. |
custom_php_resync_config_command |
Applies saved settings: writes daemon configuration, rc scripts, and restarts services. Runs at install, after saves, and at boot. |
custom_php_validation_command |
Validates submitted form input before it is saved. |
custom_php_pre_deinstall_command, custom_php_deinstall_command |
Clean up services and generated files at removal. |
nosync |
Skip the resync hook at boot. |
shortcut_section |
Selects which title-bar shortcuts apply to generated pages. |
Keep hooks idempotent. The resync hook in particular runs repeatedly and must converge on the saved configuration rather than append to previous output.
Configuration storage
Section titled “Configuration storage”Package configuration is part of config.xml, so it is included in backups, restore points, and
high-availability synchronization:
installedpackages/<name>/config— package settings (generated forms use entry0for a single settings page, and one entry per row for list views);installedpackages/package— registration records;installedpackages/menuandinstalledpackages/service— generated, owner-tagged metadata.
Custom PHP pages should read and write settings with config_get_path() and config_set_path(),
call write_config() with a change description, and then call the package’s resync function.
Services
Section titled “Services”A <service> entry makes the daemon visible in Status → Services, the dashboard services
widget, and the page title-bar controls. The framework starts and stops it with the rc script named
by <rcfile> (relative to /usr/local/etc/rc.d/) or with startcmd/stopcmd/restartcmd when
provided. Running state is detected with custom_php_service_status_command or by matching
<executable>. Packages typically generate their rc script in the resync hook with write_rcfile().
List the same service names in the package’s catalog entry so pkg_control.php offers controls for
them.
Plugin hooks
Section titled “Plugin hooks”A package subscribes to system events by listing them in its manifest:
<plugins> <item><type>plugin_carp</type></item></plugins>When the event occurs, pkg_call_plugins() loads the package’s include_file and calls a function
named after the manifest file plus the hook type. For avahi.xml and plugin_carp, that is
avahi_plugin_carp($params).
| Hook | Raised when |
|---|---|
plugin_carp |
A CARP VIP becomes master or backup |
plugin_certificates |
Certificate, CA, or CRL usage is checked or changed |
plugin_xmlrpc_send, plugin_xmlrpc_recv, plugin_xmlrpc_recv_done |
High-availability configuration synchronization sends or receives package settings |
plugin_gateway |
A monitored gateway changes status |
plugin_periodic |
Periodic maintenance runs |
plugin_nginx |
The WebUI web server configuration is generated |
plugin_statusoutput |
The diagnostic status report (/status.php) is generated |
Packages can also drop scripts into directory hooks: /usr/local/pkg/pf/ for firewall rule
generation, and /usr/local/pkg/write_config/ or /usr/local/pkg/parse_config/ for configuration
events.
Privileges, shortcuts, widgets, and help
Section titled “Privileges, shortcuts, widgets, and help”| Integration | Where the package installs it | Effect |
|---|---|---|
| Privileges | /etc/inc/priv/<name>.priv.inc |
Defines page-* privileges whose match patterns grant access to the package’s pages and make its menu entries visible to non-admin users. |
| Shortcuts | /usr/local/www/shortcuts/pkg_<name>.inc |
Adds title-bar links to the package’s main, status, and log pages and a service start/stop control. |
| Dashboard widgets | /usr/local/www/widgets/widgets/<name>.widget.php and /usr/local/www/widgets/include/<name>.inc |
Makes the widget available on the dashboard. No manifest entry is needed. |
| Context help | src/etc/inc/freesense-docs.inc in freesense |
Maps the package’s pages to its documentation guide for About this Page. |
A privilege file for the TFTP package grants both its generated settings page and its custom page:
$priv_list['page-services-tftpd'] = array();$priv_list['page-services-tftpd']['name'] = "WebCfg - Services: tftpd package";$priv_list['page-services-tftpd']['descr'] = "Allow access to tftpd package GUI";$priv_list['page-services-tftpd']['match'] = array();$priv_list['page-services-tftpd']['match'][] = "pkg_edit.php?xml=tftpd.xml*";$priv_list['page-services-tftpd']['match'][] = "tftp_files.php*";Every page a package adds must be covered by a privilege, or only administrators can reach it.
Integration checklist
Section titled “Integration checklist”info.xmlnames the manifest, and the manifest names an existinginclude_file.- Each menu entry uses a valid section, a
groupfor grouped menus, and a URL covered by a privilegematch. - Services declare an rc script or start/stop commands and a way to detect running state.
- The resync hook is idempotent and the deinstall hooks stop services and remove generated files.
- The catalog entry lists the same services and points
configure_path/status_pathat real pages. - The package is documented and, if it has a dedicated guide, mapped for context help.