1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <limits.h>
17 #include <poll.h>
18 #include <unistd.h>
19 #include <stdbool.h>
20 #include "ueventd.h"
21 #include "ueventd_read_cfg.h"
22 #include "ueventd_socket.h"
23 #define INIT_LOG_TAG "ueventd"
24 #include "init_log.h"
25 #include "init_socket.h"
26 #include "parameter.h"
27 
IsCompletenull28 static bool IsComplete()
29 {
30     static bool complete = false;
31     if (complete) {
32         return true;
33     }
34     char enable[8] = {0};
35     int ret = GetParameter("bootevent.boot.completed", "", enable, sizeof(enable));
36     if (ret != 0) {
37         INIT_LOGE("Failed to get param value");
38         return false;
39     }
40     if (strcmp(enable, "true") == 0) {
41         INIT_LOGI("boot completed");
42         complete = true;
43         return true;
44     }
45     return false;
46 }
47 
PollUeventdSocketTimeout(int ueventSockFd, bool ondemand)48 static void PollUeventdSocketTimeout(int ueventSockFd, bool ondemand)
49 {
50     struct pollfd pfd = {};
51     pfd.events = POLLIN;
52     pfd.fd = ueventSockFd;
53     int timeout = ondemand ? UEVENTD_POLL_TIME : -1;
54     int ret = -1;
55 
56     while (1) {
57         pfd.revents = 0;
58         ret = poll(&pfd, 1, timeout);
59         if (ret == 0) {
60             if (IsComplete()) {
61                 INIT_LOGI("poll ueventd socket timeout, ueventd exit");
62                 return;
63             }
64             INIT_LOGI("poll ueventd socket timeout, but init not complete");
65         } else if (ret < 0) {
66             INIT_LOGE("Failed to poll ueventd socket!");
67             return;
68         }
69         if (pfd.revents & (POLLIN | POLLERR)) {
70             ProcessUevent(ueventSockFd, NULL, 0); // Not require boot devices
71         }
72     }
73 }
74 
UeventdRetrigger(void)75 static int UeventdRetrigger(void)
76 {
77     const char *ueventdConfigs[] = {"/etc/ueventd.config", "/vendor/etc/ueventd.config", NULL};
78     int i = 0;
79     while (ueventdConfigs[i] != NULL) {
80         ParseUeventdConfigFile(ueventdConfigs[i++]);
81     }
82     int ueventSockFd = UeventdSocketInit();
83     if (ueventSockFd < 0) {
84         INIT_LOGE("Failed to create uevent socket!");
85         return -1;
86     }
87     RetriggerUevent(ueventSockFd, NULL, 0); // Not require boot devices
88     return 0;
89 }
90 
UeventdDaemon(int listen_only)91 static int UeventdDaemon(int listen_only)
92 {
93     // start log
94     EnableInitLog(INIT_INFO);
95     const char *ueventdConfigs[] = {"/etc/ueventd.config", "/vendor/etc/ueventd.config", NULL};
96     int i = 0;
97     while (ueventdConfigs[i] != NULL) {
98         ParseUeventdConfigFile(ueventdConfigs[i++]);
99     }
100     bool ondemand = true;
101     int ueventSockFd = GetControlSocket("ueventd");
102     if (ueventSockFd < 0) {
103         INIT_LOGW("Failed to get uevent socket, try to create");
104         ueventSockFd = UeventdSocketInit();
105         ondemand = false;
106     }
107     if (ueventSockFd < 0) {
108         INIT_LOGE("Failed to create uevent socket!");
109         return -1;
110     }
111     if (!listen_only && access(UEVENTD_FLAG, F_OK)) {
112         INIT_LOGI("Ueventd started, trigger uevent");
113         RetriggerUevent(ueventSockFd, NULL, 0); // Not require boot devices
114         int fd = open(UEVENTD_FLAG, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
115         if (fd < 0) {
116             INIT_LOGE("Failed to create ueventd flag!");
117             return -1;
118         }
119         (void)close(fd);
120     } else {
121         INIT_LOGI("ueventd start to process uevent message");
122         ProcessUevent(ueventSockFd, NULL, 0); // Not require boot devices
123     }
124     PollUeventdSocketTimeout(ueventSockFd, ondemand);
125     CloseUeventConfig();
126     return 0;
127 }
128 
UeventdEarlyBoot(void)129 static int UeventdEarlyBoot(void)
130 {
131     int ueventSockFd = UeventdSocketInit();
132     if (ueventSockFd < 0) {
133         return -1;
134     }
135 
136     char *devices[] = {
137         "/dev/block/vdb",
138         "/dev/block/vdc"
139     };
140 
141     RetriggerUevent(ueventSockFd, devices, 2);
142     close(ueventSockFd);
143     return 0;
144 }
145 
146 #define UEVENTD_MODE_DEAMON     0
147 #define UEVENTD_MODE_EARLY_BOOT 1
148 #define UEVENTD_MODE_RETRIGGER  2
149 #define UEVENTD_MODE_LISTEN     3
150 
usage(const char *name)151 static void usage(const char *name)
152 {
153     if (name == NULL || strlen(name) > PROCESS_NAME_MAX_LENGTH) {
154         return ;
155     }
156     printf("Usage: %s [OPTION]\n"
157            "Listening kernel uevent to create device node.\n"
158            "It will read configs from {/,/system,/chipset}/etc/ueventd.config.\n\n"
159            "The options may be used to set listening mode.\n"
160            "    -d, --daemon          working in deamon mode(default mode)\n"
161            "    -b, --boot            working in early booting mode, create required device nodes\n"
162            "    -l, --listen          listen in verbose mode\n"
163            "    -r, --retrigger       retrigger all uevents\n"
164            "    -v, --verbose         log level\n"
165            "    -h, --help            print this help info\n", name);
166 }
167 
UeventdLogPrint(int logLevel, uint32_t domain, const char *tag, const char *fmt, va_list vargs)168 static void UeventdLogPrint(int logLevel, uint32_t domain, const char *tag, const char *fmt, va_list vargs)
169 {
170     if (logLevel < (int)GetInitLogLevel()) {
171         return;
172     }
173     vprintf(fmt, vargs);
174     printf("\n");
175 }
176 
main(int argc, char *argv[])177 int main(int argc, char *argv[])
178 {
179     int opt;
180     int daemon = UEVENTD_MODE_DEAMON;
181 
182     while ((opt = getopt(argc, argv, "drblv:h")) != -1) {
183         switch (opt) {
184             case 'd':
185                 daemon = UEVENTD_MODE_DEAMON;
186                 break;
187             case 'r':
188                 SetInitCommLog(UeventdLogPrint);
189                 daemon = UEVENTD_MODE_RETRIGGER;
190                 break;
191             case 'b':
192                 SetInitCommLog(UeventdLogPrint);
193                 daemon = UEVENTD_MODE_EARLY_BOOT;
194                 break;
195             case 'v':
196                 EnableInitLog(atoi(optarg));
197                 SetInitCommLog(UeventdLogPrint);
198                 break;
199             case 'l':
200                 EnableInitLog(0);
201                 SetInitCommLog(UeventdLogPrint);
202                 daemon = UEVENTD_MODE_LISTEN;
203                 break;
204             case 'h':
205                 usage(argv[0]);
206                 exit(0);
207                 break;
208             default: /* '?' */
209                 fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n",
210                         argv[0]);
211                 exit(EXIT_FAILURE);
212         }
213     }
214 
215     if (daemon == UEVENTD_MODE_DEAMON) {
216         return UeventdDaemon(0);
217     } else if (daemon == UEVENTD_MODE_RETRIGGER) {
218         return UeventdRetrigger();
219     } else if (daemon == UEVENTD_MODE_LISTEN) {
220         return UeventdDaemon(1);
221     } else {
222         UeventdEarlyBoot();
223     }
224 
225     return 0;
226 }
227