1 /*
2 * Copyright (c) 2022 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 <cerrno>
17 #include <string>
18 #include <vector>
19 #include <iostream>
20 #include <memory>
21 #include <gtest/gtest.h>
22 #include <sys/stat.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include "ueventd.h"
26 #include "ueventd_device_handler.h"
27 #include "ueventd_socket.h"
28
29 using namespace testing::ext;
30 namespace UeventdUt {
31 namespace {
32 std::string g_testRoot{"/data/ueventd"};
33 int g_oldRootFd = -1;
34 }
35
36 class UeventdEventUnitTest : public testing::Test {
37 public:
SetUpTestCase(void)38 static void SetUpTestCase(void)
39 {
40 struct stat st{};
41 bool isExist = true;
42
43 if (stat(g_testRoot.c_str(), &st) < 0) {
44 if (errno != ENOENT) {
45 std::cout << "Cannot get stat of " << g_testRoot << std::endl;
46 // If we cannot get root for ueventd tests
47 // There is no reason to continue.
48 ASSERT_TRUE(false);
49 }
50 isExist = false;
51 }
52
53 if (isExist) {
54 RemoveDir(g_testRoot);
55 }
56 int ret = mkdir(g_testRoot.c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
57 if (ret < 0) {
58 std::cout << "Cannot create directory " << g_testRoot << " err = " << errno << std::endl;
59 ASSERT_TRUE(0);
60 }
61 if (SwitchRoot() < 0) {
62 // If we cannot do this, there is not reason to continue
63 ASSERT_TRUE(0);
64 }
65 }
66
TearDownTestCase(void)67 static void TearDownTestCase(void)
68 {
69 // Switch back to old root
70 if (g_oldRootFd < 0) {
71 std::cout << "Old root directory is not valid\n";
72 return;
73 }
74
75 if (fchdir(g_oldRootFd) < 0) {
76 std::cout << "Failed to change working directory to '/', err = " << errno << std::endl;
77 }
78
79 if (chroot(".") < 0) {
80 std::cout << "Failed to change root directory to '/', err = " << errno << std::endl;
81 }
82 close(g_oldRootFd);
83 g_oldRootFd = -1;
84 std::cout << "Change root back done\n";
85 // Remove test data
86 if (RemoveDir(g_testRoot) < 0) {
87 std::cout << "Failed to remove " << g_testRoot << ", err = " << errno << std::endl;
88 }
89 }
90
RemoveDir(const std::string & path)91 static int RemoveDir(const std::string &path)
92 {
93 if (path.empty()) {
94 // Treat it as OK
95 return 0;
96 }
97 auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);
98 if (dir == nullptr) {
99 std::cout << "Cannot open dir " << path << ", err = " << errno << std::endl;
100 return -1;
101 }
102
103 struct dirent *dp = nullptr;
104 while ((dp = readdir(dir.get())) != nullptr) {
105 // Skip hidden files
106 if (dp->d_name[0] == '.') {
107 continue;
108 }
109 bool endsWithSlash = (path.find_last_of("/") == path.size() - 1);
110 std::string fullPath {};
111 if (endsWithSlash) {
112 fullPath = path + dp->d_name;
113 } else {
114 fullPath = path + "/" + dp->d_name;
115 }
116 struct stat st {};
117 if (stat(fullPath.c_str(), &st) < 0) {
118 std::cout << "Failed to get stat of " << fullPath << std::endl;
119 continue; // Should we continue?
120 }
121 if (S_ISDIR(st.st_mode)) {
122 if (RemoveDir(fullPath) < 0) {
123 std::cout << "Failed to remove directory " << fullPath << std::endl;
124 return -1;
125 }
126 } else {
127 if (unlink(fullPath.c_str()) < 0) {
128 std::cout << "Failed to unlink file " << fullPath << std::endl;
129 return -1;
130 }
131 }
132 }
133 return rmdir(path.c_str());
134 }
135
SwitchRoot()136 static int SwitchRoot()
137 {
138 if (g_oldRootFd >= 0) {
139 close(g_oldRootFd);
140 g_oldRootFd = -1;
141 }
142 // Save old root
143 DIR *dir = opendir("/");
144 if (dir == nullptr) {
145 std::cout << "Failed to open root directory\n";
146 return -1;
147 }
148 g_oldRootFd = dirfd(dir);
149 if (g_oldRootFd < 0) {
150 std::cout << "Failed to pen root directory, err = " << errno << std::endl;
151 return -1;
152 }
153
154 // Changing working directory to "/data/ueventd"
155 if (chdir(g_testRoot.c_str()) < 0) {
156 std::cout << "Failed to change working directory to " << g_testRoot << ", err = " << errno << std::endl;
157 close(g_oldRootFd);
158 g_oldRootFd = -1;
159 }
160
161 if (chroot(g_testRoot.c_str()) < 0) {
162 std::cout << "Failed to change root directory to " << g_testRoot << ", err = " << errno << std::endl;
163 close(g_oldRootFd);
164 g_oldRootFd = -1;
165 }
166 std::cout << "Change root to " << g_testRoot << " done\n";
167 return 0;
168 }
169
SetUp()170 void SetUp() {};
TearDown()171 void TearDown() {};
172 };
173
174 // Generate uevent buffer from struct uevent.
175 // extra data used to break uevent buffer to check
176 // if ueventd will handle this situation correctly
GenerateUeventBuffer(struct Uevent & uevent,std::vector<std::string> & extraData)177 std::string GenerateUeventBuffer(struct Uevent &uevent, std::vector<std::string> &extraData)
178 {
179 std::string ueventdBuffer{};
180 if (uevent.syspath != nullptr) {
181 ueventdBuffer.append(std::string("DEVPATH=") + uevent.syspath + '\000');
182 }
183 if (uevent.subsystem != nullptr) {
184 ueventdBuffer.append(std::string("SUBSYSTEM=") + uevent.subsystem + '\000');
185 }
186 ueventdBuffer.append(std::string("ACTION=") + ActionString(uevent.action) + '\000');
187 if (uevent.deviceName != nullptr) {
188 ueventdBuffer.append(std::string("DEVNAME=") + uevent.deviceName + '\000');
189 }
190 if (uevent.partitionName != nullptr) {
191 ueventdBuffer.append(std::string("PARTNAME=") + uevent.partitionName + '\000');
192 }
193 ueventdBuffer.append(std::string("PARTN=") + std::to_string(uevent.partitionNum) + '\000');
194 ueventdBuffer.append(std::string("MAJOR=") + std::to_string(uevent.major) + '\000');
195 ueventdBuffer.append(std::string("MINOR=") + std::to_string(uevent.minor) + '\000');
196 ueventdBuffer.append(std::string("DEVUID=") + std::to_string(uevent.ug.uid) + '\000');
197 ueventdBuffer.append(std::string("DEVGID=") + std::to_string(uevent.ug.gid) + '\000');
198 if (uevent.firmware != nullptr) {
199 ueventdBuffer.append(std::string("FIRMWARE=") + uevent.firmware + '\000');
200 }
201 ueventdBuffer.append(std::string("BUSNUM=") + std::to_string(uevent.busNum) + '\000');
202 ueventdBuffer.append(std::string("DEVNUM=") + std::to_string(uevent.devNum) + '\000');
203
204 if (!extraData.empty()) {
205 for (const auto &data : extraData) {
206 ueventdBuffer.append(data);
207 }
208 }
209 return ueventdBuffer;
210 }
211
IsFileExist(const std::string & file)212 bool IsFileExist(const std::string &file)
213 {
214 struct stat st{};
215 if (file.empty()) {
216 return false;
217 }
218
219 if (stat(file.c_str(), &st) < 0) {
220 if (errno == ENOENT) {
221 std::cout << "File " << file << " is not exist\n";
222 }
223 return false;
224 }
225 return true;
226 }
227
228 HWTEST_F(UeventdEventUnitTest, TestParseUeventdEvent, TestSize.Level1)
229 {
230 struct Uevent uevent = {
231 .subsystem = "block",
232 .syspath = "/block/mmc/test",
233 .deviceName = "test",
234 .partitionName = "userdata",
235 .firmware = "",
236 .action = ACTION_ADD,
237 .partitionNum = 3,
238 .major = 1,
239 .minor = 2,
240 .ug = {
241 .uid = 0,
242 .gid = 0,
243 },
244 .busNum = 1,
245 .devNum = 2,
246 };
247
248 std::vector<std::string> extraData{};
249 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
250 std::cout << "ueventBuffer = [" << ueventBuffer << "]. size = " << ueventBuffer.length() << std::endl;
251 struct Uevent outEvent;
252 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
253 EXPECT_EQ(outEvent.action, ACTION_ADD);
254 EXPECT_EQ(outEvent.busNum, 1);
255 EXPECT_STREQ(outEvent.subsystem, "block");
256 EXPECT_STREQ(outEvent.deviceName, "test");
257 EXPECT_EQ(outEvent.devNum, 2);
258 EXPECT_EQ(outEvent.major, 1);
259 EXPECT_EQ(outEvent.minor, 2);
260 EXPECT_EQ(outEvent.partitionNum, 3);
261 EXPECT_STREQ(outEvent.partitionName, "userdata");
262 EXPECT_STREQ(outEvent.syspath, "/block/mmc/test");
263 EXPECT_EQ(outEvent.ug.gid, 0);
264 EXPECT_EQ(outEvent.ug.uid, 0);
265 }
266
267 HWTEST_F(UeventdEventUnitTest, TestUeventActions, TestSize.Level1)
268 {
269 struct Uevent uevent = {
270 .subsystem = "block",
271 .syspath = "/block/mmc/test",
272 .deviceName = "test",
273 .partitionName = "userdata",
274 .action = ACTION_UNKNOWN,
275 };
276 std::vector<std::string> extraData {};
277 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
278 std::cout << "ueventBuffer = [" << ueventBuffer << "]. size = " << ueventBuffer.length() << std::endl;
279 struct Uevent outEvent;
280 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
281 EXPECT_EQ(outEvent.action, ACTION_UNKNOWN);
282 }
283
284 HWTEST_F(UeventdEventUnitTest, TestUeventHandleBlockDevicesInvalidParameters, TestSize.Level1)
285 {
286 HandleBlockDeviceEvent(nullptr);
287 // Not block device
288 struct Uevent noBlockUevent = {
289 .subsystem = "char",
290 };
291 HandleBlockDeviceEvent(&noBlockUevent);
292
293 struct Uevent invalidDevNoUevent = {
294 .subsystem = "block",
295 .major = -1,
296 .minor = -1,
297 };
298 HandleBlockDeviceEvent(&invalidDevNoUevent);
299
300 struct Uevent invalidSysPathUevent = {
301 .subsystem = "block",
302 .syspath = nullptr,
303 .major = 1,
304 .minor = 1,
305 };
306 HandleBlockDeviceEvent(&invalidSysPathUevent);
307 }
308
309 HWTEST_F(UeventdEventUnitTest, TestUeventHandleBlockDevicesValidParameters, TestSize.Level1)
310 {
311 struct Uevent uevent = {
312 .subsystem = "block",
313 .syspath = "/block/mmc/block_device_test",
314 .deviceName = "block_device_test",
315 .partitionName = "block_device_test",
316 .firmware = "",
317 .action = ACTION_ADD,
318 .partitionNum = 3,
319 .major = 5,
320 .minor = 15,
321 .ug = {
322 .uid = 0,
323 .gid = 0,
324 },
325 .busNum = 1,
326 .devNum = 2,
327 };
328
329 HandleBlockDeviceEvent(&uevent);
330 // Check results
331 std::string blockDevice = "/dev/block/block_device_test";
332 struct stat st{};
333 int ret = stat(blockDevice.c_str(), &st);
334 EXPECT_EQ(ret, 0);
335 bool isBlock = S_ISBLK(st.st_mode);
336 EXPECT_TRUE(isBlock);
337 }
338
339 HWTEST_F(UeventdEventUnitTest, TestUeventHandleBlockDevicesRemoved, TestSize.Level1)
340 {
341 struct Uevent uevent = {
342 .subsystem = "block",
343 .syspath = "/block/mmc/block_device_test",
344 .deviceName = "block_device_test",
345 .partitionName = "block_device_test",
346 .firmware = "",
347 .action = ACTION_REMOVE,
348 .partitionNum = 3,
349 .major = 5,
350 .minor = 15,
351 .ug = {
352 .uid = 0,
353 .gid = 0,
354 },
355 .busNum = 1,
356 .devNum = 2,
357 };
358 std::string blockDevice = "/dev/block/block_device_test";
359 struct stat st{};
360 int ret = stat(blockDevice.c_str(), &st);
361 if (ret < 0) {
362 // This should not happen actually, because we've created the device node before.
363 std::cout << "Warning. Block device " << blockDevice << " is not exist.\n";
364 }
365 HandleBlockDeviceEvent(&uevent);
366 ret = stat(blockDevice.c_str(), &st);
367 EXPECT_EQ(ret, -1);
368 EXPECT_EQ(errno, ENOENT);
369 }
370
371 HWTEST_F(UeventdEventUnitTest, TestUeventHandleBlockDevicesChanged, TestSize.Level1)
372 {
373 struct Uevent uevent = {
374 .subsystem = "block",
375 .syspath = "/block/mmc/block_device_test",
376 .deviceName = "block_device_test",
377 .partitionName = "block_device_test",
378 .firmware = "",
379 .action = ACTION_REMOVE,
380 .partitionNum = 3,
381 .major = 5,
382 .minor = 15,
383 .ug = {
384 .uid = 0,
385 .gid = 0,
386 },
387 .busNum = 1,
388 .devNum = 2,
389 };
390 HandleBlockDeviceEvent(&uevent);
391 }
392
393 HWTEST_F(UeventdEventUnitTest, TestUeventHandleOtherDevicesInvalidParameters, TestSize.Level1)
394 {
395 HandleOtherDeviceEvent(nullptr);
396 // Not Character device
397 struct Uevent invalidDevNoUevent = {
398 .subsystem = "test",
399 .major = -1,
400 .minor = -1,
401 };
402 HandleOtherDeviceEvent(&invalidDevNoUevent);
403
404 struct Uevent invalidSysPathUevent = {
405 .subsystem = "test",
406 .syspath = nullptr,
407 .major = 5,
408 .minor = 9,
409 };
410 HandleOtherDeviceEvent(&invalidSysPathUevent);
411
412 struct Uevent invalidSubsystemUevent = {
413 .subsystem = "",
414 .syspath = "/devices/test/char",
415 .major = 5,
416 .minor = 9,
417 };
418 HandleOtherDeviceEvent(&invalidSubsystemUevent);
419 }
420
421 HWTEST_F(UeventdEventUnitTest, TestUeventHandleOtherDevicesValidParameters, TestSize.Level1)
422 {
423 struct Uevent uevent = {
424 .subsystem = "extcon3",
425 .syspath = "/devices/platform/headset/extcon/extcon3",
426 .deviceName = "extcon3-1",
427 .major = 5,
428 .minor = 9,
429 };
430 HandleOtherDeviceEvent(&uevent);
431 auto exist = IsFileExist("/dev/extcon3-1");
432 EXPECT_TRUE(exist);
433 exist = IsFileExist("/dev/extcon3");
434 EXPECT_FALSE(exist);
435 }
436
437 HWTEST_F(UeventdEventUnitTest, TestUeventHandleUsbDevicesWithDeviceName, TestSize.Level1)
438 {
439 struct Uevent uevent = {
440 .subsystem = "usb",
441 .syspath = "/devices/platform/headset/extcon/usb-dev",
442 .deviceName = "usb-dev",
443 .major = 8,
444 .minor = 9,
445 };
446 HandleOtherDeviceEvent(&uevent);
447 auto exist = IsFileExist("/dev/usb-dev");
448 EXPECT_TRUE(exist);
449 }
450
451 HWTEST_F(UeventdEventUnitTest, TestUeventHandleInvalidUsbDevices, TestSize.Level1)
452 {
453 struct Uevent uevent = {
454 .subsystem = "usb",
455 .syspath = "/devices/platform/headset/extcon/usb-dev-1",
456 .major = 8,
457 .minor = 10,
458 .busNum = -1,
459 .devNum = -1,
460 };
461 HandleOtherDeviceEvent(&uevent);
462 auto exist = IsFileExist("/dev/usb-dev-1");
463 EXPECT_FALSE(exist);
464 }
465
466 HWTEST_F(UeventdEventUnitTest, TestUeventHandleUsbDevicesWithBusNo, TestSize.Level1)
467 {
468 struct Uevent uevent = {
469 .subsystem = "usb",
470 .syspath = "/devices/platform/headset/extcon/usb-dev",
471 .major = 8,
472 .minor = 9,
473 .busNum = 3,
474 .devNum = 4,
475 };
476 HandleOtherDeviceEvent(&uevent);
477 auto exist = IsFileExist("/dev/bus/usb/003/004");
478 EXPECT_TRUE(exist);
479 }
480 } // UeventdUt