nuttx-apps/system/nxinit/init.c
wangjianyu3 84e87a5158 system/nxinit: Add reboot_on_failure for service
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>
2026-05-27 17:20:07 -03:00

254 lines
6.2 KiB
C

/****************************************************************************
* apps/system/nxinit/init.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <nuttx/config.h>
#include <errno.h>
#include <poll.h>
#include <sys/param.h>
#include <sys/boardctl.h>
#include <sys/wait.h>
#include <netutils/netinit.h>
#include "action.h"
#include "builtin.h"
#include "init.h"
#include "import.h"
#include "service.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MS2TIMESPEC(ts, ms) \
((ms) == INT_MAX ? NULL \
: ((ts)->tv_sec = (ms) / 1000, \
(ts)->tv_nsec = ((ms) % 1000) * 1000000, (ts)))
/****************************************************************************
* Private Types
****************************************************************************/
struct init_event_s
{
FAR struct pollfd *pfd;
FAR void *priv;
FAR struct service_manager_s *sm;
FAR struct action_manager_s *am;
CODE int (*init) (FAR struct init_event_s *);
CODE void (*handle) (FAR struct init_event_s *);
CODE void (*deinit) (FAR struct init_event_s *);
};
/****************************************************************************
* Private Functions
****************************************************************************/
static void reap_process(FAR struct service_manager_s *sm,
FAR struct action_manager_s *am)
{
FAR const char *name = "unknown";
FAR const char *status;
FAR struct service_s *service;
int wstatus;
int ret;
int pid;
for (; ; )
{
pid = waitpid(-1,
#ifdef CONFIG_SYSTEM_NXINIT_DEBUG
&wstatus,
#else
NULL,
#endif
WNOHANG);
if (pid <= 0)
{
break;
}
else if (WIFEXITED(wstatus))
{
status = "status";
ret = WEXITSTATUS(wstatus);
}
else if (WIFSIGNALED(wtatus))
{
status = "signal";
ret = WTERMSIG(wstatus);
}
else
{
continue;
}
if (pid == am->pid_running)
{
name = am->running->argv[0];
init_action_reap_command(am);
}
service = init_service_find_by_pid(sm, pid);
if (service != NULL)
{
name = service->argv[1];
init_service_reap(service, ret);
}
init_log(service ? LOG_WARNING : LOG_DEBUG,
"%s '%s' pid %d exited %s %d",
service ? "Service" : "Command", name, pid, status, ret);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
struct service_manager_s sm =
{
.services = LIST_INITIAL_VALUE(sm.services),
};
struct action_manager_s am =
{
.actions = LIST_INITIAL_VALUE(am.actions),
.ready_actions = LIST_INITIAL_VALUE(am.ready_actions),
.events = { 0 },
.current = NULL,
.running = NULL,
.pid_running = -1,
.sm = &sm,
};
const struct parser_s parser[] =
{
{"import", init_import_parse, NULL, NULL},
{"on", init_action_parse, NULL, &am},
{"service", init_service_parse, init_service_check, &sm},
{NULL},
};
struct init_event_s ev[] =
{
};
struct pollfd pfds[nitems(ev)];
size_t i;
int r;
#ifdef CONFIG_USBDEV_TRACE
usbtrace_enable(TRACE_BITSET);
#endif
for (i = 0; i < nitems(ev); i++)
{
ev[i].sm = &sm;
ev[i].am = &am;
ev[i].pfd = &pfds[i];
r = ev[i].init(&ev[i]);
if (r < 0)
{
init_err("Init event %zu", i);
goto out;
}
}
r = init_parse_config_file(parser, "/etc/init.d/init.rc");
if (r < 0)
{
goto out;
}
init_dump_actions(&am.actions);
init_dump_services(&sm.services);
init_action_add_event(&am, "boot");
boardctl(BOARDIOC_INIT, 0);
init_action_add_event(&am, "init");
#ifdef CONFIG_NETUTILS_NETINIT
netinit_bringup();
init_action_add_event(&am, "netinit");
#endif
#ifdef CONFIG_BOARDCTL_FINALINIT
boardctl(BOARDIOC_FINALINIT, 0);
init_action_add_event(&am, "finalinit");
#endif
for (; ; )
{
int t1 = init_service_refresh(&sm);
int t2 = init_action_run_command(&am);
struct timespec timeout;
int t = MIN(t1, t2);
if (t1 < 0)
{
r = t1;
break;
}
if (t2 < 0)
{
r = t2;
break;
}
r = ppoll(pfds, nitems(pfds), MS2TIMESPEC(&timeout, t), NULL);
if (r < 0 && errno != EINTR)
{
init_err("Wait event");
break;
}
for (i = 0; i < nitems(ev); i++)
{
if (ev[i].pfd->revents & ev[i].pfd->events)
{
ev[i].handle(&ev[i]);
}
}
reap_process(&sm, &am);
}
out:
while (i--)
{
if (ev[i].deinit)
{
ev[i].deinit(&ev[i]);
}
}
return r;
}