aboutsummaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/Makefile2
-rw-r--r--usr.bin/awk/awk.1136
-rw-r--r--usr.bin/beep/Makefile2
-rw-r--r--usr.bin/bsdcat/Makefile2
-rw-r--r--usr.bin/bzip2/Makefile2
-rw-r--r--usr.bin/bzip2recover/Makefile2
-rw-r--r--usr.bin/calendar/calendars/calendar.freebsd3
-rw-r--r--usr.bin/chat/Makefile1
-rw-r--r--usr.bin/cpio/Makefile2
-rw-r--r--usr.bin/cpio/tests/Makefile2
-rw-r--r--usr.bin/cut/cut.15
-rw-r--r--usr.bin/cut/cut.c4
-rw-r--r--usr.bin/elfdump/Makefile1
-rw-r--r--usr.bin/find/find.c2
-rw-r--r--usr.bin/getconf/sysconf.gperf1
-rw-r--r--usr.bin/gh-bc/Makefile6
-rw-r--r--usr.bin/gh-bc/tests/Makefile9
-rw-r--r--usr.bin/id/id.128
-rw-r--r--usr.bin/id/id.c148
-rw-r--r--usr.bin/ktrace/subr.c1
-rw-r--r--usr.bin/kyua/Makefile15
-rw-r--r--usr.bin/less/defines.h18
-rw-r--r--usr.bin/localedef/localedef.12
-rw-r--r--usr.bin/login/login.conf4
-rw-r--r--usr.bin/lsvfs/lsvfs.c51
-rw-r--r--usr.bin/lzmainfo/Makefile2
-rw-r--r--usr.bin/man/Makefile2
-rw-r--r--usr.bin/man/man.124
-rwxr-xr-xusr.bin/man/man.sh35
-rw-r--r--usr.bin/mandoc/Makefile11
-rw-r--r--usr.bin/mandoc/mandoc.ucl32
-rw-r--r--usr.bin/mdo/mdo.c857
-rw-r--r--usr.bin/mididump/Makefile2
-rw-r--r--usr.bin/mkimg/mkimg.c21
-rw-r--r--usr.bin/mktemp/mktemp.112
-rw-r--r--usr.bin/ncurses/Makefile2
-rw-r--r--usr.bin/netstat/if.c15
-rw-r--r--usr.bin/netstat/inet.c50
-rw-r--r--usr.bin/netstat/main.c4
-rw-r--r--usr.bin/netstat/sctp.c8
-rw-r--r--usr.bin/newgrp/newgrp.c12
-rwxr-xr-xusr.bin/patch/tests/unified_patch_test.sh18
-rw-r--r--usr.bin/quota/quota.c11
-rw-r--r--usr.bin/sockstat/main.c375
-rw-r--r--usr.bin/sockstat/sockstat.118
-rw-r--r--usr.bin/sockstat/tests/Makefile3
-rw-r--r--usr.bin/stat/stat.145
-rw-r--r--usr.bin/stat/stat.c176
-rwxr-xr-xusr.bin/stat/tests/stat_test.sh72
-rwxr-xr-xusr.bin/tail/tests/tail_test.sh2
-rw-r--r--usr.bin/tar/Makefile2
-rw-r--r--usr.bin/tar/tests/Makefile1
-rw-r--r--usr.bin/tcopy/tcopy.cc6
-rw-r--r--usr.bin/truss/syscalls.c2
-rw-r--r--usr.bin/unzip/Makefile2
-rw-r--r--usr.bin/unzip/tests/Makefile2
-rw-r--r--usr.bin/vmstat/vmstat.c4
-rw-r--r--usr.bin/w/pr_time.c26
-rw-r--r--usr.bin/w/uptime.118
-rw-r--r--usr.bin/w/w.14
-rw-r--r--usr.bin/w/w.c33
-rw-r--r--usr.bin/who/Makefile1
-rw-r--r--usr.bin/xz/Makefile2
-rw-r--r--usr.bin/xzdec/Makefile2
64 files changed, 1967 insertions, 396 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 512f75b5d093..da1a9b3a681f 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -219,7 +219,7 @@ SUBDIR.${MK_ISCSI}+= iscsictl
SUBDIR.${MK_KDUMP}+= kdump
SUBDIR.${MK_KDUMP}+= truss
.if ${MK_MITKRB5} == "no"
-SUBDIR.${MK_KERBEROS_SUPPORT}+= compile_et
+SUBDIR.${MK_KERBEROS}+= compile_et
.endif
SUBDIR.${MK_LDNS_UTILS}+= drill
SUBDIR.${MK_LDNS_UTILS}+= host
diff --git a/usr.bin/awk/awk.1 b/usr.bin/awk/awk.1
index 65c91738966b..612669629a02 100644
--- a/usr.bin/awk/awk.1
+++ b/usr.bin/awk/awk.1
@@ -21,7 +21,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
.\" ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
.\" THIS SOFTWARE.
-.Dd July 30, 2021
+.Dd September 3, 2025
.Dt AWK 1
.Os
.Sh NAME
@@ -32,7 +32,7 @@
.Op Fl safe
.Op Fl version
.Op Fl d Ns Op Ar n
-.Op Fl F Ar fs
+.Op Fl F Ar fs | Fl -csv
.Op Fl v Ar var Ns = Ns Ar value
.Op Ar prog | Fl f Ar progfile
.Ar
@@ -42,9 +42,11 @@ scans each input
.Ar file
for lines that match any of a set of patterns specified literally in
.Ar prog
-or in one or more files specified as
+or in one or more files
+specified as
.Fl f Ar progfile .
-With each pattern there can be an associated action that will be performed
+With each pattern
+there can be an associated action that will be performed
when a line of a
.Ar file
matches the pattern.
@@ -76,6 +78,11 @@ to dump core on fatal errors.
.It Fl F Ar fs
Define the input field separator to be the regular expression
.Ar fs .
+.It Fl -csv
+causes
+.Nm
+to process records using (more or less) standard comma-separated values
+(CSV) format.
.It Fl f Ar progfile
Read program code from the specified file
.Ar progfile
@@ -178,7 +185,7 @@ as the field separator, use the
option with a value of
.Sq [t] .
.Pp
-A pattern-action statement has the form
+A pattern-action statement has the form:
.Pp
.D1 Ar pattern Ic \&{ Ar action Ic \&}
.Pp
@@ -347,7 +354,7 @@ in a pattern.
A pattern may consist of two patterns separated by a comma;
in this case, the action is performed for all lines
from an occurrence of the first pattern
-through an occurrence of the second.
+through an occurrence of the second, inclusive.
.Pp
A relational expression is one of the following:
.Pp
@@ -363,7 +370,8 @@ A relational expression is one of the following:
.Pp
where a
.Ar relop
-is any of the six relational operators in C, and a
+is any of the six relational operators in C,
+and a
.Ar matchop
is either
.Ic ~
@@ -386,6 +394,9 @@ and after the last.
and
.Ic END
do not combine with other patterns.
+They may appear multiple times in a program and execute
+in the order they are read by
+.Nm
.Pp
Variable names with special meanings:
.Pp
@@ -428,6 +439,11 @@ The length of the string matched by the
function.
.It Va RS
Input record separator (default newline).
+If empty, blank lines separate records.
+If more than one character long,
+.Va RS
+is treated as a regular expression, and records are
+separated by text matching the expression.
.It Va RSTART
The starting position of the string matched by the
.Fn match
@@ -515,7 +531,8 @@ occurs, or 0 if it does not.
The length of
.Fa s
taken as a string,
-or of
+number of elements in an array for an array argument,
+or length of
.Va $0
if no argument is given.
.It Fn match s r
@@ -696,10 +713,44 @@ records from
.Ar file
remains open until explicitly closed with a call to
.Fn close .
+.It Fn systime
+returns the current date and time as a standard
+.Dq seconds since the epoch
+value.
+.It Fn strftime fmt timestamp
+formats
+.Fa timestamp
+(a value in seconds since the epoch)
+according to
+Fa fmt ,
+which is a format string as supported by
+.Xr strftime 3 .
+Both
+.Fa timestamp
+and
+.Fa fmt
+may be omitted; if no
+.Fa timestamp ,
+the current time of day is used, and if no
+.Fa fmt ,
+a default format of
+.Dq %a %b %e %H:%M:%S %Z %Y
+is used.
.It Fn system cmd
Executes
.Fa cmd
and returns its exit status.
+This will be -1 upon error,
+.Fa cmd 's
+exit status upon a normal exit,
+256 +
+.Va sig
+upon death-by-signal, where
+.Va sig
+is the number of the murdering signal,
+or 512 +
+.Va sig
+if there was a core dump.
.El
.Ss Bit-Operation Functions
.Bl -tag -width "lshift(a, b)"
@@ -725,6 +776,16 @@ Returns integer argument x shifted by n bits to the right.
But note that the
.Ic exit
expression can modify the exit status.
+.Sh ENVIRONMENT VARIABLES
+If
+.Va POSIXLY_CORRECT
+is set in the environment, then
+.Nm
+follows the POSIX rules for
+.Fn sub
+and
+.Fn gsub
+with respect to consecutive backslashes and ampersands.
.Sh EXAMPLES
Print lines longer than 72 characters:
.Pp
@@ -734,7 +795,7 @@ Print first two fields in opposite order:
.Pp
.Dl { print $2, $1 }
.Pp
-Same, with input fields separated by comma and/or blanks and tabs:
+Same, with input fields separated by comma and/or spaces and tabs:
.Bd -literal -offset indent
BEGIN { FS = ",[ \et]*|[ \et]+" }
{ print $2, $1 }
@@ -810,6 +871,63 @@ to it.
.Pp
The scope rules for variables in functions are a botch;
the syntax is worse.
+.Pp
+Input is expected to be UTF-8 encoded.
+Other multibyte character sets are not handled.
+However, in eight-bit locales,
+.Nm
+treats each input byte as a separate character.
+.Sh UNUSUAL FLOATING-POINT VALUES
+.Nm
+was designed before IEEE 754 arithmetic defined Not-A-Number (NaN)
+and Infinity values, which are supported by all modern floating-point
+hardware.
+.Pp
+Because
+.Nm
+uses
+.Xr strtod 3
+and
+.Xr atof 3
+to convert string values to double-precision floating-point values,
+modern C libraries also convert strings starting with
+.Va inf
+and
+.Va nan
+into infinity and NaN values respectively.
+This led to strange results,
+with something like this:
+.Bd -literal -offset indent
+echo nancy | awk '{ print $1 + 0 }'
+.Ed
+.Pp
+printing
+.Dq nan
+instead of zero.
+.Pp
+.Nm
+now follows GNU AWK, and prefilters string values before attempting
+to convert them to numbers, as follows:
+.Bl -tag -width "Hexadecimal values"
+.It Hexadecimal values
+Hexadecimal values (allowed since C99) convert to zero, as they did
+prior to C99.
+.It NaN values
+The two strings
+.Dq +nan
+and
+.Dq -nan
+(case independent) convert to NaN.
+No others do.
+(NaNs can have signs.)
+.It Infinity values
+The two strings
+.Dq +inf
+and
+.Dq -inf
+(case independent) convert to positive and negative infinity, respectively.
+No others do.
+.El
.Sh DEPRECATED BEHAVIOR
One True Awk has accepted
.Fl F Ar t
diff --git a/usr.bin/beep/Makefile b/usr.bin/beep/Makefile
index 1f2548c24819..79735e6f354a 100644
--- a/usr.bin/beep/Makefile
+++ b/usr.bin/beep/Makefile
@@ -1,3 +1,5 @@
+PACKAGE=sound
+
PROG= beep
MAN= beep.1
LIBADD= m
diff --git a/usr.bin/bsdcat/Makefile b/usr.bin/bsdcat/Makefile
index 032207217be6..06081fc2b2f8 100644
--- a/usr.bin/bsdcat/Makefile
+++ b/usr.bin/bsdcat/Makefile
@@ -11,7 +11,7 @@ BSDCAT_VERSION_STRING!= sed -n '/define.*ARCHIVE_VERSION_ONLY_STRING/{s,[^0-9.],
SRCS= bsdcat.c cmdline.c
.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
-SRCS+= err.c
+SRCS+= lafe_err.c
CFLAGS+= -DBSDCAT_VERSION_STRING=\"${BSDCAT_VERSION_STRING}\"
CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\"
diff --git a/usr.bin/bzip2/Makefile b/usr.bin/bzip2/Makefile
index 99679cc00c44..b89f8bbb85a2 100644
--- a/usr.bin/bzip2/Makefile
+++ b/usr.bin/bzip2/Makefile
@@ -1,3 +1,5 @@
+PACKAGE= bzip2
+
BZ2DIR= ${SRCTOP}/contrib/bzip2
.PATH: ${BZ2DIR}
diff --git a/usr.bin/bzip2recover/Makefile b/usr.bin/bzip2recover/Makefile
index 2b11d3a45694..b62cba373296 100644
--- a/usr.bin/bzip2recover/Makefile
+++ b/usr.bin/bzip2recover/Makefile
@@ -1,3 +1,5 @@
+PACKAGE= bzip2
+
BZ2DIR= ${SRCTOP}/contrib/bzip2
.PATH: ${BZ2DIR}
diff --git a/usr.bin/calendar/calendars/calendar.freebsd b/usr.bin/calendar/calendars/calendar.freebsd
index 1ca63b371f65..b85f2f1dee35 100644
--- a/usr.bin/calendar/calendars/calendar.freebsd
+++ b/usr.bin/calendar/calendars/calendar.freebsd
@@ -146,6 +146,7 @@
03/29 Dave Cottlehuber <dch@FreeBSD.org> born in Christchurch, New Zealand, 1973
03/29 Thierry Thomas <thierry@FreeBSD.org> born in Luxeuil les Bains, France, 1961
03/30 Po-Chuan Hsieh <sunpoet@FreeBSD.org> born in Taipei, Taiwan, Republic of China, 1978
+03/31 Vladlen Popolitov <vladlen@FreeBSD.org> born in Lipetsk region, USSR, 1969
04/01 Matthew Jacob <mjacob@FreeBSD.org> born in San Francisco, California, United States, 1958
04/01 Alexander V. Chernikov <melifaro@FreeBSD.org> born in Moscow, Russian Federation, 1984
04/01 Bill Fenner <fenner@FreeBSD.org> born in Bellefonte, Pennsylvania, United States, 1971
@@ -174,6 +175,7 @@
04/22 Jakub Klama <jceel@FreeBSD.org> born in Blachownia, Silesia, Poland, 1989
04/25 Richard Gallamore <ultima@FreeBSD.org> born in Kissimmee, Florida, United States, 1987
04/26 Rene Ladan <rene@FreeBSD.org> born in Geldrop, the Netherlands, 1980
+04/27 Jose Luis Duran <jlduran@FreeBSD.org> born in Panama City, Panama, 1978
04/28 Oleg Bulyzhin <oleg@FreeBSD.org> born in Kharkov, USSR, 1976
04/28 Andriy Voskoboinyk <avos@FreeBSD.org> born in Bila Tserkva, Ukraine, 1992
04/28 Nuno Teixeira <eduardo@FreeBSD.org> born in Aveiro, Portugal, 1974
@@ -479,6 +481,7 @@
12/05 Ivan Voras <ivoras@FreeBSD.org> born in Slavonski Brod, Croatia, 1981
12/06 Stefan Farfeleder <stefanf@FreeBSD.org> born in Wien, Austria, 1980
12/08 Michael Tuexen <tuexen@FreeBSD.org> born in Oldenburg, Germany, 1966
+12/09 Tiago Gasiba <tiga@FreeBSD.org> born in Porto, Portugal, 1978
12/10 Hiroki Tagato <tagattie@FreeBSD.org> born in Shiga, Japan, 1971
12/11 Ganael Laplanche <martymac@FreeBSD.org> born in Reims, France, 1980
12/11 Koichiro Iwao <meta@FreeBSD.org> born in Oita, Japan, 1987
diff --git a/usr.bin/chat/Makefile b/usr.bin/chat/Makefile
index dfe76f71aba7..1b53d2abb1f7 100644
--- a/usr.bin/chat/Makefile
+++ b/usr.bin/chat/Makefile
@@ -1,6 +1,7 @@
# I once used this extensively, but no longer have a modem. Feel free
# to ask me questions about it, but I disclaim ownership now. -Peter
+PACKAGE=ppp
PROG= chat
MAN= chat.8
diff --git a/usr.bin/cpio/Makefile b/usr.bin/cpio/Makefile
index 46fe36d8c18e..31b25e4199da 100644
--- a/usr.bin/cpio/Makefile
+++ b/usr.bin/cpio/Makefile
@@ -11,7 +11,7 @@ BSDCPIO_VERSION_STRING!= sed -n '/define.*ARCHIVE_VERSION_ONLY_STRING/{s,[^0-9.]
SRCS= cpio.c cmdline.c
.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
-SRCS+= err.c line_reader.c passphrase.c
+SRCS+= lafe_err.c line_reader.c passphrase.c
CFLAGS+= -DBSDCPIO_VERSION_STRING=\"${BSDCPIO_VERSION_STRING}\"
CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\"
diff --git a/usr.bin/cpio/tests/Makefile b/usr.bin/cpio/tests/Makefile
index 9e1028c7eb58..ee4da15bc7e4 100644
--- a/usr.bin/cpio/tests/Makefile
+++ b/usr.bin/cpio/tests/Makefile
@@ -26,7 +26,7 @@ CFLAGS.test_utils.c+= -Wno-cast-align
CPIO_SRCS= cmdline.c
.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
-CPIO_SRCS+= err.c
+CPIO_SRCS+= lafe_err.c
.PATH: ${_LIBARCHIVEDIR}/cpio/test
TESTS_SRCS= \
diff --git a/usr.bin/cut/cut.1 b/usr.bin/cut/cut.1
index 4f5b7b9bb9a5..42a1726e6e24 100644
--- a/usr.bin/cut/cut.1
+++ b/usr.bin/cut/cut.1
@@ -1,3 +1,6 @@
+.\"
+.\" SPDX-License-Identifier: BSD-3-Clause
+.\"
.\" Copyright (c) 1989, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
@@ -86,7 +89,7 @@ It is not an error to select columns or fields not present in the
input line.
.Pp
The options are as follows:
-.Bl -tag -width indent
+.Bl -tag -width "-d delim"
.It Fl b Ar list
The
.Ar list
diff --git a/usr.bin/cut/cut.c b/usr.bin/cut/cut.c
index 60ff5a31062a..e4e322b4e5c9 100644
--- a/usr.bin/cut/cut.c
+++ b/usr.bin/cut/cut.c
@@ -448,8 +448,8 @@ f_cut(FILE *fp, const char *fname)
break;
}
if (*pos)
- for (i = 0; i < (int)clen; i++)
- putchar(p[i - clen]);
+ (void)fwrite(p - clen, 1, clen,
+ stdout);
}
if (ch == '\n')
break;
diff --git a/usr.bin/elfdump/Makefile b/usr.bin/elfdump/Makefile
index 5b48a8b6174d..40dc04ab688e 100644
--- a/usr.bin/elfdump/Makefile
+++ b/usr.bin/elfdump/Makefile
@@ -1,3 +1,4 @@
+PACKAGE=toolchain
PROG= elfdump
.include <bsd.prog.mk>
diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c
index 8b24ecd6a306..2247ae86a94b 100644
--- a/usr.bin/find/find.c
+++ b/usr.bin/find/find.c
@@ -211,7 +211,7 @@ find_execute(PLAN *plan, char *paths[])
}
if (showinfo) {
- fprintf(stderr, "Scanning: %s/%s\n", entry->fts_path, entry->fts_name);
+ fprintf(stderr, "Scanning: %s\n", entry->fts_path);
fprintf(stderr, "Scanned: %zu\n\n", counter);
showinfo = 0;
}
diff --git a/usr.bin/getconf/sysconf.gperf b/usr.bin/getconf/sysconf.gperf
index baf341c8962b..2bd75dd47851 100644
--- a/usr.bin/getconf/sysconf.gperf
+++ b/usr.bin/getconf/sysconf.gperf
@@ -47,6 +47,7 @@ OPEN_MAX, _SC_OPEN_MAX
PAGESIZE, _SC_PAGESIZE
PAGE_SIZE, _SC_PAGESIZE
PASS_MAX, _SC_PASS_MAX
+PHYS_PAGES, _SC_PHYS_PAGES
PTHREAD_DESTRUCTOR_ITERATIONS, _SC_THREAD_DESTRUCTOR_ITERATIONS
PTHREAD_KEYS_MAX, _SC_THREAD_KEYS_MAX
PTHREAD_STACK_MIN, _SC_THREAD_STACK_MIN
diff --git a/usr.bin/gh-bc/Makefile b/usr.bin/gh-bc/Makefile
index 0fdc687c0e50..9aa5116f6faa 100644
--- a/usr.bin/gh-bc/Makefile
+++ b/usr.bin/gh-bc/Makefile
@@ -4,6 +4,7 @@ PROG= gh-bc
PROGNAME= bc
BCDIR= ${SRCTOP}/contrib/${PROGNAME}
+VERSION!= cat ${SRCTOP}/contrib/${PROGNAME}/VERSION.txt
SRCS= args.c bc.c bc_lex.c bc_parse.c data.c dc.c dc_lex.c dc_parse.c file.c history.c
SRCS+= lang.c lex.c main.c num.c opt.c parse.c program.c rand.c read.c vector.c vm.c
@@ -31,6 +32,8 @@ CATALOGS+= zh_CN.UTF-8 zh_CN.eucCN zh_CN.GB18030 zh_CN.GB2312 zh_CN.GBK
NLSNAME= bc
NLSSRCDIR= ${BCDIR}/locales
+CFLAGS+= -I${BCDIR}/include
+
CFLAGS+= -DBC_DEFAULT_BANNER=0
CFLAGS+= -DBC_DEFAULT_DIGIT_CLAMP=0
CFLAGS+= -DBC_DEFAULT_EXPR_EXIT=1
@@ -57,7 +60,8 @@ CFLAGS+= -DBUILD_TYPE=A
CFLAGS+= -DMAINEXEC=${PROGNAME}
CFLAGS+= -DNDEBUG
CFLAGS+= -DNLSPATH=/usr/share/nls/%L/%N.cat
-CFLAGS+= -I${BCDIR}/include
+
+CFLAGS+= -DVERSION=${VERSION}
# prevent floating point incompatibilities caused by -flto on some architectures
.if ${MACHINE_ARCH} != riscv64
diff --git a/usr.bin/gh-bc/tests/Makefile b/usr.bin/gh-bc/tests/Makefile
index 464ae4b5d3c3..f2c92aecb0a5 100644
--- a/usr.bin/gh-bc/tests/Makefile
+++ b/usr.bin/gh-bc/tests/Makefile
@@ -17,7 +17,8 @@ FILESfMODE= 0755
FILESGROUPS+= FILEStests
FILEStestsPACKAGE= ${PACKAGE}
FILEStestsDIR= ${TESTSDIR}/tests
-FILEStests!= echo ${TEST_DIR}/tests/*.py ${TEST_DIR}/tests/*.sh ${TEST_DIR}/tests/*.txt
+FILEStests!= echo ${TEST_DIR}/tests/*.py ${TEST_DIR}/tests/*.sed \
+ ${TEST_DIR}/tests/*.sh ${TEST_DIR}/tests/*.txt
FILEStestsMODE= 0755
FILESGROUPS+= FILESbc
@@ -56,10 +57,12 @@ PLAIN_TESTS_SH= bc_tests dc_tests
bc_tests.sh:
echo "#!/bin/sh" > ${.TARGET}
- echo "env LANG=C ${TESTSDIR}/tests/all.sh bc 1 1 0 0 0 bc" >> ${.TARGET}
+ echo 'env LANG=C BC_TEST_OUTPUT_DIR=$$(pwd) \
+ ${TESTSDIR}/tests/all.sh -n bc 1 1 0 0 bc' >> ${.TARGET}
dc_tests.sh:
echo "#!/bin/sh" > ${.TARGET}
- echo "env LANG=C ${TESTSDIR}/tests/all.sh dc 1 1 0 0 0 dc" >> ${.TARGET}
+ echo "env LANG=C BC_TEST_OUTPUT_DIR=\$pwd \
+ ${TESTSDIR}/tests/all.sh -n dc 1 1 0 0 dc" >> ${.TARGET}
.include <bsd.test.mk>
diff --git a/usr.bin/id/id.1 b/usr.bin/id/id.1
index b8dafb6650b0..62c941f84798 100644
--- a/usr.bin/id/id.1
+++ b/usr.bin/id/id.1
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd March 5, 2011
+.Dd October 23, 2025
.Dt ID 1
.Os
.Sh NAME
@@ -50,12 +50,18 @@
.Nm
.Fl c
.Nm
+.Fl d
+.Op Ar user
+.Nm
.Fl g Op Fl nr
.Op Ar user
.Nm
.Fl p
.Op Ar user
.Nm
+.Fl s
+.Op Ar user
+.Nm
.Fl u Op Fl nr
.Op Ar user
.Sh DESCRIPTION
@@ -90,6 +96,8 @@ Ignored for compatibility with other
implementations.
.It Fl c
Display current login class.
+.It Fl d
+Display the home directory of the current or specified user.
.It Fl g
Display the effective group ID as a number.
.It Fl n
@@ -128,6 +136,8 @@ Display the real ID for the
and
.Fl u
options instead of the effective ID.
+.It Fl s
+Display the shell of the current or specified user.
.It Fl u
Display the effective user ID as a number.
.El
@@ -174,8 +184,20 @@ bob pts/5 Dec 4 19:51
.Sh STANDARDS
The
.Nm
-function is expected to conform to
-.St -p1003.2 .
+utility is expected to conform to
+.St -p1003.1-2024 .
+The
+.Fl A ,
+.Fl M ,
+.Fl P ,
+.Fl c ,
+.Fl d ,
+.Fl p ,
+and
+.Fl s
+options are
+.Fx
+extensions.
.Sh HISTORY
The
historic
diff --git a/usr.bin/id/id.c b/usr.bin/id/id.c
index 7112e0dddb91..7ba07daad11e 100644
--- a/usr.bin/id/id.c
+++ b/usr.bin/id/id.c
@@ -53,79 +53,88 @@ static void pretty(struct passwd *);
#ifdef USE_BSM_AUDIT
static void auditid(void);
#endif
-static void group(struct passwd *, int);
+static void group(struct passwd *, bool);
static void maclabel(void);
+static void dir(struct passwd *);
+static void shell(struct passwd *);
static void usage(void);
static struct passwd *who(char *);
-static int isgroups, iswhoami;
+static bool isgroups, iswhoami;
int
main(int argc, char *argv[])
{
struct group *gr;
struct passwd *pw;
- int Gflag, Mflag, Pflag, ch, gflag, id, nflag, pflag, rflag, uflag;
- int Aflag, cflag;
- int error;
- const char *myname;
+ bool Aflag, Gflag, Mflag, Pflag;
+ bool cflag, dflag, gflag, nflag, pflag, rflag, sflag, uflag;
+ int ch, combo, error, id;
+ const char *myname, *optstr;
char loginclass[MAXLOGNAME];
- Gflag = Mflag = Pflag = gflag = nflag = pflag = rflag = uflag = 0;
- Aflag = cflag = 0;
+ Aflag = Gflag = Mflag = Pflag = false;
+ cflag = dflag = gflag = nflag = pflag = rflag = sflag = uflag = false;
- myname = strrchr(argv[0], '/');
- myname = (myname != NULL) ? myname + 1 : argv[0];
+ myname = getprogname();
+ optstr = "AGMPacdgnprsu";
if (strcmp(myname, "groups") == 0) {
- isgroups = 1;
- Gflag = nflag = 1;
+ isgroups = true;
+ optstr = "";
+ Gflag = nflag = true;
}
else if (strcmp(myname, "whoami") == 0) {
- iswhoami = 1;
- uflag = nflag = 1;
+ iswhoami = true;
+ optstr = "";
+ uflag = nflag = true;
}
- while ((ch = getopt(argc, argv,
- (isgroups || iswhoami) ? "" : "APGMacgnpru")) != -1)
+ while ((ch = getopt(argc, argv, optstr)) != -1) {
switch(ch) {
#ifdef USE_BSM_AUDIT
case 'A':
- Aflag = 1;
+ Aflag = true;
break;
#endif
case 'G':
- Gflag = 1;
+ Gflag = true;
break;
case 'M':
- Mflag = 1;
+ Mflag = true;
break;
case 'P':
- Pflag = 1;
+ Pflag = true;
break;
case 'a':
break;
case 'c':
- cflag = 1;
+ cflag = true;
+ break;
+ case 'd':
+ dflag = true;
break;
case 'g':
- gflag = 1;
+ gflag = true;
break;
case 'n':
- nflag = 1;
+ nflag = true;
break;
case 'p':
- pflag = 1;
+ pflag = true;
break;
case 'r':
- rflag = 1;
+ rflag = true;
+ break;
+ case 's':
+ sflag = true;
break;
case 'u':
- uflag = 1;
+ uflag = true;
break;
- case '?':
default:
usage();
}
+ }
argc -= optind;
argv += optind;
@@ -134,16 +143,13 @@ main(int argc, char *argv[])
if ((cflag || Aflag || Mflag) && argc > 0)
usage();
- switch(Aflag + Gflag + Mflag + Pflag + gflag + pflag + uflag) {
- case 1:
- break;
- case 0:
- if (!nflag && !rflag)
- break;
- /* FALLTHROUGH */
- default:
+ combo = Aflag + Gflag + Mflag + Pflag + gflag + pflag + uflag;
+ if (combo + dflag + sflag > 1)
+ usage();
+ if (combo > 1)
+ usage();
+ if (combo == 0 && (nflag || rflag))
usage();
- }
pw = *argv ? who(*argv) : NULL;
@@ -183,6 +189,11 @@ main(int argc, char *argv[])
exit(0);
}
+ if (dflag) {
+ dir(pw);
+ exit(0);
+ }
+
if (Gflag) {
group(pw, nflag);
exit(0);
@@ -203,6 +214,11 @@ main(int argc, char *argv[])
exit(0);
}
+ if (sflag) {
+ shell(pw);
+ exit(0);
+ }
+
id_print(pw);
exit(0);
}
@@ -217,7 +233,7 @@ pretty(struct passwd *pw)
if (pw) {
(void)printf("uid\t%s\n", pw->pw_name);
(void)printf("groups\t");
- group(pw, 1);
+ group(pw, true);
} else {
if ((login = getlogin()) == NULL)
err(1, "getlogin");
@@ -243,7 +259,7 @@ pretty(struct passwd *pw)
(void)printf("rgid\t%u\n", rid);
}
(void)printf("groups\t");
- group(NULL, 1);
+ group(NULL, true);
}
}
@@ -366,7 +382,7 @@ auditid(void)
#endif
static void
-group(struct passwd *pw, int nflag)
+group(struct passwd *pw, bool nflag)
{
struct group *gr;
int cnt, id, lastid, ngroups;
@@ -452,41 +468,57 @@ who(char *u)
static void
pline(struct passwd *pw)
{
-
- if (!pw) {
+ if (pw == NULL) {
if ((pw = getpwuid(getuid())) == NULL)
err(1, "getpwuid");
}
-
(void)printf("%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", pw->pw_name,
- pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class,
- (long)pw->pw_change, (long)pw->pw_expire, pw->pw_gecos,
- pw->pw_dir, pw->pw_shell);
+ pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class,
+ (long)pw->pw_change, (long)pw->pw_expire, pw->pw_gecos,
+ pw->pw_dir, pw->pw_shell);
}
+static void
+dir(struct passwd *pw)
+{
+ if (pw == NULL) {
+ if ((pw = getpwuid(getuid())) == NULL)
+ err(1, "getpwuid");
+ }
+ printf("%s\n", pw->pw_dir);
+}
static void
-usage(void)
+shell(struct passwd *pw)
{
+ if (pw == NULL) {
+ if ((pw = getpwuid(getuid())) == NULL)
+ err(1, "getpwuid");
+ }
+ printf("%s\n", pw->pw_shell);
+}
+static void
+usage(void)
+{
if (isgroups)
(void)fprintf(stderr, "usage: groups [user]\n");
else if (iswhoami)
(void)fprintf(stderr, "usage: whoami\n");
else
- (void)fprintf(stderr, "%s\n%s%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
- "usage: id [user]",
+ (void)fprintf(stderr,
+ "usage: id [user]\n"
#ifdef USE_BSM_AUDIT
- " id -A\n",
-#else
- "",
+ " id -A\n"
#endif
- " id -G [-n] [user]",
- " id -M",
- " id -P [user]",
- " id -c",
- " id -g [-nr] [user]",
- " id -p [user]",
- " id -u [-nr] [user]");
+ " id -G [-n] [user]\n"
+ " id -M\n"
+ " id -P [user]\n"
+ " id -c\n"
+ " id -d [user]\n"
+ " id -g [-nr] [user]\n"
+ " id -p [user]\n"
+ " id -s [user]\n"
+ " id -u [-nr] [user]\n");
exit(1);
}
diff --git a/usr.bin/ktrace/subr.c b/usr.bin/ktrace/subr.c
index 422a37bb413d..fac335948f46 100644
--- a/usr.bin/ktrace/subr.c
+++ b/usr.bin/ktrace/subr.c
@@ -89,6 +89,7 @@ getpoints(char *s)
break;
case 'x':
facs |= KTRFAC_EXTERR;
+ break;
case '+':
facs |= DEF_POINTS;
break;
diff --git a/usr.bin/kyua/Makefile b/usr.bin/kyua/Makefile
index a4f95f1106d9..d6131651afbf 100644
--- a/usr.bin/kyua/Makefile
+++ b/usr.bin/kyua/Makefile
@@ -10,7 +10,7 @@ KYUA_VERSION= 0.13
KYUA_SRCDIR= ${SRCTOP}/contrib/kyua
.PATH: ${KYUA_SRCDIR}
-PACKAGE= tests
+PACKAGE= kyua
PROG_CXX= kyua
SRCS= main.cpp
LIBADD= lutok sqlite3 util
@@ -129,7 +129,8 @@ SRCS+= engine/atf.cpp \
engine/execenv/execenv_host.cpp
SRCS+= os/freebsd/execenv_jail_manager.cpp \
- os/freebsd/main.cpp
+ os/freebsd/main.cpp \
+ os/freebsd/reqs_checker_kmods.cpp
SRCS+= store/dbtypes.cpp \
store/exceptions.cpp \
@@ -181,25 +182,25 @@ FILESGROUPS+= EXAMPLES
CONFS= kyua.conf-default
CONFSDIR= ${KYUA_CONFDIR}
CONFSNAME= kyua.conf
-CONFSDIRTAGS= package=tests
+CONFSDIRTAGS= package=kyua
DOCS= AUTHORS CONTRIBUTORS LICENSE
DOCSDIR= ${KYUA_DOCDIR}
-DOCSTAGS= package=tests
+DOCSTAGS= package=kyua
EXAMPLES= Kyuafile.top kyua.conf
EXAMPLESDIR= ${KYUA_EGDIR}
-EXAMPLESTAGS= package=tests
+EXAMPLESTAGS= package=kyua
.PATH: ${KYUA_SRCDIR}/examples
MISC= context.html index.html report.css test_result.html
MISCDIR= ${KYUA_MISCDIR}
-MISCTAGS= package=tests
+MISCTAGS= package=kyua
.PATH: ${KYUA_SRCDIR}/misc
STORE= migrate_v1_v2.sql migrate_v2_v3.sql schema_v3.sql
STOREDIR= ${KYUA_STOREDIR}
-STORETAGS= package=tests
+STORETAGS= package=kyua
.PATH: ${KYUA_SRCDIR}/store
CLEANFILES+= ${MAN}
diff --git a/usr.bin/less/defines.h b/usr.bin/less/defines.h
index 544749e3bddd..d95ff956bd37 100644
--- a/usr.bin/less/defines.h
+++ b/usr.bin/less/defines.h
@@ -122,10 +122,19 @@
#define LESSKEYINFILE_SYS "/etc/syslesskey"
#define LESSHISTFILE ".lesshst"
-
-/* Settings always true on Unix. */
+/* Autodetect mingw */
+#if defined(__MINGW32__)
+/*
+ * Define MSDOS_COMPILER if compiling under Microsoft C.
+ */
+#define MSDOS_COMPILER WIN32C
/*
+ * Pathname separator character.
+ */
+#define PATHNAME_SEP "\\"
+#else
+/*
* Define MSDOS_COMPILER if compiling under Microsoft C.
*/
#define MSDOS_COMPILER 0
@@ -134,6 +143,9 @@
* Pathname separator character.
*/
#define PATHNAME_SEP "/"
+#endif
+
+/* Settings always true on Unix. */
/*
* The value returned from tgetent on success.
@@ -144,7 +156,7 @@
/*
* HAVE_ANSI_PROTOS is 1 if your compiler supports ANSI function prototypes.
*/
-#define HAVE_ANSI_PROTOS 1
+#define HAVE_ANSI_PROTOS 1
/*
* HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
diff --git a/usr.bin/localedef/localedef.1 b/usr.bin/localedef/localedef.1
index be37715f490d..918b57961c6c 100644
--- a/usr.bin/localedef/localedef.1
+++ b/usr.bin/localedef/localedef.1
@@ -164,7 +164,7 @@ unless instructed otherwise by the
.Fl D (
BSD
output) option.
-The contants of this directory should generally be copied into the
+The content of this directory should generally be copied into the
appropriate subdirectory of
.Pa /usr/share/locale
in order the definitions to be visible to programs linked with libc.
diff --git a/usr.bin/login/login.conf b/usr.bin/login/login.conf
index 1069da17b4db..c65a83caa565 100644
--- a/usr.bin/login/login.conf
+++ b/usr.bin/login/login.conf
@@ -46,7 +46,6 @@ default:\
:umtxp=unlimited:\
:pipebuf=unlimited:\
:priority=0:\
- :ignoretime@:\
:umask=022:\
:charset=UTF-8:\
:lang=C.UTF-8:
@@ -149,7 +148,6 @@ russian|Russian Users Accounts:\
# :requirehome:\
# :passwordtime=90d:\
# :umask=002:\
-# :ignoretime@:\
# :tc=default:
#
#
@@ -174,7 +172,6 @@ russian|Russian Users Accounts:\
##
#staff:\
# :ignorenologin:\
-# :ignoretime:\
# :requirehome@:\
# :accounted@:\
# :path=~/bin /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin:\
@@ -265,7 +262,6 @@ russian|Russian Users Accounts:\
## - no time accounting, restricted to access via dialin lines
##
#site:\
-# :ignoretime:\
# :passwordtime@:\
# :refreshtime@:\
# :refreshperiod@:\
diff --git a/usr.bin/lsvfs/lsvfs.c b/usr.bin/lsvfs/lsvfs.c
index 5477d96434ac..8925b8988cd3 100644
--- a/usr.bin/lsvfs/lsvfs.c
+++ b/usr.bin/lsvfs/lsvfs.c
@@ -5,10 +5,12 @@
*
*/
+#include <sys/capsicum.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
+#include <capsicum_helpers.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
@@ -38,41 +40,42 @@ static const char *fmt_flags(int);
int
main(int argc, char **argv)
{
- struct xvfsconf vfc, *xvfsp;
+ struct xvfsconf *xvfsp;
size_t cnt, buflen;
int rv = 0;
argc--, argv++;
+ if (sysctlbyname("vfs.conflist", NULL, &buflen, NULL, 0) < 0)
+ err(EXIT_FAILURE, "sysctl(vfs.conflist)");
+ if ((xvfsp = malloc(buflen)) == NULL)
+ errx(EXIT_FAILURE, "malloc failed");
+ if (sysctlbyname("vfs.conflist", xvfsp, &buflen, NULL, 0) < 0)
+ err(EXIT_FAILURE, "sysctl(vfs.conflist)");
+ cnt = buflen / sizeof(struct xvfsconf);
+
+ caph_cache_catpages();
+ if (caph_enter() != 0)
+ err(EXIT_FAILURE, "failed to enter capability mode");
+
printf(HDRFMT, "Filesystem", "Num", "Refs", "Flags");
fputs(DASHES, stdout);
- if (argc > 0) {
- for (; argc > 0; argc--, argv++) {
- if (getvfsbyname(*argv, &vfc) == 0) {
- printf(FMT, vfc.vfc_name, vfc.vfc_typenum,
- vfc.vfc_refcount, fmt_flags(vfc.vfc_flags));
- } else {
- warnx("VFS %s unknown or not loaded", *argv);
- rv++;
+ for (size_t i = 0; i < cnt; i++) {
+ if (argc > 0) {
+ int j;
+ for (j = 0; j < argc; j++) {
+ if (strcmp(argv[j], xvfsp[i].vfc_name) == 0)
+ break;
}
+ if (j == argc)
+ continue;
}
- } else {
- if (sysctlbyname("vfs.conflist", NULL, &buflen, NULL, 0) < 0)
- err(EXIT_FAILURE, "sysctl(vfs.conflist)");
- if ((xvfsp = malloc(buflen)) == NULL)
- errx(EXIT_FAILURE, "malloc failed");
- if (sysctlbyname("vfs.conflist", xvfsp, &buflen, NULL, 0) < 0)
- err(EXIT_FAILURE, "sysctl(vfs.conflist)");
- cnt = buflen / sizeof(struct xvfsconf);
-
- for (size_t i = 0; i < cnt; i++) {
- printf(FMT, xvfsp[i].vfc_name, xvfsp[i].vfc_typenum,
- xvfsp[i].vfc_refcount,
- fmt_flags(xvfsp[i].vfc_flags));
- }
- free(xvfsp);
+
+ printf(FMT, xvfsp[i].vfc_name, xvfsp[i].vfc_typenum,
+ xvfsp[i].vfc_refcount, fmt_flags(xvfsp[i].vfc_flags));
}
+ free(xvfsp);
return (rv);
}
diff --git a/usr.bin/lzmainfo/Makefile b/usr.bin/lzmainfo/Makefile
index afde07163a01..e537e4c86083 100644
--- a/usr.bin/lzmainfo/Makefile
+++ b/usr.bin/lzmainfo/Makefile
@@ -1,3 +1,5 @@
+PACKAGE=xz
+
PROG= lzmainfo
XZDIR= ${SRCTOP}/contrib/xz/src
diff --git a/usr.bin/man/Makefile b/usr.bin/man/Makefile
index 9c9098270735..9e47006cac0a 100644
--- a/usr.bin/man/Makefile
+++ b/usr.bin/man/Makefile
@@ -1,3 +1,5 @@
+PACKAGE= mandoc
+
SCRIPTS= man.sh
LINKS= ${BINDIR}/man ${BINDIR}/manpath
diff --git a/usr.bin/man/man.1 b/usr.bin/man/man.1
index 820d6a5b33a9..707677ccce06 100644
--- a/usr.bin/man/man.1
+++ b/usr.bin/man/man.1
@@ -33,7 +33,7 @@
.Nd display online manual documentation pages
.Sh SYNOPSIS
.Nm
-.Op Fl adho
+.Op Fl adhlo
.Op Fl t | w
.Op Fl M Ar manpath
.Op Fl P Ar pager
@@ -144,6 +144,15 @@ Search names and descriptions of all manual pages for an extended regular
.Ar expression ,
emulating basic functionality of
.Xr apropos 1 .
+.It Fl l
+Interpret all arguments as absolute or relative filename(s)
+of the manual page(s) to display.
+No search is done and the options
+.Fl M ,
+.Fl m ,
+and
+.Fl S
+are ignored.
.It Fl m Ar arch Ns Op : Ns Ar machine
Override the default architecture and machine settings allowing lookup of
other platform specific manual pages.
@@ -269,12 +278,15 @@ will search the following paths when considering section 4 manual pages in
.Pa /usr/share/man/man4
.El
.Ss Displaying Specific Manual Files
-The
+For compatibility reasons,
.Nm
-utility also supports displaying a specific manual page if passed a path
-to the file as long as it contains a
+will interpret any argument containing at least one
.Ql /
-character.
+character as an absolute or relative path to a manual page to be
+displayed.
+This heuristic, made redundant by the more widely supported
+.Fl l
+option, is now deprecated and may be removed in future releases.
.Sh ENVIRONMENT
The following environment variables affect the execution of
.Nm :
@@ -398,7 +410,7 @@ manual page:
.Pp
Show a manual page in the current working directory:
.Pp
-.Dl $ man ./man.1
+.Dl $ man -l man.1
.Pp
Show the location of manual pages in sections 1 and 8 which contain the word
.Ql arm :
diff --git a/usr.bin/man/man.sh b/usr.bin/man/man.sh
index 18595042da5f..920223ce3c27 100755
--- a/usr.bin/man/man.sh
+++ b/usr.bin/man/man.sh
@@ -511,13 +511,21 @@ man_display_page_groff() {
# Usage: man_find_and_display page
# Search through the manpaths looking for the given page.
man_find_and_display() {
- local found_page locpath p path sect
+ local found_page has_slash locpath p path sect
# Check to see if it's a file. But only if it has a '/' in
- # the filename.
+ # the filename or if -l was specified.
case "$1" in
- */*) if [ -f "$1" -a -r "$1" ]; then
+ */*) has_slash=yes
+ ;;
+ esac
+ if [ -n "$has_slash" -o -n "$lflag" ]; then
+ if [ -f "$1" -a -r "$1" ]; then
decho "Found a usable page, displaying that"
+ if [ -z "$lflag" ]; then
+ echo "Opening a file directly is deprecated," \
+ "use -l instead." >&2
+ fi
unset use_cat
manpage="$1"
setup_cattool "$manpage"
@@ -531,9 +539,12 @@ man_find_and_display() {
man_display_page
fi
return
+ elif [ -n "$lflag" ]; then
+ echo "Cannot read $1" >&2
+ ret=1
+ return
fi
- ;;
- esac
+ fi
IFS=:
for sect in $MANSECT; do
@@ -601,7 +612,7 @@ man_parse_opts() {
local cmd_arg
OPTIND=1
- while getopts 'K:M:P:S:adfhkm:op:tw' cmd_arg; do
+ while getopts 'K:M:P:S:adfhklm:op:tw' cmd_arg; do
case "${cmd_arg}" in
K) Kflag=Kflag
REGEXP=$OPTARG ;;
@@ -613,6 +624,7 @@ man_parse_opts() {
f) fflag=fflag ;;
h) man_usage 0 ;;
k) kflag=kflag ;;
+ l) lflag=lflag ;;
m) mflag=$OPTARG ;;
o) oflag=oflag ;;
p) MANROFFSEQ=$OPTARG ;;
@@ -625,16 +637,19 @@ man_parse_opts() {
shift $(( $OPTIND - 1 ))
# Check the args for incompatible options.
-
- case "${Kflag}${fflag}${kflag}${tflag}${wflag}" in
+ case "${Kflag}${fflag}${kflag}${lflag}${tflag}${wflag}" in
Kflagfflag*) echo "Incompatible options: -K and -f"; man_usage ;;
Kflag*kflag*) echo "Incompatible options: -K and -k"; man_usage ;;
+ Kflag*lflag*) echo "Incompatible options: -K and -l"; man_usage ;;
Kflag*tflag) echo "Incompatible options: -K and -t"; man_usage ;;
fflagkflag*) echo "Incompatible options: -f and -k"; man_usage ;;
+ fflag*lflag*) echo "Incompatible options: -f and -l"; man_usage ;;
fflag*tflag*) echo "Incompatible options: -f and -t"; man_usage ;;
fflag*wflag) echo "Incompatible options: -f and -w"; man_usage ;;
- *kflagtflag*) echo "Incompatible options: -k and -t"; man_usage ;;
+ *kflaglflag*) echo "Incompatible options: -k and -l"; man_usage ;;
+ *kflag*tflag*) echo "Incompatible options: -k and -t"; man_usage ;;
*kflag*wflag) echo "Incompatible options: -k and -w"; man_usage ;;
+ *lflag*wflag) echo "Incompatible options: -l and -w"; man_usage ;;
*tflagwflag) echo "Incompatible options: -t and -w"; man_usage ;;
esac
@@ -751,7 +766,7 @@ man_setup_locale() {
# Display usage for the man utility.
man_usage() {
echo 'Usage:'
- echo ' man [-adho] [-t | -w] [-M manpath] [-P pager] [-S mansect]'
+ echo ' man [-adhlo] [-t | -w] [-M manpath] [-P pager] [-S mansect]'
echo ' [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]'
echo ' man -K | -f | -k expression [...] -- Search manual pages'
diff --git a/usr.bin/mandoc/Makefile b/usr.bin/mandoc/Makefile
index c5255b1468fd..181d4e16c8ee 100644
--- a/usr.bin/mandoc/Makefile
+++ b/usr.bin/mandoc/Makefile
@@ -3,8 +3,10 @@
MANDOCDIR= ${SRCTOP}/contrib/mandoc
.PATH: ${MANDOCDIR}
+PACKAGE= mandoc
+
PROG= mandoc
-MAN= mandoc.1 eqn.7 mandoc_char.7 tbl.7 man.7 mdoc.7 roff.7
+MAN= mandoc.1 mandoc.db.5 eqn.7 mandoc_char.7 tbl.7 man.7 mdoc.7 roff.7
MLINKS= mandoc.1 mdocml.1
.if ${MK_MAN_UTILS} != no
MAN+= apropos.1 makewhatis.8
@@ -16,6 +18,10 @@ LINKS= ${BINDIR}/mandoc ${BINDIR}/whatis \
.error MK_MAN_UTILS should be set to yes when bootstrapping
.endif
+FILESGROUPS= TRIGGERS
+TRIGGERS= mandoc.ucl
+TRIGGERSDIR= /usr/share/pkg/triggers
+
LIBMAN_SRCS= man.c \
man_macro.c \
man_validate.c
@@ -54,8 +60,7 @@ LIB_SRCS= ${LIBMAN_SRCS} \
mandoc_xr.c \
msec.c \
preconv.c \
- read.c \
- compat_recallocarray.c \
+ read.c
HTML_SRCS= eqn_html.c \
html.c \
diff --git a/usr.bin/mandoc/mandoc.ucl b/usr.bin/mandoc/mandoc.ucl
new file mode 100644
index 000000000000..75b8123d55cc
--- /dev/null
+++ b/usr.bin/mandoc/mandoc.ucl
@@ -0,0 +1,32 @@
+path_glob: [
+ "/usr/share/man/*",
+ "/usr/share/openssl/man/*",
+]
+
+cleanup: {
+ type: lua
+ sandbox: false
+ script: <<EOD
+ os.remove("/usr/share/man/mandoc.db")
+ os.remove("/usr/share/openssl/man/mandoc.db")
+EOD
+}
+
+trigger: {
+ type: lua
+ sandbox: false
+ script: <<EOD
+
+ local dirs = {
+ "/usr/share/man",
+ "/usr/share/openssl/man",
+ }
+
+ for _,dir in ipairs(dirs) do
+ if pkg.stat(dir) then
+ print("Generating apropos(1) database for "..dir.."...")
+ pkg.exec({"/usr/bin/makewhatis", dir})
+ end
+ end
+EOD
+}
diff --git a/usr.bin/mdo/mdo.c b/usr.bin/mdo/mdo.c
index 8435fc17f26f..3eb5d4e5c23f 100644
--- a/usr.bin/mdo/mdo.c
+++ b/usr.bin/mdo/mdo.c
@@ -1,13 +1,24 @@
/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
* Copyright(c) 2024 Baptiste Daroussin <bapt@FreeBSD.org>
+ * Copyright (c) 2025 Kushagra Srivastava <kushagra1403@gmail.com>
+ * Copyright (c) 2025 The FreeBSD Foundation
*
- * SPDX-License-Identifier: BSD-2-Clause
+ * Portions of this software were developed by Olivier Certner
+ * <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+ * Foundation.
*/
+#include <sys/errno.h>
#include <sys/limits.h>
+#include <sys/types.h>
#include <sys/ucred.h>
+#include <assert.h>
#include <err.h>
+#include <getopt.h>
+#include <grp.h>
#include <paths.h>
#include <pwd.h>
#include <stdbool.h>
@@ -16,84 +27,848 @@
#include <string.h>
#include <unistd.h>
+
static void
usage(void)
{
- fprintf(stderr, "usage: mdo [-u username] [-i] [--] [command [args]]\n");
- exit(EXIT_FAILURE);
+ fprintf(stderr,
+ "Usage: mdo [options] [--] [command [args...]]\n"
+ "\n"
+ "Options:\n"
+ " -u <user> Target user (name or UID; name sets groups)\n"
+ " -k Keep current user, allows selective overrides "
+ "(implies -i)\n"
+ " -i Keep current groups, unless explicitly overridden\n"
+ " -g <group> Override primary group (name or GID)\n"
+ " -G <g1,g2,...> Set supplementary groups (name or GID list)\n"
+ " -s <mods> Modify supplementary groups using:\n"
+ " @ (first) to reset, +group to add, -group to remove\n"
+ "\n"
+ "Advanced UID/GID overrides:\n"
+ " --euid <uid> Set effective UID\n"
+ " --ruid <uid> Set real UID\n"
+ " --svuid <uid> Set saved UID\n"
+ " --egid <gid> Set effective GID\n"
+ " --rgid <gid> Set real GID\n"
+ " --svgid <gid> Set saved GID\n"
+ "\n"
+ " -h Show this help message\n"
+ "\n"
+ "Examples:\n"
+ " mdo -u alice id\n"
+ " mdo -u 1001 -g wheel -G staff,operator sh\n"
+ " mdo -u bob -s +wheel,+operator id\n"
+ " mdo -k --ruid 1002 --egid 1004 id\n"
+ );
+ exit(1);
+}
+
+struct alloc {
+ void *start;
+ size_t size;
+};
+
+static const struct alloc ALLOC_INITIALIZER = {
+ .start = NULL,
+ .size = 0,
+};
+
+/*
+ * The default value should cover almost all cases.
+ *
+ * For getpwnam_r(), we assume:
+ * - 88 bytes for 'struct passwd'
+ * - Less than 16 bytes for the user name
+ * - A typical shadow hash of 106 bytes
+ * - Less than 16 bytes for the login class name
+ * - Less than 64 bytes for GECOS info
+ * - Less than 128 bytes for the home directory
+ * - Less than 32 bytes for the shell path
+ * Total: 256 + 88 + 106 = 450.
+ *
+ * For getgrnam_r(), we assume:
+ * - 32 bytes for 'struct group'
+ * - Less than 16 bytes for the group name
+ * - Some hash of 106 bytes
+ * - No more than 16 members, each of less than 16 bytes (=> 256 bytes)
+ * Total: 256 + 32 + 16 + 106 = 410.
+ *
+ * We thus choose 512 (leeway, power of 2).
+ */
+static const size_t ALLOC_FIRST_SIZE = 512;
+
+static bool
+alloc_is_empty(const struct alloc *const alloc)
+{
+ if (alloc->size == 0) {
+ assert(alloc->start == NULL);
+ return (true);
+ } else {
+ assert(alloc->start != NULL);
+ return (false);
+ }
+}
+
+static void
+alloc_realloc(struct alloc *const alloc)
+{
+ const size_t old_size = alloc->size;
+ size_t new_size;
+
+ if (old_size == 0) {
+ assert(alloc->start == NULL);
+ new_size = ALLOC_FIRST_SIZE;
+ } else if (old_size < PAGE_SIZE)
+ new_size = 2 * old_size;
+ else
+ /*
+ * We never allocate more than a page at a time when reaching
+ * a page (except perhaps for the first increment, up to two).
+ * Use roundup2() to be immune to previous cases' changes. */
+ new_size = roundup2(old_size, PAGE_SIZE) + PAGE_SIZE;
+
+ alloc->start = realloc(alloc->start, new_size);
+ if (alloc->start == NULL)
+ errx(EXIT_FAILURE,
+ "cannot realloc allocation (old size: %zu, new: %zu)",
+ old_size, new_size);
+ alloc->size = new_size;
+}
+
+static void
+alloc_free(struct alloc *const alloc)
+{
+ if (!alloc_is_empty(alloc)) {
+ free(alloc->start);
+ *alloc = ALLOC_INITIALIZER;
+ }
+}
+
+struct alloc_wrap_data {
+ int (*func)(void *data, const struct alloc *alloc);
+};
+
+/*
+ * Wraps functions needing a backing allocation.
+ *
+ * Uses 'alloc' as the starting allocation, and may extend it as necessary.
+ * 'alloc' is never freed, even on failure of the wrapped function.
+ *
+ * The function is expected to return ERANGE if and only if the provided
+ * allocation is not big enough. All other values are passed through.
+ */
+static int
+alloc_wrap(struct alloc_wrap_data *const data, struct alloc *alloc)
+{
+ int error;
+
+ /* Avoid a systematic ERANGE on first iteration. */
+ if (alloc_is_empty(alloc))
+ alloc_realloc(alloc);
+
+ for (;;) {
+ error = data->func(data, alloc);
+ if (error != ERANGE)
+ break;
+ alloc_realloc(alloc);
+ }
+
+ return (error);
+}
+
+struct getpwnam_wrapper_data {
+ struct alloc_wrap_data wrapped;
+ const char *name;
+ struct passwd **pwdp;
+};
+
+static int
+wrapped_getpwnam_r(void *data, const struct alloc *alloc)
+{
+ struct passwd *const pwd = alloc->start;
+ struct passwd *result;
+ struct getpwnam_wrapper_data *d = data;
+ int error;
+
+ assert(alloc->size >= sizeof(*pwd));
+
+ error = getpwnam_r(d->name, pwd, (char *)(pwd + 1),
+ alloc->size - sizeof(*pwd), &result);
+
+ if (error == 0) {
+ if (result == NULL)
+ error = ENOENT;
+ } else
+ assert(result == NULL);
+ *d->pwdp = result;
+ return (error);
+}
+
+/*
+ * Wraps getpwnam_r(), automatically dealing with memory allocation.
+ *
+ * 'alloc' may be any allocation (even empty), and will be extended as
+ * necessary. It is not freed on error.
+ *
+ * On success, '*pwdp' is filled with a pointer to the returned 'struct passwd',
+ * and on failure, is set to NULL.
+ */
+static int
+alloc_getpwnam(const char *name, struct passwd **pwdp,
+ struct alloc *const alloc)
+{
+ struct getpwnam_wrapper_data data;
+
+ data.wrapped.func = wrapped_getpwnam_r;
+ data.name = name;
+ data.pwdp = pwdp;
+ return (alloc_wrap((struct alloc_wrap_data *)&data, alloc));
+}
+
+struct getgrnam_wrapper_data {
+ struct alloc_wrap_data wrapped;
+ const char *name;
+ struct group **grpp;
+};
+
+static int
+wrapped_getgrnam_r(void *data, const struct alloc *alloc)
+{
+ struct group *grp = alloc->start;
+ struct group *result;
+ struct getgrnam_wrapper_data *d = data;
+ int error;
+
+ assert(alloc->size >= sizeof(*grp));
+
+ error = getgrnam_r(d->name, grp, (char *)(grp + 1),
+ alloc->size - sizeof(*grp), &result);
+
+ if (error == 0) {
+ if (result == NULL)
+ error = ENOENT;
+ } else
+ assert(result == NULL);
+ *d->grpp = result;
+ return (error);
+}
+
+/*
+ * Wraps getgrnam_r(), automatically dealing with memory allocation.
+ *
+ * 'alloc' may be any allocation (even empty), and will be extended as
+ * necessary. It is not freed on error.
+ *
+ * On success, '*grpp' is filled with a pointer to the returned 'struct group',
+ * and on failure, is set to NULL.
+ */
+static int
+alloc_getgrnam(const char *const name, struct group **const grpp,
+ struct alloc *const alloc)
+{
+ struct getgrnam_wrapper_data data;
+
+ data.wrapped.func = wrapped_getgrnam_r;
+ data.name = name;
+ data.grpp = grpp;
+ return (alloc_wrap((struct alloc_wrap_data *)&data, alloc));
+}
+
+/*
+ * Retrieve the UID from a user string.
+ *
+ * Tries first to interpret the string as a user name, then as a numeric ID
+ * (this order is prescribed by POSIX for a number of utilities).
+ *
+ * 'pwdp' and 'allocp' must be NULL or non-NULL together. If non-NULL, then
+ * 'allocp' can be any allocation (possibly empty) and will be extended to
+ * contain the result if necessary. It will not be freed (even on failure).
+ */
+static uid_t
+parse_user_pwd(const char *s, struct passwd **pwdp, struct alloc *allocp)
+{
+ struct passwd *pwd;
+ struct alloc alloc = ALLOC_INITIALIZER;
+ const char *errp;
+ uid_t uid;
+ int error;
+
+ assert((pwdp == NULL && allocp == NULL) ||
+ (pwdp != NULL && allocp != NULL));
+
+ if (pwdp == NULL) {
+ pwdp = &pwd;
+ allocp = &alloc;
+ }
+
+ error = alloc_getpwnam(s, pwdp, allocp);
+ if (error == 0) {
+ uid = (*pwdp)->pw_uid;
+ goto finish;
+ } else if (error != ENOENT)
+ errc(EXIT_FAILURE, error,
+ "cannot access the password database");
+
+ uid = strtonum(s, 0, UID_MAX, &errp);
+ if (errp != NULL)
+ errx(EXIT_FAILURE, "invalid UID '%s': %s", s, errp);
+
+finish:
+ if (allocp == &alloc)
+ alloc_free(allocp);
+ return (uid);
+}
+
+/* See parse_user_pwd() for the doc. */
+static uid_t
+parse_user(const char *s)
+{
+ return (parse_user_pwd(s, NULL, NULL));
+}
+
+/*
+ * Retrieve the GID from a group string.
+ *
+ * Tries first to interpret the string as a group name, then as a numeric ID
+ * (this order is prescribed by POSIX for a number of utilities).
+ */
+static gid_t
+parse_group(const char *s)
+{
+ struct group *grp;
+ struct alloc alloc = ALLOC_INITIALIZER;
+ const char *errp;
+ gid_t gid;
+ int error;
+
+ error = alloc_getgrnam(s, &grp, &alloc);
+ if (error == 0) {
+ gid = grp->gr_gid;
+ goto finish;
+ } else if (error != ENOENT)
+ errc(EXIT_FAILURE, error, "cannot access the group database");
+
+ gid = strtonum(s, 0, GID_MAX, &errp);
+ if (errp != NULL)
+ errx(EXIT_FAILURE, "invalid GID '%s': %s", s, errp);
+
+finish:
+ alloc_free(&alloc);
+ return (gid);
+}
+
+struct group_array {
+ u_int nb;
+ gid_t *groups;
+};
+
+static const struct group_array GROUP_ARRAY_INITIALIZER = {
+ .nb = 0,
+ .groups = NULL,
+};
+
+static bool
+group_array_is_empty(const struct group_array *const ga)
+{
+ return (ga->nb == 0);
+}
+
+static void
+realloc_groups(struct group_array *const ga, const u_int diff)
+{
+ const u_int new_nb = ga->nb + diff;
+ const size_t new_size = new_nb * sizeof(*ga->groups);
+
+ assert(new_nb >= diff && new_size >= new_nb);
+ ga->groups = realloc(ga->groups, new_size);
+ if (ga->groups == NULL)
+ err(EXIT_FAILURE, "realloc of groups failed");
+ ga->nb = new_nb;
+}
+
+static int
+gidp_cmp(const void *p1, const void *p2)
+{
+ const gid_t g1 = *(const gid_t *)p1;
+ const gid_t g2 = *(const gid_t *)p2;
+
+ return ((g1 > g2) - (g1 < g2));
+}
+
+static void
+sort_uniq_groups(struct group_array *const ga)
+{
+ size_t j = 0;
+
+ if (ga->nb <= 1)
+ return;
+
+ qsort(ga->groups, ga->nb, sizeof(gid_t), gidp_cmp);
+
+ for (size_t i = 1; i < ga->nb; ++i)
+ if (ga->groups[i] != ga->groups[j])
+ ga->groups[++j] = ga->groups[i];
+}
+
+/*
+ * Remove elements in 'set' that are in 'remove'.
+ *
+ * Expects both arrays to have been treated with sort_uniq_groups(). Works in
+ * O(n + m), modifying 'set' in place.
+ */
+static void
+remove_groups(struct group_array *const set,
+ const struct group_array *const remove)
+{
+ u_int from = 0, to = 0, rem = 0;
+ gid_t cand, to_rem;
+
+ if (set->nb == 0 || remove->nb == 0)
+ /* Nothing to remove. */
+ return;
+
+ cand = set->groups[0];
+ to_rem = remove->groups[0];
+
+ for (;;) {
+ if (cand < to_rem) {
+ /* Keep. */
+ if (to != from)
+ set->groups[to] = cand;
+ ++to;
+ cand = set->groups[++from];
+ if (from == set->nb)
+ break;
+ } else if (cand == to_rem) {
+ cand = set->groups[++from];
+ if (from == set->nb)
+ break;
+ to_rem = remove->groups[++rem]; /* No duplicates. */
+ if (rem == remove->nb)
+ break;
+ } else {
+ to_rem = remove->groups[++rem];
+ if (rem == remove->nb)
+ break;
+ }
+ }
+
+ /* All remaining groups in 'set' must be kept. */
+ if (from == to)
+ /* Nothing was removed. 'set' will stay the same. */
+ return;
+ memmove(set->groups + to, set->groups + from,
+ (set->nb - from) * sizeof(gid_t));
+ set->nb = to + (set->nb - from);
}
int
main(int argc, char **argv)
{
- struct passwd *pw;
- const char *username = "root";
+ const char *const default_user = "root";
+
+ const char *user_name = NULL;
+ const char *primary_group = NULL;
+ char *supp_groups_str = NULL;
+ char *supp_mod_str = NULL;
+ bool start_from_current_groups = false;
+ bool start_from_current_users = false;
+ const char *euid_str = NULL;
+ const char *ruid_str = NULL;
+ const char *svuid_str = NULL;
+ const char *egid_str = NULL;
+ const char *rgid_str = NULL;
+ const char *svgid_str = NULL;
+ bool need_user = false; /* '-u' or '-k' needed. */
+
+ const int go_euid = 1000;
+ const int go_ruid = 1001;
+ const int go_svuid = 1002;
+ const int go_egid = 1003;
+ const int go_rgid = 1004;
+ const int go_svgid = 1005;
+ const struct option longopts[] = {
+ {"euid", required_argument, NULL, go_euid},
+ {"ruid", required_argument, NULL, go_ruid},
+ {"svuid", required_argument, NULL, go_svuid},
+ {"egid", required_argument, NULL, go_egid},
+ {"rgid", required_argument, NULL, go_rgid},
+ {"svgid", required_argument, NULL, go_svgid},
+ {NULL, 0, NULL, 0}
+ };
+ int ch;
+
struct setcred wcred = SETCRED_INITIALIZER;
u_int setcred_flags = 0;
- bool uidonly = false;
- int ch;
- while ((ch = getopt(argc, argv, "u:i")) != -1) {
+ struct passwd *pw = NULL;
+ struct alloc pw_alloc = ALLOC_INITIALIZER;
+ struct group_array supp_groups = GROUP_ARRAY_INITIALIZER;
+ struct group_array supp_rem = GROUP_ARRAY_INITIALIZER;
+
+
+ /*
+ * Process options.
+ */
+ while (ch = getopt_long(argc, argv, "+G:g:hiks:u:", longopts, NULL),
+ ch != -1) {
switch (ch) {
- case 'u':
- username = optarg;
+ case 'G':
+ supp_groups_str = optarg;
+ need_user = true;
+ break;
+ case 'g':
+ primary_group = optarg;
+ need_user = true;
break;
+ case 'h':
+ usage();
case 'i':
- uidonly = true;
+ start_from_current_groups = true;
+ break;
+ case 'k':
+ start_from_current_users = true;
+ break;
+ case 's':
+ supp_mod_str = optarg;
+ need_user = true;
+ break;
+ case 'u':
+ user_name = optarg;
+ break;
+ case go_euid:
+ euid_str = optarg;
+ need_user = true;
+ break;
+ case go_ruid:
+ ruid_str = optarg;
+ need_user = true;
+ break;
+ case go_svuid:
+ svuid_str = optarg;
+ need_user = true;
+ break;
+ case go_egid:
+ egid_str = optarg;
+ need_user = true;
+ break;
+ case go_rgid:
+ rgid_str = optarg;
+ need_user = true;
+ break;
+ case go_svgid:
+ svgid_str = optarg;
+ need_user = true;
break;
default:
usage();
}
}
+
argc -= optind;
argv += optind;
- if ((pw = getpwnam(username)) == NULL) {
- if (strspn(username, "0123456789") == strlen(username)) {
- const char *errp = NULL;
- uid_t uid = strtonum(username, 0, UID_MAX, &errp);
- if (errp != NULL)
- err(EXIT_FAILURE, "invalid user ID '%s'",
- username);
- pw = getpwuid(uid);
+ /*
+ * Determine users.
+ *
+ * We do that first as in some cases we need to retrieve the
+ * corresponding password database entry to be able to set the primary
+ * groups.
+ */
+
+ if (start_from_current_users) {
+ if (user_name != NULL)
+ errx(EXIT_FAILURE, "-k incompatible with -u");
+
+ /*
+ * If starting from the current user(s) as a base, finding one
+ * of them in the password database and using its groups would
+ * be quite surprising, so we instead let '-k' imply '-i'.
+ */
+ start_from_current_groups = true;
+ } else {
+ uid_t uid;
+
+ /*
+ * In the case of any overrides, we impose an explicit base user
+ * via '-u' or '-k' instead of implicitly taking 'root' as the
+ * base.
+ */
+ if (user_name == NULL) {
+ if (need_user)
+ errx(EXIT_FAILURE,
+ "Some overrides specified, "
+ "'-u' or '-k' needed.");
+ user_name = default_user;
}
+
+ /*
+ * Even if all user overrides are present as well as primary and
+ * supplementary groups ones, in which case the final result
+ * doesn't depend on '-u', we still call parse_user_pwd() to
+ * check that the passed username is correct.
+ */
+ uid = parse_user_pwd(user_name, &pw, &pw_alloc);
+ wcred.sc_uid = wcred.sc_ruid = wcred.sc_svuid = uid;
+ setcred_flags |= SETCREDF_UID | SETCREDF_RUID |
+ SETCREDF_SVUID;
+ }
+
+ if (euid_str != NULL) {
+ wcred.sc_uid = parse_user(euid_str);
+ setcred_flags |= SETCREDF_UID;
+ }
+
+ if (ruid_str != NULL) {
+ wcred.sc_ruid = parse_user(ruid_str);
+ setcred_flags |= SETCREDF_RUID;
+ }
+
+ if (svuid_str != NULL) {
+ wcred.sc_svuid = parse_user(svuid_str);
+ setcred_flags |= SETCREDF_SVUID;
+ }
+
+ /*
+ * Determine primary groups.
+ */
+
+ /*
+ * When not starting from the current groups, we need to set all
+ * primary groups. If '-g' was not passed, we use the primary
+ * group from the password database as the "base" to which
+ * overrides '--egid', '--rgid' and '--svgid' apply. But if all
+ * overrides were specified, we in fact just don't need the
+ * password database at all.
+ *
+ * '-g' is treated outside of this 'if' as it can also be used
+ * as an override.
+ */
+ if (!start_from_current_groups && primary_group == NULL &&
+ (egid_str == NULL || rgid_str == NULL || svgid_str == NULL)) {
if (pw == NULL)
- err(EXIT_FAILURE, "invalid username '%s'", username);
+ errx(EXIT_FAILURE,
+ "must specify primary groups or a user name "
+ "with an entry in the password database");
+
+ wcred.sc_gid = wcred.sc_rgid = wcred.sc_svgid =
+ pw->pw_gid;
+ setcred_flags |= SETCREDF_GID | SETCREDF_RGID |
+ SETCREDF_SVGID;
+ }
+
+ if (primary_group != NULL) {
+ /*
+ * We always call parse_group() even in case all overrides are
+ * present to check that the passed group is valid.
+ */
+ wcred.sc_gid = wcred.sc_rgid = wcred.sc_svgid =
+ parse_group(primary_group);
+ setcred_flags |= SETCREDF_GID | SETCREDF_RGID | SETCREDF_SVGID;
+ }
+
+ if (egid_str != NULL) {
+ wcred.sc_gid = parse_group(egid_str);
+ setcred_flags |= SETCREDF_GID;
+ }
+
+ if (rgid_str != NULL) {
+ wcred.sc_rgid = parse_group(rgid_str);
+ setcred_flags |= SETCREDF_RGID;
+ }
+
+ if (svgid_str != NULL) {
+ wcred.sc_svgid = parse_group(svgid_str);
+ setcred_flags |= SETCREDF_SVGID;
+ }
+
+ /*
+ * Determine supplementary groups.
+ */
+
+ /*
+ * This makes sense to catch user's mistakes. It is not a strong
+ * limitation of the code below (allowing this case is just a matter of,
+ * in the block treating '-s' with '@' below, replacing an assert() by
+ * a reset of 'supp_groups').
+ */
+ if (supp_groups_str != NULL && supp_mod_str != NULL &&
+ supp_mod_str[0] == '@')
+ errx(EXIT_FAILURE, "'-G' and '-s' with '@' are incompatible");
+
+ /*
+ * Determine the supplementary groups to start with, but only if we
+ * really need to operate on them later (and set them back).
+ */
+ if (!start_from_current_groups) {
+ assert(!start_from_current_users);
+
+ if (supp_groups_str == NULL && (supp_mod_str == NULL ||
+ supp_mod_str[0] != '@')) {
+ /*
+ * If we are to replace supplementary groups (i.e.,
+ * neither '-i' nor '-k' was specified) and they are not
+ * completely specified (with '-g' or '-s' with '@'), we
+ * start from those in the groups database if we were
+ * passed a user name that is in the password database
+ * (this is a protection against erroneous ID/name
+ * conflation in the groups database), else we simply
+ * error.
+ */
+
+ if (pw == NULL)
+ errx(EXIT_FAILURE,
+ "must specify the full supplementary "
+ "groups set or a user name with an entry "
+ "in the password database");
+
+ const long ngroups_alloc = sysconf(_SC_NGROUPS_MAX) + 1;
+ gid_t *groups;
+ int ngroups;
+
+ groups = malloc(sizeof(*groups) * ngroups_alloc);
+ if (groups == NULL)
+ errx(EXIT_FAILURE,
+ "cannot allocate memory to retrieve "
+ "user groups from the groups database");
+
+ ngroups = ngroups_alloc;
+ getgrouplist(user_name, pw->pw_gid, groups, &ngroups);
+
+ if (ngroups > ngroups_alloc)
+ err(EXIT_FAILURE,
+ "too many groups for user '%s'",
+ user_name);
+
+ realloc_groups(&supp_groups, ngroups);
+ memcpy(supp_groups.groups + supp_groups.nb - ngroups,
+ groups, ngroups * sizeof(*groups));
+ free(groups);
+
+ /*
+ * Have to set SETCREDF_SUPP_GROUPS here since we may be
+ * in the case where neither '-G' nor '-s' was passed,
+ * but we still have to set the supplementary groups to
+ * those of the groups database.
+ */
+ setcred_flags |= SETCREDF_SUPP_GROUPS;
+ }
+ } else if (supp_groups_str == NULL && (supp_mod_str == NULL ||
+ supp_mod_str[0] != '@')) {
+ const int ngroups = getgroups(0, NULL);
+
+ if (ngroups > 0) {
+ realloc_groups(&supp_groups, ngroups);
+
+ if (getgroups(ngroups, supp_groups.groups +
+ supp_groups.nb - ngroups) < 0)
+ err(EXIT_FAILURE, "getgroups() failed");
+ }
+
+ /*
+ * Setting SETCREDF_SUPP_GROUPS here is not necessary, we will
+ * do it below since 'supp_mod_str' != NULL.
+ */
}
- wcred.sc_uid = wcred.sc_ruid = wcred.sc_svuid = pw->pw_uid;
- setcred_flags |= SETCREDF_UID | SETCREDF_RUID | SETCREDF_SVUID;
+ if (supp_groups_str != NULL) {
+ char *p = supp_groups_str;
+ char *tok;
- if (!uidonly) {
/*
- * If there are too many groups specified for some UID, setting
- * the groups will fail. We preserve this condition by
- * allocating one more group slot than allowed, as
- * getgrouplist() itself is just some getter function and thus
- * doesn't (and shouldn't) check the limit, and to allow
- * setcred() to actually check for overflow.
+ * We will set the supplementary groups to exactly the set
+ * passed with '-G', and we took care above not to retrieve
+ * "base" groups (current ones or those from the groups
+ * database) in this case.
*/
- const long ngroups_alloc = sysconf(_SC_NGROUPS_MAX) + 2;
- gid_t *const groups = malloc(sizeof(*groups) * ngroups_alloc);
- int ngroups = ngroups_alloc;
+ assert(group_array_is_empty(&supp_groups));
+
+ /* WARNING: 'supp_groups_str' going to be modified. */
+ while ((tok = strsep(&p, ",")) != NULL) {
+ gid_t g;
+
+ if (*tok == '\0')
+ continue;
+
+ g = parse_group(tok);
+ realloc_groups(&supp_groups, 1);
+ supp_groups.groups[supp_groups.nb - 1] = g;
+ }
+
+ setcred_flags |= SETCREDF_SUPP_GROUPS;
+ }
+
+ if (supp_mod_str != NULL) {
+ char *p = supp_mod_str;
+ char *tok;
+ gid_t gid;
- if (groups == NULL)
- err(EXIT_FAILURE, "cannot allocate memory for groups");
+ /* WARNING: 'supp_mod_str' going to be modified. */
+ while ((tok = strsep(&p, ",")) != NULL) {
+ switch (tok[0]) {
+ case '\0':
+ break;
- getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
+ case '@':
+ if (tok != supp_mod_str)
+ errx(EXIT_FAILURE, "'@' must be "
+ "the first token in '-s' option");
+ /* See same assert() above. */
+ assert(group_array_is_empty(&supp_groups));
+ break;
+
+ case '+':
+ case '-':
+ gid = parse_group(tok + 1);
+ if (tok[0] == '+') {
+ realloc_groups(&supp_groups, 1);
+ supp_groups.groups[supp_groups.nb - 1] = gid;
+ } else {
+ realloc_groups(&supp_rem, 1);
+ supp_rem.groups[supp_rem.nb - 1] = gid;
+ }
+ break;
+
+ default:
+ errx(EXIT_FAILURE,
+ "invalid '-s' token '%s' at index %zu",
+ tok, tok - supp_mod_str);
+ }
+ }
- wcred.sc_gid = wcred.sc_rgid = wcred.sc_svgid = pw->pw_gid;
- wcred.sc_supp_groups = groups + 1;
- wcred.sc_supp_groups_nb = ngroups - 1;
- setcred_flags |= SETCREDF_GID | SETCREDF_RGID | SETCREDF_SVGID |
- SETCREDF_SUPP_GROUPS;
+ setcred_flags |= SETCREDF_SUPP_GROUPS;
+ }
+
+ /*
+ * We don't need to pass the kernel a normalized representation of the
+ * new supplementary groups set (array sorted and without duplicates),
+ * so we don't do it here unless we need to remove some groups, where it
+ * enables more efficient algorithms (if the number of groups for some
+ * reason grows out of control).
+ */
+ if (!group_array_is_empty(&supp_groups) &&
+ !group_array_is_empty(&supp_rem)) {
+ sort_uniq_groups(&supp_groups);
+ sort_uniq_groups(&supp_rem);
+ remove_groups(&supp_groups, &supp_rem);
+ }
+
+ if ((setcred_flags & SETCREDF_SUPP_GROUPS) != 0) {
+ wcred.sc_supp_groups = supp_groups.groups;
+ wcred.sc_supp_groups_nb = supp_groups.nb;
}
if (setcred(setcred_flags, &wcred, sizeof(wcred)) != 0)
- err(EXIT_FAILURE, "calling setcred() failed");
+ err(EXIT_FAILURE, "setcred()");
+
+ /*
+ * We don't bother freeing memory still allocated at this point as we
+ * are about to exec() or exit.
+ */
if (*argv == NULL) {
const char *sh = getenv("SHELL");
+
if (sh == NULL)
sh = _PATH_BSHELL;
execlp(sh, sh, "-i", NULL);
diff --git a/usr.bin/mididump/Makefile b/usr.bin/mididump/Makefile
index 758bbb3a1189..5b22376b7bb8 100644
--- a/usr.bin/mididump/Makefile
+++ b/usr.bin/mididump/Makefile
@@ -1,5 +1,7 @@
.include <src.opts.mk>
+PACKAGE= sound
+
PROG= mididump
SRCS= ${PROG}.c
MAN= ${PROG}.1
diff --git a/usr.bin/mkimg/mkimg.c b/usr.bin/mkimg/mkimg.c
index a7409b686560..27b79b82ca02 100644
--- a/usr.bin/mkimg/mkimg.c
+++ b/usr.bin/mkimg/mkimg.c
@@ -142,8 +142,10 @@ static void
usage(const char *why)
{
- warnx("error: %s", why);
- fputc('\n', stderr);
+ if (why != NULL) {
+ warnx("error: %s", why);
+ fputc('\n', stderr);
+ }
fprintf(stderr, "usage: %s <options>\n", getprogname());
fprintf(stderr, " options:\n");
@@ -171,19 +173,19 @@ usage(const char *why)
print_schemes(1);
fputc('\n', stderr);
fprintf(stderr, " partition specification:\n");
- fprintf(stderr, "\t<t>[/<l>]::<size>[:[+]<offset>]\t- "
+ fprintf(stderr, "\t<type>[/<label>]::<size>[:[+]<offset>]\t- "
"empty partition of given size and\n\t\t\t\t\t"
" optional relative or absolute offset\n");
- fprintf(stderr, "\t<t>[/<l>]:=<file>[:[+]offset]\t- partition "
+ fprintf(stderr, "\t<type>[/<label>]:=<file>[:[+]offset]\t- partition "
"content and size are\n\t\t\t\t\t"
" determined by the named file and\n"
"\t\t\t\t\t optional relative or absolute offset\n");
- fprintf(stderr, "\t<t>[/<l>]:-<cmd>\t\t- partition content and size "
+ fprintf(stderr, "\t<type>[/<label>]:-<cmd>\t\t- partition content and size "
"are taken\n\t\t\t\t\t from the output of the command to run\n");
fprintf(stderr, "\t-\t\t\t\t- unused partition entry\n");
fprintf(stderr, "\t where:\n");
- fprintf(stderr, "\t\t<t>\t- scheme neutral partition type\n");
- fprintf(stderr, "\t\t<l>\t- optional scheme-dependent partition "
+ fprintf(stderr, "\t\t<type>\t- scheme neutral partition type\n");
+ fprintf(stderr, "\t\t<label>\t- optional scheme-dependent partition "
"label\n");
exit(EX_USAGE);
@@ -564,7 +566,7 @@ main(int argc, char *argv[])
bcfd = -1;
outfd = 1; /* Write to stdout by default */
- while ((c = getopt_long(argc, argv, "a:b:c:C:f:o:p:s:t:vyH:P:S:T:",
+ while ((c = getopt_long(argc, argv, "a:b:c:C:f:ho:p:s:t:vyH:P:S:T:",
longopts, NULL)) != -1) {
switch (c) {
case 'a': /* ACTIVE PARTITION, if supported */
@@ -596,6 +598,9 @@ main(int argc, char *argv[])
if (error)
errc(EX_DATAERR, error, "format");
break;
+ case 'h': /* HELP */
+ usage(NULL);
+ break;
case 'o': /* OUTPUT FILE */
if (outfd != 1)
usage("multiple output files given");
diff --git a/usr.bin/mktemp/mktemp.1 b/usr.bin/mktemp/mktemp.1
index 063f25f216dc..3b8381c0586c 100644
--- a/usr.bin/mktemp/mktemp.1
+++ b/usr.bin/mktemp/mktemp.1
@@ -27,7 +27,7 @@
.\"
.\" From: $OpenBSD: mktemp.1,v 1.8 1998/03/19 06:13:37 millert Exp $
.\"
-.Dd August 4, 2022
+.Dd September 27, 2025
.Dt MKTEMP 1
.Os
.Sh NAME
@@ -185,6 +185,13 @@ but still introduces a race condition.
Use of this
option is not encouraged.
.El
+.Sh ENVIRONMENT
+.Bl -tag -width TMPDIR
+.It Ev TMPDIR
+The directory in which to store temporary files.
+Refer to
+.Xr environ 7 .
+.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
@@ -200,7 +207,8 @@ TMPFILE=`mktemp /tmp/${tempfoo}.XXXXXXXXXX` || exit 1
echo "program output" >> $TMPFILE
.Ed
.Pp
-To allow the use of $TMPDIR:
+To allow the use of
+.Ev TMPDIR :
.Bd -literal -offset indent
tempfoo=`basename $0`
TMPFILE=`mktemp -t ${tempfoo}` || exit 1
diff --git a/usr.bin/ncurses/Makefile b/usr.bin/ncurses/Makefile
index 33001e6ab568..1ed8a2d9a915 100644
--- a/usr.bin/ncurses/Makefile
+++ b/usr.bin/ncurses/Makefile
@@ -1,4 +1,4 @@
-PACKAGE= runtime
+PACKAGE= ncurses
.include <bsd.own.mk>
.include "${SRCTOP}/lib/ncurses/config.mk"
diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c
index 1603c7662bbd..7ee03eb3689b 100644
--- a/usr.bin/netstat/if.c
+++ b/usr.bin/netstat/if.c
@@ -84,8 +84,10 @@ static const char* pfsyncacts[] = {
/* PFSYNC_ACT_BUS */ "bulk update mark",
/* PFSYNC_ACT_TDB */ "TDB replay counter update",
/* PFSYNC_ACT_EOF */ "end of frame mark",
- /* PFSYNC_ACT_INS_1400 */ "state insert",
- /* PFSYNC_ACT_UPD_1400 */ "state update",
+ /* PFSYNC_ACT_INS_1400 */ "14.0 state insert",
+ /* PFSYNC_ACT_UPD_1400 */ "14.0 state update",
+ /* PFSYNC_ACT_INS_1500 */ "state insert",
+ /* PFSYNC_ACT_UPD_1500 */ "state update",
};
static const char* pfsyncacts_name[] = {
@@ -102,8 +104,10 @@ static const char* pfsyncacts_name[] = {
/* PFSYNC_ACT_BUS */ "bulk-update-mark",
/* PFSYNC_ACT_TDB */ "TDB-replay-counter-update",
/* PFSYNC_ACT_EOF */ "end-of-frame-mark",
- /* PFSYNC_ACT_INS_1400 */ "state-insert",
- /* PFSYNC_ACT_UPD_1400 */ "state-update",
+ /* PFSYNC_ACT_INS_1400 */ "state-insert-1400",
+ /* PFSYNC_ACT_UPD_1400 */ "state-update-1400",
+ /* PFSYNC_ACT_INS_1500 */ "state-insert",
+ /* PFSYNC_ACT_UPD_1500 */ "state-update",
};
static void
@@ -278,7 +282,8 @@ next_ifma(struct ifmaddrs *ifma, const char *name, const sa_family_t family)
sdl = (struct sockaddr_dl *)ifma->ifma_name;
if (ifma->ifma_addr->sa_family == family &&
- strcmp(sdl->sdl_data, name) == 0)
+ sdl->sdl_nlen == strlen(name) &&
+ strncmp(sdl->sdl_data, name, sdl->sdl_nlen) == 0)
break;
}
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
index 7014f02032c2..dee245b63a87 100644
--- a/usr.bin/netstat/inet.c
+++ b/usr.bin/netstat/inet.c
@@ -83,7 +83,7 @@ static void inetprint(const char *, struct in_addr *, int, const char *, int,
const int);
#endif
#ifdef INET6
-static int udp_done, tcp_done, sdp_done;
+static int udp_done, udplite_done, tcp_done, sdp_done;
#endif /* INET6 */
static int
@@ -100,6 +100,9 @@ pcblist_sysctl(int proto, const char *name, char **bufp)
case IPPROTO_UDP:
mibvar = "net.inet.udp.pcblist";
break;
+ case IPPROTO_UDPLITE:
+ mibvar = "net.inet.udplite.pcblist";
+ break;
default:
mibvar = "net.inet.raw.pcblist";
break;
@@ -222,11 +225,18 @@ protopr(u_long off, const char *name, int af1, int proto)
udp_done = 1;
#endif
break;
+ case IPPROTO_UDPLITE:
+#ifdef INET6
+ if (udplite_done != 0)
+ return;
+ else
+ udplite_done = 1;
+#endif
+ break;
}
if (!pcblist_sysctl(proto, name, &buf))
return;
-
if (istcp && (cflag || Cflag)) {
fnamelen = strlen("Stack");
cnamelen = strlen("CC");
@@ -318,18 +328,18 @@ protopr(u_long off, const char *name, int af1, int proto)
"Proto", "Listen", "Local Address");
else if (Tflag)
xo_emit((Aflag && !Wflag) ?
- "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-18.18s} {T:/%s}" :
+ "{T:/%-9.9s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-18.18s} {T:/%s}" :
((!Wflag || af1 == AF_INET) ?
- "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-22.22s} {T:/%s}" :
- "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-45.45s} {T:/%s}"),
+ "{T:/%-9.9s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-22.22s} {T:/%s}" :
+ "{T:/%-9.9s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-45.45s} {T:/%s}"),
"Proto", "Rexmit", "OOORcv", "0-win",
"Local Address", "Foreign Address");
else {
xo_emit((Aflag && !Wflag) ?
- "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-18.18s} {T:/%-18.18s}" :
+ "{T:/%-9.9s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-18.18s} {T:/%-18.18s}" :
((!Wflag || af1 == AF_INET) ?
- "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-22.22s} {T:/%-22.22s}" :
- "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-45.45s} {T:/%-45.45s}"),
+ "{T:/%-9.9s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-22.22s} {T:/%-22.22s}" :
+ "{T:/%-9.9s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-45.45s} {T:/%-45.45s}"),
"Proto", "Recv-Q", "Send-Q",
"Local Address", "Foreign Address");
if (!xflag && !Rflag)
@@ -382,9 +392,14 @@ protopr(u_long off, const char *name, int af1, int proto)
vchar = ((inp->inp_vflag & INP_IPV4) != 0) ?
"4" : "";
if (istcp && (tp->t_flags & TF_TOE) != 0)
- xo_emit("{:protocol/%-3.3s%-2.2s/%s%s} ", "toe", vchar);
- else
- xo_emit("{:protocol/%-3.3s%-2.2s/%s%s} ", name, vchar);
+ xo_emit("{:protocol/%-3.3s%-6.6s/%s%s} ", "toe", vchar);
+ else {
+ int len;
+
+ len = max (2, 9 - strlen(name));
+ xo_emit("{:protocol/%.7s%-*.*s/%s%s} ", name, len, len,
+ vchar);
+ }
if (Lflag) {
char buf1[33];
@@ -767,15 +782,20 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
p1a(tcps_sc_badack, "\t\t{:bad-ack/%ju} {N:/badack}\n");
p1a(tcps_sc_unreach, "\t\t{:unreachable/%ju} {N:/unreach}\n");
p(tcps_sc_zonefail, "\t\t{:zone-failures/%ju} {N:/zone failure%s}\n");
+
+ xo_close_container("syncache");
+
+ xo_open_container("syncookies");
+
p(tcps_sc_sendcookie, "\t{:sent-cookies/%ju} {N:/cookie%s sent}\n");
- p(tcps_sc_recvcookie, "\t{:received-cookies/%ju} "
+ p(tcps_sc_recvcookie, "\t\t{:received-cookies/%ju} "
"{N:/cookie%s received}\n");
- p(tcps_sc_spurcookie, "\t{:spurious-cookies/%ju} "
+ p(tcps_sc_spurcookie, "\t\t{:spurious-cookies/%ju} "
"{N:/spurious cookie%s rejected}\n");
- p(tcps_sc_failcookie, "\t{:failed-cookies/%ju} "
+ p(tcps_sc_failcookie, "\t\t{:failed-cookies/%ju} "
"{N:/failed cookie%s rejected}\n");
- xo_close_container("syncache");
+ xo_close_container("syncookies");
xo_open_container("hostcache");
diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c
index e8f657006982..79830049948a 100644
--- a/usr.bin/netstat/main.c
+++ b/usr.bin/netstat/main.c
@@ -83,6 +83,8 @@ static struct protox {
tcp_stats, NULL, "tcp", 1, IPPROTO_TCP },
{ -1 , N_UDPSTAT, 1, protopr,
udp_stats, NULL, "udp", 1, IPPROTO_UDP },
+ { -1, -1, 1, protopr,
+ NULL, NULL, "udplite", 1, IPPROTO_UDPLITE },
#ifdef SCTP
{ -1, N_SCTPSTAT, 1, sctp_protopr,
sctp_stats, NULL, "sctp", 1, IPPROTO_SCTP },
@@ -131,6 +133,8 @@ static struct protox ip6protox[] = {
tcp_stats, NULL, "tcp", 1, IPPROTO_TCP },
{ -1 , N_UDPSTAT, 1, protopr,
udp_stats, NULL, "udp", 1, IPPROTO_UDP },
+ { -1, -1, 1, protopr,
+ NULL, NULL, "udplite", 1, IPPROTO_UDPLITE },
{ -1 , N_IP6STAT, 1, protopr,
ip6_stats, ip6_ifstats, "ip6", 1, IPPROTO_RAW },
{ -1 , N_ICMP6STAT, 1, protopr,
diff --git a/usr.bin/netstat/sctp.c b/usr.bin/netstat/sctp.c
index c3abac407327..08cfc31c12c9 100644
--- a/usr.bin/netstat/sctp.c
+++ b/usr.bin/netstat/sctp.c
@@ -622,6 +622,10 @@ sctp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
"{N:/fast path receives all one chunk}\n");
p1a(sctps_recvexpressm, "\t\t{:receives-fast-path-multipart/%ju} "
"{N:/fast path multi-part data}\n");
+ p1a(sctps_recvswcrc, "\t\t{:performed-receive-crc32c-computation/%ju} "
+ "{N:/performed receive crc32c computation}\n");
+ p1a(sctps_recvhwcrc, "\t\t{:performed-receive-crc32c-offloading/%ju} "
+ "{N:/performed receive crc32c offloading}\n");
/*
* output statistics
@@ -648,6 +652,10 @@ sctp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
"{N:/output AUTH chunk%s}\n");
p1a(sctps_senderrors, "\t\t{:send-errors/%ju} "
"{N:/ip_output error counter}\n");
+ p1a(sctps_sendswcrc, "\t\t{:performed-receive-crc32c-computation/%ju} "
+ "{N:/performed transmit crc32c computation}\n");
+ p1a(sctps_sendhwcrc, "\t\t{:performed-transmit-crc32c-offloading/%ju} "
+ "{N:/performed transmit crc32c offloading}\n");
/*
* PCKDROPREP statistics
diff --git a/usr.bin/newgrp/newgrp.c b/usr.bin/newgrp/newgrp.c
index f1da1c8cb1f5..0971f4d13b49 100644
--- a/usr.bin/newgrp/newgrp.c
+++ b/usr.bin/newgrp/newgrp.c
@@ -186,7 +186,7 @@ addgroup(const char *grpname)
}
}
- ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1;
+ ngrps_max = sysconf(_SC_NGROUPS_MAX);
if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL)
err(1, "malloc");
if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) {
@@ -194,7 +194,12 @@ addgroup(const char *grpname)
goto end;
}
- /* Remove requested gid from supp. list if it exists. */
+ /*
+ * Remove requested gid from supp. list if it exists and doesn't match
+ * our prior egid -- this exception is to avoid providing the user a
+ * means to get rid of a group that could be used for, e.g., negative
+ * permissions.
+ */
if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) {
for (i = 0; i < ngrps; i++)
if (grps[i] == grp->gr_gid)
@@ -217,10 +222,9 @@ addgroup(const char *grpname)
goto end;
}
PRIV_END;
- grps[0] = grp->gr_gid;
/* Add old effective gid to supp. list if it does not exist. */
- if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) {
+ if (!inarray(egid, grps, ngrps)) {
if (ngrps == ngrps_max)
warnx("too many groups");
else {
diff --git a/usr.bin/patch/tests/unified_patch_test.sh b/usr.bin/patch/tests/unified_patch_test.sh
index 7d4b74182c41..a91332908773 100755
--- a/usr.bin/patch/tests/unified_patch_test.sh
+++ b/usr.bin/patch/tests/unified_patch_test.sh
@@ -141,6 +141,23 @@ file_removal_body()
atf_check -o inline:"y\n" cat foo
}
+atf_test_case namespace
+namespace_head()
+{
+ atf_set "descr" "Test that patch(1) handles files with spaces in the name"
+}
+namespace_body()
+{
+ echo "ABC" > "with spaces.orig"
+ echo "ZYX" > "with spaces"
+
+ atf_check -s not-exit:0 -o save:spaces.diff \
+ diff -u "with spaces.orig" "with spaces"
+
+ atf_check mv "with spaces.orig" "with spaces"
+ atf_check -o not-empty patch < spaces.diff
+}
+
atf_test_case plinelen
plinelen_body()
{
@@ -166,5 +183,6 @@ atf_init_test_cases()
atf_add_test_case file_creation
atf_add_test_case file_nodupe
atf_add_test_case file_removal
+ atf_add_test_case namespace
atf_add_test_case plinelen
}
diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c
index b5d28fd7c184..9ad4076cec40 100644
--- a/usr.bin/quota/quota.c
+++ b/usr.bin/quota/quota.c
@@ -100,8 +100,7 @@ static char *filename = NULL;
int
main(int argc, char *argv[])
{
- int ngroups;
- gid_t mygid, gidset[NGROUPS];
+ int ngroups;
int i, ch, gflag = 0, uflag = 0, errflag = 0;
while ((ch = getopt(argc, argv, "f:ghlrquv")) != -1) {
@@ -142,11 +141,15 @@ main(int argc, char *argv[])
if (uflag)
errflag += showuid(getuid());
if (gflag) {
+ gid_t mygid, myegid, gidset[NGROUPS_MAX];
+
mygid = getgid();
- ngroups = getgroups(NGROUPS, gidset);
+ errflag += showgid(mygid);
+ myegid = getegid();
+ errflag += showgid(myegid);
+ ngroups = getgroups(NGROUPS_MAX, gidset);
if (ngroups < 0)
err(1, "getgroups");
- errflag += showgid(mygid);
for (i = 0; i < ngroups; i++)
if (gidset[i] != mygid)
errflag += showgid(gidset[i]);
diff --git a/usr.bin/sockstat/main.c b/usr.bin/sockstat/main.c
index b5e0248b743a..1f174d827e1a 100644
--- a/usr.bin/sockstat/main.c
+++ b/usr.bin/sockstat/main.c
@@ -51,6 +51,7 @@
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_var.h>
+#include <netinet/tcp_log_buf.h>
#include <arpa/inet.h>
#include <capsicum_helpers.h>
@@ -84,8 +85,10 @@
static bool opt_4; /* Show IPv4 sockets */
static bool opt_6; /* Show IPv6 sockets */
static bool opt_A; /* Show kernel address of pcb */
+static bool opt_b; /* Show BBLog state */
static bool opt_C; /* Show congestion control */
static bool opt_c; /* Show connected sockets */
+static bool opt_F; /* Show sockets for selected user only */
static bool opt_f; /* Show FIB numbers */
static bool opt_I; /* Show spliced socket addresses */
static bool opt_i; /* Show inp_gencnt */
@@ -101,16 +104,24 @@ static bool opt_u; /* Show Unix domain sockets */
static u_int opt_v; /* Verbose mode */
static bool opt_w; /* Automatically size the columns */
static bool is_xo_style_encoding;
+static bool show_path_state = false;
/*
* Default protocols to use if no -P was defined.
*/
-static const char *default_protos[] = {"sctp", "tcp", "udp", "divert" };
+static const char *default_protos[] = {"sctp", "tcp", "udp", "udplite",
+ "divert" };
static size_t default_numprotos = nitems(default_protos);
static int *protos; /* protocols to use */
static size_t numprotos; /* allocated size of protos[] */
+/*
+ * Show sockets for user username or UID specified
+ */
+static char *filter_user_optarg = NULL; /* saved optarg for username/UID resolving */
+static uid_t filter_user_uid; /* UID to show sockets for */
+
struct addr {
union {
struct sockaddr_storage address;
@@ -141,6 +152,7 @@ struct sock {
int proto;
int state;
int fibnum;
+ int bblog_state;
const char *protoname;
char stack[TCP_FUNCTION_NAME_LEN_MAX];
char cc[TCP_CA_NAME_MAX];
@@ -212,6 +224,18 @@ _enforce_ksize(size_t received_size, size_t expected_size, const char *struct_na
}
#define enforce_ksize(_sz, _struct) (_enforce_ksize(_sz, sizeof(_struct), #_struct))
+static inline bool
+filtered_uid(uid_t i_uid)
+{
+ return ((i_uid) == filter_user_uid);
+}
+
+static inline bool
+need_nosocks(void)
+{
+ return !(opt_F || (opt_j >= 0));
+}
+
static int
get_proto_type(const char *proto)
{
@@ -581,6 +605,7 @@ gather_sctp(void)
!(local_all_loopback ||
foreign_all_loopback))) {
RB_INSERT(socks_t, &socks, sock);
+ show_path_state = true;
} else {
free_socket(sock);
}
@@ -621,6 +646,10 @@ gather_inet(int proto)
varname = "net.inet.udp.pcblist";
protoname = "udp";
break;
+ case IPPROTO_UDPLITE:
+ varname = "net.inet.udplite.pcblist";
+ protoname = "udplite";
+ break;
case IPPROTO_DIVERT:
varname = "net.inet.divert.pcblist";
protoname = "div";
@@ -669,6 +698,7 @@ gather_inet(int proto)
protoname = xtp->t_flags & TF_TOE ? "toe" : "tcp";
break;
case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
case IPPROTO_DIVERT:
xip = (struct xinpcb *)xig;
if (!check_ksize(xip->xi_len, struct xinpcb))
@@ -738,6 +768,7 @@ gather_inet(int proto)
sock->vflag = xip->inp_vflag;
if (proto == IPPROTO_TCP) {
sock->state = xtp->t_state;
+ sock->bblog_state = xtp->t_logstate;
memcpy(sock->stack, xtp->xt_stack,
TCP_FUNCTION_NAME_LEN_MAX);
memcpy(sock->cc, xtp->xt_cc, TCP_CA_NAME_MAX);
@@ -746,7 +777,8 @@ gather_inet(int proto)
if (sock->socket != 0)
RB_INSERT(socks_t, &socks, sock);
else
- SLIST_INSERT_HEAD(&nosocks, sock, socket_list);
+ if (need_nosocks())
+ SLIST_INSERT_HEAD(&nosocks, sock, socket_list);
}
out:
free(buf);
@@ -850,6 +882,8 @@ getfiles(void)
struct xfile *xfiles;
size_t len, olen;
+ int filenum = 0;
+
olen = len = sizeof(*xfiles);
if ((xfiles = malloc(len)) == NULL)
xo_err(1, "malloc()");
@@ -868,14 +902,23 @@ getfiles(void)
if ((files = malloc(nfiles * sizeof(struct file))) == NULL)
xo_err(1, "malloc()");
+ /* Fill files structure, optionally for specified user */
for (int i = 0; i < nfiles; i++) {
- files[i].xf_data = xfiles[i].xf_data;
- files[i].xf_pid = xfiles[i].xf_pid;
- files[i].xf_uid = xfiles[i].xf_uid;
- files[i].xf_fd = xfiles[i].xf_fd;
- RB_INSERT(files_t, &ftree, &files[i]);
+ if (opt_F && !filtered_uid(xfiles[i].xf_uid))
+ continue;
+ files[filenum].xf_data = xfiles[i].xf_data;
+ files[filenum].xf_pid = xfiles[i].xf_pid;
+ files[filenum].xf_uid = xfiles[i].xf_uid;
+ files[filenum].xf_fd = xfiles[i].xf_fd;
+ RB_INSERT(files_t, &ftree, &files[filenum]);
+ filenum++;
}
+ /* Adjust global nfiles to match the number of files we
+ * actually filled into files[] array
+ */
+ nfiles = filenum;
+
free(xfiles);
}
@@ -1056,6 +1099,37 @@ sctp_path_state(int state)
}
}
+static const char *
+bblog_state(int state)
+{
+ switch (state) {
+ case TCP_LOG_STATE_OFF:
+ return "OFF";
+ break;
+ case TCP_LOG_STATE_TAIL:
+ return "TAIL";
+ break;
+ case TCP_LOG_STATE_HEAD:
+ return "HEAD";
+ break;
+ case TCP_LOG_STATE_HEAD_AUTO:
+ return "HEAD_AUTO";
+ break;
+ case TCP_LOG_STATE_CONTINUAL:
+ return "CONTINUAL";
+ break;
+ case TCP_LOG_STATE_TAIL_AUTO:
+ return "TAIL_AUTO";
+ break;
+ case TCP_LOG_VIA_BBPOINTS:
+ return "BBPOINTS";
+ break;
+ default:
+ return "UNKNOWN";
+ break;
+ }
+}
+
static int
format_unix_faddr(struct addr *faddr, char *buf, size_t bufsize) {
#define SAFEBUF (buf == NULL ? NULL : buf + pos)
@@ -1143,6 +1217,7 @@ struct col_widths {
int encaps;
int path_state;
int conn_state;
+ int bblog_state;
int stack;
int cc;
};
@@ -1158,7 +1233,9 @@ calculate_sock_column_widths(struct col_widths *cw, struct sock *s)
first = true;
len = strlen(s->protoname);
- if (s->vflag & (INP_IPV4 | INP_IPV6))
+ if (s->vflag & INP_IPV4)
+ len += 1;
+ if (s->vflag & INP_IPV6)
len += 1;
cw->proto = MAX(cw->proto, len);
@@ -1194,40 +1271,40 @@ calculate_sock_column_widths(struct col_widths *cw, struct sock *s)
{ .socket = s->splice_socket });
if (sp != NULL) {
len = formataddr(&sp->laddr->address,
- NULL, 0);
+ NULL, 0);
cw->splice_address = MAX(
- cw->splice_address, len);
+ cw->splice_address, len);
}
}
}
if (opt_i) {
- if (s->proto == IPPROTO_TCP || s->proto == IPPROTO_UDP)
- {
+ if (s->proto == IPPROTO_TCP ||
+ s->proto == IPPROTO_UDP) {
len = snprintf(NULL, 0,
- "%" PRIu64, s->inp_gencnt);
+ "%" PRIu64, s->inp_gencnt);
cw->inp_gencnt = MAX(cw->inp_gencnt, len);
}
}
if (opt_U) {
if (faddr != NULL &&
- ((s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) ||
- (s->proto == IPPROTO_TCP &&
- s->state != TCPS_CLOSED &&
- s->state != TCPS_LISTEN))) {
+ ((s->proto == IPPROTO_SCTP &&
+ s->state != SCTP_CLOSED &&
+ s->state != SCTP_BOUND &&
+ s->state != SCTP_LISTEN) ||
+ (s->proto == IPPROTO_TCP &&
+ s->state != TCPS_CLOSED &&
+ s->state != TCPS_LISTEN))) {
len = snprintf(NULL, 0, "%u",
- ntohs(faddr->encaps_port));
+ ntohs(faddr->encaps_port));
cw->encaps = MAX(cw->encaps, len);
}
}
if (opt_s) {
if (faddr != NULL &&
- s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) {
+ s->proto == IPPROTO_SCTP &&
+ s->state != SCTP_CLOSED &&
+ s->state != SCTP_BOUND &&
+ s->state != SCTP_LISTEN) {
len = strlen(sctp_path_state(faddr->state));
cw->path_state = MAX(cw->path_state, len);
}
@@ -1235,21 +1312,22 @@ calculate_sock_column_widths(struct col_widths *cw, struct sock *s)
if (first) {
if (opt_s) {
if (s->proto == IPPROTO_SCTP ||
- s->proto == IPPROTO_TCP) {
+ s->proto == IPPROTO_TCP) {
switch (s->proto) {
case IPPROTO_SCTP:
len = strlen(
sctp_conn_state(s->state));
cw->conn_state = MAX(
- cw->conn_state, len);
+ cw->conn_state, len);
break;
case IPPROTO_TCP:
if (s->state >= 0 &&
s->state < TCP_NSTATES) {
- len = strlen(
- tcpstates[s->state]);
- cw->conn_state = MAX(
- cw->conn_state, len);
+ len = strlen(
+ tcpstates[s->state]);
+ cw->conn_state = MAX(
+ cw->conn_state,
+ len);
}
break;
}
@@ -1426,8 +1504,8 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize)
cw->splice_address, buf);
}
if (opt_i) {
- if (s->proto == IPPROTO_TCP || s->proto == IPPROTO_UDP)
- {
+ if (s->proto == IPPROTO_TCP ||
+ s->proto == IPPROTO_UDP) {
snprintf(buf, bufsize, "%" PRIu64,
s->inp_gencnt);
xo_emit(" {:id/%*s}", cw->inp_gencnt, buf);
@@ -1436,29 +1514,29 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize)
}
if (opt_U) {
if (faddr != NULL &&
- ((s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) ||
- (s->proto == IPPROTO_TCP &&
- s->state != TCPS_CLOSED &&
- s->state != TCPS_LISTEN))) {
+ ((s->proto == IPPROTO_SCTP &&
+ s->state != SCTP_CLOSED &&
+ s->state != SCTP_BOUND &&
+ s->state != SCTP_LISTEN) ||
+ (s->proto == IPPROTO_TCP &&
+ s->state != TCPS_CLOSED &&
+ s->state != TCPS_LISTEN))) {
xo_emit(" {:encaps/%*u}", cw->encaps,
- ntohs(faddr->encaps_port));
+ ntohs(faddr->encaps_port));
} else if (!is_xo_style_encoding)
xo_emit(" {:encaps/%*s}", cw->encaps, "??");
}
- if (opt_s) {
+ if (opt_s && show_path_state) {
if (faddr != NULL &&
- s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) {
+ s->proto == IPPROTO_SCTP &&
+ s->state != SCTP_CLOSED &&
+ s->state != SCTP_BOUND &&
+ s->state != SCTP_LISTEN) {
xo_emit(" {:path-state/%-*s}", cw->path_state,
- sctp_path_state(faddr->state));
+ sctp_path_state(faddr->state));
} else if (!is_xo_style_encoding)
xo_emit(" {:path-state/%-*s}", cw->path_state,
- "??");
+ "??");
}
if (first) {
if (opt_s) {
@@ -1467,31 +1545,40 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize)
switch (s->proto) {
case IPPROTO_SCTP:
xo_emit(" {:conn-state/%-*s}",
- cw->conn_state,
- sctp_conn_state(s->state));
+ cw->conn_state,
+ sctp_conn_state(s->state));
break;
case IPPROTO_TCP:
if (s->state >= 0 &&
- s->state < TCP_NSTATES)
+ s->state < TCP_NSTATES)
xo_emit(" {:conn-state/%-*s}",
- cw->conn_state,
- tcpstates[s->state]);
+ cw->conn_state,
+ tcpstates[s->state]);
else if (!is_xo_style_encoding)
xo_emit(" {:conn-state/%-*s}",
- cw->conn_state, "??");
+ cw->conn_state, "??");
break;
}
} else if (!is_xo_style_encoding)
xo_emit(" {:conn-state/%-*s}",
- cw->conn_state, "??");
+ cw->conn_state, "??");
+ }
+ if (opt_b) {
+ if (s->proto == IPPROTO_TCP)
+ xo_emit(" {:bblog-state/%-*s}",
+ cw->bblog_state,
+ bblog_state(s->bblog_state));
+ else if (!is_xo_style_encoding)
+ xo_emit(" {:bblog-state/%-*s}",
+ cw->bblog_state, "??");
}
if (opt_S) {
if (s->proto == IPPROTO_TCP)
xo_emit(" {:stack/%-*s}",
- cw->stack, s->stack);
+ cw->stack, s->stack);
else if (!is_xo_style_encoding)
xo_emit(" {:stack/%-*s}",
- cw->stack, "??");
+ cw->stack, "??");
}
if (opt_C) {
if (s->proto == IPPROTO_TCP)
@@ -1499,23 +1586,53 @@ display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize)
else if (!is_xo_style_encoding)
xo_emit(" {:cc/%-*s}", cw->cc, "??");
}
+ } else if (!is_xo_style_encoding) {
+ if (opt_s)
+ xo_emit(" {:conn-state/%-*s}", cw->conn_state,
+ "??");
+ if (opt_b)
+ xo_emit(" {:bblog-state/%-*s}", cw->bblog_state,
+ "??");
+ if (opt_S)
+ xo_emit(" {:stack/%-*s}", cw->stack, "??");
+ if (opt_C)
+ xo_emit(" {:cc/%-*s}", cw->cc, "??");
}
if (laddr != NULL)
laddr = laddr->next;
if (faddr != NULL)
faddr = faddr->next;
+ xo_emit("\n");
if (!is_xo_style_encoding && (laddr != NULL || faddr != NULL))
xo_emit("{:user/%-*s} {:command/%-*s} {:pid/%*s}"
- " {:fd/%*s}", cw->user, "??", cw->command, "??",
- cw->pid, "??", cw->fd, "??");
+ " {:fd/%*s} {:proto/%-*s}", cw->user, "??",
+ cw->command, "??", cw->pid, "??", cw->fd, "??",
+ cw->proto, "??");
first = false;
}
- xo_emit("\n");
}
static void
display(void)
{
+ static const char *__HDR_USER="USER",
+ *__HDR_COMMAND="COMMAND",
+ *__HDR_PID="PID",
+ *__HDR_FD="FD",
+ *__HDR_PROTO="PROTO",
+ *__HDR_LOCAL_ADDRESS="LOCAL ADDRESS",
+ *__HDR_FOREIGN_ADDRESS="FOREIGN ADDRESS",
+ *__HDR_PCB_KVA="PCB KVA",
+ *__HDR_FIB="FIB",
+ *__HDR_SPLICE_ADDRESS="SPLICE ADDRESS",
+ *__HDR_ID="ID",
+ *__HDR_ENCAPS="ENCAPS",
+ *__HDR_PATH_STATE="PATH STATE",
+ *__HDR_CONN_STATE="CONN STATE",
+ *__HDR_BBLOG_STATE="BBLOG STATE",
+ *__HDR_STACK="STACK",
+ *__HDR_CC="CC";
+
struct passwd *pwd;
struct file *xf;
struct sock *s;
@@ -1530,22 +1647,23 @@ display(void)
if (!is_xo_style_encoding) {
cw = (struct col_widths) {
- .user = strlen("USER"),
+ .user = strlen(__HDR_USER),
.command = 10,
- .pid = strlen("PID"),
- .fd = strlen("FD"),
- .proto = strlen("PROTO"),
- .local_addr = opt_w ? strlen("LOCAL ADDRESS") : 21,
- .foreign_addr = opt_w ? strlen("FOREIGN ADDRESS") : 21,
+ .pid = strlen(__HDR_PID),
+ .fd = strlen(__HDR_FD),
+ .proto = strlen(__HDR_PROTO),
+ .local_addr = opt_w ? strlen(__HDR_LOCAL_ADDRESS) : 21,
+ .foreign_addr = opt_w ? strlen(__HDR_FOREIGN_ADDRESS) : 21,
.pcb_kva = 18,
- .fib = strlen("FIB"),
- .splice_address = strlen("SPLICE ADDRESS"),
- .inp_gencnt = strlen("ID"),
- .encaps = strlen("ENCAPS"),
- .path_state = strlen("PATH STATE"),
- .conn_state = strlen("CONN STATE"),
- .stack = strlen("STACK"),
- .cc = strlen("CC"),
+ .fib = strlen(__HDR_FIB),
+ .splice_address = strlen(__HDR_SPLICE_ADDRESS),
+ .inp_gencnt = strlen(__HDR_ID),
+ .encaps = strlen(__HDR_ENCAPS),
+ .path_state = strlen(__HDR_PATH_STATE),
+ .conn_state = strlen(__HDR_CONN_STATE),
+ .bblog_state = strlen(__HDR_BBLOG_STATE),
+ .stack = strlen(__HDR_STACK),
+ .cc = strlen(__HDR_CC),
};
calculate_column_widths(&cw);
} else
@@ -1556,30 +1674,34 @@ display(void)
xo_open_list("socket");
if (!opt_q) {
xo_emit("{T:/%-*s} {T:/%-*s} {T:/%*s} {T:/%*s} {T:/%-*s} "
- "{T:/%-*s} {T:/%-*s}", cw.user, "USER", cw.command,
- "COMMAND", cw.pid, "PID", cw.fd, "FD", cw.proto,
- "PROTO", cw.local_addr, "LOCAL ADDRESS",
- cw.foreign_addr, "FOREIGN ADDRESS");
+ "{T:/%-*s} {T:/%-*s}", cw.user, __HDR_USER, cw.command,
+ __HDR_COMMAND, cw.pid, __HDR_PID, cw.fd, __HDR_FD, cw.proto,
+ __HDR_PROTO, cw.local_addr, __HDR_LOCAL_ADDRESS,
+ cw.foreign_addr, __HDR_FOREIGN_ADDRESS);
if (opt_A)
- xo_emit(" {T:/%-*s}", cw.pcb_kva, "PCB KVA");
+ xo_emit(" {T:/%-*s}", cw.pcb_kva, __HDR_PCB_KVA);
if (opt_f)
/* RT_MAXFIBS is 65535. */
- xo_emit(" {T:/%*s}", cw.fib, "FIB");
+ xo_emit(" {T:/%*s}", cw.fib, __HDR_FIB);
if (opt_I)
xo_emit(" {T:/%-*s}", cw.splice_address,
- "SPLICE ADDRESS");
+ __HDR_SPLICE_ADDRESS);
if (opt_i)
- xo_emit(" {T:/%*s}", cw.inp_gencnt, "ID");
+ xo_emit(" {T:/%*s}", cw.inp_gencnt, __HDR_ID);
if (opt_U)
- xo_emit(" {T:/%*s}", cw.encaps, "ENCAPS");
+ xo_emit(" {T:/%*s}", cw.encaps, __HDR_ENCAPS);
if (opt_s) {
- xo_emit(" {T:/%-*s}", cw.path_state, "PATH STATE");
- xo_emit(" {T:/%-*s}", cw.conn_state, "CONN STATE");
+ if (show_path_state)
+ xo_emit(" {T:/%-*s}", cw.path_state,
+ __HDR_PATH_STATE);
+ xo_emit(" {T:/%-*s}", cw.conn_state, __HDR_CONN_STATE);
}
+ if (opt_b)
+ xo_emit(" {T:/%-*s}", cw.bblog_state, __HDR_BBLOG_STATE);
if (opt_S)
- xo_emit(" {T:/%-*s}", cw.stack, "STACK");
+ xo_emit(" {T:/%-*s}", cw.stack, __HDR_STACK);
if (opt_C)
- xo_emit(" {T:/%-*s}", cw.cc, "CC");
+ xo_emit(" {T:/%-*s}", cw.cc, __HDR_CC);
xo_emit("\n");
}
cap_setpassent(cappwd, 1);
@@ -1596,31 +1718,31 @@ display(void)
if (opt_n ||
(pwd = cap_getpwuid(cappwd, xf->xf_uid)) == NULL)
xo_emit("{:user/%-*lu}", cw.user,
- (u_long)xf->xf_uid);
+ (u_long)xf->xf_uid);
else
xo_emit("{:user/%-*s}", cw.user, pwd->pw_name);
if (!is_xo_style_encoding)
xo_emit(" {:command/%-*.10s}", cw.command,
- getprocname(xf->xf_pid));
+ getprocname(xf->xf_pid));
else
xo_emit(" {:command/%-*s}", cw.command,
- getprocname(xf->xf_pid));
+ getprocname(xf->xf_pid));
xo_emit(" {:pid/%*lu}", cw.pid, (u_long)xf->xf_pid);
xo_emit(" {:fd/%*d}", cw.fd, xf->xf_fd);
display_sock(s, &cw, buf, bufsize);
xo_close_instance("socket");
}
}
- if (opt_j >= 0)
- return;
+ if (!need_nosocks())
+ goto out;
SLIST_FOREACH(s, &nosocks, socket_list) {
if (!check_ports(s))
continue;
xo_open_instance("socket");
if (!is_xo_style_encoding)
xo_emit("{:user/%-*s} {:command/%-*s} {:pid/%*s}"
- " {:fd/%*s}", cw.user, "??", cw.command, "??",
- cw.pid, "??", cw.fd, "??");
+ " {:fd/%*s}", cw.user, "??", cw.command, "??",
+ cw.pid, "??", cw.fd, "??");
display_sock(s, &cw, buf, bufsize);
xo_close_instance("socket");
}
@@ -1632,11 +1754,12 @@ display(void)
xo_open_instance("socket");
if (!is_xo_style_encoding)
xo_emit("{:user/%-*s} {:command/%-*s} {:pid/%*s}"
- " {:fd/%*s}", cw.user, "??", cw.command, "??",
- cw.pid, "??", cw.fd, "??");
+ " {:fd/%*s}", cw.user, "??", cw.command, "??",
+ cw.pid, "??", cw.fd, "??");
display_sock(s, &cw, buf, bufsize);
xo_close_instance("socket");
}
+out:
xo_close_list("socket");
xo_close_container("sockstat");
if (xo_finish() < 0)
@@ -1701,11 +1824,44 @@ jail_getvnet(int jid)
return (vnet);
}
+/*
+ * Parse username and/or UID
+ */
+static bool
+parse_filter_user(void)
+{
+ struct passwd *pwd;
+ char *ep;
+ uid_t uid;
+ bool rv = false;
+
+ uid = (uid_t)strtol(filter_user_optarg, &ep, 10);
+
+ /* Open and/or rewind capsicumized password file */
+ cap_setpassent(cappwd, 1);
+
+ if (*ep == '\0') {
+ /* We have an UID specified, check if it's valid */
+ if ((pwd = cap_getpwuid(cappwd, uid)) == NULL)
+ goto out;
+ filter_user_uid = uid;
+ } else {
+ /* Check if we have a valid username */
+ if ((pwd = cap_getpwnam(cappwd, filter_user_optarg)) == NULL)
+ goto out;
+ filter_user_uid = pwd->pw_uid;
+ }
+
+ rv = true;
+out:
+ return (rv);
+}
+
static void
usage(void)
{
xo_error(
-"usage: sockstat [--libxo ...] [-46ACcfIiLlnqSsUuvw] [-j jid] [-p ports]\n"
+"usage: sockstat [--libxo ...] [-46AbCcfIiLlnqSsUuvw] [-F uid/username] [-j jid] [-p ports]\n"
" [-P protocols]\n");
exit(1);
}
@@ -1715,19 +1871,21 @@ main(int argc, char *argv[])
{
cap_channel_t *capcas;
cap_net_limit_t *limit;
- const char *pwdcmds[] = { "setpassent", "getpwuid" };
- const char *pwdfields[] = { "pw_name" };
+ const char *pwdcmds[] = { "setpassent", "getpwuid", "getpwnam" };
+ const char *pwdfields[] = { "pw_name", "pw_uid" };
int protos_defined = -1;
int o, i, err;
argc = xo_parse_args(argc, argv);
if (argc < 0)
exit(1);
- if (xo_get_style(NULL) != XO_STYLE_TEXT &&
- xo_get_style(NULL) != XO_STYLE_HTML)
- is_xo_style_encoding = true;
+ if (xo_get_style(NULL) != XO_STYLE_TEXT) {
+ show_path_state = true;
+ if (xo_get_style(NULL) != XO_STYLE_HTML)
+ is_xo_style_encoding = true;
+ }
opt_j = -1;
- while ((o = getopt(argc, argv, "46ACcfIij:Llnp:P:qSsUuvw")) != -1)
+ while ((o = getopt(argc, argv, "46AbCcF:fIij:Llnp:P:qSsUuvw")) != -1)
switch (o) {
case '4':
opt_4 = true;
@@ -1738,12 +1896,20 @@ main(int argc, char *argv[])
case 'A':
opt_A = true;
break;
+ case 'b':
+ opt_b = true;
+ break;
case 'C':
opt_C = true;
break;
case 'c':
opt_c = true;
break;
+ case 'F':
+ /* Save optarg for later use when we enter capabilities mode */
+ filter_user_optarg = optarg;
+ opt_F = true;
+ break;
case 'f':
opt_f = true;
break;
@@ -1855,6 +2021,9 @@ main(int argc, char *argv[])
if (cap_pwd_limit_fields(cappwd, pwdfields, nitems(pwdfields)) < 0)
xo_err(1, "Unable to apply pwd commands limits");
+ if (opt_F && !parse_filter_user())
+ xo_errx(1, "Invalid username or UID specified");
+
if ((!opt_4 && !opt_6) && protos_defined != -1)
opt_4 = opt_6 = true;
if (!opt_4 && !opt_6 && !opt_u)
diff --git a/usr.bin/sockstat/sockstat.1 b/usr.bin/sockstat/sockstat.1
index 091911cd0879..b0fae81ee566 100644
--- a/usr.bin/sockstat/sockstat.1
+++ b/usr.bin/sockstat/sockstat.1
@@ -25,7 +25,7 @@
.\" (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 July 17, 2025
+.Dd October 29, 2025
.Dt SOCKSTAT 1
.Os
.Sh NAME
@@ -34,7 +34,8 @@
.Sh SYNOPSIS
.Nm
.Op Fl -libxo
-.Op Fl 46ACcfIiLlnqSsUuvw
+.Op Fl 46AbCcfIiLlnqSsUuvw
+.Op Fl F Ar user
.Op Fl j Ar jail
.Op Fl p Ar ports
.Op Fl P Ar protocols
@@ -65,11 +66,18 @@ Show
.It Fl A
Show the address of a protocol control block (PCB) associated with a socket;
used for debugging.
+.It Fl b
+Show the BBLog state of the socket.
+This is currently only implemented for TCP.
.It Fl C
Display the congestion control module, if applicable.
This is currently only implemented for TCP.
.It Fl c
Show connected sockets.
+.It Fl F Ar user
+Show sockets for specified
+.Ar user
+(user name or UID) only.
.It Fl f
Show the FIB number of each socket.
.It Fl I
@@ -202,10 +210,16 @@ is specified (only for SCTP or TCP).
The path state if
.Fl s
is specified (only for SCTP).
+When using traditional text output, this column is only shown when there is at
+least one path state to show.
.It Li CONN STATE
The connection state if
.Fl s
is specified (only for SCTP or TCP).
+.It Li BBLOG STATE
+The BBLog state if
+.Fl b
+is specified (only for TCP).
.It Li STACK
The protocol stack if
.Fl S
diff --git a/usr.bin/sockstat/tests/Makefile b/usr.bin/sockstat/tests/Makefile
index 9971bca2d474..5412e9d842aa 100644
--- a/usr.bin/sockstat/tests/Makefile
+++ b/usr.bin/sockstat/tests/Makefile
@@ -1,5 +1,6 @@
ATF_TESTS_C+= sockstat_test
-SRCS.sockstat_test= sockstat_test.c ../sockstat.c
+SRCS.sockstat_test= sockstat_test.c sockstat.c
+.PATH: ${.CURDIR:H}
LIBADD= xo
diff --git a/usr.bin/stat/stat.1 b/usr.bin/stat/stat.1
index 2996781fafa6..55e64de0767e 100644
--- a/usr.bin/stat/stat.1
+++ b/usr.bin/stat/stat.1
@@ -6,6 +6,8 @@
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by Andrew Brown and Jan Schaumann.
.\"
+.\" Copyright (c) 2025 Klara, Inc.
+.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
@@ -27,7 +29,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd June 22, 2017
+.Dd September 9, 2025
.Dt STAT 1
.Os
.Sh NAME
@@ -36,7 +38,7 @@
.Nd display file status
.Sh SYNOPSIS
.Nm
-.Op Fl FHLnq
+.Op Fl FHhLnq
.Op Fl f Ar format | Fl l | r | s | x
.Op Fl t Ar timefmt
.Op Ar
@@ -129,6 +131,45 @@ and use
instead of
.Xr lstat 2 .
This requires root privileges.
+.It Fl h
+For each file argument, print a line consisting of a comma-separated
+list of holes, a space, and the file name.
+Each hole is reported as its starting offset as a decimal number
+followed by a hyphen and the ending offset (one less than the starting
+offset of the data region that follows the hole) as a decimal number.
+If the file ends in a hole, the ending offset of the final hole will
+be one less than the size of the file.
+Otherwise, the final entry in the list (indeed, the only entry in the
+list, if the file is not sparse), is a single decimal number
+corresponding to the size of the file, representing the virtual hole
+at the end of the file.
+.Pp
+If the argument is a directory, instead of a list of holes, a single
+number is printed, corresponding to the minimum hole size for that
+directory as reported by
+.Xr pathconf 2 ,
+followed by a space and the directory name.
+.Pp
+Please note that the only way to retrieve information about the holes
+in a file is to open it and walk the list of holes and data regions
+using
+.Xr lseek 2 .
+If the file is being modified by another process at the same time as
+.Nm
+is inspecting it, the result may be inconsistent.
+.Pp
+This option cannot be combined with the
+.Fl F ,
+.Fl f ,
+.Fl H ,
+.Fl L ,
+.Fl l ,
+.Fl r ,
+.Fl s ,
+.Fl t ,
+or
+.Fl x
+options.
.It Fl L
Use
.Xr stat 2
diff --git a/usr.bin/stat/stat.c b/usr.bin/stat/stat.c
index 1fd8288728c1..0ed5d3ae5b53 100644
--- a/usr.bin/stat/stat.c
+++ b/usr.bin/stat/stat.c
@@ -7,6 +7,8 @@
* This code is derived from software contributed to The NetBSD Foundation
* by Andrew Brown.
*
+ * Copyright (c) 2025 Klara, Inc.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -47,18 +49,19 @@ __RCSID("$NetBSD: stat.c,v 1.33 2011/01/15 22:54:10 njoly Exp $"
#endif /* HAVE_CONFIG_H */
#include <sys/param.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <locale.h>
#include <paths.h>
#include <pwd.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -178,22 +181,24 @@ __RCSID("$NetBSD: stat.c,v 1.33 2011/01/15 22:54:10 njoly Exp $"
#define SHOW_filename 'N'
#define SHOW_sizerdev 'Z'
-void usage(const char *);
-void output(const struct stat *, const char *,
- const char *, int, int);
-int format1(const struct stat *, /* stat info */
+static void usage(const char *);
+static void output(const struct stat *, const char *, const char *, int);
+static int format1(const struct stat *, /* stat info */
const char *, /* the file name */
const char *, int, /* the format string itself */
char *, size_t, /* a place to put the output */
int, int, int, int, /* the parsed format */
int, int);
-int hex2byte(const char [2]);
+static int hex2byte(const char [2]);
#if HAVE_STRUCT_STAT_ST_FLAGS
-char *xfflagstostr(unsigned long);
+static char *xfflagstostr(unsigned long);
#endif
+static int fdlistholes(int, const char *);
+static int listholes(const char *);
static const char *timefmt;
static int linkfail;
+static bool nonl;
#define addchar(s, c, nl) \
do { \
@@ -205,20 +210,22 @@ int
main(int argc, char *argv[])
{
struct stat st;
- int ch, rc, errs, am_readlink;
- int lsF, fmtchar, usestat, nfs_handle, fn, nonl, quiet;
- const char *statfmt, *options, *synopsis;
char dname[sizeof _PATH_DEV + SPECNAMELEN] = _PATH_DEV;
- fhandle_t fhnd;
+ const char *statfmt, *options, *synopsis;
const char *file;
+ fhandle_t fhnd;
+ int ch, rc, errs, am_readlink, fn, fmtchar;
+ bool lsF, holes, usestat, nfs_handle, quiet;
am_readlink = 0;
- lsF = 0;
+ errs = 0;
+ lsF = false;
fmtchar = '\0';
- usestat = 0;
- nfs_handle = 0;
- nonl = 0;
- quiet = 0;
+ holes = false;
+ usestat = false;
+ nfs_handle = false;
+ nonl = false;
+ quiet = false;
linkfail = 0;
statfmt = NULL;
timefmt = NULL;
@@ -231,28 +238,35 @@ main(int argc, char *argv[])
fmtchar = 'f';
quiet = 1;
} else {
- options = "f:FHlLnqrst:x";
- synopsis = "[-FLnq] [-f format | -l | -r | -s | -x] "
+ options = "Ff:HhLlnqrst:x";
+ synopsis = "[-FHhLnq] [-f format | -l | -r | -s | -x] "
"[-t timefmt] [file|handle ...]";
}
while ((ch = getopt(argc, argv, options)) != -1)
switch (ch) {
case 'F':
- lsF = 1;
+ lsF = true;
break;
case 'H':
- nfs_handle = 1;
+ nfs_handle = true;
+ break;
+ case 'h':
+ holes = true;
break;
case 'L':
- usestat = 1;
+ usestat = true;
break;
case 'n':
- nonl = 1;
+ nonl = true;
+ break;
+ case 't':
+ timefmt = optarg;
break;
case 'q':
- quiet = 1;
+ quiet = true;
break;
+ /* remaining cases are purposefully out of order */
case 'f':
if (am_readlink) {
statfmt = "%R";
@@ -269,9 +283,6 @@ main(int argc, char *argv[])
fmtchar, ch);
fmtchar = ch;
break;
- case 't':
- timefmt = optarg;
- break;
default:
usage(synopsis);
}
@@ -280,6 +291,28 @@ main(int argc, char *argv[])
argv += optind;
fn = 1;
+ if (holes) {
+ if (fmtchar || lsF || nfs_handle || usestat || timefmt)
+ usage(synopsis);
+ if (argc > 0) {
+ while (argc-- > 0) {
+ if (listholes(*argv) != 0) {
+ if (!quiet)
+ warn("%s", *argv);
+ errs++;
+ }
+ argv++;
+ }
+ } else {
+ if (fdlistholes(STDIN_FILENO, "stdin") != 0) {
+ if (!quiet)
+ warn("stdin");
+ errs++;
+ }
+ }
+ exit(errs ? 1 : 0);
+ }
+
if (fmtchar == '\0') {
if (lsF)
fmtchar = 'l';
@@ -318,7 +351,6 @@ main(int argc, char *argv[])
if (timefmt == NULL)
timefmt = TIME_FORMAT;
- errs = 0;
do {
if (argc == 0) {
if (fdevname_r(STDIN_FILENO, dname +
@@ -361,8 +393,7 @@ main(int argc, char *argv[])
errno == ENOENT &&
(rc = lstat(file, &st)) == -1)
errno = ENOENT;
- }
- else
+ } else
rc = lstat(file, &st);
}
@@ -371,9 +402,8 @@ main(int argc, char *argv[])
linkfail = 1;
if (!quiet)
warn("%s", file);
- }
- else
- output(&st, file, statfmt, fn, nonl);
+ } else
+ output(&st, file, statfmt, fn);
argv++;
argc--;
@@ -387,7 +417,7 @@ main(int argc, char *argv[])
/*
* fflagstostr() wrapper that leaks only once
*/
-char *
+static char *
xfflagstostr(unsigned long fflags)
{
static char *str = NULL;
@@ -402,10 +432,9 @@ xfflagstostr(unsigned long fflags)
}
#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
-void
+static void
usage(const char *synopsis)
{
-
(void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis);
exit(1);
}
@@ -413,9 +442,8 @@ usage(const char *synopsis)
/*
* Parses a format string.
*/
-void
-output(const struct stat *st, const char *file,
- const char *statfmt, int fn, int nonl)
+static void
+output(const struct stat *st, const char *file, const char *statfmt, int fn)
{
int flags, size, prec, ofmt, hilo, what;
char buf[PATH_MAX + 4 + 1];
@@ -606,7 +634,7 @@ output(const struct stat *st, const char *file,
/*
* Arranges output according to a single parsed format substring.
*/
-int
+static int
format1(const struct stat *st,
const char *file,
const char *fmt, int flen,
@@ -1073,7 +1101,7 @@ format1(const struct stat *st,
(void)strcat(lfmt, "ll");
switch (ofmt) {
case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break;
- case FMTF_OCTAL: (void)strcat(lfmt, "o"); break;
+ case FMTF_OCTAL: (void)strcat(lfmt, "o"); break;
case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break;
case FMTF_HEX: (void)strcat(lfmt, "x"); break;
}
@@ -1083,9 +1111,75 @@ format1(const struct stat *st,
#define hex2nibble(c) (c <= '9' ? c - '0' : toupper(c) - 'A' + 10)
-int
+static int
hex2byte(const char c[2]) {
if (!(ishexnumber(c[0]) && ishexnumber(c[1])))
return -1;
return (hex2nibble(c[0]) << 4) + hex2nibble(c[1]);
}
+
+static int
+fdlistholes(int fd, const char *fn)
+{
+ struct stat sb;
+ off_t pos = 0, off;
+ long l;
+
+ if (fstat(fd, &sb) < 0)
+ return (-1);
+ if (S_ISDIR(sb.st_mode)) {
+ if ((l = fpathconf(fd, _PC_MIN_HOLE_SIZE)) < 0)
+ return (-1);
+ printf("%ld", l);
+ } else if (!S_ISREG(sb.st_mode)) {
+ errno = ESPIPE;
+ return (-1);
+ } else {
+ for (;;) {
+ if ((off = lseek(fd, pos, SEEK_HOLE)) < 0) {
+ if (errno != ENXIO)
+ return (-1);
+ /*
+ * This can only happen if the file was
+ * truncated while we were scanning it, or
+ * on the initial seek if the file is
+ * empty. Report the virtual hole at the
+ * end of the file at this position.
+ */
+ off = pos;
+ }
+ printf("%jd", (intmax_t)off);
+ pos = off;
+ if ((off = lseek(fd, pos, SEEK_DATA)) < 0) {
+ if (errno != ENXIO)
+ return (-1);
+ /*
+ * There are no more data regions in the
+ * file, or it got truncated. However, we
+ * may not be at the end yet.
+ */
+ if ((off = lseek(fd, 0, SEEK_END)) > pos)
+ printf("-%jd", (intmax_t)off - 1);
+ break;
+ }
+ printf("-%jd,", (intmax_t)off - 1);
+ pos = off;
+ }
+ }
+ printf(" %s", fn);
+ if (!nonl)
+ printf("\n");
+ return (0);
+}
+
+static int
+listholes(const char *fn)
+{
+ int fd, ret;
+
+ if ((fd = open(fn, O_RDONLY)) < 0)
+ return (-1);
+ ret = fdlistholes(fd, fn);
+ close(fd);
+ return (ret);
+}
diff --git a/usr.bin/stat/tests/stat_test.sh b/usr.bin/stat/tests/stat_test.sh
index e75fd0c56490..afe698575034 100755
--- a/usr.bin/stat/tests/stat_test.sh
+++ b/usr.bin/stat/tests/stat_test.sh
@@ -1,6 +1,7 @@
#
# Copyright (c) 2017 Dell EMC
# All rights reserved.
+# Copyright (c) 2025 Klara, Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -45,6 +46,76 @@ F_flag_body()
atf_check -o match:'.* f\|' stat -Fn f
}
+atf_test_case h_flag cleanup
+h_flag_head()
+{
+ atf_set "descr" "Verify the output format for -h"
+ atf_set "require.user" "root"
+}
+h_flag_body()
+{
+ # POSIX defines a hole as “[a] contiguous region of bytes
+ # within a file, all having the value of zero” and requires
+ # that “all seekable files shall have a virtual hole starting
+ # at the current size of the file” but says “it is up to the
+ # implementation to define when sparse files can be created
+ # and with what granularity for the size of holes”. It also
+ # defines a sparse file as “[a] file that contains more holes
+ # than just the virtual hole at the end of the file”. That's
+ # pretty much the extent of its discussion of holes, apart
+ # from the description of SEEK_HOLE and SEEK_DATA in the lseek
+ # manual page. In other words, there is no portable way to
+ # reliably create a hole in a file on any given file system.
+ #
+ # On FreeBSD, this test is likely to run on either tmpfs, ufs
+ # (ffs2), or zfs. Of those three, only tmpfs has predictable
+ # semantics and supports all possible configurations (the
+ # minimum hole size on zfs is variable for small files, and
+ # ufs will not allow a file to end in a hole).
+ atf_check mkdir mnt
+ atf_check mount -t tmpfs tmpfs mnt
+ cd mnt
+
+ # For a directory, prints the minimum hole size, which on
+ # tmpfs is the system page size.
+ ps=$(sysctl -n hw.pagesize)
+ atf_check -o inline:"$((ps)) .\n" stat -h .
+ atf_check -o inline:"$((ps)) ." stat -hn .
+
+ # For a file, prints a list of holes.
+ atf_check truncate -s 0 foo
+ atf_check -o inline:"0 foo" \
+ stat -hn foo
+ atf_check truncate -s "$((ps))" foo
+ atf_check -o inline:"0-$((ps-1)) foo" \
+ stat -hn foo
+ atf_check dd status=none if=/COPYRIGHT of=foo \
+ oseek="$((ps))" bs=1 count=1
+ atf_check -o inline:"0-$((ps-1)),$((ps+1)) foo" \
+ stat -hn foo
+ atf_check truncate -s "$((ps*3))" foo
+ atf_check -o inline:"0-$((ps-1)),$((ps*2))-$((ps*3-1)) foo" \
+ stat -hn foo
+
+ # Test multiple files.
+ atf_check dd status=none if=/COPYRIGHT of=bar
+ sz=$(stat -f%z bar)
+ atf_check -o inline:"0-$((ps-1)),$((ps*2))-$((ps*3-1)) foo
+$((sz)) bar
+" \
+ stat -h foo bar
+
+ # For a device, fail.
+ atf_check -s exit:1 -e match:"/dev/null: Illegal seek" \
+ stat -h /dev/null
+}
+h_flag_cleanup()
+{
+ if [ -d mnt ]; then
+ umount mnt || true
+ fi
+}
+
atf_test_case l_flag
l_flag_head()
{
@@ -233,6 +304,7 @@ atf_init_test_cases()
{
atf_add_test_case F_flag
#atf_add_test_case H_flag
+ atf_add_test_case h_flag
#atf_add_test_case L_flag
#atf_add_test_case f_flag
atf_add_test_case l_flag
diff --git a/usr.bin/tail/tests/tail_test.sh b/usr.bin/tail/tests/tail_test.sh
index 74d6908f7568..82c74a0d4da4 100755
--- a/usr.bin/tail/tests/tail_test.sh
+++ b/usr.bin/tail/tests/tail_test.sh
@@ -341,6 +341,7 @@ follow_create_body()
rm -f infile
tail -F infile > outfile &
pid=$!
+ sleep 0.1
seq 1 5 >infile
sleep 2
atf_check cmp infile outfile
@@ -360,6 +361,7 @@ follow_rename_body()
seq 1 3 > infile
tail -F infile > outfile &
pid=$!
+ sleep 0.1
seq 4 5 > infile_new
atf_check mv infile infile_old
atf_check mv infile_new infile
diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile
index 9260315fb30b..8b0d3e4a6cf0 100644
--- a/usr.bin/tar/Makefile
+++ b/usr.bin/tar/Makefile
@@ -17,7 +17,7 @@ SRCS= bsdtar.c \
write.c
.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
-SRCS+= err.c \
+SRCS+= lafe_err.c \
line_reader.c \
passphrase.c
diff --git a/usr.bin/tar/tests/Makefile b/usr.bin/tar/tests/Makefile
index 929f8127f9b3..116425b0621f 100644
--- a/usr.bin/tar/tests/Makefile
+++ b/usr.bin/tar/tests/Makefile
@@ -27,6 +27,7 @@ TESTS_SRCS= \
test_0.c \
test_basic.c \
test_copy.c \
+ test_crlf_mtree.c \
test_empty_mtree.c \
test_extract_tar_bz2.c \
test_extract_tar_grz.c \
diff --git a/usr.bin/tcopy/tcopy.cc b/usr.bin/tcopy/tcopy.cc
index 891c37f871e5..a1dd35682aac 100644
--- a/usr.bin/tcopy/tcopy.cc
+++ b/usr.bin/tcopy/tcopy.cc
@@ -580,7 +580,9 @@ getspace(size_t blk)
static void
usage(void)
{
- fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n");
+ fprintf(stderr,
+ "usage: tcopy [-crvx] [-l logfile] [-s maxblk] [src [dest]]\n"
+ );
exit(1);
}
@@ -738,7 +740,7 @@ main(int argc, char *argv[])
warnx("illegal block size");
usage();
}
- if (maxblk <= 0) {
+ if (tmp <= 0) {
warnx("illegal block size");
usage();
}
diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c
index 656d642e1f19..7b299bd2e1ff 100644
--- a/usr.bin/truss/syscalls.c
+++ b/usr.bin/truss/syscalls.c
@@ -403,7 +403,7 @@ static const struct syscall_decode decoded_syscalls[] = {
{ .name = "nanosleep", .ret_type = 1, .nargs = 1,
.args = { { Timespec, 0 } } },
{ .name = "nmount", .ret_type = 1, .nargs = 3,
- .args = { { Ptr, 0 }, { UInt, 1 }, { Mountflags, 2 } } },
+ .args = { { Iovec | IN, 0 }, { UInt, 1 }, { Mountflags, 2 } } },
{ .name = "open", .ret_type = 1, .nargs = 3,
.args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } },
{ .name = "openat", .ret_type = 1, .nargs = 4,
diff --git a/usr.bin/unzip/Makefile b/usr.bin/unzip/Makefile
index 2db5e9ac4c99..63f49a203685 100644
--- a/usr.bin/unzip/Makefile
+++ b/usr.bin/unzip/Makefile
@@ -12,7 +12,7 @@ BSDUNZIP_VERSION_STRING!= sed -n '/define.*ARCHIVE_VERSION_ONLY_STRING/{s,[^0-9.
SRCS= bsdunzip.c
.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
-SRCS+= cmdline.c err.c passphrase.c
+SRCS+= cmdline.c lafe_err.c passphrase.c
CFLAGS+= -DBSDUNZIP_VERSION_STRING=\"${BSDUNZIP_VERSION_STRING}\"
CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\"
diff --git a/usr.bin/unzip/tests/Makefile b/usr.bin/unzip/tests/Makefile
index 404a546410e4..fada172b1bd7 100644
--- a/usr.bin/unzip/tests/Makefile
+++ b/usr.bin/unzip/tests/Makefile
@@ -23,7 +23,7 @@ CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive_fe -I${_LIBARCHIVEDIR}/test_utils
CFLAGS.test_utils.c+= -Wno-cast-align
.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
-UNZIP_SRCS+= err.c
+UNZIP_SRCS+= lafe_err.c
.PATH: ${_LIBARCHIVEDIR}/unzip/test
TESTS_SRCS= \
diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c
index 7a7c83fe1ac8..9b4d3a25ee07 100644
--- a/usr.bin/vmstat/vmstat.c
+++ b/usr.bin/vmstat/vmstat.c
@@ -1465,6 +1465,7 @@ display_object(struct kinfo_vmobject *kvo)
xo_emit("{:active/%5ju} ", (uintmax_t)kvo->kvo_active);
xo_emit("{:inactive/%5ju} ", (uintmax_t)kvo->kvo_inactive);
xo_emit("{:laundry/%5ju} ", (uintmax_t)kvo->kvo_laundry);
+ xo_emit("{:wired/%5ju} ", (uintmax_t)kvo->kvo_wired);
xo_emit("{:refcount/%3d} ", kvo->kvo_ref_count);
xo_emit("{:shadowcount/%3d} ", kvo->kvo_shadow_count);
@@ -1568,7 +1569,8 @@ doobjstat(void)
return;
}
xo_emit("{T:RES/%5s} {T:ACT/%5s} {T:INACT/%5s} {T:LAUND/%5s} "
- "{T:REF/%3s} {T:SHD/%3s} {T:CM/%2s} {T:TP/%3s} {T:PATH/%s}\n");
+ "{T:WIRED/%5s} {T:REF/%3s} {T:SHD/%3s} {T:CM/%2s} {T:TP/%3s} "
+ "{T:PATH/%s}\n");
xo_open_list("object");
for (i = 0; i < cnt; i++)
display_object(&kvo[i]);
diff --git a/usr.bin/w/pr_time.c b/usr.bin/w/pr_time.c
index aef8b5dfaa87..445431fe3ec5 100644
--- a/usr.bin/w/pr_time.c
+++ b/usr.bin/w/pr_time.c
@@ -79,8 +79,13 @@ pr_attime(time_t *started, time_t *now)
(void)wcsftime(buf, sizeof(buf), fmt, &tp);
len = wcslen(buf);
width = wcswidth(buf, len);
- xo_attr("since", "%lu", (unsigned long) *started);
- xo_attr("delta", "%lu", (unsigned long) diff);
+ if (xo_get_style(NULL) == XO_STYLE_XML) {
+ xo_attr("since", "%lu", (unsigned long)*started);
+ xo_attr("delta", "%lu", (unsigned long)diff);
+ } else {
+ xo_emit("{e:login-time-since/%lu}{e:login-time-delta/%lu}",
+ (unsigned long)*started, (unsigned long)diff);
+ }
if (len == width)
xo_emit("{:login-time/%-7.7ls/%ls}", buf);
else if (width < 7)
@@ -100,10 +105,16 @@ pr_attime(time_t *started, time_t *now)
int
pr_idle(time_t idle)
{
+ /* In encoded formats, emit the raw data as well */
+ if (xo_get_style(NULL) == XO_STYLE_XML)
+ xo_attr("seconds", "%lu", (unsigned long) idle);
+ else
+ xo_emit("{e:idle-seconds/%lu}", (unsigned long) idle);
+
/* If idle more than 36 hours, print as a number of days. */
if (idle >= 36 * 3600) {
int days = idle / 86400;
- xo_emit(" {:idle/%dday%s} ", days, days > 1 ? "s" : " " );
+ xo_emit(" {q:idle/%dday%s} ", days, days > 1 ? "s" : " " );
if (days >= 100)
return (2);
if (days >= 10)
@@ -111,16 +122,17 @@ pr_idle(time_t idle)
}
/* If idle more than an hour, print as HH:MM. */
- else if (idle >= 3600)
- xo_emit(" {:idle/%2d:%02d/} ",
+ else if (idle >= 3600) {
+ xo_emit(" {q:idle/%2d:%02d} ",
(int)(idle / 3600), (int)((idle % 3600) / 60));
+ }
else if (idle / 60 == 0)
- xo_emit(" - ");
+ xo_emit(" - {q:idle//0}");
/* Else print the minutes idle. */
else
- xo_emit(" {:idle/%2d} ", (int)(idle / 60));
+ xo_emit(" {q:idle/%2d} ", (int)(idle / 60));
return (0); /* not idle longer than 9 days */
}
diff --git a/usr.bin/w/uptime.1 b/usr.bin/w/uptime.1
index b93972d3f932..37881793736f 100644
--- a/usr.bin/w/uptime.1
+++ b/usr.bin/w/uptime.1
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 18, 2020
+.Dd September 11, 2025
.Dt UPTIME 1
.Os
.Sh NAME
@@ -33,6 +33,7 @@
.Nd show how long system has been running
.Sh SYNOPSIS
.Nm
+.Op Fl -libxo
.Sh DESCRIPTION
The
.Nm
@@ -40,6 +41,17 @@ utility displays the current time,
the length of time the system has been up,
the number of users, and the load average of the system over the last
1, 5, and 15 minutes.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl -libxo
+Generate output via
+.Xr libxo 3
+in a selection of different human and machine readable formats.
+See
+.Xr xo_options 7
+for details on command line arguments.
+.El
.Sh FILES
.Bl -tag -width /boot/kernel/kernel
.It Pa /boot/kernel/kernel
@@ -51,7 +63,9 @@ $ uptime
11:23AM up 3:01, 8 users, load averages: 21.09, 15.43, 12.79
.Ed
.Sh SEE ALSO
-.Xr w 1
+.Xr w 1 ,
+.Xr libxo 3 ,
+.Xr xo_options 7
.Sh HISTORY
The
.Nm
diff --git a/usr.bin/w/w.1 b/usr.bin/w/w.1
index 159eb3370c8c..2dbcffdeda1f 100644
--- a/usr.bin/w/w.1
+++ b/usr.bin/w/w.1
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 16, 2025
+.Dd September 11, 2025
.Dt W 1
.Os
.Sh NAME
@@ -54,7 +54,7 @@ user is on, the host from which the user is logged in, the time the user
logged on, the time since the user last typed anything,
and the name and arguments of the current process.
.Pp
-The options are as follows:
+The following options are available:
.Bl -tag -width indent
.It Fl -libxo
Generate output via
diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c
index ac1df96077d3..502bf5a412b9 100644
--- a/usr.bin/w/w.c
+++ b/usr.bin/w/w.c
@@ -473,7 +473,7 @@ main(int argc, char *argv[])
static void
pr_header(time_t *nowp, int nusers)
{
- char buf[64];
+ char buf[64], *s, *e;
struct sbuf upbuf;
double avenrun[3];
struct timespec tp;
@@ -484,8 +484,15 @@ pr_header(time_t *nowp, int nusers)
* Print time of day.
*/
if (strftime(buf, sizeof(buf),
- use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0)
- xo_emit("{:time-of-day/%s} ", buf);
+ use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0) {
+ s = buf;
+ if (xo_get_style(NULL) != XO_STYLE_TEXT) {
+ /* trim leading whitespace */
+ while (isspace((unsigned char)*s))
+ s++;
+ }
+ xo_emit("{:time-of-day/%s} ", s);
+ }
/*
* Print how long system has been up.
*/
@@ -516,21 +523,31 @@ pr_header(time_t *nowp, int nusers)
if (days > 0)
sbuf_printf(&upbuf, " %ld day%s,",
- days, days > 1 ? "s" : "");
+ days, days > 1 ? "s" : "");
if (hrs > 0 && mins > 0)
sbuf_printf(&upbuf, " %2ld:%02ld,", hrs, mins);
else if (hrs > 0)
sbuf_printf(&upbuf, " %ld hr%s,",
- hrs, hrs > 1 ? "s" : "");
+ hrs, hrs > 1 ? "s" : "");
else if (mins > 0)
sbuf_printf(&upbuf, " %ld min%s,",
- mins, mins > 1 ? "s" : "");
+ mins, mins > 1 ? "s" : "");
else
sbuf_printf(&upbuf, " %ld sec%s,",
- secs, secs > 1 ? "s" : "");
+ secs, secs > 1 ? "s" : "");
if (sbuf_finish(&upbuf) != 0)
xo_err(1, "Could not generate output");
- xo_emit("{:uptime-human/%s}", sbuf_data(&upbuf));
+ s = sbuf_data(&upbuf);
+ if (xo_get_style(NULL) != XO_STYLE_TEXT) {
+ e = s + sbuf_len(&upbuf) - 1;
+ /* trim leading whitespace */
+ while (isspace((unsigned char)*s))
+ s++;
+ /* trim trailing comma */
+ if (e > s && *e == ',')
+ *e = '\0';
+ }
+ xo_emit("{:uptime-human/%s}", s);
sbuf_delete(&upbuf);
}
diff --git a/usr.bin/who/Makefile b/usr.bin/who/Makefile
index 77626f51824a..c7c455d5261c 100644
--- a/usr.bin/who/Makefile
+++ b/usr.bin/who/Makefile
@@ -1,4 +1,3 @@
PROG= who
-PACKAGE= acct
.include <bsd.prog.mk>
diff --git a/usr.bin/xz/Makefile b/usr.bin/xz/Makefile
index 0d5bce4c16f0..0a9103d60a13 100644
--- a/usr.bin/xz/Makefile
+++ b/usr.bin/xz/Makefile
@@ -1,5 +1,7 @@
.include <src.opts.mk>
+PACKAGE=xz
+
PROG= xz
LINKS= ${BINDIR}/xz ${BINDIR}/unxz
diff --git a/usr.bin/xzdec/Makefile b/usr.bin/xzdec/Makefile
index 7c43b2e03d78..6bf3dc07a408 100644
--- a/usr.bin/xzdec/Makefile
+++ b/usr.bin/xzdec/Makefile
@@ -1,3 +1,5 @@
+PACKAGE=xz
+
PROG= xzdec
LINKS= ${BINDIR}/xzdec ${BINDIR}/lzdec