tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[PATCH] Allow qualified filename (bar/foo) PATH searching.



POSIX requires that PATH searching be done only if the identifier
(aka "file") has no '/' (if it has, it is a "path").
    
Allow searching qualified filenames, i.e. Resource Identifier,
like dk/ctl, vect/in/dxf by setting an environment variable
PATH_SEARCH_OPT with the setting 'Q' in the definition.
    
It works in all the three shells (sh(1), ksh(1) and csh(1)) as
well as with all the utilities using exec[lv]p or posit_spawnp (also
then system(3)).
    
A man page pathsearch(7) is added explaining this all.

The diff is attached and this can be also retrieved as:
commit 502cb391d594a2722cca46e135f79c02516981d5

with https://github.com/tlaronde/BeSiDe

diff --git a/include/pathsearch.h b/include/pathsearch.h
new file mode 100644
index 000000000000..59df79021843
--- /dev/null
+++ b/include/pathsearch.h
@@ -0,0 +1,34 @@
+/*
+ * Written by Thierry Laronde <tlaronde%kergis.com@localhost> 2024-09
+ * Public domain.
+ * See pathsearch(7).
+ */
+
+#ifndef _PATHSEARCH_H_
+#define	_PATHSEARCH_H_
+
+#include <string.h>
+
+/*
+ * For the feature testing macros, the parameters are environment
+ * variable values of the same but upper case name.
+ */
+#define PATH_SEARCH_OPT_QFILENAME 'q'
+#define PATH_SEARCH_OPT_QFILENAME_FORCED 'Q'
+#define	PATH_SEARCH_QFILENAME_ON(path_search_opt) \
+( (path_search_opt) != NULL \
+	&& strchr(path_search_opt, PATH_SEARCH_OPT_QFILENAME_FORCED) != NULL )
+
+/*
+ * A qualified filename should be considered a Resource Identifier
+ * to be contrasted to a Locator (where a resource lies in the
+ * namespace). See pathsearch(7).
+ */
+#define PATH_SEARCH_IS_QFILENAME(name, name_len) \
+	( (name) != NULL && (name_len) != 0 \
+		&& (name)[0] != '/' && strstr(name, "./") == NULL \
+		&& (name)[(name_len)-1] != '/' \
+		&& (name)[(name_len)-1] != '.' )
+
+#endif /* !_PATHSEARCH_H_ */
+
diff --git a/share/man/man7/pathsearch.7 b/share/man/man7/pathsearch.7
new file mode 100644
index 000000000000..6ecc313e772b
--- /dev/null
+++ b/share/man/man7/pathsearch.7
@@ -0,0 +1,234 @@
+.\"
+.\" Public Domain.
+.\"
+.Dd September 29, 2024
+.Dt PATHSEARCH 7
+.Os
+.Sh NAME
+.Nm pathsearch
+.Nd when and how the search of a file to execute is handled.
+.Sh DESCRIPTION
+A program to execute is given to a shell, to one of the
+.Xr exec 3
+or 
+.Xr posix_spawn 3
+family of functions in two forms: either as a
+.Em locator
+specifying exactly (even if implicitely) where the file is, or as an
+.Em identifier
+to be searched in order to be located. (POSIX generally use
+.Em path
+as a mean to imply a locator and
+.Em file
+to imply an identifier.)
+.Pp
+The shells
+.Xr sh 1 ,
+.Xr ksh 1 ,
+.Xr csh 1 ,
+as well as the variants of the
+.Xr execlp 3
+and
+.Xr execvp 3
+functions, and the
+.Xr posix_spawnp 3
+function (used by the implementation of
+.Xr system 3 )
+handle both locators and identifiers. The other variants of
+.Xr exec 3
+or
+.Xr posix_spawn 3
+expect only locators and do no path searching.
+.Pp
+For the path searching, two environment variables drive the searching:
+.Em PATH
+and
+.Em PATH_SEARCH_OPT .
+.Pp
+In order for the searching to be done, the command given as argument
+has to be identified as an identifier, and not as a locator.
+.Pp
+The traditional POSIX rule for this is simple: when path searching is
+to be attempted, if the command name given does not contain a
+.Ql \&/ ,
+it is considered an identifier and is then searched. Otherwise, it is a
+locator and no search is done.
+.Pp
+Hence, with the traditional behavior, given a command as 
+.Qq bar/foo ,
+no path searching would be done and this will be tried in the current
+working directory.
+.Pp
+The rules concerning what is an identifier can be changed using the
+.Em PATH_SEARCH_OPT
+variable. See below
+.Sx Extending identifiers to qualified filenames .
+.Pp
+When the argument is considered an identifier (according to whatever
+rule), it is not searched everywhere but only in the directories
+specified in the
+.Em PATH
+environment variable.
+.Pp
+The
+.Em PATH
+is a sequence of colon
+.Ql \&:
+separated directory pathnames to be tried, in turn, for locating the
+identified resource. Initially, it is set to
+.Pp
+.Dl /usr/bin:/bin:/usr/pkg/bin:/usr/local/bin
+by
+.Xr login 1 .
+It can be redefined later.
+.Pp
+An empty dir specification (that can be expressed in PATH by a leading
+or a trailing colon, or by two consecutive colons) means the current
+working directory. This is considered a security risk since, by the very
+nature of the current working, what will be executed depends on the
+context.  It is thus highly recommended to verify that an empty dir
+alternative has not slipped by mistake in the PATH definition.
+.Pp
+The
+.Xr sh 1
+syntax allows for example to interpolate a dir specification via a
+variable, but offers means to protect the PATH from having by mistake
+a current working dir definition because the variable is not defined
+or empty, in this way:
+.Pp
+.Dl PATH="/bin:/usr/bin:${MYSCRIPTS:+${MYSCRIPTS}:}/usr/pkg/bin"
+.Pp
+or
+.Pp
+.Dl PATH="/bin:/usr/bin${MYSCRIPTS:+:${MYSCRIPTS}}:/usr/pkg/bin"
+.Pp
+The trick here is that the variable is only expanded if it is
+defined and not empty, and, in this case only, a
+.Ql \&:
+colon is added to its definition. The result being that if the
+.Em MYSCRIPTS
+variable were not defined or empty, this will not result in two
+consecutive colons, adding by mistake the current working directory as
+an alternative dir to search in. See
+.Xr sh 1
+for an explanation of the syntax.
+.Ss Extending identifiers to qualified filenames
+The environment variable
+.Em PATH_SEARCH_OPT
+can be defined to change the path searching.
+.Pp
+The definition of the PATH_SEARCH_OPT is a string of ASCII letters
+(the options are case sensitive).
+.Pp
+At the moment, only one option is defined:
+.Bl -tag -width "Q"
+.It Q
+The rule identifying an identifier is changed to allow qualified
+filenames like
+.Qq bar/foo
+to be searched for with the PATH. A
+.Em qualified filename
+is a Resource Identifier (not a locator) consisting of a sequence
+of one or more components separated by
+.Ql / ,
+the sequence not starting or ending by a
+.Ql /
+and any component not ending by a
+.Ql \&. .
+Thus
+.Qq fs/create ,
+.Qq fs/ck ,
+.Qq fs/mount ,
+.Qq fs/ctl
+are all qualified filenames, as well as
+.Qq texlive/latex
+or
+.Qq kertex/latex ,
+as well as (G.R.A.S.S. example)
+.Qq vect/in/dxf .
+But 
+.Qq /vect/in/dxf
+is not an identifier but a locator, as well as
+.Qq ./vect/in/dxf
+or
+.Qq vect/in/dxf.
+in this latter case because of the trailing dot of the last component.
+.El
+.Sh IMPLEMENTATION NOTES
+Since the behavior can not be changed only at a shell level, there
+has to be a mean to inform both the shell and the libc to change the
+behavior. Hence the
+.Em PATH_SEARCH_OPT
+environment variable. And since it would be suboptimal to create
+another variable when something has to be optionally changed in the way
+the path is searched, the variable has to have a definition.
+.Pp
+The upper case was chosen because, initially, there was a lower case
+variant meaning: do qualified filename searching if not
+.Em POSIXLY_CORRECT .
+But if this extended behavior is not, strictly speaking, POSIX
+compliant, it is not a violation of the spirit: a qualified filename
+is an identifier, not a locator that is a path wandering instruction; a
+qualified filename can not escape the directory. In this sense, and
+without judging the relevance of the notion of a
+.Qq restricted shell ,
+as is expressed, for example, in
+.Xr ksh 1 ,
+a qualified filename is neither absolute nor relative, so is
+compatible with the notion. So the lower case variant was dropped, but
+the principle of distinguishing between lower case (conditional) and
+upper case (mandatory) is retained for possible further extensions.
+.Pp
+The new rule was designed so it can match the meaning (an identifier
+not a locator), be compatible with a Unix filesystem hierarchy, and
+easy to test. The restriction of no ending
+.Ql \&.
+was initially for the simplicity and thus efficiency of testing. But
+it makes some sense. 
+.Ql \&.
+and
+.Qq \&.\&.
+and not identifiers (they are not uniq and do not exist as names in
+any filesystem) but are
+.Qq pronoun
+locators. They end with a dot. So any sequence ending with a dot can
+be reserved for the group of pronoun locators allowing extensions
+of the notion.
+.Pp
+The fact that, when extended filenames search is requested, a command
+passed as
+.Qq bar/foo
+will be searched and not executed in the current directory, thus
+imposing, if it was actually the intention to do so, to specify it as
+.Qq ./bar/foo
+is not considered a slight inconvenient but an improvement.
+.Sh SEE ALSO
+.Xr environ 7 ,
+.Xr exec 3 ,
+.Xr posix_spawn 3 ,
+.Xr sh 1 ,
+.Xr ksh 1 ,
+.Xr csh 1 .
+.Sh SOURCES
+.Pa include/pathsearch.h
+.Pp lib/libc/gen/execvp.c
+.Pp lib/libc/gen/posix_spawnp.c
+.Pp bin/sh/exec.c
+.Pp bin/ksh/exec.c
+.Pp bin/csh/exec.c
+.Sh HISTORY
+Allowing qualified filenames for commands to be searched (not exactly
+with the same rules) was first encountered by the author in the
+.Em Plan9
+system.
+.Sh AUTHOR
+.An "Thierry Laronde" Aq Mt tlaronde%kergis.com@localhost .
+.Pp
+The
+.Xr sh 1
+way of defining the
+.Ev PATH
+with interpolated variables, protecting from undefined ones, comes
+from
+.An "Robert Elz" Aq Mt kre%munnari.OZ.AU@localhost .
+
diff --git a/bin/csh/exec.c b/bin/csh/exec.c
index bd06892a1c92..b45e1d3d5222 100644
--- a/bin/csh/exec.c
+++ b/bin/csh/exec.c
@@ -45,6 +45,7 @@ __RCSID("$NetBSD: exec.c,v 1.33 2019/01/05 16:54:00 christos Exp $");
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <pathsearch.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -55,7 +56,8 @@ __RCSID("$NetBSD: exec.c,v 1.33 2019/01/05 16:54:00 christos Exp $");
 
 /*
  * System level search and execute of a command.  We look in each directory
- * for the specified command name.  If the name contains a '/' then we
+ * for the specified command name.  If the name contains a '/' and
+ * qualified filenames is not on (see pathsearch.7) then we
  * execute only the full path name.  If there is no search path then we
  * execute only full path names.
  */
@@ -194,13 +196,26 @@ doexec(Char **v, struct command *t)
     sigemptyset(&nsigset);
     (void)sigprocmask(SIG_SETMASK, &nsigset, NULL);
     /*
-     * If no path, no words in path, or a / in the filename then restrict the
-     * command search.
+     * If no path, no words in path, or a / in the filename but not
+     * doing qualified filename, then restrict the command search.
      */
-    if (pathv == 0 || pathv->vec[0] == 0 || slash)
+    {
+    const char *path_search_opt;
+    size_t ln;
+    int do_qfilename;
+
+    ln = strlen(short2str(*av));
+
+    path_search_opt = getenv("PATH_SEARCH_OPT");
+    do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+	&& PATH_SEARCH_IS_QFILENAME(short2str(*av), ln);
+
+    if (pathv == 0 || pathv->vec[0] == 0
+	|| (do_qfilename == 0 && slash) )
 	pv = justabs;
     else
 	pv = pathv->vec;
+    }
     sav = Strspl(STRslash, *av); 	/* / command name for postpending */
     Vsav = sav;
     if (havhash)
@@ -510,12 +525,21 @@ iscommand(Char *name)
     Char **pv, *sav;
     int hashval, hashval1, i;
     int slash;
+    const char *path_search_opt;
+    size_t ln;
+    int do_qfilename;
 
     hashval = 0;
     slash = any(short2str(name), '/');
     v = adrof(STRpath);
     
-    if (v == 0 || v->vec[0] == 0 || slash)
+    ln = strlen(short2str(name));
+
+    path_search_opt = getenv("PATH_SEARCH_OPT");
+    do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(short2str(name), ln);
+
+    if (v == 0 || v->vec[0] == 0 || (do_qfilename == 0 && slash) )
 	pv = justabs;
     else
 	pv = v->vec;
@@ -700,10 +724,20 @@ tellmewhat(struct wordent *lexp, Char *str)
     if ((i = iscommand(sp->word)) != 0) {
 	Char **pv;
 	struct varent *v;
+	const char *path_search_opt;
+	size_t ln;
+	int do_qfilename;
 	int    slash = any(short2str(sp->word), '/');
 
+	ln = strlen(short2str(sp->word));
+
+	path_search_opt = getenv("PATH_SEARCH_OPT");
+	do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(short2str(sp->word), ln);
+
+
 	v = adrof(STRpath);
-	if (v == 0 || v->vec[0] == 0 || slash)
+	if (v == 0 || v->vec[0] == 0 || (do_qfilename == 0 && slash) )
 	    pv = justabs;
 	else
 	    pv = v->vec;
diff --git a/bin/ksh/exec.c b/bin/ksh/exec.c
index 721f301f393f..0bf7d6e0cecd 100644
--- a/bin/ksh/exec.c
+++ b/bin/ksh/exec.c
@@ -11,6 +11,7 @@ __RCSID("$NetBSD: exec.c,v 1.28 2018/06/03 12:18:29 kamil Exp $");
 
 #include <sys/stat.h>
 #include <ctype.h>
+#include <pathsearch.h>
 #include <stdbool.h>
 
 #include "sh.h"
@@ -575,7 +576,17 @@ comexec(t, tp, ap, flags)
 		rv = subst_exstat;
 		goto Leave;
 	} else if (!tp) {
-		if (Flag(FRESTRICTED) && ksh_strchr_dirsep(cp)) {
+		const char *path_search_opt;
+		int ln;
+		int do_qfilename;
+
+		ln = strlen(cp);
+		path_search_opt = str_val(global("PATH_SEARCH_OPT"));
+		do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+			&& PATH_SEARCH_IS_QFILENAME(cp, ln);
+
+		if (Flag(FRESTRICTED) && do_qfilename == 0
+			&& ksh_strchr_dirsep(cp)) {
 			warningf(true, "%s: restricted", cp);
 			rv = 1;
 			goto Leave;
@@ -899,8 +910,16 @@ findcom(name, flags)
 	int insert = Flag(FTRACKALL);	/* insert if not found */
 	char *fpath;			/* for function autoloading */
 	char *npath;
+	const char *path_search_opt;
+	size_t ln;
+	int do_qfilename;
+
+ 	ln = strlen(name);
+	path_search_opt = str_val(global("PATH_SEARCH_OPT"));
+	do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(name, ln);
 
-	if (ksh_strchr_dirsep(name) != NULL) {
+	if (do_qfilename == 0 && ksh_strchr_dirsep(name) != NULL) {
 		insert = 0;
 		/* prevent FPATH search below */
 		flags &= ~FC_FUNC;
@@ -1050,18 +1069,26 @@ search(name, pathx, mode, errnop)
 	const char *sp, *p;
 	char *xp;
 	XString xs;
+	const char *path_search_opt;
 	int namelen;
+	int do_qfilename;
 
 	if (errnop)
 		*errnop = 0;
 
-	if (ksh_strchr_dirsep(name)) {
+	namelen = strlen(name);
+
+	path_search_opt = str_val(global("PATH_SEARCH_OPT"));
+	do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(name, namelen);
+
+	if (do_qfilename == 0 && ksh_strchr_dirsep(name)) {
 		if (search_access(name, mode, errnop) == 0)
 			return (char *)__UNCONST(name);
 		return NULL;
 	}
 
-	namelen = strlen(name) + 1;
+	++namelen;	/* to include terminal nul */
 	Xinit(xs, xp, 128, ATEMP);
 
 	sp = pathx;
diff --git a/bin/sh/exec.c b/bin/sh/exec.c
index 4b1c5885371c..09dc0cd09a66 100644
--- a/bin/sh/exec.c
+++ b/bin/sh/exec.c
@@ -47,6 +47,7 @@ __RCSID("$NetBSD: exec.c,v 1.59 2024/07/12 07:30:30 kre Exp $");
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <pathsearch.h>	/* PATH_SEARCH_OPT */
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -125,12 +126,26 @@ void
 shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
 {
 	char *cmdname;
+#ifndef SMALL
+	const char *path_search_opt;
+	size_t ln;
+	int do_qfilename;
+#endif
 	int e, action;
 	struct stat statb;
 
 	action = E_EXEC;
 
+#ifndef SMALL
+ 	ln = strlen(argv[0]);
+	path_search_opt = lookupvar("PATH_SEARCH_OPT");
+	do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(argv[0], ln);
+
+	if (do_qfilename == 0 && strchr(argv[0], '/') != NULL) {
+#else
 	if (strchr(argv[0], '/') != NULL) {
+#endif
 		tryexec(argv[0], argv, envp, vforked);
 		e = errno;
 		if (e == EACCES && stat(argv[0], &statb) == -1)
@@ -574,9 +589,27 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
 	struct stat statb;
 	int e;
 	int (*bltin)(int,char **);
+#ifndef SMALL
+	const char *path_search_opt;
+	size_t ln;
+	int do_qfilename;
+#endif
 
+#ifndef SMALL
+ 	ln = strlen(name);
+	path_search_opt = lookupvar("PATH_SEARCH_OPT");
+	do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(name, ln);
+
+	/*
+	 * If not searching for a qualified filename, then if the name
+	 * contains a slash, don't use PATH or hash table.
+	 */
+	if (do_qfilename == 0 && strchr(name, '/') != NULL) {
+#else
 	/* If name contains a slash, don't use PATH or hash table */
 	if (strchr(name, '/') != NULL) {
+#endif
 		if (act & DO_ABS) {
 			while (stat(name, &statb) < 0) {
 #ifdef SYSV
diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi
index a2274fb94a4b..021b9d2eea1f 100644
--- a/distrib/sets/lists/comp/mi
+++ b/distrib/sets/lists/comp/mi
@@ -3191,6 +3191,7 @@
 ./usr/include/openssl/x509v3err.h		comp-c-include
 ./usr/include/panel.h				comp-c-include
 ./usr/include/paths.h				comp-c-include
+./usr/include/pathsearch.h			comp-c-include
 ./usr/include/pcap-namedb.h			comp-c-include
 ./usr/include/pcap.h				comp-c-include
 ./usr/include/pcap/bpf.h			comp-c-include
diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi
index e1d4e9c3e2f1..3ddc26880564 100644
--- a/distrib/sets/lists/man/mi
+++ b/distrib/sets/lists/man/mi
@@ -6148,6 +6148,7 @@
 ./usr/share/man/man7/openssl-threads.7		man-crypto-man		.man,openssl=30
 ./usr/share/man/man7/orders.7			man-reference-man	.man
 ./usr/share/man/man7/packages.7			man-obsolete		obsolete
+./usr/share/man/man7/pathsearch.7		man-reference-man
 ./usr/share/man/man7/pcap-filter.7		man-netutil-man		.man
 ./usr/share/man/man7/pcap-linktype.7		man-netutil-man		.man
 ./usr/share/man/man7/pcap-tstamp.7		man-netutil-man		.man
diff --git a/distrib/sets/lists/manhtml/mi b/distrib/sets/lists/manhtml/mi
index 0427facdeab2..9c1d76b19182 100644
--- a/distrib/sets/lists/manhtml/mi
+++ b/distrib/sets/lists/manhtml/mi
@@ -2370,6 +2370,7 @@
 ./usr/share/man/html7/openssl-glossary.html	man-crypto-htmlman		html,openssl=30
 ./usr/share/man/html7/openssl-threads.html	man-crypto-htmlman		html,openssl=30
 ./usr/share/man/html7/orders.html		man-reference-htmlman	html
+./usr/share/man/html7/pathsearch.html		man-reference-htmlman	html
 ./usr/share/man/html7/pcap-filter.html		man-netutil-htmlman	html
 ./usr/share/man/html7/pcap-linktype.html	man-netutil-htmlman	html
 ./usr/share/man/html7/pcap-tstamp.html		man-netutil-htmlman	html
diff --git a/include/Makefile b/include/Makefile
index f7e000fb2cb2..1ac92f38c095 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -16,7 +16,7 @@ INCS=	a.out.h aio.h ar.h assert.h atomic.h \
 	login_cap.h lwp.h malloc.h math.h md2.h \
 	memory.h mntopts.h monetary.h mpool.h mqueue.h \
 	ndbm.h netconfig.h netdb.h netgroup.h nlist.h nl_types.h nsswitch.h \
-	paths.h pwd.h quota.h randomid.h ranlib.h re_comp.h regex.h regexp.h \
+	paths.h pathsearch.h pwd.h quota.h randomid.h ranlib.h re_comp.h regex.h regexp.h \
 	resolv.h res_update.h rmt.h sched.h search.h semaphore.h setjmp.h \
 	string.h sgtty.h signal.h spawn.h stab.h stddef.h stdio.h \
 	stdlib.h stdnoreturn.h strings.h stringlist.h struct.h sysexits.h \
diff --git a/lib/libc/gen/execvp.c b/lib/libc/gen/execvp.c
index d29e2a0fc6d2..3219a99e7796 100644
--- a/lib/libc/gen/execvp.c
+++ b/lib/libc/gen/execvp.c
@@ -47,6 +47,7 @@ __RCSID("$NetBSD: execvp.c,v 1.32 2024/01/20 14:52:47 christos Exp $");
 #include <limits.h>
 #include <unistd.h>
 #include <paths.h>
+#include <pathsearch.h>
 #include "reentrant.h"
 #include "extern.h"
 
@@ -62,9 +63,11 @@ execvpe(const char *name, char * const *argv, char * const * envp)
 	int cnt;
 	size_t lp, ln;
 	int eacces = 0;
+	int do_qfilename;
 	unsigned int etxtbsy = 0;
 	char buf[PATH_MAX];
 	const char *bp, *path, *p;
+	const char *path_search_opt;
 
 	_DIAGASSERT(name != NULL);
 
@@ -74,8 +77,16 @@ execvpe(const char *name, char * const *argv, char * const * envp)
 		goto done;
 	}
 	ln = strlen(name);
-	/* If it's an absolute or relative path name, it's easy. */
-	if (strchr(name, '/')) {
+
+	path_search_opt = getenv("PATH_SEARCH_OPT");
+	do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(name, ln);
+
+	/* 
+	 * If it's an absolute or relative path name (and not a
+	 * qualified filename and asked to search for it), it's easy.
+	 */
+	if (do_qfilename == 0 && strchr(name, '/')) {
 		bp = name;
 		path = "";
 		goto retry;
diff --git a/lib/libc/gen/posix_spawnp.c b/lib/libc/gen/posix_spawnp.c
index 2474bbe6970c..7e7e20e04313 100644
--- a/lib/libc/gen/posix_spawnp.c
+++ b/lib/libc/gen/posix_spawnp.c
@@ -39,6 +39,7 @@ __RCSID("$NetBSD: posix_spawnp.c,v 1.4 2020/05/11 14:54:34 kre Exp $");
 #include <assert.h>
 #include <errno.h>
 #include <paths.h>
+#include <pathsearch.h>
 #include <spawn.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -53,15 +54,23 @@ int posix_spawnp(pid_t * __restrict pid, const char * __restrict file,
 {
 	char fpath[FILENAME_MAX];
 	const char *path, *p;
+	const char *path_search_opt;
 	size_t lp, ln;
 	int err;
+	int do_qfilename;
 
 	_DIAGASSERT(file != NULL);
 
+	ln = strlen(file);
+	path_search_opt = getenv("PATH_SEARCH_OPT");
+	do_qfilename = PATH_SEARCH_QFILENAME_ON(path_search_opt)
+		&& PATH_SEARCH_IS_QFILENAME(file, ln);
+
 	/*
-	 * If there is a / in the name, fall straight through to posix_spawn().
+	 * If not treating a qualified filename, and there is a / in
+	 *  the name, fall straight through to posix_spawn().
 	 */
-	if (strchr(file, '/') != NULL)
+	if (do_qfilename == 0 && strchr(file, '/') != NULL)
 		return posix_spawn(pid, file, fa, sa, cav, env);
 
 	/* Get the path we're searching. */
@@ -72,7 +81,6 @@ int posix_spawnp(pid_t * __restrict pid, const char * __restrict file,
 	 * Find an executable image with the given name in the PATH
 	 */
 
-	ln = strlen(file);
 	err = 0;
 	do {
 		/* Find the end of this path element. */
diff --git a/share/man/man7/Makefile b/share/man/man7/Makefile
index 9a8ab4202f52..d40457be1d81 100644
--- a/share/man/man7/Makefile
+++ b/share/man/man7/Makefile
@@ -20,6 +20,7 @@ MAN+=	module.7
 MAN+=	nls.7
 MAN+=	operator.7
 MAN+=	orders.7
+MAN+=	pathsearch.7
 MAN+=	pkgsrc.7
 MAN+=	release.7
 MAN+=	rfc6056.7
diff --git a/share/man/man7/environ.7 b/share/man/man7/environ.7
index b1c06f50ed4d..f88dbe818528 100644
--- a/share/man/man7/environ.7
+++ b/share/man/man7/environ.7
@@ -29,7 +29,7 @@
 .\"
 .\"	@(#)environ.7	8.3 (Berkeley) 4/19/94
 .\"
-.Dd January 21, 2011
+.Dd September 29, 2024
 .Dt ENVIRON 7
 .Os
 .Sh NAME
@@ -174,6 +174,9 @@ is set to
 .Pp
 initially by
 .Xr login 1 .
+.It Ev PATH_SEARCH_OPT
+options to change the way path searching is done. Initially unset. See
+.Xr pathsearch 7 .
 .It Ev PRINTER
 The name of the default printer to be used by
 .Xr lpr 1 ,
-- 
        Thierry Laronde <tlaronde +AT+ kergis +dot+ com>
                     http://www.kergis.com/
                    http://kertex.kergis.com/
Key fingerprint = 0FF7 E906 FBAF FE95 FD89  250D 52B1 AE95 6006 F40C


Home | Main Index | Thread Index | Old Index