1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2024-11-11 Shell moved lwp_startup() from lwp.c;
9 * added lwp_teardown()
10 */
11
12 #define DBG_TAG "lwp"
13 #define DBG_LVL DBG_INFO
14 #include <rtdbg.h>
15
16 #include "lwp_internal.h"
17
18 #include <rthw.h>
19 #include <rtthread.h>
20
21 #include <dfs_file.h>
22 #include <dfs_mnt.h>
23 #include <dfs_fs.h>
24
25 /**
26 * lwp_runtime:
27 * Runtime environment provide by init process including boot scripts,
28 * poweroff, shutdown, reboot, service management etc. In the kernel, lwp will
29 * provide the underlying software bootstrap and cleanup for the init proc.
30 *
31 */
32
lwp_startup_debug_request(void)33 rt_weak int lwp_startup_debug_request(void)
34 {
35 return 0;
36 }
37
38 #define LATENCY_TIMES (3)
39 #define LATENCY_IN_MSEC (128)
40 #define LWP_CONSOLE_PATH "CONSOLE=/dev/console"
41 const char *init_search_path[] = {
42 "/sbin/init",
43 "/bin/init",
44 };
45
46 /**
47 * Startup process 1 and do the essential works
48 */
lwp_startup(void)49 static int lwp_startup(void)
50 {
51 int error;
52
53 const char *init_path;
54 char *argv[] = {0, "&"};
55 char *envp[] = {LWP_CONSOLE_PATH, 0};
56
57 #ifdef LWP_DEBUG_INIT
58 int command;
59 int countdown = LATENCY_TIMES;
60 while (countdown)
61 {
62 command = lwp_startup_debug_request();
63 if (command)
64 {
65 return 0;
66 }
67 rt_kprintf("Press any key to stop init process startup ... %d\n", countdown);
68 countdown -= 1;
69 rt_thread_mdelay(LATENCY_IN_MSEC);
70 }
71 rt_kprintf("Starting init ...\n");
72 #endif /* LWP_DEBUG_INIT */
73
74 for (size_t i = 0; i < sizeof(init_search_path)/sizeof(init_search_path[0]); i++)
75 {
76 struct stat s;
77 init_path = init_search_path[i];
78 error = stat(init_path, &s);
79 if (error == 0)
80 {
81 argv[0] = (void *)init_path;
82 error = lwp_execve((void *)init_path, 0, sizeof(argv)/sizeof(argv[0]), argv, envp);
83 if (error < 0)
84 {
85 LOG_W("%s: failed to setup runtime environment\b"
86 "\tlwp_execve() failed with code %d", __func__, error);
87 }
88 else if (error != 1)
89 {
90 LOG_W("%s: pid 1 is already allocated", __func__);
91 error = -EBUSY;
92 }
93 else
94 {
95 rt_lwp_t p = lwp_from_pid_locked(1);
96 p->sig_protected = 1;
97
98 error = 0;
99 }
100 break;
101 }
102 }
103
104 if (error)
105 {
106 LOG_D("%s: failed to setup runtime environment\b"
107 "\tinit program not found", __func__);
108 }
109 return error;
110 }
111 INIT_APP_EXPORT(lwp_startup);
112
113 /* don't use heap for safety */
114 static struct rt_work _teardown_work;
115
116 #define INIT_PID 1
_teardown_entry(struct rt_work * work,void * work_data)117 static void _teardown_entry(struct rt_work *work, void *work_data)
118 {
119 int error;
120 void (*cb_on_reboot)(void) = work_data;
121
122 /* cleanup of process */
123 do
124 {
125 error = lwp_pid_wait_for_empty(RT_KILLABLE, RT_WAITING_FOREVER);
126 }
127 while (error);
128 LOG_I("All processes exited");
129
130 cb_on_reboot();
131 return;
132 }
133
_get_parent_pid(struct rt_lwp * lwp)134 static int _get_parent_pid(struct rt_lwp *lwp)
135 {
136 return lwp->parent ? lwp->parent->pid : 0;
137 }
138
139 /* reverse operation of lwp_startup() */
lwp_teardown(struct rt_lwp * lwp,void (* cb)(void))140 sysret_t lwp_teardown(struct rt_lwp *lwp, void (*cb)(void))
141 {
142 struct rt_work *work;
143
144 if (lwp->pid != INIT_PID && _get_parent_pid(lwp) != INIT_PID)
145 {
146 /* The calling process has insufficient privilege */
147 return -EPERM;
148 }
149
150 work = &_teardown_work;
151 rt_work_init(work, _teardown_entry, cb);
152
153 #define SOME_DELAY (RT_TICK_PER_SECOND / 10) /* allow idle to cleanup resource */
154 rt_work_submit(work, SOME_DELAY);
155
156 lwp_exit(lwp, LWP_CREATE_STAT_EXIT(EXIT_SUCCESS));
157
158 /* never return */
159 RT_ASSERT(0);
160 return 0;
161 }
162