1/*
2 * Copyright (c) 2023-2024 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 <algorithm>
17#include <cerrno>
18#include <climits>
19#include <cstring>
20#include <fstream>
21#include <iostream>
22#include <iterator>
23#include <optional>
24#include <sstream>
25#include <string>
26#include <unordered_map>
27#include <vector>
28
29#include <unistd.h>
30
31#include <libudev.h>
32#include <linux/input.h>
33
34#include "mmi_log.h"
35
36#undef MMI_LOG_DOMAIN
37#define MMI_LOG_DOMAIN MMI_LOG_SERVER
38#undef MMI_LOG_TAG
39#define MMI_LOG_TAG "MmiLibudev"
40
41using namespace std::literals;
42namespace {
43constexpr int UTIL_PATH_SIZE { 1024 };
44constexpr int UTIL_LINE_SIZE { 16384 };
45
46bool StartsWith(std::string_view str, std::string_view prefix)
47{
48    return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix;
49}
50
51bool ChopTail(std::string_view &str, char sep)
52{
53    auto pos = str.rfind(sep);
54    if (pos == std::string_view::npos) {
55        return false;
56    }
57    str.remove_suffix(str.size() - pos);
58    return true;
59}
60
61std::string ResolveSymLink(const std::string &syspath)
62{
63    constexpr auto backStr = "../"sv;
64    char linkTarget[UTIL_PATH_SIZE];
65
66    ssize_t len = readlink(syspath.c_str(), linkTarget, sizeof(linkTarget));
67    if (len <= 0 || len == static_cast<ssize_t>(sizeof(linkTarget))) {
68        return syspath;
69    }
70
71    std::string_view tail{ linkTarget, len };
72    int32_t back = 0;
73    for (; StartsWith(tail, backStr); back++) {
74        tail.remove_prefix(backStr.size());
75    }
76
77    std::string_view base = syspath;
78    for (int32_t i = 0; i <= back; i++) {
79        if (!ChopTail(base, '/')) {
80            return syspath;
81        }
82    }
83
84    return std::string{ base }.append("/").append(tail);
85}
86
87std::optional<std::string> GetLinkValue(const std::string &slink, const std::string &syspath)
88{
89    auto path = syspath + "/" + slink;
90
91    char target[UTIL_PATH_SIZE];
92    ssize_t len = readlink(path.c_str(), target, sizeof(target));
93    if (len <= 0 || len == static_cast<ssize_t>(sizeof(target))) {
94        MMI_HILOGE("Failed to read link");
95        return std::nullopt;
96    }
97
98    std::string_view result{ target, len };
99    auto pos = result.rfind('/');
100    if (pos == std::string_view::npos) {
101        MMI_HILOGE("Failed to get link value");
102        return std::nullopt;
103    }
104    return std::string{ result.substr(pos + 1) };
105}
106
107class BitVector {
108public:
109    // This type depends on kernel definition
110    using val_t = unsigned long;
111
112    // Input string is hexadecimal 64-bit numbers separated by spaces with high bit number first
113    explicit BitVector(const std::string &str)
114    {
115        std::istringstream ss{ str };
116        ss >> std::hex;
117        std::copy(std::istream_iterator<val_t>(ss), std::istream_iterator<val_t>(), std::back_inserter(bits_));
118        // Since numbers in string starts with high number we need to reverse vector to count numbers from low to high
119        std::reverse(bits_.begin(), bits_.end());
120    }
121
122    [[nodiscard]] bool CheckBit(size_t idx) const
123    {
124        auto vidx = idx / (sizeof(val_t) * CHAR_BIT);
125        auto bidx = idx % (sizeof(val_t) * CHAR_BIT);
126        if (vidx >= bits_.size()) {
127            return false;
128        }
129        return (bits_[vidx] & (1ULL << bidx)) != 0;
130    }
131
132private:
133    std::vector<val_t> bits_;
134};
135} // namespace
136
137struct udev {};
138
139struct udev_device {
140public:
141    // Not copyable and not movable
142    udev_device(udev_device &) = delete;
143    udev_device(udev_device &&) = delete;
144    udev_device &operator = (udev_device &) = delete;
145    udev_device &operator = (udev_device &&) = delete;
146
147    static udev_device *NewFromSyspath(const std::string &syspathParam)
148    {
149        // path starts in sys
150        if (!StartsWith(syspathParam, "/sys/") || syspathParam.back() == '/') {
151            errno = EINVAL;
152            return nullptr;
153        }
154
155        // resolve possible symlink to real path
156        std::string path = ResolveSymLink(syspathParam);
157        if (StartsWith(path, "/sys/devices/")) {
158            // all "devices" require a "uevent" file
159            struct stat statbuf;
160            std::string filename = path + "/uevent";
161            if (stat(filename.c_str(), &statbuf) != 0) {
162                return nullptr;
163            }
164        } else {
165            return nullptr;
166        }
167
168        auto *inst = new udev_device;
169        inst->SetSyspath(std::move(path));
170
171        return inst;
172    }
173
174    static udev_device *NewFromDevnum(char type, dev_t devnum)
175    {
176        const char *typeStr = nullptr;
177
178        if (type == 'b') {
179            typeStr = "block";
180        } else if (type == 'c') {
181            typeStr = "char";
182        } else {
183            MMI_HILOGE("Param invalid");
184            errno = EINVAL;
185            return nullptr;
186        }
187
188        // use /sys/dev/{block,char}/<maj>:<min> link
189        auto majStr = std::to_string(major(devnum));
190        auto minStr = std::to_string(minor(devnum));
191        return NewFromSyspath("/sys/dev/"s + typeStr + "/" + majStr + ":" + minStr);
192    }
193
194    void Ref()
195    {
196        refcount++;
197    }
198
199    void Unref()
200    {
201        if (--refcount <= 0) {
202            delete this;
203        }
204    }
205
206    udev_device *GetParent()
207    {
208        if (!parentDevice_.has_value()) {
209            parentDevice_ = NewFromChild(this);
210        }
211        return *parentDevice_;
212    }
213
214    const std::string &GetSyspath() const
215    {
216        return syspath;
217    }
218
219    const std::string &GetSysname() const
220    {
221        return sysname;
222    }
223
224    const std::string &GetDevnode()
225    {
226        return GetProperty("DEVNAME");
227    }
228
229    bool IsInitialized()
230    {
231        if (!ueventLoaded) {
232            ReadUeventFile();
233        }
234        return ueventLoaded;
235    }
236
237    udev_device *GetParentWithSubsystem(const std::string &subsystem)
238    {
239        udev_device *parent = GetParent();
240        while (parent != nullptr) {
241            auto parentSubsystem = parent->GetSubsystem();
242            if (parentSubsystem.has_value() && parentSubsystem.value() == subsystem) {
243                break;
244            }
245            parent = parent->GetParent();
246        }
247
248        if (parent == nullptr) {
249            errno = ENOENT;
250        }
251        return parent;
252    }
253
254    bool HasProperty(const std::string &key)
255    {
256        if (!ueventLoaded) {
257            ReadUeventFile();
258        }
259        return property_.find(key) != property_.end();
260    }
261
262    const std::string &GetProperty(const std::string &key)
263    {
264        if (!ueventLoaded) {
265            ReadUeventFile();
266        }
267        return property_[key];
268    }
269
270private:
271    udev_device() = default;
272
273    ~udev_device()
274    {
275        if (parentDevice_.has_value() && parentDevice_.value() != nullptr) {
276            parentDevice_.value()->Unref();
277        }
278    }
279
280    static udev_device *NewFromChild(udev_device *child)
281    {
282        std::string_view path = child->GetSyspath();
283
284        while (true) {
285            if (!ChopTail(path, '/')) {
286                break;
287            }
288            udev_device *parent = NewFromSyspath(std::string{ path });
289            if (parent != nullptr) {
290                return parent;
291            }
292        }
293
294        return nullptr;
295    }
296
297    void SetSyspath(std::string newSyspath)
298    {
299        syspath = std::move(newSyspath);
300
301        AddProperty("DEVPATH", syspath.substr(0, "/sys"sv.size()));
302
303        auto pos = syspath.rfind('/');
304        if (pos == std::string::npos) {
305            return;
306        }
307        sysname = syspath.substr(pos + 1);
308
309        // some devices have '!' in their name, change that to '/'
310        for (char &c : sysname) {
311            if (c == '!') {
312                c = '/';
313            }
314        }
315    }
316
317    void AddPropertyFromString(const std::string &line)
318    {
319        auto pos = line.find('=');
320        if (pos == std::string::npos) {
321            return;
322        }
323        std::string key = line.substr(0, pos);
324        if (key == "DEVNAME") {
325            SetDevnode(line.substr(pos + 1));
326            return;
327        }
328        AddProperty(std::move(key), line.substr(pos + 1));
329    }
330
331    void ReadUeventFile()
332    {
333        if (ueventLoaded) {
334            return;
335        }
336
337        auto filename = syspath + "/uevent";
338        char realPath[PATH_MAX] = {};
339        CHKPV(realpath(filename.c_str(), realPath));
340        std::ifstream f(realPath, std::ios_base::in);
341        if (!f.is_open()) {
342            MMI_HILOGE("ReadUeventFile(): path:%{private}s, error:%{public}s", realPath, std::strerror(errno));
343            return;
344        }
345        ueventLoaded = true;
346
347        char line[UTIL_LINE_SIZE];
348        while (f.getline(line, sizeof(line))) {
349            AddPropertyFromString(line);
350        }
351
352        CheckInputProperties();
353    }
354
355    bool CheckAccel(const BitVector &ev, const BitVector &abs, const BitVector &prop)
356    {
357        bool hasKeys = ev.CheckBit(EV_KEY);
358        bool has3dCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y) && abs.CheckBit(ABS_Z);
359        bool isAccelerometer = prop.CheckBit(INPUT_PROP_ACCELEROMETER);
360
361        if (!hasKeys && has3dCoordinates) {
362            isAccelerometer = true;
363        }
364
365        if (isAccelerometer) {
366            SetInputProperty("ID_INPUT_ACCELEROMETER");
367        }
368        return isAccelerometer;
369    }
370
371    bool HasJoystickAxesOrButtons(const BitVector &abs, const BitVector &key)
372    {
373        bool hasJoystickAxesOrButtons = false;
374        // Some mouses have so much buttons that they overflow in joystick range, ignore them
375        if (!key.CheckBit(BTN_JOYSTICK - 1)) {
376            for (int32_t button = BTN_JOYSTICK; button < BTN_DIGI && !hasJoystickAxesOrButtons; button++) {
377                hasJoystickAxesOrButtons = key.CheckBit(button);
378            }
379            for (int32_t button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !hasJoystickAxesOrButtons;
380                button++) {
381                hasJoystickAxesOrButtons = key.CheckBit(button);
382            }
383            for (int32_t button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !hasJoystickAxesOrButtons; button++) {
384                hasJoystickAxesOrButtons = key.CheckBit(button);
385            }
386        }
387        for (int32_t axis = ABS_RX; axis < ABS_PRESSURE && !hasJoystickAxesOrButtons; axis++) {
388            hasJoystickAxesOrButtons = abs.CheckBit(axis);
389        }
390        return hasJoystickAxesOrButtons;
391    }
392
393    bool CheckPointingStick(const BitVector &prop)
394    {
395        if (prop.CheckBit(INPUT_PROP_POINTING_STICK)) {
396            SetInputProperty("ID_INPUT_POINTINGSTICK");
397            return true;
398        }
399        return false;
400    }
401
402    void CheckAndSetProp(std::string prop, const bool &flag)
403    {
404        if (flag) {
405            SetInputProperty(prop);
406            MMI_HILOGD("device has prop with %{public}s", prop.c_str());
407        }
408    }
409
410    void CheckMouseButton(const BitVector &key, bool &flag)
411    {
412        for (int32_t button = BTN_MOUSE; button < BTN_JOYSTICK && !flag; button++) {
413            flag = key.CheckBit(button);
414        }
415    }
416
417    void UpdateProByKey(const BitVector &key, const bool &isDirect, bool &probablyTablet, bool &probablyTouchpad,
418        bool &probablyTouchscreen)
419    {
420        probablyTablet = key.CheckBit(BTN_STYLUS) || key.CheckBit(BTN_TOOL_PEN);
421        probablyTouchpad = key.CheckBit(BTN_TOOL_FINGER) && !key.CheckBit(BTN_TOOL_PEN) && !isDirect;
422        probablyTouchscreen = key.CheckBit(BTN_TOUCH) && isDirect;
423    }
424
425    bool CheckMtCoordinates(const BitVector &abs)
426    {
427        bool hasMtCoordinates = abs.CheckBit(ABS_MT_POSITION_X) && abs.CheckBit(ABS_MT_POSITION_Y);
428        /* unset hasMtCoordinates if devices claims to have all abs axis */
429        if (hasMtCoordinates && abs.CheckBit(ABS_MT_SLOT) && abs.CheckBit(ABS_MT_SLOT - 1)) {
430            hasMtCoordinates = false;
431        }
432        return hasMtCoordinates;
433    }
434
435    void UpdateProByStatus(const bool &isMouse, const bool &isTouchpad, const bool &isTouchscreen,
436        const bool &isJoystick, const bool &isTablet)
437    {
438        CheckAndSetProp("ID_INPUT_MOUSE", isMouse);
439        CheckAndSetProp("ID_INPUT_TOUCHPAD", isTouchpad);
440        CheckAndSetProp("ID_INPUT_TOUCHSCREEN", isTouchscreen);
441        CheckAndSetProp("ID_INPUT_JOYSTICK", isJoystick);
442        CheckAndSetProp("ID_INPUT_TABLET", isTablet);
443    }
444
445    bool CheckPointers(const BitVector &ev, const BitVector &abs, const BitVector &key, const BitVector &rel,
446        const BitVector &prop)
447    {
448        bool isDirect = prop.CheckBit(INPUT_PROP_DIRECT);
449        bool hasAbsCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y);
450        bool hasRelCoordinates = ev.CheckBit(EV_REL) && rel.CheckBit(REL_X) && rel.CheckBit(REL_Y);
451        bool hasMtCoordinates = CheckMtCoordinates(abs);
452
453        bool hasMouseButton = false;
454        CheckMouseButton(key, hasMouseButton);
455
456        bool probablyTablet;
457        bool probablyTouchpad;
458        bool probablyTouchscreen;
459        UpdateProByKey(key, isDirect, probablyTablet, probablyTouchpad, probablyTouchscreen);
460        bool probablyJoystick = HasJoystickAxesOrButtons(abs, key);
461
462        bool isTablet = false;
463        bool isMouse = false;
464        bool isTouchpad = false;
465        bool isTouchscreen = false;
466        bool isJoystick = false;
467        if (hasAbsCoordinates) {
468            if (probablyTablet) {
469                isTablet = true;
470            } else if (probablyTouchpad) {
471                isTouchpad = true;
472            } else if (hasMouseButton) {
473                /* This path is taken by VMware's USB mouse, which has
474                 * absolute axes, but no touch/pressure button. */
475                isMouse = true;
476            } else if (probablyTouchscreen) {
477                isTouchscreen = true;
478            } else {
479                isJoystick = probablyJoystick;
480            }
481        } else {
482            isJoystick = probablyJoystick;
483        }
484
485        if (hasMtCoordinates) {
486            if (probablyTablet) {
487                isTablet = true;
488            } else if (probablyTouchpad) {
489                isTouchpad = true;
490            } else if (probablyTouchscreen) {
491                isTouchscreen = true;
492            }
493        }
494
495        /* mouse buttons and no axis */
496        if (!isTablet && !isTouchpad && !isJoystick && hasMouseButton && (hasRelCoordinates || !hasAbsCoordinates)) {
497            isMouse = true;
498        }
499
500        UpdateProByStatus(isMouse, isTouchpad, isTouchscreen, isJoystick, isTablet);
501
502        return isTablet || isMouse || isTouchpad || isTouchscreen || isJoystick || CheckPointingStick(prop);
503    }
504
505    bool CheckKeys(const BitVector &ev, const BitVector &key)
506    {
507        if (!ev.CheckBit(EV_KEY)) {
508            return false;
509        }
510
511        /* only consider KEY_* here, not BTN_* */
512        bool found = false;
513        for (int32_t i = 0; i < BTN_MISC && !found; ++i) {
514            found = key.CheckBit(i);
515        }
516        /* If there are no keys in the lower block, check the higher blocks */
517        for (int32_t i = KEY_OK; i < BTN_DPAD_UP && !found; ++i) {
518            found = key.CheckBit(i);
519        }
520        for (int32_t i = KEY_ALS_TOGGLE; i < BTN_TRIGGER_HAPPY && !found; ++i) {
521            found = key.CheckBit(i);
522        }
523
524        if (found) {
525            SetInputProperty("ID_INPUT_KEY");
526        }
527
528        /* the first 32 bits are ESC, numbers, and Q to D; if we have all of
529         * those, consider it a full keyboard; do not test KEY_RESERVED, though */
530        bool isKeyboard = true;
531        for (int32_t i = KEY_ESC; i < KEY_D && isKeyboard; i++) {
532            isKeyboard = key.CheckBit(i);
533        }
534        if (isKeyboard) {
535            SetInputProperty("ID_INPUT_KEYBOARD");
536        }
537
538        return found || isKeyboard;
539    }
540
541    void SetInputProperty(std::string prop)
542    {
543        AddProperty("ID_INPUT", "1");
544        AddProperty(std::move(prop), "1");
545    }
546
547    void CheckInputProperties()
548    {
549        BitVector ev{ GetProperty("EV") };
550        BitVector abs{ GetProperty("ABS") };
551        BitVector key{ GetProperty("KEY") };
552        BitVector rel{ GetProperty("REL") };
553        BitVector prop{ GetProperty("PROP") };
554
555        bool isPointer = CheckAccel(ev, abs, prop) || CheckPointers(ev, abs, key, rel, prop);
556        bool isKey = CheckKeys(ev, key);
557        /* Some evdev nodes have only a scrollwheel */
558        if (!isPointer && !isKey && ev.CheckBit(EV_REL) && (rel.CheckBit(REL_WHEEL) || rel.CheckBit(REL_HWHEEL))) {
559            SetInputProperty("ID_INPUT_KEY");
560        }
561        if (ev.CheckBit(EV_SW)) {
562            SetInputProperty("ID_INPUT_SWITCH");
563        }
564    }
565
566    void SetDevnode(std::string newDevnode)
567    {
568        if (newDevnode[0] != '/') {
569            newDevnode = "/dev/" + newDevnode;
570        }
571        AddProperty("DEVNAME", std::move(newDevnode));
572    }
573
574    void AddProperty(std::string key, std::string value)
575    {
576        property_[std::move(key)] = std::move(value);
577    }
578
579    std::optional<std::string> GetSubsystem()
580    {
581        if (!subsystem_.has_value()) {
582            auto res = GetLinkValue("subsystem", syspath);
583            // read "subsystem" link
584            if (res.has_value()) {
585                SetSubsystem(std::move(*res));
586                return subsystem_;
587            }
588            subsystem_ = "";
589        }
590        return subsystem_;
591    }
592
593    void SetSubsystem(std::string newSubsystem)
594    {
595        subsystem_ = newSubsystem;
596        AddProperty("SUBSYSTEM", std::move(newSubsystem));
597    }
598
599private:
600    int refcount = 1;
601    std::string syspath;
602    std::string sysname;
603
604    std::optional<udev_device *> parentDevice_;
605    std::optional<std::string> subsystem_;
606
607    bool ueventLoaded = false;
608    std::unordered_map<std::string, std::string> property_;
609};
610
611// C-style interface
612
613udev *udev_new(void)
614{
615    static udev instance{};
616    return &instance;
617}
618
619udev *udev_unref([[maybe_unused]] udev *udev)
620{
621    return nullptr;
622}
623
624udev_device *udev_device_ref(udev_device *device)
625{
626    CHKPP(device);
627    device->Ref();
628    return device;
629}
630
631udev_device *udev_device_unref(udev_device *device)
632{
633    CHKPP(device);
634    device->Unref();
635    return nullptr;
636}
637
638udev *udev_device_get_udev(udev_device *device)
639{
640    CHKPP(device);
641    return udev_new();
642}
643
644udev_device *udev_device_new_from_syspath(udev *udev, const char *syspath)
645{
646    if (udev == nullptr || syspath == nullptr) {
647        errno = EINVAL;
648        return nullptr;
649    }
650    return udev_device::NewFromSyspath(syspath);
651}
652
653udev_device *udev_device_new_from_devnum(udev *udev, char type, dev_t devnum)
654{
655    if (udev == nullptr) {
656        errno = EINVAL;
657        return nullptr;
658    }
659    return udev_device::NewFromDevnum(type, devnum);
660}
661
662udev_device *udev_device_get_parent(udev_device *device)
663{
664    if (device == nullptr) {
665        errno = EINVAL;
666        return nullptr;
667    }
668    return device->GetParent();
669}
670
671udev_device *udev_device_get_parent_with_subsystem_devtype(udev_device *device, const char *subsystem,
672    const char *devtype)
673{
674    CHKPP(device);
675    if (subsystem == nullptr) {
676        errno = EINVAL;
677        return nullptr;
678    }
679    // Searching with specific devtype is not supported, since not used by libinput
680    CHKPP(devtype);
681    return device->GetParentWithSubsystem(subsystem);
682}
683
684const char *udev_device_get_syspath(udev_device *device)
685{
686    CHKPP(device);
687    return device->GetSyspath().c_str();
688}
689
690const char *udev_device_get_sysname(udev_device *device)
691{
692    CHKPP(device);
693    return device->GetSysname().c_str();
694}
695
696const char *udev_device_get_devnode(udev_device *device)
697{
698    CHKPP(device);
699    return device->GetDevnode().c_str();
700}
701
702int udev_device_get_is_initialized(udev_device *device)
703{
704    return (device != nullptr) ? static_cast<int>(device->IsInitialized()) : -1;
705}
706
707const char *udev_device_get_property_value(udev_device *device, const char *key)
708{
709    CHKPP(device);
710    CHKPP(key);
711    std::string skey{ key };
712    if (!device->HasProperty(key)) {
713        return nullptr;
714    }
715    return device->GetProperty(key).c_str();
716}
717