diff --git a/binfmt/binfmt_exec.c b/binfmt/binfmt_exec.c index 5b23488891f..222a7de72b0 100644 --- a/binfmt/binfmt_exec.c +++ b/binfmt/binfmt_exec.c @@ -212,6 +212,9 @@ errout: * program. * argv - A pointer to an array of string arguments. The end of the * array is indicated with a NULL entry. + * envp - An array of character pointers to null-terminated strings + * that provide the environment for the new process image. + * The environment array is terminated by a null pointer. * exports - The address of the start of the caller-provided symbol * table. This symbol table contains the addresses of symbols * exported by the caller and made available for linking the @@ -226,11 +229,12 @@ errout: ****************************************************************************/ int exec(FAR const char *filename, FAR char * const *argv, - FAR const struct symtab_s *exports, int nexports) + FAR char * const *envp, FAR const struct symtab_s *exports, + int nexports) { int ret; - ret = exec_spawn(filename, argv, NULL, exports, nexports, NULL); + ret = exec_spawn(filename, argv, envp, exports, nexports, NULL); if (ret < 0) { set_errno(-ret); diff --git a/include/nuttx/binfmt/binfmt.h b/include/nuttx/binfmt/binfmt.h index 6ebeec446ec..8c17d8f53bf 100644 --- a/include/nuttx/binfmt/binfmt.h +++ b/include/nuttx/binfmt/binfmt.h @@ -309,6 +309,9 @@ int exec_module(FAR const struct binary_s *binp, * program. * argv - A pointer to an array of string arguments. The end of the * array is indicated with a NULL entry. + * envp - An array of character pointers to null-terminated strings + * that provide the environment for the new process image. + * The environment array is terminated by a null pointer. * exports - The address of the start of the caller-provided symbol * table. This symbol table contains the addresses of symbols * exported by the caller and made available for linking the @@ -322,7 +325,8 @@ int exec_module(FAR const struct binary_s *binp, ****************************************************************************/ int exec(FAR const char *filename, FAR char * const *argv, - FAR const struct symtab_s *exports, int nexports); + FAR char * const *envp, FAR const struct symtab_s *exports, + int nexports); /**************************************************************************** * Name: exec_spawn diff --git a/include/sys/syscall_lookup.h b/include/sys/syscall_lookup.h index d183f0931e8..201ea50846a 100644 --- a/include/sys/syscall_lookup.h +++ b/include/sys/syscall_lookup.h @@ -151,7 +151,7 @@ SYSCALL_LOOKUP(up_assert, 2) #endif #ifdef CONFIG_LIBC_EXECFUNCS SYSCALL_LOOKUP(posix_spawn, 6) - SYSCALL_LOOKUP(execv, 2) + SYSCALL_LOOKUP(execve, 3) #endif #endif diff --git a/include/unistd.h b/include/unistd.h index d2fd98827b2..de35eb42dd5 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -368,7 +368,10 @@ int lchown(FAR const char *path, uid_t owner, gid_t group); #ifdef CONFIG_LIBC_EXECFUNCS int execl(FAR const char *path, FAR const char *arg0, ...); +int execle(FAR const char *path, FAR const char *arg0, ...); int execv(FAR const char *path, FAR char * const argv[]); +int execve(FAR const char *path, FAR char *const argv[], + FAR char *const envp[]); #endif /* Byte operations */ diff --git a/libs/libc/unistd/Make.defs b/libs/libc/unistd/Make.defs index 35e9e6d5ec0..926f1c7b9c9 100644 --- a/libs/libc/unistd/Make.defs +++ b/libs/libc/unistd/Make.defs @@ -39,7 +39,7 @@ CSRCS += lib_chdir.c lib_getcwd.c lib_restoredir.c endif ifeq ($(CONFIG_LIBC_EXECFUNCS),y) -CSRCS += lib_execl.c +CSRCS += lib_execl.c lib_execle.c lib_execv.c endif ifneq ($(CONFIG_DISABLE_MOUNTPOINTS),y) diff --git a/libs/libc/unistd/lib_execle.c b/libs/libc/unistd/lib_execle.c new file mode 100644 index 00000000000..5bbdea0e102 --- /dev/null +++ b/libs/libc/unistd/lib_execle.c @@ -0,0 +1,183 @@ +/**************************************************************************** + * libs/libc/unistd/lib_execle.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "libc.h" + +#ifdef CONFIG_LIBC_EXECFUNCS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This is an artificial limit to detect error conditions where an argv[] + * list is not properly terminated. + */ + +#define MAX_EXECL_ARGS 256 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: execle + * + * Description: + * The standard 'exec' family of functions will replace the current process + * image with a new process image. The new image will be constructed from a + * regular, executable file called the new process image file. There will + * be no return from a successful exec, because the calling process image + * is overlaid by the new process image. + * + * Simplified 'execl()' and 'execv()' functions are provided by NuttX for + * compatibility. NuttX is a tiny embedded RTOS that does not support + * processes and hence the concept of overlaying a tasks process image with + * a new process image does not make any sense. In NuttX, these functions + * are wrapper functions that: + * + * 1. Call the non-standard binfmt function 'exec', and then + * 2. exit(0). + * + * Note the inefficiency when 'exec[l|v]()' is called in the normal, two- + * step process: (1) first call vfork() to create a new thread, then (2) + * call 'exec[l|v]()' to replace the new thread with a program from the + * file system. Since the new thread will be terminated by the + * 'exec[l|v]()' call, it really served no purpose other than to support + * Unix compatility. + * + * The non-standard binfmt function 'exec()' needs to have (1) a symbol + * table that provides the list of symbols exported by the base code, and + * (2) the number of symbols in that table. This information is currently + * provided to 'exec()' from 'exec[l|v]()' via NuttX configuration setting: + * + * CONFIG_LIBC_EXECFUNCS : Enable exec[l|v] support + * CONFIG_EXECFUNCS_SYMTAB_ARRAY : Symbol table name used by exec[l|v] + * CONFIG_EXECFUNCS_NSYMBOLS_VAR : Variable holding number of symbols in + * the table + * + * As a result of the above, the current implementations of 'execl()' and + * 'execv()' suffer from some incompatibilities that may or may not be + * addressed in a future version of NuttX. Other than just being an + * inefficient use of MCU resource, the most serious of these is that + * the exec'ed task will not have the same task ID as the vfork'ed + * function. So the parent function cannot know the ID of the exec'ed + * task. + * + * Input Parameters: + * path - The path to the program to be executed. If CONFIG_LIBC_ENVPATH + * is defined in the configuration, then this may be a relative path + * from the current working directory. Otherwise, path must be the + * absolute path to the program. + * ... - A list of the string arguments to be recevied by the + * program. Zero indicates the end of the list. + * + * Returned Value: + * This function does not return on success. On failure, it will return + * -1 (ERROR) and will set the 'errno' value appropriately. + * + ****************************************************************************/ + +int execle(FAR const char *path, FAR const char *arg0, ...) +{ + FAR char *arg = (FAR char *)arg0; + FAR char **argv; + FAR char **envp; + size_t nargs; + va_list ap; + int argc; + int ret; + + /* Count the number of arguments */ + + va_start(ap, arg0); + nargs = 0; + + while (arg != NULL) + { + /* Yes.. increment the number of arguments. Here is a sanity + * check to prevent running away with an unterminated argv[] list. + * MAX_EXECL_ARGS should be sufficiently large that this never + * happens in normal usage. + */ + + if (++nargs > MAX_EXECL_ARGS) + { + set_errno(E2BIG); + va_end(ap); + return ERROR; + } + + arg = va_arg(ap, FAR char *); + } + + envp = va_arg(ap, FAR char **); + va_end(ap); + + /* Allocate a temporary argv[] array */ + + argv = (FAR char **)lib_malloc((nargs + 1) * sizeof(FAR char *)); + if (argv == NULL) + { + set_errno(ENOMEM); + return ERROR; + } + + argv[0] = (FAR char *)arg0; + + /* Collect the arguments into the argv[] array */ + + va_start(ap, arg0); + for (argc = 1; argc <= nargs; argc++) + { + argv[argc] = va_arg(ap, FAR char *); + } + + va_end(ap); + + /* Then let execv() do the real work */ + + ret = execv(path, argv, envp); + + /* Free the allocated argv[] list */ + + lib_free(argv); + return ret; +} + +#endif /* CONFIG_LIBC_EXECFUNCS */ diff --git a/libs/libc/unistd/lib_execv.c b/libs/libc/unistd/lib_execv.c new file mode 100644 index 00000000000..59f0bc34916 --- /dev/null +++ b/libs/libc/unistd/lib_execv.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * libs/libc/unistd/lib_execv.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#ifdef CONFIG_LIBC_EXECFUNCS + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: execv + * + * Description: + * The standard 'exec' family of functions will replace the current process + * image with a new process image. The new image will be constructed from a + * regular, executable file called the new process image file. There will + * be no return from a successful exec, because the calling process image + * is overlaid by the new process image. + * + * Simplified 'execl()' and 'execv()' functions are provided by NuttX for + * compatibility. NuttX is a tiny embedded RTOS that does not support + * processes and hence the concept of overlaying a tasks process image with + * a new process image does not make any sense. In NuttX, these functions + * are wrapper functions that: + * + * 1. Call the non-standard binfmt function 'exec', and then + * 2. exit(0). + * + * Note the inefficiency when 'exec[l|v]()' is called in the normal, two- + * step process: (1) first call vfork() to create a new thread, then (2) + * call 'exec[l|v]()' to replace the new thread with a program from the + * file system. Since the new thread will be terminated by the + * 'exec[l|v]()' call, it really served no purpose other than to support + * Unix compatility. + * + * The non-standard binfmt function 'exec()' needs to have (1) a symbol + * table that provides the list of symbols exported by the base code, and + * (2) the number of symbols in that table. This information is currently + * provided to 'exec()' from 'exec[l|v]()' via NuttX configuration + * settings: + * + * CONFIG_LIBC_EXECFUNCS : Enable exec[l|v] support + * CONFIG_EXECFUNCS_HAVE_SYMTAB : Defined if there is a pre-defined + * symbol table + * CONFIG_EXECFUNCS_SYMTAB_ARRAY : Symbol table name used by exec[l|v] + * CONFIG_EXECFUNCS_NSYMBOLS_VAR : Variable holding number of symbols + * in the table + * + * As a result of the above, the current implementations of 'execl()' and + * 'execv()' suffer from some incompatibilities that may or may not be + * addressed in a future version of NuttX. Other than just being an + * inefficient use of MCU resource, the most serious of these is that + * the exec'ed task will not have the same task ID as the vfork'ed + * function. So the parent function cannot know the ID of the exec'ed + * task. + * + * Input Parameters: + * path - The path to the program to be executed. If CONFIG_LIBC_ENVPATH + * is defined in the configuration, then this may be a relative path + * from the current working directory. Otherwise, path must be the + * absolute path to the program. + * argv - A pointer to an array of string arguments. The end of the + * array is indicated with a NULL entry. + * + * Returned Value: + * This function does not return on success. On failure, it will return + * -1 (ERROR) and will set the 'errno' value appropriately. + * + ****************************************************************************/ + +int execv(FAR const char *path, FAR char * const argv[]) +{ + return execve(path, argv, NULL); +} + +#endif /* CONFIG_LIBC_EXECFUNCS */ diff --git a/sched/task/Make.defs b/sched/task/Make.defs index 85b893e642d..406ea35a0b5 100644 --- a/sched/task/Make.defs +++ b/sched/task/Make.defs @@ -45,7 +45,7 @@ endif ifneq ($(CONFIG_BINFMT_DISABLE),y) ifeq ($(CONFIG_LIBC_EXECFUNCS),y) -CSRCS += task_execv.c task_posixspawn.c +CSRCS += task_execve.c task_posixspawn.c endif endif diff --git a/sched/task/task_execv.c b/sched/task/task_execve.c similarity index 93% rename from sched/task/task_execv.c rename to sched/task/task_execve.c index b4472e82891..555271effef 100644 --- a/sched/task/task_execv.c +++ b/sched/task/task_execve.c @@ -1,5 +1,5 @@ /**************************************************************************** - * sched/task/task_execv.c + * sched/task/task_execve.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -100,6 +100,9 @@ * absolute path to the program. * argv - A pointer to an array of string arguments. The end of the * array is indicated with a NULL entry. + * envp - An array of character pointers to null-terminated strings that + * provide the environment for the new process image. The environment + * array is terminated by a null pointer. * * Returned Value: * This function does not return on success. On failure, it will return @@ -107,7 +110,8 @@ * ****************************************************************************/ -int execv(FAR const char *path, FAR char * const argv[]) +int execve(FAR const char *path, FAR char * const argv[], + FAR char *const envp[]) { FAR const struct symtab_s *symtab; int nsymbols; @@ -119,7 +123,7 @@ int execv(FAR const char *path, FAR char * const argv[]) /* Start the task */ - ret = exec(path, (FAR char * const *)argv, symtab, nsymbols); + ret = exec(path, argv, envp, symtab, nsymbols); if (ret < 0) { serr("ERROR: exec failed: %d\n", get_errno()); diff --git a/syscall/syscall.csv b/syscall/syscall.csv index 0705de96a23..2b0b8148b08 100644 --- a/syscall/syscall.csv +++ b/syscall/syscall.csv @@ -24,7 +24,7 @@ "dup2","unistd.h","","int","int","int" "eventfd","sys/eventfd.h","defined(CONFIG_EVENT_FD)","int","unsigned int","int" "exec","nuttx/binfmt/binfmt.h","!defined(CONFIG_BINFMT_DISABLE) && !defined(CONFIG_BUILD_KERNEL)","int","FAR const char *","FAR char * const *","FAR const struct symtab_s *","int" -"execv","unistd.h","!defined(CONFIG_BINFMT_DISABLE) && defined(CONFIG_LIBC_EXECFUNCS)","int","FAR const char *","FAR char * const []|FAR char * const *" +"execve","unistd.h","!defined(CONFIG_BINFMT_DISABLE) && defined(CONFIG_LIBC_EXECFUNCS)","int","FAR const char *","FAR char * const []|FAR char * const *","FAR char * const []|FAR char * const *" "exit","stdlib.h","","noreturn","int" "fchmod","sys/stat.h","","int","int","mode_t" "fchown","unistd.h","","int","int","uid_t","gid_t"