xref: /freebsd/stand/libsa/libsa.3 (revision 95eb4b873b6a8b527c5bd78d7191975dfca38998)
1.\" Copyright (c) Michael Smith
2.\" All rights reserved.
3.\"
4.\" Redistribution and use in source and binary forms, with or without
5.\" modification, are permitted provided that the following conditions
6.\" are met:
7.\" 1. Redistributions of source code must retain the above copyright
8.\"    notice, this list of conditions and the following disclaimer.
9.\" 2. Redistributions in binary form must reproduce the above copyright
10.\"    notice, this list of conditions and the following disclaimer in the
11.\"    documentation and/or other materials provided with the distribution.
12.\"
13.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23.\" SUCH DAMAGE.
24.\"
25.Dd September 9, 2022
26.Dt LIBSA 3
27.Os
28.Sh NAME
29.Nm libsa
30.Nd support library for standalone executables
31.Sh SYNOPSIS
32.In stand.h
33.Sh DESCRIPTION
34The
35.Nm
36library provides a set of supporting functions for standalone
37applications, mimicking where possible the standard
38.Bx
39programming
40environment.
41The following sections group these functions by kind.
42Unless specifically described here, see the corresponding section 3
43manpages for the given functions.
44.Sh STRING FUNCTIONS
45String functions are available as documented in
46.Xr string 3
47and
48.Xr bstring 3 .
49.Sh MEMORY ALLOCATION
50.Bl -hang -width 10n
51.It Xo
52.Ft "void *"
53.Fn malloc "size_t size"
54.Xc
55.Pp
56Allocate
57.Fa size
58bytes of memory from the heap using a best-fit algorithm.
59.It Xo
60.Ft void
61.Fn free "void *ptr"
62.Xc
63.Pp
64Free the allocated object at
65.Fa ptr .
66.It Xo
67.Ft void
68.Fn setheap "void *start" "void *limit"
69.Xc
70.Pp
71Initialise the heap.
72This function must be called before calling
73.Fn alloc
74for the first time.
75The region between
76.Fa start
77and
78.Fa limit
79will be used for the heap; attempting to allocate beyond this will result
80in a panic.
81.It Xo
82.Ft "char *"
83.Fn sbrk "int junk"
84.Xc
85.Pp
86Provides the behaviour of
87.Fn sbrk 0 ,
88i.e., returns the highest point that the heap has reached.
89This value can
90be used during testing to determine the actual heap usage.
91The
92.Fa junk
93argument is ignored.
94.El
95.Sh ENVIRONMENT
96A set of functions are provided for manipulating a flat variable space similar
97to the traditional shell-supported environment.
98Major enhancements are support
99for set/unset hook functions.
100.Bl -hang -width 10n
101.It Xo
102.Ft "char *"
103.Fn getenv "const char *name"
104.Xc
105.It Xo
106.Ft int
107.Fn setenv "const char *name" "const char *value" "int overwrite"
108.Xc
109.It Xo
110.Ft int
111.Fn putenv "char *string"
112.Xc
113.It Xo
114.Ft int
115.Fn unsetenv "const char *name"
116.Xc
117.Pp
118These functions behave similarly to their standard library counterparts.
119.It Xo
120.Ft "struct env_var *"
121.Fn env_getenv "const char *name"
122.Xc
123.Pp
124Looks up a variable in the environment and returns its entire
125data structure.
126.It Xo
127.Ft int
128.Fn env_setenv "const char *name" "int flags" "const void *value" "ev_sethook_t sethook" "ev_unsethook_t unsethook"
129.Xc
130.Pp
131Creates a new or sets an existing environment variable called
132.Fa name .
133If creating a new variable, the
134.Fa sethook
135and
136.Fa unsethook
137arguments may be specified.
138.Pp
139The set hook is invoked whenever an attempt
140is made to set the variable, unless the EV_NOHOOK flag is set.
141Typically
142a set hook will validate the
143.Fa value
144argument, and then call
145.Fn env_setenv
146again with EV_NOHOOK set to actually save the value.
147The predefined function
148.Fn env_noset
149may be specified to refuse all attempts to set a variable.
150.Pp
151The unset hook is invoked when an attempt is made to unset a variable.
152If it
153returns zero, the variable will be unset.
154The predefined function
155.Fa env_nounset
156may be used to prevent a variable being unset.
157.El
158.Sh STANDARD LIBRARY SUPPORT
159.Bl -hang -width 10n
160.It Xo
161.Ft int
162.Fn abs "int i"
163.Xc
164.It Xo
165.Ft int
166.Fn getopt "int argc" "char * const *argv" "const char *optstring"
167.Xc
168.It Xo
169.Ft long
170.Fn strtol "const char *nptr" "char **endptr" "int base"
171.Xc
172.It Xo
173.Ft long long
174.Fn strtoll "const char *nptr" "char **endptr" "int base"
175.Xc
176.It Xo
177.Ft long
178.Fn strtoul "const char *nptr" "char **endptr" "int base"
179.Xc
180.It Xo
181.Ft long long
182.Fn strtoull "const char *nptr" "char **endptr" "int base"
183.Xc
184.It Xo
185.Ft void
186.Fn srandom "unsigned int seed"
187.Xc
188.It Xo
189.Ft "long"
190.Fn random void
191.Xc
192.It Xo
193.Ft "char *"
194.Fn strerror "int error"
195.Xc
196.Pp
197Returns error messages for the subset of errno values supported by
198.Nm .
199.It Fn assert expression
200.Pp
201Requires
202.In assert.h .
203.It Xo
204.Ft int
205.Fn setjmp "jmp_buf env"
206.Xc
207.It Xo
208.Ft void
209.Fn longjmp "jmp_buf env" "int val"
210.Xc
211.Pp
212Defined as
213.Fn _setjmp
214and
215.Fn _longjmp
216respectively as there is no signal state to manipulate.
217Requires
218.In setjmp.h .
219.El
220.Sh CHARACTER I/O
221.Bl -hang -width 10n
222.It Xo
223.Ft void
224.Fn gets "char *buf"
225.Xc
226.Pp
227Read characters from the console into
228.Fa buf .
229All of the standard cautions apply to this function.
230.It Xo
231.Ft void
232.Fn ngets "char *buf" "int size"
233.Xc
234.Pp
235Read at most
236.Fa size
237- 1 characters from the console into
238.Fa buf .
239If
240.Fa size
241is less than 1, the function's behaviour is as for
242.Fn gets .
243.It Xo
244.Ft int
245.Fn fgetstr "char *buf" "int size" "int fd"
246.Xc
247.Pp
248Read a line of at most
249.Fa size
250characters into
251.Fa buf .
252Line terminating characters are stripped, and the buffer is always
253.Dv NUL
254terminated.
255Returns the number of characters in
256.Fa buf
257if successful, or -1 if a read error occurs.
258.It Xo
259.Ft int
260.Fn printf "const char *fmt" "..."
261.Xc
262.It Xo
263.Ft void
264.Fn vprintf "const char *fmt" "va_list ap"
265.Xc
266.It Xo
267.Ft int
268.Fn sprintf "char *buf" "const char *fmt" "..."
269.Xc
270.It Xo
271.Ft void
272.Fn vsprintf "char *buf" "const char *fmt" "va_list ap"
273.Xc
274.Pp
275The *printf functions implement a subset of the standard
276.Fn printf
277family functionality and some extensions.
278The following standard conversions
279are supported: c,d,n,o,p,s,u,x.
280The following modifiers are supported:
281+,-,#,*,0,field width,precision,l.
282.Pp
283The
284.Li b
285conversion is provided to decode error registers.
286Its usage is:
287.Bd -ragged -offset indent
288printf(
289.Qq reg=%b\en ,
290regval,
291.Qq <base><arg>*
292);
293.Ed
294.Pp
295where <base> is the output expressed as a control character, e.g.\& \e10 gives
296octal, \e20 gives hex.
297Each <arg> is a sequence of characters, the first of
298which gives the bit number to be inspected (origin 1) and the next characters
299(up to a character less than 32) give the text to be displayed if the bit is set.
300Thus
301.Bd -ragged -offset indent
302printf(
303.Qq reg=%b\en ,
3043,
305.Qq \e10\e2BITTWO\e1BITONE
306);
307.Ed
308.Pp
309would give the output
310.Bd -ragged -offset indent
311reg=3<BITTWO,BITONE>
312.Ed
313.Pp
314The
315.Li D
316conversion provides a hexdump facility, e.g.
317.Bd -ragged -offset indent
318printf(
319.Qq %6D ,
320ptr,
321.Qq \&:
322); gives
323.Qq XX:XX:XX:XX:XX:XX
324.Ed
325.Bd -ragged -offset indent
326printf(
327.Qq %*D ,
328len,
329ptr,
330.Qq "\ "
331); gives
332.Qq XX XX XX ...
333.Ed
334.El
335.Sh CHARACTER TESTS AND CONVERSIONS
336.Bl -hang -width 10n
337.It Xo
338.Ft int
339.Fn isupper "int c"
340.Xc
341.It Xo
342.Ft int
343.Fn islower "int c"
344.Xc
345.It Xo
346.Ft int
347.Fn isspace "int c"
348.Xc
349.It Xo
350.Ft int
351.Fn isdigit "int c"
352.Xc
353.It Xo
354.Ft int
355.Fn isxdigit "int c"
356.Xc
357.It Xo
358.Ft int
359.Fn isascii "int c"
360.Xc
361.It Xo
362.Ft int
363.Fn isalpha "int c"
364.Xc
365.It Xo
366.Ft int
367.Fn isalnum "int c"
368.Xc
369.It Xo
370.Ft int
371.Fn iscntrl "int c"
372.Xc
373.It Xo
374.Ft int
375.Fn isgraph "int c"
376.Xc
377.It Xo
378.Ft int
379.Fn ispunct "int c"
380.Xc
381.It Xo
382.Ft int
383.Fn toupper "int c"
384.Xc
385.It Xo
386.Ft int
387.Fn tolower "int c"
388.Xc
389.El
390.Sh FILE I/O
391.Bl -hang -width 10n
392.It Xo
393.Ft int
394.Fn open "const char *path" "int flags"
395.Xc
396.Pp
397Similar to the behaviour as specified in
398.Xr open 2 ,
399except that file creation is not supported, so the mode parameter is not
400required.
401The
402.Fa flags
403argument may be one of O_RDONLY, O_WRONLY and O_RDWR.
404Only UFS currently supports writing.
405.It Xo
406.Ft int
407.Fn close "int fd"
408.Xc
409.It Xo
410.Ft void
411.Fn closeall void
412.Xc
413.Pp
414Close all open files.
415.It Xo
416.Ft ssize_t
417.Fn read "int fd" "void *buf" "size_t len"
418.Xc
419.It Xo
420.Ft ssize_t
421.Fn write "int fd" "void *buf" "size_t len"
422.Xc
423.Pp
424(No file systems currently support writing.)
425.It Xo
426.Ft off_t
427.Fn lseek "int fd" "off_t offset" "int whence"
428.Xc
429.Pp
430Files being automatically uncompressed during reading cannot seek backwards
431from the current point.
432.It Xo
433.Ft int
434.Fn stat "const char *path" "struct stat *sb"
435.Xc
436.It Xo
437.Ft int
438.Fn fstat "int fd" "struct stat *sb"
439.Xc
440.Pp
441The
442.Fn stat
443and
444.Fn fstat
445functions only fill out the following fields in the
446.Fa sb
447structure: st_mode,st_nlink,st_uid,st_gid,st_size.
448The
449.Nm tftp
450file system cannot provide meaningful values for this call, and the
451.Nm cd9660
452file system always reports files having uid/gid of zero.
453.El
454.Sh PAGER
455The
456.Nm
457library supplies a simple internal pager to ease reading the output of large
458commands.
459.Bl -hang -width 10n
460.It Xo
461.Ft void
462.Fn pager_open
463.Xc
464.Pp
465Initialises the pager and tells it that the next line output will be the top of the
466display.
467The environment variable LINES is consulted to determine the number of
468lines to be displayed before pausing.
469.It Xo
470.Ft void
471.Fn pager_close void
472.Xc
473.Pp
474Closes the pager.
475.It Xo
476.Ft int
477.Fn pager_output "const char *lines"
478.Xc
479.Pp
480Sends the lines in the
481.Dv NUL Ns
482-terminated buffer at
483.Fa lines
484to the pager.
485Newline characters are counted in order to determine the number
486of lines being output (wrapped lines are not accounted for).
487The
488.Fn pager_output
489function will return zero when all of the lines have been output, or nonzero
490if the display was paused and the user elected to quit.
491.It Xo
492.Ft int
493.Fn pager_file "const char *fname"
494.Xc
495.Pp
496Attempts to open and display the file
497.Fa fname .
498Returns -1 on error, 0 at EOF, or 1 if the user elects to quit while reading.
499.El
500.Sh FEATURE SUPPORT
501A set of functions are provided to communicate support of features from the
502loader binary to the interpreter.
503These are used to do something sensible if we are still operating with a loader
504binary that behaves differently than expected.
505.Bl -hang -width 10n
506.It Xo
507.Ft void
508.Fn feature_enable "uint32_t mask"
509.Xc
510.Pp
511Enable the referenced
512.Fa mask
513feature, which should be one of the
514.Li FEATURE_*
515macros defined in
516.In stand.h .
517.It Xo
518.Ft bool
519.Fn feature_name_is_enabled "const char *name"
520.Xc
521.Pp
522Check if the referenced
523.Fa name
524feature is enabled.
525The
526.Fa name
527is usually the same name as the
528.Li FEATURE_*
529macro, but with the FEATURE_ prefix stripped off.
530The authoritative source of feature names is the mapping table near the top in
531.Pa stand/libsa/features.c .
532.It Xo
533.Ft void
534.Fn "(feature_iter_fn)" "void *cookie" "const char *name" "const char *desc" "bool enabled"
535.Xc
536.Pp
537The
538.Fa cookie
539argument is passed as-is from the argument of the same name to
540.Fn feature_iter .
541The
542.Fa name
543and
544.Fa desc
545arguments are defined in the mapping table in
546.Pa stand/libsa/features.c .
547The
548.Fa enabled
549argument indicates the current status of the feature, though one could
550theoretically turn a feature off in later execution.
551As such, this should likely not be trusted if it is needed after the iteration
552has finished.
553.It Xo
554.Ft void
555.Fn "feature_iter" "feature_iter_fn *iter_fn" "void *cookie"
556.Xc
557.Pp
558Iterate over the current set of features.
559.El
560.Sh MISC
561.Bl -hang -width 10n
562.It Xo
563.Ft char *
564.Fn devformat "struct devdesc *"
565.Xc
566.Pp
567Format the specified device as a string.
568.It Xo
569.Ft int
570.Fn devparse "struct devdesc **dev" "const char *devdesc" "const char **path"
571.Xc
572.Pp
573Parse the
574.Dv devdesc
575string of the form
576.Sq device:[/path/to/file] .
577The
578.Dv devsw
579table is used to match the start of the
580.Sq device
581string with
582.Fa dv_name .
583If
584.Fa dv_parsedev
585is non-NULL, then it will be called to parse the rest of the string and allocate
586the
587.Dv struct devdesc
588for this path.
589If NULL, then a default routine will be called that will allocate a simple
590.Dv struct devdesc ,
591parse a unit number and ensure there's no trailing characters.
592If
593.Dv path
594is non-NULL, then a pointer to the remainder of the
595.Dv devdesc
596string after the device specification is written.
597.It Xo
598.Ft int
599.Fn devinit void
600Calls all the
601.Fa dv_init
602routines in the
603.Dv devsw
604array, returning the number of routines that returned an error.
605.It Xo
606.Ft void
607.Fn twiddle void
608.Xc
609.Pp
610Successive calls emit the characters in the sequence |,/,-,\\ followed by a
611backspace in order to provide reassurance to the user.
612.El
613.Sh REQUIRED LOW-LEVEL SUPPORT
614The following resources are consumed by
615.Nm
616- stack, heap, console and devices.
617.Pp
618The stack must be established before
619.Nm
620functions can be invoked.
621Stack requirements vary depending on the functions
622and file systems used by the consumer and the support layer functions detailed
623below.
624.Pp
625The heap must be established before calling
626.Fn alloc
627or
628.Fn open
629by calling
630.Fn setheap .
631Heap usage will vary depending on the number of simultaneously open files,
632as well as client behaviour.
633Automatic decompression will allocate more
634than 64K of data per open file.
635.Pp
636Console access is performed via the
637.Fn getchar ,
638.Fn putchar
639and
640.Fn ischar
641functions detailed below.
642.Pp
643Device access is initiated via
644.Fn devopen
645and is performed through the
646.Fn dv_strategy ,
647.Fn dv_ioctl
648and
649.Fn dv_close
650functions in the device switch structure that
651.Fn devopen
652returns.
653.Pp
654The consumer must provide the following support functions:
655.Bl -hang -width 10n
656.It Xo
657.Ft int
658.Fn getchar void
659.Xc
660.Pp
661Return a character from the console, used by
662.Fn gets ,
663.Fn ngets
664and pager functions.
665.It Xo
666.Ft int
667.Fn ischar void
668.Xc
669.Pp
670Returns nonzero if a character is waiting from the console.
671.It Xo
672.Ft void
673.Fn putchar int
674.Xc
675.Pp
676Write a character to the console, used by
677.Fn gets ,
678.Fn ngets ,
679.Fn *printf ,
680.Fn panic
681and
682.Fn twiddle
683and thus by many other functions for debugging and informational output.
684.It Xo
685.Ft int
686.Fn devopen "struct open_file *of" "const char *name" "const char **file"
687.Xc
688.Pp
689Open the appropriate device for the file named in
690.Fa name ,
691returning in
692.Fa file
693a pointer to the remaining body of
694.Fa name
695which does not refer to the device.
696The
697.Va f_dev
698field in
699.Fa of
700will be set to point to the
701.Vt devsw
702structure for the opened device if successful.
703Device identifiers must
704always precede the path component, but may otherwise be arbitrarily formatted.
705Used by
706.Fn open
707and thus for all device-related I/O.
708.It Xo
709.Ft int
710.Fn devclose "struct open_file *of"
711.Xc
712.Pp
713Close the device allocated for
714.Fa of .
715The device driver itself will already have been called for the close; this call
716should clean up any allocation made by devopen only.
717.It Xo
718.Ft void
719.Fn __abort
720.Xc
721.Pp
722Calls
723.Fn panic
724with a fixed string.
725.It Xo
726.Ft void
727.Fn panic "const char *msg" "..."
728.Xc
729.Pp
730Signal a fatal and unrecoverable error condition.
731The
732.Fa msg ...
733arguments are as for
734.Fn printf .
735.El
736.Sh INTERNAL FILE SYSTEMS
737Internal file systems are enabled by the consumer exporting the array
738.Vt struct fs_ops *file_system[] ,
739which should be initialised with pointers
740to
741.Vt struct fs_ops
742structures.
743The following file system handlers are supplied by
744.Nm ,
745the consumer may supply other file systems of their own:
746.Bl -hang -width ".Va cd9660_fsops"
747.It Va ufs_fsops
748The
749.Bx
750UFS.
751.It Va ext2fs_fsops
752Linux ext2fs file system.
753.It Va tftp_fsops
754File access via TFTP.
755.It Va nfs_fsops
756File access via NFS.
757.It Va cd9660_fsops
758ISO 9660 (CD-ROM) file system.
759.It Va gzipfs_fsops
760Stacked file system supporting gzipped files.
761When trying the gzipfs file system,
762.Nm
763appends
764.Li .gz
765to the end of the filename, and then tries to locate the file using the other
766file systems.
767Placement of this file system in the
768.Va file_system[]
769array determines whether gzipped files will be opened in preference to non-gzipped
770files.
771It is only possible to seek a gzipped file forwards, and
772.Fn stat
773and
774.Fn fstat
775on gzipped files will report an invalid length.
776.It Va bzipfs_fsops
777The same as
778.Va gzipfs_fsops ,
779but for
780.Xr bzip2 1 Ns -compressed
781files.
782.El
783.Pp
784The array of
785.Vt struct fs_ops
786pointers should be terminated with a NULL.
787.Sh DEVICES
788Devices are exported by the supporting code via the array
789.Vt struct devsw *devsw[]
790which is a NULL terminated array of pointers to device switch structures.
791.Sh DRIVER INTERFACE
792The driver needs to provide a common set of entry points that are
793used by
794.Nm libsa
795to interface with the device.
796.Bd -literal
797struct devsw {
798    const char	dv_name[DEV_NAMLEN];
799    int		dv_type;
800    int		(*dv_init)(void);
801    int		(*dv_strategy)(void *devdata, int rw, daddr_t blk,
802			size_t size, char *buf, size_t *rsize);
803    int		(*dv_open)(struct open_file *f, ...);
804    int		(*dv_close)(struct open_file *f);
805    int		(*dv_ioctl)(struct open_file *f, u_long cmd, void *data);
806    int		(*dv_print)(int verbose);
807    void	(*dv_cleanup)(void);
808    char *	(*dv_fmtdev)(struct devdesc *);
809    int		(*dv_parsedev)(struct devdesc **dev, const char *devpart,
810    		const char **path);
811    bool	(*dv_match)(struct devsw *dv, const char *devspec);
812};
813.Ed
814.Bl -tag -width ".Fn dv_strategy"
815.It Fn dv_name
816The device's name.
817.It Fn dv_type
818Type of device.
819The supported types are:
820.Bl -tag -width "DEVT_NONE"
821.It DEVT_NONE
822.It DEVT_DISK
823.It DEVT_NET
824.It DEVT_CD
825.It DEVT_ZFS
826.It DEVT_FD
827.El
828Each type may have its own associated (struct type_devdesc),
829which has the generic (struct devdesc) as its first member.
830.It Fn dv_init
831Driver initialization routine.
832This routine should probe for available units.
833Drivers are responsible for maintaining lists of units for later enumeration.
834No other driver routines may be called before
835.Fn dv_init
836returns.
837.It Fn dv_open
838The driver open routine.
839.It Fn dv_close
840The driver close routine.
841.It Fn dv_ioctl
842The driver ioctl routine.
843.It Fn dv_print
844Prints information about the available devices.
845Information should be presented with
846.Fn pager_output .
847.It Fn dv_cleanup
848Cleans up any memory used by the device before the next stage is run.
849.It Fn dv_fmtdev
850Converts the specified devdesc to the canonical string representation
851for that device.
852.It Fn dv_parsedev
853Parses the device portion of a file path.
854The
855.Dv devpart
856will point to the
857.Sq tail
858of device name, possibly followed by a colon and a path within the device.
859The
860.Sq tail
861is, by convention, the part of the device specification that follows the
862.Fa dv_name
863part of the string.
864So when
865.Fa devparse
866is parsing the string
867.Dq disk3p5:/xxx ,
868.Dv devpart
869will point to the
870.Sq 3
871in that string.
872The parsing routine is expected to allocate a new
873.Dv struct devdesc
874or subclass and return it in
875.Dv dev
876when successful.
877This routine should set
878.Dv path
879to point to the portion of the string after device specification, or
880.Dq /xxx
881in the earlier example.
882Generally, code needing to parse a path will use
883.Fa devparse
884instead of calling this routine directly.
885.It Fn dv_match
886.Dv NULL
887to specify that all device paths starting with
888.Fa dv_name
889match.
890Otherwise, this function returns 0 for a match and a non-zero
891.Dv errno
892to indicate why it didn't match.
893This is helpful when you claim the device path after using it to query
894properties on systems that have uniform naming for different types of
895devices.
896.El
897.Sh HISTORY
898The
899.Nm
900library contains contributions from many sources, including:
901.Bl -bullet -compact
902.It
903.Nm libsa
904from
905.Nx
906.It
907.Nm libc
908and
909.Nm libkern
910from
911.Fx 3.0 .
912.It
913.Nm zalloc
914from
915.An Matthew Dillon Aq Mt dillon@backplane.com
916.El
917.Pp
918The reorganisation and port to
919.Fx 3.0 ,
920the environment functions and this manpage were written by
921.An Mike Smith Aq Mt msmith@FreeBSD.org .
922.Sh BUGS
923The lack of detailed memory usage data is unhelpful.
924