You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2026/10/15 11:47:35 UTC

(nuttx-apps) 02/02: system/nxinit: add stdout and stderr service options

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git

commit 22f51f210f5d4d41df064ae38acd4b8fe8dbb1e9
Author: wangjianyu3 <wa...@xiaomi.com>
AuthorDate: Wed Oct 14 14:32:13 2026 +0800

    system/nxinit: add stdout and stderr service options
    
    Redirect a service's stdout/stderr to a file (nsh "cmd > file" style)
    behind SYSTEM_NXINIT_SERVICE_REDIR (default n, not recommended).  Share
    the console file actions and string assignment as common helpers.
    
    Assisted-by: OpenCode:mimo-2.6-pro
    Signed-off-by: wangjianyu3 <wa...@xiaomi.com>
---
 system/nxinit/Kconfig   |  11 +++
 system/nxinit/service.c | 237 +++++++++++++++++++++++++++++++++++-------------
 system/nxinit/service.h |   5 +
 3 files changed, 191 insertions(+), 62 deletions(-)

diff --git a/system/nxinit/Kconfig b/system/nxinit/Kconfig
index 4b0d242bf..48a78263b 100644
--- a/system/nxinit/Kconfig
+++ b/system/nxinit/Kconfig
@@ -127,6 +127,17 @@ config SYSTEM_NXINIT_CONSOLE_DEV
 
 comment "NXInit Testing"
 
+config SYSTEM_NXINIT_SERVICE_REDIR
+	bool "Service stdout/stderr redirection options (not recommended)"
+	default n
+	---help---
+		Enable the "stdout <path>" and "stderr <path>" service options,
+		which redirect a service's stdout/stderr to a file like nsh
+		"cmd > file".  They are not Android init service options and
+		are not recommended: a service should log through its log
+		interface instead.  Only meant to keep rcS redirections when
+		moving a board from nsh to init.
+
 config SYSTEM_NXINIT_TEST
 	bool "NxInit unit tests"
 	default n
diff --git a/system/nxinit/service.c b/system/nxinit/service.c
index bfdd99367..c4df0bbe2 100644
--- a/system/nxinit/service.c
+++ b/system/nxinit/service.c
@@ -106,6 +106,12 @@ static int option_oneshot(FAR struct service_manager_s *sm,
                           int argc, FAR char **argv);
 static int option_console(FAR struct service_manager_s *sm,
                           int argc, FAR char **argv);
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+static int option_stdout(FAR struct service_manager_s *sm,
+                         int argc, FAR char **argv);
+static int option_stderr(FAR struct service_manager_s *sm,
+                         int argc, FAR char **argv);
+#endif
 #ifdef CONFIG_BOARDCTL_RESET
 static int option_reboot_on_failure(FAR struct service_manager_s *sm,
                                     int argc, FAR char **argv);
@@ -124,6 +130,10 @@ static const struct cmd_map_s g_option[] =
   {"override", 1, 1, option_override},
   {"oneshot", 1, 1, option_oneshot},
   {"console", 1, 2, option_console},
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+  {"stdout", 2, 2, option_stdout},
+  {"stderr", 2, 2, option_stderr},
+#endif
 #ifdef CONFIG_BOARDCTL_RESET
   {"reboot_on_failure", 2, 2, option_reboot_on_failure},
 #endif
@@ -208,6 +218,10 @@ static void remove_service(FAR struct service_s *service)
     }
 
   free(service->console);
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+  free(service->stdout_path);
+  free(service->stderr_path);
+#endif
   list_delete(&service->node);
   free(service);
 }
@@ -294,6 +308,32 @@ static int option_oneshot(FAR struct service_manager_s *sm,
   return 0;
 }
 
+/****************************************************************************
+ * Name: service_strdup
+ *
+ * Description:
+ *   Duplicate 'src' into *dst, replacing any previous value.  Option
+ *   strings point into the parser line buffer, which is reused for the
+ *   next line, so they must be duplicated here.  On allocation failure
+ *   the previous value is kept.
+ *
+ ****************************************************************************/
+
+static int service_strdup(FAR char **dst, FAR const char *src)
+{
+  FAR char *dup = strdup(src);
+
+  if (dup == NULL)
+    {
+      init_err("Alloc '%s'", src);
+      return -ENOMEM;
+    }
+
+  free(*dst);
+  *dst = dup;
+  return 0;
+}
+
 /****************************************************************************
  * Name: option_console
  *
@@ -315,22 +355,32 @@ static int option_console(FAR struct service_manager_s *sm,
 
   if (argc > 1)
     {
-      /* 'argv' points into the parser line buffer, which is reused for the
-       * next line, so the device name must be duplicated here.
-       */
-
-      free(s->console);
-      s->console = strdup(argv[1]);
-      if (s->console == NULL)
-        {
-          init_err("Alloc console device");
-          return -ENOMEM;
-        }
+      return service_strdup(&s->console, argv[1]);
     }
 
   return 0;
 }
 
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+static int option_stdout(FAR struct service_manager_s *sm,
+                         int argc, FAR char **argv)
+{
+  FAR struct service_s *s = list_last_entry(&sm->services, struct service_s,
+                                            node);
+
+  return service_strdup(&s->stdout_path, argv[1]);
+}
+
+static int option_stderr(FAR struct service_manager_s *sm,
+                         int argc, FAR char **argv)
+{
+  FAR struct service_s *s = list_last_entry(&sm->services, struct service_s,
+                                            node);
+
+  return service_strdup(&s->stderr_path, argv[1]);
+}
+#endif
+
 #ifdef CONFIG_BOARDCTL_RESET
 static int option_reboot_on_failure(FAR struct service_manager_s *sm,
                                     int argc, FAR char **argv)
@@ -343,6 +393,102 @@ static int option_reboot_on_failure(FAR struct service_manager_s *sm,
 }
 #endif
 
+/****************************************************************************
+ * Name: service_file_actions
+ *
+ * Description:
+ *   Build the spawn file actions which redirect the stdio of a service:
+ *   its console device when flagged SVC_CONSOLE and, with
+ *   CONFIG_SYSTEM_NXINIT_SERVICE_REDIR, its stdout/stderr files.  The
+ *   actions are performed in the context of the new task, so the stdio of
+ *   NxInit itself is left untouched.  On error the actions are destroyed
+ *   and a negative errno is returned.
+ *
+ ****************************************************************************/
+
+static int service_file_actions(FAR posix_spawn_file_actions_t *actions,
+                               FAR struct service_s *service)
+{
+  int ret;
+
+  ret = posix_spawn_file_actions_init(actions);
+  if (ret != 0)
+    {
+      init_err("posix_spawn_file_actions_init %d", ret);
+      return -ret;
+    }
+
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+  /* Without the redirection options the caller has already checked
+   * SVC_CONSOLE, so only check it again when redirection can add
+   * file actions of its own.
+   */
+
+  if (check_flags(service, SVC_CONSOLE))
+#endif
+    {
+      FAR const char *dev = service->console ?
+                            service->console :
+                            CONFIG_SYSTEM_NXINIT_CONSOLE_DEV;
+
+      ret = posix_spawn_file_actions_addopen(actions, STDIN_FILENO, dev,
+                                            O_RDWR, 0);
+      if (ret == 0)
+        {
+          ret = posix_spawn_file_actions_adddup2(actions, STDIN_FILENO,
+                                                 STDOUT_FILENO);
+        }
+
+      if (ret == 0)
+        {
+          ret = posix_spawn_file_actions_adddup2(actions, STDIN_FILENO,
+                                                 STDERR_FILENO);
+        }
+
+      if (ret != 0)
+        {
+          init_err("Add console '%s' file action %d", dev, ret);
+          goto errout;
+        }
+    }
+
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+  if (service->stdout_path != NULL)
+    {
+      ret = posix_spawn_file_actions_addopen(actions, STDOUT_FILENO,
+                                            service->stdout_path,
+                                            O_WRONLY | O_CREAT | O_TRUNC,
+                                            0666);
+      if (ret != 0)
+        {
+          init_err("Add stdout '%s' file action %d", service->stdout_path,
+                   ret);
+          goto errout;
+        }
+    }
+
+  if (service->stderr_path != NULL)
+    {
+      ret = posix_spawn_file_actions_addopen(actions, STDERR_FILENO,
+                                            service->stderr_path,
+                                            O_WRONLY | O_CREAT | O_TRUNC,
+                                            0666);
+      if (ret != 0)
+        {
+          init_err("Add stderr '%s' file action %d", service->stderr_path,
+                   ret);
+          goto errout;
+        }
+    }
+#endif
+
+  return 0;
+
+errout:
+  posix_spawn_file_actions_destroy(actions);
+  return -ret;
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -488,55 +634,6 @@ void init_service_reap(FAR struct service_s *service, int status)
     }
 }
 
-/****************************************************************************
- * Name: console_file_actions
- *
- * Description:
- *   Build the spawn file actions which redirect the stdio of a service
- *   flagged SVC_CONSOLE to its console device.  The actions are performed
- *   in the context of the new task, so the stdio of NxInit itself is left
- *   untouched.
- *
- ****************************************************************************/
-
-static int console_file_actions(FAR posix_spawn_file_actions_t *actions,
-                               FAR struct service_s *service)
-{
-  FAR const char *dev = service->console ?
-                        service->console : CONFIG_SYSTEM_NXINIT_CONSOLE_DEV;
-  int ret;
-
-  ret = posix_spawn_file_actions_init(actions);
-  if (ret != 0)
-    {
-      init_err("posix_spawn_file_actions_init %d", ret);
-      return -ret;
-    }
-
-  ret = posix_spawn_file_actions_addopen(actions, STDIN_FILENO, dev,
-                                        O_RDWR, 0);
-  if (ret == 0)
-    {
-      ret = posix_spawn_file_actions_adddup2(actions, STDIN_FILENO,
-                                             STDOUT_FILENO);
-    }
-
-  if (ret == 0)
-    {
-      ret = posix_spawn_file_actions_adddup2(actions, STDIN_FILENO,
-                                             STDERR_FILENO);
-    }
-
-  if (ret != 0)
-    {
-      init_err("Add console '%s' file action %d", dev, ret);
-      posix_spawn_file_actions_destroy(actions);
-      return -ret;
-    }
-
-  return 0;
-}
-
 int init_service_start(FAR struct service_s *service)
 {
   FAR posix_spawn_file_actions_t *pactions = NULL;
@@ -581,9 +678,13 @@ int init_service_start(FAR struct service_s *service)
 
   clock_gettime(CLOCK_MONOTONIC, &service->time_started);
 
-  if (check_flags(service, SVC_CONSOLE))
+  if (check_flags(service, SVC_CONSOLE)
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+      || service->stdout_path != NULL || service->stderr_path != NULL
+#endif
+     )
     {
-      ret = console_file_actions(&actions, service);
+      ret = service_file_actions(&actions, service);
       if (ret < 0)
         {
           posix_spawnattr_destroy(&attr);
@@ -836,6 +937,18 @@ void init_dump_service(FAR struct service_s *s)
     }
 
   init_debug("  restart_period: %d", s->restart_period);
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+  if (s->stdout_path != NULL)
+    {
+      init_debug("  stdout: '%s'", s->stdout_path);
+    }
+
+  if (s->stderr_path != NULL)
+    {
+      init_debug("  stderr: '%s'", s->stderr_path);
+    }
+
+#endif
 #ifdef CONFIG_BOARDCTL_RESET
   init_debug("  reboot_on_failure: %d", s->reset_reason);
 #endif
diff --git a/system/nxinit/service.h b/system/nxinit/service.h
index be11c4299..1b93ab98f 100644
--- a/system/nxinit/service.h
+++ b/system/nxinit/service.h
@@ -116,6 +116,11 @@ struct service_s
 
   FAR char *console;
 
+#ifdef CONFIG_SYSTEM_NXINIT_SERVICE_REDIR
+  FAR char *stdout_path;
+  FAR char *stderr_path;
+#endif
+
   /* The "target" of service option "reboot_on_failure" */
 
 #ifdef CONFIG_BOARDCTL_RESET