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 <selinux/selinux.h>
26
27#include "param_stub.h"
28#include "ueventd.h"
29#include "ueventd_device_handler.h"
30#include "ueventd_socket.h"
31
32using namespace testing::ext;
33
34namespace UeventdUt {
35namespace {
36    std::string g_testRoot{"/data/ueventd"};
37    int g_oldRootFd = -1;
38}
39
40class UeventdEventUnitTest : public testing::Test {
41public:
42static void SetUpTestCase(void)
43{
44    struct stat st{};
45    bool isExist = true;
46
47    if (stat(g_testRoot.c_str(), &st) < 0) {
48        if (errno != ENOENT) {
49            std::cout << "Cannot get stat of " << g_testRoot << std::endl;
50            // If we cannot get root for ueventd tests
51            // There is no reason to continue.
52            ASSERT_TRUE(false);
53        }
54        isExist = false;
55    }
56
57    if (isExist) {
58        RemoveDir(g_testRoot);
59    }
60    int ret = mkdir(g_testRoot.c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
61    if (ret < 0) {
62        std::cout << "Cannot create directory " << g_testRoot << " err = " << errno << std::endl;
63        ASSERT_TRUE(0);
64    }
65    if (SwitchRoot() < 0) {
66        // If we cannot do this, there is not reason to continue
67        ASSERT_TRUE(0);
68    }
69}
70
71static void TearDownTestCase(void)
72{
73    // Switch back to old root
74    if (g_oldRootFd < 0) {
75        std::cout << "Old root directory is not valid\n";
76        return;
77    }
78
79    if (fchdir(g_oldRootFd) < 0) {
80        std::cout << "Failed to change working directory to '/', err = " << errno << std::endl;
81    }
82
83    if (chroot(".") < 0) {
84        std::cout << "Failed to change root directory to '/', err = " << errno << std::endl;
85    }
86    close(g_oldRootFd);
87    g_oldRootFd = -1;
88    std::cout << "Change root back done\n";
89    // Remove test data
90    if (RemoveDir(g_testRoot) < 0) {
91        std::cout << "Failed to remove " << g_testRoot << ", err = " << errno << std::endl;
92    }
93}
94
95static int RemoveDir(const std::string &path)
96{
97    if (path.empty()) {
98        // Treat it as OK
99        return 0;
100    }
101    auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);
102    if (dir == nullptr) {
103        std::cout << "Cannot open dir " << path << ", err = " << errno << std::endl;
104        return -1;
105    }
106
107    struct dirent *dp = nullptr;
108    while ((dp = readdir(dir.get())) != nullptr) {
109        // Skip hidden files
110        if (dp->d_name[0] == '.') {
111            continue;
112        }
113        bool endsWithSlash = (path.find_last_of("/") == path.size() - 1);
114        std::string fullPath {};
115        if (endsWithSlash) {
116            fullPath = path + dp->d_name;
117        } else {
118            fullPath = path + "/" + dp->d_name;
119        }
120        struct stat st {};
121        if (stat(fullPath.c_str(), &st) < 0) {
122            std::cout << "Failed to get stat of " << fullPath << std::endl;
123            continue; // Should we continue?
124        }
125        if (S_ISDIR(st.st_mode)) {
126            if (RemoveDir(fullPath) < 0) {
127                std::cout << "Failed to remove directory " << fullPath << std::endl;
128                return -1;
129            }
130        } else {
131            if (unlink(fullPath.c_str()) < 0) {
132                std::cout << "Failed to unlink file " << fullPath << std::endl;
133                return -1;
134            }
135        }
136    }
137    return rmdir(path.c_str());
138}
139
140static int SwitchRoot()
141{
142    if (g_oldRootFd >= 0) {
143        close(g_oldRootFd);
144        g_oldRootFd = -1;
145    }
146    // Save old root
147    DIR *dir = opendir("/");
148    if (dir == nullptr) {
149        std::cout << "Failed to open root directory\n";
150        return -1;
151    }
152    g_oldRootFd = dirfd(dir);
153    if (g_oldRootFd < 0) {
154        std::cout << "Failed to pen root directory, err = " << errno << std::endl;
155        return -1;
156    }
157
158    // Changing working directory to "/data/ueventd"
159    if (chdir(g_testRoot.c_str()) < 0) {
160        std::cout << "Failed to change working directory to " << g_testRoot << ", err = " << errno << std::endl;
161        close(g_oldRootFd);
162        g_oldRootFd = -1;
163    }
164
165    if (chroot(g_testRoot.c_str()) < 0) {
166        std::cout << "Failed to change root directory to " << g_testRoot << ", err = " << errno << std::endl;
167        close(g_oldRootFd);
168        g_oldRootFd = -1;
169    }
170    std::cout << "Change root to " << g_testRoot << " done\n";
171    return 0;
172}
173
174void SetUp() {};
175void TearDown() {};
176};
177
178// Generate uevent buffer from struct uevent.
179// extra data used to break uevent buffer to check
180// if ueventd will handle this situation correctly
181std::string GenerateUeventBuffer(struct Uevent &uevent, std::vector<std::string> &extraData)
182{
183    std::string ueventdBuffer{};
184    if (uevent.syspath != nullptr) {
185        ueventdBuffer.append(std::string("DEVPATH=") + uevent.syspath + '\000');
186    }
187    if (uevent.subsystem != nullptr) {
188        ueventdBuffer.append(std::string("SUBSYSTEM=") + uevent.subsystem + '\000');
189    }
190    ueventdBuffer.append(std::string("ACTION=") + ActionString(uevent.action) + '\000');
191    if (uevent.deviceName != nullptr) {
192        ueventdBuffer.append(std::string("DEVNAME=") + uevent.deviceName + '\000');
193    }
194    if (uevent.partitionName != nullptr) {
195        ueventdBuffer.append(std::string("PARTNAME=") + uevent.partitionName + '\000');
196    }
197    ueventdBuffer.append(std::string("PARTN=") + std::to_string(uevent.partitionNum) + '\000');
198    ueventdBuffer.append(std::string("MAJOR=") + std::to_string(uevent.major) + '\000');
199    ueventdBuffer.append(std::string("MINOR=") + std::to_string(uevent.minor) + '\000');
200    ueventdBuffer.append(std::string("DEVUID=") + std::to_string(uevent.ug.uid) + '\000');
201    ueventdBuffer.append(std::string("DEVGID=") + std::to_string(uevent.ug.gid) + '\000');
202    if (uevent.firmware != nullptr) {
203        ueventdBuffer.append(std::string("FIRMWARE=") + uevent.firmware + '\000');
204    }
205    ueventdBuffer.append(std::string("BUSNUM=") + std::to_string(uevent.busNum) + '\000');
206    ueventdBuffer.append(std::string("DEVNUM=") + std::to_string(uevent.devNum) + '\000');
207
208    if (!extraData.empty()) {
209        for (const auto &data : extraData) {
210            ueventdBuffer.append(data);
211        }
212    }
213    return ueventdBuffer;
214}
215
216bool IsFileExist(const std::string &file)
217{
218    struct stat st{};
219    if (file.empty()) {
220        return false;
221    }
222
223    if (stat(file.c_str(), &st) < 0) {
224        if (errno == ENOENT) {
225            std::cout << "File " << file << " is not exist\n";
226        }
227        return false;
228    }
229    return true;
230}
231
232HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_ParseUeventdEvent001, TestSize.Level1)
233{
234    struct Uevent uevent = {
235        .subsystem = "block",
236        .syspath = "/block/mmc/test",
237        .deviceName = "test",
238        .partitionName = "userdata",
239        .firmware = "",
240        .action = ACTION_ADD,
241        .partitionNum = 3,
242        .major = 1,
243        .minor = 2,
244        .ug = {
245            .uid = 0,
246            .gid = 0,
247        },
248        .busNum = 1,
249        .devNum = 2,
250    };
251
252    std::vector<std::string> extraData{};
253    auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
254    std::cout << "ueventBuffer = [" << ueventBuffer << "]. size = " << ueventBuffer.length() << std::endl;
255    struct Uevent outEvent;
256    ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
257    EXPECT_EQ(outEvent.action, ACTION_ADD);
258    EXPECT_EQ(outEvent.busNum, 1);
259    EXPECT_STREQ(outEvent.subsystem, "block");
260    EXPECT_STREQ(outEvent.deviceName, "test");
261    EXPECT_EQ(outEvent.devNum, 2);
262    EXPECT_EQ(outEvent.major, 1);
263    EXPECT_EQ(outEvent.minor, 2);
264    EXPECT_EQ(outEvent.partitionNum, 3);
265    EXPECT_STREQ(outEvent.partitionName, "userdata");
266    EXPECT_STREQ(outEvent.syspath, "/block/mmc/test");
267    EXPECT_EQ(outEvent.ug.gid, 0);
268    EXPECT_EQ(outEvent.ug.uid, 0);
269    HandleUevent(&outEvent);
270}
271
272HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_Actions001, TestSize.Level1)
273{
274    struct Uevent uevent = {
275        .subsystem = "block",
276        .syspath = "/block/mmc/test",
277        .deviceName = "test",
278        .partitionName = "userdata",
279        .action = ACTION_UNKNOWN,
280    };
281    std::vector<std::string> extraData {};
282    auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
283    std::cout << "ueventBuffer = [" << ueventBuffer << "]. size = " << ueventBuffer.length() << std::endl;
284    struct Uevent outEvent;
285    ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
286    EXPECT_EQ(outEvent.action, ACTION_UNKNOWN);
287    HandleUevent(&outEvent);
288}
289
290HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesInvalidParameters001, TestSize.Level1)
291{
292    HandleBlockDeviceEvent(nullptr);
293    // Not block device
294    struct Uevent noBlockUevent = {
295        .subsystem = "char",
296    };
297    HandleBlockDeviceEvent(&noBlockUevent);
298
299    struct Uevent invalidDevNoUevent = {
300        .subsystem = "block",
301        .major = -1,
302        .minor = -1,
303    };
304    HandleBlockDeviceEvent(&invalidDevNoUevent);
305
306    struct Uevent invalidSysPathUevent = {
307        .subsystem = "block",
308        .syspath = nullptr,
309        .major = 1,
310        .minor = 1,
311    };
312    HandleBlockDeviceEvent(&invalidSysPathUevent);
313}
314
315HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesValidParameters002, TestSize.Level1)
316{
317    struct Uevent uevent = {
318        .subsystem = "block",
319        .syspath = "/block/mmc/block_device_test",
320        .deviceName = "block_device_test",
321        .partitionName = "block_device_test",
322        .firmware = "",
323        .action = ACTION_ADD,
324        .partitionNum = 3,
325        .major = 5,
326        .minor = 15,
327        .ug = {
328            .uid = 0,
329            .gid = 0,
330        },
331        .busNum = 1,
332        .devNum = 2,
333    };
334
335    HandleBlockDeviceEvent(&uevent);
336    // Check results
337    std::string blockDevice = "/dev/block/block_device_test";
338    struct stat st{};
339    int ret = stat(blockDevice.c_str(), &st);
340    EXPECT_EQ(ret, 0);
341    bool isBlock = S_ISBLK(st.st_mode);
342    EXPECT_TRUE(isBlock);
343}
344
345HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesRemoved001, TestSize.Level1)
346{
347    struct Uevent uevent = {
348        .subsystem = "block",
349        .syspath = "/block/mmc/block_device_test",
350        .deviceName = "block_device_test",
351        .partitionName = "block_device_test",
352        .firmware = "",
353        .action = ACTION_REMOVE,
354        .partitionNum = 3,
355        .major = 5,
356        .minor = 15,
357        .ug = {
358            .uid = 0,
359            .gid = 0,
360        },
361        .busNum = 1,
362        .devNum = 2,
363    };
364    std::string blockDevice = "/dev/block/block_device_test";
365    struct stat st{};
366    int ret = stat(blockDevice.c_str(), &st);
367    if (ret < 0) {
368        // This should not happen actually, because we've created the device node before.
369        std::cout << "Warning. Block device " << blockDevice << " is not exist.\n";
370    }
371    HandleBlockDeviceEvent(&uevent);
372    ret = stat(blockDevice.c_str(), &st);
373    EXPECT_EQ(ret, -1);
374    EXPECT_EQ(errno, ENOENT);
375}
376
377HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesChanged001, TestSize.Level1)
378{
379    struct Uevent uevent = {
380        .subsystem = "block",
381        .syspath = "/block/mmc/block_device_test",
382        .deviceName = "block_device_test",
383        .partitionName = "block_device_test",
384        .firmware = "",
385        .action = ACTION_REMOVE,
386        .partitionNum = 3,
387        .major = 5,
388        .minor = 15,
389        .ug = {
390            .uid = 0,
391            .gid = 0,
392        },
393        .busNum = 1,
394        .devNum = 2,
395    };
396    HandleBlockDeviceEvent(&uevent);
397}
398
399HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleOtherDevicesInvalidParameters001, TestSize.Level1)
400{
401    HandleOtherDeviceEvent(nullptr);
402    // Not Character device
403    struct Uevent invalidDevNoUevent = {
404        .subsystem = "test",
405        .major = -1,
406        .minor = -1,
407    };
408    HandleOtherDeviceEvent(&invalidDevNoUevent);
409
410    struct Uevent invalidSysPathUevent = {
411        .subsystem = "test",
412        .syspath = nullptr,
413        .major = 5,
414        .minor = 9,
415    };
416    HandleOtherDeviceEvent(&invalidSysPathUevent);
417
418    struct Uevent invalidSubsystemUevent = {
419        .subsystem = "",
420        .syspath = "/devices/test/char",
421        .major = 5,
422        .minor = 9,
423    };
424    HandleOtherDeviceEvent(&invalidSubsystemUevent);
425}
426
427HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleOtherDevicesValidParameters001, TestSize.Level1)
428{
429    struct Uevent uevent = {
430        .subsystem = "extcon3",
431        .syspath = "/devices/platform/headset/extcon/extcon3",
432        .deviceName = "extcon3-1",
433        .major = 5,
434        .minor = 9,
435    };
436    HandleOtherDeviceEvent(&uevent);
437    auto exist = IsFileExist("/dev/extcon3-1");
438    EXPECT_TRUE(exist);
439    exist = IsFileExist("/dev/extcon3");
440    EXPECT_FALSE(exist);
441}
442
443HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleUsbDevicesWithDeviceName001, TestSize.Level1)
444{
445    struct Uevent uevent = {
446        .subsystem = "usb",
447        .syspath = "/devices/platform/headset/extcon/usb-dev",
448        .deviceName = "usb-dev",
449        .major = 8,
450        .minor = 9,
451    };
452    HandleOtherDeviceEvent(&uevent);
453    auto exist = IsFileExist("/dev/usb-dev");
454    EXPECT_TRUE(exist);
455}
456
457HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleInvalidUsbDevices001, TestSize.Level1)
458{
459    struct Uevent uevent = {
460        .subsystem = "usb",
461        .syspath = "/devices/platform/headset/extcon/usb-dev-1",
462        .major = 8,
463        .minor = 10,
464        .busNum = -1,
465        .devNum = -1,
466    };
467    HandleOtherDeviceEvent(&uevent);
468    auto exist = IsFileExist("/dev/usb-dev-1");
469    EXPECT_FALSE(exist);
470}
471
472HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleUsbDevicesWithBusNo001, TestSize.Level1)
473{
474    struct Uevent uevent = {
475        .subsystem = "usb",
476        .syspath = "/devices/platform/headset/extcon/usb-dev",
477        .major = 8,
478        .minor = 9,
479        .busNum = 3,
480        .devNum = 4,
481    };
482    HandleOtherDeviceEvent(&uevent);
483    auto exist = IsFileExist("/dev/bus/usb/003/004");
484    EXPECT_TRUE(exist);
485}
486
487HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_Handle001, TestSize.Level1)
488{
489    char path[] = {"/data/ueventd"};
490    RetriggerUeventByPath(g_oldRootFd, path);
491}
492
493HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_FirmwareUevent001, TestSize.Level1)
494{
495    struct Uevent uevent = {
496        .subsystem = "firmware",
497        .syspath = "/block/mmc/test",
498        .deviceName = "test",
499        .partitionName = "userdata",
500        .firmware = "",
501        .action = ACTION_ADD,
502        .partitionNum = 3,
503        .major = 1,
504        .minor = 2,
505        .ug = {
506            .uid = 0,
507            .gid = 0,
508        },
509        .busNum = 1,
510        .devNum = 2,
511    };
512
513    std::vector<std::string> extraData{};
514    auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
515    struct Uevent outEvent;
516    ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
517    EXPECT_EQ(outEvent.action, ACTION_ADD);
518    EXPECT_EQ(outEvent.busNum, 1);
519    EXPECT_STREQ(outEvent.subsystem, "firmware");
520    EXPECT_STREQ(outEvent.deviceName, "test");
521    EXPECT_EQ(outEvent.devNum, 2);
522    EXPECT_EQ(outEvent.major, 1);
523    EXPECT_EQ(outEvent.minor, 2);
524    EXPECT_EQ(outEvent.partitionNum, 3);
525    EXPECT_STREQ(outEvent.partitionName, "userdata");
526    EXPECT_STREQ(outEvent.syspath, "/block/mmc/test");
527    EXPECT_EQ(outEvent.ug.gid, 0);
528    EXPECT_EQ(outEvent.ug.uid, 0);
529    HandleUevent(&outEvent);
530}
531
532HWTEST_F(UeventdEventUnitTest, Init_UeventdEventTest_PlatformEvent001, TestSize.Level1)
533{
534    struct Uevent uevent = {
535        .subsystem = "platform",
536        .syspath = "/block/mmc/test",
537        .deviceName = "test",
538        .partitionName = "userdata",
539        .firmware = "",
540        .action = ACTION_ADD,
541        .partitionNum = 3,
542        .major = 1,
543        .minor = 2,
544        .ug = {
545            .uid = 0,
546            .gid = 0,
547        },
548        .busNum = 1,
549        .devNum = 2,
550    };
551
552    std::vector<std::string> extraData{};
553    auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
554    struct Uevent outEvent;
555    ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
556    EXPECT_EQ(outEvent.action, ACTION_ADD);
557    EXPECT_EQ(outEvent.busNum, 1);
558    EXPECT_STREQ(outEvent.subsystem, "platform");
559    EXPECT_STREQ(outEvent.deviceName, "test");
560    EXPECT_EQ(outEvent.devNum, 2);
561    EXPECT_EQ(outEvent.major, 1);
562    EXPECT_EQ(outEvent.minor, 2);
563    EXPECT_EQ(outEvent.partitionNum, 3);
564    EXPECT_STREQ(outEvent.partitionName, "userdata");
565    EXPECT_STREQ(outEvent.syspath, "/block/mmc/test");
566    EXPECT_EQ(outEvent.ug.gid, 0);
567    EXPECT_EQ(outEvent.ug.uid, 0);
568    HandleUevent(&outEvent);
569}
570
571HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_PlatformEventUsb001, TestSize.Level1)
572{
573    struct Uevent uevent = {
574        .subsystem = "usb",
575        .syspath = "/block/mmc/test",
576        .deviceName = "test",
577        .partitionName = "userdata",
578        .firmware = "",
579        .action = ACTION_ADD,
580        .partitionNum = 3,
581        .major = 1,
582        .minor = 2,
583        .ug = {
584            .uid = 0,
585            .gid = 0,
586        },
587        .busNum = 1,
588        .devNum = 2,
589    };
590
591    std::vector<std::string> extraData{};
592    auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
593    struct Uevent outEvent;
594    ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
595    EXPECT_EQ(outEvent.action, ACTION_ADD);
596    EXPECT_EQ(outEvent.busNum, 1);
597    EXPECT_STREQ(outEvent.subsystem, "usb");
598    EXPECT_STREQ(outEvent.deviceName, "test");
599    EXPECT_EQ(outEvent.devNum, 2);
600    EXPECT_EQ(outEvent.major, 1);
601    EXPECT_EQ(outEvent.minor, 2);
602    EXPECT_EQ(outEvent.partitionNum, 3);
603    EXPECT_STREQ(outEvent.partitionName, "userdata");
604    EXPECT_STREQ(outEvent.syspath, "/block/mmc/test");
605    EXPECT_EQ(outEvent.ug.gid, 0);
606    EXPECT_EQ(outEvent.ug.uid, 0);
607    HandleUevent(&outEvent);
608}
609
610static void TestUeventAction(ACTION action)
611{
612    struct Uevent uevent = {
613        .subsystem = "block",
614        .syspath = "/block/mmc/test",
615        .deviceName = "test",
616        .partitionName = "userdata",
617        .firmware = "",
618        .action = action,
619        .partitionNum = 3,
620        .major = 1,
621        .minor = 2,
622        .ug = {
623            .uid = 0,
624            .gid = 0,
625        },
626        .busNum = 1,
627        .devNum = 2,
628    };
629
630    std::vector<std::string> extraData{};
631    auto ueventBuffer = GenerateUeventBuffer(uevent, extraData);
632    struct Uevent outEvent;
633    ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent);
634    EXPECT_EQ(outEvent.action, action);
635    HandleUevent(&outEvent);
636}
637
638HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_ActionAdd001, TestSize.Level1)
639{
640    TestUeventAction(ACTION_ADD);
641    TestUeventAction(ACTION_REMOVE);
642    TestUeventAction(ACTION_CHANGE);
643    TestUeventAction(ACTION_MOVE);
644    TestUeventAction(ACTION_ONLINE);
645
646    TestUeventAction(ACTION_OFFLINE);
647    TestUeventAction(ACTION_BIND);
648    TestUeventAction(ACTION_UNBIND);
649    TestUeventAction(ACTION_UNKNOWN);
650}
651} // UeventdUt