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