system/nxinit: Add NuttX Init ("NxInit") reopened to be integrated on NuttX v13#3504
system/nxinit: Add NuttX Init ("NxInit") reopened to be integrated on NuttX v13#3504acassis wants to merge 7 commits into
Conversation
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>
linguini1
left a comment
There was a problem hiding this comment.
I don't see anything really wrong with this, just some small comments.
Very excited for this in NuttX!
| if SYSTEM_NXINIT | ||
|
|
||
| # | ||
| # Basic |
There was a problem hiding this comment.
These should maybe be comment "Basic", etc. to show up in the menu.
| ``` | ||
|
|
||
| config SYSTEM_NXINIT_ACTION_WARN_SLOW | ||
| int "Warn if command takes too long" |
There was a problem hiding this comment.
int "Slow command warning timeout" (slightly clearer to user)
| struct service_class_s | ||
| { | ||
| struct list_node node; /* Service class list node */ | ||
| FAR char name[0]; |
There was a problem hiding this comment.
What is 0 size array? Just a char *?
|
|
||
| static const struct cmd_map_s g_option[] = | ||
| { | ||
| {"class", 2, 99, option_class}, |
There was a problem hiding this comment.
Should max arguments not be limited by SYSTEM_NXINIT_ACTION_CMD_ARGS_MAX? Or is that for something else? 99 seems high
| #define TIMESPEC2MS(t) (((t).tv_sec * 1000) + (t).tv_nsec / 1000000) | ||
|
|
||
| #ifdef CONFIG_SYSTEM_NXINIT_DEBUG | ||
| #define init_debug(...) syslog(LOG_DEBUG, ##__VA_ARGS__) |
There was a problem hiding this comment.
For all logs, can we add | LOG_USER to make it clear this is a user-space log?
| #define init_err(...) | ||
| #endif | ||
|
|
||
| #define init_log(p, ...) \ |
There was a problem hiding this comment.
Why do we need this macro? We can just choose the appropriate other macro (init_debug, init_err, etc) for the log level.
| #endif | ||
|
|
||
| #ifdef CONFIG_SYSTEM_NXINIT_ERR | ||
| #define init_err(f, ...) syslog(LOG_ERR, "Error " f, ##__VA_ARGS__) |
There was a problem hiding this comment.
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).
|
|
||
| init_action_add_event(&am, "boot"); | ||
|
|
||
| boardctl(BOARDIOC_INIT, 0); |
| } | ||
| } | ||
|
|
||
| r = init_parse_config_file(parser, "/etc/init.d/init.rc"); |
There was a problem hiding this comment.
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?
|
Also would be good to test on a couple devices! |
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:
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