1 | /* |
2 | Copyright 2009 Benjamin K. Stuhl <bks24@cornell.edu> |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) version 3, or any |
8 | later version accepted by the membership of KDE e.V. (or its |
9 | successor approved by the membership of KDE e.V.), which shall |
10 | act as a proxy defined in Section 6 of version 3 of the license. |
11 | |
12 | This library is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | Lesser General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Lesser General Public |
18 | License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "udevqt.h" |
22 | #include "udevqt_p.h" |
23 | |
24 | #include <sys/stat.h> |
25 | #include <QtCore/QSocketNotifier> |
26 | |
27 | namespace UdevQt { |
28 | |
29 | ClientPrivate::ClientPrivate(Client *q_) |
30 | : udev(0), monitor(0), q(q_), monitorNotifier(0) |
31 | { |
32 | } |
33 | |
34 | ClientPrivate::~ClientPrivate() |
35 | { |
36 | udev_unref(udev); |
37 | delete monitorNotifier; |
38 | |
39 | if (monitor) |
40 | udev_monitor_unref(monitor); |
41 | } |
42 | |
43 | void ClientPrivate::init(const QStringList &subsystemList, ListenToWhat what) |
44 | { |
45 | udev = udev_new(); |
46 | |
47 | if (what != ListenToNone) { |
48 | setWatchedSubsystems(subsystemList); |
49 | } |
50 | } |
51 | |
52 | void ClientPrivate::setWatchedSubsystems(const QStringList &subsystemList) |
53 | { |
54 | // create a listener |
55 | struct udev_monitor *newM = udev_monitor_new_from_netlink(udev, "udev" ); |
56 | |
57 | if (!newM) { |
58 | qWarning("UdevQt: unable to create udev monitor connection" ); |
59 | return; |
60 | } |
61 | |
62 | // apply our filters; an empty list means listen to everything |
63 | foreach (const QString& subsysDevtype, subsystemList) { |
64 | int ix = subsysDevtype.indexOf("/" ); |
65 | |
66 | if (ix > 0) { |
67 | QByteArray subsystem = subsysDevtype.left(ix).toLatin1(); |
68 | QByteArray devType = subsysDevtype.mid(ix + 1).toLatin1(); |
69 | udev_monitor_filter_add_match_subsystem_devtype(newM, subsystem.constData(), devType.constData()); |
70 | } else { |
71 | udev_monitor_filter_add_match_subsystem_devtype(newM, subsysDevtype.toLatin1().constData(), NULL); |
72 | } |
73 | } |
74 | |
75 | // start the new monitor receiving |
76 | udev_monitor_enable_receiving(newM); |
77 | QSocketNotifier *sn = new QSocketNotifier(udev_monitor_get_fd(newM), QSocketNotifier::Read); |
78 | QObject::connect(sn, SIGNAL(activated(int)), q, SLOT(_uq_monitorReadyRead(int))); |
79 | |
80 | // kill any previous monitor |
81 | delete monitorNotifier; |
82 | if (monitor) |
83 | udev_monitor_unref(monitor); |
84 | |
85 | // and save our new one |
86 | monitor = newM; |
87 | monitorNotifier = sn; |
88 | watchedSubsystems = subsystemList; |
89 | } |
90 | |
91 | void ClientPrivate::_uq_monitorReadyRead(int fd) |
92 | { |
93 | Q_UNUSED(fd); |
94 | monitorNotifier->setEnabled(false); |
95 | struct udev_device *dev = udev_monitor_receive_device(monitor); |
96 | monitorNotifier->setEnabled(true); |
97 | |
98 | if (!dev) |
99 | return; |
100 | |
101 | Device device(new DevicePrivate(dev, false)); |
102 | |
103 | QByteArray action(udev_device_get_action(dev)); |
104 | if (action == "add" ) { |
105 | emit q->deviceAdded(device); |
106 | } else if (action == "remove" ) { |
107 | emit q->deviceRemoved(device); |
108 | } else if (action == "change" ) { |
109 | emit q->deviceChanged(device); |
110 | } else if (action == "online" ) { |
111 | emit q->deviceOnlined(device); |
112 | } else if (action == "offline" ) { |
113 | emit q->deviceOfflined(device); |
114 | } else { |
115 | qWarning("UdevQt: unhandled device action \"%s\"" , action.constData()); |
116 | } |
117 | } |
118 | |
119 | DeviceList ClientPrivate::(struct udev_enumerate *en) |
120 | { |
121 | DeviceList ret; |
122 | struct udev_list_entry *list, *entry; |
123 | |
124 | udev_enumerate_scan_devices(en); |
125 | list = udev_enumerate_get_list_entry(en); |
126 | udev_list_entry_foreach(entry, list) { |
127 | struct udev_device *ud = udev_device_new_from_syspath(udev_enumerate_get_udev(en), |
128 | udev_list_entry_get_name(entry)); |
129 | |
130 | if (!ud) |
131 | continue; |
132 | |
133 | ret << Device(new DevicePrivate(ud, false)); |
134 | } |
135 | |
136 | udev_enumerate_unref(en); |
137 | |
138 | return ret; |
139 | } |
140 | |
141 | |
142 | Client::Client(QObject *parent) |
143 | : QObject(parent) |
144 | , d(new ClientPrivate(this)) |
145 | { |
146 | d->init(QStringList(), ClientPrivate::ListenToNone); |
147 | } |
148 | |
149 | Client::Client(const QStringList& subsystemList, QObject *parent) |
150 | : QObject(parent) |
151 | , d(new ClientPrivate(this)) |
152 | { |
153 | d->init(subsystemList, ClientPrivate::ListenToList); |
154 | } |
155 | |
156 | Client::~Client() |
157 | { |
158 | delete d; |
159 | } |
160 | |
161 | QStringList Client::watchedSubsystems() const |
162 | { |
163 | // we're watching a specific list |
164 | if (!d->watchedSubsystems.isEmpty()) |
165 | return d->watchedSubsystems; |
166 | |
167 | // we're not watching anything |
168 | if (!d->monitor) |
169 | return QStringList(); |
170 | |
171 | // we're watching everything: figure out what "everything" currently is |
172 | // we don't cache it, since it may be subject to change, depending on hotplug |
173 | struct udev_enumerate *en = udev_enumerate_new(d->udev); |
174 | udev_enumerate_scan_subsystems(en); |
175 | QStringList s = listFromListEntry(udev_enumerate_get_list_entry(en)); |
176 | udev_enumerate_unref(en); |
177 | return s; |
178 | } |
179 | |
180 | void Client::setWatchedSubsystems(const QStringList &subsystemList) |
181 | { |
182 | d->setWatchedSubsystems(subsystemList); |
183 | } |
184 | |
185 | DeviceList Client::devicesByProperty(const QString &property, const QVariant &value) |
186 | { |
187 | struct udev_enumerate *en = udev_enumerate_new(d->udev); |
188 | |
189 | if (value.isValid()) { |
190 | udev_enumerate_add_match_property(en, property.toLatin1().constData(), value.toString().toLatin1().constData()); |
191 | } else { |
192 | udev_enumerate_add_match_property(en, property.toLatin1().constData(), NULL); |
193 | } |
194 | |
195 | return d->deviceListFromEnumerate(en); |
196 | } |
197 | |
198 | DeviceList Client::allDevices() |
199 | { |
200 | struct udev_enumerate *en = udev_enumerate_new(d->udev); |
201 | return d->deviceListFromEnumerate(en); |
202 | } |
203 | |
204 | DeviceList Client::devicesBySubsystem(const QString &subsystem) |
205 | { |
206 | struct udev_enumerate *en = udev_enumerate_new(d->udev); |
207 | |
208 | udev_enumerate_add_match_subsystem(en, subsystem.toLatin1().constData()); |
209 | return d->deviceListFromEnumerate(en); |
210 | } |
211 | |
212 | Device Client::deviceByDeviceFile(const QString &deviceFile) |
213 | { |
214 | struct stat sb; |
215 | |
216 | if (stat(deviceFile.toLatin1().constData(), &sb) != 0) |
217 | return Device(); |
218 | |
219 | struct udev_device *ud = 0; |
220 | |
221 | if (S_ISBLK(sb.st_mode)) |
222 | ud = udev_device_new_from_devnum(d->udev, 'b', sb.st_rdev); |
223 | else if (S_ISCHR(sb.st_mode)) |
224 | ud = udev_device_new_from_devnum(d->udev, 'c', sb.st_rdev); |
225 | |
226 | if (!ud) |
227 | return Device(); |
228 | |
229 | return Device(new DevicePrivate(ud, false)); |
230 | } |
231 | |
232 | Device Client::deviceBySysfsPath(const QString &sysfsPath) |
233 | { |
234 | struct udev_device *ud = udev_device_new_from_syspath(d->udev, sysfsPath.toLatin1().constData()); |
235 | |
236 | if (!ud) |
237 | return Device(); |
238 | |
239 | return Device(new DevicePrivate(ud, false)); |
240 | } |
241 | |
242 | Device Client::deviceBySubsystemAndName(const QString &subsystem, const QString &name) |
243 | { |
244 | struct udev_device *ud = udev_device_new_from_subsystem_sysname(d->udev, |
245 | subsystem.toLatin1().constData(), |
246 | name.toLatin1().constData()); |
247 | |
248 | if (!ud) |
249 | return Device(); |
250 | |
251 | return Device(new DevicePrivate(ud, false)); |
252 | } |
253 | |
254 | } |
255 | |
256 | #include "udevqt.moc" |
257 | |