diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 95328d6d293f..a756236fd534 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -1,1211 +1,1213 @@ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 tags=package=tests . bin cat .. chflags .. chmod .. cp .. date .. dd .. echo .. expr .. hostname .. ln .. ls .. mkdir .. mv .. pax .. pkill .. pwait .. rm .. rmdir .. sh builtins .. errors .. execution .. expansion .. invocation .. parameters .. parser .. set-e .. .. sleep .. test .. timeout .. .. cddl lib .. sbin .. usr.bin ctfconvert .. ztest .. .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. env .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. kinst .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. oformat .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. sugar .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. i386 arrays .. funcs .. pid .. ustack .. .. amd64 arrays .. .. .. zfsd .. .. .. etc rc.d .. .. examples .. games .. gnu lib .. usr.bin diff .. .. .. include .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. csu dynamic .. dynamiclib .. static .. .. googletest gmock .. gmock_main .. gtest .. gtest_main .. .. libarchive .. libbe .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. iconv .. inet .. locale .. net getaddrinfo data .. .. .. nss .. regex data .. .. resolv .. rpc .. ssp .. setjmp .. stdio .. stdlib .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcam .. libcasper services cap_dns .. cap_grp .. cap_pwd .. cap_sysctl .. .. .. libcrypt .. libdevdctl .. libexecinfo .. libkvm .. libmp .. libnv .. libproc .. libregex data .. .. librt .. libsbuf .. libsysdecode .. libthr dlopen .. .. libutil .. libxo .. msun .. .. libexec atf atf-check .. atf-pytest-wrapper .. atf-sh .. .. nuageinit .. rc .. rtld-elf rtld_deepbind .. .. tftpd .. .. sbin bectl .. dhclient .. devd .. growfs .. ifconfig .. ipfw .. md5 .. mdconfig .. newfs_msdos .. nvmecontrol .. pfctl files .. .. ping .. route .. sysctl .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. googletest .. plain .. tap .. .. .. zoneinfo .. .. sys acl .. aio .. audit .. auditpipe .. capsicum .. cddl zfs bin .. include .. tests acl cifs .. nontrivial .. trivial .. .. atime .. bootfs .. cache .. cachefile .. clean_mirror .. cli_root zfs_upgrade .. zfs_promote .. zfs_clone .. zfs_property .. zfs_destroy .. zpool_create .. zpool_history .. zpool_expand .. zpool_remove .. zfs_mount .. zfs_unshare .. zdb .. zpool_online .. zpool_get .. zpool_export .. zfs_copies .. zfs_get .. zfs .. zpool_clear .. zpool_import blockfiles .. .. zpool .. zpool_offline .. zpool_replace .. zfs_rollback .. zpool_set .. zfs_send .. zfs_set .. zpool_detach .. zfs_diff .. zpool_scrub .. zfs_inherit .. zfs_snapshot .. zfs_share .. zpool_destroy .. zpool_status .. zfs_unmount .. zfs_receive .. zfs_create .. zpool_upgrade blockfiles .. .. zpool_add .. zfs_rename .. zpool_attach .. zfs_reservation .. .. cli_user misc .. zfs_list .. zpool_iostat .. zpool_list .. .. compression .. ctime .. delegate .. devices .. exec .. grow_pool .. grow_replicas .. history .. hotplug .. hotspare .. inheritance .. interop .. inuse .. iscsi .. large_files .. largest_pool .. link_count .. migration .. mmap .. mount .. mv_files .. nestedfs .. no_space .. online_offline .. pool_names .. poolversion .. quota .. redundancy .. refquota .. refreserv .. rename_dirs .. replacement .. reservation .. rootpool .. rsend .. scrub_mirror .. slog .. snapshot .. snapused .. sparse .. threadsappend .. truncate .. txg_integrity .. userquota .. utils_test .. write_dirs .. xattr .. zfsd .. zil .. zinject .. zones .. zvol zvol_ENOSPC .. zvol_cli .. zvol_misc .. zvol_swap .. .. zvol_thrash .. .. .. .. compat32 .. cam ctl .. .. devrandom .. dtrace .. fifo .. file .. fs fusefs .. tarfs .. tmpfs .. .. geom class concat .. eli .. gate .. gpt .. mirror .. multipath .. nop .. part .. raid3 .. shsec .. stripe .. uzip etalon .. .. .. .. kern acct .. execve .. pipe .. .. kqueue libkqueue .. .. mac bsdextended .. ipacl .. portacl .. .. mqueue .. net if_ovpn .. routing .. .. netgraph .. netinet .. netinet6 frag6 .. .. netipsec tunnel .. .. netlink .. netmap .. netpfil common .. ipfw .. pf ioctl .. .. .. opencrypto .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. utimensat .. .. posixshm .. sys .. vfs .. vm stack .. .. vmm .. .. usr.bin apply .. asa .. awk bugs-fixed .. netbsd .. .. basename .. bintrans .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. bsdcat .. calendar .. cmp .. compress .. cpio .. col .. comm .. csplit .. cut .. dc .. diff .. diff3 .. dirname .. du .. + env + .. factor .. file2c .. file .. find .. fold .. getconf .. gh-bc .. grep .. gzip .. head .. hexdump .. ident .. indent .. join .. jot .. lastcomm .. limits .. locale .. lockf .. lorder .. m4 .. mkimg .. mktemp .. ncal .. opensm .. patch .. pr .. printf .. procstat .. renice .. rs .. sdiff .. sed regress.multitest.out .. .. seq .. soelim .. sort .. split .. stat .. tail .. tar .. tftp .. touch .. tr .. truncate .. tsort .. units .. unifdef .. uniq .. unzip .. vmstat .. wc .. xargs .. xinstall .. xo .. yacc yacc .. .. .. usr.sbin chown .. ctladm .. daemon .. etcupdate .. extattr .. fstyp .. jail .. makefs .. mixer .. newsyslog .. nmtree .. praudit .. pw .. rpcbind .. sa .. syslogd .. .. .. # vim: set expandtab ts=4 sw=4: diff --git a/usr.bin/env/Makefile b/usr.bin/env/Makefile index af2e65dd5fc1..d7a34fce92aa 100644 --- a/usr.bin/env/Makefile +++ b/usr.bin/env/Makefile @@ -1,9 +1,13 @@ # From: @(#)Makefile 8.1 (Berkeley) 6/6/93 +.include PACKAGE= runtime PROG= env SRCS= env.c envopts.c LIBADD= util +HAS_TESTS= +SUBDIR.${MK_TESTS}= tests + .include diff --git a/usr.bin/env/env.1 b/usr.bin/env/env.1 index 8aa78e110505..4badea716143 100644 --- a/usr.bin/env/env.1 +++ b/usr.bin/env/env.1 @@ -1,530 +1,551 @@ .\" Copyright (c) 1980, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the Institute of Electrical and Electronics Engineers, Inc. .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" From @(#)printenv.1 8.1 (Berkeley) 6/6/93 .\" From FreeBSD: src/usr.bin/printenv/printenv.1,v 1.17 2002/11/26 17:33:35 ru Exp .\" -.Dd March 3, 2021 +.Dd October 8, 2024 .Dt ENV 1 .Os .Sh NAME .Nm env .Nd set environment and execute command, or print environment .Sh SYNOPSIS .Nm .Op Fl 0iv .Op Fl L Ns | Ns Fl U Ar user Ns Op / Ns Ar class .Op Fl u Ar name .Op Ar name Ns = Ns Ar value ... .Nm .Op Fl iv +.Op Fl C Ar altwd .Op Fl L Ns | Ns Fl U Ar user Ns Op / Ns Ar class .Op Fl P Ar altpath .Op Fl S Ar string .Op Fl u Ar name .Op Ar name Ns = Ns Ar value ... .Ar utility Op Ar argument ... .Sh DESCRIPTION The .Nm utility executes another .Ar utility after modifying the environment as specified on the command line. Each .Ar name Ns = Ns Ar value option specifies the setting of an environment variable, .Ar name , with a value of .Ar value . All such environment variables are set before the .Ar utility is executed. .Pp The options are as follows: .Bl -tag -width indent .It Fl 0 End each output line with NUL, not newline. .It Fl i Execute the .Ar utility with only those environment variables specified by .Ar name Ns = Ns Ar value options. The environment inherited by .Nm is ignored completely. +.\" -C +.It Fl C Ar altwd +Change to the specified alternate working directory before executing +the specified +.Ar utility +program. .\" -L | -U .It Fl L | Fl U Ar user Ns Op / Ns Ar class Add the environment variable definitions from .Xr login.conf 5 for the specified user and login class to the environment, after processing any .Fl i or .Fl u options, but before processing any .Ar name Ns = Ns Ar value options. If .Fl L is used, only the system-wide .Pa /etc/login.conf.db file is read; if .Fl U is used, then the specified user's .Pa ~/.login_conf is read as well. The user may be specified by name or by uid. If a username of .Sq Li \&- is given, then no user lookup will be done, the login class will default to .Sq Li default if not explicitly given, and no substitutions will be done on the values. .\" -P .It Fl P Ar altpath Search the set of directories as specified by .Ar altpath to locate the specified .Ar utility program, instead of using the value of the .Ev PATH environment variable. .\" -S .It Fl S Ar string Split apart the given .Ar string into multiple strings, and process each of the resulting strings as separate arguments to the .Nm utility. The .Fl S option recognizes some special character escape sequences and also supports environment-variable substitution, as described below. .\" -u .It Fl u Ar name If the environment variable .Ar name is in the environment, then remove it before processing the remaining options. This is similar to the .Ic unset command in .Xr sh 1 . The value for .Ar name must not include the .Ql = character. .\" -v .It Fl v Print verbose information for each step of processing done by the .Nm utility. Additional information will be printed if .Fl v is specified multiple times. .El .Pp The above options are only recognized when they are specified before any .Ar name Ns = Ns Ar value options. .Pp If no .Ar utility is specified, .Nm prints out the names and values of the variables in the environment. Each name/value pair is separated by a new line unless .Fl 0 is specified, in which case name/value pairs are separated by NUL. Both .Fl 0 and .Ar utility may not be specified together. +.Pp +The +.Nm +utility does not handle values of +.Ar utility +which have an equals sign +.Pq Ql = +in their name, for obvious reasons. +This can easily be worked around by interposing the +.Xr command 1 +utility, which simply executes its arguments; see +.Sx EXAMPLES +below. .\" .Ss Details of -S (split-string) processing The processing of the .Fl S option will split the given .Ar string into separate arguments based on any space or characters found in the .Ar string . Each of those new arguments will then be treated as if it had been specified as a separate argument on the original .Nm command. .Pp Spaces and tabs may be embedded in one of those new arguments by using single .Pq Dq Li ' or double .Pq Ql \&" quotes, or backslashes .Pq Ql \e . Single quotes will escape all non-single quote characters, up to the matching single quote. Double quotes will escape all non-double quote characters, up to the matching double quote. It is an error if the end of the .Ar string is reached before the matching quote character. .Pp If .Fl S would create a new argument that starts with the .Ql # character, then that argument and the remainder of the .Ar string will be ignored. The .Ql \e# sequence can be used when you want a new argument to start with a .Ql # character, without causing the remainder of the .Ar string to be skipped. .Pp While processing the .Ar string value, .Fl S processing will treat certain character combinations as escape sequences which represent some action to take. The character escape sequences are in backslash notation. The characters and their meanings are as follows: .Pp .Bl -tag -width indent -offset indent -compact .It Cm \ec Ignore the remaining characters in the .Ar string . This must not appear inside a double-quoted string. .It Cm \ef Replace with a character. .It Cm \en Replace with a character. .It Cm \er Replace with a character. .It Cm \et Replace with a character. .It Cm \ev Replace with a character. .It Cm \e# Replace with a .Ql # character. This would be useful when you need a .Ql # as the first character in one of the arguments created by splitting apart the given .Ar string . .It Cm \e$ Replace with a .Ql $ character. .It Cm \e_ If this is found inside of a double-quoted string, then replace it with a single blank. If this is found outside of a quoted string, then treat this as the separator character between new arguments in the original .Ar string . .It Cm \e" Replace with a character. .It Cm \e\' Replace with a character. .It Cm \e\e Replace with a backslash character. .El .Pp The sequences for and backslash are the only sequences which are recognized inside of a single-quoted string. The other sequences have no special meaning inside a single-quoted string. All escape sequences are recognized inside of a double-quoted string. It is an error if a single .Ql \e character is followed by a character other than the ones listed above. .Pp The processing of .Fl S also supports substitution of values from environment variables. To do this, the name of the environment variable must be inside of .Ql ${} , such as: .Li ${SOMEVAR} . The common shell syntax of .Li $SOMEVAR is not supported. All values substituted will be the values of the environment variables as they were when the .Nm utility was originally invoked. Those values will not be checked for any of the escape sequences as described above. And any settings of .Ar name Ns = Ns Ar value will not effect the values used for substitution in .Fl S processing. .Pp Also, .Fl S processing cannot reference the value of the special parameters which are defined by most shells. For instance, .Fl S cannot recognize special parameters such as: .Ql $* , .Ql $@ , .Ql $# , .Ql $? or .Ql $$ if they appear inside the given .Ar string . .\" .Ss Use in shell-scripts The .Nm utility is often used as the .Ar interpreter on the first line of interpreted scripts, as described in .Xr execve 2 . .Pp Note that the way the kernel parses the .Ql #! (first line) of an interpreted script has changed as of .Fx 6.0 . Prior to that, the .Fx kernel would split that first line into separate arguments based on any whitespace (space or characters) found in the line. So, if a script named .Pa /usr/local/bin/someport had a first line of: .Pp .Dl "#!/usr/local/bin/php -n -q -dsafe_mode=0" .Pp then the .Pa /usr/local/bin/php program would have been started with the arguments of: .Bd -literal -offset indent arg[0] = '/usr/local/bin/php' arg[1] = '-n' arg[2] = '-q' arg[3] = '-dsafe_mode=0' arg[4] = '/usr/local/bin/someport' .Ed .Pp plus any arguments the user specified when executing .Pa someport . However, this processing of multiple options on the .Ql #! line is not the way any other operating system parses the first line of an interpreted script. So after a change which was made for .Fx 6.0 release, that script will result in .Pa /usr/local/bin/php being started with the arguments of: .Bd -literal -offset indent arg[0] = '/usr/local/bin/php' arg[1] = '-n -q -dsafe_mode=0' arg[2] = '/usr/local/bin/someport' .Ed .Pp plus any arguments the user specified. This caused a significant change in the behavior of a few scripts. In the case of above script, to have it behave the same way under .Fx 6.0 as it did under earlier releases, the first line should be changed to: .Pp .Dl "#!/usr/bin/env -S /usr/local/bin/php -n -q -dsafe_mode=0" .Pp The .Nm utility will be started with the entire line as a single argument: .Pp .Dl "arg[1] = '-S /usr/local/bin/php -n -q -dsafe_mode=0'" .Pp and then .Fl S processing will split that line into separate arguments before executing .Pa /usr/local/bin/php . .\" .Sh ENVIRONMENT The .Nm utility uses the .Ev PATH environment variable to locate the requested .Ar utility if the name contains no .Ql / characters, unless the .Fl P option has been specified. .Sh EXIT STATUS .Ex -std An exit status of 126 indicates that .Ar utility was found, but could not be executed. An exit status of 127 indicates that .Ar utility could not be found. .Sh EXAMPLES Since the .Nm utility is often used as part of the first line of an interpreted script, the following examples show a number of ways that the .Nm utility can be useful in scripts. .Pp The kernel processing of an interpreted script does not allow a script to directly reference some other script as its own interpreter. As a way around this, the main difference between .Pp .Dl #!/usr/local/bin/foo and .Dl "#!/usr/bin/env /usr/local/bin/foo" .Pp is that the latter works even if .Pa /usr/local/bin/foo is itself an interpreted script. .Pp Probably the most common use of .Nm is to find the correct interpreter for a script, when the interpreter may be in different directories on different systems. The following example will find the .Ql perl interpreter by searching through the directories specified by .Ev PATH . .Pp .Dl "#!/usr/bin/env perl" .Pp One limitation of that example is that it assumes the user's value for .Ev PATH is set to a value which will find the interpreter you want to execute. The .Fl P option can be used to make sure a specific list of directories is used in the search for .Ar utility . Note that the .Fl S option is also required for this example to work correctly. .Pp .Dl "#!/usr/bin/env -S -P/usr/local/bin:/usr/bin perl" .Pp The above finds .Ql perl only if it is in .Pa /usr/local/bin or .Pa /usr/bin . That could be combined with the present value of .Ev PATH , to provide more flexibility. Note that spaces are not required between the .Fl S and .Fl P options: .Pp .Dl "#!/usr/bin/env -S-P/usr/local/bin:/usr/bin:${PATH} perl" +.Pp +To execute a utility with an equal sign in its name: +.Bd -literal -offset indent +env name=value ... command foo=bar arg ... +.Ed .Sh COMPATIBILITY The .Nm utility accepts the .Fl option as a synonym for .Fl i . .Sh SEE ALSO .Xr printenv 1 , .Xr sh 1 , .Xr execvp 3 , .Xr login.conf 5 , .Xr environ 7 .Sh STANDARDS The .Nm utility conforms to .St -p1003.1-2001 . The -.Fl 0 , L , P , S , U , u +.Fl 0 , C , L , P , S , U , u and .Fl v options are non-standard extensions supported by .Fx , but which may not be available on other operating systems. .Sh HISTORY The .Nm command appeared in .Bx 4.4 . The .Fl P , S and .Fl v options were added in .Fx 6.0 . The .Fl 0 , L and .Fl U options were added in .Fx 13.0 . -.Sh BUGS The -.Nm -utility does not handle values of -.Ar utility -which have an equals sign -.Pq Ql = -in their name, for obvious reasons. -.Pp +.Fl C +option was added in +.Fx 14.2 . +.Sh BUGS The .Nm utility does not take multibyte characters into account when processing the .Fl S option, which may lead to incorrect results in some locales. diff --git a/usr.bin/env/env.c b/usr.bin/env/env.c index 784caa76063e..724a17d8be4f 100644 --- a/usr.bin/env/env.c +++ b/usr.bin/env/env.c @@ -1,228 +1,242 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)env.c 8.3 (Berkeley) 4/2/94"; #endif /* not lint */ #endif #include #include #include #include #include #include #include #include #include #include #include #include "envopts.h" extern char **environ; int env_verbosity; static void usage(void) __dead2; /* * Exit codes. */ #define EXIT_CANCELED 125 /* Internal error prior to exec attempt. */ #define EXIT_CANNOT_INVOKE 126 /* Program located, but not usable. */ #define EXIT_ENOENT 127 /* Could not find program to exec. */ int main(int argc, char **argv) { - char *altpath, **ep, *p, **parg, term; + char *altpath, *altwd, **ep, *p, **parg, term; char *cleanenv[1]; char *login_class, *login_name; struct passwd *pw; login_cap_t *lc; bool login_as_user; uid_t uid; int ch, want_clear; int rtrn; altpath = NULL; + altwd = NULL; login_class = NULL; login_name = NULL; pw = NULL; lc = NULL; login_as_user = false; want_clear = 0; term = '\n'; - while ((ch = getopt(argc, argv, "-0iL:P:S:U:u:v")) != -1) + while ((ch = getopt(argc, argv, "-0C:iL:P:S:U:u:v")) != -1) switch(ch) { case '-': case 'i': want_clear = 1; break; case '0': term = '\0'; break; + case 'C': + altwd = optarg; + break; case 'U': login_as_user = true; /* FALLTHROUGH */ case 'L': login_name = optarg; break; case 'P': - altpath = strdup(optarg); + altpath = optarg; break; case 'S': /* * The -S option, for "split string on spaces, with * support for some simple substitutions"... */ split_spaces(optarg, &optind, &argc, &argv); break; case 'u': if (env_verbosity) fprintf(stderr, "#env unset:\t%s\n", optarg); rtrn = unsetenv(optarg); if (rtrn == -1) err(EXIT_FAILURE, "unsetenv %s", optarg); break; case 'v': env_verbosity++; if (env_verbosity > 1) fprintf(stderr, "#env verbosity now at %d\n", env_verbosity); break; case '?': default: usage(); } if (want_clear) { environ = cleanenv; cleanenv[0] = NULL; if (env_verbosity) fprintf(stderr, "#env clearing environ\n"); } if (login_name != NULL) { login_class = strchr(login_name, '/'); if (login_class) *login_class++ = '\0'; if (*login_name != '\0' && strcmp(login_name, "-") != 0) { pw = getpwnam(login_name); if (pw == NULL) { char *endp = NULL; errno = 0; uid = strtoul(login_name, &endp, 10); if (errno == 0 && *endp == '\0') pw = getpwuid(uid); } if (pw == NULL) errx(EXIT_FAILURE, "no such user: %s", login_name); } /* * Note that it is safe for pw to be null here; the libutil * code handles that, bypassing substitution of $ and using * the class "default" if no class name is given either. */ if (login_class != NULL) { lc = login_getclass(login_class); if (lc == NULL) errx(EXIT_FAILURE, "no such login class: %s", login_class); } else { lc = login_getpwclass(pw); if (lc == NULL) errx(EXIT_FAILURE, "login_getpwclass failed"); } /* * This is not done with setusercontext() because that will * try and use ~/.login_conf even when we don't want it to. */ setclassenvironment(lc, pw, 1); setclassenvironment(lc, pw, 0); if (login_as_user) { login_close(lc); if ((lc = login_getuserclass(pw)) != NULL) { setclassenvironment(lc, pw, 1); setclassenvironment(lc, pw, 0); } } endpwent(); if (lc != NULL) login_close(lc); } for (argv += optind; *argv && (p = strchr(*argv, '=')); ++argv) { if (env_verbosity) fprintf(stderr, "#env setenv:\t%s\n", *argv); *p = '\0'; rtrn = setenv(*argv, p + 1, 1); *p = '='; if (rtrn == -1) err(EXIT_FAILURE, "setenv %s", *argv); } if (*argv) { if (term == '\0') errx(EXIT_CANCELED, "cannot specify command with -0"); + if (altwd && chdir(altwd) != 0) + err(EXIT_CANCELED, "cannot change directory to '%s'", + altwd); if (altpath) search_paths(altpath, argv); if (env_verbosity) { fprintf(stderr, "#env executing:\t%s\n", *argv); for (parg = argv, argc = 0; *parg; parg++, argc++) fprintf(stderr, "#env arg[%d]=\t'%s'\n", argc, *parg); if (env_verbosity > 1) sleep(1); } execvp(*argv, argv); err(errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE, "%s", *argv); + } else { + if (altwd) + errx(EXIT_CANCELED, "must specify command with -C"); + if (altpath) + errx(EXIT_CANCELED, "must specify command with -P"); } for (ep = environ; *ep; ep++) (void)printf("%s%c", *ep, term); + if (fflush(stdout) != 0) + err(1, "stdout"); exit(0); } static void usage(void) { (void)fprintf(stderr, - "usage: env [-0iv] [-L|-U user[/class]] [-P utilpath] [-S string] [-u name]\n" - " [name=value ...] [utility [argument ...]]\n"); + "usage: env [-0iv] [-C workdir] [-L|-U user[/class]] [-P utilpath] [-S string]\n" + " [-u name] [name=value ...] [utility [argument ...]]\n"); exit(1); } diff --git a/usr.bin/env/tests/Makefile b/usr.bin/env/tests/Makefile new file mode 100644 index 000000000000..3d2f77b34b67 --- /dev/null +++ b/usr.bin/env/tests/Makefile @@ -0,0 +1,6 @@ +PACKAGE= tests + +ATF_TESTS_SH= env_test +BINDIR= ${TESTSDIR} + +.include diff --git a/usr.bin/env/tests/env_test.sh b/usr.bin/env/tests/env_test.sh new file mode 100644 index 000000000000..2dc8f1a4c911 --- /dev/null +++ b/usr.bin/env/tests/env_test.sh @@ -0,0 +1,160 @@ +# +# Copyright (c) 2024 Klara, Inc. +# +# SPDX-License-Identifier: BSD-2-Clause +# + +magic_words="Squeamish $$ Ossifrage" + +atf_test_case basic +basic_head() +{ + atf_set "descr" "Basic test case" +} +basic_body() +{ + atf_check -o match:"^magic_words=${magic_words}\$" \ + env magic_words="${magic_words}" + export MAGIC_WORDS="${magic_words}" + atf_check -o match:"^MAGIC_WORDS=${magic_words}\$" \ + env + unset MAGIC_WORDS +} + +atf_test_case unset +unset_head() +{ + atf_set "descr" "Unset a variable" +} +unset_body() +{ + export MAGIC_WORDS="${magic_words}" + atf_check -o not-match:"^MAGIC_WORDS=" \ + env -u MAGIC_WORDS + unset MAGIC_WORDS +} + +atf_test_case empty +empty_head() +{ + atf_set "descr" "Empty environment" +} +empty_body() +{ + atf_check env -i +} + +atf_test_case true +true_head() +{ + atf_set "descr" "Run true" +} +true_body() +{ + atf_check env true +} + +atf_test_case false +false_head() +{ + atf_set "descr" "Run false" +} +false_body() +{ + atf_check -s exit:1 env false +} + +atf_test_case false +false_head() +{ + atf_set "descr" "Run false" +} +false_body() +{ + atf_check -s exit:1 env false +} + +atf_test_case altpath +altpath_head() +{ + atf_set "descr" "Use alternate path" +} +altpath_body() +{ + echo "echo ${magic_words}" >magic_words + chmod 0755 magic_words + atf_check -s exit:125 -e match:"must specify command" \ + env -P "${PWD}" + atf_check -s exit:127 -e match:"No such file" \ + env magic_words + atf_check -o inline:"${magic_words}\n" \ + env -P "${PWD}" magic_words +} + +atf_test_case equal +equal_head() +{ + atf_set "descr" "Command name contains equal sign" +} +equal_body() +{ + echo "echo ${magic_words}" >"magic=words" + chmod 0755 "magic=words" + atf_check -o match:"^${PWD}/magic=words$" \ + env "${PWD}/magic=words" + atf_check -s exit:125 -e match:"must specify command" \ + env -P "${PATH}:${PWD}" "magic=words" + atf_check -o inline:"${magic_words}\n" \ + env command "${PWD}/magic=words" + atf_check -o inline:"${magic_words}\n" \ + env PATH="${PATH}:${PWD}" command "magic=words" +} + +atf_test_case chdir +chdir_head() +{ + atf_set "descr" "Change working directory" +} +chdir_body() +{ + local subdir="dir.$$" + atf_check -o inline:"${PWD}\n" \ + env pwd + atf_check -s exit:125 -e match:"must specify command" \ + env -C "${subdir}" + atf_check -s exit:125 \ + -e match:"cannot change directory to '${subdir}':" \ + env -C "${subdir}" pwd + atf_check mkdir "${subdir}" + atf_check -o inline:"${PWD}/${subdir}\n" \ + env -C "${subdir}" pwd +} + +atf_test_case stdout +stdout_head() +{ + atf_set descr "Failure to write to stdout" +} +stdout_body() +{ + ( + trap "" PIPE + env 2>stderr + echo $? >result + ) | true + atf_check -o inline:"1\n" cat result + atf_check -o match:"stdout" cat stderr +} + +atf_init_test_cases() +{ + atf_add_test_case basic + atf_add_test_case unset + atf_add_test_case empty + atf_add_test_case true + atf_add_test_case false + atf_add_test_case altpath + atf_add_test_case equal + atf_add_test_case chdir + atf_add_test_case stdout +}