summaryrefslogtreecommitdiffstats
path: root/lib/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libutil')
-rw-r--r--lib/libutil/Makefile90
-rw-r--r--lib/libutil/Makefile.depend19
-rw-r--r--lib/libutil/_secure_path.375
-rw-r--r--lib/libutil/_secure_path.c74
-rw-r--r--lib/libutil/auth.c44
-rw-r--r--lib/libutil/expand_number.387
-rw-r--r--lib/libutil/expand_number.c93
-rw-r--r--lib/libutil/flopen.3101
-rw-r--r--lib/libutil/flopen.c105
-rw-r--r--lib/libutil/fparseln.3157
-rw-r--r--lib/libutil/fparseln.c228
-rw-r--r--lib/libutil/gr_util.c651
-rw-r--r--lib/libutil/hexdump.3109
-rw-r--r--lib/libutil/hexdump.c96
-rw-r--r--lib/libutil/humanize_number.3202
-rw-r--r--lib/libutil/humanize_number.c179
-rw-r--r--lib/libutil/kinfo_getallproc.375
-rw-r--r--lib/libutil/kinfo_getallproc.c98
-rw-r--r--lib/libutil/kinfo_getfile.380
-rw-r--r--lib/libutil/kinfo_getfile.c77
-rw-r--r--lib/libutil/kinfo_getproc.374
-rw-r--r--lib/libutil/kinfo_getproc.c71
-rw-r--r--lib/libutil/kinfo_getvmmap.379
-rw-r--r--lib/libutil/kinfo_getvmmap.c77
-rw-r--r--lib/libutil/kinfo_getvmobject.374
-rw-r--r--lib/libutil/kinfo_getvmobject.c94
-rw-r--r--lib/libutil/kld.393
-rw-r--r--lib/libutil/kld.c76
-rw-r--r--lib/libutil/libutil.h252
-rw-r--r--lib/libutil/login.conf.5471
-rw-r--r--lib/libutil/login_auth.372
-rw-r--r--lib/libutil/login_auth.c108
-rw-r--r--lib/libutil/login_cap.3579
-rw-r--r--lib/libutil/login_cap.c819
-rw-r--r--lib/libutil/login_cap.h167
-rw-r--r--lib/libutil/login_class.3222
-rw-r--r--lib/libutil/login_class.c565
-rw-r--r--lib/libutil/login_crypt.c50
-rw-r--r--lib/libutil/login_ok.3142
-rw-r--r--lib/libutil/login_ok.c250
-rw-r--r--lib/libutil/login_times.3179
-rw-r--r--lib/libutil/login_times.c162
-rw-r--r--lib/libutil/login_tty.365
-rw-r--r--lib/libutil/login_tty.c62
-rw-r--r--lib/libutil/pidfile.3297
-rw-r--r--lib/libutil/pidfile.c278
-rw-r--r--lib/libutil/property.396
-rw-r--r--lib/libutil/property.c259
-rw-r--r--lib/libutil/pty.3145
-rw-r--r--lib/libutil/pty.c117
-rw-r--r--lib/libutil/pw_util.3287
-rw-r--r--lib/libutil/pw_util.c664
-rw-r--r--lib/libutil/quotafile.3290
-rw-r--r--lib/libutil/quotafile.c595
-rw-r--r--lib/libutil/realhostname.3102
-rw-r--r--lib/libutil/realhostname.c200
-rw-r--r--lib/libutil/realhostname_sa.3132
-rw-r--r--lib/libutil/stub.c46
-rw-r--r--lib/libutil/tests/Makefile14
-rw-r--r--lib/libutil/tests/flopen_test.c210
-rw-r--r--lib/libutil/tests/grp_test.c117
-rw-r--r--lib/libutil/tests/humanize_number_test.c598
-rw-r--r--lib/libutil/tests/pidfile_test.c280
-rw-r--r--lib/libutil/tests/trimdomain-nodomain_test.c91
-rw-r--r--lib/libutil/tests/trimdomain_test.c91
-rw-r--r--lib/libutil/trimdomain.385
-rw-r--r--lib/libutil/trimdomain.c115
-rw-r--r--lib/libutil/uucplock.3183
-rw-r--r--lib/libutil/uucplock.c227
69 files changed, 12962 insertions, 0 deletions
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile
new file mode 100644
index 0000000..35d146a
--- /dev/null
+++ b/lib/libutil/Makefile
@@ -0,0 +1,90 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+SHLIBDIR?= /lib
+
+.include <src.opts.mk>
+
+LIB= util
+SHLIB_MAJOR= 9
+
+SRCS= _secure_path.c auth.c expand_number.c flopen.c fparseln.c gr_util.c \
+ hexdump.c humanize_number.c kinfo_getfile.c \
+ kinfo_getallproc.c kinfo_getproc.c kinfo_getvmmap.c \
+ kinfo_getvmobject.c kld.c \
+ login_auth.c login_cap.c \
+ login_class.c login_crypt.c login_ok.c login_times.c login_tty.c \
+ pidfile.c property.c pty.c pw_util.c quotafile.c realhostname.c \
+ stub.c trimdomain.c uucplock.c
+INCS= libutil.h login_cap.h
+
+CFLAGS+= -DLIBC_SCCS
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../libc/gen/
+
+MAN+= expand_number.3 flopen.3 fparseln.3 hexdump.3 \
+ humanize_number.3 kinfo_getallproc.3 kinfo_getfile.3 \
+ kinfo_getproc.3 kinfo_getvmmap.3 kinfo_getvmobject.3 kld.3 \
+ login_auth.3 login_cap.3 \
+ login_class.3 login_ok.3 login_times.3 login_tty.3 pidfile.3 \
+ property.3 pty.3 quotafile.3 realhostname.3 realhostname_sa.3 \
+ _secure_path.3 trimdomain.3 uucplock.3 pw_util.3
+MAN+= login.conf.5
+MLINKS+= kld.3 kld_isloaded.3 kld.3 kld_load.3
+MLINKS+=login_auth.3 auth_cat.3 login_auth.3 auth_checknologin.3
+MLINKS+=login_cap.3 login_close.3 login_cap.3 login_getcapbool.3 \
+ login_cap.3 login_getcaplist.3 login_cap.3 login_getcapnum.3 \
+ login_cap.3 login_getcapsize.3 login_cap.3 login_getcapstr.3 \
+ login_cap.3 login_getcaptime.3 login_cap.3 login_getclass.3 \
+ login_cap.3 login_getclassbyname.3 login_cap.3 login_getpath.3 \
+ login_cap.3 login_getpwclass.3 login_cap.3 login_getstyle.3 \
+ login_cap.3 login_getuserclass.3 login_cap.3 login_setcryptfmt.3
+MLINKS+=login_class.3 setclasscontext.3 login_class.3 setclassenvironment.3 \
+ login_class.3 setclassresources.3 login_class.3 setusercontext.3
+MLINKS+=login_ok.3 auth_hostok.3 login_ok.3 auth_timeok.3 \
+ login_ok.3 auth_ttyok.3
+MLINKS+=login_times.3 in_lt.3 login_times.3 in_ltm.3 \
+ login_times.3 in_ltms.3 \
+ login_times.3 in_lts.3 \
+ login_times.3 parse_lt.3
+MLINKS+=pidfile.3 pidfile_close.3 \
+ pidfile.3 pidfile_fileno.3 \
+ pidfile.3 pidfile_open.3 \
+ pidfile.3 pidfile_remove.3 \
+ pidfile.3 pidfile_write.3
+MLINKS+= property.3 property_find.3 property.3 properties_free.3
+MLINKS+= property.3 properties_read.3
+MLINKS+= pty.3 forkpty.3 pty.3 openpty.3
+MLINKS+=quotafile.3 quota_close.3 \
+ quotafile.3 quota_fsname.3 \
+ quotafile.3 quota_open.3 \
+ quotafile.3 quota_qfname.3 \
+ quotafile.3 quota_read.3 \
+ quotafile.3 quota_statfs.3 \
+ quotafile.3 quota_write_limits.3 \
+ quotafile.3 quota_write_usage.3
+MLINKS+=uucplock.3 uu_lock.3 uucplock.3 uu_lock_txfr.3 \
+ uucplock.3 uu_lockerr.3 uucplock.3 uu_unlock.3
+MLINKS+=pw_util.3 pw_copy.3 \
+ pw_util.3 pw_dup.3 \
+ pw_util.3 pw_edit.3 \
+ pw_util.3 pw_equal.3 \
+ pw_util.3 pw_fini.3 \
+ pw_util.3 pw_init.3 \
+ pw_util.3 pw_make.3 \
+ pw_util.3 pw_make_v7.3 \
+ pw_util.3 pw_mkdb.3 \
+ pw_util.3 pw_lock.3 \
+ pw_util.3 pw_scan.3 \
+ pw_util.3 pw_tempname.3 \
+ pw_util.3 pw_tmp.3
+
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
+.include <bsd.lib.mk>
diff --git a/lib/libutil/Makefile.depend b/lib/libutil/Makefile.depend
new file mode 100644
index 0000000..54c1f6f
--- /dev/null
+++ b/lib/libutil/Makefile.depend
@@ -0,0 +1,19 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ gnu/lib/libgcc \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libutil/_secure_path.3 b/lib/libutil/_secure_path.3
new file mode 100644
index 0000000..cf89315
--- /dev/null
+++ b/lib/libutil/_secure_path.3
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1997 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" this list of conditions, and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 2, 1997
+.Dt _SECURE_PATH 3
+.Os
+.Sh NAME
+.Nm _secure_path
+.Nd determine if a file appears to be secure
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft int
+.Fn _secure_path "const char *path" "uid_t uid" "gid_t gid"
+.Sh DESCRIPTION
+This function does some basic security checking on a given path.
+It is intended to be used by processes running with root privileges
+in order to decide whether or not to trust the contents of a given
+file.
+It uses a method often used to detect system compromise.
+.Pp
+A file is considered
+.Sq secure
+if it meets the following conditions:
+.Bl -enum
+.It
+The file exists, and is a regular file (not a symlink, device
+special or named pipe, etc.),
+.It
+Is not world writable.
+.It
+Is owned by the given uid or uid 0, if uid is not -1,
+.It
+Is not group writable or it has group ownership by the given
+gid, if gid is not -1.
+.El
+.Sh RETURN VALUES
+This function returns zero if the file exists and may be
+considered secure, -2 if the file does not exist, and
+-1 otherwise to indicate a security failure.
+The
+.Xr syslog 3
+function is used to log any failure of this function, including the
+reason, at LOG_ERR priority.
+.Sh SEE ALSO
+.Xr lstat 2 ,
+.Xr syslog 3
+.Sh HISTORY
+Code from which this function was derived was contributed to the
+.Fx
+project by Berkeley Software Design, Inc.
+.Sh BUGS
+The checks carried out are rudimentary and no attempt is made
+to eliminate race conditions between use of this function and
+access to the file referenced.
diff --git a/lib/libutil/_secure_path.c b/lib/libutil/_secure_path.c
new file mode 100644
index 0000000..363378b
--- /dev/null
+++ b/lib/libutil/_secure_path.c
@@ -0,0 +1,74 @@
+/*-
+ * Based on code copyright (c) 1995,1997 by
+ * Berkeley Software Design, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <libutil.h>
+#include <stddef.h>
+#include <syslog.h>
+
+/*
+ * Check for common security problems on a given path
+ * It must be:
+ * 1. A regular file, and exists
+ * 2. Owned and writable only by root (or given owner)
+ * 3. Group ownership is given group or is non-group writable
+ *
+ * Returns: -2 if file does not exist,
+ * -1 if security test failure
+ * 0 otherwise
+ */
+
+int
+_secure_path(const char *path, uid_t uid, gid_t gid)
+{
+ int r = -1;
+ struct stat sb;
+ const char *msg = NULL;
+
+ if (lstat(path, &sb) < 0) {
+ if (errno == ENOENT) /* special case */
+ r = -2; /* if it is just missing, skip the log entry */
+ else
+ msg = "%s: cannot stat %s: %m";
+ }
+ else if (!S_ISREG(sb.st_mode))
+ msg = "%s: %s is not a regular file";
+ else if (sb.st_mode & S_IWOTH)
+ msg = "%s: %s is world writable";
+ else if ((int)uid != -1 && sb.st_uid != uid && sb.st_uid != 0) {
+ if (uid == 0)
+ msg = "%s: %s is not owned by root";
+ else
+ msg = "%s: %s is not owned by uid %d";
+ } else if ((int)gid != -1 && sb.st_gid != gid && (sb.st_mode & S_IWGRP))
+ msg = "%s: %s is group writeable by non-authorised groups";
+ else
+ r = 0;
+ if (msg != NULL)
+ syslog(LOG_ERR, msg, "_secure_path", path, uid);
+ return r;
+}
diff --git a/lib/libutil/auth.c b/lib/libutil/auth.c
new file mode 100644
index 0000000..b57a784
--- /dev/null
+++ b/lib/libutil/auth.c
@@ -0,0 +1,44 @@
+/*
+ * Simple authentication database handling code.
+ *
+ * Copyright (c) 1998
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdlib.h>
+
+#include <libutil.h>
+
+char *
+auth_getval(const char *name)
+{
+
+ (void)name;
+ return (NULL);
+}
diff --git a/lib/libutil/expand_number.3 b/lib/libutil/expand_number.3
new file mode 100644
index 0000000..2f5871f
--- /dev/null
+++ b/lib/libutil/expand_number.3
@@ -0,0 +1,87 @@
+.\" Copyright (c) 2007 Eric Anderson <anderson@FreeBSD.org>
+.\" Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 15, 2010
+.Dt EXPAND_NUMBER 3
+.Os
+.Sh NAME
+.Nm expand_number
+.Nd format a number from human readable form
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fo expand_number
+.Fa "const char *buf" "uint64_t *num"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn expand_number
+function unformats the
+.Fa buf
+string and stores a unsigned 64-bit quantity at address pointed out by the
+.Fa num
+argument.
+.Pp
+The
+.Fn expand_number
+function
+is case-insensitive and
+follows the SI power of two convention.
+.Pp
+The prefixes are:
+.Bl -column "Prefix" "Description" "1000000000000000000" -offset indent
+.It Sy "Prefix" Ta Sy "Description" Ta Sy "Multiplier"
+.It Li K Ta No kilo Ta 1024
+.It Li M Ta No mega Ta 1048576
+.It Li G Ta No giga Ta 1073741824
+.It Li T Ta No tera Ta 1099511627776
+.It Li P Ta No peta Ta 1125899906842624
+.It Li E Ta No exa Ta 1152921504606846976
+.El
+.Sh RETURN VALUES
+.Rv -std
+.Sh ERRORS
+The
+.Fn expand_number
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The given string contains no digits.
+.It Bq Er EINVAL
+An unrecognized prefix was given.
+.It Bq Er ERANGE
+Result doesn't fit into 64 bits.
+.El
+.Sh SEE ALSO
+.Xr humanize_number 3
+.Sh HISTORY
+The
+.Fn expand_number
+function first appeared in
+.Fx 6.3 .
diff --git a/lib/libutil/expand_number.c b/lib/libutil/expand_number.c
new file mode 100644
index 0000000..24bcc39
--- /dev/null
+++ b/lib/libutil/expand_number.c
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2007 Eric Anderson <anderson@FreeBSD.org>
+ * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libutil.h>
+#include <stdint.h>
+
+int
+expand_number(const char *buf, uint64_t *num)
+{
+ char *endptr;
+ uintmax_t umaxval;
+ uint64_t number;
+ unsigned shift;
+ int serrno;
+
+ serrno = errno;
+ errno = 0;
+ umaxval = strtoumax(buf, &endptr, 0);
+ if (umaxval > UINT64_MAX)
+ errno = ERANGE;
+ if (errno != 0)
+ return (-1);
+ errno = serrno;
+ number = umaxval;
+
+ switch (tolower((unsigned char)*endptr)) {
+ case 'e':
+ shift = 60;
+ break;
+ case 'p':
+ shift = 50;
+ break;
+ case 't':
+ shift = 40;
+ break;
+ case 'g':
+ shift = 30;
+ break;
+ case 'm':
+ shift = 20;
+ break;
+ case 'k':
+ shift = 10;
+ break;
+ case 'b':
+ case '\0': /* No unit. */
+ *num = number;
+ return (0);
+ default:
+ /* Unrecognized unit. */
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if ((number << shift) >> shift != number) {
+ /* Overflow */
+ errno = ERANGE;
+ return (-1);
+ }
+ *num = number << shift;
+ return (0);
+}
diff --git a/lib/libutil/flopen.3 b/lib/libutil/flopen.3
new file mode 100644
index 0000000..aba788c
--- /dev/null
+++ b/lib/libutil/flopen.3
@@ -0,0 +1,101 @@
+.\"-
+.\" Copyright (c) 2007 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 6, 2009
+.Dt FLOPEN 3
+.Os
+.Sh NAME
+.Nm flopen
+.Nd "Reliably open and lock a file"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/fcntl.h
+.In libutil.h
+.Ft int
+.Fn flopen "const char *path" "int flags"
+.Ft int
+.Fn flopen "const char *path" "int flags" "mode_t mode"
+.Sh DESCRIPTION
+The
+.Fn flopen
+function opens or creates a file and acquires an exclusive lock on it.
+It is essentially equivalent with calling
+.Fn open
+with the same parameters followed by
+.Fn flock
+with an
+.Va operation
+argument of
+.Dv LOCK_EX ,
+except that
+.Fn flopen
+will attempt to detect and handle races that may occur between opening
+/ creating the file and locking it.
+Thus, it is well suited for opening lock files, PID files, spool
+files, mailboxes and other kinds of files which are used for
+synchronization between processes.
+.Pp
+If
+.Va flags
+includes
+.Dv O_NONBLOCK
+and the file is already locked,
+.Fn flopen
+will fail and set
+.Va errno
+to
+.Dv EWOULDBLOCK .
+.Pp
+As with
+.Fn open ,
+the additional
+.Va mode
+argument is required if
+.Va flags
+includes
+.Dv O_CREAT .
+.Sh RETURN VALUES
+If successful,
+.Fn flopen
+returns a valid file descriptor.
+Otherwise, it returns -1, and sets
+.Va errno
+as described in
+.Xr flock 2
+and
+.Xr open 2 .
+.Sh SEE ALSO
+.Xr errno 2 ,
+.Xr flock 2 ,
+.Xr open 2
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+function and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org .
diff --git a/lib/libutil/flopen.c b/lib/libutil/flopen.c
new file mode 100644
index 0000000..754c9c0
--- /dev/null
+++ b/lib/libutil/flopen.c
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 2007 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <libutil.h>
+
+int
+flopen(const char *path, int flags, ...)
+{
+ int fd, operation, serrno, trunc;
+ struct stat sb, fsb;
+ mode_t mode;
+
+#ifdef O_EXLOCK
+ flags &= ~O_EXLOCK;
+#endif
+
+ mode = 0;
+ if (flags & O_CREAT) {
+ va_list ap;
+
+ va_start(ap, flags);
+ mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */
+ va_end(ap);
+ }
+
+ operation = LOCK_EX;
+ if (flags & O_NONBLOCK)
+ operation |= LOCK_NB;
+
+ trunc = (flags & O_TRUNC);
+ flags &= ~O_TRUNC;
+
+ for (;;) {
+ if ((fd = open(path, flags, mode)) == -1)
+ /* non-existent or no access */
+ return (-1);
+ if (flock(fd, operation) == -1) {
+ /* unsupported or interrupted */
+ serrno = errno;
+ (void)close(fd);
+ errno = serrno;
+ return (-1);
+ }
+ if (stat(path, &sb) == -1) {
+ /* disappeared from under our feet */
+ (void)close(fd);
+ continue;
+ }
+ if (fstat(fd, &fsb) == -1) {
+ /* can't happen [tm] */
+ serrno = errno;
+ (void)close(fd);
+ errno = serrno;
+ return (-1);
+ }
+ if (sb.st_dev != fsb.st_dev ||
+ sb.st_ino != fsb.st_ino) {
+ /* changed under our feet */
+ (void)close(fd);
+ continue;
+ }
+ if (trunc && ftruncate(fd, 0) != 0) {
+ /* can't happen [tm] */
+ serrno = errno;
+ (void)close(fd);
+ errno = serrno;
+ return (-1);
+ }
+ return (fd);
+ }
+}
diff --git a/lib/libutil/fparseln.3 b/lib/libutil/fparseln.3
new file mode 100644
index 0000000..245fba7
--- /dev/null
+++ b/lib/libutil/fparseln.3
@@ -0,0 +1,157 @@
+.\" $NetBSD: fparseln.3,v 1.7 1999/07/02 15:49:12 simonb Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1997 Christos Zoulas. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christos Zoulas.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd December 1, 1997
+.Dt FPARSELN 3
+.Os
+.Sh NAME
+.Nm fparseln
+.Nd return the next logical line from a stream
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In stdio.h
+.In libutil.h
+.Ft "char *"
+.Fo fparseln
+.Fa "FILE *stream" "size_t *len" "size_t *lineno"
+.Fa "const char delim[3]" "int flags"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn fparseln
+function
+returns a pointer to the next logical line from the stream referenced by
+.Fa stream .
+This string is
+.Dv NUL
+terminated and it is dynamically allocated on each invocation.
+It is the
+responsibility of the caller to free the pointer.
+.Pp
+By default, if a character is escaped, both it and the preceding escape
+character will be present in the returned string.
+Various
+.Fa flags
+alter this behaviour.
+.Pp
+The meaning of the arguments is as follows:
+.Bl -tag -width "lineno"
+.It Fa stream
+The stream to read from.
+.It Fa len
+If not
+.Dv NULL ,
+the length of the string is stored in the memory location to which it
+points.
+.It Fa lineno
+If not
+.Dv NULL ,
+the value of the memory location to which is pointed to, is incremented
+by the number of lines actually read from the file.
+.It Fa delim
+Contains the escape, continuation, and comment characters.
+If a character is
+.Dv NUL
+then processing for that character is disabled.
+If
+.Dv NULL ,
+all characters default to values specified below.
+The contents of
+.Fa delim
+is as follows:
+.Bl -tag -width "delim[0]"
+.It Fa delim[0]
+The escape character, which defaults to
+.Cm \e ,
+is used to remove any special meaning from the next character.
+.It Fa delim[1]
+The continuation character, which defaults to
+.Cm \e ,
+is used to indicate that the next line should be concatenated with the
+current one if this character is the last character on the current line
+and is not escaped.
+.It Fa delim[2]
+The comment character, which defaults to
+.Cm # ,
+if not escaped indicates the beginning of a comment that extends until the
+end of the current line.
+.El
+.It Fa flags
+If non-zero, alter the operation of
+.Fn fparseln .
+The various flags, which may be
+.Em or Ns -ed
+together, are:
+.Bl -tag -width "FPARSELN_UNESCCOMM"
+.It Dv FPARSELN_UNESCCOMM
+Remove escape preceding an escaped comment.
+.It Dv FPARSELN_UNESCCONT
+Remove escape preceding an escaped continuation.
+.It Dv FPARSELN_UNESCESC
+Remove escape preceding an escaped escape.
+.It Dv FPARSELN_UNESCREST
+Remove escape preceding any other character.
+.It Dv FPARSELN_UNESCALL
+All of the above.
+.El
+.El
+.Sh RETURN VALUES
+Upon successful completion a pointer to the parsed line is returned;
+otherwise,
+.Dv NULL
+is returned.
+.Pp
+The
+.Fn fparseln
+function uses internally
+.Xr fgetln 3 ,
+so all error conditions that apply to
+.Xr fgetln 3 ,
+apply to
+.Fn fparseln .
+In addition
+.Fn fparseln
+may set
+.Va errno
+to
+.Er ENOMEM
+and return
+.Dv NULL
+if it runs out of memory.
+.Sh SEE ALSO
+.Xr fgetln 3
+.Sh HISTORY
+The
+.Fn fparseln
+function first appeared in
+.Nx 1.4
+and
+.Fx 4.0 .
diff --git a/lib/libutil/fparseln.c b/lib/libutil/fparseln.c
new file mode 100644
index 0000000..d03357e
--- /dev/null
+++ b/lib/libutil/fparseln.c
@@ -0,0 +1,228 @@
+/* $NetBSD: fparseln.c,v 1.7 2007/03/08 19:57:53 drochner Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christos Zoulas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libutil.h>
+
+static int isescaped(const char *, const char *, int);
+
+/* isescaped():
+ * Return true if the character in *p that belongs to a string
+ * that starts in *sp, is escaped by the escape character esc.
+ */
+static int
+isescaped(const char *sp, const char *p, int esc)
+{
+ const char *cp;
+ size_t ne;
+
+#if 0
+ _DIAGASSERT(sp != NULL);
+ _DIAGASSERT(p != NULL);
+#endif
+
+ /* No escape character */
+ if (esc == '\0')
+ return 0;
+
+ /* Count the number of escape characters that precede ours */
+ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
+ continue;
+
+ /* Return true if odd number of escape characters */
+ return (ne & 1) != 0;
+}
+
+
+/* fparseln():
+ * Read a line from a file parsing continuations ending in \
+ * and eliminating trailing newlines, or comments starting with
+ * the comment char.
+ */
+char *
+fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags)
+{
+ static const char dstr[3] = { '\\', '\\', '#' };
+
+ size_t s, len;
+ char *buf;
+ char *ptr, *cp;
+ int cnt;
+ char esc, con, nl, com;
+
+#if 0
+ _DIAGASSERT(fp != NULL);
+#endif
+
+ len = 0;
+ buf = NULL;
+ cnt = 1;
+
+ if (str == NULL)
+ str = dstr;
+
+ esc = str[0];
+ con = str[1];
+ com = str[2];
+ /*
+ * XXX: it would be cool to be able to specify the newline character,
+ * but unfortunately, fgetln does not let us
+ */
+ nl = '\n';
+
+ while (cnt) {
+ cnt = 0;
+
+ if (lineno)
+ (*lineno)++;
+
+ if ((ptr = fgetln(fp, &s)) == NULL)
+ break;
+
+ if (s && com) { /* Check and eliminate comments */
+ for (cp = ptr; cp < ptr + s; cp++)
+ if (*cp == com && !isescaped(ptr, cp, esc)) {
+ s = cp - ptr;
+ cnt = s == 0 && buf == NULL;
+ break;
+ }
+ }
+
+ if (s && nl) { /* Check and eliminate newlines */
+ cp = &ptr[s - 1];
+
+ if (*cp == nl)
+ s--; /* forget newline */
+ }
+
+ if (s && con) { /* Check and eliminate continuations */
+ cp = &ptr[s - 1];
+
+ if (*cp == con && !isescaped(ptr, cp, esc)) {
+ s--; /* forget continuation char */
+ cnt = 1;
+ }
+ }
+
+ if (s == 0) {
+ /*
+ * nothing to add, skip realloc except in case
+ * we need a minimal buf to return an empty line
+ */
+ if (cnt || buf != NULL)
+ continue;
+ }
+
+ if ((cp = realloc(buf, len + s + 1)) == NULL) {
+ free(buf);
+ return NULL;
+ }
+ buf = cp;
+
+ (void) memcpy(buf + len, ptr, s);
+ len += s;
+ buf[len] = '\0';
+ }
+
+ if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
+ strchr(buf, esc) != NULL) {
+ ptr = cp = buf;
+ while (cp[0] != '\0') {
+ int skipesc;
+
+ while (cp[0] != '\0' && cp[0] != esc)
+ *ptr++ = *cp++;
+ if (cp[0] == '\0' || cp[1] == '\0')
+ break;
+
+ skipesc = 0;
+ if (cp[1] == com)
+ skipesc += (flags & FPARSELN_UNESCCOMM);
+ if (cp[1] == con)
+ skipesc += (flags & FPARSELN_UNESCCONT);
+ if (cp[1] == esc)
+ skipesc += (flags & FPARSELN_UNESCESC);
+ if (cp[1] != com && cp[1] != con && cp[1] != esc)
+ skipesc = (flags & FPARSELN_UNESCREST);
+
+ if (skipesc)
+ cp++;
+ else
+ *ptr++ = *cp++;
+ *ptr++ = *cp++;
+ }
+ *ptr = '\0';
+ len = strlen(buf);
+ }
+
+ if (size)
+ *size = len;
+ return buf;
+}
+
+#ifdef TEST
+
+int
+main(int argc, char *argv[])
+{
+ char *ptr;
+ size_t size, line;
+
+ line = 0;
+ while ((ptr = fparseln(stdin, &size, &line, NULL,
+ FPARSELN_UNESCALL)) != NULL)
+ printf("line %d (%d) |%s|\n", line, size, ptr);
+ return 0;
+}
+
+/*
+
+# This is a test
+line 1
+line 2 \
+line 3 # Comment
+line 4 \# Not comment \\\\
+
+# And a comment \
+line 5 \\\
+line 6
+
+*/
+
+#endif /* TEST */
diff --git a/lib/libutil/gr_util.c b/lib/libutil/gr_util.c
new file mode 100644
index 0000000..93b3eb2
--- /dev/null
+++ b/lib/libutil/gr_util.c
@@ -0,0 +1,651 @@
+/*-
+ * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <libutil.h>
+#include <paths.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int lockfd = -1;
+static char group_dir[PATH_MAX];
+static char group_file[PATH_MAX];
+static char tempname[PATH_MAX];
+static int initialized;
+static size_t grmemlen(const struct group *, const char *, int *);
+static struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx);
+
+/*
+ * Initialize statics
+ */
+int
+gr_init(const char *dir, const char *group)
+{
+
+ if (dir == NULL) {
+ strcpy(group_dir, _PATH_ETC);
+ } else {
+ if (strlen(dir) >= sizeof(group_dir)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ strcpy(group_dir, dir);
+ }
+
+ if (group == NULL) {
+ if (dir == NULL) {
+ strcpy(group_file, _PATH_GROUP);
+ } else if (snprintf(group_file, sizeof(group_file), "%s/group",
+ group_dir) > (int)sizeof(group_file)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ } else {
+ if (strlen(group) >= sizeof(group_file)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ strcpy(group_file, group);
+ }
+
+ initialized = 1;
+ return (0);
+}
+
+/*
+ * Lock the group file
+ */
+int
+gr_lock(void)
+{
+ if (*group_file == '\0')
+ return (-1);
+
+ for (;;) {
+ struct stat st;
+
+ lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0);
+ if (lockfd == -1) {
+ if (errno == EWOULDBLOCK) {
+ errx(1, "the group file is busy");
+ } else {
+ err(1, "could not lock the group file: ");
+ }
+ }
+ if (fstat(lockfd, &st) == -1)
+ err(1, "fstat() failed: ");
+ if (st.st_nlink != 0)
+ break;
+ close(lockfd);
+ lockfd = -1;
+ }
+ return (lockfd);
+}
+
+/*
+ * Create and open a presmuably safe temp file for editing group data
+ */
+int
+gr_tmp(int mfd)
+{
+ char buf[8192];
+ ssize_t nr;
+ const char *p;
+ int tfd;
+
+ if (*group_file == '\0')
+ return (-1);
+ if ((p = strrchr(group_file, '/')))
+ ++p;
+ else
+ p = group_file;
+ if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX",
+ (int)(p - group_file), group_file) >= (int)sizeof(tempname)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ if ((tfd = mkostemp(tempname, O_SYNC)) == -1)
+ return (-1);
+ if (mfd != -1) {
+ while ((nr = read(mfd, buf, sizeof(buf))) > 0)
+ if (write(tfd, buf, (size_t)nr) != nr)
+ break;
+ if (nr != 0) {
+ unlink(tempname);
+ *tempname = '\0';
+ close(tfd);
+ return (-1);
+ }
+ }
+ return (tfd);
+}
+
+/*
+ * Copy the group file from one descriptor to another, replacing, deleting
+ * or adding a single record on the way.
+ */
+int
+gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr)
+{
+ char buf[8192], *end, *line, *p, *q, *r, t;
+ struct group *fgr;
+ const struct group *sgr;
+ size_t len;
+ int eof, readlen;
+
+ if (old_gr == NULL && gr == NULL)
+ return(-1);
+
+ sgr = old_gr;
+ /* deleting a group */
+ if (gr == NULL) {
+ line = NULL;
+ } else {
+ if ((line = gr_make(gr)) == NULL)
+ return (-1);
+ }
+
+ /* adding a group */
+ if (sgr == NULL)
+ sgr = gr;
+
+ eof = 0;
+ len = 0;
+ p = q = end = buf;
+ for (;;) {
+ /* find the end of the current line */
+ for (p = q; q < end && *q != '\0'; ++q)
+ if (*q == '\n')
+ break;
+
+ /* if we don't have a complete line, fill up the buffer */
+ if (q >= end) {
+ if (eof)
+ break;
+ if ((size_t)(q - p) >= sizeof(buf)) {
+ warnx("group line too long");
+ errno = EINVAL; /* hack */
+ goto err;
+ }
+ if (p < end) {
+ q = memmove(buf, p, end -p);
+ end -= p - buf;
+ } else {
+ p = q = end = buf;
+ }
+ readlen = read(ffd, end, sizeof(buf) - (end -buf));
+ if (readlen == -1)
+ goto err;
+ else
+ len = (size_t)readlen;
+ if (len == 0 && p == buf)
+ break;
+ end += len;
+ len = end - buf;
+ if (len < (ssize_t)sizeof(buf)) {
+ eof = 1;
+ if (len > 0 && buf[len -1] != '\n')
+ ++len, *end++ = '\n';
+ }
+ continue;
+ }
+
+ /* is it a blank line or a comment? */
+ for (r = p; r < q && isspace(*r); ++r)
+ /* nothing */;
+ if (r == q || *r == '#') {
+ /* yep */
+ if (write(tfd, p, q -p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+
+ /* is it the one we're looking for? */
+
+ t = *q;
+ *q = '\0';
+
+ fgr = gr_scan(r);
+
+ /* fgr is either a struct group for the current line,
+ * or NULL if the line is malformed.
+ */
+
+ *q = t;
+ if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) {
+ /* nope */
+ if (fgr != NULL)
+ free(fgr);
+ if (write(tfd, p, q - p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+ if (old_gr && !gr_equal(fgr, old_gr)) {
+ warnx("entry inconsistent");
+ free(fgr);
+ errno = EINVAL; /* hack */
+ goto err;
+ }
+ free(fgr);
+
+ /* it is, replace or remove it */
+ if (line != NULL) {
+ len = strlen(line);
+ if (write(tfd, line, len) != (int) len)
+ goto err;
+ } else {
+ /* when removed, avoid the \n */
+ q++;
+ }
+ /* we're done, just copy the rest over */
+ for (;;) {
+ if (write(tfd, q, end - q) != end - q)
+ goto err;
+ q = buf;
+ readlen = read(ffd, buf, sizeof(buf));
+ if (readlen == 0)
+ break;
+ else
+ len = (size_t)readlen;
+ if (readlen == -1)
+ goto err;
+ end = buf + len;
+ }
+ goto done;
+ }
+
+ /* if we got here, we didn't find the old entry */
+ if (line == NULL) {
+ errno = ENOENT;
+ goto err;
+ }
+ len = strlen(line);
+ if ((size_t)write(tfd, line, len) != len ||
+ write(tfd, "\n", 1) != 1)
+ goto err;
+ done:
+ if (line != NULL)
+ free(line);
+ return (0);
+ err:
+ if (line != NULL)
+ free(line);
+ return (-1);
+}
+
+/*
+ * Regenerate the group file
+ */
+int
+gr_mkdb(void)
+{
+ int fd;
+
+ if (chmod(tempname, 0644) != 0)
+ return (-1);
+
+ if (rename(tempname, group_file) != 0)
+ return (-1);
+
+ /*
+ * Make sure new group file is safe on disk. To improve performance we
+ * will call fsync() to the directory where file lies
+ */
+ if ((fd = open(group_dir, O_RDONLY|O_DIRECTORY)) == -1)
+ return (-1);
+
+ if (fsync(fd) != 0) {
+ close(fd);
+ return (-1);
+ }
+
+ close(fd);
+ return(0);
+}
+
+/*
+ * Clean up. Preserves errno for the caller's convenience.
+ */
+void
+gr_fini(void)
+{
+ int serrno;
+
+ if (!initialized)
+ return;
+ initialized = 0;
+ serrno = errno;
+ if (*tempname != '\0') {
+ unlink(tempname);
+ *tempname = '\0';
+ }
+ if (lockfd != -1)
+ close(lockfd);
+ errno = serrno;
+}
+
+/*
+ * Compares two struct group's.
+ */
+int
+gr_equal(const struct group *gr1, const struct group *gr2)
+{
+
+ /* Check that the non-member information is the same. */
+ if (gr1->gr_name == NULL || gr2->gr_name == NULL) {
+ if (gr1->gr_name != gr2->gr_name)
+ return (false);
+ } else if (strcmp(gr1->gr_name, gr2->gr_name) != 0)
+ return (false);
+ if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) {
+ if (gr1->gr_passwd != gr2->gr_passwd)
+ return (false);
+ } else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0)
+ return (false);
+ if (gr1->gr_gid != gr2->gr_gid)
+ return (false);
+
+ /*
+ * Check all members in both groups.
+ * getgrnam can return gr_mem with a pointer to NULL.
+ * gr_dup and gr_add strip out this superfluous NULL, setting
+ * gr_mem to NULL for no members.
+ */
+ if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) {
+ int i;
+
+ for (i = 0;
+ gr1->gr_mem[i] != NULL && gr2->gr_mem[i] != NULL; i++) {
+ if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0)
+ return (false);
+ }
+ if (gr1->gr_mem[i] != NULL || gr2->gr_mem[i] != NULL)
+ return (false);
+ } else if (gr1->gr_mem != NULL && gr1->gr_mem[0] != NULL) {
+ return (false);
+ } else if (gr2->gr_mem != NULL && gr2->gr_mem[0] != NULL) {
+ return (false);
+ }
+
+ return (true);
+}
+
+/*
+ * Make a group line out of a struct group.
+ */
+char *
+gr_make(const struct group *gr)
+{
+ const char *group_line_format = "%s:%s:%ju:";
+ const char *sep;
+ char *line;
+ char *p;
+ size_t line_size;
+ int ndx;
+
+ /* Calculate the length of the group line. */
+ line_size = snprintf(NULL, 0, group_line_format, gr->gr_name,
+ gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1;
+ if (gr->gr_mem != NULL) {
+ for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++)
+ line_size += strlen(gr->gr_mem[ndx]) + 1;
+ if (ndx > 0)
+ line_size--;
+ }
+
+ /* Create the group line and fill it. */
+ if ((line = p = malloc(line_size)) == NULL)
+ return (NULL);
+ p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd,
+ (uintmax_t)gr->gr_gid);
+ if (gr->gr_mem != NULL) {
+ sep = "";
+ for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) {
+ p = stpcpy(p, sep);
+ p = stpcpy(p, gr->gr_mem[ndx]);
+ sep = ",";
+ }
+ }
+
+ return (line);
+}
+
+/*
+ * Duplicate a struct group.
+ */
+struct group *
+gr_dup(const struct group *gr)
+{
+ return (gr_add(gr, NULL));
+}
+/*
+ * Add a new member name to a struct group.
+ */
+struct group *
+gr_add(const struct group *gr, const char *newmember)
+{
+ char *mem;
+ size_t len;
+ int num_mem;
+
+ num_mem = 0;
+ len = grmemlen(gr, newmember, &num_mem);
+ /* Create new group and copy old group into it. */
+ if ((mem = malloc(len)) == NULL)
+ return (NULL);
+ return (grcopy(gr, mem, newmember, num_mem));
+}
+
+/* It is safer to walk the pointers given at gr_mem since there is no
+ * guarantee the gr_mem + strings are contiguous in the given struct group
+ * but compactify the new group into the following form.
+ *
+ * The new struct is laid out like this in memory. The example given is
+ * for a group with two members only.
+ *
+ * {
+ * (char *name)
+ * (char *passwd)
+ * (int gid)
+ * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area
+ * gr_mem area
+ * (member1 *)
+ * (member2 *)
+ * (NULL)
+ * (name string)
+ * (passwd string)
+ * (member1 string)
+ * (member2 string)
+ * }
+ */
+/*
+ * Copy the contents of a group plus given name to a preallocated group struct
+ */
+static struct group *
+grcopy(const struct group *gr, char *dst, const char *name, int ndx)
+{
+ int i;
+ struct group *newgr;
+
+ newgr = (struct group *)(void *)dst; /* avoid alignment warning */
+ dst += sizeof(*newgr);
+ if (ndx != 0) {
+ newgr->gr_mem = (char **)(void *)(dst); /* avoid alignment warning */
+ dst += (ndx + 1) * sizeof(*newgr->gr_mem);
+ } else
+ newgr->gr_mem = NULL;
+ if (gr->gr_name != NULL) {
+ newgr->gr_name = dst;
+ dst = stpcpy(dst, gr->gr_name) + 1;
+ } else
+ newgr->gr_name = NULL;
+ if (gr->gr_passwd != NULL) {
+ newgr->gr_passwd = dst;
+ dst = stpcpy(dst, gr->gr_passwd) + 1;
+ } else
+ newgr->gr_passwd = NULL;
+ newgr->gr_gid = gr->gr_gid;
+ i = 0;
+ /* Original group struct might have a NULL gr_mem */
+ if (gr->gr_mem != NULL) {
+ for (; gr->gr_mem[i] != NULL; i++) {
+ newgr->gr_mem[i] = dst;
+ dst = stpcpy(dst, gr->gr_mem[i]) + 1;
+ }
+ }
+ /* If name is not NULL, newgr->gr_mem is known to be not NULL */
+ if (name != NULL) {
+ newgr->gr_mem[i++] = dst;
+ dst = stpcpy(dst, name) + 1;
+ }
+ /* if newgr->gr_mem is not NULL add NULL marker */
+ if (newgr->gr_mem != NULL)
+ newgr->gr_mem[i] = NULL;
+
+ return (newgr);
+}
+
+/*
+ * Calculate length of a struct group + given name
+ */
+static size_t
+grmemlen(const struct group *gr, const char *name, int *num_mem)
+{
+ size_t len;
+ int i;
+
+ if (gr == NULL)
+ return (0);
+ /* Calculate size of the group. */
+ len = sizeof(*gr);
+ if (gr->gr_name != NULL)
+ len += strlen(gr->gr_name) + 1;
+ if (gr->gr_passwd != NULL)
+ len += strlen(gr->gr_passwd) + 1;
+ i = 0;
+ if (gr->gr_mem != NULL) {
+ for (; gr->gr_mem[i] != NULL; i++) {
+ len += strlen(gr->gr_mem[i]) + 1;
+ len += sizeof(*gr->gr_mem);
+ }
+ }
+ if (name != NULL) {
+ i++;
+ len += strlen(name) + 1;
+ len += sizeof(*gr->gr_mem);
+ }
+ /* Allow for NULL pointer */
+ if (i != 0)
+ len += sizeof(*gr->gr_mem);
+ *num_mem = i;
+ return(len);
+}
+
+/*
+ * Scan a line and place it into a group structure.
+ */
+static bool
+__gr_scan(char *line, struct group *gr)
+{
+ char *loc;
+ int ndx;
+
+ /* Assign non-member information to structure. */
+ gr->gr_name = line;
+ if ((loc = strchr(line, ':')) == NULL)
+ return (false);
+ *loc = '\0';
+ gr->gr_passwd = loc + 1;
+ if (*gr->gr_passwd == ':')
+ *gr->gr_passwd = '\0';
+ else {
+ if ((loc = strchr(loc + 1, ':')) == NULL)
+ return (false);
+ *loc = '\0';
+ }
+ if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1)
+ return (false);
+
+ /* Assign member information to structure. */
+ if ((loc = strchr(loc + 1, ':')) == NULL)
+ return (false);
+ line = loc + 1;
+ gr->gr_mem = NULL;
+ ndx = 0;
+ do {
+ gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) *
+ (ndx + 1));
+ if (gr->gr_mem == NULL)
+ return (false);
+
+ /* Skip locations without members (i.e., empty string). */
+ do {
+ gr->gr_mem[ndx] = strsep(&line, ",");
+ } while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0');
+ } while (gr->gr_mem[ndx++] != NULL);
+
+ return (true);
+}
+
+/*
+ * Create a struct group from a line.
+ */
+struct group *
+gr_scan(const char *line)
+{
+ struct group gr;
+ char *line_copy;
+ struct group *new_gr;
+
+ if ((line_copy = strdup(line)) == NULL)
+ return (NULL);
+ if (!__gr_scan(line_copy, &gr)) {
+ free(line_copy);
+ return (NULL);
+ }
+ new_gr = gr_dup(&gr);
+ free(line_copy);
+ if (gr.gr_mem != NULL)
+ free(gr.gr_mem);
+
+ return (new_gr);
+}
diff --git a/lib/libutil/hexdump.3 b/lib/libutil/hexdump.3
new file mode 100644
index 0000000..9fa96f8
--- /dev/null
+++ b/lib/libutil/hexdump.3
@@ -0,0 +1,109 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 2003 Scott Long
+.\"
+.\" All rights reserved.
+.\"
+.\" This program is free software.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 8, 2014
+.Dt HEXDUMP 3
+.Os
+.Sh NAME
+.Nm hexdump
+.Nd "dump a block of bytes to standard out in hexadecimal form"
+.Sh SYNOPSIS
+.In libutil.h
+.In sys/sbuf.h
+.Ft void
+.Fn hexdump "void *ptr" "int length" "const char *hdr" "int flags"
+.Ft void
+.Fo sbuf_hexdump
+.Fa "struct sbuf *sb"
+.Fa "void *ptr"
+.Fa "int length"
+.Fa "const char *hdr"
+.Fa "int flags"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn hexdump
+function prints an array of bytes to standard out in hexadecimal form,
+along with the
+.Tn ASCII
+representation of the bytes, if possible.
+By default, each line of
+output will start with an offset count, followed by 16 hexadecimal values,
+followed by 16
+.Tn ASCII
+characters.
+.Pp
+The
+.Fn sbuf_hexdump
+function prints the hexdump to the supplied
+.Xr sbuf 9 .
+.Bl -tag -width indent
+.It Fa ptr
+Pointer to the array of bytes to print.
+It does not need to be
+.Dv NUL Ns
+-terminated.
+.It Fa length
+Number of bytes to print.
+.It Fa hdr
+Pointer to a
+.Dv NUL Ns
+-terminated character string that will be prepended to each
+line of output.
+A value of
+.Dv NULL
+implies that no header will be printed.
+.It Fa flags
+Flags for controlling the formatting of the output.
+.Bl -tag -width ".Dv HD_OMIT_COUNT"
+.It Bits 0-7
+Integer value of the number of bytes to display on each line.
+A value of 0 implies that the default value of 16 will be used.
+.It Bits 8-15
+Character
+.Tn ASCII
+value to use as the separator for the hexadecimal output.
+A value of 0 implies that the default value of 32
+.Tn ( ASCII
+space) will be used.
+.It Dv HD_OMIT_COUNT
+Do not print the offset column at the beginning of each line.
+.It Dv HD_OMIT_HEX
+Do not print the hexadecimal values on each line.
+.It Dv HD_OMIT_CHARS
+Do not print the character values on each line.
+.El
+.El
+.Sh SEE ALSO
+.Xr ascii 7 ,
+.Xr sbuf 9
+.Sh AUTHORS
+This manual page was written by
+.An Scott Long .
diff --git a/lib/libutil/hexdump.c b/lib/libutil/hexdump.c
new file mode 100644
index 0000000..76b93ce
--- /dev/null
+++ b/lib/libutil/hexdump.c
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <libutil.h>
+#include <stdio.h>
+
+void
+hexdump(const void *ptr, int length, const char *hdr, int flags)
+{
+ int i, j, k;
+ int cols;
+ const unsigned char *cp;
+ char delim;
+
+ if ((flags & HD_DELIM_MASK) != 0)
+ delim = (flags & HD_DELIM_MASK) >> 8;
+ else
+ delim = ' ';
+
+ if ((flags & HD_COLUMN_MASK) != 0)
+ cols = flags & HD_COLUMN_MASK;
+ else
+ cols = 16;
+
+ cp = ptr;
+ for (i = 0; i < length; i+= cols) {
+ if (hdr != NULL)
+ printf("%s", hdr);
+
+ if ((flags & HD_OMIT_COUNT) == 0)
+ printf("%04x ", i);
+
+ if ((flags & HD_OMIT_HEX) == 0) {
+ for (j = 0; j < cols; j++) {
+ k = i + j;
+ if (k < length)
+ printf("%c%02x", delim, cp[k]);
+ else
+ printf(" ");
+ }
+ }
+
+ if ((flags & HD_OMIT_CHARS) == 0) {
+ printf(" |");
+ for (j = 0; j < cols; j++) {
+ k = i + j;
+ if (k >= length)
+ printf(" ");
+ else if (cp[k] >= ' ' && cp[k] <= '~')
+ printf("%c", cp[k]);
+ else
+ printf(".");
+ }
+ printf("|");
+ }
+ printf("\n");
+ }
+}
+
diff --git a/lib/libutil/humanize_number.3 b/lib/libutil/humanize_number.3
new file mode 100644
index 0000000..3a883ca
--- /dev/null
+++ b/lib/libutil/humanize_number.3
@@ -0,0 +1,202 @@
+.\" $NetBSD: humanize_number.3,v 1.4 2003/04/16 13:34:37 wiz Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1999, 2002 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Luke Mewburn and by Tomas Svensson.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd October 7, 2013
+.Dt HUMANIZE_NUMBER 3
+.Os
+.Sh NAME
+.Nm humanize_number
+.Nd format a number into a human readable form
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fo humanize_number
+.Fa "char *buf" "size_t len" "int64_t number" "const char *suffix"
+.Fa "int scale" "int flags"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn humanize_number
+function formats the signed 64-bit quantity given in
+.Fa number
+into
+.Fa buf .
+A space and then
+.Fa suffix
+is appended to the end.
+The buffer pointed to by
+.Fa buf
+must be at least
+.Fa len
+bytes long.
+.Pp
+If the formatted number (including
+.Fa suffix )
+would be too long to fit into
+.Fa buf ,
+then divide
+.Fa number
+by 1024 until it will.
+In this case, prefix
+.Fa suffix
+with the appropriate designator.
+The
+.Fn humanize_number
+function follows the traditional computer science conventions by
+default, rather than the IEE/IEC (and now also SI) power of two
+convention or the power of ten notion.
+This behaviour however can be altered by specifying the
+.Dv HN_DIVISOR_1000
+and
+.Dv HN_IEC_PREFIXES
+flags.
+.Pp
+The traditional
+.Pq default
+prefixes are:
+.Bl -column "Prefix" "Description" "1000000000000000000" -offset indent
+.It Sy "Prefix" Ta Sy "Description" Ta Sy "Multiplier" Ta Sy "Multiplier 1000x"
+.It Li (note) Ta No kilo Ta 1024 Ta 1000
+.It Li M Ta No mega Ta 1048576 Ta 1000000
+.It Li G Ta No giga Ta 1073741824 Ta 1000000000
+.It Li T Ta No tera Ta 1099511627776 Ta 1000000000000
+.It Li P Ta No peta Ta 1125899906842624 Ta 1000000000000000
+.It Li E Ta No exa Ta 1152921504606846976 Ta 1000000000000000000
+.El
+.Pp
+Note:
+An uppercase K indicates a power of two, a lowercase k a power of ten.
+.Pp
+The IEE/IEC (and now also SI) power of two prefixes are:
+.Bl -column "Prefix" "Description" "1000000000000000000" -offset indent
+.It Sy "Prefix" Ta Sy "Description" Ta Sy "Multiplier"
+.It Li Ki Ta No kibi Ta 1024
+.It Li Mi Ta No mebi Ta 1048576
+.It Li Gi Ta No gibi Ta 1073741824
+.It Li Ti Ta No tebi Ta 1099511627776
+.It Li Pi Ta No pebi Ta 1125899906842624
+.It Li Ei Ta No exbi Ta 1152921504606846976
+.El
+.Pp
+The
+.Fa len
+argument must be at least 4 plus the length of
+.Fa suffix ,
+in order to ensure a useful result is generated into
+.Fa buf .
+To use a specific prefix, specify this as
+.Fa scale
+.Po multiplier = 1024 ^ scale;
+when
+.Dv HN_DIVISOR_1000
+is specified,
+multiplier = 1000 ^ scale
+.Pc .
+This cannot be combined with any of the
+.Fa scale
+flags below.
+.Pp
+The following flags may be passed in
+.Fa scale :
+.Bl -tag -width ".Dv HN_DIVISOR_1000" -offset indent
+.It Dv HN_AUTOSCALE
+Format the buffer using the lowest multiplier possible.
+.It Dv HN_GETSCALE
+Return the prefix index number (the number of times
+.Fa number
+must be divided to fit) instead of formatting it to the buffer.
+.El
+.Pp
+The following flags may be passed in
+.Fa flags :
+.Bl -tag -width ".Dv HN_DIVISOR_1000" -offset indent
+.It Dv HN_DECIMAL
+If the final result is less than 10, display it using one decimal place.
+.It Dv HN_NOSPACE
+Do not put a space between
+.Fa number
+and the prefix.
+.It Dv HN_B
+Use
+.Ql B
+(bytes) as prefix if the original result does not have a prefix.
+.It Dv HN_DIVISOR_1000
+Divide
+.Fa number
+with 1000 instead of 1024.
+.It Dv HN_IEC_PREFIXES
+Use the IEE/IEC notion of prefixes (Ki, Mi, Gi...).
+This flag has no effect when
+.Dv HN_DIVISOR_1000
+is also specified.
+.El
+.Sh RETURN VALUES
+Upon success, the
+.Nm
+function returns the number of characters that would have been stored in
+.Fa buf
+(excluding the terminating
+.Dv NUL )
+if
+.Fa buf
+was large enough, or \-1 upon failure.
+Even upon failure, the contents of
+.Fa buf
+may be modified.
+If
+.Dv HN_GETSCALE
+is specified, the prefix index number will be returned instead.
+.Sh SEE ALSO
+.Xr expand_number 3
+.Sh STANDARDS
+The
+.Dv HN_DIVISOR_1000
+and
+.Dv HN_IEC_PREFIXES
+flags
+conform to
+.Tn ISO/IEC
+Std\~80000-13:2008
+and
+.Tn IEEE
+Std\~1541-2002.
+.Sh HISTORY
+The
+.Fn humanize_number
+function first appeared in
+.Nx 2.0
+and then in
+.Fx 5.3 .
+The
+.Dv HN_IEC_PREFIXES
+flag was introduced in
+.Fx 9.0 .
diff --git a/lib/libutil/humanize_number.c b/lib/libutil/humanize_number.c
new file mode 100644
index 0000000..b773422
--- /dev/null
+++ b/lib/libutil/humanize_number.c
@@ -0,0 +1,179 @@
+/* $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
+ * Copyright 2013 John-Mark Gurney <jmg@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <libutil.h>
+
+static const int maxscale = 7;
+
+int
+humanize_number(char *buf, size_t len, int64_t quotient,
+ const char *suffix, int scale, int flags)
+{
+ const char *prefixes, *sep;
+ int i, r, remainder, s1, s2, sign;
+ int divisordeccut;
+ int64_t divisor, max;
+ size_t baselen;
+
+ /* Since so many callers don't check -1, NUL terminate the buffer */
+ if (len > 0)
+ buf[0] = '\0';
+
+ /* validate args */
+ if (buf == NULL || suffix == NULL)
+ return (-1);
+ if (scale < 0)
+ return (-1);
+ else if (scale >= maxscale &&
+ ((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
+ return (-1);
+ if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
+ return (-1);
+
+ /* setup parameters */
+ remainder = 0;
+
+ if (flags & HN_IEC_PREFIXES) {
+ baselen = 2;
+ /*
+ * Use the prefixes for power of two recommended by
+ * the International Electrotechnical Commission
+ * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
+ *
+ * HN_IEC_PREFIXES implies a divisor of 1024 here
+ * (use of HN_DIVISOR_1000 would have triggered
+ * an assertion earlier).
+ */
+ divisor = 1024;
+ divisordeccut = 973; /* ceil(.95 * 1024) */
+ if (flags & HN_B)
+ prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
+ else
+ prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
+ } else {
+ baselen = 1;
+ if (flags & HN_DIVISOR_1000) {
+ divisor = 1000;
+ divisordeccut = 950;
+ if (flags & HN_B)
+ prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+ else
+ prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+ } else {
+ divisor = 1024;
+ divisordeccut = 973; /* ceil(.95 * 1024) */
+ if (flags & HN_B)
+ prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+ else
+ prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+ }
+ }
+
+#define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])
+
+ if (quotient < 0) {
+ sign = -1;
+ quotient = -quotient;
+ baselen += 2; /* sign, digit */
+ } else {
+ sign = 1;
+ baselen += 1; /* digit */
+ }
+ if (flags & HN_NOSPACE)
+ sep = "";
+ else {
+ sep = " ";
+ baselen++;
+ }
+ baselen += strlen(suffix);
+
+ /* Check if enough room for `x y' + suffix + `\0' */
+ if (len < baselen + 1)
+ return (-1);
+
+ if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
+ /* See if there is additional columns can be used. */
+ for (max = 1, i = len - baselen; i-- > 0;)
+ max *= 10;
+
+ /*
+ * Divide the number until it fits the given column.
+ * If there will be an overflow by the rounding below,
+ * divide once more.
+ */
+ for (i = 0;
+ (quotient >= max || (quotient == max - 1 &&
+ remainder >= divisordeccut)) && i < maxscale; i++) {
+ remainder = quotient % divisor;
+ quotient /= divisor;
+ }
+
+ if (scale & HN_GETSCALE)
+ return (i);
+ } else {
+ for (i = 0; i < scale && i < maxscale; i++) {
+ remainder = quotient % divisor;
+ quotient /= divisor;
+ }
+ }
+
+ /* If a value <= 9.9 after rounding and ... */
+ /*
+ * XXX - should we make sure there is enough space for the decimal
+ * place and if not, don't do HN_DECIMAL?
+ */
+ if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&
+ i > 0 && flags & HN_DECIMAL) {
+ s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
+ divisor / 10);
+ s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
+ r = snprintf(buf, len, "%d%s%d%s%s%s",
+ sign * s1, localeconv()->decimal_point, s2,
+ sep, SCALE2PREFIX(i), suffix);
+ } else
+ r = snprintf(buf, len, "%" PRId64 "%s%s%s",
+ sign * (quotient + (remainder + divisor / 2) / divisor),
+ sep, SCALE2PREFIX(i), suffix);
+
+ return (r);
+}
diff --git a/lib/libutil/kinfo_getallproc.3 b/lib/libutil/kinfo_getallproc.3
new file mode 100644
index 0000000..7005d4f
--- /dev/null
+++ b/lib/libutil/kinfo_getallproc.3
@@ -0,0 +1,75 @@
+.\"
+.\" Copyright (c) 2009 Ulf Lilleengen
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2015
+.Dt KINFO_GETALLPROC 3
+.Os
+.Sh NAME
+.Nm kinfo_getallproc
+.Nd function for getting process information of all processes from kernel
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/user.h
+.In libutil.h
+.Ft struct kinfo_proc *
+.Fn kinfo_getallproc "int *cntp"
+.Sh DESCRIPTION
+This function is used for obtaining process information of all processes from
+the kernel.
+.Pp
+The
+.Ar cntp
+field is a pointer containing the number of process structures returned.
+This function is a wrapper around
+.Xr sysctl 3
+with the
+.Dv KERN_PROC_PROC
+mib.
+While the kernel returns a packed structure, this function expands the
+data into a fixed record format.
+.Sh RETURN VALUES
+On success the
+.Fn kinfo_getallproc
+function returns a pointer to
+.Ar cntp
+.Vt struct kinfo_proc
+structures as defined by
+.In sys/user.h .
+The pointer was obtained by an internal call to
+.Xr malloc 3
+and must be freed by the caller with a call to
+.Xr free 3 .
+On failure the
+.Fn kinfo_getallproc
+function returns
+.Dv NULL .
+.Sh SEE ALSO
+.Xr free 3 ,
+.Xr malloc 3 ,
+.Xr sysctl 3
diff --git a/lib/libutil/kinfo_getallproc.c b/lib/libutil/kinfo_getallproc.c
new file mode 100644
index 0000000..0f43ce2
--- /dev/null
+++ b/lib/libutil/kinfo_getallproc.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * Copyright (c) 2009 Ulf Lilleengen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libutil.h"
+
+
+/*
+ * Sort processes first by pid and then tid.
+ */
+static int
+kinfo_proc_compare(const void *a, const void *b)
+{
+ int i;
+
+ i = ((const struct kinfo_proc *)a)->ki_pid -
+ ((const struct kinfo_proc *)b)->ki_pid;
+ if (i != 0)
+ return (i);
+ i = ((const struct kinfo_proc *)a)->ki_tid -
+ ((const struct kinfo_proc *)b)->ki_tid;
+ return (i);
+}
+
+static void
+kinfo_proc_sort(struct kinfo_proc *kipp, int count)
+{
+
+ qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
+}
+
+struct kinfo_proc *
+kinfo_getallproc(int *cntp)
+{
+ struct kinfo_proc *kipp;
+ size_t len;
+ int mib[3];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PROC;
+
+ len = 0;
+ if (sysctl(mib, 3, NULL, &len, NULL, 0) < 0)
+ return (NULL);
+
+ kipp = malloc(len);
+ if (kipp == NULL)
+ return (NULL);
+
+ if (sysctl(mib, 3, kipp, &len, NULL, 0) < 0)
+ goto bad;
+ if (len % sizeof(*kipp) != 0)
+ goto bad;
+ if (kipp->ki_structsize != sizeof(*kipp))
+ goto bad;
+ *cntp = len / sizeof(*kipp);
+ kinfo_proc_sort(kipp, len / sizeof(*kipp));
+ return (kipp);
+bad:
+ *cntp = 0;
+ free(kipp);
+ return (NULL);
+}
diff --git a/lib/libutil/kinfo_getfile.3 b/lib/libutil/kinfo_getfile.3
new file mode 100644
index 0000000..2a35687
--- /dev/null
+++ b/lib/libutil/kinfo_getfile.3
@@ -0,0 +1,80 @@
+.\"
+.\" Copyright (c) 2008 Peter Wemm
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2015
+.Dt KINFO_GETFILE 3
+.Os
+.Sh NAME
+.Nm kinfo_getfile
+.Nd function for getting per-process file descriptor information
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/user.h
+.In libutil.h
+.Ft struct kinfo_file *
+.Fn kinfo_getfile "pid_t pid" "int *cntp"
+.Sh DESCRIPTION
+This function is used for obtaining the file descriptor information
+of a particular process.
+.Pp
+The
+.Ar pid
+field contains the process identifier.
+This should be the a process that you have privilege to access.
+The
+.Ar cntp
+field allows the caller to know how many records are returned.
+.Pp
+This function is a wrapper around
+.Xr sysctl 3
+with the
+.Dv KERN_PROC_FILEDESC
+mib.
+While the kernel returns a packed structure, this function expands the
+data into a fixed record format.
+.Sh RETURN VALUES
+On success the
+.Fn kinfo_getfile
+function returns a pointer to an array of
+.Vt struct kinfo_file
+structures as defined by
+.In sys/user.h .
+The array was obtained by an internal call to
+.Xr malloc 3
+and must be freed by the caller with a call to
+.Xr free 3 .
+On failure the
+.Fn kinfo_getfile
+function returns
+.Dv NULL .
+.Sh SEE ALSO
+.Xr free 3 ,
+.Xr kinfo_getvmmap 3 ,
+.Xr malloc 3 ,
+.Xr sysctl 3
diff --git a/lib/libutil/kinfo_getfile.c b/lib/libutil/kinfo_getfile.c
new file mode 100644
index 0000000..8a5477f
--- /dev/null
+++ b/lib/libutil/kinfo_getfile.c
@@ -0,0 +1,77 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libutil.h"
+
+struct kinfo_file *
+kinfo_getfile(pid_t pid, int *cntp)
+{
+ int mib[4];
+ int error;
+ int cnt;
+ size_t len;
+ char *buf, *bp, *eb;
+ struct kinfo_file *kif, *kp, *kf;
+
+ *cntp = 0;
+ len = 0;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_FILEDESC;
+ mib[3] = pid;
+
+ error = sysctl(mib, 4, NULL, &len, NULL, 0);
+ if (error)
+ return (NULL);
+ len = len * 4 / 3;
+ buf = malloc(len);
+ if (buf == NULL)
+ return (NULL);
+ error = sysctl(mib, 4, buf, &len, NULL, 0);
+ if (error) {
+ free(buf);
+ return (NULL);
+ }
+ /* Pass 1: count items */
+ cnt = 0;
+ bp = buf;
+ eb = buf + len;
+ while (bp < eb) {
+ kf = (struct kinfo_file *)(uintptr_t)bp;
+ if (kf->kf_structsize == 0)
+ break;
+ bp += kf->kf_structsize;
+ cnt++;
+ }
+
+ kif = calloc(cnt, sizeof(*kif));
+ if (kif == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ bp = buf;
+ eb = buf + len;
+ kp = kif;
+ /* Pass 2: unpack */
+ while (bp < eb) {
+ kf = (struct kinfo_file *)(uintptr_t)bp;
+ if (kf->kf_structsize == 0)
+ break;
+ /* Copy/expand into pre-zeroed buffer */
+ memcpy(kp, kf, kf->kf_structsize);
+ /* Advance to next packed record */
+ bp += kf->kf_structsize;
+ /* Set field size to fixed length, advance */
+ kp->kf_structsize = sizeof(*kp);
+ kp++;
+ }
+ free(buf);
+ *cntp = cnt;
+ return (kif); /* Caller must free() return value */
+}
diff --git a/lib/libutil/kinfo_getproc.3 b/lib/libutil/kinfo_getproc.3
new file mode 100644
index 0000000..f5ccbc5
--- /dev/null
+++ b/lib/libutil/kinfo_getproc.3
@@ -0,0 +1,74 @@
+.\"
+.\" Copyright (c) 2009 Ulf Lilleengen
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2015
+.Dt KINFO_GETPROC 3
+.Os
+.Sh NAME
+.Nm kinfo_getproc
+.Nd function for getting process information from kernel
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/user.h
+.In libutil.h
+.Ft struct kinfo_proc *
+.Fn kinfo_getproc "pid_t pid"
+.Sh DESCRIPTION
+This function is used for obtaining process information from the kernel.
+.Pp
+The
+.Ar pid
+field contains the process identifier.
+This should be a process that you have privilege to access.
+This function is a wrapper around
+.Xr sysctl 3
+with the
+.Dv KERN_PROC_PID
+mib.
+While the kernel returns a packed structure, this function expands the
+data into a fixed record format.
+.Sh RETURN VALUES
+On success the
+.Fn kinfo_getproc
+function returns a pointer to a
+.Vt struct kinfo_proc
+structure as defined by
+.In sys/user.h .
+The pointer was obtained by an internal call to
+.Xr malloc 3
+and must be freed by the caller with a call to
+.Xr free 3 .
+On failure the
+.Fn kinfo_getproc
+function returns
+.Dv NULL .
+.Sh SEE ALSO
+.Xr free 3 ,
+.Xr malloc 3 ,
+.Xr sysctl 3
diff --git a/lib/libutil/kinfo_getproc.c b/lib/libutil/kinfo_getproc.c
new file mode 100644
index 0000000..2ae6b57
--- /dev/null
+++ b/lib/libutil/kinfo_getproc.c
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2009 Ulf Lilleengen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libutil.h"
+
+struct kinfo_proc *
+kinfo_getproc(pid_t pid)
+{
+ struct kinfo_proc *kipp;
+ int mib[4];
+ size_t len;
+
+ len = 0;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+ if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0)
+ return (NULL);
+
+ kipp = malloc(len);
+ if (kipp == NULL)
+ return (NULL);
+
+ if (sysctl(mib, 4, kipp, &len, NULL, 0) < 0)
+ goto bad;
+ if (len != sizeof(*kipp))
+ goto bad;
+ if (kipp->ki_structsize != sizeof(*kipp))
+ goto bad;
+ if (kipp->ki_pid != pid)
+ goto bad;
+ return (kipp);
+bad:
+ free(kipp);
+ return (NULL);
+}
diff --git a/lib/libutil/kinfo_getvmmap.3 b/lib/libutil/kinfo_getvmmap.3
new file mode 100644
index 0000000..c6a1370
--- /dev/null
+++ b/lib/libutil/kinfo_getvmmap.3
@@ -0,0 +1,79 @@
+.\"
+.\" Copyright (c) 2008 Peter Wemm
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2015
+.Dt KINFO_GETVMMAP 3
+.Os
+.Sh NAME
+.Nm kinfo_getvmmap
+.Nd function for getting per-process memory map information
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/user.h
+.In libutil.h
+.Ft struct kinfo_vmentry *
+.Fn kinfo_getvmmap "pid_t pid" "int *cntp"
+.Sh DESCRIPTION
+This function is used for obtaining virtual memory mapping information
+of a particular process.
+.Pp
+The
+.Ar pid
+field contains the process identifier.
+This should be the a process that you have privilege to access.
+The
+.Ar cntp
+field is allows the caller to know how many records are returned.
+.Pp
+This function is a wrapper around
+.Xr sysctl 3
+with the
+.Dv KERN_PROC_VMMAP
+mib.
+While the kernel returns a packed structure, this function expands the
+data into a fixed record format.
+.Sh RETURN VALUES
+On success the
+.Fn kinfo_getvmmap
+function returns a pointer to an array of
+.Vt struct kinfo_vmentry
+structures as defined by
+.In sys/user.h .
+The array was obtained by an internal call to
+.Xr malloc 3
+and must be freed by the caller with a call to
+.Xr free 3 .
+On failure the
+.Fn kinfo_getvmmap
+function returns
+.Dv NULL .
+.Sh SEE ALSO
+.Xr free 3 ,
+.Xr kinfo_getfile 3 ,
+.Xr malloc 3
diff --git a/lib/libutil/kinfo_getvmmap.c b/lib/libutil/kinfo_getvmmap.c
new file mode 100644
index 0000000..9d9e427
--- /dev/null
+++ b/lib/libutil/kinfo_getvmmap.c
@@ -0,0 +1,77 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libutil.h"
+
+struct kinfo_vmentry *
+kinfo_getvmmap(pid_t pid, int *cntp)
+{
+ int mib[4];
+ int error;
+ int cnt;
+ size_t len;
+ char *buf, *bp, *eb;
+ struct kinfo_vmentry *kiv, *kp, *kv;
+
+ *cntp = 0;
+ len = 0;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_VMMAP;
+ mib[3] = pid;
+
+ error = sysctl(mib, 4, NULL, &len, NULL, 0);
+ if (error)
+ return (NULL);
+ len = len * 4 / 3;
+ buf = malloc(len);
+ if (buf == NULL)
+ return (NULL);
+ error = sysctl(mib, 4, buf, &len, NULL, 0);
+ if (error) {
+ free(buf);
+ return (NULL);
+ }
+ /* Pass 1: count items */
+ cnt = 0;
+ bp = buf;
+ eb = buf + len;
+ while (bp < eb) {
+ kv = (struct kinfo_vmentry *)(uintptr_t)bp;
+ if (kv->kve_structsize == 0)
+ break;
+ bp += kv->kve_structsize;
+ cnt++;
+ }
+
+ kiv = calloc(cnt, sizeof(*kiv));
+ if (kiv == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ bp = buf;
+ eb = buf + len;
+ kp = kiv;
+ /* Pass 2: unpack */
+ while (bp < eb) {
+ kv = (struct kinfo_vmentry *)(uintptr_t)bp;
+ if (kv->kve_structsize == 0)
+ break;
+ /* Copy/expand into pre-zeroed buffer */
+ memcpy(kp, kv, kv->kve_structsize);
+ /* Advance to next packed record */
+ bp += kv->kve_structsize;
+ /* Set field size to fixed length, advance */
+ kp->kve_structsize = sizeof(*kp);
+ kp++;
+ }
+ free(buf);
+ *cntp = cnt;
+ return (kiv); /* Caller must free() return value */
+}
diff --git a/lib/libutil/kinfo_getvmobject.3 b/lib/libutil/kinfo_getvmobject.3
new file mode 100644
index 0000000..dc0edd2
--- /dev/null
+++ b/lib/libutil/kinfo_getvmobject.3
@@ -0,0 +1,74 @@
+.\"
+.\" Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 27, 2015
+.Dt KINFO_GETVMOBJECT 3
+.Os
+.Sh NAME
+.Nm kinfo_getvmobject
+.Nd function for getting system-wide memory information
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/user.h
+.In libutil.h
+.Ft struct kinfo_vmobject *
+.Fn kinfo_getvmobject "int *cntp"
+.Sh DESCRIPTION
+This function is used to obtain information about the objects using memory
+in the system.
+.Pp
+The
+.Ar cntp
+argument allows the caller to know how many records are returned.
+.Pp
+This function is a wrapper around the
+.Dq vm.objects
+.Xr sysctl 3
+MIB.
+While the kernel returns a packed structure, this function expands the
+data into a fixed record format.
+.Sh RETURN VALUES
+On success the
+.Fn kinfo_getvmobject
+function returns a pointer to an array of
+.Vt struct kinfo_vmobject
+structures as defined by
+.In sys/user.h .
+The array is allocated by an internal call to
+.Xr malloc 3
+and must be freed by the caller with a call to
+.Xr free 3 .
+On failure the
+.Fn kinfo_getvmobject
+function returns
+.Dv NULL .
+.Sh SEE ALSO
+.Xr free 3 ,
+.Xr kinfo_getvmmap 3 ,
+.Xr malloc 3
diff --git a/lib/libutil/kinfo_getvmobject.c b/lib/libutil/kinfo_getvmobject.c
new file mode 100644
index 0000000..7e031da
--- /dev/null
+++ b/lib/libutil/kinfo_getvmobject.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2013 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libutil.h"
+
+struct kinfo_vmobject *
+kinfo_getvmobject(int *cntp)
+{
+ char *buf, *bp, *ep;
+ struct kinfo_vmobject *kvo, *list, *kp;
+ size_t len;
+ int cnt, i;
+
+ buf = NULL;
+ for (i = 0; i < 3; i++) {
+ if (sysctlbyname("vm.objects", NULL, &len, NULL, 0) < 0)
+ return (NULL);
+ buf = reallocf(buf, len);
+ if (buf == NULL)
+ return (NULL);
+ if (sysctlbyname("vm.objects", buf, &len, NULL, 0) == 0)
+ goto unpack;
+ if (errno != ENOMEM) {
+ free(buf);
+ return (NULL);
+ }
+ }
+ free(buf);
+ return (NULL);
+
+unpack:
+ /* Count items */
+ cnt = 0;
+ bp = buf;
+ ep = buf + len;
+ while (bp < ep) {
+ kvo = (struct kinfo_vmobject *)(uintptr_t)bp;
+ bp += kvo->kvo_structsize;
+ cnt++;
+ }
+
+ list = calloc(cnt, sizeof(*list));
+ if (list == NULL) {
+ free(buf);
+ return (NULL);
+ }
+
+ /* Unpack */
+ bp = buf;
+ kp = list;
+ while (bp < ep) {
+ kvo = (struct kinfo_vmobject *)(uintptr_t)bp;
+ memcpy(kp, kvo, kvo->kvo_structsize);
+ bp += kvo->kvo_structsize;
+ kp->kvo_structsize = sizeof(*kp);
+ kp++;
+ }
+ free(buf);
+ *cntp = cnt;
+ return (list);
+}
diff --git a/lib/libutil/kld.3 b/lib/libutil/kld.3
new file mode 100644
index 0000000..f6b2c13
--- /dev/null
+++ b/lib/libutil/kld.3
@@ -0,0 +1,93 @@
+.\"-
+.\" Copyright (c) 2006 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 18, 2006
+.Dt KLD 3
+.Os
+.Sh NAME
+.Nm kld_isloaded ,
+.Nm kld_load
+.Nd kld utility functions
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fn kld_isloaded "const char *name"
+.Ft int
+.Fn kld_load "const char *name"
+.Sh DESCRIPTION
+These functions facilitate loading kernel modules from userland
+applications.
+.Pp
+The
+.Fn kld_isloaded
+function takes a name and returns a non-zero value if a module of that
+name is currently loaded.
+The name can be either the name of a module file
+.Pq Pa cpufreq.ko ,
+the same name without the
+.Pa .ko
+extension
+.Pq Pa cpufreq ,
+or the name of a module contained within that file
+.Pq Pa cpu/ichss .
+Only the latter will return correct results if the module is compiled
+into the kernel.
+.Pp
+The
+.Fn kld_load
+function is a simple wrapper around the
+.Xr kldload 2
+function.
+It returns zero if and only if the corresponding
+.Fn kldload
+call succeeded or returned
+.Er EEXIST
+(signifying that the requested module was already loaded).
+.Sh SEE ALSO
+.Xr kldfirstmod 2 ,
+.Xr kldload 2 ,
+.Xr kldnext 2 ,
+.Xr kldstat 2 ,
+.Xr modfnext 2 ,
+.Xr modstat 2 ,
+.Xr kld 4
+.Sh HISTORY
+The
+.Fn kld_isloaded
+and
+.Fn kld_load
+functions first appeared in
+.Fx 6.3 .
+.Sh AUTHORS
+The
+.Fn kld_isloaded
+and
+.Fn kld_load
+functions and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org .
diff --git a/lib/libutil/kld.c b/lib/libutil/kld.c
new file mode 100644
index 0000000..b4a6ab6
--- /dev/null
+++ b/lib/libutil/kld.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2006 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <errno.h>
+#include <libutil.h>
+#include <string.h>
+
+int
+kld_isloaded(const char *name)
+{
+ struct kld_file_stat fstat;
+ struct module_stat mstat;
+ const char *ko;
+ int fid, mid;
+
+ for (fid = kldnext(0); fid > 0; fid = kldnext(fid)) {
+ fstat.version = sizeof(fstat);
+ if (kldstat(fid, &fstat) != 0)
+ continue;
+ /* check if the file name matches the supplied name */
+ if (strcmp(fstat.name, name) == 0)
+ return (1);
+ /* strip .ko and try again */
+ if ((ko = strstr(fstat.name, ".ko")) != NULL &&
+ strlen(name) == (size_t)(ko - fstat.name) &&
+ strncmp(fstat.name, name, ko - fstat.name) == 0)
+ return (1);
+ /* look for a matching module within the file */
+ for (mid = kldfirstmod(fid); mid > 0; mid = modfnext(mid)) {
+ mstat.version = sizeof(mstat);
+ if (modstat(mid, &mstat) != 0)
+ continue;
+ if (strcmp(mstat.name, name) == 0)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+kld_load(const char *name)
+{
+ if (kldload(name) == -1 && errno != EEXIST)
+ return (-1);
+ return (0);
+}
diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h
new file mode 100644
index 0000000..b20ffa2
--- /dev/null
+++ b/lib/libutil/libutil.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1996 Peter Wemm <peter@FreeBSD.org>.
+ * All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LIBUTIL_H_
+#define _LIBUTIL_H_
+
+#include <sys/cdefs.h>
+#include <sys/_types.h>
+#include <sys/_stdint.h>
+
+#ifndef _GID_T_DECLARED
+typedef __gid_t gid_t;
+#define _GID_T_DECLARED
+#endif
+
+#ifndef _MODE_T_DECLARED
+typedef __mode_t mode_t;
+#define _MODE_T_DECLARED
+#endif
+
+#ifndef _PID_T_DECLARED
+typedef __pid_t pid_t;
+#define _PID_T_DECLARED
+#endif
+
+#ifndef _SIZE_T_DECLARED
+typedef __size_t size_t;
+#define _SIZE_T_DECLARED
+#endif
+
+#ifndef _UID_T_DECLARED
+typedef __uid_t uid_t;
+#define _UID_T_DECLARED
+#endif
+
+#define PROPERTY_MAX_NAME 64
+#define PROPERTY_MAX_VALUE 512
+
+/* For properties.c. */
+typedef struct _property {
+ struct _property *next;
+ char *name;
+ char *value;
+} *properties;
+
+/* Avoid pulling in all the include files for no need. */
+struct in_addr;
+struct pidfh;
+struct sockaddr;
+struct termios;
+struct winsize;
+
+__BEGIN_DECLS
+char *auth_getval(const char *_name);
+void clean_environment(const char * const *_white,
+ const char * const *_more_white);
+int expand_number(const char *_buf, uint64_t *_num);
+int extattr_namespace_to_string(int _attrnamespace, char **_string);
+int extattr_string_to_namespace(const char *_string, int *_attrnamespace);
+int flopen(const char *_path, int _flags, ...);
+int forkpty(int *_amaster, char *_name,
+ struct termios *_termp, struct winsize *_winp);
+void hexdump(const void *_ptr, int _length, const char *_hdr, int _flags);
+int humanize_number(char *_buf, size_t _len, int64_t _number,
+ const char *_suffix, int _scale, int _flags);
+struct kinfo_file *
+ kinfo_getfile(pid_t _pid, int *_cntp);
+struct kinfo_vmentry *
+ kinfo_getvmmap(pid_t _pid, int *_cntp);
+struct kinfo_vmobject *
+ kinfo_getvmobject(int *_cntp);
+struct kinfo_proc *
+ kinfo_getallproc(int *_cntp);
+struct kinfo_proc *
+ kinfo_getproc(pid_t _pid);
+int kld_isloaded(const char *_name);
+int kld_load(const char *_name);
+int login_tty(int _fd);
+int openpty(int *_amaster, int *_aslave, char *_name,
+ struct termios *_termp, struct winsize *_winp);
+int pidfile_close(struct pidfh *_pfh);
+int pidfile_fileno(const struct pidfh *_pfh);
+struct pidfh *
+ pidfile_open(const char *_path, mode_t _mode, pid_t *_pidptr);
+int pidfile_remove(struct pidfh *_pfh);
+int pidfile_write(struct pidfh *_pfh);
+void properties_free(properties _list);
+char *property_find(properties _list, const char *_name);
+properties
+ properties_read(int _fd);
+int realhostname(char *_host, size_t _hsize, const struct in_addr *_ip);
+int realhostname_sa(char *_host, size_t _hsize, struct sockaddr *_addr,
+ int _addrlen);
+int _secure_path(const char *_path, uid_t _uid, gid_t _gid);
+void trimdomain(char *_fullhost, int _hostsize);
+const char *
+ uu_lockerr(int _uu_lockresult);
+int uu_lock(const char *_ttyname);
+int uu_unlock(const char *_ttyname);
+int uu_lock_txfr(const char *_ttyname, pid_t _pid);
+
+/*
+ * Conditionally prototype the following functions if the include
+ * files upon which they depend have been included.
+ */
+#ifdef _STDIO_H_
+char *fparseln(FILE *_fp, size_t *_len, size_t *_lineno,
+ const char _delim[3], int _flags);
+#endif
+
+#ifdef _PWD_H_
+int pw_copy(int _ffd, int _tfd, const struct passwd *_pw,
+ struct passwd *_old_pw);
+struct passwd
+ *pw_dup(const struct passwd *_pw);
+int pw_edit(int _notsetuid);
+int pw_equal(const struct passwd *_pw1, const struct passwd *_pw2);
+void pw_fini(void);
+int pw_init(const char *_dir, const char *_master);
+char *pw_make(const struct passwd *_pw);
+char *pw_make_v7(const struct passwd *_pw);
+int pw_mkdb(const char *_user);
+int pw_lock(void);
+struct passwd *
+ pw_scan(const char *_line, int _flags);
+const char *
+ pw_tempname(void);
+int pw_tmp(int _mfd);
+#endif
+
+#ifdef _GRP_H_
+int gr_copy(int __ffd, int _tfd, const struct group *_gr,
+ struct group *_old_gr);
+struct group *
+ gr_dup(const struct group *_gr);
+struct group *
+ gr_add(const struct group *_gr, const char *_newmember);
+int gr_equal(const struct group *_gr1, const struct group *_gr2);
+void gr_fini(void);
+int gr_init(const char *_dir, const char *_master);
+int gr_lock(void);
+char *gr_make(const struct group *_gr);
+int gr_mkdb(void);
+struct group *
+ gr_scan(const char *_line);
+int gr_tmp(int _mdf);
+#endif
+
+#ifdef _UFS_UFS_QUOTA_H_
+struct fstab;
+struct quotafile;
+int quota_check_path(const struct quotafile *_qf, const char *_path);
+void quota_close(struct quotafile *_qf);
+int quota_convert(struct quotafile *_qf, int _wordsize);
+const char *
+ quota_fsname(const struct quotafile *_qf);
+int quota_maxid(struct quotafile *_qf);
+int quota_off(struct quotafile *_qf);
+int quota_on(struct quotafile *_qf);
+struct quotafile *
+ quota_open(struct fstab *_fs, int _quotatype, int _openflags);
+const char *
+ quota_qfname(const struct quotafile *_qf);
+int quota_read(struct quotafile *_qf, struct dqblk *_dqb, int _id);
+int quota_write_limits(struct quotafile *_qf, struct dqblk *_dqb, int _id);
+int quota_write_usage(struct quotafile *_qf, struct dqblk *_dqb, int _id);
+#endif
+
+__END_DECLS
+
+/* fparseln(3) */
+#define FPARSELN_UNESCESC 0x01
+#define FPARSELN_UNESCCONT 0x02
+#define FPARSELN_UNESCCOMM 0x04
+#define FPARSELN_UNESCREST 0x08
+#define FPARSELN_UNESCALL 0x0f
+
+/* Flags for hexdump(3). */
+#define HD_COLUMN_MASK 0xff
+#define HD_DELIM_MASK 0xff00
+#define HD_OMIT_COUNT (1 << 16)
+#define HD_OMIT_HEX (1 << 17)
+#define HD_OMIT_CHARS (1 << 18)
+
+/* Values for humanize_number(3)'s flags parameter. */
+#define HN_DECIMAL 0x01
+#define HN_NOSPACE 0x02
+#define HN_B 0x04
+#define HN_DIVISOR_1000 0x08
+#define HN_IEC_PREFIXES 0x10
+
+/* Values for humanize_number(3)'s scale parameter. */
+#define HN_GETSCALE 0x10
+#define HN_AUTOSCALE 0x20
+
+/* Return values from realhostname(). */
+#define HOSTNAME_FOUND 0
+#define HOSTNAME_INCORRECTNAME 1
+#define HOSTNAME_INVALIDADDR 2
+#define HOSTNAME_INVALIDNAME 3
+
+/* Flags for pw_scan(). */
+#define PWSCAN_MASTER 0x01
+#define PWSCAN_WARN 0x02
+
+/* Return values from uu_lock(). */
+#define UU_LOCK_INUSE 1
+#define UU_LOCK_OK 0
+#define UU_LOCK_OPEN_ERR (-1)
+#define UU_LOCK_READ_ERR (-2)
+#define UU_LOCK_CREAT_ERR (-3)
+#define UU_LOCK_WRITE_ERR (-4)
+#define UU_LOCK_LINK_ERR (-5)
+#define UU_LOCK_TRY_ERR (-6)
+#define UU_LOCK_OWNER_ERR (-7)
+
+#endif /* !_LIBUTIL_H_ */
diff --git a/lib/libutil/login.conf.5 b/lib/libutil/login.conf.5
new file mode 100644
index 0000000..e780940
--- /dev/null
+++ b/lib/libutil/login.conf.5
@@ -0,0 +1,471 @@
+.\" Copyright (c) 1996 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" this list of conditions, and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 8, 2011
+.Dt LOGIN.CONF 5
+.Os
+.Sh NAME
+.Nm login.conf
+.Nd login class capability database
+.Sh SYNOPSIS
+.Pa /etc/login.conf ,
+.Pa ~/.login_conf
+.Sh DESCRIPTION
+.Nm
+contains various attributes and capabilities of login classes.
+A login class (an optional annotation against each record in the user
+account database,
+.Pa /etc/master.passwd )
+determines session accounting, resource limits and user environment settings.
+It is used by various programs in the system to set up a user's login
+environment and to enforce policy, accounting and administrative restrictions.
+It also provides the means by which users are able to be
+authenticated to the system and the types of authentication available.
+Attributes in addition to the ones described here are available with
+third-party packages.
+.Pp
+A special record "default" in the system user class capability database
+.Pa /etc/login.conf
+is used automatically for any
+non-root user without a valid login class in
+.Pa /etc/master.passwd .
+A user with a uid of 0 without a valid login class will use the record
+"root" if it exists, or "default" if not.
+.Pp
+Users may individually create a file called
+.Pa .login_conf
+in their home directory using the same format, consisting of a single
+entry with a record id of "me".
+If present, this file is used by
+.Xr login 1
+to set user-defined environment settings which override those specified
+in the system login capabilities database.
+Only a subset of login capabilities may be overridden, typically those
+which do not involve authentication, resource limits and accounting.
+.Pp
+Records in a class capabilities database consist of a number of
+colon-separated fields.
+The first entry for each record gives one or more names that a record is
+to be known by, each separated by a '|' character.
+The first name is the most common abbreviation.
+The last name given should be a long name that is more descriptive
+of the capability entry, and all others are synonyms.
+All names but the last should be in lower case and contain no blanks;
+the last name may contain upper case characters and blanks for
+readability.
+.Pp
+Note that since a colon
+.Pq Ql :\&
+is used to separate capability entries, a
+.Ql \ec
+escape sequence must be used to embed a literal colon in the
+value or name of a capability.
+.Pp
+The default
+.Pa /etc/login.conf
+shipped with
+.Fx
+is an out of the box configuration.
+Whenever changes to this, or
+the user's
+.Pa ~/.login_conf ,
+file are made, the modifications will not be picked up until
+.Xr cap_mkdb 1
+is used to compile the file into a database.
+This database file will have a
+.Pa .db
+extension and is accessed through
+.Xr cgetent 3 .
+See
+.Xr getcap 3
+for a more in-depth description of the format of a capability database.
+.Sh CAPABILITIES
+Fields within each record in the database follow the
+.Xr getcap 3
+conventions for boolean, type string
+.Ql \&=
+and type numeric
+.Ql \&# ,
+although type numeric is deprecated in favour of the string format and
+either form is accepted for a numeric datum.
+Values fall into the following categories:
+.Bl -tag -width "program"
+.It bool
+If the name is present, then the boolean value is true; otherwise, it is
+false
+.It file
+Path name to a data file
+.It program
+Path name to an executable file
+.It list
+A list of values (or pairs of values) separated by commas or spaces
+.It path
+A space or comma separated list of path names, following the usual csh
+conventions (leading tilde with and without username being expanded to
+home directories etc.)
+.It number
+A numeric value, either decimal (default), hexadecimal (with leading 0x),
+or octal (with a leading 0).
+With a numeric type, only one numeric value is allowed.
+Numeric types may also be specified in string format (i.e., the capability
+tag being delimited from the value by '=' instead of '#').
+Whichever method is used, then all records in the database must use the
+same method to allow values to be correctly overridden in interpolated
+records.
+A numeric value may be infinite.
+.It size
+A number which expresses a size.
+The default interpretation of a value is the number of bytes, but a
+suffix may specify alternate units:
+.Bl -tag -offset indent -compact -width xxxx
+.It b
+explicitly selects 512-byte blocks
+.It k
+selects kilobytes (1024 bytes)
+.It m
+specifies a multiplier of 1 megabyte (1048576 bytes),
+.It g
+specifies units of gigabytes, and
+.It t
+represents terabytes.
+.El
+A size value is a numeric quantity and case of the suffix is not significant.
+Concatenated values are added together.
+A size value may be infinite.
+.It time
+A period of time, by default in seconds.
+A prefix may specify a different unit:
+.Bl -tag -offset indent -compact -width xxxx
+.It y
+indicates the number of 365 day years,
+.It w
+indicates the number of weeks,
+.It d
+the number of days,
+.It h
+the number of hours,
+.It m
+the number of minutes, and
+.It s
+the number of seconds.
+.El
+Concatenated values are added together.
+For example, 2 hours and 40 minutes may be written either as
+9600s, 160m or 2h40m.
+A time value may be infinite.
+.El
+.Pp
+.Dq infinity ,
+.Dq inf ,
+.Dq unlimited ,
+.Dq unlimit,
+and -1
+are considered infinite values.
+.Pp
+The usual convention to interpolate capability entries using the special
+.Em tc=value
+notation may be used.
+.Sh RESOURCE LIMITS
+.Bl -column pseudoterminals indent indent
+.It Sy "Name Type Notes Description"
+.It "coredumpsize size Maximum coredump size limit."
+.It "cputime time CPU usage limit."
+.It "datasize size Maximum data size limit."
+.It "filesize size Maximum file size limit."
+.It "maxproc number Maximum number of processes."
+.It "memorylocked size Maximum locked in core memory size limit."
+.It "memoryuse size Maximum of core memory use size limit."
+.It "openfiles number Maximum number of open files per process."
+.It "sbsize size Maximum permitted socketbuffer size."
+.It "vmemoryuse size Maximum permitted total VM usage per process."
+.It "stacksize size Maximum stack size limit."
+.It "pseudoterminals number Maximum number of pseudo-terminals."
+.It "swapuse size Maximum swap space size limit."
+.El
+.Pp
+These resource limit entries actually specify both the maximum
+and current limits (see
+.Xr getrlimit 2 ) .
+The current (soft) limit is the one normally used, although the user is
+permitted to increase the current limit to the maximum (hard) limit.
+The maximum and current limits may be specified individually by appending a
+-max or -cur to the capability name.
+.Sh ENVIRONMENT
+.Bl -column ignorenologin indent xbinxxusrxbin
+.It Sy "Name Type Notes Description"
+.It "charset string Set $MM_CHARSET environment variable to the specified"
+value.
+.It "cpumask string List of cpus to bind the user to."
+The syntax is the same as for the
+.Fl l
+argument of
+.Xr cpuset 1
+or the word
+.Ql default .
+If set to
+.Ql default
+no action is taken.
+.It "hushlogin bool false Same as having a ~/.hushlogin file."
+.It "ignorenologin bool false Login not prevented by nologin."
+.It "ftp-chroot bool false Limit FTP access with"
+.Xr chroot 2
+to the
+.Ev HOME
+directory of the user.
+See
+.Xr ftpd 8
+for details.
+.It "label string Default MAC policy; see"
+.Xr maclabel 7 .
+.It "lang string Set $LANG environment variable to the specified value."
+.It "manpath path Default search path for manpages."
+.It "nocheckmail bool false Display mail status at login."
+.It "nologin file If the file exists it will be displayed and"
+the login session will be terminated.
+.It "path path /bin /usr/bin Default search path."
+.It "priority number Initial priority (nice) level."
+.It "requirehome bool false Require a valid home directory to login."
+.It "setenv list A comma-separated list of environment variables and"
+values to which they are to be set.
+.It "shell prog Session shell to execute rather than the"
+shell specified in the passwd file.
+The SHELL environment variable will
+contain the shell specified in the password file.
+.It "term string Default terminal type if not able to determine"
+from other means.
+.It "timezone string Default value of $TZ environment variable."
+.It "umask number 022 Initial umask. Should always have a leading 0 to"
+ensure octal interpretation.
+.It "welcome file /etc/motd File containing welcome message."
+.El
+.Sh AUTHENTICATION
+.Bl -column passwd_prompt indent indent
+.It Sy "Name Type Notes Description"
+.\" .It "approve program Program to approve login.
+.It "copyright file File containing additional copyright information"
+.It "host.allow list List of remote host wildcards from which users in"
+the class may access.
+.It "host.deny list List of remote host wildcards from which users"
+in the class may not access.
+.It "login_prompt string The login prompt given by"
+.Xr login 1
+.It "login-backoff number 3 The number of login attempts"
+allowed before the backoff delay is inserted after each subsequent
+attempt.
+The backoff delay is the number of tries above
+.Em login-backoff
+multiplied by 5 seconds.
+.It "login-retries number 10 The number of login attempts"
+allowed before the login fails.
+.It "passwd_format string sha512 The encryption format that new or"
+changed passwords will use.
+Valid values include "des", "md5", "blf", "sha256" and "sha512"; see
+.Xr crypt 3
+for details.
+NIS clients using a
+.No non- Ns Fx
+NIS server should probably use "des".
+.It "passwd_prompt string The password prompt presented by"
+.Xr login 1
+.It "times.allow list List of time periods during which"
+logins are allowed.
+.It "times.deny list List of time periods during which logins are"
+disallowed.
+.It "ttys.allow list List of ttys and ttygroups which users"
+in the class may use for access.
+.It "ttys.deny list List of ttys and ttygroups which users"
+in the class may not use for access.
+.It "warnexpire time Advance notice for pending account expiry."
+.It "warnpassword time Advance notice for pending password expiry."
+.\".It "widepasswords bool false Use the wide password format. The wide password
+.\" format allows up to 128 significant characters in the password.
+.El
+.Pp
+These fields are intended to be used by
+.Xr passwd 1
+and other programs in the login authentication system.
+.Pp
+Capabilities that set environment variables are scanned for both
+.Ql \&~
+and
+.Ql \&$
+characters, which are substituted for a user's home directory and name
+respectively.
+To pass these characters literally into the environment variable, escape
+the character by preceding it with a backslash '\\'.
+.Pp
+The
+.Em host.allow
+and
+.Em host.deny
+entries are comma separated lists used for checking remote access to the system,
+and consist of a list of hostnames and/or IP addresses against which remote
+network logins are checked.
+Items in these lists may contain wildcards in the form used by shell programs
+for wildcard matching (See
+.Xr fnmatch 3
+for details on the implementation).
+The check on hosts is made against both the remote system's Internet address
+and hostname (if available).
+If both lists are empty or not specified, then logins from any remote host
+are allowed.
+If host.allow contains one or more hosts, then only remote systems matching
+any of the items in that list are allowed to log in.
+If host.deny contains one or more hosts, then a login from any matching hosts
+will be disallowed.
+.Pp
+The
+.Em times.allow
+and
+.Em times.deny
+entries consist of a comma-separated list of time periods during which the users
+in a class are allowed to be logged in.
+These are expressed as one or more day codes followed by a start and end times
+expressed in 24 hour format, separated by a hyphen or dash.
+For example, MoThSa0200-1300 translates to Monday, Thursday and Saturday between
+the hours of 2 am and 1 p.m..
+If both of these time lists are empty, users in the class are allowed access at
+any time.
+If
+.Em times.allow
+is specified, then logins are only allowed during the periods given.
+If
+.Em times.deny
+is specified, then logins are denied during the periods given, regardless of whether
+one of the periods specified in
+.Em times.allow
+applies.
+.Pp
+Note that
+.Xr login 1
+enforces only that the actual login falls within periods allowed by these entries.
+Further enforcement over the life of a session requires a separate daemon to
+monitor transitions from an allowed period to a non-allowed one.
+.Pp
+The
+.Em ttys.allow
+and
+.Em ttys.deny
+entries contain a comma-separated list of tty devices (without the /dev/ prefix)
+that a user in a class may use to access the system, and/or a list of ttygroups
+(See
+.Xr getttyent 3
+and
+.Xr ttys 5
+for information on ttygroups).
+If neither entry exists, then the choice of login device used by the user is
+unrestricted.
+If only
+.Em ttys.allow
+is specified, then the user is restricted only to ttys in the given
+group or device list.
+If only
+.Em ttys.deny
+is specified, then the user is prevented from using the specified devices or
+devices in the group.
+If both lists are given and are non-empty, the user is restricted to those
+devices allowed by ttys.allow that are not available by ttys.deny.
+.Pp
+The
+.Em minpasswordlen
+and
+.Em minpasswordcase
+facilities for enforcing restrictions on password quality, which used
+to be supported by
+.Nm ,
+have been superseded by the
+.Xr pam_passwdqc 8
+PAM module.
+.Sh RESERVED CAPABILITIES
+The following capabilities are reserved for the purposes indicated and
+may be supported by third-party software.
+They are not implemented in the base system.
+.Bl -column host.accounted indent indent
+.It Sy "Name Type Notes Description"
+.It "accounted bool false Enable session time accounting for all users"
+in this class.
+.It "auth list passwd Allowed authentication styles."
+The first item is the default style.
+.It "auth-" Ns Ar type Ta "list Allowed authentication styles for the"
+authentication
+.Ar type .
+.It "autodelete time Time after expiry when account is auto-deleted."
+.It "bootfull bool false Enable 'boot only if ttygroup is full' strategy"
+when terminating sessions.
+.It "daytime time Maximum login time per day."
+.It "expireperiod time Time for expiry allocation."
+.It "graceexpire time Grace days for expired account."
+.It "gracetime time Additional grace login time allowed."
+.It "host.accounted list List of remote host wildcards from which"
+login sessions will be accounted.
+.It "host.exempt list List of remote host wildcards from which"
+login session accounting is exempted.
+.It "idletime time Maximum idle time before logout."
+.It "minpasswordlen number 6 The minimum length a local"
+password may be.
+.It "mixpasswordcase bool true Whether"
+.Xr passwd 1
+will warn the user if an all lower case password is entered.
+.It "monthtime time Maximum login time per month."
+.It "passwordtime time Used by"
+.Xr passwd 1
+to set next password expiry date.
+.It "refreshtime time New time allowed on account refresh."
+.It "refreshperiod str How often account time is refreshed."
+.It "sessiontime time Maximum login time per session."
+.It "sessionlimit number Maximum number of concurrent"
+login sessions on ttys in any group.
+.It "ttys.accounted list List of ttys and ttygroups for which"
+login accounting is active.
+.It "ttys.exempt list List of ttys and ttygroups for which login accounting"
+is exempt.
+.It "warntime time Advance notice for pending out-of-time."
+.It "weektime time Maximum login time per week."
+.El
+.Pp
+The
+.Em ttys.accounted
+and
+.Em ttys.exempt
+fields operate in a similar manner to
+.Em ttys.allow
+and
+.Em ttys.deny
+as explained
+above.
+Similarly with the
+.Em host.accounted
+and
+.Em host.exempt
+lists.
+.Sh SEE ALSO
+.Xr cap_mkdb 1 ,
+.Xr login 1 ,
+.Xr chroot 2 ,
+.Xr getcap 3 ,
+.Xr getttyent 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr pam 3 ,
+.Xr passwd 5 ,
+.Xr ttys 5 ,
+.Xr ftpd 8 ,
+.Xr pam_passwdqc 8
diff --git a/lib/libutil/login_auth.3 b/lib/libutil/login_auth.3
new file mode 100644
index 0000000..003f6e9
--- /dev/null
+++ b/lib/libutil/login_auth.3
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" this list of conditions, and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 29, 1996
+.Dt LOGIN_AUTH 3
+.Os
+.Sh NAME
+.\" .Nm authenticate
+.\" .Nm auth_script
+.\" .Nm auth_env
+.\" .Nm auth_scan
+.\" .Nm auth_rmfiles
+.Nm auth_checknologin ,
+.Nm auth_cat
+.\" .Nm auth_ttyok
+.\" .Nm auth_hostok
+.\" .Nm auth_timesok
+.Nd "authentication style support library for login class capabilities database"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In login_cap.h
+.\" .Ft int
+.\". Fn authenticate "const char *name" "const char *classname" "const char *style" "const char *service"
+.\" .Ft int
+.\" .Fn auth_script "const char * path" ...
+.\" .Ft void
+.\" .Fn auth_env "void"
+.\" .Ft int
+.\" .Fn auth_scan "int ok"
+.\" .Ft void
+.\" .Fn auth_rmfiles "void"
+.Ft void
+.Fn auth_checknologin "login_cap_t *lc"
+.Ft int
+.Fn auth_cat "const char *file"
+.\" .Ft int
+.\" .Fn auth_ttyok "login_cap_t *lc" "const char *tty"
+.\" .Ft int
+.\" .Fn auth_hostok "login_cap_t *lc" "const char *hostname" "char const *ip"
+.\" .Ft int
+.\" .Fn auth_timesok "login_cap_t *lc" "time_t now"
+.Sh DESCRIPTION
+This set of functions support the login class authorisation style interface provided
+by
+.Xr login.conf 5 .
+.\" .Sh RETURN VALUES
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_auth.c b/lib/libutil/login_auth.c
new file mode 100644
index 0000000..1fc7e73
--- /dev/null
+++ b/lib/libutil/login_auth.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Portions copyright (c) 1995,1997 by
+ * Berkeley Software Design, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Low-level routines relating to the user capabilities database
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <limits.h>
+#include <login_cap.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+
+/*
+ * auth_checknologin()
+ * Checks for the existance of a nologin file in the login_cap
+ * capability <lc>. If there isn't one specified, then it checks
+ * to see if this class should just ignore nologin files. Lastly,
+ * it tries to print out the default nologin file, and, if such
+ * exists, it exits.
+ */
+
+void
+auth_checknologin(login_cap_t *lc)
+{
+ const char *file;
+
+ /* Do we ignore a nologin file? */
+ if (login_getcapbool(lc, "ignorenologin", 0))
+ return;
+
+ /* Note that <file> will be "" if there is no nologin capability */
+ if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL)
+ exit(1);
+
+ /*
+ * *file is true IFF there was a "nologin" capability
+ * Note that auth_cat() returns 1 only if the specified
+ * file exists, and is readable. E.g., /.nologin exists.
+ */
+ if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN))
+ exit(1);
+}
+
+
+/*
+ * auth_cat()
+ * Checks for the readability of <file>; if it can be opened for
+ * reading, it prints it out to stdout, and then exits. Otherwise,
+ * it returns 0 (meaning no nologin file).
+ */
+
+int
+auth_cat(const char *file)
+{
+ int fd, count;
+ char buf[BUFSIZ];
+
+ if ((fd = open(file, O_RDONLY | O_CLOEXEC)) < 0)
+ return 0;
+ while ((count = read(fd, buf, sizeof(buf))) > 0)
+ (void)write(fileno(stdout), buf, count);
+ close(fd);
+ sleep(5); /* wait an arbitrary time to drain */
+ return 1;
+}
diff --git a/lib/libutil/login_cap.3 b/lib/libutil/login_cap.3
new file mode 100644
index 0000000..9421718
--- /dev/null
+++ b/lib/libutil/login_cap.3
@@ -0,0 +1,579 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" this list of conditions, and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 14, 2007
+.Dt LOGIN_CAP 3
+.Os
+.Sh NAME
+.Nm login_close ,
+.Nm login_getcapbool ,
+.Nm login_getcaplist ,
+.Nm login_getcapnum ,
+.Nm login_getcapstr ,
+.Nm login_getcapsize ,
+.Nm login_getcaptime ,
+.Nm login_getclass ,
+.Nm login_getclassbyname ,
+.Nm login_getpwclass ,
+.Nm login_getstyle ,
+.Nm login_getuserclass ,
+.Nm login_setcryptfmt
+.Nd "functions for accessing the login class capabilities database"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In login_cap.h
+.Ft void
+.Fn login_close "login_cap_t *lc"
+.Ft login_cap_t *
+.Fn login_getclassbyname "const char *nam" "const struct passwd *pwd"
+.Ft login_cap_t *
+.Fn login_getclass "const char *nam"
+.Ft login_cap_t *
+.Fn login_getpwclass "const struct passwd *pwd"
+.Ft login_cap_t *
+.Fn login_getuserclass "const struct passwd *pwd"
+.Ft "const char *"
+.Fn login_getcapstr "login_cap_t *lc" "const char *cap" "const char *def" "const char *error"
+.Ft "const char **"
+.Fn login_getcaplist "login_cap_t *lc" "const char *cap" "const char *chars"
+.Ft "const char *"
+.Fn login_getpath "login_cap_t *lc" "const char *cap" "const char *error"
+.Ft rlim_t
+.Fn login_getcaptime "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error"
+.Ft rlim_t
+.Fn login_getcapnum "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error"
+.Ft rlim_t
+.Fn login_getcapsize "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error"
+.Ft int
+.Fn login_getcapbool "login_cap_t *lc" "const char *cap" "int def"
+.Ft "const char *"
+.Fn login_getstyle "login_cap_t *lc" "const char *style" "const char *auth"
+.Ft const char *
+.Fn login_setcryptfmt "login_cap_t *lc" "const char *def" "const char *error"
+.Sh DESCRIPTION
+These functions represent a programming interface to the login
+classes database provided in
+.Xr login.conf 5 .
+This database contains capabilities, attributes and default environment
+and accounting settings for users and programs running as specific users,
+as determined by the login class field within entries in
+.Pa /etc/master.passwd .
+.Pp
+Entries in
+.Xr login.conf 5
+consist of colon
+.Ql \&:
+separated fields, the first field in each record being one or more
+identifiers for the record (which must be unique for the entire database),
+each separated by a
+.Ql | ,
+and may optionally include a description as
+the last
+.Sq name .
+Remaining fields in the record consist of keyword/data pairs.
+Long lines may be continued with a backslash within empty entries,
+with the second and subsequent lines optionally indented for readability.
+This is similar to the format used in
+.Xr termcap 5 ,
+except that keywords are not limited to two significant characters,
+and are usually longer for improved readability.
+As with termcap entries, multiple records can be linked together
+(one record including another) using a field containing
+.Ql tc= Ns Va <recordid> .
+The result is that the entire record referenced by
+.Va <recordid>
+replaces the
+.Va tc=
+field at the point at which it occurs.
+See
+.Xr getcap 3
+for further details on the format and use of a capabilities database.
+.Pp
+The
+.Nm login_cap
+interface provides a convenient means of retrieving login class
+records with all
+.Va tc=
+references expanded.
+A program will typically call one of
+.Fn login_getclass ,
+.Fn login_getpwclass ,
+.Fn login_getuserclass
+or
+.Fn login_getclassbyname
+according to its requirements.
+Each of these functions returns a login capabilities structure,
+.Vt login_cap_t ,
+which may subsequently be used to interrogate the database for
+specific values using the rest of the API.
+Once the
+.Vt login_cap_t
+is of no further use, the
+.Fn login_close
+function should be called to free all resources used.
+.Pp
+The structure of
+.Vt login_cap_t
+is defined in
+.In login_cap.h ,
+as:
+.Bd -literal -offset indent
+typedef struct {
+ char *lc_class;
+ char *lc_cap;
+ char *lc_style;
+} login_cap_t;
+.Ed
+.Pp
+The
+.Fa lc_class
+member contains a pointer to the name of the login class
+retrieved.
+This may not necessarily be the same as the one requested,
+either directly via
+.Fn login_getclassbyname ,
+or indirectly via a user's login record using
+.Fn login_getpwclass ,
+by class name using
+.Fn login_getclass .
+If the referenced user has no login class specified in
+.Pa /etc/master.passwd ,
+the class name is
+.Dv NULL
+or an empty string.
+If the class
+specified does not exist in the database, each of these
+functions will search for a record with an id of
+.Ql default ,
+with that name returned in the
+.Fa lc_class
+field.
+In addition, if the referenced user has a UID of 0 (normally,
+.Ql root ,
+although the user name is not considered) then
+.Fn login_getpwclass
+will search for a record with an id of
+.Ql root
+before it searches
+for the record with the id of
+.Ql default .
+.Pp
+The
+.Fa lc_cap
+field is used internally by the library to contain the
+expanded login capabilities record.
+Programs with unusual requirements may wish to use this
+with the lower-level
+.Fn getcap
+style functions to access the record directly.
+.Pp
+The
+.Fa lc_style
+field is set by the
+.Fn login_getstyle
+function to the authorisation style, according to the requirements
+of the program handling a login itself.
+.Pp
+The
+.Fn login_getclassbyname
+function is the basic means to get a
+.Vt login_cap_t
+object.
+It accepts two arguments: the first one,
+.Fa name ,
+is the record identifier of the
+record to be retrieved; the second,
+.Fa pwd ,
+is an optional pointer to a
+.Vt passwd
+structure.
+First of all, its arguments are used by the function
+to choose between system and user modes of operation.
+When in system mode, only the system login class database is used.
+When in user mode, the supplemental login class database in the
+user's home directory is allowed to override settings from the system
+database in a limited way as noted below.
+To minimize security implications, user mode is entered by
+.Fn login_getclassbyname
+if and only if
+.Fa name
+is
+.Dv LOGIN_MECLASS
+.Pq Ql me
+and
+.Fa pwd
+is not
+.Dv NULL .
+Otherwise system mode is chosen.
+.Pp
+In system mode, any record in the system database
+.Pa /etc/login.conf
+can be accessed,
+and a fallback to the default record is provided as follows.
+If
+.Fa name
+is
+.Dv NULL ,
+an empty string, or a class that does not exist
+in the login class database, then the
+.Dv LOGIN_DEFCLASS
+record
+.Pq Ql default
+is returned instead.
+.Pp
+In user mode, only the
+.Dv LOGIN_MECLASS
+record
+.Pq Ql me
+is accessed and no fallback to the
+.Ql default
+record is provided.
+The directory specified by
+.Fa pwd->pw_dir
+is searched for
+a login database file called
+.Pa .login_conf ,
+and only the
+.Ql me
+capability record
+contained within it may override the system record with the same name
+while other records are ignored.
+Using this scheme, an application can explicitly
+allow users to override a selected subset of login settings.
+To do so, the application should obtain two
+.Vt login_cap_t
+objects, one in user mode and the other in system mode,
+and then query the user object before the
+system object for login parameters that are allowed to
+be overridden by the user.
+For example, the user's
+.Pa .login_conf
+can provide a convenient way for a user to set up their preferred
+login environment before the shell is invoked on login if supported by
+.Xr login 1 .
+.Pp
+Note that access to the
+.Pa /etc/login.conf
+and
+.Pa .login_conf
+files will only be performed subject to the security checks documented in
+.Xr _secure_path 3
+for the uids 0 and
+.Fa pwd->pw_uid
+respectively.
+.Pp
+If the specified record is
+.Dv NULL ,
+empty or does not exist, and the
+system has no
+.Ql default
+record available to fall back to, there is a
+memory allocation error or for some reason
+.Xr cgetent 3
+is unable to access the login capabilities database, this function
+returns
+.Dv NULL .
+.Pp
+The functions
+.Fn login_getclass ,
+.Fn login_getpwclass
+and
+.Fn login_getuserclass
+retrieve the applicable login class record for the user's passwd
+entry or class name by calling
+.Fn login_getclassbyname .
+On failure,
+.Dv NULL
+is returned.
+The difference between these functions is that
+.Fn login_getuserclass
+includes the user's overriding
+.Pa .login_conf
+that exists in the user's home directory, and
+.Fn login_getpwclass
+and
+.Fn login_getclass
+restrict lookup only to the system login class database in
+.Pa /etc/login.conf .
+As explained earlier,
+.Fn login_getpwclass
+differs from
+.Fn login_getclass
+in that it allows the default class for a super-user as
+.Ql root
+if none has been specified in the password database.
+Otherwise, if the passwd pointer is
+.Dv NULL ,
+or the user record
+has no login class, then the system
+.Ql default
+entry is retrieved.
+Essentially,
+.Fn login_getclass name
+is equivalent to
+.Fn login_getclassbyname name NULL
+and
+.Fn login_getuserclass pwd
+to
+.Fn login_getclassbyname LOGIN_MECLASS pwd .
+.Pp
+Once a program no longer wishes to use a
+.Vt login_cap_t
+object,
+.Fn login_close
+may be called to free all resources used by the login class.
+The
+.Fn login_close
+function may be passed a
+.Dv NULL
+pointer with no harmful side-effects.
+.Pp
+The remaining functions may be used to retrieve individual
+capability records.
+Each function takes a
+.Vt login_cap_t
+object as its first parameter,
+a capability tag as the second, and remaining parameters being
+default and error values that are returned if the capability is
+not found.
+The type of the additional parameters passed and returned depend
+on the
+.Em type
+of capability each deals with, be it a simple string, a list,
+a time value, a file or memory size value, a path (consisting of
+a colon-separated list of directories) or a boolean flag.
+The manpage for
+.Xr login.conf 5
+deals in specific tags and their type.
+.Pp
+Note that with all functions in this group, you should not call
+.Xr free 3
+on any pointers returned.
+Memory allocated during retrieval or processing of capability
+tags is automatically reused by subsequent calls to functions
+in this group, or deallocated on calling
+.Fn login_close .
+.Bl -tag -width "login_getcaplist()"
+.It Fn login_getcapstr
+This function returns a simple string capability.
+If the string is not found, then the value in
+.Fa def
+is returned as the default value, or if an error
+occurs, the value in the
+.Fa error
+parameter is returned.
+.It Fn login_getcaplist
+This function returns the value corresponding to the named
+capability tag as a list of values in a
+.Dv NULL
+terminated array.
+Within the login class database, some tags are of type
+.Vt list ,
+which consist of one or more comma- or space separated
+values.
+Usually, this function is not called directly from an
+application, but is used indirectly via
+.Fn login_getstyle .
+.It Fn login_getpath
+This function returns a list of directories separated by colons
+.Ql \&: .
+Capability tags for which this function is called consist of a list of
+directories separated by spaces.
+.It Fn login_getcaptime
+This function returns a
+.Vt time value
+associated with a particular capability tag with the value expressed
+in seconds (the default), minutes, hours, days, weeks or (365 day)
+years or any combination of these.
+A suffix determines the units used:
+.Ql S
+for seconds,
+.Ql M
+for minutes,
+.Ql H
+for hours,
+.Ql D
+for days,
+.Ql W
+for weeks and
+.Ql Y
+for 365 day years.
+Case of the units suffix is ignored.
+.Pp
+Time values are normally used for setting resource, accounting and
+session limits.
+If supported by the operating system and compiler (which is true of
+.Fx ) ,
+the value returned is a
+.Vt quad
+.Pq Vt long long ,
+of type
+.Vt rlim_t .
+A value
+.Ql inf
+or
+.Ql infinity
+may be used to express an infinite
+value, in which case
+.Dv RLIM_INFINITY
+is returned.
+.It Fn login_getcapnum
+This function returns a numeric value for a tag, expressed either as
+.Ql tag=<value>
+or the standard
+.Fn cgetnum
+format
+.Ql tag#<value> .
+The first format should be used in preference to the second, the
+second format is provided for compatibility and consistency with the
+.Xr getcap 3
+database format where numeric types use the
+.Ql \&#
+as the delimiter for numeric values.
+If in the first format, then the value given may be
+.Ql inf
+or
+.Ql infinity
+which results in a return value of
+.Dv RLIM_INFINITY .
+If the given capability tag cannot be found, the
+.Fa def
+parameter is returned, and if an error occurs, the
+.Fa error
+parameter is returned.
+.It Fn login_getcapsize
+.Fn login_getcapsize
+returns a value representing a size (typically, file or memory)
+which may be expressed as bytes (the default), 512 byte blocks,
+kilobytes, megabytes, gigabytes, and on systems that support the
+.Vt long long
+type, terabytes.
+The suffix used determines the units, and multiple values and
+units may be used in combination (e.g.\& 1m500k = 1.5 megabytes).
+A value with no suffix is interpreted as bytes,
+.Ql B
+as 512-byte blocks,
+.Ql K
+as kilobytes,
+.Ql M
+as megabytes,
+.Ql G
+as gigabytes and
+.Ql T
+as terabytes.
+Case is ignored.
+The error value is returned if there is a login capabilities database
+error, if an invalid suffix is used, or if a numeric value cannot be
+interpreted.
+.It Fn login_getcapbool
+This function returns a boolean value tied to a particular flag.
+It returns 0 if the given capability tag is not present or is
+negated by the presence of a
+.Ql tag@
+(see
+.Xr getcap 3
+for more information on boolean flags), and returns 1 if the tag
+is found.
+.It Fn login_getstyle
+This function is used by the login authorisation system to determine
+the style of login available in a particular case.
+The function accepts three parameters, the
+.Fa lc
+entry itself and
+two optional parameters, and authorisation type
+.Fa auth
+and
+.Fa style ,
+and
+applies these to determine the authorisation style that best suites
+these rules.
+.Bl -bullet
+.It
+If
+.Fa auth
+is neither
+.Dv NULL
+nor an empty string, look for a tag of type
+.Ql auth- Ns Fa <auth>
+in the capability record.
+If not present, then look for the default tag
+.Va auth= .
+.It
+If no valid authorisation list was found from the previous step, then
+default to
+.Ql passwd
+as the authorisation list.
+.It
+If
+.Fa style
+is not
+.Dv NULL
+or empty, look for it in the list of authorisation
+methods found from the previous step.
+If
+.Fa style
+is
+.Dv NULL
+or an empty string, then default to
+.Ql passwd
+authorisation.
+.It
+If
+.Fa style
+is found in the chosen list of authorisation methods, then
+return that, otherwise return
+.Dv NULL .
+.El
+.Pp
+This scheme allows the administrator to determine the types of
+authorisation methods accepted by the system, depending on the
+means by which the access occurs.
+For example, the administrator may require skey or kerberos as
+the authentication method used for access to the system via the
+network, and standard methods via direct dialup or console
+logins, significantly reducing the risk of password discovery
+by "snooping" network packets.
+.It Fn login_setcryptfmt
+The
+.Fn login_setcryptfmt
+function is used to set the
+.Xr crypt 3
+format using the
+.Va passwd_format
+configuration entry.
+If no entry is found,
+.Fa def
+is taken to be used as the fallback.
+If calling
+.Xr crypt_set_format 3
+on the specifier fails,
+.Fa error
+is returned to indicate this.
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr crypt 3 ,
+.Xr getcap 3 ,
+.Xr login_class 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_cap.c b/lib/libutil/login_cap.c
new file mode 100644
index 0000000..8915d0a
--- /dev/null
+++ b/lib/libutil/login_cap.c
@@ -0,0 +1,819 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Portions copyright (c) 1995,1997
+ * Berkeley Software Design, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Low-level routines relating to the user capabilities database
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <login_cap.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/*
+ * allocstr()
+ * Manage a single static pointer for handling a local char* buffer,
+ * resizing as necessary to contain the string.
+ *
+ * allocarray()
+ * Manage a static array for handling a group of strings, resizing
+ * when necessary.
+ */
+
+static int lc_object_count = 0;
+
+static size_t internal_stringsz = 0;
+static char * internal_string = NULL;
+static size_t internal_arraysz = 0;
+static const char ** internal_array = NULL;
+
+static char path_login_conf[] = _PATH_LOGIN_CONF;
+
+static char *
+allocstr(const char *str)
+{
+ char *p;
+
+ size_t sz = strlen(str) + 1; /* realloc() only if necessary */
+ if (sz <= internal_stringsz)
+ p = strcpy(internal_string, str);
+ else if ((p = realloc(internal_string, sz)) != NULL) {
+ internal_stringsz = sz;
+ internal_string = strcpy(p, str);
+ }
+ return p;
+}
+
+
+static const char **
+allocarray(size_t sz)
+{
+ static const char **p;
+
+ if (sz <= internal_arraysz)
+ p = internal_array;
+ else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) {
+ internal_arraysz = sz;
+ internal_array = p;
+ }
+ return p;
+}
+
+
+/*
+ * arrayize()
+ * Turn a simple string <str> separated by any of
+ * the set of <chars> into an array. The last element
+ * of the array will be NULL, as is proper.
+ * Free using freearraystr()
+ */
+
+static const char **
+arrayize(const char *str, const char *chars, int *size)
+{
+ int i;
+ char *ptr;
+ const char *cptr;
+ const char **res = NULL;
+
+ /* count the sub-strings */
+ for (i = 0, cptr = str; *cptr; i++) {
+ int count = strcspn(cptr, chars);
+ cptr += count;
+ if (*cptr)
+ ++cptr;
+ }
+
+ /* alloc the array */
+ if ((ptr = allocstr(str)) != NULL) {
+ if ((res = allocarray(++i)) == NULL)
+ free((void *)(uintptr_t)(const void *)str);
+ else {
+ /* now split the string */
+ i = 0;
+ while (*ptr) {
+ int count = strcspn(ptr, chars);
+ res[i++] = ptr;
+ ptr += count;
+ if (*ptr)
+ *ptr++ = '\0';
+ }
+ res[i] = NULL;
+ }
+ }
+
+ if (size)
+ *size = i;
+
+ return res;
+}
+
+
+/*
+ * login_close()
+ * Frees up all resources relating to a login class
+ *
+ */
+
+void
+login_close(login_cap_t * lc)
+{
+ if (lc) {
+ free(lc->lc_style);
+ free(lc->lc_class);
+ free(lc->lc_cap);
+ free(lc);
+ if (--lc_object_count == 0) {
+ free(internal_string);
+ free(internal_array);
+ internal_array = NULL;
+ internal_arraysz = 0;
+ internal_string = NULL;
+ internal_stringsz = 0;
+ cgetclose();
+ }
+ }
+}
+
+
+/*
+ * login_getclassbyname()
+ * Get the login class by its name.
+ * If the name given is NULL or empty, the default class
+ * LOGIN_DEFCLASS (i.e., "default") is fetched.
+ * If the name given is LOGIN_MECLASS and
+ * 'pwd' argument is non-NULL and contains an non-NULL
+ * dir entry, then the file _FILE_LOGIN_CONF is picked
+ * up from that directory and used before the system
+ * login database. In that case the system login database
+ * is looked up using LOGIN_MECLASS, too, which is a bug.
+ * Return a filled-out login_cap_t structure, including
+ * class name, and the capability record buffer.
+ */
+
+login_cap_t *
+login_getclassbyname(char const *name, const struct passwd *pwd)
+{
+ login_cap_t *lc;
+
+ if ((lc = malloc(sizeof(login_cap_t))) != NULL) {
+ int r, me, i = 0;
+ uid_t euid = 0;
+ gid_t egid = 0;
+ const char *msg = NULL;
+ const char *dir;
+ char userpath[MAXPATHLEN];
+
+ static char *login_dbarray[] = { NULL, NULL, NULL };
+
+ me = (name != NULL && strcmp(name, LOGIN_MECLASS) == 0);
+ dir = (!me || pwd == NULL) ? NULL : pwd->pw_dir;
+ /*
+ * Switch to user mode before checking/reading its ~/.login_conf
+ * - some NFSes have root read access disabled.
+ *
+ * XXX: This fails to configure additional groups.
+ */
+ if (dir) {
+ euid = geteuid();
+ egid = getegid();
+ (void)setegid(pwd->pw_gid);
+ (void)seteuid(pwd->pw_uid);
+ }
+
+ if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir,
+ _FILE_LOGIN_CONF) < MAXPATHLEN) {
+ if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1)
+ login_dbarray[i++] = userpath;
+ }
+ /*
+ * XXX: Why to add the system database if the class is `me'?
+ */
+ if (_secure_path(path_login_conf, 0, 0) != -1)
+ login_dbarray[i++] = path_login_conf;
+ login_dbarray[i] = NULL;
+
+ memset(lc, 0, sizeof(login_cap_t));
+ lc->lc_cap = lc->lc_class = lc->lc_style = NULL;
+
+ if (name == NULL || *name == '\0')
+ name = LOGIN_DEFCLASS;
+
+ switch (cgetent(&lc->lc_cap, login_dbarray, name)) {
+ case -1: /* Failed, entry does not exist */
+ if (me)
+ break; /* Don't retry default on 'me' */
+ if (i == 0)
+ r = -1;
+ else if ((r = open(login_dbarray[0], O_RDONLY | O_CLOEXEC)) >= 0)
+ close(r);
+ /*
+ * If there's at least one login class database,
+ * and we aren't searching for a default class
+ * then complain about a non-existent class.
+ */
+ if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0)
+ syslog(LOG_ERR, "login_getclass: unknown class '%s'", name);
+ /* fall-back to default class */
+ name = LOGIN_DEFCLASS;
+ msg = "%s: no default/fallback class '%s'";
+ if (cgetent(&lc->lc_cap, login_dbarray, name) != 0 && r >= 0)
+ break;
+ /* FALLTHROUGH - just return system defaults */
+ case 0: /* success! */
+ if ((lc->lc_class = strdup(name)) != NULL) {
+ if (dir) {
+ (void)seteuid(euid);
+ (void)setegid(egid);
+ }
+ ++lc_object_count;
+ return lc;
+ }
+ msg = "%s: strdup: %m";
+ break;
+ case -2:
+ msg = "%s: retrieving class information: %m";
+ break;
+ case -3:
+ msg = "%s: 'tc=' reference loop '%s'";
+ break;
+ case 1:
+ msg = "couldn't resolve 'tc=' reference in '%s'";
+ break;
+ default:
+ msg = "%s: unexpected cgetent() error '%s': %m";
+ break;
+ }
+ if (dir) {
+ (void)seteuid(euid);
+ (void)setegid(egid);
+ }
+ if (msg != NULL)
+ syslog(LOG_ERR, msg, "login_getclass", name);
+ free(lc);
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ * login_getclass()
+ * Get the login class for the system (only) login class database.
+ * Return a filled-out login_cap_t structure, including
+ * class name, and the capability record buffer.
+ */
+
+login_cap_t *
+login_getclass(const char *cls)
+{
+ return login_getclassbyname(cls, NULL);
+}
+
+
+/*
+ * login_getpwclass()
+ * Get the login class for a given password entry from
+ * the system (only) login class database.
+ * If the password entry's class field is not set, or
+ * the class specified does not exist, then use the
+ * default of LOGIN_DEFCLASS (i.e., "default") for an unprivileged
+ * user or that of LOGIN_DEFROOTCLASS (i.e., "root") for a super-user.
+ * Return a filled-out login_cap_t structure, including
+ * class name, and the capability record buffer.
+ */
+
+login_cap_t *
+login_getpwclass(const struct passwd *pwd)
+{
+ const char *cls = NULL;
+
+ if (pwd != NULL) {
+ cls = pwd->pw_class;
+ if (cls == NULL || *cls == '\0')
+ cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
+ }
+ /*
+ * XXX: pwd should be unused by login_getclassbyname() unless cls is `me',
+ * so NULL can be passed instead of pwd for more safety.
+ */
+ return login_getclassbyname(cls, pwd);
+}
+
+
+/*
+ * login_getuserclass()
+ * Get the `me' login class, allowing user overrides via ~/.login_conf.
+ * Note that user overrides are allowed only in the `me' class.
+ */
+
+login_cap_t *
+login_getuserclass(const struct passwd *pwd)
+{
+ return login_getclassbyname(LOGIN_MECLASS, pwd);
+}
+
+
+/*
+ * login_getcapstr()
+ * Given a login_cap entry, and a capability name, return the
+ * value defined for that capability, a default if not found, or
+ * an error string on error.
+ */
+
+const char *
+login_getcapstr(login_cap_t *lc, const char *cap, const char *def, const char *error)
+{
+ char *res;
+ int ret;
+
+ if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0')
+ return def;
+
+ if ((ret = cgetstr(lc->lc_cap, cap, &res)) == -1)
+ return def;
+ return (ret >= 0) ? res : error;
+}
+
+
+/*
+ * login_getcaplist()
+ * Given a login_cap entry, and a capability name, return the
+ * value defined for that capability split into an array of
+ * strings.
+ */
+
+const char **
+login_getcaplist(login_cap_t *lc, const char *cap, const char *chars)
+{
+ const char *lstring;
+
+ if (chars == NULL)
+ chars = ", \t";
+ if ((lstring = login_getcapstr(lc, cap, NULL, NULL)) != NULL)
+ return arrayize(lstring, chars, NULL);
+ return NULL;
+}
+
+
+/*
+ * login_getpath()
+ * From the login_cap_t <lc>, get the capability <cap> which is
+ * formatted as either a space or comma delimited list of paths
+ * and append them all into a string and separate by semicolons.
+ * If there is an error of any kind, return <error>.
+ */
+
+const char *
+login_getpath(login_cap_t *lc, const char *cap, const char *error)
+{
+ const char *str;
+ char *ptr;
+ int count;
+
+ str = login_getcapstr(lc, cap, NULL, NULL);
+ if (str == NULL)
+ return error;
+ ptr = __DECONST(char *, str); /* XXXX Yes, very dodgy */
+ while (*ptr) {
+ count = strcspn(ptr, ", \t");
+ ptr += count;
+ if (*ptr)
+ *ptr++ = ':';
+ }
+ return str;
+}
+
+
+static int
+isinfinite(const char *s)
+{
+ static const char *infs[] = {
+ "infinity",
+ "inf",
+ "unlimited",
+ "unlimit",
+ "-1",
+ NULL
+ };
+ const char **i = &infs[0];
+
+ while (*i != NULL) {
+ if (strcasecmp(s, *i) == 0)
+ return 1;
+ ++i;
+ }
+ return 0;
+}
+
+
+static u_quad_t
+rmultiply(u_quad_t n1, u_quad_t n2)
+{
+ u_quad_t m, r;
+ int b1, b2;
+
+ static int bpw = 0;
+
+ /* Handle simple cases */
+ if (n1 == 0 || n2 == 0)
+ return 0;
+ if (n1 == 1)
+ return n2;
+ if (n2 == 1)
+ return n1;
+
+ /*
+ * sizeof() returns number of bytes needed for storage.
+ * This may be different from the actual number of useful bits.
+ */
+ if (!bpw) {
+ bpw = sizeof(u_quad_t) * 8;
+ while (((u_quad_t)1 << (bpw-1)) == 0)
+ --bpw;
+ }
+
+ /*
+ * First check the magnitude of each number. If the sum of the
+ * magnatude is way to high, reject the number. (If this test
+ * is not done then the first multiply below may overflow.)
+ */
+ for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
+ ;
+ for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
+ ;
+ if (b1 + b2 - 2 > bpw) {
+ errno = ERANGE;
+ return (UQUAD_MAX);
+ }
+
+ /*
+ * Decompose the multiplication to be:
+ * h1 = n1 & ~1
+ * h2 = n2 & ~1
+ * l1 = n1 & 1
+ * l2 = n2 & 1
+ * (h1 + l1) * (h2 + l2)
+ * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
+ *
+ * Since h1 && h2 do not have the low bit set, we can then say:
+ *
+ * (h1>>1 * h2>>1 * 4) + ...
+ *
+ * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
+ * overflow.
+ *
+ * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
+ * then adding in residual amout will cause an overflow.
+ */
+
+ m = (n1 >> 1) * (n2 >> 1);
+ if (m >= ((u_quad_t)1 << (bpw-2))) {
+ errno = ERANGE;
+ return (UQUAD_MAX);
+ }
+ m *= 4;
+
+ r = (n1 & n2 & 1)
+ + (n2 & 1) * (n1 & ~(u_quad_t)1)
+ + (n1 & 1) * (n2 & ~(u_quad_t)1);
+
+ if ((u_quad_t)(m + r) < m) {
+ errno = ERANGE;
+ return (UQUAD_MAX);
+ }
+ m += r;
+
+ return (m);
+}
+
+
+/*
+ * login_getcaptime()
+ * From the login_cap_t <lc>, get the capability <cap>, which is
+ * formatted as a time (e.g., "<cap>=10h3m2s"). If <cap> is not
+ * present in <lc>, return <def>; if there is an error of some kind,
+ * return <error>.
+ */
+
+rlim_t
+login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
+{
+ char *res, *ep, *oval;
+ int r;
+ rlim_t tot;
+
+ errno = 0;
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+
+ /*
+ * Look for <cap> in lc_cap.
+ * If it's not there (-1), return <def>.
+ * If there's an error, return <error>.
+ */
+
+ if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1)
+ return def;
+ else if (r < 0) {
+ errno = ERANGE;
+ return error;
+ }
+
+ /* "inf" and "infinity" are special cases */
+ if (isinfinite(res))
+ return RLIM_INFINITY;
+
+ /*
+ * Now go through the string, turning something like 1h2m3s into
+ * an integral value. Whee.
+ */
+
+ errno = 0;
+ tot = 0;
+ oval = res;
+ while (*res) {
+ rlim_t tim = strtoq(res, &ep, 0);
+ rlim_t mult = 1;
+
+ if (ep == NULL || ep == res || errno != 0) {
+ invalid:
+ syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s",
+ lc->lc_class, cap, oval);
+ errno = ERANGE;
+ return error;
+ }
+ /* Look for suffixes */
+ switch (*ep++) {
+ case 0:
+ ep--;
+ break; /* end of string */
+ case 's': case 'S': /* seconds */
+ break;
+ case 'm': case 'M': /* minutes */
+ mult = 60;
+ break;
+ case 'h': case 'H': /* hours */
+ mult = 60L * 60L;
+ break;
+ case 'd': case 'D': /* days */
+ mult = 60L * 60L * 24L;
+ break;
+ case 'w': case 'W': /* weeks */
+ mult = 60L * 60L * 24L * 7L;
+ break;
+ case 'y': case 'Y': /* 365-day years */
+ mult = 60L * 60L * 24L * 365L;
+ break;
+ default:
+ goto invalid;
+ }
+ res = ep;
+ tot += rmultiply(tim, mult);
+ if (errno)
+ goto invalid;
+ }
+
+ return tot;
+}
+
+
+/*
+ * login_getcapnum()
+ * From the login_cap_t <lc>, extract the numerical value <cap>.
+ * If it is not present, return <def> for a default, and return
+ * <error> if there is an error.
+ * Like login_getcaptime(), only it only converts to a number, not
+ * to a time; "infinity" and "inf" are 'special.'
+ */
+
+rlim_t
+login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
+{
+ char *ep, *res;
+ int r;
+ rlim_t val;
+
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+
+ /*
+ * For BSDI compatibility, try for the tag=<val> first
+ */
+ if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) {
+ long lval;
+ /* string capability not present, so try for tag#<val> as numeric */
+ if ((r = cgetnum(lc->lc_cap, cap, &lval)) == -1)
+ return def; /* Not there, so return default */
+ else if (r >= 0)
+ return (rlim_t)lval;
+ }
+
+ if (r < 0) {
+ errno = ERANGE;
+ return error;
+ }
+
+ if (isinfinite(res))
+ return RLIM_INFINITY;
+
+ errno = 0;
+ val = strtoq(res, &ep, 0);
+ if (ep == NULL || ep == res || errno != 0) {
+ syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s",
+ lc->lc_class, cap, res);
+ errno = ERANGE;
+ return error;
+ }
+
+ return val;
+}
+
+
+
+/*
+ * login_getcapsize()
+ * From the login_cap_t <lc>, extract the capability <cap>, which is
+ * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity".
+ * If not present, return <def>, or <error> if there is an error of
+ * some sort.
+ */
+
+rlim_t
+login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
+{
+ char *ep, *res, *oval;
+ int r;
+ rlim_t tot;
+
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+
+ if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1)
+ return def;
+ else if (r < 0) {
+ errno = ERANGE;
+ return error;
+ }
+
+ if (isinfinite(res))
+ return RLIM_INFINITY;
+
+ errno = 0;
+ tot = 0;
+ oval = res;
+ while (*res) {
+ rlim_t siz = strtoq(res, &ep, 0);
+ rlim_t mult = 1;
+
+ if (ep == NULL || ep == res || errno != 0) {
+ invalid:
+ syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s",
+ lc->lc_class, cap, oval);
+ errno = ERANGE;
+ return error;
+ }
+ switch (*ep++) {
+ case 0: /* end of string */
+ ep--;
+ break;
+ case 'b': case 'B': /* 512-byte blocks */
+ mult = 512;
+ break;
+ case 'k': case 'K': /* 1024-byte Kilobytes */
+ mult = 1024;
+ break;
+ case 'm': case 'M': /* 1024-k kbytes */
+ mult = 1024 * 1024;
+ break;
+ case 'g': case 'G': /* 1Gbyte */
+ mult = 1024 * 1024 * 1024;
+ break;
+ case 't': case 'T': /* 1TBte */
+ mult = 1024LL * 1024LL * 1024LL * 1024LL;
+ break;
+ default:
+ goto invalid;
+ }
+ res = ep;
+ tot += rmultiply(siz, mult);
+ if (errno)
+ goto invalid;
+ }
+
+ return tot;
+}
+
+
+/*
+ * login_getcapbool()
+ * From the login_cap_t <lc>, check for the existance of the capability
+ * of <cap>. Return <def> if <lc>->lc_cap is NULL, otherwise return
+ * the whether or not <cap> exists there.
+ */
+
+int
+login_getcapbool(login_cap_t *lc, const char *cap, int def)
+{
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+ return (cgetcap(lc->lc_cap, cap, ':') != NULL);
+}
+
+
+/*
+ * login_getstyle()
+ * Given a login_cap entry <lc>, and optionally a type of auth <auth>,
+ * and optionally a style <style>, find the style that best suits these
+ * rules:
+ * 1. If <auth> is non-null, look for an "auth-<auth>=" string
+ * in the capability; if not present, default to "auth=".
+ * 2. If there is no auth list found from (1), default to
+ * "passwd" as an authorization list.
+ * 3. If <style> is non-null, look for <style> in the list of
+ * authorization methods found from (2); if <style> is NULL, default
+ * to LOGIN_DEFSTYLE ("passwd").
+ * 4. If the chosen style is found in the chosen list of authorization
+ * methods, return that; otherwise, return NULL.
+ * E.g.:
+ * login_getstyle(lc, NULL, "ftp");
+ * login_getstyle(lc, "login", NULL);
+ * login_getstyle(lc, "skey", "network");
+ */
+
+const char *
+login_getstyle(login_cap_t *lc, const char *style, const char *auth)
+{
+ int i;
+ const char **authtypes = NULL;
+ char *auths= NULL;
+ char realauth[64];
+
+ static const char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL };
+
+ if (auth != NULL && *auth != '\0') {
+ if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < (int)sizeof(realauth))
+ authtypes = login_getcaplist(lc, realauth, NULL);
+ }
+
+ if (authtypes == NULL)
+ authtypes = login_getcaplist(lc, "auth", NULL);
+
+ if (authtypes == NULL)
+ authtypes = defauthtypes;
+
+ /*
+ * We have at least one authtype now; auths is a comma-separated
+ * (or space-separated) list of authentication types. We have to
+ * convert from this to an array of char*'s; authtypes then gets this.
+ */
+ i = 0;
+ if (style != NULL && *style != '\0') {
+ while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0)
+ i++;
+ }
+
+ lc->lc_style = NULL;
+ if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL)
+ lc->lc_style = auths;
+
+ if (lc->lc_style != NULL)
+ lc->lc_style = strdup(lc->lc_style);
+
+ return lc->lc_style;
+}
diff --git a/lib/libutil/login_cap.h b/lib/libutil/login_cap.h
new file mode 100644
index 0000000..ec1421b
--- /dev/null
+++ b/lib/libutil/login_cap.h
@@ -0,0 +1,167 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Low-level routines relating to the user capabilities database
+ *
+ * Was login_cap.h,v 1.9 1997/05/07 20:00:01 eivind Exp
+ * $FreeBSD$
+ */
+
+#ifndef _LOGIN_CAP_H_
+#define _LOGIN_CAP_H_
+
+#define LOGIN_DEFCLASS "default"
+#define LOGIN_DEFROOTCLASS "root"
+#define LOGIN_MECLASS "me"
+#define LOGIN_DEFSTYLE "passwd"
+#define LOGIN_DEFSERVICE "login"
+#define LOGIN_DEFUMASK 022
+#define LOGIN_DEFPRI 0
+#define _PATH_LOGIN_CONF "/etc/login.conf"
+#define _FILE_LOGIN_CONF ".login_conf"
+#define _PATH_AUTHPROG "/usr/libexec/login_"
+
+#define LOGIN_SETGROUP 0x0001 /* set group */
+#define LOGIN_SETLOGIN 0x0002 /* set login (via setlogin) */
+#define LOGIN_SETPATH 0x0004 /* set path */
+#define LOGIN_SETPRIORITY 0x0008 /* set priority */
+#define LOGIN_SETRESOURCES 0x0010 /* set resources (cputime, etc.) */
+#define LOGIN_SETUMASK 0x0020 /* set umask, obviously */
+#define LOGIN_SETUSER 0x0040 /* set user (via setuid) */
+#define LOGIN_SETENV 0x0080 /* set user environment */
+#define LOGIN_SETMAC 0x0100 /* set user default MAC label */
+#define LOGIN_SETCPUMASK 0x0200 /* set user cpumask */
+#define LOGIN_SETLOGINCLASS 0x0400 /* set login class in the kernel */
+#define LOGIN_SETALL 0x07ff /* set everything */
+
+#define BI_AUTH "authorize" /* accepted authentication */
+#define BI_REJECT "reject" /* rejected authentication */
+#define BI_CHALLENG "reject challenge" /* reject with a challenge */
+#define BI_SILENT "reject silent" /* reject silently */
+#define BI_REMOVE "remove" /* remove file on error */
+#define BI_ROOTOKAY "authorize root" /* root authenticated */
+#define BI_SECURE "authorize secure" /* okay on non-secure line */
+#define BI_SETENV "setenv" /* set environment variable */
+#define BI_VALUE "value" /* set local variable */
+
+#define AUTH_OKAY 0x01 /* user authenticated */
+#define AUTH_ROOTOKAY 0x02 /* root login okay */
+#define AUTH_SECURE 0x04 /* secure login */
+#define AUTH_SILENT 0x08 /* silent rejection */
+#define AUTH_CHALLENGE 0x10 /* a chellenge was given */
+
+#define AUTH_ALLOW (AUTH_OKAY | AUTH_ROOTOKAY | AUTH_SECURE)
+
+typedef struct login_cap {
+ char *lc_class;
+ char *lc_cap;
+ char *lc_style;
+} login_cap_t;
+
+typedef struct login_time {
+ u_short lt_start; /* Start time */
+ u_short lt_end; /* End time */
+#define LTM_NONE 0x00
+#define LTM_SUN 0x01
+#define LTM_MON 0x02
+#define LTM_TUE 0x04
+#define LTM_WED 0x08
+#define LTM_THU 0x10
+#define LTM_FRI 0x20
+#define LTM_SAT 0x40
+#define LTM_ANY 0x7F
+#define LTM_WK 0x3E
+#define LTM_WD 0x41
+ u_char lt_dow; /* Days of week */
+} login_time_t;
+
+#define LC_MAXTIMES 64
+
+#include <sys/cdefs.h>
+__BEGIN_DECLS
+struct passwd;
+
+void login_close(login_cap_t *);
+login_cap_t *login_getclassbyname(const char *, const struct passwd *);
+login_cap_t *login_getclass(const char *);
+login_cap_t *login_getpwclass(const struct passwd *);
+login_cap_t *login_getuserclass(const struct passwd *);
+
+const char *login_getcapstr(login_cap_t *, const char *, const char *,
+ const char *);
+const char **login_getcaplist(login_cap_t *, const char *, const char *);
+const char *login_getstyle(login_cap_t *, const char *, const char *);
+rlim_t login_getcaptime(login_cap_t *, const char *, rlim_t, rlim_t);
+rlim_t login_getcapnum(login_cap_t *, const char *, rlim_t, rlim_t);
+rlim_t login_getcapsize(login_cap_t *, const char *, rlim_t, rlim_t);
+const char *login_getpath(login_cap_t *, const char *, const char *);
+int login_getcapbool(login_cap_t *, const char *, int);
+const char *login_setcryptfmt(login_cap_t *, const char *, const char *);
+
+int setclasscontext(const char *, unsigned int);
+void setclasscpumask(login_cap_t *);
+int setusercontext(login_cap_t *, const struct passwd *, uid_t, unsigned int);
+void setclassresources(login_cap_t *);
+void setclassenvironment(login_cap_t *, const struct passwd *, int);
+
+/* Most of these functions are deprecated */
+int auth_approve(login_cap_t *, const char *, const char *);
+int auth_check(const char *, const char *, const char *, const char *, int *);
+void auth_env(void);
+char *auth_mkvalue(const char *);
+int auth_response(const char *, const char *, const char *, const char *, int *,
+ const char *, const char *);
+void auth_rmfiles(void);
+int auth_scan(int);
+int auth_script(const char *, ...);
+int auth_script_data(const char *, int, const char *, ...);
+char *auth_valud(const char *);
+int auth_setopt(const char *, const char *);
+void auth_clropts(void);
+
+void auth_checknologin(login_cap_t *);
+int auth_cat(const char *);
+
+int auth_ttyok(login_cap_t *, const char *);
+int auth_hostok(login_cap_t *, const char *, char const *);
+int auth_timeok(login_cap_t *, time_t);
+
+struct tm;
+
+login_time_t parse_lt(const char *);
+int in_lt(const login_time_t *, time_t *);
+int in_ltm(const login_time_t *, struct tm *, time_t *);
+int in_ltms(const login_time_t *, struct tm *, time_t *);
+int in_lts(const login_time_t *, time_t *);
+
+/* helper functions */
+
+int login_strinlist(const char **, char const *, int);
+int login_str2inlist(const char **, const char *, const char *, int);
+login_time_t * login_timelist(login_cap_t *, char const *, int *,
+ login_time_t **);
+int login_ttyok(login_cap_t *, const char *, const char *, const char *);
+int login_hostok(login_cap_t *, const char *, const char *, const char *,
+ const char *);
+
+__END_DECLS
+
+#endif /* _LOGIN_CAP_H_ */
diff --git a/lib/libutil/login_class.3 b/lib/libutil/login_class.3
new file mode 100644
index 0000000..c87faae
--- /dev/null
+++ b/lib/libutil/login_class.3
@@ -0,0 +1,222 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" this list of conditions, and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 24, 2011
+.Dt LOGIN_CLASS 3
+.Os
+.Sh NAME
+.Nm setclasscontext ,
+.Nm setclasscpumask ,
+.Nm setclassenvironment ,
+.Nm setclassresources ,
+.Nm setusercontext
+.Nd "functions for using the login class capabilities database"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In login_cap.h
+.Ft int
+.Fn setclasscontext "const char *classname" "unsigned int flags"
+.Ft void
+.Fn setclasscpumask "login_cap_t *lc"
+.Ft void
+.Fn setclassenvironment "login_cap_t *lc" "const struct passwd *pwd" "int paths"
+.Ft void
+.Fn setclassresources "login_cap_t *lc"
+.Ft int
+.Fn setusercontext "login_cap_t *lc" "const struct passwd *pwd" "uid_t uid" "unsigned int flags"
+.Sh DESCRIPTION
+These functions provide a higher level interface to the login class
+database than those documented in
+.Xr login_cap 3 .
+These functions are used to set resource limits, environment and
+accounting settings for users on logging into the system and when
+selecting an appropriate set of environment and resource settings
+for system daemons based on login classes.
+These functions may only be called if the current process is
+running with root privileges.
+If the LOGIN_SETLOGIN flag is used this function calls
+.Xr setlogin 2 ,
+and due care must be taken as detailed in the manpage for that
+function and this affects all processes running in the same session
+and not just the current process.
+.Pp
+The
+.Fn setclasscontext
+function sets various class context values (resource limits, umask and
+process priorities) based on values for a specific named class.
+.Pp
+The
+.Fn setusercontext
+function sets class context values based on a given login_cap_t
+object and a specific passwd record (if login_cap_t is NULL),
+the current session's login, and the current process
+user and group ownership.
+Each of these actions is selectable via bit-flags passed
+in the
+.Ar flags
+parameter, which is comprised of one or more of the following:
+.Bl -tag -width LOGIN_SETLOGINCLASS
+.It LOGIN_SETLOGIN
+Set the login associated with the current session to the user
+specified in the passwd structure using
+.Xr setlogin 2 .
+The
+.Ar pwd
+parameter must not be NULL if this option is used.
+.It LOGIN_SETUSER
+Set ownership of the current process to the uid specified in the
+.Ar uid
+parameter using
+.Xr setuid 2 .
+.It LOGIN_SETGROUP
+Set group ownership of the current process to the group id
+specified in the passwd structure using
+.Xr setgid 2 ,
+and calls
+.Xr initgroups 3
+to set up the group access list for the current process.
+The
+.Ar pwd
+parameter must not be NULL if this option is used.
+.It LOGIN_SETRESOURCES
+Set resource limits for the current process based on values
+specified in the system login class database.
+Class capability tags used, with and without -cur (soft limit)
+or -max (hard limit) suffixes and the corresponding resource
+setting:
+.Bd -literal
+cputime RLIMIT_CPU
+filesize RLIMIT_FSIZE
+datasize RLIMIT_DATA
+stacksize RLIMIT_STACK
+coredumpsize RLIMIT_CORE
+memoryuse RLIMIT_RSS
+memorylocked RLIMIT_MEMLOCK
+maxproc RLIMIT_NPROC
+openfiles RLIMIT_NOFILE
+sbsize RLIMIT_SBSIZE
+vmemoryuse RLIMIT_VMEM
+pseudoterminals RLIMIT_NPTS
+swapuse RLIMIT_SWAP
+kqueues RLIMIT_KQUEUES
+.Ed
+.It LOGIN_SETPRIORITY
+Set the scheduling priority for the current process based on the
+value specified in the system login class database.
+Class capability tags used:
+.Bd -literal
+priority
+.Ed
+.It LOGIN_SETUMASK
+Set the umask for the current process to a value in the user or
+system login class database.
+Class capability tags used:
+.Bd -literal
+umask
+.Ed
+.It LOGIN_SETPATH
+Set the "path" and "manpath" environment variables based on values
+in the user or system login class database.
+Class capability tags used with the corresponding environment
+variables set:
+.Bd -literal
+path PATH
+manpath MANPATH
+.Ed
+.It LOGIN_SETENV
+Set various environment variables based on values in the user or
+system login class database.
+Class capability tags used with the corresponding environment
+variables set:
+.Bd -literal
+lang LANG
+charset MM_CHARSET
+timezone TZ
+term TERM
+.Ed
+.Pp
+Additional environment variables may be set using the list type
+capability "setenv=var1 val1,var2 val2..,varN valN".
+.It LOGIN_SETMAC
+Set the MAC label for the current process to the label specified
+in system login class database.
+.It LOGIN_SETCPUMASK
+Create a new
+.Xr cpuset 2
+and set the cpu affinity to the specified mask.
+The string may contain a comma separated list of numbers and/or number
+ranges as handled by the
+.Xr cpuset 1
+utility or the case-insensitive string
+.Ql default .
+If the string is
+.Ql default
+no action will be taken.
+.It LOGIN_SETLOGINCLASS
+Set the login class of the current process using
+.Xr setloginclass 2 .
+.It LOGIN_SETALL
+Enables all of the above settings.
+.El
+.Pp
+Note that when setting environment variables and a valid passwd
+pointer is provided in the
+.Ar pwd
+parameter, the characters
+.Ql \&~
+and
+.Ql \&$
+are substituted for the user's home directory and login name
+respectively.
+.Pp
+The
+.Fn setclasscpumask ,
+.Fn setclassresources
+and
+.Fn setclassenvironment
+functions are subsets of the setcontext functions above, but may
+be useful in isolation.
+.Sh RETURN VALUES
+The
+.Fn setclasscontext
+and
+.Fn setusercontext
+functions return -1 if an error occurred, or 0 on success.
+If an error occurs when attempting to set the user, login, group
+or resources, a message is reported to
+.Xr syslog 3 ,
+with LOG_ERR priority and directed to the currently active facility.
+.Sh SEE ALSO
+.Xr cpuset 1 ,
+.Xr ps 1 ,
+.Xr cpuset 2 ,
+.Xr setgid 2 ,
+.Xr setlogin 2 ,
+.Xr setloginclass 2 ,
+.Xr setuid 2 ,
+.Xr getcap 3 ,
+.Xr initgroups 3 ,
+.Xr login_cap 3 ,
+.Xr mac_set_proc 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_class.c b/lib/libutil/login_class.c
new file mode 100644
index 0000000..9ffca8e
--- /dev/null
+++ b/lib/libutil/login_class.c
@@ -0,0 +1,565 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * High-level routines relating to use of the user capabilities database
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/mac.h>
+#include <sys/resource.h>
+#include <sys/rtprio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <login_cap.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+
+static struct login_res {
+ const char *what;
+ rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
+ int why;
+} resources[] = {
+ { "cputime", login_getcaptime, RLIMIT_CPU },
+ { "filesize", login_getcapsize, RLIMIT_FSIZE },
+ { "datasize", login_getcapsize, RLIMIT_DATA },
+ { "stacksize", login_getcapsize, RLIMIT_STACK },
+ { "memoryuse", login_getcapsize, RLIMIT_RSS },
+ { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK },
+ { "maxproc", login_getcapnum, RLIMIT_NPROC },
+ { "openfiles", login_getcapnum, RLIMIT_NOFILE },
+ { "coredumpsize", login_getcapsize, RLIMIT_CORE },
+ { "sbsize", login_getcapsize, RLIMIT_SBSIZE },
+ { "vmemoryuse", login_getcapsize, RLIMIT_VMEM },
+ { "pseudoterminals", login_getcapnum, RLIMIT_NPTS },
+ { "swapuse", login_getcapsize, RLIMIT_SWAP },
+ { "kqueues", login_getcapsize, RLIMIT_KQUEUES },
+ { NULL, 0, 0 }
+};
+
+
+void
+setclassresources(login_cap_t *lc)
+{
+ struct login_res *lr;
+
+ if (lc == NULL)
+ return;
+
+ for (lr = resources; lr->what != NULL; ++lr) {
+ struct rlimit rlim;
+
+ /*
+ * The login.conf file can have <limit>, <limit>-max, and
+ * <limit>-cur entries.
+ * What we do is get the current current- and maximum- limits.
+ * Then, we try to get an entry for <limit> from the capability,
+ * using the current and max limits we just got as the
+ * default/error values.
+ * *Then*, we try looking for <limit>-cur and <limit>-max,
+ * again using the appropriate values as the default/error
+ * conditions.
+ */
+
+ if (getrlimit(lr->why, &rlim) != 0)
+ syslog(LOG_ERR, "getting %s resource limit: %m", lr->what);
+ else {
+ char name_cur[40];
+ char name_max[40];
+ rlim_t rcur = rlim.rlim_cur;
+ rlim_t rmax = rlim.rlim_max;
+
+ sprintf(name_cur, "%s-cur", lr->what);
+ sprintf(name_max, "%s-max", lr->what);
+
+ rcur = (*lr->who)(lc, lr->what, rcur, rcur);
+ rmax = (*lr->who)(lc, lr->what, rmax, rmax);
+ rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur);
+ rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax);
+
+ if (setrlimit(lr->why, &rlim) == -1)
+ syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
+ }
+ }
+}
+
+
+
+static struct login_vars {
+ const char *tag;
+ const char *var;
+ const char *def;
+ int overwrite;
+} pathvars[] = {
+ { "path", "PATH", NULL, 1},
+ { "cdpath", "CDPATH", NULL, 1},
+ { "manpath", "MANPATH", NULL, 1},
+ { NULL, NULL, NULL, 0}
+}, envars[] = {
+ { "lang", "LANG", NULL, 1},
+ { "charset", "MM_CHARSET", NULL, 1},
+ { "timezone", "TZ", NULL, 1},
+ { "term", "TERM", NULL, 0},
+ { NULL, NULL, NULL, 0}
+};
+
+static char *
+substvar(const char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
+{
+ char *np = NULL;
+
+ if (var != NULL) {
+ int tildes = 0;
+ int dollas = 0;
+ char *p;
+ const char *q;
+
+ if (pwd != NULL) {
+ for (q = var; *q != '\0'; ++q) {
+ tildes += (*q == '~');
+ dollas += (*q == '$');
+ }
+ }
+
+ np = malloc(strlen(var) + (dollas * nlen)
+ - dollas + (tildes * (pch+hlen))
+ - tildes + 1);
+
+ if (np != NULL) {
+ p = strcpy(np, var);
+
+ if (pwd != NULL) {
+ /*
+ * This loop does user username and homedir substitutions
+ * for unescaped $ (username) and ~ (homedir)
+ */
+ while (*(p += strcspn(p, "~$")) != '\0') {
+ int l = strlen(p);
+
+ if (p > np && *(p-1) == '\\') /* Escaped: */
+ memmove(p - 1, p, l + 1); /* Slide-out the backslash */
+ else if (*p == '~') {
+ int v = pch && *(p+1) != '/'; /* Avoid double // */
+ memmove(p + hlen + v, p + 1, l); /* Subst homedir */
+ memmove(p, pwd->pw_dir, hlen);
+ if (v)
+ p[hlen] = '/';
+ p += hlen + v;
+ }
+ else /* if (*p == '$') */ {
+ memmove(p + nlen, p + 1, l); /* Subst username */
+ memmove(p, pwd->pw_name, nlen);
+ p += nlen;
+ }
+ }
+ }
+ }
+ }
+
+ return (np);
+}
+
+
+void
+setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
+{
+ struct login_vars *vars = paths ? pathvars : envars;
+ int hlen = pwd ? strlen(pwd->pw_dir) : 0;
+ int nlen = pwd ? strlen(pwd->pw_name) : 0;
+ char pch = 0;
+
+ if (hlen && pwd->pw_dir[hlen-1] != '/')
+ ++pch;
+
+ while (vars->tag != NULL) {
+ const char * var = paths ? login_getpath(lc, vars->tag, NULL)
+ : login_getcapstr(lc, vars->tag, NULL, NULL);
+
+ char * np = substvar(var, pwd, hlen, pch, nlen);
+
+ if (np != NULL) {
+ setenv(vars->var, np, vars->overwrite);
+ free(np);
+ } else if (vars->def != NULL) {
+ setenv(vars->var, vars->def, 0);
+ }
+ ++vars;
+ }
+
+ /*
+ * If we're not processing paths, then see if there is a setenv list by
+ * which the admin and/or user may set an arbitrary set of env vars.
+ */
+ if (!paths) {
+ const char **set_env = login_getcaplist(lc, "setenv", ",");
+
+ if (set_env != NULL) {
+ while (*set_env != NULL) {
+ char *p = strchr(*set_env, '=');
+
+ if (p != NULL) { /* Discard invalid entries */
+ char *np;
+
+ *p++ = '\0';
+ if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
+ setenv(*set_env, np, 1);
+ free(np);
+ }
+ }
+ ++set_env;
+ }
+ }
+ }
+}
+
+
+static int
+list2cpuset(const char *list, cpuset_t *mask)
+{
+ enum { NONE, NUM, DASH } state;
+ int lastnum;
+ int curnum;
+ const char *l;
+
+ state = NONE;
+ curnum = lastnum = 0;
+ for (l = list; *l != '\0';) {
+ if (isdigit(*l)) {
+ curnum = atoi(l);
+ if (curnum > CPU_SETSIZE)
+ errx(EXIT_FAILURE,
+ "Only %d cpus supported", CPU_SETSIZE);
+ while (isdigit(*l))
+ l++;
+ switch (state) {
+ case NONE:
+ lastnum = curnum;
+ state = NUM;
+ break;
+ case DASH:
+ for (; lastnum <= curnum; lastnum++)
+ CPU_SET(lastnum, mask);
+ state = NONE;
+ break;
+ case NUM:
+ default:
+ return (0);
+ }
+ continue;
+ }
+ switch (*l) {
+ case ',':
+ switch (state) {
+ case NONE:
+ break;
+ case NUM:
+ CPU_SET(curnum, mask);
+ state = NONE;
+ break;
+ case DASH:
+ return (0);
+ break;
+ }
+ break;
+ case '-':
+ if (state != NUM)
+ return (0);
+ state = DASH;
+ break;
+ default:
+ return (0);
+ }
+ l++;
+ }
+ switch (state) {
+ case NONE:
+ break;
+ case NUM:
+ CPU_SET(curnum, mask);
+ break;
+ case DASH:
+ return (0);
+ }
+ return (1);
+}
+
+
+void
+setclasscpumask(login_cap_t *lc)
+{
+ const char *maskstr;
+ cpuset_t maskset;
+ cpusetid_t setid;
+
+ maskstr = login_getcapstr(lc, "cpumask", NULL, NULL);
+ CPU_ZERO(&maskset);
+ if (maskstr == NULL)
+ return;
+ if (strcasecmp("default", maskstr) == 0)
+ return;
+ if (!list2cpuset(maskstr, &maskset)) {
+ syslog(LOG_WARNING,
+ "list2cpuset(%s) invalid mask specification", maskstr);
+ return;
+ }
+
+ if (cpuset(&setid) != 0) {
+ syslog(LOG_ERR, "cpuset(): %s", strerror(errno));
+ return;
+ }
+
+ if (cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
+ sizeof(maskset), &maskset) != 0)
+ syslog(LOG_ERR, "cpuset_setaffinity(%s): %s", maskstr,
+ strerror(errno));
+}
+
+
+/*
+ * setclasscontext()
+ *
+ * For the login class <class>, set various class context values
+ * (limits, mainly) to the values for that class. Which values are
+ * set are controlled by <flags> -- see <login_class.h> for the
+ * possible values.
+ *
+ * setclasscontext() can only set resources, priority, and umask.
+ */
+
+int
+setclasscontext(const char *classname, unsigned int flags)
+{
+ int rc;
+ login_cap_t *lc;
+
+ lc = login_getclassbyname(classname, NULL);
+
+ flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
+ LOGIN_SETUMASK | LOGIN_SETPATH;
+
+ rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
+ login_close(lc);
+ return (rc);
+}
+
+
+
+/*
+ * Private function which takes care of processing
+ */
+
+static mode_t
+setlogincontext(login_cap_t *lc, const struct passwd *pwd,
+ mode_t mymask, unsigned long flags)
+{
+ if (lc) {
+ /* Set resources */
+ if (flags & LOGIN_SETRESOURCES)
+ setclassresources(lc);
+ /* See if there's a umask override */
+ if (flags & LOGIN_SETUMASK)
+ mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
+ /* Set paths */
+ if (flags & LOGIN_SETPATH)
+ setclassenvironment(lc, pwd, 1);
+ /* Set environment */
+ if (flags & LOGIN_SETENV)
+ setclassenvironment(lc, pwd, 0);
+ /* Set cpu affinity */
+ if (flags & LOGIN_SETCPUMASK)
+ setclasscpumask(lc);
+ }
+ return (mymask);
+}
+
+
+
+/*
+ * setusercontext()
+ *
+ * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
+ * set the context as in setclasscontext(). <flags> controls which
+ * values are set.
+ *
+ * The difference between setclasscontext() and setusercontext() is
+ * that the former sets things up for an already-existing process,
+ * while the latter sets things up from a root context. Such as might
+ * be called from login(1).
+ *
+ */
+
+int
+setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
+{
+ rlim_t p;
+ mode_t mymask;
+ login_cap_t *llc = NULL;
+ struct sigaction sa, prevsa;
+ struct rtprio rtp;
+ int error;
+
+ if (lc == NULL) {
+ if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
+ llc = lc; /* free this when we're done */
+ }
+
+ if (flags & LOGIN_SETPATH)
+ pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
+
+ /* we need a passwd entry to set these */
+ if (pwd == NULL)
+ flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC);
+
+ /* Set the process priority */
+ if (flags & LOGIN_SETPRIORITY) {
+ p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
+
+ if (p > PRIO_MAX) {
+ rtp.type = RTP_PRIO_IDLE;
+ p -= PRIO_MAX + 1;
+ rtp.prio = p > RTP_PRIO_MAX ? RTP_PRIO_MAX : p;
+ if (rtprio(RTP_SET, 0, &rtp))
+ syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
+ pwd ? pwd->pw_name : "-",
+ lc ? lc->lc_class : LOGIN_DEFCLASS);
+ } else if (p < PRIO_MIN) {
+ rtp.type = RTP_PRIO_REALTIME;
+ p -= PRIO_MIN - RTP_PRIO_MAX;
+ rtp.prio = p < RTP_PRIO_MIN ? RTP_PRIO_MIN : p;
+ if (rtprio(RTP_SET, 0, &rtp))
+ syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
+ pwd ? pwd->pw_name : "-",
+ lc ? lc->lc_class : LOGIN_DEFCLASS);
+ } else {
+ if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
+ syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
+ pwd ? pwd->pw_name : "-",
+ lc ? lc->lc_class : LOGIN_DEFCLASS);
+ }
+ }
+
+ /* Setup the user's group permissions */
+ if (flags & LOGIN_SETGROUP) {
+ if (setgid(pwd->pw_gid) != 0) {
+ syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid);
+ login_close(llc);
+ return (-1);
+ }
+ if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
+ syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name,
+ (u_long)pwd->pw_gid);
+ login_close(llc);
+ return (-1);
+ }
+ }
+
+ /* Set up the user's MAC label. */
+ if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) {
+ const char *label_string;
+ mac_t label;
+
+ label_string = login_getcapstr(lc, "label", NULL, NULL);
+ if (label_string != NULL) {
+ if (mac_from_text(&label, label_string) == -1) {
+ syslog(LOG_ERR, "mac_from_text('%s') for %s: %m",
+ pwd->pw_name, label_string);
+ return (-1);
+ }
+ if (mac_set_proc(label) == -1)
+ error = errno;
+ else
+ error = 0;
+ mac_free(label);
+ if (error != 0) {
+ syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s",
+ label_string, pwd->pw_name, strerror(error));
+ return (-1);
+ }
+ }
+ }
+
+ /* Set the sessions login */
+ if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
+ syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
+ login_close(llc);
+ return (-1);
+ }
+
+ /* Inform the kernel about current login class */
+ if (lc != NULL && lc->lc_class != NULL && (flags & LOGIN_SETLOGINCLASS)) {
+ /*
+ * XXX: This is a workaround to fail gracefully in case the kernel
+ * does not support setloginclass(2).
+ */
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sigfillset(&sa.sa_mask);
+ sigaction(SIGSYS, &sa, &prevsa);
+ error = setloginclass(lc->lc_class);
+ sigaction(SIGSYS, &prevsa, NULL);
+ if (error != 0) {
+ syslog(LOG_ERR, "setloginclass(%s): %m", lc->lc_class);
+#ifdef notyet
+ login_close(llc);
+ return (-1);
+#endif
+ }
+ }
+
+ mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
+ mymask = setlogincontext(lc, pwd, mymask, flags);
+ login_close(llc);
+
+ /* This needs to be done after anything that needs root privs */
+ if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
+ syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid);
+ return (-1); /* Paranoia again */
+ }
+
+ /*
+ * Now, we repeat some of the above for the user's private entries
+ */
+ if (getuid() == uid && (lc = login_getuserclass(pwd)) != NULL) {
+ mymask = setlogincontext(lc, pwd, mymask, flags);
+ login_close(lc);
+ }
+
+ /* Finally, set any umask we've found */
+ if (flags & LOGIN_SETUMASK)
+ umask(mymask);
+
+ return (0);
+}
diff --git a/lib/libutil/login_crypt.c b/lib/libutil/login_crypt.c
new file mode 100644
index 0000000..c65fc9b
--- /dev/null
+++ b/lib/libutil/login_crypt.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2000 Brian Fundakowski Feldman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <login_cap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+const char *
+login_setcryptfmt(login_cap_t *lc, const char *def, const char *error) {
+ const char *cipher;
+
+ cipher = login_getcapstr(lc, "passwd_format", def, NULL);
+ if (getenv("CRYPT_DEBUG") != NULL)
+ fprintf(stderr, "login_setcryptfmt: "
+ "passwd_format = %s\n", cipher);
+ if (cipher == NULL)
+ return (error);
+ if (!crypt_set_format(cipher))
+ return (error);
+ return (cipher);
+}
diff --git a/lib/libutil/login_ok.3 b/lib/libutil/login_ok.3
new file mode 100644
index 0000000..9022ff5
--- /dev/null
+++ b/lib/libutil/login_ok.3
@@ -0,0 +1,142 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" this list of conditions, and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 2, 1997
+.Dt LOGIN_OK 3
+.Os
+.Sh NAME
+.Nm auth_ttyok ,
+.Nm auth_hostok ,
+.Nm auth_timeok
+.Nd functions for checking login class based login restrictions
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In time.h
+.In login_cap.h
+.Ft int
+.Fn auth_ttyok "login_cap_t *lc" "const char *tty"
+.Ft int
+.Fn auth_hostok "login_cap_t *lc" "const char *host" "char const *ip"
+.Ft int
+.Fn auth_timeok "login_cap_t *lc" "time_t t"
+.Sh DESCRIPTION
+This set of functions checks to see if login is allowed based on login
+class capability entries in the login database,
+.Xr login.conf 5 .
+.Pp
+The
+.Fn auth_ttyok
+function checks to see if the named tty is available to users of a specific
+class, and is either in the
+.Em ttys.allow
+access list, and not in
+the
+.Em ttys.deny
+access list.
+An empty
+.Em ttys.allow
+list (or if no such capability exists for
+the given login class) logins via any tty device are allowed unless
+the
+.Em ttys.deny
+list exists and is non-empty, and the device or its
+tty group (see
+.Xr ttys 5 )
+is not in the list.
+Access to ttys may be allowed or restricted specifically by tty device
+name, a device name which includes a wildcard (e.g.\& ttyD* or cuaD*),
+or may name a ttygroup, when group=<name> tags have been assigned in
+.Pa /etc/ttys .
+Matching of ttys and ttygroups is case sensitive.
+Passing a
+.Dv NULL
+or empty string as the
+.Ar tty
+parameter causes the function to return a non-zero value.
+.Pp
+The
+.Fn auth_hostok
+function checks for any host restrictions for remote logins.
+The function checks on both a host name and IP address (given in its
+text form, typically n.n.n.n) against the
+.Em host.allow
+and
+.Em host.deny
+login class capabilities.
+As with ttys and their groups, wildcards and character classes may be
+used in the host allow and deny capability records.
+The
+.Xr fnmatch 3
+function is used for matching, and the matching on hostnames is case
+insensitive.
+Note that this function expects that the hostname is fully expanded
+(i.e., the local domain name added if necessary) and the IP address
+is in its canonical form.
+No hostname or address lookups are attempted.
+.Pp
+It is possible to call this function with either the hostname or
+the IP address missing (i.e.\&
+.Dv NULL )
+and matching will be performed
+only on the basis of the parameter given.
+Passing
+.Dv NULL
+or empty strings in both parameters will result in
+a non-zero return value.
+.Pp
+The
+.Fn auth_timeok
+function checks to see that a given time value is within the
+.Em times.allow
+login class capability and not within the
+.Em times.deny
+access lists.
+An empty or non-existent
+.Em times.allow
+list allows access at any
+time, except if a given time is falls within a period in the
+.Em times.deny
+list.
+The format of time period records contained in both
+.Em times.allow
+and
+.Em times.deny
+capability fields is explained in detail in the
+.Xr login_times 3
+manual page.
+.Sh RETURN VALUES
+A non-zero return value from any of these functions indicates that
+login access is granted.
+A zero return value means either that the item being tested is not
+in the
+.Em allow
+access list, or is within the
+.Em deny
+access list.
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr login_times 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_ok.c b/lib/libutil/login_ok.c
new file mode 100644
index 0000000..26971a9
--- /dev/null
+++ b/lib/libutil/login_ok.c
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 1996 by
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Support allow/deny lists in login class capabilities
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <login_cap.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ttyent.h>
+#include <unistd.h>
+
+
+/* -- support functions -- */
+
+/*
+ * login_strinlist()
+ * This function is intentionally public - reused by TAS.
+ * Returns TRUE (non-zero) if a string matches a pattern
+ * in a given array of patterns. 'flags' is passed directly
+ * to fnmatch(3).
+ */
+
+int
+login_strinlist(const char **list, char const *str, int flags)
+{
+ int rc = 0;
+
+ if (str != NULL && *str != '\0') {
+ int i = 0;
+
+ while (rc == 0 && list[i] != NULL)
+ rc = fnmatch(list[i++], str, flags) == 0;
+ }
+ return rc;
+}
+
+
+/*
+ * login_str2inlist()
+ * Locate either or two strings in a given list
+ */
+
+int
+login_str2inlist(const char **ttlst, const char *str1, const char *str2, int flags)
+{
+ int rc = 0;
+
+ if (login_strinlist(ttlst, str1, flags))
+ rc = 1;
+ else if (login_strinlist(ttlst, str2, flags))
+ rc = 1;
+ return rc;
+}
+
+
+/*
+ * login_timelist()
+ * This function is intentionally public - reused by TAS.
+ * Returns an allocated list of time periods given an array
+ * of time periods in ascii form.
+ */
+
+login_time_t *
+login_timelist(login_cap_t *lc, char const *cap, int *ltno,
+ login_time_t **ltptr)
+{
+ int j = 0;
+ struct login_time *lt = NULL;
+ const char **tl;
+
+ if ((tl = login_getcaplist(lc, cap, NULL)) != NULL) {
+
+ while (tl[j++] != NULL)
+ ;
+ if (*ltno >= j)
+ lt = *ltptr;
+ else if ((lt = realloc(*ltptr, j * sizeof(struct login_time))) != NULL) {
+ *ltno = j;
+ *ltptr = lt;
+ }
+ if (lt != NULL) {
+ int i = 0;
+
+ for (--j; i < j; i++)
+ lt[i] = parse_lt(tl[i]);
+ lt[i].lt_dow = LTM_NONE;
+ }
+ }
+ return lt;
+}
+
+
+/*
+ * login_ttyok()
+ * This function is a variation of auth_ttyok(), but it checks two
+ * arbitrary capability lists not necessarily related to access.
+ * This hook is provided for the accounted/exclude accounting lists.
+ */
+
+int
+login_ttyok(login_cap_t *lc, const char *tty, const char *allowcap,
+ const char *denycap)
+{
+ int rc = 1;
+
+ if (lc != NULL && tty != NULL && *tty != '\0') {
+ struct ttyent *te;
+ char *grp;
+ const char **ttl;
+
+ te = getttynam(tty); /* Need group name */
+ grp = te ? te->ty_group : NULL;
+ ttl = login_getcaplist(lc, allowcap, NULL);
+
+ if (ttl != NULL && !login_str2inlist(ttl, tty, grp, 0))
+ rc = 0; /* tty or ttygroup not in allow list */
+ else {
+
+ ttl = login_getcaplist(lc, denycap, NULL);
+ if (ttl != NULL && login_str2inlist(ttl, tty, grp, 0))
+ rc = 0; /* tty or ttygroup in deny list */
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * auth_ttyok()
+ * Determine whether or not login on a tty is accessible for
+ * a login class
+ */
+
+int
+auth_ttyok(login_cap_t *lc, const char * tty)
+{
+ return login_ttyok(lc, tty, "ttys.allow", "ttys.deny");
+}
+
+
+/*
+ * login_hostok()
+ * This function is a variation of auth_hostok(), but it checks two
+ * arbitrary capability lists not necessarily related to access.
+ * This hook is provided for the accounted/exclude accounting lists.
+ */
+
+int
+login_hostok(login_cap_t *lc, const char *host, const char *ip,
+ const char *allowcap, const char *denycap)
+{
+ int rc = 1; /* Default is ok */
+
+ if (lc != NULL &&
+ ((host != NULL && *host != '\0') || (ip != NULL && *ip != '\0'))) {
+ const char **hl;
+
+ hl = login_getcaplist(lc, allowcap, NULL);
+ if (hl != NULL && !login_str2inlist(hl, host, ip, FNM_CASEFOLD))
+ rc = 0; /* host or IP not in allow list */
+ else {
+
+ hl = login_getcaplist(lc, denycap, NULL);
+ if (hl != NULL && login_str2inlist(hl, host, ip, FNM_CASEFOLD))
+ rc = 0; /* host or IP in deny list */
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * auth_hostok()
+ * Determine whether or not login from a host is ok
+ */
+
+int
+auth_hostok(login_cap_t *lc, const char *host, const char *ip)
+{
+ return login_hostok(lc, host, ip, "host.allow", "host.deny");
+}
+
+
+/*
+ * auth_timeok()
+ * Determine whether or not login is ok at a given time
+ */
+
+int
+auth_timeok(login_cap_t *lc, time_t t)
+{
+ int rc = 1; /* Default is ok */
+
+ if (lc != NULL && t != (time_t)0 && t != (time_t)-1) {
+ struct tm *tptr;
+
+ static int ltimesno = 0;
+ static struct login_time *ltimes = NULL;
+
+ if ((tptr = localtime(&t)) != NULL) {
+ struct login_time *lt;
+
+ lt = login_timelist(lc, "times.allow", &ltimesno, &ltimes);
+ if (lt != NULL && in_ltms(lt, tptr, NULL) == -1)
+ rc = 0; /* not in allowed times list */
+ else {
+
+ lt = login_timelist(lc, "times.deny", &ltimesno, &ltimes);
+ if (lt != NULL && in_ltms(lt, tptr, NULL) != -1)
+ rc = 0; /* in deny times list */
+ }
+ if (ltimes) {
+ free(ltimes);
+ ltimes = NULL;
+ ltimesno = 0;
+ }
+ }
+ }
+
+ return rc;
+}
diff --git a/lib/libutil/login_times.3 b/lib/libutil/login_times.3
new file mode 100644
index 0000000..9d1f88d
--- /dev/null
+++ b/lib/libutil/login_times.3
@@ -0,0 +1,179 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" this list of conditions, and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 20, 2008
+.Dt LOGIN_TIMES 3
+.Os
+.Sh NAME
+.Nm parse_lt ,
+.Nm in_lt ,
+.Nm in_ltm ,
+.Nm in_ltms ,
+.Nm in_lts
+.Nd functions for parsing and checking login time periods
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In time.h
+.In login_cap.h
+.Ft login_time_t
+.Fn parse_lt "const char *str"
+.Ft int
+.Fn in_lt "const login_time_t *lt" "time_t *ends"
+.Ft int
+.Fn in_ltm "const login_time_t *lt" "struct tm *t" "time_t *ends"
+.Ft int
+.Fn in_ltms "const login_time_t *lt" "struct tm *t" "time_t *ends"
+.Ft int
+.Fn in_lts "const login_time_t *lt" "time_t *ends"
+.Sh DESCRIPTION
+This set of functions may be used for parsing and checking login and
+session times against a predefined list of allowed login times as
+used in
+.Xr login.conf 5 .
+.Pp
+The format of allowed and disallowed session times specified in the
+.Ar times.allow
+and
+.Ar times.deny
+capability fields in a login class are comprised of a prefix which
+specifies one or more 2- or 3-character day codes, followed by
+a start and end time in 24 hour format separated by a hyphen.
+Day codes may be concatenated together to select specific days, or
+the special mnemonics "Any" and "All" (for any/all days of the week),
+"Wk" for any day of the week (excluding Saturdays and Sundays) and
+"Wd" for any weekend day may be used.
+.Pp
+For example, the following time period:
+.Dl MoThFrSa1400-2200
+is interpreted as Monday, Thursday through Saturday between the hours
+of 2pm and 10pm.
+.Dl Wd0600-1800
+means Saturday and Sunday, between the hours of 6am through 6pm, and
+.Dl Any0400-1600
+means any day of the week, between 4am and 4pm.
+.Pp
+Note that all time periods reference system local time.
+.Pp
+The
+.Fn parse_lt
+function converts the ASCII representation of a time period into
+a structure of type
+.Ft login_time_t .
+This is defined as:
+.Bd -literal
+typedef struct login_time
+{
+ u_short lt_start; /* Start time */
+ u_short lt_end; /* End time */
+ u_char lt_dow; /* Days of week */
+} login_time_t;
+.Ed
+.Pp
+The
+.Ar lt_start
+and
+.Ar lt_end
+fields contain the number of minutes past midnight at which the
+described period begins and ends.
+The
+.Ar lt_dow
+field is a bit field, containing one bit for each day of the week
+and one bit unused.
+A series
+.Em LTM_*
+macros may be used for testing bits individually and in combination.
+If no bits are set in this field - i.e., it contains the value
+.Em LTM_NONE
+- then the entire period is assumed invalid.
+This is used as a convention to mark the termination of an array
+of login_time_t values.
+If
+.Fn parse_lt
+returns a
+.Ar login_time_t
+with
+.Ar lt_dow
+equal to
+.Em LTM_NONE
+then a parsing error was encountered.
+.Pp
+The remaining functions provide the ability to test a given time_t or
+struct tm value against a specific time period or array of time
+periods.
+The
+.Fn in_ltm
+function determines whether the given time described by the struct tm
+passed as the second parameter falls within the period described
+by the first parameter.
+A boolean value is returned, indicating whether or not the time
+specified falls within the period.
+If the time does fall within the time period, and the third
+parameter to the function is not NULL, the time at which the
+period ends relative to the time passed is returned.
+.Pp
+The
+.Fn in_ltms
+function is similar to
+.Fn in_ltm
+except that the first parameter must be a pointer to an array
+of login_time_t objects, which is up to LC_MAXTIMES (64)
+elements in length, and terminated by an element with its
+.Ar lt_dow
+field set to
+.Em LTM_NONE .
+.Pp
+The
+.Fn in_lt
+and
+.Fn in_lts
+functions are equivalent to
+.Fn in_ltm
+and
+.Fn in_ltms ,
+respectively, with the second argument set to the current time as
+returned by
+.Xr localtime 3 .
+.Sh RETURN VALUES
+The
+.Fn parse_lt
+function returns a filled in structure of type login_time_t containing the
+parsed time period.
+If a parsing error occurs, the lt_dow field is set to
+.Em LTM_NONE
+(i.e., 0).
+.Pp
+The
+.Fn in_ltm
+function returns non-zero if the given time falls within the period described
+by the login_time_t passed as the first parameter.
+.Pp
+The
+.Fn in_ltms
+function returns the index of the first time period found in which the given
+time falls, or -1 if none of them apply.
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_times.c b/lib/libutil/login_times.c
new file mode 100644
index 0000000..d13ed99
--- /dev/null
+++ b/lib/libutil/login_times.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1996 by
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Login period parsing and comparison functions.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <login_cap.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+static struct
+{
+ const char *dw;
+ u_char cn;
+ u_char fl;
+} dws[] =
+{
+ { "su", 2, LTM_SUN }, { "mo", 2, LTM_MON }, { "tu", 2, LTM_TUE },
+ { "we", 2, LTM_WED }, { "th", 2, LTM_THU }, { "fr", 2, LTM_FRI },
+ { "sa", 2, LTM_SAT }, { "any",3, LTM_ANY }, { "all",3, LTM_ANY },
+ { "wk", 2, LTM_WK }, { "wd", 2, LTM_WD }, { NULL, 0, 0 }
+};
+
+static char *
+parse_time(char * ptr, u_short * t)
+{
+ u_short val;
+
+ for (val = 0; *ptr && isdigit(*ptr); ptr++)
+ val = (u_short)(val * 10 + (*ptr - '0'));
+
+ *t = (u_short)((val / 100) * 60 + (val % 100));
+
+ return (ptr);
+}
+
+
+login_time_t
+parse_lt(const char *str)
+{
+ login_time_t t;
+
+ memset(&t, 0, sizeof t);
+ t.lt_dow = LTM_NONE;
+ if (str && *str && strcmp(str, "Never") != 0 && strcmp(str, "None") != 0) {
+ int i;
+ login_time_t m = t;
+ char *p;
+ char buf[64];
+
+ /* Make local copy and force lowercase to simplify parsing */
+ strlcpy(buf, str, sizeof buf);
+ for (i = 0; buf[i]; i++)
+ buf[i] = (char)tolower(buf[i]);
+ p = buf;
+
+ while (isalpha(*p)) {
+
+ i = 0;
+ while (dws[i].dw && strncmp(p, dws[i].dw, dws[i].cn) != 0)
+ i++;
+ if (dws[i].dw == NULL)
+ break;
+ m.lt_dow |= dws[i].fl;
+ p += dws[i].cn;
+ }
+
+ if (m.lt_dow == LTM_NONE) /* No (valid) prefix, assume any */
+ m.lt_dow |= LTM_ANY;
+
+ if (isdigit(*p))
+ p = parse_time(p, &m.lt_start);
+ else
+ m.lt_start = 0;
+ if (*p == '-')
+ p = parse_time(p + 1, &m.lt_end);
+ else
+ m.lt_end = 1440;
+
+ t = m;
+ }
+ return (t);
+}
+
+
+int
+in_ltm(const login_time_t *ltm, struct tm *tt, time_t *ends)
+{
+ int rc = 0;
+
+ if (tt != NULL) {
+ /* First, examine the day of the week */
+ if ((u_char)(0x01 << tt->tm_wday) & ltm->lt_dow) {
+ /* Convert `current' time to minute of the day */
+ u_short now = (u_short)((tt->tm_hour * 60) + tt->tm_min);
+
+ if (tt->tm_sec > 30)
+ ++now;
+ if (now >= ltm->lt_start && now < ltm->lt_end) {
+ rc = 2;
+ if (ends != NULL) {
+ /* If requested, return ending time for this period */
+ tt->tm_hour = (int)(ltm->lt_end / 60);
+ tt->tm_min = (int)(ltm->lt_end % 60);
+ *ends = mktime(tt);
+ }
+ }
+ }
+ }
+ return (rc);
+}
+
+
+int
+in_lt(const login_time_t *ltm, time_t *t)
+{
+
+ return (in_ltm(ltm, localtime(t), t));
+}
+
+int
+in_ltms(const login_time_t *ltm, struct tm *tm, time_t *t)
+{
+ int i = 0;
+
+ while (i < LC_MAXTIMES && ltm[i].lt_dow != LTM_NONE) {
+ if (in_ltm(ltm + i, tm, t))
+ return (i);
+ i++;
+ }
+ return (-1);
+}
+
+int
+in_lts(const login_time_t *ltm, time_t *t)
+{
+
+ return (in_ltms(ltm, localtime(t), t));
+}
diff --git a/lib/libutil/login_tty.3 b/lib/libutil/login_tty.3
new file mode 100644
index 0000000..907b97c
--- /dev/null
+++ b/lib/libutil/login_tty.3
@@ -0,0 +1,65 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd December 29, 1996
+.Dt LOGIN_TTY 3
+.Os
+.Sh NAME
+.Nm login_tty
+.Nd prepare a tty for a new login session
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fn login_tty "int fd"
+.Sh DESCRIPTION
+The function
+.Fn login_tty
+prepares a terminal for a new login session.
+The file descriptor
+.Ar fd
+passed to
+.Fn login_tty
+must be opened for reading and writing on a terminal device.
+It will be
+made the controlling terminal for the calling process, after allocating
+a new session with
+.Xr setsid 2 .
+This terminal device will also be made the standard input, standard output,
+and standard error output of the calling process.
+.Sh RETURN VALUES
+The
+.Fn login_tty
+function returns -1 if it could not make the device referenced by
+.Ar fd
+the controlling terminal of the calling process, and 0 otherwise.
+.Sh SEE ALSO
+.Xr dup2 2 ,
+.Xr ioctl 2 ,
+.Xr setsid 2 ,
+.Xr tty 4
diff --git a/lib/libutil/login_tty.c b/lib/libutil/login_tty.c
new file mode 100644
index 0000000..92dc87f
--- /dev/null
+++ b/lib/libutil/login_tty.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)login_tty.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <libutil.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+
+int
+login_tty(int fd)
+{
+ pid_t s;
+
+ s = setsid();
+ if (s == -1)
+ s = getsid(0);
+ if (tcsetsid(fd, s) == -1)
+ return (-1);
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ if (fd > 2)
+ (void) close(fd);
+ return (0);
+}
diff --git a/lib/libutil/pidfile.3 b/lib/libutil/pidfile.3
new file mode 100644
index 0000000..d5e2470
--- /dev/null
+++ b/lib/libutil/pidfile.3
@@ -0,0 +1,297 @@
+.\" Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 8, 2012
+.Dt PIDFILE 3
+.Os
+.Sh NAME
+.Nm pidfile_open ,
+.Nm pidfile_write ,
+.Nm pidfile_close ,
+.Nm pidfile_remove
+.Nd "library for PID files handling"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft "struct pidfh *"
+.Fn pidfile_open "const char *path" "mode_t mode" "pid_t *pidptr"
+.Ft int
+.Fn pidfile_write "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_close "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_remove "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_fileno "struct pidfh *pfh"
+.Sh DESCRIPTION
+The
+.Nm pidfile
+family of functions allows daemons to handle PID files.
+It uses
+.Xr flopen 3
+to lock a pidfile and detect already running daemons.
+.Pp
+The
+.Fn pidfile_open
+function opens (or creates) a file specified by the
+.Fa path
+argument and locks it.
+If
+.Fa pidptr
+argument is not
+.Dv NULL
+and file can not be locked, the function will use it to store a PID of an
+already running daemon or
+.Li -1
+in case daemon did not write its PID yet.
+The function does not write process' PID into the file here, so it can be
+used before
+.Fn fork Ns ing
+and exit with a proper error message when needed.
+If the
+.Fa path
+argument is
+.Dv NULL ,
+.Pa /var/run/ Ns Ao Va progname Ac Ns Pa .pid
+file will be used.
+The
+.Fn pidfile_open
+function sets the O_CLOEXEC close-on-exec flag when opening the pidfile.
+.Pp
+The
+.Fn pidfile_write
+function writes process' PID into a previously opened file.
+The file is truncated before write, so calling the
+.Fn pidfile_write
+function multiple times is supported.
+.Pp
+The
+.Fn pidfile_close
+function closes a pidfile.
+It should be used after daemon
+.Fn fork Ns s
+to start a child process.
+.Pp
+The
+.Fn pidfile_remove
+function closes and removes a pidfile.
+.Pp
+The
+.Fn pidfile_fileno
+function returns the file descriptor for the open pidfile.
+.Sh RETURN VALUES
+The
+.Fn pidfile_open
+function returns a valid pointer to a
+.Vt pidfh
+structure on success, or
+.Dv NULL
+if an error occurs.
+If an error occurs,
+.Va errno
+will be set.
+.Pp
+.Rv -std pidfile_write pidfile_close pidfile_remove
+.Pp
+The
+.Fn pidfile_fileno
+function returns the low-level file descriptor.
+It returns
+.Li -1
+and sets
+.Va errno
+if a NULL
+.Vt pidfh
+is specified, or if the pidfile is no longer open.
+.Sh EXAMPLES
+The following example shows in which order these functions should be used.
+Note that it is safe to pass
+.Dv NULL
+to
+.Fn pidfile_write ,
+.Fn pidfile_remove ,
+.Fn pidfile_close
+and
+.Fn pidfile_fileno
+functions.
+.Bd -literal
+struct pidfh *pfh;
+pid_t otherpid, childpid;
+
+pfh = pidfile_open("/var/run/daemon.pid", 0600, &otherpid);
+if (pfh == NULL) {
+ if (errno == EEXIST) {
+ errx(EXIT_FAILURE, "Daemon already running, pid: %jd.",
+ (intmax_t)otherpid);
+ }
+ /* If we cannot create pidfile from other reasons, only warn. */
+ warn("Cannot open or create pidfile");
+ /*
+ * Eventhough pfh is NULL we can continue, as the other pidfile_*
+ * function can handle such situation by doing nothing except setting
+ * errno to EDOOFUS.
+ */
+}
+
+if (daemon(0, 0) == -1) {
+ warn("Cannot daemonize");
+ pidfile_remove(pfh);
+ exit(EXIT_FAILURE);
+}
+
+pidfile_write(pfh);
+
+for (;;) {
+ /* Do work. */
+ childpid = fork();
+ switch (childpid) {
+ case -1:
+ syslog(LOG_ERR, "Cannot fork(): %s.", strerror(errno));
+ break;
+ case 0:
+ pidfile_close(pfh);
+ /* Do child work. */
+ break;
+ default:
+ syslog(LOG_INFO, "Child %jd started.", (intmax_t)childpid);
+ break;
+ }
+}
+
+pidfile_remove(pfh);
+exit(EXIT_SUCCESS);
+.Ed
+.Sh ERRORS
+The
+.Fn pidfile_open
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EEXIST
+Some process already holds the lock on the given pidfile, meaning that a
+daemon is already running.
+If
+.Fa pidptr
+argument is not
+.Dv NULL
+the function will use it to store a PID of an already running daemon or
+.Li -1
+in case daemon did not write its PID yet.
+.It Bq Er ENAMETOOLONG
+Specified pidfile's name is too long.
+.It Bq Er EINVAL
+Some process already holds the lock on the given pidfile, but PID read
+from there is invalid.
+.El
+.Pp
+The
+.Fn pidfile_open
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr fstat 2 ,
+.Xr open 2 ,
+and
+.Xr read 2
+calls.
+.Pp
+The
+.Fn pidfile_write
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EDOOFUS
+Improper function use.
+Probably called before
+.Fn pidfile_open .
+.El
+.Pp
+The
+.Fn pidfile_write
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr fstat 2 ,
+.Xr ftruncate 2 ,
+and
+.Xr write 2
+calls.
+.Pp
+The
+.Fn pidfile_close
+function may fail and set
+.Va errno
+for any errors specified for the
+.Xr close 2
+and
+.Xr fstat 2
+calls.
+.Pp
+The
+.Fn pidfile_remove
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EDOOFUS
+Improper function use.
+Probably called not from the process which made
+.Fn pidfile_write .
+.El
+.Pp
+The
+.Fn pidfile_remove
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr close 2 ,
+.Xr fstat 2 ,
+.Xr write 2 ,
+and
+.Xr unlink 2
+system calls and the
+.Xr flopen 3
+library function.
+.Pp
+The
+.Fn pidfile_fileno
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EDOOFUS
+Improper function use.
+Probably called not from the process which used
+.Fn pidfile_open .
+.El
+.Sh SEE ALSO
+.Xr open 2 ,
+.Xr daemon 3 ,
+.Xr flopen 3
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm pidfile
+functionality is based on ideas from
+.An John-Mark Gurney Aq Mt jmg@FreeBSD.org .
+.Pp
+The code and manual page was written by
+.An Pawel Jakub Dawidek Aq Mt pjd@FreeBSD.org .
diff --git a/lib/libutil/pidfile.c b/lib/libutil/pidfile.c
new file mode 100644
index 0000000..3a5e512
--- /dev/null
+++ b/lib/libutil/pidfile.c
@@ -0,0 +1,278 @@
+/*-
+ * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <err.h>
+#include <errno.h>
+#include <libutil.h>
+
+struct pidfh {
+ int pf_fd;
+ char pf_path[MAXPATHLEN + 1];
+ dev_t pf_dev;
+ ino_t pf_ino;
+};
+
+static int _pidfile_remove(struct pidfh *pfh, int freeit);
+
+static int
+pidfile_verify(const struct pidfh *pfh)
+{
+ struct stat sb;
+
+ if (pfh == NULL || pfh->pf_fd == -1)
+ return (EDOOFUS);
+ /*
+ * Check remembered descriptor.
+ */
+ if (fstat(pfh->pf_fd, &sb) == -1)
+ return (errno);
+ if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
+ return (EDOOFUS);
+ return (0);
+}
+
+static int
+pidfile_read(const char *path, pid_t *pidptr)
+{
+ char buf[16], *endptr;
+ int error, fd, i;
+
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd == -1)
+ return (errno);
+
+ i = read(fd, buf, sizeof(buf) - 1);
+ error = errno; /* Remember errno in case close() wants to change it. */
+ close(fd);
+ if (i == -1)
+ return (error);
+ else if (i == 0)
+ return (EAGAIN);
+ buf[i] = '\0';
+
+ *pidptr = strtol(buf, &endptr, 10);
+ if (endptr != &buf[i])
+ return (EINVAL);
+
+ return (0);
+}
+
+struct pidfh *
+pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
+{
+ struct pidfh *pfh;
+ struct stat sb;
+ int error, fd, len, count;
+ struct timespec rqtp;
+
+ pfh = malloc(sizeof(*pfh));
+ if (pfh == NULL)
+ return (NULL);
+
+ if (path == NULL)
+ len = snprintf(pfh->pf_path, sizeof(pfh->pf_path),
+ "/var/run/%s.pid", getprogname());
+ else
+ len = snprintf(pfh->pf_path, sizeof(pfh->pf_path),
+ "%s", path);
+ if (len >= (int)sizeof(pfh->pf_path)) {
+ free(pfh);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+
+ /*
+ * Open the PID file and obtain exclusive lock.
+ * We truncate PID file here only to remove old PID immediatelly,
+ * PID file will be truncated again in pidfile_write(), so
+ * pidfile_write() can be called multiple times.
+ */
+ fd = flopen(pfh->pf_path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NONBLOCK, mode);
+ if (fd == -1) {
+ if (errno == EWOULDBLOCK) {
+ if (pidptr == NULL) {
+ errno = EEXIST;
+ } else {
+ count = 20;
+ rqtp.tv_sec = 0;
+ rqtp.tv_nsec = 5000000;
+ for (;;) {
+ errno = pidfile_read(pfh->pf_path,
+ pidptr);
+ if (errno != EAGAIN || --count == 0)
+ break;
+ nanosleep(&rqtp, 0);
+ }
+ if (errno == EAGAIN)
+ *pidptr = -1;
+ if (errno == 0 || errno == EAGAIN)
+ errno = EEXIST;
+ }
+ }
+ free(pfh);
+ return (NULL);
+ }
+
+ /*
+ * Remember file information, so in pidfile_write() we are sure we write
+ * to the proper descriptor.
+ */
+ if (fstat(fd, &sb) == -1) {
+ error = errno;
+ unlink(pfh->pf_path);
+ close(fd);
+ free(pfh);
+ errno = error;
+ return (NULL);
+ }
+
+ pfh->pf_fd = fd;
+ pfh->pf_dev = sb.st_dev;
+ pfh->pf_ino = sb.st_ino;
+
+ return (pfh);
+}
+
+int
+pidfile_write(struct pidfh *pfh)
+{
+ char pidstr[16];
+ int error, fd;
+
+ /*
+ * Check remembered descriptor, so we don't overwrite some other
+ * file if pidfile was closed and descriptor reused.
+ */
+ errno = pidfile_verify(pfh);
+ if (errno != 0) {
+ /*
+ * Don't close descriptor, because we are not sure if it's ours.
+ */
+ return (-1);
+ }
+ fd = pfh->pf_fd;
+
+ /*
+ * Truncate PID file, so multiple calls of pidfile_write() are allowed.
+ */
+ if (ftruncate(fd, 0) == -1) {
+ error = errno;
+ _pidfile_remove(pfh, 0);
+ errno = error;
+ return (-1);
+ }
+
+ snprintf(pidstr, sizeof(pidstr), "%u", getpid());
+ if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) {
+ error = errno;
+ _pidfile_remove(pfh, 0);
+ errno = error;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+pidfile_close(struct pidfh *pfh)
+{
+ int error;
+
+ error = pidfile_verify(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+
+ if (close(pfh->pf_fd) == -1)
+ error = errno;
+ free(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+_pidfile_remove(struct pidfh *pfh, int freeit)
+{
+ int error;
+
+ error = pidfile_verify(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+
+ if (unlink(pfh->pf_path) == -1)
+ error = errno;
+ if (close(pfh->pf_fd) == -1) {
+ if (error == 0)
+ error = errno;
+ }
+ if (freeit)
+ free(pfh);
+ else
+ pfh->pf_fd = -1;
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ return (0);
+}
+
+int
+pidfile_remove(struct pidfh *pfh)
+{
+
+ return (_pidfile_remove(pfh, 1));
+}
+
+int
+pidfile_fileno(const struct pidfh *pfh)
+{
+
+ if (pfh == NULL || pfh->pf_fd == -1) {
+ errno = EDOOFUS;
+ return (-1);
+ }
+ return (pfh->pf_fd);
+}
diff --git a/lib/libutil/property.3 b/lib/libutil/property.3
new file mode 100644
index 0000000..3d40114
--- /dev/null
+++ b/lib/libutil/property.3
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (c) 1998 Jordan Hubbard
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd October 7, 1998
+.Dt PROPERTIES 3
+.Os
+.Sh NAME
+.Nm properties_read ,
+.Nm property_find ,
+.Nm properties_free
+.Nd "functions to allow creating simple property lists from ASCII file data"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft properties
+.Fn properties_read "int fd"
+.Ft char *
+.Fn property_find "properties list" "const char *name"
+.Ft void
+.Fn properties_free "properties list"
+.Sh DESCRIPTION
+.Bd -literal
+typedef struct _properties {
+ struct _properties *next;
+ char *name;
+ char *value;
+} *properties;
+.Ed
+.Pp
+The function
+.Fn properties_read
+reads
+.Fa name = value
+pairs from the file descriptor passed in
+.Fa fd
+and returns the head of a new property list, assuming that the
+file's contents have been parsed properly, or NULL in case
+of error.
+.Pp
+The
+.Fn property_find
+function returns the associated value string for the property named
+.Fa name
+if found, otherwise NULL.
+The value returned may be up to
+.Dv PROPERTY_MAX_VALUE
+bytes in length.
+.Pp
+The
+.Fn properties_free
+function is used to free the structure returned by
+.Fn properties_read
+when it is no longer needed.
+.Sh FILE FORMAT
+Each property in the file is assumed to have the format of
+.Fa name = value
+where
+.Fa name
+is an alphanumeric string (and any punctuation not including the `=' character)
+and
+.Fa value
+is an arbitrary string of text terminated by a newline character.
+If newlines
+are desired, the entire value should be enclosed in { } (curly-bracket)
+characters.
+Any line beginning with a # or ; character is assumed to
+be a comment and will be ignored.
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh BUGS
+Simplistic.
diff --git a/lib/libutil/property.c b/lib/libutil/property.c
new file mode 100644
index 0000000..a944f9d
--- /dev/null
+++ b/lib/libutil/property.c
@@ -0,0 +1,259 @@
+/*
+ *
+ * Simple property list handling code.
+ *
+ * Copyright (c) 1998
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <err.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static properties
+property_alloc(char *name, char *value)
+{
+ properties n;
+
+ if ((n = (properties)malloc(sizeof(struct _property))) == NULL)
+ return (NULL);
+ n->next = NULL;
+ if (name != NULL) {
+ if ((n->name = strdup(name)) == NULL) {
+ free(n);
+ return (NULL);
+ }
+ } else
+ n->name = NULL;
+ if (value != NULL) {
+ if ((n->value = strdup(value)) == NULL) {
+ free(n->name);
+ free(n);
+ return (NULL);
+ }
+ } else
+ n->value = NULL;
+ return (n);
+}
+
+properties
+properties_read(int fd)
+{
+ properties head, ptr;
+ char hold_n[PROPERTY_MAX_NAME + 1];
+ char hold_v[PROPERTY_MAX_VALUE + 1];
+ char buf[BUFSIZ * 4];
+ int bp, n, v, max;
+ enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state, last_state;
+ int ch = 0, blevel = 0;
+
+ n = v = bp = max = 0;
+ head = ptr = NULL;
+ state = last_state = LOOK;
+ while (state != STOP) {
+ if (state != COMMIT) {
+ if (bp == max) {
+ last_state = state;
+ state = FILL;
+ } else
+ ch = buf[bp++];
+ }
+ switch(state) {
+ case FILL:
+ if ((max = read(fd, buf, sizeof buf)) < 0) {
+ properties_free(head);
+ return (NULL);
+ }
+ if (max == 0) {
+ state = STOP;
+ } else {
+ /*
+ * Restore the state from before the fill (which will be
+ * initialised to LOOK for the first FILL). This ensures that
+ * if we were part-way through eg., a VALUE state, when the
+ * buffer ran out, that the previous operation will be allowed
+ * to complete.
+ */
+ state = last_state;
+ ch = buf[0];
+ bp = 0;
+ }
+ continue;
+
+ case LOOK:
+ if (isspace((unsigned char)ch))
+ continue;
+ /* Allow shell or lisp style comments */
+ else if (ch == '#' || ch == ';') {
+ state = COMMENT;
+ continue;
+ }
+ else if (isalnum((unsigned char)ch) || ch == '_') {
+ if (n >= PROPERTY_MAX_NAME) {
+ n = 0;
+ state = COMMENT;
+ }
+ else {
+ hold_n[n++] = ch;
+ state = NAME;
+ }
+ }
+ else
+ state = COMMENT; /* Ignore the rest of the line */
+ break;
+
+ case COMMENT:
+ if (ch == '\n')
+ state = LOOK;
+ break;
+
+ case NAME:
+ if (ch == '\n' || !ch) {
+ hold_n[n] = '\0';
+ hold_v[0] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else if (isspace((unsigned char)ch))
+ continue;
+ else if (ch == '=') {
+ hold_n[n] = '\0';
+ v = n = 0;
+ state = VALUE;
+ }
+ else
+ hold_n[n++] = ch;
+ break;
+
+ case VALUE:
+ if (v == 0 && ch == '\n') {
+ hold_v[v] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else if (v == 0 && isspace((unsigned char)ch))
+ continue;
+ else if (ch == '{') {
+ state = MVALUE;
+ ++blevel;
+ }
+ else if (ch == '\n' || !ch) {
+ hold_v[v] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else {
+ if (v >= PROPERTY_MAX_VALUE) {
+ state = COMMENT;
+ v = n = 0;
+ break;
+ }
+ else
+ hold_v[v++] = ch;
+ }
+ break;
+
+ case MVALUE:
+ /* multiline value */
+ if (v >= PROPERTY_MAX_VALUE) {
+ warn("properties_read: value exceeds max length");
+ state = COMMENT;
+ n = v = 0;
+ }
+ else if (ch == '}' && !--blevel) {
+ hold_v[v] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else {
+ hold_v[v++] = ch;
+ if (ch == '{')
+ ++blevel;
+ }
+ break;
+
+ case COMMIT:
+ if (head == NULL) {
+ if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL)
+ return (NULL);
+ } else {
+ if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) {
+ properties_free(head);
+ return (NULL);
+ }
+ ptr = ptr->next;
+ }
+ state = LOOK;
+ v = n = 0;
+ break;
+
+ case STOP:
+ /* we don't handle this here, but this prevents warnings */
+ break;
+ }
+ }
+ if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL)
+ return (NULL);
+
+ return (head);
+}
+
+char *
+property_find(properties list, const char *name)
+{
+ if (list == NULL || name == NULL || !name[0])
+ return (NULL);
+ while (list != NULL) {
+ if (list->name != NULL && strcmp(list->name, name) == 0)
+ return (list->value);
+ list = list->next;
+ }
+ return (NULL);
+}
+
+void
+properties_free(properties list)
+{
+ properties tmp;
+
+ while (list) {
+ tmp = list->next;
+ if (list->name)
+ free(list->name);
+ if (list->value)
+ free(list->value);
+ free(list);
+ list = tmp;
+ }
+}
diff --git a/lib/libutil/pty.3 b/lib/libutil/pty.3
new file mode 100644
index 0000000..0f06cc1
--- /dev/null
+++ b/lib/libutil/pty.3
@@ -0,0 +1,145 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd November 11, 2015
+.Dt PTY 3
+.Os
+.Sh NAME
+.Nm openpty ,
+.Nm forkpty
+.Nd auxiliary functions to obtain a pseudo-terminal
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/ioctl.h
+.In termios.h
+.In libutil.h
+.Ft int
+.Fn openpty "int *amaster" "int *aslave" "char *name" "struct termios *termp" "struct winsize *winp"
+.Ft int
+.Fn forkpty "int *amaster" "char *name" "struct termios *termp" "struct winsize *winp"
+.Sh DESCRIPTION
+The function
+.Fn openpty
+attempts to obtain the next available pseudo-terminal from the system (see
+.Xr pty 4 ) .
+If it successfully finds one, it subsequently changes the
+ownership of the slave device to the real UID of the current process,
+the group membership to the group
+.Dq tty
+(if such a group exists in the system), the access permissions for
+reading and writing by the owner, and for writing by the group, and
+invalidates any current use of the line by calling
+.Xr revoke 2 .
+.Pp
+If the argument
+.Fa name
+is not
+.Dv NULL ,
+.Fn openpty
+copies the pathname of the slave pty to this area.
+The caller is
+responsible for allocating the required space in this array.
+.Pp
+If the arguments
+.Fa termp
+or
+.Fa winp
+are not
+.Dv NULL ,
+.Fn openpty
+initializes the termios and window size settings from the structures
+these arguments point to, respectively.
+.Pp
+Upon return, the open file descriptors for the master and slave side
+of the pty are returned in the locations pointed to by
+.Fa amaster
+and
+.Fa aslave ,
+respectively.
+.Pp
+The
+.Fn forkpty
+function first calls
+.Fn openpty
+to obtain the next available pseudo-terminal from the system.
+Upon success,
+it forks off a new process.
+In the child process, it closes the descriptor
+for the master side of the pty, and calls
+.Xr login_tty 3
+for the slave pty.
+In the parent process, it closes the descriptor for the
+slave side of the pty.
+The arguments
+.Fa amaster ,
+.Fa name ,
+.Fa termp ,
+and
+.Fa winp
+have the same meaning as described for
+.Fn openpty .
+.Sh RETURN VALUES
+The
+.Fn openpty
+function returns 0 on success, or -1 on failure.
+.Pp
+The
+.Fn forkpty
+function returns -1 on failure, 0 in the slave process, and the process ID of
+the slave process in the parent process.
+.Sh ERRORS
+The
+.Fn openpty
+function may fail and set the global variable
+.Dv errno
+for any of the errors specified for the
+.Xr grantpt 3 ,
+.Xr posix_openpt 2 ,
+.Xr ptsname 3 ,
+and
+.Xr unlockpt 3
+functions and the
+.Xr revoke 2
+system call.
+.Pp
+In addition to this,
+.Fn forkpty
+may set it to any value as described for
+.Xr fork 2 .
+.Sh SEE ALSO
+.Xr chmod 2 ,
+.Xr chown 2 ,
+.Xr fork 2 ,
+.Xr getuid 2 ,
+.Xr open 2 ,
+.Xr revoke 2 ,
+.Xr login_tty 3 ,
+.Xr pty 4 ,
+.Xr termios 4 ,
+.Xr group 5
diff --git a/lib/libutil/pty.c b/lib/libutil/pty.c
new file mode 100644
index 0000000..7e06408
--- /dev/null
+++ b/lib/libutil/pty.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)pty.c 8.3 (Berkeley) 5/16/94";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <libutil.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+int
+openpty(int *amaster, int *aslave, char *name, struct termios *termp,
+ struct winsize *winp)
+{
+ const char *slavename;
+ int master, slave;
+
+ master = posix_openpt(O_RDWR|O_NOCTTY);
+ if (master == -1)
+ return (-1);
+
+ if (grantpt(master) == -1)
+ goto bad;
+
+ if (unlockpt(master) == -1)
+ goto bad;
+
+ slavename = ptsname(master);
+ if (slavename == NULL)
+ goto bad;
+
+ slave = open(slavename, O_RDWR);
+ if (slave == -1)
+ goto bad;
+
+ *amaster = master;
+ *aslave = slave;
+
+ if (name)
+ strcpy(name, slavename);
+ if (termp)
+ tcsetattr(slave, TCSAFLUSH, termp);
+ if (winp)
+ ioctl(slave, TIOCSWINSZ, (char *)winp);
+
+ return (0);
+
+bad: close(master);
+ return (-1);
+}
+
+int
+forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp)
+{
+ int master, slave, pid;
+
+ if (openpty(&master, &slave, name, termp, winp) == -1)
+ return (-1);
+ switch (pid = fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ /*
+ * child
+ */
+ (void) close(master);
+ login_tty(slave);
+ return (0);
+ }
+ /*
+ * parent
+ */
+ *amaster = master;
+ (void) close(slave);
+ return (pid);
+}
diff --git a/lib/libutil/pw_util.3 b/lib/libutil/pw_util.3
new file mode 100644
index 0000000..901135e
--- /dev/null
+++ b/lib/libutil/pw_util.3
@@ -0,0 +1,287 @@
+.\" Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 02, 2015
+.Dt PW_UTIL 3
+.Os
+.Sh NAME
+.Nm pw_copy ,
+.Nm pw_dup ,
+.Nm pw_edit ,
+.Nm pw_equal ,
+.Nm pw_fini ,
+.Nm pw_init ,
+.Nm pw_make ,
+.Nm pw_make_v7 ,
+.Nm pw_mkdb ,
+.Nm pw_lock ,
+.Nm pw_scan ,
+.Nm pw_tempname ,
+.Nm pw_tmp
+.Nd "functions for passwd file handling"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In pwd.h
+.In libutil.h
+.Ft int
+.Fn pw_copy "int ffd" "int tfd" "const struct passwd *pw" "struct passwd *oldpw"
+.Ft "struct passwd *"
+.Fn pw_dup "const struct passwd *pw"
+.Ft int
+.Fn pw_edit "int nosetuid"
+.Ft int
+.Fn pw_equal "const struct passwd *pw1" "const struct passwd *pw2"
+.Ft void
+.Fn pw_fini "void"
+.Ft int
+.Fn pw_init "const char *dir" const char *master"
+.Ft "char *"
+.Fn pw_make "const struct passwd *pw"
+.Ft "char *"
+.Fn pw_make_v7 "const struct passwd *pw"
+.Ft int
+.Fn pw_mkdb "const char *user"
+.Ft int
+.Fn pw_lock "void"
+.Ft "struct passwd *"
+.Fn pw_scan "const char *line" "int flags"
+.Ft "const char *"
+.Fn pw_tempname "void"
+.Ft int
+.Fn pw_tmp "int mfd"
+.Sh DESCRIPTION
+The
+.Fn pw_copy
+function reads a password file from
+.Vt ffd
+and writes it back out to
+.Vt tfd
+possibly with modifications:
+.Bl -dash
+.It
+If
+.Fa pw
+is
+.Dv NULL
+and
+.Fa oldpw
+is not
+.Dv NULL ,
+then the record represented by
+.Fa oldpw
+will not be copied (corresponding to user deletion).
+.It
+If
+.Fa pw
+and
+.Fa oldpw
+are not
+.Dv NULL
+then the record corresponding to
+.Fa pw
+will be replaced by the record corresponding to
+.Fa oldpw .
+.It
+If
+.Vt pw
+is set and
+.Vt oldpw
+is
+.Dv NULL
+then the record corresponding to
+.Vt pw
+will be appended (corresponding to user addition).
+.El
+.Pp
+The
+.Fn pw_copy
+function returns -1 in case of failure otherwise 0.
+.Pp
+The
+.Fn pw_dup
+function duplicates the
+.Vt struct passwd
+pointed to by
+.Fa pw
+and returns a pointer to the copy, or
+.Dv NULL
+in case of failure.
+The new
+.Vt struct passwd
+is allocated with
+.Xr malloc 3 ,
+and it is the caller's responsibility to free it with
+.Xr free 3 .
+.Pp
+The
+.Fn pw_edit
+function invokes the command specified by the
+.Ev EDITOR
+environment variable (or
+.Pa /usr/bin/vi
+if
+.Ev EDITOR
+is not defined)
+on a temporary copy of the master password file created by
+.Fn pw_tmp .
+If the file was modified,
+.Fn pw_edit
+installs it and regenerates the password database.
+The
+.Fn pw_edit
+function returns -1 in case of failure, 0 if the file was not modified,
+and a non-zero positive number if the file was modified and successfully
+installed.
+.Pp
+The
+.Fn pw_equal
+function compares two
+.Vt struct passwd
+and returns 0 if they are equal.
+.Pp
+The
+.Fn pw_fini
+function destroy the temporary file created by
+.Fn pw_tmp
+if any,
+kills any running instance of
+.Ev EDITOR
+executed by
+.Fn pw_edit
+if any,
+and closes the lock created by
+.Fn pw_lock
+if any.
+.Pp
+The
+.Fn pw_init
+initialize the static variable representing the path a password file.
+.Fa dir
+is the directory where the password file is located.
+If set to
+.Dv NULL ,
+it will default to
+.Pa /etc .
+.Fa master
+is the name of the password file.
+If set to
+.Dv NULL?
+it will default to
+.Pa master.passwd
+.Pp
+The
+.Fn pw_make
+function creates a properly formatted
+.Bx
+.Xr passwd 5
+line from a
+.Vt struct passwd ,
+and returns a pointer to the resulting string.
+The string is allocated with
+.Xr malloc 3 ,
+and it is the caller's responsibility to free it with
+.Xr free 3 .
+.Pp
+The
+.Fn pw_make_v7
+function creates a properly formatted
+.Ux V7
+.Xr passwd 5
+line from a
+.Vt struct passwd ,
+and returns a pointer to the resulting string.
+The string is allocated with
+.Xr malloc 3 ,
+and it is the caller's responsibility to free it with
+.Xr free 3 .
+.Pp
+The
+.Fn pw_mkdb
+function regenerates the password database by running
+.Xr pwd_mkdb 8 .
+If
+.Fa user
+only the record corresponding to that user will be updated.
+The
+.Fn pw_mkdb
+function returns 0 in case of success and -1 in case of failure.
+.Pp
+The
+.Fn pw_lock
+function locks the master password file.
+It returns a file descriptor to the master password file on success
+and -1 on failure.
+.Pp
+The
+.Fn pw_scan
+function is a wrapper around the internal libc function
+.Fn __pw_scan .
+It scans the master password file for a line corresponding to the
+.Fa line
+provided and return a
+.Vt struct passwd
+if it matched an existing record.
+In case of failure, it returns
+.Dv NULL .
+Otherwise, it returns a pointer to a
+.Vt struct passwd
+containing the matching record.
+The
+.Vt struct passwd
+is allocated with
+.Xr malloc 3 ,
+and it is the caller's responsibility to free it with
+.Xr free 3 .
+.Pp
+The
+.Fn pw_tempname
+function returns the temporary name of the masterfile created via
+.Fn pw_tmp .
+.Pp
+The
+.Fn pw_tmp
+creates and opens a presumably safe temporary password file.
+If
+.Fa mfd
+is a file descriptor to an open password file, it will be read and
+written back to the temporary password file.
+Otherwise if should be set -1.
+The
+.Fn pw_tmp
+returns an open file descriptor to the temporary password file or -1 in case of
+failure.
+.Sh AUTHORS
+Portions of this software were developed for the
+.Fx
+Project by ThinkSec AS and Network Associates Laboratories, the
+Security Research Division of Network Associates, Inc.\& under
+DARPA/SPAWAR contract N66001-01-C-8035
+.Pq Dq CBOSS ,
+as part of the DARPA CHATS research program.
+.Pp
+This manual page was written by
+.An Baptiste Daroussin Aq Mt bapt@FreeBSD.org .
diff --git a/lib/libutil/pw_util.c b/lib/libutil/pw_util.c
new file mode 100644
index 0000000..af749d5
--- /dev/null
+++ b/lib/libutil/pw_util.c
@@ -0,0 +1,664 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * This file is used by all the "password" programs; vipw(8), chpass(1),
+ * and passwd(1).
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libutil.h"
+
+static pid_t editpid = -1;
+static int lockfd = -1;
+static char masterpasswd[PATH_MAX];
+static char passwd_dir[PATH_MAX];
+static char tempname[PATH_MAX];
+static int initialized;
+
+#if 0
+void
+pw_cont(int sig)
+{
+
+ if (editpid != -1)
+ kill(editpid, sig);
+}
+#endif
+
+/*
+ * Initialize statics and set limits, signals & umask to try to avoid
+ * interruptions, crashes etc. that might expose passord data.
+ */
+int
+pw_init(const char *dir, const char *master)
+{
+#if 0
+ struct rlimit rlim;
+#endif
+
+ if (dir == NULL) {
+ strcpy(passwd_dir, _PATH_ETC);
+ } else {
+ if (strlen(dir) >= sizeof(passwd_dir)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ strcpy(passwd_dir, dir);
+ }
+
+ if (master == NULL) {
+ if (dir == NULL) {
+ strcpy(masterpasswd, _PATH_MASTERPASSWD);
+ } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s",
+ passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ } else {
+ if (strlen(master) >= sizeof(masterpasswd)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ strcpy(masterpasswd, master);
+ }
+
+ /*
+ * The code that follows is extremely disruptive to the calling
+ * process, and is therefore disabled until someone can conceive
+ * of a realistic scenario where it would fend off a compromise.
+ * Race conditions concerning the temporary files can be guarded
+ * against in other ways than masking signals (by checking stat(2)
+ * results after creation).
+ */
+#if 0
+ /* Unlimited resource limits. */
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &rlim);
+ (void)setrlimit(RLIMIT_FSIZE, &rlim);
+ (void)setrlimit(RLIMIT_STACK, &rlim);
+ (void)setrlimit(RLIMIT_DATA, &rlim);
+ (void)setrlimit(RLIMIT_RSS, &rlim);
+
+ /* Don't drop core (not really necessary, but GP's). */
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rlim);
+
+ /* Turn off signals. */
+ (void)signal(SIGALRM, SIG_IGN);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTERM, SIG_IGN);
+ (void)signal(SIGCONT, pw_cont);
+
+ /* Create with exact permissions. */
+ (void)umask(0);
+#endif
+ initialized = 1;
+ return (0);
+}
+
+/*
+ * Lock the master password file.
+ */
+int
+pw_lock(void)
+{
+
+ if (*masterpasswd == '\0')
+ return (-1);
+
+ /*
+ * If the master password file doesn't exist, the system is hosed.
+ * Might as well try to build one. Set the close-on-exec bit so
+ * that users can't get at the encrypted passwords while editing.
+ * Open should allow flock'ing the file; see 4.4BSD. XXX
+ */
+ for (;;) {
+ struct stat st;
+
+ lockfd = flopen(masterpasswd, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0);
+ if (lockfd == -1) {
+ if (errno == EWOULDBLOCK) {
+ errx(1, "the password db file is busy");
+ } else {
+ err(1, "could not lock the passwd file: ");
+ }
+ }
+
+ /*
+ * If the password file was replaced while we were trying to
+ * get the lock, our hardlink count will be 0 and we have to
+ * close and retry.
+ */
+ if (fstat(lockfd, &st) == -1)
+ err(1, "fstat() failed: ");
+ if (st.st_nlink != 0)
+ break;
+ close(lockfd);
+ lockfd = -1;
+ }
+ return (lockfd);
+}
+
+/*
+ * Create and open a presumably safe temp file for editing the password
+ * data, and copy the master password file into it.
+ */
+int
+pw_tmp(int mfd)
+{
+ char buf[8192];
+ ssize_t nr;
+ const char *p;
+ int tfd;
+
+ if (*masterpasswd == '\0')
+ return (-1);
+ if ((p = strrchr(masterpasswd, '/')))
+ ++p;
+ else
+ p = masterpasswd;
+ if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX",
+ (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ if ((tfd = mkostemp(tempname, O_SYNC)) == -1)
+ return (-1);
+ if (mfd != -1) {
+ while ((nr = read(mfd, buf, sizeof(buf))) > 0)
+ if (write(tfd, buf, (size_t)nr) != nr)
+ break;
+ if (nr != 0) {
+ unlink(tempname);
+ *tempname = '\0';
+ close(tfd);
+ return (-1);
+ }
+ }
+ return (tfd);
+}
+
+/*
+ * Regenerate the password database.
+ */
+int
+pw_mkdb(const char *user)
+{
+ int pstat;
+ pid_t pid;
+
+ (void)fflush(stderr);
+ switch ((pid = fork())) {
+ case -1:
+ return (-1);
+ case 0:
+ /* child */
+ if (user == NULL)
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+ "-d", passwd_dir, tempname, (char *)NULL);
+ else
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+ "-d", passwd_dir, "-u", user, tempname,
+ (char *)NULL);
+ _exit(1);
+ /* NOTREACHED */
+ default:
+ /* parent */
+ break;
+ }
+ if (waitpid(pid, &pstat, 0) == -1)
+ return (-1);
+ if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
+ return (0);
+ errno = 0;
+ return (-1);
+}
+
+/*
+ * Edit the temp file. Return -1 on error, >0 if the file was modified, 0
+ * if it was not.
+ */
+int
+pw_edit(int notsetuid)
+{
+ struct sigaction sa, sa_int, sa_quit;
+ sigset_t oldsigset, nsigset;
+ struct stat st1, st2;
+ const char *editor;
+ int pstat;
+
+ if ((editor = getenv("EDITOR")) == NULL)
+ editor = _PATH_VI;
+ if (stat(tempname, &st1) == -1)
+ return (-1);
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGINT, &sa, &sa_int);
+ sigaction(SIGQUIT, &sa, &sa_quit);
+ sigemptyset(&nsigset);
+ sigaddset(&nsigset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nsigset, &oldsigset);
+ switch ((editpid = fork())) {
+ case -1:
+ return (-1);
+ case 0:
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldsigset, NULL);
+ if (notsetuid) {
+ (void)setgid(getgid());
+ (void)setuid(getuid());
+ }
+ errno = 0;
+ execlp(editor, basename(editor), tempname, (char *)NULL);
+ _exit(errno);
+ default:
+ /* parent */
+ break;
+ }
+ for (;;) {
+ if (waitpid(editpid, &pstat, WUNTRACED) == -1) {
+ if (errno == EINTR)
+ continue;
+ unlink(tempname);
+ editpid = -1;
+ break;
+ } else if (WIFSTOPPED(pstat)) {
+ raise(WSTOPSIG(pstat));
+ } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) {
+ editpid = -1;
+ break;
+ } else {
+ unlink(tempname);
+ editpid = -1;
+ break;
+ }
+ }
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldsigset, NULL);
+ if (stat(tempname, &st2) == -1)
+ return (-1);
+ return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec ||
+ st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec);
+}
+
+/*
+ * Clean up. Preserve errno for the caller's convenience.
+ */
+void
+pw_fini(void)
+{
+ int serrno, status;
+
+ if (!initialized)
+ return;
+ initialized = 0;
+ serrno = errno;
+ if (editpid != -1) {
+ kill(editpid, SIGTERM);
+ kill(editpid, SIGCONT);
+ waitpid(editpid, &status, 0);
+ editpid = -1;
+ }
+ if (*tempname != '\0') {
+ unlink(tempname);
+ *tempname = '\0';
+ }
+ if (lockfd != -1)
+ close(lockfd);
+ errno = serrno;
+}
+
+/*
+ * Compares two struct pwds.
+ */
+int
+pw_equal(const struct passwd *pw1, const struct passwd *pw2)
+{
+ return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
+ pw1->pw_uid == pw2->pw_uid &&
+ pw1->pw_gid == pw2->pw_gid &&
+ strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
+ pw1->pw_change == pw2->pw_change &&
+ pw1->pw_expire == pw2->pw_expire &&
+ strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
+ strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
+ strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
+}
+
+/*
+ * Make a passwd line out of a struct passwd.
+ */
+char *
+pw_make(const struct passwd *pw)
+{
+ char *line;
+
+ asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name,
+ pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid,
+ pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire,
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ return (line);
+}
+
+/*
+ * Make a passwd line (in v7 format) out of a struct passwd
+ */
+char *
+pw_make_v7(const struct passwd *pw)
+{
+ char *line;
+
+ asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name,
+ (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid,
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ return (line);
+}
+
+/*
+ * Copy password file from one descriptor to another, replacing, deleting
+ * or adding a single record on the way.
+ */
+int
+pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw)
+{
+ char buf[8192], *end, *line, *p, *q, *r, t;
+ struct passwd *fpw;
+ const struct passwd *spw;
+ size_t len;
+ int eof, readlen;
+
+ if (old_pw == NULL && pw == NULL)
+ return (-1);
+
+ spw = old_pw;
+ /* deleting a user */
+ if (pw == NULL) {
+ line = NULL;
+ } else {
+ if ((line = pw_make(pw)) == NULL)
+ return (-1);
+ }
+
+ /* adding a user */
+ if (spw == NULL)
+ spw = pw;
+
+ eof = 0;
+ len = 0;
+ p = q = end = buf;
+ for (;;) {
+ /* find the end of the current line */
+ for (p = q; q < end && *q != '\0'; ++q)
+ if (*q == '\n')
+ break;
+
+ /* if we don't have a complete line, fill up the buffer */
+ if (q >= end) {
+ if (eof)
+ break;
+ if ((size_t)(q - p) >= sizeof(buf)) {
+ warnx("passwd line too long");
+ errno = EINVAL; /* hack */
+ goto err;
+ }
+ if (p < end) {
+ q = memmove(buf, p, end - p);
+ end -= p - buf;
+ } else {
+ p = q = end = buf;
+ }
+ readlen = read(ffd, end, sizeof(buf) - (end - buf));
+ if (readlen == -1)
+ goto err;
+ else
+ len = (size_t)readlen;
+ if (len == 0 && p == buf)
+ break;
+ end += len;
+ len = end - buf;
+ if (len < (ssize_t)sizeof(buf)) {
+ eof = 1;
+ if (len > 0 && buf[len - 1] != '\n')
+ ++len, *end++ = '\n';
+ }
+ continue;
+ }
+
+ /* is it a blank line or a comment? */
+ for (r = p; r < q && isspace(*r); ++r)
+ /* nothing */ ;
+ if (r == q || *r == '#') {
+ /* yep */
+ if (write(tfd, p, q - p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+
+ /* is it the one we're looking for? */
+
+ t = *q;
+ *q = '\0';
+
+ fpw = pw_scan(r, PWSCAN_MASTER);
+
+ /*
+ * fpw is either the struct passwd for the current line,
+ * or NULL if the line is malformed.
+ */
+
+ *q = t;
+ if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) {
+ /* nope */
+ if (fpw != NULL)
+ free(fpw);
+ if (write(tfd, p, q - p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+ if (old_pw && !pw_equal(fpw, old_pw)) {
+ warnx("entry inconsistent");
+ free(fpw);
+ errno = EINVAL; /* hack */
+ goto err;
+ }
+ free(fpw);
+
+ /* it is, replace or remove it */
+ if (line != NULL) {
+ len = strlen(line);
+ if (write(tfd, line, len) != (int)len)
+ goto err;
+ } else {
+ /* when removed, avoid the \n */
+ q++;
+ }
+ /* we're done, just copy the rest over */
+ for (;;) {
+ if (write(tfd, q, end - q) != end - q)
+ goto err;
+ q = buf;
+ readlen = read(ffd, buf, sizeof(buf));
+ if (readlen == 0)
+ break;
+ else
+ len = (size_t)readlen;
+ if (readlen == -1)
+ goto err;
+ end = buf + len;
+ }
+ goto done;
+ }
+
+ /* if we got here, we didn't find the old entry */
+ if (line == NULL) {
+ errno = ENOENT;
+ goto err;
+ }
+ len = strlen(line);
+ if ((size_t)write(tfd, line, len) != len ||
+ write(tfd, "\n", 1) != 1)
+ goto err;
+ done:
+ if (line != NULL)
+ free(line);
+ return (0);
+ err:
+ if (line != NULL)
+ free(line);
+ return (-1);
+}
+
+/*
+ * Return the current value of tempname.
+ */
+const char *
+pw_tempname(void)
+{
+
+ return (tempname);
+}
+
+/*
+ * Duplicate a struct passwd.
+ */
+struct passwd *
+pw_dup(const struct passwd *pw)
+{
+ char *dst;
+ struct passwd *npw;
+ ssize_t len;
+
+ len = sizeof(*npw);
+ if (pw->pw_name != NULL)
+ len += strlen(pw->pw_name) + 1;
+ if (pw->pw_passwd != NULL)
+ len += strlen(pw->pw_passwd) + 1;
+ if (pw->pw_class != NULL)
+ len += strlen(pw->pw_class) + 1;
+ if (pw->pw_gecos != NULL)
+ len += strlen(pw->pw_gecos) + 1;
+ if (pw->pw_dir != NULL)
+ len += strlen(pw->pw_dir) + 1;
+ if (pw->pw_shell != NULL)
+ len += strlen(pw->pw_shell) + 1;
+ if ((npw = malloc((size_t)len)) == NULL)
+ return (NULL);
+ memcpy(npw, pw, sizeof(*npw));
+ dst = (char *)npw + sizeof(*npw);
+ if (pw->pw_name != NULL) {
+ npw->pw_name = dst;
+ dst = stpcpy(npw->pw_name, pw->pw_name) + 1;
+ }
+ if (pw->pw_passwd != NULL) {
+ npw->pw_passwd = dst;
+ dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1;
+ }
+ if (pw->pw_class != NULL) {
+ npw->pw_class = dst;
+ dst = stpcpy(npw->pw_class, pw->pw_class) + 1;
+ }
+ if (pw->pw_gecos != NULL) {
+ npw->pw_gecos = dst;
+ dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1;
+ }
+ if (pw->pw_dir != NULL) {
+ npw->pw_dir = dst;
+ dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1;
+ }
+ if (pw->pw_shell != NULL) {
+ npw->pw_shell = dst;
+ dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1;
+ }
+ return (npw);
+}
+
+#include "pw_scan.h"
+
+/*
+ * Wrapper around an internal libc function
+ */
+struct passwd *
+pw_scan(const char *line, int flags)
+{
+ struct passwd pw, *ret;
+ char *bp;
+
+ if ((bp = strdup(line)) == NULL)
+ return (NULL);
+ if (!__pw_scan(bp, &pw, flags)) {
+ free(bp);
+ return (NULL);
+ }
+ ret = pw_dup(&pw);
+ free(bp);
+ return (ret);
+}
diff --git a/lib/libutil/quotafile.3 b/lib/libutil/quotafile.3
new file mode 100644
index 0000000..362cf01
--- /dev/null
+++ b/lib/libutil/quotafile.3
@@ -0,0 +1,290 @@
+.\"-
+.\" Copyright (c) 2009 Dag-Erling Coïdan Smørgrav and
+.\" Marshall Kirk McKusick. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 28, 2009
+.Dt QUOTAFILE 3
+.Os
+.Sh NAME
+.Nm quota_open
+.Nm quota_close
+.Nm quota_on
+.Nm quota_off
+.Nm quota_read
+.Nm quota_write_limits
+.Nm quota_write_usage
+.Nm quota_fsname
+.Nm quota_qfname
+.Nm quota_maxid
+.Nm quota_check_path
+.Nm quota_convert
+.Nd "Manipulate quotas"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/mount.h
+.In ufs/ufs/quota.h
+.In fcntl.h
+.In fstab.h
+.In libutil.h
+.Ft "struct quotafile *"
+.Fn quota_open "struct fstab *fs" "int quotatype" "int openflags"
+.Ft int
+.Fn quota_close "struct quotafile *qf"
+.Ft int
+.Fn quota_on "const struct quotafile *qf"
+.Ft int
+.Fn quota_off "const struct quotafile *qf"
+.Ft int
+.Fn quota_read "struct quotafile *qf" "struct dqblk *dqb" "int id"
+.Ft int
+.Fn quota_write_limits "struct quotafile *qf" "struct dqblk *dqb" "int id"
+.Ft int
+.Fn quota_write_usage "struct quotafile *qf" "struct dqblk *dqb" "int id"
+.Ft "const char *"
+.Fn quota_fsname "const struct quotafile *qf"
+.Ft "const char *"
+.Fn quota_qfname "const struct quotafile *qf"
+.Ft int
+.Fn quota_maxid "const struct quotafile *qf"
+.Ft int
+.Fn quota_check_path "const struct quotafile *qf" "const char *path"
+.Ft int
+.Fn quota_convert "struct quotafile *qf" "int wordsize"
+.Sh DESCRIPTION
+These functions are designed to simplify access to filesystem quotas.
+If quotas are active on a filesystem,
+these functions will access them directly from the kernel using the
+.Fn quotactl
+system call.
+If quotas are not active,
+these functions will access them by reading and writing
+the quota files directly.
+.Pp
+The
+.Fn quota_open
+function takes a pointer to an
+.Vt fstab
+entry corresponding to the filesystem on which quotas
+are to be accessed.
+The
+.Va quotatype
+field indicates the type of quotas being sought, either
+.Dv USRQUOTA
+or
+.Dv GRPQUOTA .
+The
+.Va openflags
+are those used by the
+.Fn open
+system call, usually either
+.Dv O_RDONLY
+if the quotas are just to be read, or
+.Dv O_RDWR
+if the quotas are to be updated.
+The
+.Dv O_CREAT
+flag should be specified if a new quota file of the requested type
+should be created if it does not already exist.
+.Pp
+The
+.Fn quota_close
+function closes any open file descriptors and frees any storage
+associated with the filesystem and quota type referenced by
+.Va qf .
+.Pp
+The
+.Fn quota_on
+function enables quotas for the filesystem associated with its
+.Va qf
+argument which may have been opened with
+.Dv O_RDONLY
+or
+.Dv O_RDWR .
+The
+.Fn quota_on
+function returns 0 if successful;
+otherwise the value\~-1 is returned and the global variable
+.Va errno
+is set to indicate the error, see
+.Xr quotactl 2
+for the possible errors.
+.Pp
+The
+.Fn quota_off
+function disables quotas for the filesystem associated with its
+.Va qf
+argument which may have been opened with
+.Dv O_RDONLY
+or
+.Dv O_RDWR .
+The
+.Fn quota_off
+function returns 0 if successful;
+otherwise the value\~-1 is returned and the global variable
+.Va errno
+is set to indicate the error, see
+.Xr quotactl 2
+for the possible errors.
+.Pp
+The
+.Fn quota_read
+function reads the quota from the filesystem and quota type referenced by
+.Va qf
+for the user (or group) specified by
+.Va id
+into the
+.Vt dqblk
+quota structure pointed to by
+.Va dqb .
+.Pp
+The
+.Fn quota_write_limits
+function updates the limit fields (but not the usage fields)
+for the filesystem and quota type referenced by
+.Va qf
+for the user (or group) specified by
+.Va id
+from the
+.Vt dqblk
+quota structure pointed to by
+.Va dqb .
+.Pp
+The
+.Fn quota_write_usage
+function updates the usage fields (but not the limit fields)
+for the filesystem and quota type referenced by
+.Va qf
+for the user (or group) specified by
+.Va id
+from the
+.Vt dqblk
+quota structure pointed to by
+.Va dqb .
+.Pp
+The
+.Fn quota_fsname
+function returns a pointer to a buffer containing the path to the root
+of the file system that corresponds to its
+.Va qf
+argument, as listed in
+.Pa /etc/fstab .
+Note that this may be a symbolic link to the actual directory.
+.Pp
+The
+.Fn quota_qfname
+function returns a pointer to a buffer containing the name of the
+quota file that corresponds to its
+.Va qf
+argument.
+Note that this may be a symbolic link to the actual file.
+.Pp
+The
+.Fn quota_maxid
+function returns the maximum user (or group)
+.Va id
+contained in the quota file associated with its
+.Va qf
+argument.
+.Pp
+The
+.Fn quota_check_path
+function checks if the specified path is within the filesystem that
+corresponds to its
+.Va qf
+argument.
+If the
+.Va path
+argument refers to a symbolic link,
+.Fn quota_check_path
+will follow it.
+.Pp
+The
+.Fn quota_convert
+function converts the quota file associated with its
+.Va qf
+argument to the data size specified by its
+.Va wordsize
+argument.
+The supported wordsize arguments are 32 for the old 32-bit
+quota file format and 64 for the new 64-bit quota file format.
+The
+.Fn quota_convert
+function may only be called to operate on quota files that
+are not currently active.
+.Sh IMPLEMENTATION NOTES
+If the underlying quota file is in or converted to the old 32-bit format,
+limit and usage values written to the quota file will be clipped to 32 bits.
+.Sh RETURN VALUES
+If the filesystem has quotas associated with it,
+.Fn quota_open
+returns a pointer to a
+.Vt quotafile
+structure used in subsequent quota access calls.
+If the filesystem has no quotas, or access permission is denied
+.Dv NULL
+is returned and
+.Va errno
+is set to indicate the error.
+.Pp
+The
+.Fn quota_check_path
+function returns\~1 for a positive result and\~0 for a negative
+result.
+If an error occurs, it returns\~-1 and sets
+.Va errno
+to indicate the error.
+.Pp
+The
+.Fn quota_read ,
+.Fn quota_write_limits ,
+.Fn quota_write_usage ,
+.Fn quota_convert ,
+and
+.Fn quota_close
+functions return zero on success.
+On error they return\~-1
+and set
+.Va errno
+to indicate the error.
+.Sh SEE ALSO
+.Xr quotactl 2 ,
+.Xr quota.group 5 ,
+.Xr quota.user 5
+.Sh HISTORY
+The
+.Nm quotafile
+functions first appeared in
+.Fx 8.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm quotafile
+functions and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org
+and
+.An Marshall Kirk McKusick Aq Mt mckusick@mckusick.com .
diff --git a/lib/libutil/quotafile.c b/lib/libutil/quotafile.c
new file mode 100644
index 0000000..6b5a44d
--- /dev/null
+++ b/lib/libutil/quotafile.c
@@ -0,0 +1,595 @@
+/*-
+ * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2008 Marshall Kirk McKusick
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/quota.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <libutil.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct quotafile {
+ int fd; /* -1 means using quotactl for access */
+ int accmode; /* access mode */
+ int wordsize; /* 32-bit or 64-bit limits */
+ int quotatype; /* USRQUOTA or GRPQUOTA */
+ dev_t dev; /* device */
+ char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */
+ char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */
+};
+
+static const char *qfextension[] = INITQFNAMES;
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+static int
+hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
+{
+ char *opt;
+ char *cp;
+ struct statfs sfb;
+ char buf[BUFSIZ];
+ static char initname, usrname[100], grpname[100];
+
+ /*
+ * 1) we only need one of these
+ * 2) fstab may specify a different filename
+ */
+ if (!initname) {
+ (void)snprintf(usrname, sizeof(usrname), "%s%s",
+ qfextension[USRQUOTA], QUOTAFILENAME);
+ (void)snprintf(grpname, sizeof(grpname), "%s%s",
+ qfextension[GRPQUOTA], QUOTAFILENAME);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = strchr(opt, '=')))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ /*
+ * Ensure that the filesystem is mounted.
+ */
+ if (statfs(fs->fs_file, &sfb) != 0 ||
+ strcmp(fs->fs_file, sfb.f_mntonname)) {
+ return (0);
+ }
+ if (cp) {
+ strncpy(qfnamep, cp, qfbufsize);
+ } else {
+ (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
+ QUOTAFILENAME, qfextension[type]);
+ }
+ return (1);
+}
+
+struct quotafile *
+quota_open(struct fstab *fs, int quotatype, int openflags)
+{
+ struct quotafile *qf;
+ struct dqhdr64 dqh;
+ struct group *grp;
+ struct stat st;
+ int qcmd, serrno;
+
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ return (NULL);
+ if ((qf = calloc(1, sizeof(*qf))) == NULL)
+ return (NULL);
+ qf->fd = -1;
+ qf->quotatype = quotatype;
+ strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
+ if (stat(qf->fsname, &st) != 0)
+ goto error;
+ qf->dev = st.st_dev;
+ serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
+ qcmd = QCMD(Q_GETQUOTASIZE, quotatype);
+ if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0)
+ return (qf);
+ if (serrno == 0) {
+ errno = EOPNOTSUPP;
+ goto error;
+ }
+ qf->accmode = openflags & O_ACCMODE;
+ if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 &&
+ (openflags & O_CREAT) != O_CREAT)
+ goto error;
+ /* File open worked, so process it */
+ if (qf->fd != -1) {
+ qf->wordsize = 32;
+ switch (read(qf->fd, &dqh, sizeof(dqh))) {
+ case -1:
+ goto error;
+ case sizeof(dqh):
+ if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
+ /* no magic, assume 32 bits */
+ qf->wordsize = 32;
+ return (qf);
+ }
+ if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
+ be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
+ be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
+ /* correct magic, wrong version / lengths */
+ errno = EINVAL;
+ goto error;
+ }
+ qf->wordsize = 64;
+ return (qf);
+ default:
+ qf->wordsize = 32;
+ return (qf);
+ }
+ /* not reached */
+ }
+ /* open failed, but O_CREAT was specified, so create a new file */
+ if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) <
+ 0)
+ goto error;
+ qf->wordsize = 64;
+ memset(&dqh, 0, sizeof(dqh));
+ memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
+ dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
+ dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
+ dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
+ if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
+ /* it was one we created ourselves */
+ unlink(qf->qfname);
+ goto error;
+ }
+ grp = getgrnam(QUOTAGROUP);
+ fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
+ fchmod(qf->fd, 0640);
+ return (qf);
+error:
+ serrno = errno;
+ /* did we have an open file? */
+ if (qf->fd != -1)
+ close(qf->fd);
+ free(qf);
+ errno = serrno;
+ return (NULL);
+}
+
+void
+quota_close(struct quotafile *qf)
+{
+
+ if (qf->fd != -1)
+ close(qf->fd);
+ free(qf);
+}
+
+int
+quota_on(struct quotafile *qf)
+{
+ int qcmd;
+
+ qcmd = QCMD(Q_QUOTAON, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, 0, qf->qfname));
+}
+
+int
+quota_off(struct quotafile *qf)
+{
+
+ return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0));
+}
+
+const char *
+quota_fsname(const struct quotafile *qf)
+{
+
+ return (qf->fsname);
+}
+
+const char *
+quota_qfname(const struct quotafile *qf)
+{
+
+ return (qf->qfname);
+}
+
+int
+quota_check_path(const struct quotafile *qf, const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) == -1)
+ return (-1);
+ return (st.st_dev == qf->dev);
+}
+
+int
+quota_maxid(struct quotafile *qf)
+{
+ struct stat st;
+ int maxid;
+
+ if (stat(qf->qfname, &st) < 0)
+ return (0);
+ switch (qf->wordsize) {
+ case 32:
+ maxid = st.st_size / sizeof(struct dqblk32) - 1;
+ break;
+ case 64:
+ maxid = st.st_size / sizeof(struct dqblk64) - 2;
+ break;
+ default:
+ maxid = 0;
+ break;
+ }
+ return (maxid > 0 ? maxid : 0);
+}
+
+static int
+quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk32 dqb32;
+ off_t off;
+
+ off = id * sizeof(struct dqblk32);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
+ case 0:
+ memset(dqb, 0, sizeof(*dqb));
+ return (0);
+ case sizeof(dqb32):
+ dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
+ dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
+ dqb->dqb_curblocks = dqb32.dqb_curblocks;
+ dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
+ dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
+ dqb->dqb_curinodes = dqb32.dqb_curinodes;
+ dqb->dqb_btime = dqb32.dqb_btime;
+ dqb->dqb_itime = dqb32.dqb_itime;
+ return (0);
+ default:
+ return (-1);
+ }
+}
+
+static int
+quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk64 dqb64;
+ off_t off;
+
+ off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
+ case 0:
+ memset(dqb, 0, sizeof(*dqb));
+ return (0);
+ case sizeof(dqb64):
+ dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
+ dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
+ dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
+ dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
+ dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
+ dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
+ dqb->dqb_btime = be64toh(dqb64.dqb_btime);
+ dqb->dqb_itime = be64toh(dqb64.dqb_itime);
+ return (0);
+ default:
+ return (-1);
+ }
+}
+
+int
+quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ int qcmd;
+
+ if (qf->fd == -1) {
+ qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, id, dqb));
+ }
+ switch (qf->wordsize) {
+ case 32:
+ return (quota_read32(qf, dqb, id));
+ case 64:
+ return (quota_read64(qf, dqb, id));
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ /* not reached */
+}
+
+#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
+
+static int
+quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
+{
+ struct dqblk32 dqb32;
+ off_t off;
+
+ dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
+ dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
+ dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
+ dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
+ dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
+ dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
+ dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
+ dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
+
+ off = id * sizeof(struct dqblk32);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
+ return (0);
+ return (-1);
+}
+
+static int
+quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
+{
+ struct dqblk64 dqb64;
+ off_t off;
+
+ dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
+ dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
+ dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
+ dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
+ dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
+ dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
+ dqb64.dqb_btime = htobe64(dqb->dqb_btime);
+ dqb64.dqb_itime = htobe64(dqb->dqb_itime);
+
+ off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
+ if (lseek(qf->fd, off, SEEK_SET) == -1)
+ return (-1);
+ if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
+ return (0);
+ return (-1);
+}
+
+int
+quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk dqbuf;
+ int qcmd;
+
+ if (qf->fd == -1) {
+ qcmd = QCMD(Q_SETUSE, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, id, dqb));
+ }
+ /*
+ * Have to do read-modify-write of quota in file.
+ */
+ if ((qf->accmode & O_RDWR) != O_RDWR) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (quota_read(qf, &dqbuf, id) != 0)
+ return (-1);
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it.
+ */
+ if (dqbuf.dqb_bsoftlimit && id != 0 &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+ dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
+ dqbuf.dqb_btime = 0;
+ if (dqbuf.dqb_isoftlimit && id != 0 &&
+ dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
+ dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
+ dqbuf.dqb_itime = 0;
+ dqbuf.dqb_curinodes = dqb->dqb_curinodes;
+ dqbuf.dqb_curblocks = dqb->dqb_curblocks;
+ /*
+ * Write it back.
+ */
+ switch (qf->wordsize) {
+ case 32:
+ return (quota_write32(qf, &dqbuf, id));
+ case 64:
+ return (quota_write64(qf, &dqbuf, id));
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ /* not reached */
+}
+
+int
+quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
+{
+ struct dqblk dqbuf;
+ int qcmd;
+
+ if (qf->fd == -1) {
+ qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
+ return (quotactl(qf->fsname, qcmd, id, dqb));
+ }
+ /*
+ * Have to do read-modify-write of quota in file.
+ */
+ if ((qf->accmode & O_RDWR) != O_RDWR) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (quota_read(qf, &dqbuf, id) != 0)
+ return (-1);
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it
+ * or if there previously was no soft limit, but
+ * now have one and are over it.
+ */
+ if (dqbuf.dqb_bsoftlimit && id != 0 &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+ dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
+ dqb->dqb_btime = 0;
+ if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
+ dqb->dqb_bsoftlimit > 0 &&
+ dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
+ dqb->dqb_btime = 0;
+ if (dqbuf.dqb_isoftlimit && id != 0 &&
+ dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
+ dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
+ dqb->dqb_itime = 0;
+ if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
+ dqb->dqb_isoftlimit > 0 &&
+ dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
+ dqb->dqb_itime = 0;
+ dqb->dqb_curinodes = dqbuf.dqb_curinodes;
+ dqb->dqb_curblocks = dqbuf.dqb_curblocks;
+ /*
+ * Write it back.
+ */
+ switch (qf->wordsize) {
+ case 32:
+ return (quota_write32(qf, dqb, id));
+ case 64:
+ return (quota_write64(qf, dqb, id));
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ /* not reached */
+}
+
+/*
+ * Convert a quota file from one format to another.
+ */
+int
+quota_convert(struct quotafile *qf, int wordsize)
+{
+ struct quotafile *newqf;
+ struct dqhdr64 dqh;
+ struct dqblk dqblk;
+ struct group *grp;
+ int serrno, maxid, id, fd;
+
+ /*
+ * Quotas must not be active and quotafile must be open
+ * for reading and writing.
+ */
+ if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) {
+ errno = EBADF;
+ return (-1);
+ }
+ if ((wordsize != 32 && wordsize != 64) ||
+ wordsize == qf->wordsize) {
+ errno = EINVAL;
+ return (-1);
+ }
+ maxid = quota_maxid(qf);
+ if ((newqf = calloc(1, sizeof(*qf))) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ *newqf = *qf;
+ snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname,
+ qf->wordsize);
+ if (rename(qf->qfname, newqf->qfname) < 0) {
+ free(newqf);
+ return (-1);
+ }
+ if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC,
+ 0)) < 0) {
+ serrno = errno;
+ goto error;
+ }
+ newqf->wordsize = wordsize;
+ if (wordsize == 64) {
+ memset(&dqh, 0, sizeof(dqh));
+ memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
+ dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
+ dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
+ dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
+ if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
+ serrno = errno;
+ goto error;
+ }
+ }
+ grp = getgrnam(QUOTAGROUP);
+ fchown(newqf->fd, 0, grp ? grp->gr_gid : 0);
+ fchmod(newqf->fd, 0640);
+ for (id = 0; id <= maxid; id++) {
+ if ((quota_read(qf, &dqblk, id)) < 0)
+ break;
+ switch (newqf->wordsize) {
+ case 32:
+ if ((quota_write32(newqf, &dqblk, id)) < 0)
+ break;
+ continue;
+ case 64:
+ if ((quota_write64(newqf, &dqblk, id)) < 0)
+ break;
+ continue;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ }
+ if (id < maxid) {
+ serrno = errno;
+ goto error;
+ }
+ /*
+ * Update the passed in quotafile to reference the new file
+ * of the converted format size.
+ */
+ fd = qf->fd;
+ qf->fd = newqf->fd;
+ newqf->fd = fd;
+ qf->wordsize = newqf->wordsize;
+ quota_close(newqf);
+ return (0);
+error:
+ /* put back the original file */
+ (void) rename(newqf->qfname, qf->qfname);
+ quota_close(newqf);
+ errno = serrno;
+ return (-1);
+}
diff --git a/lib/libutil/realhostname.3 b/lib/libutil/realhostname.3
new file mode 100644
index 0000000..28e2325
--- /dev/null
+++ b/lib/libutil/realhostname.3
@@ -0,0 +1,102 @@
+.\" Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 6, 1999
+.Dt REALHOSTNAME 3
+.Os
+.Sh NAME
+.Nm realhostname
+.Nd "convert an IP number to the real host name"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fn realhostname "char *host" "size_t hsize" "const struct in_addr *ip"
+.Sh DESCRIPTION
+The function
+.Fn realhostname
+converts
+.Ar ip
+to the corresponding host name.
+This is done by resolving
+.Ar ip
+to a host name and then ensuring that the host name resolves
+back to
+.Ar ip .
+.Pp
+.Ar host
+must point to a buffer of at least
+.Ar hsize
+bytes, and will always be written to by this function.
+.Pp
+If the name resolution does not work both ways or if the host name is longer
+than
+.Ar hsize
+bytes,
+.Xr inet_ntoa 3
+is used to convert
+.Ar ip
+to an ASCII form.
+.Pp
+If the string written to
+.Ar host
+is
+.Ar hsize
+bytes long,
+.Ar host
+will not be NUL terminated.
+.Sh RETURN VALUES
+The
+.Fn realhostname
+function will return one of the following constants which are defined in
+.In libutil.h :
+.Bl -tag -width XXX -offset XXX
+.It Li HOSTNAME_FOUND
+A valid host name was found.
+.It Li HOSTNAME_INCORRECTNAME
+A host name was found, but it did not resolve back to the passed
+.Ar ip .
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDADDR
+.Ar ip
+could not be resolved.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDNAME
+A host name was found, but it could not be resolved back to any ip number.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.El
+.Sh SEE ALSO
+.Xr gethostbyaddr 3 ,
+.Xr gethostbyname 3 ,
+.Xr inet_ntoa 3 ,
+.Xr realhostname_sa 3
diff --git a/lib/libutil/realhostname.c b/lib/libutil/realhostname.c
new file mode 100644
index 0000000..87ac1dd
--- /dev/null
+++ b/lib/libutil/realhostname.c
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libutil.h"
+
+struct sockinet {
+ u_char si_len;
+ u_char si_family;
+ u_short si_port;
+};
+
+int
+realhostname(char *host, size_t hsize, const struct in_addr *ip)
+{
+ char trimmed[MAXHOSTNAMELEN];
+ int result;
+ struct hostent *hp;
+
+ result = HOSTNAME_INVALIDADDR;
+ hp = gethostbyaddr((const char *)ip, sizeof(*ip), AF_INET);
+
+ if (hp != NULL) {
+ strlcpy(trimmed, hp->h_name, sizeof(trimmed));
+ trimdomain(trimmed, strlen(trimmed));
+ if (strlen(trimmed) <= hsize) {
+ char lookup[MAXHOSTNAMELEN];
+
+ strlcpy(lookup, hp->h_name, sizeof(lookup));
+ hp = gethostbyname(lookup);
+ if (hp == NULL)
+ result = HOSTNAME_INVALIDNAME;
+ else for (; ; hp->h_addr_list++) {
+ if (*hp->h_addr_list == NULL) {
+ result = HOSTNAME_INCORRECTNAME;
+ break;
+ }
+ if (!memcmp(*hp->h_addr_list, ip, sizeof(*ip))) {
+ strncpy(host, trimmed, hsize);
+ return HOSTNAME_FOUND;
+ }
+ }
+ }
+ }
+
+ strncpy(host, inet_ntoa(*ip), hsize);
+
+ return result;
+}
+
+/*
+ * struct sockaddr has very lax alignment requirements, since all its
+ * members are char or equivalent. This is a problem when trying to
+ * dereference a struct sockaddr_in6 * that was passed in as a struct
+ * sockaddr *. Although we know (or trust) that the passed-in struct was
+ * properly aligned, the compiler doesn't, and (rightly) complains. These
+ * macros perform the cast in a way that the compiler will accept.
+ */
+#define SOCKADDR_IN6(p) ((struct sockaddr_in6 *)(void *)(p))
+#define SOCKADDR_IN(p) ((struct sockaddr_in *)(void *)(p))
+#define SOCKINET(p) ((struct sockinet *)(void *)(p))
+
+int
+realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, int addrlen)
+{
+ int result, error;
+ char buf[NI_MAXHOST];
+#ifdef INET6
+ struct sockaddr_in lsin;
+#endif
+
+ result = HOSTNAME_INVALIDADDR;
+
+#ifdef INET6
+ /* IPv4 mapped IPv6 addr consideraton, specified in rfc2373. */
+ if (addr->sa_family == AF_INET6 &&
+ addrlen == sizeof(struct sockaddr_in6) &&
+ IN6_IS_ADDR_V4MAPPED(&SOCKADDR_IN6(addr)->sin6_addr)) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = SOCKADDR_IN6(addr);
+
+ memset(&lsin, 0, sizeof(lsin));
+ lsin.sin_len = sizeof(struct sockaddr_in);
+ lsin.sin_family = AF_INET;
+ lsin.sin_port = sin6->sin6_port;
+ memcpy(&lsin.sin_addr, &sin6->sin6_addr.s6_addr[12],
+ sizeof(struct in_addr));
+ addr = (struct sockaddr *)&lsin;
+ addrlen = lsin.sin_len;
+ }
+#endif
+
+ error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
+ NI_NAMEREQD);
+ if (error == 0) {
+ struct addrinfo hints, *res, *ores;
+ struct sockaddr *sa;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = addr->sa_family;
+ hints.ai_flags = AI_CANONNAME | AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+
+ error = getaddrinfo(buf, NULL, &hints, &res);
+ if (error) {
+ result = HOSTNAME_INVALIDNAME;
+ goto numeric;
+ }
+ for (ores = res; ; res = res->ai_next) {
+ if (res == NULL) {
+ freeaddrinfo(ores);
+ result = HOSTNAME_INCORRECTNAME;
+ goto numeric;
+ }
+ sa = res->ai_addr;
+ if (sa == NULL) {
+ freeaddrinfo(ores);
+ result = HOSTNAME_INCORRECTNAME;
+ goto numeric;
+ }
+ if (sa->sa_len == addrlen &&
+ sa->sa_family == addr->sa_family) {
+ SOCKINET(sa)->si_port = SOCKINET(addr)->si_port;
+#ifdef INET6
+ /*
+ * XXX: sin6_socpe_id may not been
+ * filled by DNS
+ */
+ if (sa->sa_family == AF_INET6 &&
+ SOCKADDR_IN6(sa)->sin6_scope_id == 0)
+ SOCKADDR_IN6(sa)->sin6_scope_id =
+ SOCKADDR_IN6(addr)->sin6_scope_id;
+#endif
+ if (!memcmp(sa, addr, sa->sa_len)) {
+ result = HOSTNAME_FOUND;
+ if (ores->ai_canonname == NULL) {
+ freeaddrinfo(ores);
+ goto numeric;
+ }
+ strlcpy(buf, ores->ai_canonname,
+ sizeof(buf));
+ trimdomain(buf, hsize);
+ if (strlen(buf) > hsize &&
+ addr->sa_family == AF_INET) {
+ freeaddrinfo(ores);
+ goto numeric;
+ }
+ strncpy(host, buf, hsize);
+ break;
+ }
+ }
+ }
+ freeaddrinfo(ores);
+ } else {
+ numeric:
+ if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
+ NI_NUMERICHOST) == 0)
+ strncpy(host, buf, hsize);
+ }
+
+ return result;
+}
+
+
diff --git a/lib/libutil/realhostname_sa.3 b/lib/libutil/realhostname_sa.3
new file mode 100644
index 0000000..cd76b07
--- /dev/null
+++ b/lib/libutil/realhostname_sa.3
@@ -0,0 +1,132 @@
+.\" Copyright (C) 1995, 1996, 1997, 1998, 1999, and 2000 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 11, 2000
+.Dt REALHOSTNAME_SA 3
+.Os
+.Sh NAME
+.Nm realhostname_sa
+.Nd "convert a"
+.Vt "struct sockaddr"
+to the real host name
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In netinet/in.h
+.In libutil.h
+.Ft int
+.Fn realhostname_sa "char *host" "size_t hsize" "struct sockaddr *addr" "int addrlen"
+.Sh DESCRIPTION
+The function
+.Fn realhostname_sa
+converts
+.Ar addr
+to the corresponding host name.
+This is done by resolving
+.Ar addr
+to a host name and then ensuring that the host name resolves
+back to
+.Ar addr .
+.Pp
+.Ar host
+must point to a buffer of at least
+.Ar hsize
+bytes, and will always be written to by this function.
+.Pp
+If the name resolution does not work both ways or if the host name is longer
+than
+.Ar hsize
+bytes,
+.Xr getnameinfo 3
+with NI_NUMERICHOST specified, is used to convert
+.Ar addr
+to an ASCII form.
+.Pp
+If the string written to
+.Ar host
+is
+.Ar hsize
+bytes long,
+.Ar host
+will not be NUL terminated.
+.Sh RETURN VALUES
+The
+.Fn realhostname_sa
+function will return one of the following constants which are defined in
+.In libutil.h :
+.Bl -tag -width XXX -offset XXX
+.It Li HOSTNAME_FOUND
+A valid host name was found.
+.It Li HOSTNAME_INCORRECTNAME
+A host name was found, but it did not resolve back to the passed
+.Ar ip .
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDADDR
+.Ar ip
+could not be resolved.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDNAME
+A host name was found, but it could not be resolved back to any ip number.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.El
+.Sh SEE ALSO
+.Xr getaddrinfo 3 ,
+.Xr getnameinfo 3 ,
+.Xr realhostname 3
diff --git a/lib/libutil/stub.c b/lib/libutil/stub.c
new file mode 100644
index 0000000..ec78a35
--- /dev/null
+++ b/lib/libutil/stub.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2000 Brian Fundakowski Feldman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * Stub out what's in -lcrypt.
+ */
+
+#pragma weak crypt_set_format
+/* ARGSUSED */
+int
+crypt_set_format(const char *f __unused) {
+
+ if (getenv("CRYPT_DEBUG") != NULL)
+ fprintf(stderr, "crypt_set_format: eek, stub called!\n");
+ return (0);
+}
diff --git a/lib/libutil/tests/Makefile b/lib/libutil/tests/Makefile
new file mode 100644
index 0000000..81b2a49
--- /dev/null
+++ b/lib/libutil/tests/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+TAP_TESTS_C+= flopen_test
+TAP_TESTS_C+= grp_test
+TAP_TESTS_C+= humanize_number_test
+.if ${MACHINE_CPUARCH} != "aarch64" # PR202304: pidfile_test hangs on arm64
+TAP_TESTS_C+= pidfile_test
+.endif
+TAP_TESTS_C+= trimdomain_test
+TAP_TESTS_C+= trimdomain-nodomain_test
+
+LIBADD+= util
+
+.include <bsd.test.mk>
diff --git a/lib/libutil/tests/flopen_test.c b/lib/libutil/tests/flopen_test.c
new file mode 100644
index 0000000..0471584
--- /dev/null
+++ b/lib/libutil/tests/flopen_test.c
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libutil.h>
+
+/*
+ * Test that flopen() can create a file.
+ */
+const char *
+test_flopen_create(void)
+{
+ const char *fn = "test_flopen_create";
+ const char *result = NULL;
+ int fd;
+
+ unlink(fn);
+ fd = flopen(fn, O_RDWR|O_CREAT, 0640);
+ if (fd < 0) {
+ result = strerror(errno);
+ } else {
+ close(fd);
+ }
+ unlink(fn);
+ return (result);
+}
+
+/*
+ * Test that flopen() can open an existing file.
+ */
+const char *
+test_flopen_open(void)
+{
+ const char *fn = "test_flopen_open";
+ const char *result = NULL;
+ int fd;
+
+ fd = open(fn, O_RDWR|O_CREAT, 0640);
+ if (fd < 0) {
+ result = strerror(errno);
+ } else {
+ close(fd);
+ fd = flopen(fn, O_RDWR);
+ if (fd < 0) {
+ result = strerror(errno);
+ } else {
+ close(fd);
+ }
+ }
+ unlink(fn);
+ return (result);
+}
+
+/*
+ * Test that flopen() can lock against itself
+ */
+const char *
+test_flopen_lock_self(void)
+{
+ const char *fn = "test_flopen_lock_self";
+ const char *result = NULL;
+ int fd1, fd2;
+
+ unlink(fn);
+ fd1 = flopen(fn, O_RDWR|O_CREAT, 0640);
+ if (fd1 < 0) {
+ result = strerror(errno);
+ } else {
+ fd2 = flopen(fn, O_RDWR|O_NONBLOCK);
+ if (fd2 >= 0) {
+ result = "second open succeeded";
+ close(fd2);
+ }
+ close(fd1);
+ }
+ unlink(fn);
+ return (result);
+}
+
+/*
+ * Test that flopen() can lock against other processes
+ */
+const char *
+test_flopen_lock_other(void)
+{
+ const char *fn = "test_flopen_lock_other";
+ const char *result = NULL;
+ volatile int fd1, fd2;
+
+ unlink(fn);
+ fd1 = flopen(fn, O_RDWR|O_CREAT, 0640);
+ if (fd1 < 0) {
+ result = strerror(errno);
+ } else {
+ fd2 = -42;
+ if (vfork() == 0) {
+ fd2 = flopen(fn, O_RDWR|O_NONBLOCK);
+ close(fd2);
+ _exit(0);
+ }
+ if (fd2 == -42)
+ result = "vfork() doesn't work as expected";
+ if (fd2 >= 0)
+ result = "second open succeeded";
+ close(fd1);
+ }
+ unlink(fn);
+ return (result);
+}
+
+/*
+ * Test that child processes inherit the lock
+ */
+const char *
+test_flopen_lock_child(void)
+{
+ const char *fn = "test_flopen_lock_child";
+ const char *result = NULL;
+ pid_t pid;
+ volatile int fd1, fd2;
+
+ unlink(fn);
+ fd1 = flopen(fn, O_RDWR|O_CREAT, 0640);
+ if (fd1 < 0) {
+ result = strerror(errno);
+ } else {
+ pid = fork();
+ if (pid == -1) {
+ result = strerror(errno);
+ } else if (pid == 0) {
+ select(0, 0, 0, 0, 0);
+ _exit(0);
+ }
+ close(fd1);
+ if ((fd2 = flopen(fn, O_RDWR|O_NONBLOCK)) != -1) {
+ result = "second open succeeded";
+ close(fd2);
+ }
+ kill(pid, SIGINT);
+ }
+ unlink(fn);
+ return (result);
+}
+
+static struct test {
+ const char *name;
+ const char *(*func)(void);
+} t[] = {
+ { "flopen_create", test_flopen_create },
+ { "flopen_open", test_flopen_open },
+ { "flopen_lock_self", test_flopen_lock_self },
+ { "flopen_lock_other", test_flopen_lock_other },
+ { "flopen_lock_child", test_flopen_lock_child },
+};
+
+int
+main(void)
+{
+ const char *result;
+ int i, nt;
+
+ nt = sizeof(t) / sizeof(*t);
+ printf("1..%d\n", nt);
+ for (i = 0; i < nt; ++i) {
+ if ((result = t[i].func()) != NULL)
+ printf("not ok %d - %s # %s\n", i + 1,
+ t[i].name, result);
+ else
+ printf("ok %d - %s\n", i + 1,
+ t[i].name);
+ }
+ exit(0);
+}
diff --git a/lib/libutil/tests/grp_test.c b/lib/libutil/tests/grp_test.c
new file mode 100644
index 0000000..ae7ce73
--- /dev/null
+++ b/lib/libutil/tests/grp_test.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libutil.h>
+
+
+/*
+ * Static values for building and testing an artificial group.
+ */
+static char grpName[] = "groupName";
+static char grpPasswd[] = "groupPwd";
+static gid_t grpGID = 1234;
+static char *grpMems[] = { "mem1", "mem2", "mem3", NULL };
+static const char *origStrGrp = "groupName:groupPwd:1234:mem1,mem2,mem3";
+
+
+/*
+ * Build a group to test against without depending on a real group to be found
+ * within /etc/group.
+ */
+static void
+build_grp(struct group *grp)
+{
+ grp->gr_name = grpName;
+ grp->gr_passwd = grpPasswd;
+ grp->gr_gid = grpGID;
+ grp->gr_mem = grpMems;
+
+ return;
+}
+
+
+int
+main(void)
+{
+ char *strGrp;
+ int testNdx;
+ struct group *dupGrp;
+ struct group *scanGrp;
+ struct group origGrp;
+
+ /* Setup. */
+ printf("1..4\n");
+ testNdx = 0;
+
+ /* Manually build a group using static values. */
+ build_grp(&origGrp);
+
+ /* Copy the group. */
+ testNdx++;
+ if ((dupGrp = gr_dup(&origGrp)) == NULL)
+ printf("not ");
+ printf("ok %d - %s\n", testNdx, "gr_dup");
+
+ /* Compare the original and duplicate groups. */
+ testNdx++;
+ if (! gr_equal(&origGrp, dupGrp))
+ printf("not ");
+ printf("ok %d - %s\n", testNdx, "gr_equal");
+
+ /* Create group string from the duplicate group structure. */
+ testNdx++;
+ strGrp = gr_make(dupGrp);
+ if (strcmp(strGrp, origStrGrp) != 0)
+ printf("not ");
+ printf("ok %d - %s\n", testNdx, "gr_make");
+
+ /*
+ * Create group structure from string and compare it to the original
+ * group structure.
+ */
+ testNdx++;
+ if ((scanGrp = gr_scan(strGrp)) == NULL || ! gr_equal(&origGrp,
+ scanGrp))
+ printf("not ");
+ printf("ok %d - %s\n", testNdx, "gr_scan");
+
+ /* Clean up. */
+ free(scanGrp);
+ free(strGrp);
+ free(dupGrp);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/lib/libutil/tests/humanize_number_test.c b/lib/libutil/tests/humanize_number_test.c
new file mode 100644
index 0000000..2d66204
--- /dev/null
+++ b/lib/libutil/tests/humanize_number_test.c
@@ -0,0 +1,598 @@
+/*-
+ * Copyright 2012 Clifton Royston
+ * Copyright 2013 John-Mark Gurney
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/param.h>
+#include <inttypes.h>
+#include <libutil.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAX_STR_FLAGS_RESULT 80
+#define MAX_INT_STR_DIGITS 12
+
+static const int64_t halfExabyte = (int64_t)500*1000*1000*1000*1000*1000L;
+
+static struct {
+ int retval;
+ const char *res;
+ int64_t num;
+ int flags;
+ int scale;
+} test_args[] = {
+ /* tests 0-13 test 1000 suffixes */
+ { 2, "0 ", (int64_t)0L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 k", (int64_t)500L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 G", (int64_t)500*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 T", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 P", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 2, "1 ", (int64_t)1L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "2 k", (int64_t)1500L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "2 M", (int64_t)1500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "2 G", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "2 T", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "2 P", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "2 E", (int64_t)1500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+
+ /* tests 14-27 test 1024 suffixes */
+ { 2, "0 ", (int64_t)0L, 0, HN_AUTOSCALE },
+ { 3, "1 K", (int64_t)512L, 0, HN_AUTOSCALE },
+ { 3, "1 M", (int64_t)512*1024L, 0, HN_AUTOSCALE },
+ { 3, "1 G", (int64_t)512*1024*1024L, 0, HN_AUTOSCALE },
+ { 3, "1 T", (int64_t)512*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 3, "1 P", (int64_t)512*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 3, "1 E", (int64_t)512*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 2, "1 ", (int64_t)1L, 0, HN_AUTOSCALE },
+ { 3, "2 K", (int64_t)1536L, 0, HN_AUTOSCALE },
+ { 3, "2 M", (int64_t)1536*1024L, 0, HN_AUTOSCALE },
+ { 3, "2 G", (int64_t)1536*1024*1024L, 0, HN_AUTOSCALE },
+ { 3, "2 T", (int64_t)1536*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 3, "2 P", (int64_t)1536*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 3, "2 E", (int64_t)1536*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+
+ /* tests 28-37 test rounding */
+ { 3, "0 M", (int64_t)500*1000L-1, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "1 M", (int64_t)1000*1000L + 500*1000L-1, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "2 M", (int64_t)1000*1000L + 500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 3, "0 K", (int64_t)512L-1, 0, HN_AUTOSCALE },
+ { 3, "1 K", (int64_t)512L, 0, HN_AUTOSCALE },
+ { 3, "0 M", (int64_t)512*1024L-1, 0, HN_AUTOSCALE },
+ { 3, "1 M", (int64_t)512*1024L, 0, HN_AUTOSCALE },
+ { 3, "1 M", (int64_t)1024*1024L + 512*1024L-1, 0, HN_AUTOSCALE },
+ { 3, "2 M", (int64_t)1024*1024L + 512*1024L, 0, HN_AUTOSCALE },
+
+ /* tests 38-61 test specific scale factors with 1000 divisor */
+ { 3, "0 k", (int64_t)0L, HN_DIVISOR_1000, 1 },
+ { 3, "1 k", (int64_t)500L, HN_DIVISOR_1000, 1 },
+ { 3, "0 M", (int64_t)500L, HN_DIVISOR_1000, 2 },
+ { 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, 2 },
+ { 3, "0 G", (int64_t)500*1000L, HN_DIVISOR_1000, 3 },
+ { 3, "1 G", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 3 },
+ { 3, "0 T", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 4 },
+ { 3, "1 T", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, 4 },
+ { 3, "0 P", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, 5 },
+ { 3, "1 P", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, 5 },
+ { 3, "0 E", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, 6 },
+ { 3, "1 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6 },
+ { 3, "0 k", (int64_t)1L, HN_DIVISOR_1000, 1 },
+ { 3, "2 k", (int64_t)1500L, HN_DIVISOR_1000, 1 },
+ { 3, "0 M", (int64_t)1500L, HN_DIVISOR_1000, 2 },
+ { 3, "2 M", (int64_t)1500*1000L, HN_DIVISOR_1000, 2 },
+ { 3, "0 G", (int64_t)1500*1000L, HN_DIVISOR_1000, 3 },
+ { 3, "2 G", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 3 },
+ { 3, "0 T", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 4 },
+ { 3, "2 T", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, 4 },
+ { 3, "0 P", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, 5 },
+ { 3, "2 P", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, 5 },
+ { 3, "0 E", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, 6 },
+ { 3, "2 E", (int64_t)1500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6 },
+
+ /* tests 62-85 test specific scale factors with 1024 divisor */
+ { 3, "0 K", (int64_t)0L, 0, 1 },
+ { 3, "1 K", (int64_t)512L, 0, 1 },
+ { 3, "0 M", (int64_t)512L, 0, 2 },
+ { 3, "1 M", (int64_t)512*1024L, 0, 2 },
+ { 3, "0 G", (int64_t)512*1024L, 0, 3 },
+ { 3, "1 G", (int64_t)512*1024*1024L, 0, 3 },
+ { 3, "0 T", (int64_t)512*1024*1024L, 0, 4 },
+ { 3, "1 T", (int64_t)512*1024*1024*1024L, 0, 4 },
+ { 3, "0 P", (int64_t)512*1024*1024*1024L, 0, 5 },
+ { 3, "1 P", (int64_t)512*1024*1024*1024*1024L, 0, 5 },
+ { 3, "0 E", (int64_t)512*1024*1024*1024*1024L, 0, 6 },
+ { 3, "1 E", (int64_t)512*1024*1024*1024*1024*1024L, 0, 6 },
+ { 3, "0 K", (int64_t)1L, 0, 1 },
+ { 3, "2 K", (int64_t)1536L, 0, 1 },
+ { 3, "0 M", (int64_t)1536L, 0, 2 },
+ { 3, "2 M", (int64_t)1536*1024L, 0, 2 },
+ { 3, "0 G", (int64_t)1536*1024L, 0, 3 },
+ { 3, "2 G", (int64_t)1536*1024*1024L, 0, 3 },
+ { 3, "0 T", (int64_t)1536*1024*1024L, 0, 4 },
+ { 3, "2 T", (int64_t)1536*1024*1024*1024L, 0, 4 },
+ { 3, "0 P", (int64_t)1536*1024*1024*1024L, 0, 5 },
+ { 3, "2 P", (int64_t)1536*1024*1024*1024*1024L, 0, 5 },
+ { 3, "0 E", (int64_t)1536*1024*1024*1024*1024L, 0, 6 },
+ { 3, "2 E", (int64_t)1536*1024*1024*1024*1024*1024L, 0, 6 },
+
+ /* tests 86-99 test invalid specific scale values of < 0 or >= 7 with
+ and without HN_DIVISOR_1000 set */
+ /* all should return errors with new code; with old, the latter 3
+ are instead processed as if having AUTOSCALE and/or GETSCALE set */
+ { -1, "", (int64_t)1L, 0, 7 },
+ { -1, "", (int64_t)1L, HN_DIVISOR_1000, 7 },
+ { -1, "", (int64_t)1L, 0, 1000 },
+ { -1, "", (int64_t)1L, HN_DIVISOR_1000, 1000 },
+ { -1, "", (int64_t)0L, 0, 1000*1000 },
+ { -1, "", (int64_t)0L, HN_DIVISOR_1000, 1000*1000 },
+ { -1, "", (int64_t)0L, 0, INT_MAX },
+ { -1, "", (int64_t)0L, HN_DIVISOR_1000, INT_MAX },
+
+ /* Negative scale values are not handled well
+ by the existing library routine - should report as error */
+ /* all should return errors with new code, fail assertion with old */
+
+ { -1, "", (int64_t)1L, 0, -1 },
+ { -1, "", (int64_t)1L, HN_DIVISOR_1000, -1 },
+ { -1, "", (int64_t)1L, 0, -1000 },
+ { -1, "", (int64_t)1L, HN_DIVISOR_1000, -1000 },
+
+ /* __INT_MIN doesn't print properly, skipped. */
+
+ { -1, "", (int64_t)1L, 0, -__INT_MAX },
+ { -1, "", (int64_t)1L, HN_DIVISOR_1000, -__INT_MAX },
+
+
+ /* tests for scale == 0, without autoscale */
+ /* tests 100-114 test scale 0 with 1000 divisor - print first N digits */
+ { 2, "0 ", (int64_t)0L, HN_DIVISOR_1000, 0 },
+ { 2, "1 ", (int64_t)1L, HN_DIVISOR_1000, 0 },
+ { 3, "10 ", (int64_t)10L, HN_DIVISOR_1000, 0 },
+ { 3, "0 M", (int64_t)150L, HN_DIVISOR_1000, HN_NOSPACE },
+ { 3, "0 M", (int64_t)500L, HN_DIVISOR_1000, HN_NOSPACE },
+ { 3, "0 M", (int64_t)999L, HN_DIVISOR_1000, HN_NOSPACE },
+ { 4, "150", (int64_t)150L, HN_DIVISOR_1000, 0 },
+ { 4, "500", (int64_t)500L, HN_DIVISOR_1000, 0 },
+ { 4, "999", (int64_t)999L, HN_DIVISOR_1000, 0 },
+ { 5, "100", (int64_t)1000L, HN_DIVISOR_1000, 0 },
+ { 5, "150", (int64_t)1500L, HN_DIVISOR_1000, 0 },
+ { 7, "500", (int64_t)500*1000L, HN_DIVISOR_1000, 0 },
+ { 8, "150", (int64_t)1500*1000L, HN_DIVISOR_1000, 0 },
+ { 10, "500", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 0 },
+ { 11, "150", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 0 },
+
+ /* tests 115-126 test scale 0 with 1024 divisor - print first N digits */
+ { 2, "0 ", (int64_t)0L, 0, 0 },
+ { 2, "1 ", (int64_t)1L, 0, 0 },
+ { 3, "10 ", (int64_t)10L, 0, 0 },
+ { 4, "150", (int64_t)150L, 0, 0 },
+ { 4, "500", (int64_t)500L, 0, 0 },
+ { 4, "999", (int64_t)999L, 0, 0 },
+ { 5, "100", (int64_t)1000L, 0, 0 },
+ { 5, "150", (int64_t)1500L, 0, 0 },
+ { 7, "500", (int64_t)500*1000L, 0, 0 },
+ { 8, "150", (int64_t)1500*1000L, 0, 0 },
+ { 10, "500", (int64_t)500*1000*1000L, 0, 0 },
+ { 11, "150", (int64_t)1500*1000*1000L, 0, 0 },
+
+ /* Test boundary cases for very large positive/negative number formatting */
+ /* Explicit scale, divisor 1024 */
+
+ /* XXX = requires length 5 (buflen 6) for some cases*/
+ /* KLUDGE - test loop below will bump length 5 up to 5 */
+ { 3, "8 E", INT64_MAX, 0, 6 },
+ { 4, "-8 E", -INT64_MAX, 0, 6 },
+ { 3, "0 E", (int64_t)92*1024*1024*1024*1024*1024L, 0, 6 },
+ { 3, "0 E", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 6 },
+ { 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, 0, 6 },
+ { 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 6 },
+ { 3, "0 E", (int64_t)81*1024*1024*1024*1024*1024L, 0, 6 },
+ { 3, "0 E", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 6 },
+ { 4, "92 P", (int64_t)92*1024*1024*1024*1024*1024L, 0, 5 },
+ { 5, "-92 P", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 5 },
+ { 4, "82 P", (int64_t)82*1024*1024*1024*1024*1024L, 0, 5 },
+ { 5, "-82 P", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 5 },
+ { 4, "81 P", (int64_t)81*1024*1024*1024*1024*1024L, 0, 5 },
+ { 5, "-81 P", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 5 },
+
+ /* Explicit scale, divisor 1000 */
+ { 3, "9 E", INT64_MAX, HN_DIVISOR_1000, 6 },
+ { 4, "-9 E", -INT64_MAX, HN_DIVISOR_1000, 6 },
+ { 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6 },
+ { 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6 },
+ { 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6 },
+ { 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6 },
+ { 4, "92 P", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5 },
+ { 5, "-92 P", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5 },
+ { 4, "91 P", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5 },
+ { 5, "-91 P", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5 },
+
+ /* Autoscale, divisor 1024 */
+ { 3, "8 E", INT64_MAX, 0, HN_AUTOSCALE },
+ { 4, "-8 E", -INT64_MAX, 0, HN_AUTOSCALE },
+ { 4, "92 P", (int64_t)92*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 5, "-92 P", -(int64_t)92*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 4, "82 P", (int64_t)82*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 5, "-82 P", -(int64_t)82*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 4, "81 P", (int64_t)81*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ { 5, "-81 P", -(int64_t)81*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE },
+ /* Autoscale, divisor 1000 */
+ { 3, "9 E", INT64_MAX, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 4, "-9 E", -INT64_MAX, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 4, "92 P", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "-92 P", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 4, "91 P", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "-91 P", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE },
+
+ /* 0 scale, divisor 1024 */
+ { 12, "skdj", INT64_MAX, 0, 0 },
+ { 21, "-9223", -INT64_MAX, 0, 0 },
+ { 19, "10358", (int64_t)92*1024*1024*1024*1024*1024L, 0, 0 },
+ { 20, "-1035", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 0 },
+ { 18, "92323", (int64_t)82*1024*1024*1024*1024*1024L, 0, 0 },
+ { 19, "-9232", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 0 },
+ { 18, "91197", (int64_t)81*1024*1024*1024*1024*1024L, 0, 0 },
+ { 19, "-9119", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 0 },
+
+ /* 0 scale, divisor 1000 */
+ /* XXX - why does this fail? */
+ { -1, "", INT64_MAX, HN_DIVISOR_1000, 0 },
+ { 21, "-9223", -INT64_MAX, HN_DIVISOR_1000, 0 },
+ { 19, "10358", (int64_t)92*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0 },
+ { 20, "-1035", -(int64_t)92*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0 },
+ { 18, "92323", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0 },
+ { 19, "-9232", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0 },
+ /* Expected to pass */
+ { 18, "91197", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0 },
+ { 19, "-9119", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0 },
+
+
+
+ /* Need to implement tests for GETSCALE */
+/* { ?, "", (int64_t)0L, HN_DIVISOR_1000, HN_GETSCALE },
+ ...
+*/
+ /* Tests for HN_DECIMAL */
+ /* Positive, Autoscale */
+ { 5, "500 k", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "994 k", (int64_t)994*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "995 k", (int64_t)995*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "999 k", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "1.0 M", (int64_t)1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "1.5 M", (int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "1.9 M", (int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "2.0 M", (int64_t)1950*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "9.9 M", (int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 4, "10 M", (int64_t)9950*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "500 M", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "994 M", (int64_t)994*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "995 M", (int64_t)995*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "999 M", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+
+ { 5, "500 K", (int64_t)500*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "994 K", (int64_t)994*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "995 K", (int64_t)995*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "999 K", (int64_t)999*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "1.0 M", (int64_t)1000*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "1.0 M", (int64_t)1018*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "1.0 M", (int64_t)1019*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "1.5 M", (int64_t)1536*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "1.9 M", (int64_t)1996*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "2.0 M", (int64_t)1997*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "2.0 M", (int64_t)2047*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "2.0 M", (int64_t)2048*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "2.0 M", (int64_t)2099*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "2.1 M", (int64_t)2100*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "9.9 M", (int64_t)10188*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ /* XXX - shouldn't the following two be "10. M"? */
+ { 4, "10 M", (int64_t)10189*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 4, "10 M", (int64_t)10240*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "500 M", (int64_t)500*1024*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "994 M", (int64_t)994*1024*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "995 M", (int64_t)995*1024*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "999 M", (int64_t)999*1024*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "1.0 G", (int64_t)1000*1024*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "1.0 G", (int64_t)1023*1024*1024L, HN_DECIMAL, HN_AUTOSCALE },
+
+ /* Negative, Autoscale - should pass */
+ { 6, "-1.5 ", -(int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 6, "-1.9 ", -(int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 6, "-9.9 ", -(int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+
+ { 6, "-1.5 ", -(int64_t)1536*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 6, "-1.9 ", -(int64_t)1949*1024L, HN_DECIMAL, HN_AUTOSCALE },
+ { 6, "-9.7 ", -(int64_t)9949*1024L, HN_DECIMAL, HN_AUTOSCALE },
+
+ /* Positive/negative, at maximum scale */
+ { 5, "500 P", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "1.9 E", (int64_t)1949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "8.9 E", (int64_t)8949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 5, "9.2 E", INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ /* Negatives work with latest rev only: */
+ { 6, "-9.2 ", -INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+ { 6, "-8.9 ", -(int64_t)8949*1000*1000*1000*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE },
+
+ { 5, "8.0 E", INT64_MAX, HN_DECIMAL, HN_AUTOSCALE },
+ { 5, "7.9 E", INT64_MAX-(int64_t)100*1024*1024*1024*1024*1024LL, HN_DECIMAL, HN_AUTOSCALE },
+ { 6, "-8.0 ", -INT64_MAX, HN_DECIMAL, HN_AUTOSCALE },
+ { 6, "-7.9 ", -INT64_MAX+(int64_t)100*1024*1024*1024*1024*1024LL, HN_DECIMAL, HN_AUTOSCALE },
+
+ /* Positive, Fixed scales */
+ { 5, "500 k", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1 },
+ { 5, "0.5 M", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "949 k", (int64_t)949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1 },
+ { 5, "0.9 M", (int64_t)949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "950 k", (int64_t)950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1 },
+ { 5, "1.0 M", (int64_t)950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "999 k", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1 },
+ { 5, "1.0 M", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "1.5 M", (int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "1.9 M", (int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "2.0 M", (int64_t)1950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "9.9 M", (int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 4, "10 M", (int64_t)9950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "500 M", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "0.5 G", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 3 },
+ { 5, "999 M", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2 },
+ { 5, "1.0 G", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 3 },
+ /* Positive/negative, at maximum scale */
+ { 5, "500 P", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 5 },
+ { 5, "1.0 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6 },
+ { 5, "1.9 E", (int64_t)1949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6 },
+ { 5, "8.9 E", (int64_t)8949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6 },
+ { 5, "9.2 E", INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, 6 },
+
+ /* HN_DECIMAL + binary + fixed scale cases not completed */
+ { 5, "512 K", (int64_t)512*1024L, HN_DECIMAL, 1 },
+ { 5, "0.5 M", (int64_t)512*1024L, HN_DECIMAL, 2 },
+
+ /* Negative, Fixed scales */
+ /* Not yet added, but should work with latest rev */
+
+};
+
+
+/* Command line options usage */
+static void
+usage(char * progname) {
+ printf("%s: tests libutil humanize_number function\n", progname);
+ printf("Usage: %s [-nE] [-l num] [-v]\n\n", progname);
+ printf("Options:\n");
+ printf("\t-l num\tSet max length for result; buflen = num + 1\n");
+ printf("\t\t (NOTE: does not change expected result strings.)\n");
+ printf("\t-n\tInclude negative scale tests, which cause older libutil\n");
+ printf("\t\t version of function to coredump with assertion failure\n");
+ printf("\t-E\tInclude numbers > 1/2 Exa[byte] which currently fail\n");
+ printf("\t-v\tVerbose - always print summary results\n");
+ printf("\t-h, -?\tShow options\n");
+}
+
+/* Parse command line options */
+static void
+read_options(int argc, char * const argv[], size_t *bufLength,
+ int *includeNegativeScale, int *includeExabytes, int *verbose) {
+ int ch;
+ size_t temp;
+
+ while ((ch = getopt(argc, argv, "nEh?vl:")) != -1) {
+ switch (ch) {
+ default:
+ usage(argv[0]);
+ exit(1);
+ break; /* UNREACHABLE */
+ case 'h' :
+ case '?' :
+ usage(argv[0]);
+ exit(0);
+ break; /* UNREACHABLE */
+ case 'l' :
+ sscanf(optarg, "%zu", &temp);
+ *bufLength = temp + 1;
+ break;
+ case 'n' :
+ *includeNegativeScale = 1;
+ break;
+ case 'E' :
+ *includeExabytes = 1;
+ break;
+ case 'v' :
+ *verbose = 1;
+ break;
+ }
+ }
+}
+
+static struct {
+ int value;
+ const char *name;
+ } flags[] = {
+ { HN_AUTOSCALE, "HN_AUTOSCALE" },
+ { HN_GETSCALE, "HN_GETSCALE" },
+ { HN_DIVISOR_1000, "HN_DIVISOR_1000"},
+ { HN_B, "HN_B"},
+ { HN_DECIMAL, "HN_DECIMAL"},
+};
+
+static const char *separator = "|";
+
+/* Format flags parameter for meaningful display */
+static char *
+str_flags(int hn_flags, char *noFlags) {
+ size_t i;
+ char * result;
+
+ result = malloc(MAX_STR_FLAGS_RESULT);
+ result[0] = '\0';
+
+ for (i = 0; i < sizeof flags / sizeof *flags; i++) {
+ if (hn_flags & flags[i].value) {
+ if (*result != 0)
+ strlcat(result, separator,
+ MAX_STR_FLAGS_RESULT);
+ strlcat(result, flags[i].name, MAX_STR_FLAGS_RESULT);
+ }
+ }
+
+ if (strlen(result) == 0)
+ strlcat(result, noFlags, MAX_STR_FLAGS_RESULT);
+ return result;
+}
+
+
+/* Format scale parameter for meaningful display */
+static char *
+str_scale(int scale) {
+ char *result;
+
+ if (scale == HN_AUTOSCALE || scale == HN_GETSCALE)
+ return str_flags(scale, "");
+
+ result = malloc(MAX_INT_STR_DIGITS);
+ result[0] = '\0';
+ snprintf(result, MAX_INT_STR_DIGITS, "%d", scale);
+ return result;
+}
+
+static void
+testskipped(size_t i)
+{
+
+ printf("ok %zu # skip - not turned on\n", i);
+}
+
+int
+main(int argc, char * const argv[])
+{
+ char *buf;
+ char *flag_str, *scale_str;
+ size_t buflen, errcnt, i, skipped, tested;
+ int r;
+ int includeNegScale;
+ int includeExabyteTests;
+ int verbose;
+
+ buflen = 4;
+ includeNegScale = 0;
+ includeExabyteTests = 0;
+ verbose = 0;
+
+ read_options(argc, argv, &buflen, &includeNegScale,
+ &includeExabyteTests, &verbose);
+
+ buf = malloc(buflen);
+ errcnt = 0;
+ tested = 0;
+ skipped = 0;
+
+ if (buflen != 4)
+ printf("Warning: buffer size %zu != 4, expect some results to differ.\n", buflen);
+
+ printf("1..%zu\n", nitems(test_args));
+ for (i = 0; i < nitems(test_args); i++) {
+ /* KLUDGE */
+ if (test_args[i].num == INT64_MAX && buflen == 4) {
+ /* Start final tests which require buffer of 6 */
+ free(buf);
+ buflen = 6;
+ buf = malloc(buflen);
+ if (verbose)
+ printf("Buffer length increased to %zu\n",
+ buflen);
+ }
+
+ if (test_args[i].scale < 0 && ! includeNegScale) {
+ skipped++;
+ testskipped(i + 1);
+ continue;
+ }
+ if (test_args[i].num >= halfExabyte && ! includeExabyteTests) {
+ skipped++;
+ testskipped(i + 1);
+ continue;
+ }
+
+ r = humanize_number(buf, buflen, test_args[i].num, "",
+ test_args[i].scale, test_args[i].flags);
+ flag_str = str_flags(test_args[i].flags, "[no flags]");
+ scale_str = str_scale(test_args[i].scale);
+
+ if (r != test_args[i].retval) {
+ if (verbose)
+ printf("wrong return value on index %zu, "
+ "buflen: %zu, got: %d + \"%s\", "
+ "expected %d + \"%s\"; num = %jd, "
+ "scale = %s, flags= %s.\n",
+ i, buflen, r, buf, test_args[i].retval,
+ test_args[i].res,
+ (intmax_t)test_args[i].num,
+ scale_str, flag_str);
+ else
+ printf("not ok %zu # return %d != %d\n",
+ i + 1, r, test_args[i].retval);
+ errcnt++;
+ } else if (strcmp(buf, test_args[i].res) != 0) {
+ if (verbose)
+ printf("result mismatch on index %zu, got: "
+ "\"%s\", expected \"%s\"; num = %jd, "
+ "scale = %s, flags= %s.\n",
+ i, buf, test_args[i].res,
+ (intmax_t)test_args[i].num,
+ scale_str, flag_str);
+ else
+ printf("not ok %zu # buf \"%s\" != \"%s\"\n",
+ i + 1, buf, test_args[i].res);
+ errcnt++;
+ } else {
+ if (verbose)
+ printf("successful result on index %zu, "
+ "returned %d, got: \"%s\"; num = %jd, "
+ "scale = %s, flags= %s.\n",
+ i, r, buf,
+ (intmax_t)test_args[i].num,
+ scale_str, flag_str);
+ else
+ printf("ok %zu\n", i + 1);
+ }
+ tested++;
+ }
+
+ if (verbose)
+ printf("total errors: %zu/%zu tests, %zu skipped\n", errcnt,
+ tested, skipped);
+
+ if (errcnt)
+ return 1;
+
+ return 0;
+}
diff --git a/lib/libutil/tests/pidfile_test.c b/lib/libutil/tests/pidfile_test.c
new file mode 100644
index 0000000..0b70bc8
--- /dev/null
+++ b/lib/libutil/tests/pidfile_test.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libutil.h>
+
+/*
+ * We need a signal handler so kill(2) will interrupt our child's
+ * select(2) instead of killing it.
+ */
+static void
+signal_handler(int sig)
+{
+ (void)sig;
+}
+
+/*
+ * Test that pidfile_open() can create a pidfile and that pidfile_write()
+ * can write to it.
+ */
+static const char *
+test_pidfile_uncontested(void)
+{
+ const char *fn = "test_pidfile_uncontested";
+ struct pidfh *pf;
+ pid_t other = 0;
+
+ unlink(fn);
+ pf = pidfile_open(fn, 0600, &other);
+ if (pf == NULL && other != 0)
+ return ("pidfile exists and is locked");
+ if (pf == NULL)
+ return (strerror(errno));
+ if (pidfile_write(pf) != 0) {
+ pidfile_close(pf);
+ unlink(fn);
+ return ("failed to write PID");
+ }
+ pidfile_close(pf);
+ unlink(fn);
+ return (NULL);
+}
+
+/*
+ * Test that pidfile_open() locks against self.
+ */
+static const char *
+test_pidfile_self(void)
+{
+ const char *fn = "test_pidfile_self";
+ struct pidfh *pf1, *pf2;
+ pid_t other = 0;
+ int serrno;
+
+ unlink(fn);
+ pf1 = pidfile_open(fn, 0600, &other);
+ if (pf1 == NULL && other != 0)
+ return ("pidfile exists and is locked");
+ if (pf1 == NULL)
+ return (strerror(errno));
+ if (pidfile_write(pf1) != 0) {
+ serrno = errno;
+ pidfile_close(pf1);
+ unlink(fn);
+ return (strerror(serrno));
+ }
+ // second open should fail
+ pf2 = pidfile_open(fn, 0600, &other);
+ if (pf2 != NULL) {
+ pidfile_close(pf1);
+ pidfile_close(pf2);
+ unlink(fn);
+ return ("managed to opened pidfile twice");
+ }
+ if (other != getpid()) {
+ pidfile_close(pf1);
+ unlink(fn);
+ return ("pidfile contained wrong PID");
+ }
+ pidfile_close(pf1);
+ unlink(fn);
+ return (NULL);
+}
+
+/*
+ * Common code for test_pidfile_{contested,inherited}.
+ */
+static const char *
+common_test_pidfile_child(const char *fn, int parent_open)
+{
+ struct pidfh *pf = NULL;
+ pid_t other = 0, pid = 0;
+ int fd[2], serrno, status;
+ char ch;
+
+ unlink(fn);
+ if (pipe(fd) != 0)
+ return (strerror(errno));
+
+ if (parent_open) {
+ pf = pidfile_open(fn, 0600, &other);
+ if (pf == NULL && other != 0)
+ return ("pidfile exists and is locked");
+ if (pf == NULL)
+ return (strerror(errno));
+ }
+
+ pid = fork();
+ if (pid == -1)
+ return (strerror(errno));
+ if (pid == 0) {
+ // child
+ close(fd[0]);
+ signal(SIGINT, signal_handler);
+ if (!parent_open) {
+ pf = pidfile_open(fn, 0600, &other);
+ if (pf == NULL && other != 0)
+ return ("pidfile exists and is locked");
+ if (pf == NULL)
+ return (strerror(errno));
+ }
+ if (pidfile_write(pf) != 0) {
+ serrno = errno;
+ pidfile_close(pf);
+ unlink(fn);
+ return (strerror(serrno));
+ }
+ if (pf == NULL)
+ _exit(1);
+ if (pidfile_write(pf) != 0)
+ _exit(1);
+ if (write(fd[1], "*", 1) != 1)
+ _exit(1);
+ select(0, 0, 0, 0, 0);
+ _exit(0);
+ }
+ // parent
+ close(fd[1]);
+ if (pf)
+ pidfile_close(pf);
+
+ // wait for the child to signal us
+ if (read(fd[0], &ch, 1) != 1) {
+ serrno = errno;
+ unlink(fn);
+ kill(pid, SIGTERM);
+ errno = serrno;
+ return (strerror(errno));
+ }
+
+ // We shouldn't be able to lock the same pidfile as our child
+ pf = pidfile_open(fn, 0600, &other);
+ if (pf != NULL) {
+ pidfile_close(pf);
+ unlink(fn);
+ return ("managed to lock contested pidfile");
+ }
+
+ // Failed to lock, but not because it was contested
+ if (other == 0) {
+ unlink(fn);
+ return (strerror(errno));
+ }
+
+ // Locked by the wrong process
+ if (other != pid) {
+ unlink(fn);
+ return ("pidfile contained wrong PID");
+ }
+
+ // check our child's fate
+ if (pf)
+ pidfile_close(pf);
+ unlink(fn);
+ if (kill(pid, SIGINT) != 0)
+ return (strerror(errno));
+ if (waitpid(pid, &status, 0) == -1)
+ return (strerror(errno));
+ if (WIFSIGNALED(status))
+ return ("child caught signal");
+ if (WEXITSTATUS(status) != 0)
+ return ("child returned non-zero status");
+
+ // success
+ return (NULL);
+}
+
+/*
+ * Test that pidfile_open() fails when attempting to open a pidfile that
+ * is already locked, and that it returns the correct PID.
+ */
+static const char *
+test_pidfile_contested(void)
+{
+ const char *fn = "test_pidfile_contested";
+ const char *result;
+
+ result = common_test_pidfile_child(fn, 0);
+ return (result);
+}
+
+/*
+ * Test that the pidfile lock is inherited.
+ */
+static const char *
+test_pidfile_inherited(void)
+{
+ const char *fn = "test_pidfile_inherited";
+ const char *result;
+
+ result = common_test_pidfile_child(fn, 1);
+ return (result);
+}
+
+static struct test {
+ const char *name;
+ const char *(*func)(void);
+} t[] = {
+ { "pidfile_uncontested", test_pidfile_uncontested },
+ { "pidfile_self", test_pidfile_self },
+ { "pidfile_contested", test_pidfile_contested },
+ { "pidfile_inherited", test_pidfile_inherited },
+};
+
+int
+main(void)
+{
+ const char *result;
+ int i, nt;
+
+ nt = sizeof(t) / sizeof(*t);
+ printf("1..%d\n", nt);
+ for (i = 0; i < nt; ++i) {
+ if ((result = t[i].func()) != NULL)
+ printf("not ok %d - %s # %s\n", i + 1,
+ t[i].name, result);
+ else
+ printf("ok %d - %s\n", i + 1,
+ t[i].name);
+ }
+ exit(0);
+}
diff --git a/lib/libutil/tests/trimdomain-nodomain_test.c b/lib/libutil/tests/trimdomain-nodomain_test.c
new file mode 100644
index 0000000..c02ccdb
--- /dev/null
+++ b/lib/libutil/tests/trimdomain-nodomain_test.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2005 Brooks Davis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TESTDOMAIN ""
+#define TESTHOST "testhost"
+#define TESTFQDN "testhost" TESTDOMAIN
+
+int failures = 0;
+int tests = 0;
+
+/*
+ * Evily override gethostname(3) so trimdomain always gets the same result.
+ * This makes the tests much easier to write and less likely to fail on
+ * oddly configured systems.
+ */
+int
+gethostname(char *name, size_t namelen)
+{
+ if (strlcpy(name, TESTFQDN, namelen) > namelen) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ return (0);
+}
+
+void
+testit(const char *input, int hostsize, const char *output, const char *test)
+{
+ char *testhost;
+ const char *expected = (output == NULL) ? input : output;
+
+ testhost = strdup(input);
+ trimdomain(testhost, hostsize < 0 ? (int)strlen(testhost) : hostsize);
+ tests++;
+ if (strcmp(testhost, expected) != 0) {
+ printf("not ok %d - %s\n", tests, test);
+ printf("# %s -> %s (expected %s)\n", input, testhost, expected);
+ } else
+ printf("ok %d - %s\n", tests, test);
+ free(testhost);
+ return;
+}
+
+int
+main(void)
+{
+
+ printf("1..5\n");
+
+ testit(TESTFQDN, -1, TESTHOST, "self");
+ testit("XXX" TESTDOMAIN, -1, "XXX", "different host, same domain");
+ testit("XXX" TESTDOMAIN, 1, NULL, "short hostsize");
+ testit("bogus.example.net", -1, NULL, "arbitrary host");
+ testit("XXX." TESTFQDN, -1, NULL, "domain is local hostname");
+
+ return (0);
+}
diff --git a/lib/libutil/tests/trimdomain_test.c b/lib/libutil/tests/trimdomain_test.c
new file mode 100644
index 0000000..08fcfc0
--- /dev/null
+++ b/lib/libutil/tests/trimdomain_test.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2005 Brooks Davis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TESTDOMAIN ".domain.example.com"
+#define TESTHOST "testhost"
+#define TESTFQDN "testhost" TESTDOMAIN
+
+int failures = 0;
+int tests = 0;
+
+/*
+ * Evily override gethostname(3) so trimdomain always gets the same result.
+ * This makes the tests much easier to write and less likely to fail on
+ * oddly configured systems.
+ */
+int
+gethostname(char *name, size_t namelen)
+{
+ if (strlcpy(name, TESTFQDN, namelen) > namelen) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ return (0);
+}
+
+void
+testit(const char *input, int hostsize, const char *output, const char *test)
+{
+ char *testhost;
+ const char *expected = (output == NULL) ? input : output;
+
+ testhost = strdup(input);
+ trimdomain(testhost, hostsize < 0 ? (int)strlen(testhost) : hostsize);
+ tests++;
+ if (strcmp(testhost, expected) != 0) {
+ printf("not ok %d - %s\n", tests, test);
+ printf("# %s -> %s (expected %s)\n", input, testhost, expected);
+ } else
+ printf("ok %d - %s\n", tests, test);
+ free(testhost);
+ return;
+}
+
+int
+main(void)
+{
+
+ printf("1..5\n");
+
+ testit(TESTFQDN, -1, TESTHOST, "self");
+ testit("XXX" TESTDOMAIN, -1, "XXX", "different host, same domain");
+ testit("XXX" TESTDOMAIN, 1, NULL, "short hostsize");
+ testit("bogus.example.net", -1, NULL, "arbitrary host");
+ testit("XXX." TESTFQDN, -1, NULL, "domain is local hostname");
+
+ return (0);
+}
diff --git a/lib/libutil/trimdomain.3 b/lib/libutil/trimdomain.3
new file mode 100644
index 0000000..8d600c0
--- /dev/null
+++ b/lib/libutil/trimdomain.3
@@ -0,0 +1,85 @@
+.\" Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 7, 1999
+.Dt TRIMDOMAIN 3
+.Os
+.Sh NAME
+.Nm trimdomain
+.Nd "trim the current domain name from a host name"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft void
+.Fn trimdomain "char *fullhost" "int hostsize"
+.Sh DESCRIPTION
+The function
+.Fn trimdomain
+removes the current domain name from the passed
+.Ar fullhost
+name by writing a
+.Dv NUL
+character over the first period of the passed name.
+The current domain
+name is determined by calling
+.Xr gethostname 3
+and removing everything up to the first period.
+The name is determined
+the first time this function is called and is cached for future use.
+.Pp
+The
+.Fn trimdomain
+function will only trim the domain name if the passed
+.Ar fullname
+ends with the current domain name and if the length of the resulting host
+name does not exceed
+.Ar hostsize .
+.Pp
+If the passed
+.Ar fullname
+is actually a
+.Dv DISPLAY
+specification of the form
+.Sm off
+.Ar host . domain : nn Oo .
+.Ar nn
+.Oc
+.Sm on
+and the domain name is the same as the local domain name,
+.Fn trimdomain
+will remove the embedded domain name, copying the screen and display
+numbers to the end of the base host name and resulting in
+.Sm off
+.Ar host : nn Op . Ar nn .
+.Sm on
+.Sh RETURN VALUES
+The
+.Fn trimdomain
+function does not return a value.
+.Sh SEE ALSO
+.Xr gethostname 3
diff --git a/lib/libutil/trimdomain.c b/lib/libutil/trimdomain.c
new file mode 100644
index 0000000..85c27e0
--- /dev/null
+++ b/lib/libutil/trimdomain.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * Based on original work by Atsushi Murai <amurai@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <libutil.h>
+#include <string.h>
+#include <unistd.h>
+
+static int isDISP(const char *);
+
+/*-
+ * Trim the current domain name from fullhost, but only if the result
+ * is less than or equal to hostsize in length.
+ *
+ * This function understands $DISPLAY type fullhosts.
+ *
+ * For example:
+ *
+ * trimdomain("abcde.my.domain", 5) -> "abcde"
+ * trimdomain("abcde.my.domain", 4) -> "abcde.my.domain"
+ * trimdomain("abcde.my.domain:0.0", 9) -> "abcde:0.0"
+ * trimdomain("abcde.my.domain:0.0", 8) -> "abcde.my.domain:0.0"
+ */
+void
+trimdomain(char *fullhost, int hostsize)
+{
+ static size_t dlen;
+ static int first = 1;
+ static char domain[MAXHOSTNAMELEN];
+ char *end, *s;
+ size_t len;
+
+ if (first) {
+ /* XXX: Should we assume that our domain is this persistent ? */
+ first = 0;
+ if (gethostname(domain, sizeof(domain) - 1) == 0 &&
+ (s = strchr(domain, '.')) != NULL)
+ memmove(domain, s + 1, strlen(s + 1) + 1);
+ else
+ domain[0] = '\0';
+ dlen = strlen(domain);
+ }
+
+ if (domain[0] == '\0')
+ return;
+
+ s = fullhost;
+ end = s + hostsize + 1;
+ if ((s = memchr(s, '.', (size_t)(end - s))) != NULL) {
+ if (strncasecmp(s + 1, domain, dlen) == 0) {
+ if (s[dlen + 1] == '\0') {
+ /* Found -- lose the domain. */
+ *s = '\0';
+ } else if (s[dlen + 1] == ':' &&
+ isDISP(s + dlen + 2) &&
+ (len = strlen(s + dlen + 1)) < (size_t)(end - s)) {
+ /* Found -- shuffle the DISPLAY back. */
+ memmove(s, s + dlen + 1, len + 1);
+ }
+ }
+ }
+}
+
+/*
+ * Is the given string NN or NN.NN where ``NN'' is an all-numeric string ?
+ */
+static int
+isDISP(const char *disp)
+{
+ size_t w;
+ int res;
+
+ w = strspn(disp, "0123456789");
+ res = 0;
+ if (w > 0) {
+ if (disp[w] == '\0')
+ res = 1; /* NN */
+ else if (disp[w] == '.') {
+ disp += w + 1;
+ w = strspn(disp, "0123456789");
+ if (w > 0 && disp[w] == '\0')
+ res = 1; /* NN.NN */
+ }
+ }
+ return (res);
+}
diff --git a/lib/libutil/uucplock.3 b/lib/libutil/uucplock.3
new file mode 100644
index 0000000..5d8647d
--- /dev/null
+++ b/lib/libutil/uucplock.3
@@ -0,0 +1,183 @@
+.\"
+.\" Copyright (c) 1996 Brian Somers <brian@awfulhak.demon.co.uk>
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd March 30, 1997
+.Dt UUCPLOCK 3
+.Os
+.Sh NAME
+.Nm uu_lock ,
+.Nm uu_unlock ,
+.Nm uu_lockerr
+.Nd acquire and release control of a serial device
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft int
+.Fn uu_lock "const char *ttyname"
+.Ft int
+.Fn uu_lock_txfr "const char *ttyname" "pid_t pid"
+.Ft int
+.Fn uu_unlock "const char *ttyname"
+.Ft const char *
+.Fn uu_lockerr "int uu_lockresult"
+.Sh DESCRIPTION
+The
+.Fn uu_lock
+function attempts to create a lock file called
+.Pa /var/spool/lock/LCK..
+with a suffix given by the passed
+.Fa ttyname .
+If the file already exists, it is expected to contain the process
+id of the locking program.
+.Pp
+If the file does not already exist, or the owning process given by
+the process id found in the lock file is no longer running,
+.Fn uu_lock
+will write its own process id into the file and return success.
+.Pp
+.Fn uu_lock_txfr
+transfers lock ownership to another process.
+.Fn uu_lock
+must have previously been successful.
+.Pp
+.Fn uu_unlock
+removes the lockfile created by
+.Fn uu_lock
+for the given
+.Fa ttyname .
+Care should be taken that
+.Fn uu_lock
+was successful before calling
+.Fn uu_unlock .
+.Pp
+.Fn uu_lockerr
+returns an error string representing the error
+.Fa uu_lockresult ,
+as returned from
+.Fn uu_lock .
+.Sh RETURN VALUES
+.Fn uu_unlock
+returns 0 on success and -1 on failure.
+.Pp
+.Fn uu_lock
+may return any of the following values:
+.Pp
+.Dv UU_LOCK_INUSE :
+The lock is in use by another process.
+.Pp
+.Dv UU_LOCK_OK :
+The lock was successfully created.
+.Pp
+.Dv UU_LOCK_OPEN_ERR :
+The lock file could not be opened via
+.Xr open 2 .
+.Pp
+.Dv UU_LOCK_READ_ERR :
+The lock file could not be read via
+.Xr read 2 .
+.Pp
+.Dv UU_LOCK_CREAT_ERR :
+Cannot create temporary lock file via
+.Xr creat 2 .
+.Pp
+.Dv UU_LOCK_WRITE_ERR :
+The current process id could not be written to the lock file via a call to
+.Xr write 2 .
+.Pp
+.Dv UU_LOCK_LINK_ERR :
+Cannot link temporary lock file via
+.Xr link 2 .
+.Pp
+.Dv UU_LOCK_TRY_ERR :
+Locking attempts are failed after 5 tries.
+.Pp
+If a value of
+.Dv UU_LOCK_OK
+is passed to
+.Fn uu_lockerr ,
+an empty string is returned.
+Otherwise, a string specifying
+the reason for failure is returned.
+.Fn uu_lockerr
+uses the current value of
+.Va errno
+to determine the exact error.
+Care should be made not to allow
+.Va errno
+to be changed between calls to
+.Fn uu_lock
+and
+.Fn uu_lockerr .
+.Pp
+.Fn uu_lock_txfr
+may return any of the following values:
+.Pp
+.Dv UU_LOCK_OK :
+The transfer was successful.
+The specified process now holds the device
+lock.
+.Pp
+.Dv UU_LOCK_OWNER_ERR :
+The current process does not already own a lock on the specified device.
+.Pp
+.Dv UU_LOCK_WRITE_ERR :
+The new process id could not be written to the lock file via a call to
+.Xr write 2 .
+.Sh ERRORS
+If
+.Fn uu_lock
+returns one of the error values above, the global value
+.Va errno
+can be used to determine the cause.
+Refer to the respective manual pages
+for further details.
+.Pp
+.Fn uu_unlock
+will set the global variable
+.Va errno
+to reflect the reason that the lock file could not be removed.
+Refer to the description of
+.Xr unlink 2
+for further details.
+.Sh SEE ALSO
+.Xr lseek 2 ,
+.Xr open 2 ,
+.Xr read 2 ,
+.Xr write 2
+.Sh BUGS
+It is possible that a stale lock is not recognised as such if a new
+processes is assigned the same processes id as the program that left
+the stale lock.
+.Pp
+The calling process must have write permissions to the
+.Pa /var/spool/lock
+directory.
+There is no mechanism in place to ensure that the
+permissions of this directory are the same as those of the
+serial devices that might be locked.
diff --git a/lib/libutil/uucplock.c b/lib/libutil/uucplock.c
new file mode 100644
index 0000000..424ac26
--- /dev/null
+++ b/lib/libutil/uucplock.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)uucplock.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <dirent.h>
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "libutil.h"
+
+#define MAXTRIES 5
+
+#define LOCKTMP "LCKTMP..%d"
+#define LOCKFMT "LCK..%s"
+
+#define GORET(level, val) { err = errno; uuerr = (val); \
+ goto __CONCAT(ret, level); }
+
+/* Forward declarations */
+static int put_pid (int fd, pid_t pid);
+static pid_t get_pid (int fd,int *err);
+
+/*
+ * uucp style locking routines
+ */
+
+int
+uu_lock(const char *tty_name)
+{
+ int fd, tmpfd, i;
+ pid_t pid, pid_old;
+ char lckname[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN],
+ lcktmpname[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+ int err, uuerr;
+
+ pid = getpid();
+ (void)snprintf(lcktmpname, sizeof(lcktmpname), _PATH_UUCPLOCK LOCKTMP,
+ pid);
+ (void)snprintf(lckname, sizeof(lckname), _PATH_UUCPLOCK LOCKFMT,
+ tty_name);
+ if ((tmpfd = open(lcktmpname, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
+ 0664)) < 0)
+ GORET(0, UU_LOCK_CREAT_ERR);
+
+ for (i = 0; i < MAXTRIES; i++) {
+ if (link (lcktmpname, lckname) < 0) {
+ if (errno != EEXIST)
+ GORET(1, UU_LOCK_LINK_ERR);
+ /*
+ * file is already locked
+ * check to see if the process holding the lock
+ * still exists
+ */
+ if ((fd = open(lckname, O_RDONLY | O_CLOEXEC)) < 0)
+ GORET(1, UU_LOCK_OPEN_ERR);
+
+ if ((pid_old = get_pid (fd, &err)) == -1)
+ GORET(2, UU_LOCK_READ_ERR);
+
+ close(fd);
+
+ if (kill(pid_old, 0) == 0 || errno != ESRCH)
+ GORET(1, UU_LOCK_INUSE);
+ /*
+ * The process that locked the file isn't running, so
+ * we'll lock it ourselves
+ */
+ (void)unlink(lckname);
+ } else {
+ if (!put_pid (tmpfd, pid))
+ GORET(3, UU_LOCK_WRITE_ERR);
+ break;
+ }
+ }
+ GORET(1, (i >= MAXTRIES) ? UU_LOCK_TRY_ERR : UU_LOCK_OK);
+
+ret3:
+ (void)unlink(lckname);
+ goto ret1;
+ret2:
+ (void)close(fd);
+ret1:
+ (void)close(tmpfd);
+ (void)unlink(lcktmpname);
+ret0:
+ errno = err;
+ return uuerr;
+}
+
+int
+uu_lock_txfr(const char *tty_name, pid_t pid)
+{
+ int fd, err;
+ char lckname[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+
+ snprintf(lckname, sizeof(lckname), _PATH_UUCPLOCK LOCKFMT, tty_name);
+
+ if ((fd = open(lckname, O_RDWR | O_CLOEXEC)) < 0)
+ return UU_LOCK_OWNER_ERR;
+ if (get_pid(fd, &err) != getpid())
+ err = UU_LOCK_OWNER_ERR;
+ else {
+ lseek(fd, (off_t)0, SEEK_SET);
+ err = put_pid(fd, pid) ? 0 : UU_LOCK_WRITE_ERR;
+ }
+ close(fd);
+
+ return err;
+}
+
+int
+uu_unlock(const char *tty_name)
+{
+ char tbuf[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+
+ (void)snprintf(tbuf, sizeof(tbuf), _PATH_UUCPLOCK LOCKFMT, tty_name);
+ return unlink(tbuf);
+}
+
+const char *
+uu_lockerr(int uu_lockresult)
+{
+ static char errbuf[128];
+ const char *fmt;
+
+ switch (uu_lockresult) {
+ case UU_LOCK_INUSE:
+ return "device in use";
+ case UU_LOCK_OK:
+ return "";
+ case UU_LOCK_OPEN_ERR:
+ fmt = "open error: %s";
+ break;
+ case UU_LOCK_READ_ERR:
+ fmt = "read error: %s";
+ break;
+ case UU_LOCK_CREAT_ERR:
+ fmt = "creat error: %s";
+ break;
+ case UU_LOCK_WRITE_ERR:
+ fmt = "write error: %s";
+ break;
+ case UU_LOCK_LINK_ERR:
+ fmt = "link error: %s";
+ break;
+ case UU_LOCK_TRY_ERR:
+ fmt = "too many tries: %s";
+ break;
+ case UU_LOCK_OWNER_ERR:
+ fmt = "not locking process: %s";
+ break;
+ default:
+ fmt = "undefined error: %s";
+ break;
+ }
+
+ (void)snprintf(errbuf, sizeof(errbuf), fmt, strerror(errno));
+ return errbuf;
+}
+
+static int
+put_pid(int fd, pid_t pid)
+{
+ char buf[32];
+ int len;
+
+ len = sprintf (buf, "%10d\n", (int)pid);
+ return write (fd, buf, (size_t)len) == len;
+}
+
+static pid_t
+get_pid(int fd, int *err)
+{
+ int bytes_read;
+ char buf[32];
+ pid_t pid;
+
+ bytes_read = read (fd, buf, sizeof (buf) - 1);
+ if (bytes_read > 0) {
+ buf[bytes_read] = '\0';
+ pid = (pid_t)strtol (buf, (char **) NULL, 10);
+ } else {
+ pid = -1;
+ *err = bytes_read ? errno : EINVAL;
+ }
+ return pid;
+}
+
+/* end of uucplock.c */
OpenPOWER on IntegriCloud