[To Section 1 - The OpenBSD Ports System] [To Section 3 - Special Porting Topics]
The most important thing to do is to communicate. Ask people on ports@openbsd.org if they are working on the same port. Tell the original software authors about it, including problems you may find. If licensing information appears incorrect tell them. If you had to jump through hoops to make the port build, tell them what can be fixed. If they are only developing on Linux and feel like ignoring the rest of the Unix world, try to make them change their view.
COMMUNICATION makes the difference between a successful port and a port that will slowly be abandoned by everyone.
First look at the porting information on this page. Test, then re-test, and finally test again!
OpenBSD now fully supports updates. This means that some issues must be taken into account.
Submit the port. Create a gzipped tarball of the port directory. You can then either place it on a public FTP or HTTP server, sending its URL to ports@openbsd.org or send the port MIME encoded to the same address. Pick whichever method works best for you.
Porting some new software takes time. Maintaining it over time is harder. It is quite okay to port software, and let other people handle it afterwards. It is also okay to help other people update and maintain other ports, as long as you communicate to avoid doing the same things twice.
In the OpenBSD culture, MAINTAINERship is not a status item,
but a responsibility. We have CVS and comments to give credit to the
person who did the work. A port MAINTAINER is something else:
a person who assumes responsibility for the working of the port, and is
willing to spend some time ensuring it works as best as can be.
/usr/ports/, pick a primary category for your port.
Create a new directory below /usr/ports/<category>/
or /usr/ports/mystuff/<category>/ and create the basic
infrastructure there. Copy the template Makefile from
/usr/ports/infrastructure/templates/Makefile.template.
Fill in CATEGORIES with the primary category you have chosen.
For more complex ports, you have more options and tools available to you:
Set these values to Yes if it is permitted or to a comment string stating why
it is not. Pay attention to any special conditions you may need to fulfill
later on. E.g., some ports require to install a copy of the license. We
recommend you place the license in
/usr/local/share/doc/<name>/.
In addition to the PERMIT_* values, put a license marker like
# License above them as a comment, this way we know why
the PERMIT_* values are set the way they are.
scripts/. This will be run before any configuration
specified by CONFIGURE_STYLE is run.
SEPARATE_BUILD.
CONFIGURE_STYLE=gnu can),
and may help people who mount their ports tree on several arches.
configure most of the time.
Note: make sure host-dependent files go in /etc or
/etc/<name>, but NEVER REPLACE OR MODIFY
existing files in /etc. Best to have install place them
in /usr/local/share/<name> and then copy to
/etc or /etc/<name> only if the files do not exist.
If the files exist, display a message that says such-and-such files need
to be modified.
This also guarantees that the files will be included in
the package since everything under /usr/local is included in the PLIST.
To handle the copying carefully,
the @sample keyword is preferably used within the PLIST.
After a package has been installed the contents of pkg/MESSAGE
will be displayed if it exists.
The OpenBSD file locations are:
user executables: /usr/local/bin system admin executables: /usr/local/sbin program executables: /usr/local/libexec libraries: /usr/local/lib architecture dependent data: /usr/local/lib/<name> installed include files: /usr/local/include or /usr/local/include/<name> single-machine data: /etc or /etc/<name> local state: /var/run games score files: /var/games GNU info files: /usr/local/info man pages: /usr/local/man/... read-only architecture-independent: /usr/local/share/<name> misc documentation: /usr/local/share/doc/<name> examples files: /usr/local/share/examples/<name>
SEPARATE_BUILD=concurrent -- someone can use the same
source tree to build on distinct arches simultaneously.
Otherwise, set SEPARATE_BUILD=simple -- building on
distinct arches simultaneously may be met with problems, as some
source files may be regenerated at awkward moments.
/usr/ports/infrastructure/db/user.list for your port to
use and make sure your port gets added to this file at commit time.
${INSTALL_STRIP}.
${INSTALL_PROGRAM} honors this automatically and is
preferable to unconditional stripping (e.g., by an "install-strip"
target or by running "strip" from the Makefile). You can use
file(1) to determine if a binary is stripped or not.
pkg/SECURITY file. This file
should list audited potential problems, along with relevant patches,
so that another person can see at first glance what has been done.
Example:
$OpenBSD$
${WRKDIR}/receiver.c
call to mktemp (wrapper function do_mktemp) does seem to be correct.
The server makes extensive use of strlcpy/strlcat/snprintf.
/etc/mtree directory is up-to-date.
(The next step uses the mtree lists to remove existing directories from
generated packing-lists). Remember that the OpenBSD
(U)pdate does not touch /etc...
For automatic updating of /etc, sysmerge(8) may help.
Peruse `PLIST' and verify that everything was installed and that it was installed in the proper locations. Anything not installed can be added to a port Makefile `post-install' rule.
Ports that install shared libraries will have another file called PFRAG.shared.
LIB_DEPENDS, Re-run make plist. It
is possible some directories do not need to be in the PLIST, as they've
been installed by a dependency.
/usr/ports/packages/`arch -s`/all/
in the environment.
make port-lib-depends-check
and add every LIB_DEPENDS or WANTLIB annotation
that is needed until it runs cleanly.
You may want to read
the update guidelines
to understand why this is so important.
NO_REGRESS=Yes if a port has no test infrastructure.
If dependencies are required to run the tests, but not to build the
port, list them in REGRESS_DEPENDS.
Please note: do not set NO_REGRESS if a port has an empty
regression infrastructure.
Try to get others to test it on a variety of platforms for you.
sizeof(int) != sizeof(long) on this platform.
/usr/ports/infrastructure/db/user.list
since the port was created.
We normally use "import" for a new port,
rather than adding a zillion (or a dozen) files individually.
Import uses "vendor branch" version numbers like 1.1.1.1, but don't worry
about that! :-) If you make changes to a specific file (edit, then
cvs commit), it will be 1.2, and that will be used.
In short, import is typically used when a port is created. From that point on cvs add and cvs rm are typically used to add or remove files, and the normal edit->commit cycle for changes. You might use something like this:
$ cd kaffe1 $ make clean # you really don't want to check in all of work! $ cvs -d cvs.openbsd.org:/cvs import -m 'kaffe port' ports/lang/kaffe1 \ YourName YourName_YYYY-MMM-DD
$ cd kaffe1 $ make clean >/dev/null $ cvs import -m 'kaffe1.0(==JDK1.1) port' ports/lang/kaffe1 ian ian_1998-Sep-08 ian@cvs.openbsd.org's password: (not shown, obviously) N ports/lang/kaffe1/Makefile cvs server: Importing /cvs/ports/lang/kaffe1/files N ports/lang/kaffe1/files/md5 cvs server: Importing /cvs/ports/lang/kaffe1/pkg N ports/lang/kaffe1/pkg/COMMENT N ports/lang/kaffe1/pkg/DESCR N ports/lang/kaffe1/pkg/PLIST No conflicts created by this import $
/usr/ports/infrastructure/db/user.list.
port-lib-depends-check to see what libraries your software
needs (that will end up in LIB_DEPENDS or WANTLIB, usually). Identify
various files and binaries in the dependencies that have to be present
for the port to work.
By this point, you should have a fair understanding of your port's working.
Considering all possible options, you should be left with a much smaller set of options for your port, mostly depending on what packages are needed to run the software. For now, do not worry about build dependencies. Remember that the OpenBSD ports system is focused on the end user, and the end user will want to install binary packages, so it doesn't matter if you need a huge piece of software to build your port if it doesn't show up as a library or runtime dependency.
In this case, try setting the MULTI_PACKAGES variable to a list of -sub packages, add corresponding COMMENTS, and look at your packaging. Basically, MULTI_PACKAGES only affects the packaging: if you have MULTI_PACKAGES=-s1 -s2 all stuff relevant to the package will exist in two variants: COMMENT-s1 for the first package, COMMENT-s2 for the second package, PLIST-s1, PLIST-s2, DESCR-s1, DESCR-s2. You need to write those COMMENT-s1 and COMMENT-s2 in the Makefile, and to split your PLIST into two parts, and to create DESCR-s1/DESCR-s2. You will also need to specify separate PKGNAMEs for all subpackages.
It is a good idea to start with the minimal framework work required: just copy the existing description and comments, because you will have to fiddle with MULTI_PACKAGES and stuff before you polish this.
Once you've separated the files properly, you will need to check dependencies: LIB_DEPENDS, WANTLIB, and RUN_DEPENDS will be split for each subpackage. It is usually time to check that your multi-packaging "works", and that those nasty dependencies you don't want to force on the user are indeed relegated to a specific subpackage.
Assuming everything works, you're mostly done. Just pick reasonable names for the various packages, and fill in the comments and descriptions. The end-user will be able to just install the package(s) they want.
But wait. What about the build, you say? Well, having a lot of dependencies during build is not a problem. Most packages are built by the OpenBSD team using special build runs (known as bulk builds) where a developer just builds all possible packages on a dedicated machine (or several, for slow architectures). Since everything will get built, having big dependencies is not an issue. Building the same thing several times, is an issue, though, which is why MULTI_PACKAGES are the best way to handle options (when possible): one build, one set of packages to test, better quality overall...
If you also want to help people who build packages themselves, you may consider adding PSEUDO_FLAVORS. A pseudo-flavor is a way to tweak an option (say, disable the graphical interface) that's very similar to actual flavors. In fact, the biggest difference is a functional difference: a pseudo flavor should only affect the set of packages that get built, but it is never allowed to modify the actual package contents.
For instance, assuming you separated the graphical interface into a separate subpackage (MULTI_PACKAGES=-core -x11), you could create a pseudo flavor no_x11 that avoids building the -x11 subpackage. The crucial point is that this flavor should NOT affect the -core package in any way.
You would end up with a Makefile that looks something like this:
CATEGORIES = app
COMMENT-core = foo core application
COMMENT-x11 = foo graphical interface
V = 1.0
DISTNAME = foo-1.0
PKGNAME-core = foo-core-$V
PKGNAME-x11 = foo-x11-$V
PSEUDO_FLAVORS = no_x11
FLAVOR ?=
CONFIGURE_STYLE = gnu
MULTI_PACKAGES = -core
WANTLIB = c m crypto ssl
WANTLIB-x11 = ${WANTLIB} X11 Xt
RUN_DEPENDS-x11 = ::${BASE_PKGPATH},-core
.if ${FLAVOR:L:Mno_x11}
CONFIGURE_ARGS += --disable-x11
.else
MULTI_PACKAGES += -x11
.endif
.include <bsd.port.mk>
Notice that you only have to write a very small conditional section in the
Makefile: the system doesn't care at all that you define extra variables.
Specific attention must be provided to library inter-dependencies: they cannot be specified as WANTLIB, since WANTLIB do not contain any marker that says which package they come from. Rather, they must be specified as LIB_DEPENDS. Normal LIB_DEPENDS are checked at the start of build, and during packaging. If a LIB_DEPENDS specifies one of the subpackages currently being built, then the infrastructure will detect this and only check the dependency at packaging time (and thus packages may be created in a specific order to satisfy interdependencies).
The infrastructure provides specific variables to help in writing inter-dependencies: BUILD_PKGPATH contains the PKGPATH used during building the current packages, taking flavors and pseudo-flavors into account. It is highly recommended to use this variable instead of rolling your own: failure to do so will often trigger rebuilds in interesting flavors situations. For instance:
... FLAVORS = a b FLAVOR ?= MULTI_PACKAGES = -p1 -p2 LIB_DEPENDS-p1 = foo::some/pkgpath,-p2 ...If you go on and build in some/pkgpath with FLAVOR=a, then creating the subpackage for -p1 will trigger a rebuild with FLAVOR=''. You would write
LIB_DEPENDS-p1 = foo:${BUILD_PKGPATH},-p2
instead.
There is also a BASE_PKGPATH variable, which does not take pseudo-flavors into account. This variable has limited applicability: it corresponds to a transition between old MULTI_PACKAGES and newer ones, where the old "main" subpackage did not have any marker in its pkgpath, and thus the corresponding package needs a @pkgpath ${BASE_PKGPATH} in its packing-list. (In general, pseudo-flavors are build information, and should not make their way into packages and packing-lists).
PKGNAME = foo-1.0 FLAVORS = f1 f2 f3and you build the port with FLAVOR='f3 f1', then FULLPKGNAME=foo-1.0-f1-f3 (FLAVORS is used to reorder specified FLAVORS in a canonical way).
There are sometimes mixed situations, where some packages do depend on the FLAVOR, and some don't. For instance, some ports include a large set of documentation that does not depend on the FLAVOR, and some actual programs that depend on the FLAVOR. In such cases, you can specify the FULLPKGNAME for the documentation subpackage explicitly. E.g., something like this:
CATEGORIES = app
COMMENT-core = foo application
COMMENT-doc = foo documentation
V = 1.0
DISTNAME = foo-1.0
PKGNAME-core = foo-$V
FULLPKGNAME-doc = foo-doc-$V
FLAVORS = crypto
MULTI_PACKAGES = -core -doc
WANTLIB-core = c m
.if ${FLAVOR:L:Mcrypto}
WANTLIB-core += ssl crypto
CONFIGURE_ARGS += --enable-crypto
.endif
As mentioned in the documentation, all package names have the same structure:
stem-version-flavor_extension.
By default, packages with the same stem do conflict, and update paths will look at candidates with the same stem. The right package will be the one coming from the exact same PKGPATH, or matching @pkgpath annotation in the packing-list.
Usually, MULTI_PACKAGES should not conflict, so they must have different names (and the infrastructure has no way to build those names). On the other hand, flavors should conflict, and thus have the same name. The flavor information should end at the end of the package name, except for pseudo-flavors, which do not change the way a package is built.
As far as dependencies go, by default, specifying a PKGPATH will just create a stem-* dependency, meaning any package with the right stem will match the dependency. By default, any flavor will match. If only specific flavors are desired, you must include them in your specification, e.g., stem-*-flavor. If some flavors are unwanted, you can remove them from matching packages, e.g., stem-*-!flavor.
Since OpenBSD 3.8, pkg_add(1) can update packages. So maintainers have to be aware of one simple fact: update is not instantaneous. Even if a user just goes from release to release, each time they run pkg_add -ui, the system will replace each package with a new version, one package at a time. So the user will run a mixed system, even if it is for just a few minutes.
There are basically two update models of which maintainers must be aware.
You should note that part of the update process, especially the macroscopic changes for users who update every six months, is not yet automated. The global update mechanism is still a work in progress, and pkg_add will be able to cope with more issues in the future. As of now, you should focus on making sure the system updates correctly, one port at a time, and that one port's update takes other ports into account, as far as conflicts and other issues are concerned.
SHARED_LIBS setup. This will help during updates.
PLIST_DB and build a database of packing-lists.
This is useful to find out about forgotten package name bumps, and also
to check for conflicts with older packages.
HOMEPAGE,
MAINTAINER or description changes.
make patch to have an initial copy of the port before
the update.
make makesum and make patch as a starting basis.
pkg_add -ui, make sure they are visible in the package.
When your packaging policy changes, document it in the package description.
For instance, if you move the documentation into a separate package for
space constraints, the main package description should mention that the
documentation is now available as a separate package.
/usr/local/etc/rc.d./usr/local is often shared between several machines
thanks to NFS. For this reason, configuration files that are specific
to a given machine can't be stored under /usr/local,
/etc is the central repository for per machine
configuration files. Moreover, OpenBSD policy is to never update
files under /etc automatically. Ports that need some
specific boot setup should advise the administrator about what to do
instead of blindly installing files.
-lcrypt.libc.
/usr/ports/infrastructure/db/user.list for details.
$OpenBSD$ CVS tag to
the Makefile. If importing a port from another system be sure to
leave their tag in the Makefile, too.
strcat/strcpy/strcmp/sprintf. In general,
sprintf should be replaced with snprintf.
/tmp with symbolic links to more strategic files, such as
/etc/master.passwd.
fopen and freopen
create a new file or open an existing file for
writing. An attacker may create a symbolic link from
/etc/master.passwd to /tmp/addrpool_dump. The
instant you open it, your password file is hosed. Yes, even with
an unlink right before. You only narrow the window
of opportunity. Use open with
O_CREAT|O_EXCL and fdopen instead.
mktemp
function. Heed the warnings of the bsd linker about its uses.
These must be fixed.
This is not quite as simple as s/mktemp/mkstemp/g. mktemp(3) for more information.
Correct code using mkstemp includes the source to
ed or mail.
A rare instance of code that uses mktemp correctly
can be found in the rsync port.
startx problem. As a setuid program,
you could launch startx with any file as a script. If the file was not
a valid shell script, a syntax error message would follow, along with the
first line of the offending file, without any further permission check.
Pretty handy to grab the first line of a shadow passwd file, considering
these often start with root entry. Do not open your file, and then do
an fstat on the open descriptor to check if you should have
been able to open it (or the attacker will play with /dev/rst0 and rewind
your tape) -- open it with the correct uid/gid/grouplist set.
popen and
system.
Use fork, pipe and execve instead.
/dev/fd/0.
inetd
control and just add the relevant entries to inetd.conf.
You must know the appropriate magic for writing daemons to achieve that.
It could be argued that you have no business writing setuid programs
if you don't know how to do that.
xkobo port for an instance of such a change.
PATH (never use system with an
unqualified name, avoid execvp), but also more subtle
items such as your locale, timezone, termcap, and so on.
Be aware of transitivity: even though you're taking full precautions,
programs you call directly won't necessarily. Never
use system in privileged programs, build your command
line, a controlled environment, and call execve directly.
The perlsec man page is a good tutorial on such problems.
issetugid addresses this problem, from the
library writer point of view. Don't try to port libraries unless you
understand this issue thoroughly.
__OpenBSD__ should be used sparingly, if at all.
Constructs that look like
#if defined(__NetBSD__) || defined(__FreeBSD__)
are often inappropriate. Don't add blindly __OpenBSD__
to it. Instead, try to figure out what's going on, and what actual
feature is needed. Manual pages are often useful, as they include
historic comments, stating when a particular feature was incorporated
into BSD. Checking the numeric value of BSD against known
releases is often the right way. See
The NetBSD pkgsrc guide
for more information.
BSD is a bad idea. Try to include sys/param.h.
This not only defines BSD, it also gives it a proper value.
The right code fragment should look like:
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif
tcgetattr works than whether
you're running under BSD 4.3 or later, or SystemVR4. These kind of
tests just confuse the issue. The way to go about it is, for instance,
to test for one particular system, set up a slew of
HAVE_TCGETATTR defines, then proceed to the next system.
This technique separates features tests from specific OSes.
In a hurry, another porter can just add the whole set of
-DHAVE_XXX defines to the Makefile. One may also write
or add to a configure script to check for that feature and add it
automatically. As an example not to follow, check nethack 3.2.2
source: it assumes loads of things based on the system type. Most
of these assumptions are obsolete and no longer reflect reality:
POSIX functions are more useful than older BSD versus SystemV
differences, to the point that some traditional bsd functions are
now only supported through compatibility libraries.
#define POSIX_C_SOURCE
throughout the whole project, not when you feel like it.
unistd.h, fcntl.h or
termios.h.
The man page frequently states where the prototype can be found.
You might need another slew of HAVE_XXX macros to
procure the right file. Don't worry about including the same file
twice, include files have guards that prevent all kinds of nastiness.unsigned long instead of
size_t), or get some const status wrong.
Also, some compilers, such as OpenBSD's own gcc,
are able to do a better job with some very frequent functions such as
strlen if you include the right header file.
implicit declaration of function foo()
indicates that a function prototype is missing.
This means that the compiler will assume the return type
to be an integer.
Where the function actually returns a pointer, on 64-bit
platforms it will be truncated, usually causing a segmentation
fault.
/* prototype part */
#ifdef USE_OWN_GCVT
char *foo_gcvt(double number, size_t ndigit, char *buf);
#else
/* include correct file */
#include <stdlib.h>
/* use system function */
#define foo_gcvt gcvt
#endif
/* definition part */
#ifdef USE_OWN_GCVT
char *foo_gcvt(double number, size_t ndigit, char *buf)
{
/* proper definition */
}
/* typical use */
s = foo_gcvt(n, 15, b);
bsd.port.mk set the installers
path. Specifically, they set /usr/bin and
/bin to be searched before
/usr/local/bin and /usr/X11R6/bin.
${NO_SHARED_LIBS} is set to yes (beware, it can be defined
only after inclusion of bsd.port.mk). If your port has
a GNU configure simply add the line
CONFIGURE_ARGS += ${CONFIGURE_SHARED} to the Makefile.
bsd.port.mk, as people are supposed to update their
whole ports tree, including bsd.port.mk.
NEED_VERSION is now obsolete.
update-plist to generate and update
packing-lists instead of doing things manually.
You can comment unwanted lines out.
update-plist can detect most file types and copy most
extra annotations correctly.
USE_SYSTRACE=Yes to /etc/mk.conf to
detect misbehaving scripts, makefiles, etc.
curses.h/libcurses/libtermlib are the
``new curses''. Change:ncurses.h ==> curses.h_USE_OLD_CURSES_
before including curses.h (usually in a Makefile) and
linking with -locurses.
sgtty to the newer POSIX tcgetattr family.
Avoid the older style in new code. Some code may define
tcgetattr to be a synonym for the older
sgtty, but this is at best a stopgap measure on OpenBSD.
The xterm source code is a very good example of
what not to do. Try to get your system functionality right: you
want a type that remembers the state of your terminal
(possible typedef), you want a function that extracts the current
state, and a function that sets the new state.
Functions that modify this state are more difficult, as they tend
to vary depending upon the system. Also, don't forget that you will
have to handle cases where you are not connected to a terminal,
and that you need to handle signals: not only termination, but
also background (SIGTSTP). You should always leave
the terminal in a sane state. Do your tests under an older shell,
such as sh, which does not reset the terminal in any way at
program's termination.
TERMCAP variable and get it to work properly.
sigaction to ensure a specific semantics, along
with other system calls referenced in the corresponding manpage.
[To Section 1 - The OpenBSD Ports System] [To Section 3 - Special Porting Topics]