Skip to content

system/nxinit: Add NuttX Init ("NxInit") reopened to be integrated on NuttX v13#3504

Open
acassis wants to merge 7 commits into
apache:masterfrom
acassis:nxinit
Open

system/nxinit: Add NuttX Init ("NxInit") reopened to be integrated on NuttX v13#3504
acassis wants to merge 7 commits into
apache:masterfrom
acassis:nxinit

Conversation

@acassis
Copy link
Copy Markdown
Contributor

@acassis acassis commented May 26, 2026

Summary

Add NuttX Init ("NxInit")

This component (abbreviated as "NxInit") is specifically developed for NuttX and intended for system initialization. While we have adopted the Android Init Language among various syntax options, this is a brand-new implementation—it is not a port or variant of Android Init.

Refer to the implementation of Android Init Language, consists of five broad classes of statements: Actions, Commands, Services, Options, and Imports.

Actions support two types of triggers: event and action. Action triggers also support runtime triggering. Services support lifecycle management, including automatic restart (at specified intervals), and starting/stopping individually or by class. Import supports files or directories, and we may add a static method in the future. The following are some differences:

The Android Init Language treats lines starting with # as comments, while we use a preprocessor to handle comments.
For action commands, we can omit "exec" and directly execute built-in apps or nsh builtins.
Regarding the property service, users can either adapt it by self or directly use the preset NVS-based properties.
Only part of standard action commands and service options are implemented currlently.

To enable system/init:

-CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INIT_ENTRYPOINT="init_main"
+CONFIG_SYSTEM_NXINIT=y

Impact

NuttX will be able to start applications and services without depending NSH and without using the app as NuttX entrypoint directly.

Testing

   [    0.150000] [ 3] [ 0] init_main: == Dump Services ==
   ...
   [    0.160000] [ 3] [ 0] init_main: Service 0x40486aa8 name 'telnet' path 'telnetd'
   [    0.160000] [ 3] [ 0] init_main:   pid: 0
   [    0.160000] [ 3] [ 0] init_main:   arguments:
   [    0.160000] [ 3] [ 0] init_main:       [0] 'service'
   [    0.160000] [ 3] [ 0] init_main:       [1] 'telnet'
   [    0.160000] [ 3] [ 0] init_main:       [2] 'telnetd'
   [    0.160000] [ 3] [ 0] init_main:   classes:
   [    0.160000] [ 3] [ 0] init_main:     'test'
   [    0.170000] [ 3] [ 0] init_main:   restart_period: 3000
   [    0.170000] [ 3] [ 0] init_main:   reboot_on_failure: -1
   [    0.170000] [ 3] [ 0] init_main:   flags:
 > [    0.170000] [ 3] [ 0] init_main:     'oneshot'
   ...
   [    0.370000] [ 3] [ 0] init_main: starting service 'telnet' ...
   [    0.380000] [ 3] [ 0] init_main: service 'telnet' flag 0x2 add 0x4
   [    0.380000] [ 3] [ 0] init_main:   +flag 'running'
   [    0.380000] [ 3] [ 0] init_main: service 'telnet' flag 0x6 add 0x8
   [    0.380000] [ 3] [ 0] init_main:   -flag 'restarting'
   [    0.380000] [ 3] [ 0] init_main: service 'telnet' flag 0x6 add 0x1
   [    0.380000] [ 3] [ 0] init_main:   -flag 'disabled'
   [    0.380000] [ 3] [ 0] init_main: started service 'telnet' pid 9
   ...
   nsh> kill -9 9
   nsh> [    7.350000] [ 3] [ 0] init_main: service 'telnet' flag 0x6 add 0x4
   [    7.350000] [ 3] [ 0] init_main:   -flag 'running'
   [    7.350000] [ 3] [ 0] init_main: service 'telnet' flag 0x2 add 0x80000001
   [    7.350000] [ 3] [ 0] init_main:   +flag 'disabled'
   [    7.350000] [ 3] [ 0] init_main:   +flag 'remove'
   [    7.350000] [ 3] [ 0] init_main: service 'telnet' pid 9 exited status 1
 > [    7.360000] [ 3] [ 0] init_main: removing service 'telnet' ...
``


This component (abbreviated as "NxInit") is specifically developed
for NuttX and intended for system initialization. While we have
adopted the Android Init Language among various syntax options,
this is a brand-new implementation—it is not a port or variant of
Android Init.

Refer to the implementation of Android Init Language, consists of five broad
classes of statements: Actions, Commands, Services, Options, and Imports.

Actions support two types of triggers: event and action. Action triggers also
support runtime triggering. Services support lifecycle management, including
automatic restart (at specified intervals), and starting/stopping
individually or by class. Import supports files or directories, and we may
add a static method in the future. The following are some differences:
  1. The Android Init Language treats lines starting with `#` as comments,
     while we use a preprocessor to handle comments.
  2. For action commands, we can omit "exec" and directly execute
     built-in apps or nsh builtins.
  3. Regarding the property service, users can either adapt it by self or
     directly use the preset NVS-based properties.
  4. Only part of standard action commands and service options are
     implemented currlently.

To enable system/nxinit:
  ```diff
  -CONFIG_INIT_ENTRYPOINT="nsh_main"
  +CONFIG_INIT_ENTRYPOINT="init_main"
  +CONFIG_SYSTEM_NXINIT=y
  ```

For format and additional details, refer to:
  https://android.googlesource.com/platform/system/core/+/
  master/init/README.md

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Usage:
  class_start <classname>
  class_stop <classname>

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Enable CONFIG_SYSTEM_SYSTEM to support nsh builtins, which depends on nsh.

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
If the command of an action takes too long (greater than
CONFIG_SYSTEM_NXINIT_ACTION_WARN_SLOW milliseconds, 50 ms by default),
a warning log will be output for analysis and debugging.

For example:

  a. sleep 1
       [    0.340000] [ 3] [ 0] init_main: executing NSH command 'sleep 1'
       [    1.360000] [ 3] [ 0] init_main: NSH command 'sleep 1' exited 0
     > [    1.360000] [ 3] [ 0] init_main: command 'sleep' took 1020 ms

  b. hello (add sleep(1) to examples/hello)

       [    1.390000] [ 3] [ 0] init_main: executed command 'hello' pid 14
       [    1.390000] [ 3] [ 0] init_main: waiting 'hello' pid 14
       Hello, World!!
     > [    2.400000] [ 3] [ 0] init_main: command 'hello' pid 14 took 1010 ms
       [    2.400000] [ 3] [ 0] init_main: command 'hello' pid 14 exited status 0

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Add support for the oneshot option to the service.

Test
  - RC
      service telnet telnetd
          class test
    >     oneshot
          restart_period 3000
  - Runtime
      [    0.150000] [ 3] [ 0] init_main: == Dump Services ==
      ...
      [    0.160000] [ 3] [ 0] init_main: Service 0x40486aa8 name 'telnet' path 'telnetd'
      [    0.160000] [ 3] [ 0] init_main:   pid: 0
      [    0.160000] [ 3] [ 0] init_main:   arguments:
      [    0.160000] [ 3] [ 0] init_main:       [0] 'service'
      [    0.160000] [ 3] [ 0] init_main:       [1] 'telnet'
      [    0.160000] [ 3] [ 0] init_main:       [2] 'telnetd'
      [    0.160000] [ 3] [ 0] init_main:   classes:
      [    0.160000] [ 3] [ 0] init_main:     'test'
      [    0.170000] [ 3] [ 0] init_main:   restart_period: 3000
      [    0.170000] [ 3] [ 0] init_main:   reboot_on_failure: -1
      [    0.170000] [ 3] [ 0] init_main:   flags:
    > [    0.170000] [ 3] [ 0] init_main:     'oneshot'
      ...
      [    0.370000] [ 3] [ 0] init_main: starting service 'telnet' ...
      [    0.380000] [ 3] [ 0] init_main: service 'telnet' flag 0x2 add 0x4
      [    0.380000] [ 3] [ 0] init_main:   +flag 'running'
      [    0.380000] [ 3] [ 0] init_main: service 'telnet' flag 0x6 add 0x8
      [    0.380000] [ 3] [ 0] init_main:   -flag 'restarting'
      [    0.380000] [ 3] [ 0] init_main: service 'telnet' flag 0x6 add 0x1
      [    0.380000] [ 3] [ 0] init_main:   -flag 'disabled'
      [    0.380000] [ 3] [ 0] init_main: started service 'telnet' pid 9
      ...
      nsh> kill -9 9
      nsh> [    7.350000] [ 3] [ 0] init_main: service 'telnet' flag 0x6 add 0x4
      [    7.350000] [ 3] [ 0] init_main:   -flag 'running'
      [    7.350000] [ 3] [ 0] init_main: service 'telnet' flag 0x2 add 0x80000001
      [    7.350000] [ 3] [ 0] init_main:   +flag 'disabled'
      [    7.350000] [ 3] [ 0] init_main:   +flag 'remove'
      [    7.350000] [ 3] [ 0] init_main: service 'telnet' pid 9 exited status 1
    > [    7.360000] [ 3] [ 0] init_main: removing service 'telnet' ...

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Format: exec_start <service>

Start the specified service and pause the processing of any additional
initialization commands until the service completes its execution. This
command operates similarly to the `exec` command; the key difference is
that it utilizes an existing service definition rather than requiring
the `exec` argument vector.

This feature is particularly intended for use with the `reboot_on_failure`
built-in command to perform all types of essential checks during system boot.

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Add support for the reboot_on_failure option to the service.

When the execution of a command within a certain action fails (returning
a non-zero status code), NxInit will continue to execute subsequent commands or
actions and will not proactively terminate the startup process. To implement
the functionality of "terminating the startup process after a command
execution fails", there are two methods:
a. Execute conditional statements (if/then/else/fi) via exec command,
   but this depends on sh:
   ```
   on init
       exec -- set +e; \
               mount -t fatfs /dev/data /data ; \
               if [ $? -ne 0 ] ; \
               then \
                 echo "failed" ; \
                 reboot ; \
               else \
                 echo "succeed" ; \
               fi;
   ```
b. Via service's oneshot + reboot_on_failure:
   /* Although the example uses sh, it does not depend on it and can be
    * replaced with any other Builtin Apps.
    */
   ```
   on init
       exec_start mkdir_tmp
       ls /tmp

   service mkdir_tmp sh -c "mkdir /tmp"
       reboot_on_failure 0
       oneshot
   ```

Test
  - RC
      service console sh
          class core
          override
    >     reboot_on_failure 0
          restart_period 10000
  - Runtime
      [    0.150000] [ 3] [ 0] init_main: == Dump Services ==
      ...
      [    0.170000] [ 3] [ 0] init_main: Service 0x40486ea0 name 'console' path 'sh'
      [    0.170000] [ 3] [ 0] init_main:   pid: 0
      [    0.170000] [ 3] [ 0] init_main:   arguments:
      [    0.170000] [ 3] [ 0] init_main:       [0] 'service'
      [    0.170000] [ 3] [ 0] init_main:       [1] 'console'
      [    0.170000] [ 3] [ 0] init_main:       [2] 'sh'
      [    0.170000] [ 3] [ 0] init_main:   classes:
      [    0.170000] [ 3] [ 0] init_main:     'core'
      [    0.170000] [ 3] [ 0] init_main:   restart_period: 10000
    > [    0.170000] [ 3] [ 0] init_main:   reboot_on_failure: 0
      [    0.170000] [ 3] [ 0] init_main:   flags:
      [    0.170000] [ 3] [ 0] init_main:     'override'
      ...
      [    0.380000] [ 3] [ 0] init_main: started service 'console' pid 4
      ...
      nsh> kill -9 4
      [    8.060000] [ 3] [ 0] init_main: service 'console' flag 0x20000004 add 0x4
      [    8.060000] [ 3] [ 0] init_main:   -flag 'running'
      [    8.060000] [ 3] [ 0] init_main: service 'console' flag 0x20000000 add 0x8
      [    8.060000] [ 3] [ 0] init_main:   +flag 'restarting'
    > [    8.060000] [ 3] [ 0] init_main: Error reboot on failure of service 'console' reason 0

Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
@acassis
Copy link
Copy Markdown
Contributor Author

acassis commented May 26, 2026

@simbit18 @lupyuen any idea what is this label error:

 label
Unhandled error: ReferenceError: keywords is not defined

Copy link
Copy Markdown
Contributor

@linguini1 linguini1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything really wrong with this, just some small comments.

Very excited for this in NuttX!

Comment thread system/nxinit/Kconfig
if SYSTEM_NXINIT

#
# Basic
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should maybe be comment "Basic", etc. to show up in the menu.

Comment thread system/nxinit/Kconfig
```

config SYSTEM_NXINIT_ACTION_WARN_SLOW
int "Warn if command takes too long"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int "Slow command warning timeout" (slightly clearer to user)

Comment thread system/nxinit/service.h
struct service_class_s
{
struct list_node node; /* Service class list node */
FAR char name[0];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is 0 size array? Just a char *?

Comment thread system/nxinit/service.c

static const struct cmd_map_s g_option[] =
{
{"class", 2, 99, option_class},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should max arguments not be limited by SYSTEM_NXINIT_ACTION_CMD_ARGS_MAX? Or is that for something else? 99 seems high

Comment thread system/nxinit/init.h
#define TIMESPEC2MS(t) (((t).tv_sec * 1000) + (t).tv_nsec / 1000000)

#ifdef CONFIG_SYSTEM_NXINIT_DEBUG
#define init_debug(...) syslog(LOG_DEBUG, ##__VA_ARGS__)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For all logs, can we add | LOG_USER to make it clear this is a user-space log?

Comment thread system/nxinit/init.h
#define init_err(...)
#endif

#define init_log(p, ...) \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this macro? We can just choose the appropriate other macro (init_debug, init_err, etc) for the log level.

Comment thread system/nxinit/init.h
#endif

#ifdef CONFIG_SYSTEM_NXINIT_ERR
#define init_err(f, ...) syslog(LOG_ERR, "Error " f, ##__VA_ARGS__)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe splitting hairs, but I don't think we need to append "Error". Syslog can already do this "[ERROR]" prefix if users want. This will save some amount of space on strings (maybe negligible but still).

Comment thread system/nxinit/init.c

init_action_add_event(&am, "boot");

boardctl(BOARDIOC_INIT, 0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deprecated.

Comment thread system/nxinit/init.c
}
}

r = init_parse_config_file(parser, "/etc/init.d/init.rc");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think /etc/init.d/init.rc should be a configurable location.

Any way to bake init scripts into systems that don't want to add a whole file system? Maybe we can later add a build tool that compiles the script into a parse-able string instead?

@linguini1
Copy link
Copy Markdown
Contributor

Also would be good to test on a couple devices!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants