Developing Packages¶
Developers familiar with FreeBSD, pkg, and PHP, will find it fairly
straightforward to create packages for pfSense® software. End users and
organizations can benefit from developing a package that does not exist.
To submit a new package, create a pull request on GitHub for the pfSense software FreeBSD-Ports Repository so Netgate can evaluate the work for inclusion into the package repository for access by all users.
When developing packages, always target the latest development version of pfSense software first.
pfSense Software Package System¶
On pfSense software, every pfSense software package is also a FreeBSD port.
These are installed and managed via pkg, even when using the GUI to add or
remove packages. Binary packages from FreeBSD are added as dependencies of the
pfSense software package, so they are installed automatically as well, along
with any of their own required dependencies.
The basic idea is to make packages for pfSense software similar to FreeBSD packages, but with customization. One way this is achieved is by adding metadata about packages to the firewall configuration when it is installed, and also creating the configuration screen of an application using XML. pfSense software provides an optional framework to create the web interface and to store it in the XML configuration file of the firewall. The package writer is expected to convert the data from XML to the native format of the application.
Package System¶
pfSense software packages are typically composed of:
A Manifest File
Package configuration file(s)
Supporting files (
.incfiles, additional.phpweb interface files, etc.)
See also
See Package Port Directory Structure for a more in-depth list of files and their locations in the package structure.
Manifest File¶
The manifest file is located inside the package’s port directory:
<category>/pfSense-pkg-<package name>/files/usr/local/share/pfSense-pkg-<package name>/info.xml
For example:
sysutils/pfSense-pkg-Cron/files/usr/local/share/pfSense-pkg-Cron/info.xml
This file contains basic information about the package. The format of the manifest XML file is as follows:
<pfsensepkgs>
<package>
<name>someprogram</name>
<descr><![CDATA[Some cool program.]]></descr>
<version>%%PKGVERSION%%</version>
<configurationfile>someprogram.xml</configurationfile>
</package>
</pfsensepkgs>
Note
The version entry is updated automatically, use the template as shown.
Package Configuration Files¶
The manifest specifies a Package Configuration File for the package using the
config_file tag. The convention is to keep this file inside the
files/usr/local/pkg/ directory inside the port structure for the package.
The easiest way to get a feel for the format is to look at existing packages and how they use these configuration files, how their fields look, how the code behaves, and so on.
The format is:
<?xml version="1.0" encoding="utf-8" ?>
<packagegui>
<copyright></copyright>
<name></name>
<title></title>
<include_file></include_file>
<aftersaveredirect></aftersaveredirect>
<menu>
<name></name>
<section></section>
<configfile></configfile>
<tooltiptext></tooltiptext>
<url>/pkg.php?xml=package.xml</url>
</menu>
<tabs>
<tab>
<text></text>
<url></url>
<active/>
<tab_level/>
</tab>
</tabs>
<service>
<name></name>
<rcfile></rcfile>
<executable></executable>
<description></description>
</service>
<plugins>
<item>
<type>plugin_name</type>
<item>
</plugins>
<adddeleteeditpagefields>
<columnitem>
<fielddescr></fielddescr>
<fieldname></fieldname>
</columnitem>
</adddeleteeditpagefields>
<fields>
<field>
<fielddescr></fielddescr>
<fieldname></fieldname>
<description></description>
<size></size>
<type></type>
</field>
</fields>
<custom_php_global_functions><!-- PHP function call --></custom_php_global_functions>
<custom_php_install_command><!-- PHP function call --></custom_php_install_command>
<custom_php_pre_deinstall_command><!-- PHP function call --></custom_php_pre_deinstall_command>
<custom_php_deinstall_command><!-- PHP function call --></custom_php_deinstall_command>
<custom_add_php_command><!-- PHP function call --></custom_add_php_command>
<custom_add_php_command_late><!-- PHP function call --></custom_add_php_command_late>
<custom_delete_php_command><!-- PHP function call --></custom_delete_php_command>
<custom_php_resync_config_command><!-- PHP function call --></custom_php_resync_config_command>
<start_command><!-- PHP function call --></start_command>
<custom_php_service_status_command><!-- PHP function call --></custom_php_service_status_command>
<custom_php_validation_command><!-- PHP function call --></custom_php_validation_command>
<custom_php_after_head_command><!-- PHP function call --></custom_php_after_head_command>
<custom_php_command_before_form><!-- PHP function call --></custom_php_command_before_form>
<custom_php_after_form_command><!-- PHP function call --></custom_php_after_form_command>
</packagegui>
Field types¶
interfaces_selectionCombo/list box with interfaces list:
<field> <fielddescr>Interface Selection</fielddescr> <fieldname>interfaces</fieldname> <type>interfaces_selection</type> <description>Select interfaces to listen on</description> <multiple/><!-- (optional) --> <size>10</size><!-- (optional) --> <hideinterfaceregex>(wan|loopback)</hideinterfaceregex><!-- (optional) --> <showvirtualips/><!-- (optional) --> <showips/><!-- (optional) --> <showlistenall/><!-- (optional) --> </field>
checkboxField with text description and an enable/disable checkbox:
<field> <fielddescr>Enable</fielddescr> <fieldname>enable_package</fieldname> <type>checkbox</type> <description>Select this option to enable this config</description> </field>
inputSingle line text edit element
<field> <fielddescr>username</fielddescr> <fieldname>username</fieldname> <type>input</type> <description>Enter package username</description> </field>
passwordSpecial input element for passwords, all input will be masked with
*symbol on GUI but clear text on XML config file:<field> <fielddescr>password</fielddescr> <fieldname>password</fieldname> <type>password</type> <description>Enter password</description> </field>
textareaMulti-line text edit element:
<field> <fielddescr>Custom options</fielddescr> <fieldname>custom_options</fieldname> <type>textarea</type> <description>Paste custom config here</description> <encoding>base64</encoding><!-- (optional) --> </field>
selectCombo Box with drop-down list items:
<field> <fielddescr>Some Choice</fielddescr> <fieldname>some_choice</fieldname> <description><![CDATA[Select a choice]]></description> <type>select</type> <options> <option><name>Choice A</name><value>a</value></option> <option><name>Choice B</name><value>b</value></option> </options> <multiple/><!-- (optional) --> <size>10</size><!-- (optional) --> </field>
infoInformation text without any options to select:
<field> <fielddescr>Additional info</fielddescr> <fieldname>just_info</fieldname> <type>info</type> <description>show info text on package GUI</description> </field>
buttonAdditional buttons to take additional actions on packages:
<field> <fielddescr>Reload config</fielddescr> <fieldname>reload</fieldname> <type>button</type> <description>click to force a config reload</description> <placeonbottom/><!-- Use this option to place the button besides save default button --> </field>
In package
.incfile, to check what button was selected, use:if (($_POST['Submit'] == 'Save') { /* Do save stuff */ } if (($_POST['Submit'] == 'Reload') || !isset($_POST['Submit'])) { /* Do reload stuff */ }
Field groups¶
combinefieldsSeveral options can be combined into a single row as an option group using
<combinefields>For example this block groups three options onto a single row:
<field> <fielddescr>Option 1</fielddescr> <fieldname>option1</fieldname> <description>Option 1</description> <type>input</type> <combinefields>begin</combinefields> </field> <field> <fielddescr>Option 2</fielddescr> <fieldname>option1</fieldname> <description>Option 2</description> <type>input</type> <field> <fielddescr>Option 3</fielddescr> <fieldname>option3</fieldname> <description>Option 3</description> <type>input</type> <combinefields>end</combinefields> </field>
rowhelperUsed in
pkg_edit.phpto add multiple config lines like a table on package GUI. Inside<rowhelper>, add any field type described above:<field> <fielddescr><![CDATA[Lists]]></fielddescr> <fieldname>none</fieldname> <description><![CDATA['Format' - Choose the file format that url will retrieve or local file format.]]></description> <type>rowhelper</type> <rowhelper> <rowhelperfield> <fielddescr>Format</fielddescr> <fieldname>format</fieldname> <type>select</type> <options> <option><name>gz</name><value>gz</value></option> <option><name>txt</name><value>txt</value></option> </options> </rowhelperfield> <rowhelperfield> <fielddescr>URL or local file</fielddescr> <fieldname>url</fieldname> <type>input</type> <size>75</size> </rowhelperfield> </rowhelper> </field>
adddeleteeditpagefieldsUsed with
pkg.phpto have multiple configuration items the same XML page. Useful to access lists, users lists, multi daemon configurations, etc.:<adddeleteeditpagefields> <columnitem> <fielddescr>Alias</fielddescr> <fieldname>aliasname</fieldname> </columnitem> <columnitem> <fielddescr>Description</fielddescr> <fieldname>description</fieldname> </columnitem> <columnitem> <fielddescr>Action</fielddescr> <fieldname>action</fieldname> </columnitem> <columnitem> <fielddescr>Update Frequency</fielddescr> <fieldname>cron</fieldname> </columnitem> </adddeleteeditpagefields>
Binaries from FreeBSD¶
The actual binaries are normal FreeBSD package binaries for that particular program. Once listed as a dependency for a pfSense package in its Makefile, the builders compile the dependencies automatically and copy them to the pfSense software package servers. There is no need to specify these in XML.
Updating Packages¶
When updating a package is it important to bump the version in its Makefile otherwise the package will not be rebuilt and made available to others.
Repository Branches¶
When submitting changes, they are typically submitted to the devel branch of
the FreeBSD-Ports Repository. In order to show to all users, the changes must
be placed in the current release branch as well, such as
RELENG_2_8_0.
Ideally, the changes should be submitted to the development branches and tested on systems pulling packages from the development repository. Once the changes have been tested, they can be placed into the release branch for deployment to a wider audience.
Testing/Building Individual Packages¶
Archive files from pkg may be copied to the firewall and added with pkg
directly. The good thing about using pkg is that the GUI packages and CLI
packages are all the same. Files for pfSense software packages are all kept
together inside the archive, dependencies such as FreeBSD packages are in
separate archives.
The package may be compiled on a local FreeBSD 15.0-CURRENT@bf06074106cf
builder, then pkg delete the old version and then pkg add the new one or
use any other pkg operations needed.
For example, a basic package like Cron is pfSense-pkg-Cron-0.3.3, so if
a new copy is built and put on the firewall:
# pkg add /path/to/file/pfSense-pkg-Cron-0.3.3.txz
It will also work with pkg add and a URL to an HTTP or HTTPS web server.
The process for making a package is:
Check out the FreeBSD-Ports Repository.
Make changes
Run
make package:$ git clone git@github.com:pfsense/FreeBSD-ports.git pfSense-ports $ cd pfSense-ports/blah/pfSense-pkg-foo/ [hack, hack, hack] $ make package (might need sudo) $ scp work/pkg/pfSense-pkg-foo* root@myfirewall:.
And then on the firewall:
# pkg add pfSense-pkg-foo-<version>.txz
Poudriere could also be setup for a custom repository but in most cases that will be overkill.
There are additional considerations when adding files, like updating the plist, and crafting a new pfSense package from scratch may be tricky if there is no prior knowledge of how the FreeBSD ports tree works.