• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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