1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /*
8  * This file defines a wrapper around libudev so we can avoid
9  * linking directly to it and use dlopen instead.
10  */
11 
12 #ifndef HAL_LINUX_UDEV_H_
13 #define HAL_LINUX_UDEV_H_
14 
15 #include <dlfcn.h>
16 
17 #include "mozilla/ArrayUtils.h"
18 
19 namespace mozilla {
20 
21 struct udev;
22 struct udev_device;
23 struct udev_enumerate;
24 struct udev_list_entry;
25 struct udev_monitor;
26 
27 class udev_lib {
28  public:
udev_lib()29   udev_lib() : lib(nullptr), udev(nullptr) {
30     // Be careful about ABI compat! 0 -> 1 didn't change any
31     // symbols this code relies on, per:
32     // https://lists.fedoraproject.org/pipermail/devel/2012-June/168227.html
33     const char* lib_names[] = {"libudev.so.0", "libudev.so.1"};
34     // Check whether a library is already loaded so we don't load two
35     // conflicting libs.
36     for (unsigned i = 0; i < ArrayLength(lib_names); i++) {
37       lib = dlopen(lib_names[i], RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL);
38       if (lib) {
39         break;
40       }
41     }
42     // If nothing loads the first time through, it means no version of libudev
43     // was already loaded.
44     if (!lib) {
45       for (unsigned i = 0; i < ArrayLength(lib_names); i++) {
46         lib = dlopen(lib_names[i], RTLD_LAZY | RTLD_GLOBAL);
47         if (lib) {
48           break;
49         }
50       }
51     }
52     if (lib && LoadSymbols()) {
53       udev = udev_new();
54     }
55   }
56 
~udev_lib()57   ~udev_lib() {
58     if (udev) {
59       udev_unref(udev);
60     }
61 
62     if (lib) {
63       dlclose(lib);
64     }
65   }
66 
67   explicit operator bool() { return udev; }
68 
69  private:
70 #define DLSYM(s)                       \
71   do {                                 \
72     (s) = (decltype(s))dlsym(lib, #s); \
73     if (!(s)) return false;            \
74   } while (0)
75 
LoadSymbols()76   bool LoadSymbols() {
77     DLSYM(udev_new);
78     DLSYM(udev_unref);
79     DLSYM(udev_device_unref);
80     DLSYM(udev_device_new_from_syspath);
81     DLSYM(udev_device_get_devnode);
82     DLSYM(udev_device_get_parent_with_subsystem_devtype);
83     DLSYM(udev_device_get_property_value);
84     DLSYM(udev_device_get_action);
85     DLSYM(udev_device_get_sysattr_value);
86     DLSYM(udev_enumerate_new);
87     DLSYM(udev_enumerate_unref);
88     DLSYM(udev_enumerate_add_match_subsystem);
89     DLSYM(udev_enumerate_scan_devices);
90     DLSYM(udev_enumerate_get_list_entry);
91     DLSYM(udev_list_entry_get_next);
92     DLSYM(udev_list_entry_get_name);
93     DLSYM(udev_monitor_new_from_netlink);
94     DLSYM(udev_monitor_filter_add_match_subsystem_devtype);
95     DLSYM(udev_monitor_enable_receiving);
96     DLSYM(udev_monitor_get_fd);
97     DLSYM(udev_monitor_receive_device);
98     DLSYM(udev_monitor_unref);
99 
100     return true;
101   }
102 
103 #undef DLSYM
104 
105   void* lib;
106 
107  public:
108   struct udev* udev;
109 
110   // Function pointers returned from dlsym.
111   struct udev* (*udev_new)(void);
112   void (*udev_unref)(struct udev*);
113 
114   void (*udev_device_unref)(struct udev_device*);
115   struct udev_device* (*udev_device_new_from_syspath)(struct udev*,
116                                                       const char*);
117   const char* (*udev_device_get_devnode)(struct udev_device*);
118   struct udev_device* (*udev_device_get_parent_with_subsystem_devtype)(
119       struct udev_device*, const char*, const char*);
120   const char* (*udev_device_get_property_value)(struct udev_device*,
121                                                 const char*);
122   const char* (*udev_device_get_action)(struct udev_device*);
123   const char* (*udev_device_get_sysattr_value)(struct udev_device*,
124                                                const char*);
125 
126   struct udev_enumerate* (*udev_enumerate_new)(struct udev*);
127   void (*udev_enumerate_unref)(struct udev_enumerate*);
128   int (*udev_enumerate_add_match_subsystem)(struct udev_enumerate*,
129                                             const char*);
130   int (*udev_enumerate_scan_devices)(struct udev_enumerate*);
131   struct udev_list_entry* (*udev_enumerate_get_list_entry)(
132       struct udev_enumerate*);
133 
134   struct udev_list_entry* (*udev_list_entry_get_next)(struct udev_list_entry*);
135   const char* (*udev_list_entry_get_name)(struct udev_list_entry*);
136 
137   struct udev_monitor* (*udev_monitor_new_from_netlink)(struct udev*,
138                                                         const char*);
139   int (*udev_monitor_filter_add_match_subsystem_devtype)(struct udev_monitor*,
140                                                          const char*,
141                                                          const char*);
142   int (*udev_monitor_enable_receiving)(struct udev_monitor*);
143   int (*udev_monitor_get_fd)(struct udev_monitor*);
144   struct udev_device* (*udev_monitor_receive_device)(struct udev_monitor*);
145   void (*udev_monitor_unref)(struct udev_monitor*);
146 };
147 
148 }  // namespace mozilla
149 
150 #endif  // HAL_LINUX_UDEV_H_
151