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