diff options
Diffstat (limited to 'lib/libutil')
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", <imesno, <imes); + if (lt != NULL && in_ltms(lt, tptr, NULL) == -1) + rc = 0; /* not in allowed times list */ + else { + + lt = login_timelist(lc, "times.deny", <imesno, <imes); + 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 */ |