Solid

udevqtclient.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Benjamin K. Stuhl <bks24@cornell.edu>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "udevqtclient.h"
8#include "udevqt_p.h"
9
10#include "devices_debug.h"
11
12#include <QSocketNotifier>
13#include <qplatformdefs.h>
14
15namespace UdevQt
16{
17ClientPrivate::ClientPrivate(Client *q_)
18 : udev(nullptr)
19 , monitor(nullptr)
20 , q(q_)
21 , monitorNotifier(nullptr)
22{
23}
24
25ClientPrivate::~ClientPrivate()
26{
27 udev_unref(udev);
28 delete monitorNotifier;
29
30 if (monitor) {
31 udev_monitor_unref(monitor);
32 }
33}
34
35void ClientPrivate::init(const QStringList &subsystemList, ListenToWhat what)
36{
37 udev = udev_new();
38
39 if (what != ListenToNone) {
40 setWatchedSubsystems(subsystemList);
41 }
42}
43
44void ClientPrivate::setWatchedSubsystems(const QStringList &subsystemList)
45{
46 // create a listener
47 struct udev_monitor *newM = udev_monitor_new_from_netlink(udev, "udev");
48
49 if (!newM) {
50 qWarning("UdevQt: unable to create udev monitor connection");
51 return;
52 }
53
54 // apply our filters; an empty list means listen to everything
55 for (const QString &subsysDevtype : subsystemList) {
56 int ix = subsysDevtype.indexOf("/");
57
58 if (ix > 0) {
59 QByteArray subsystem = subsysDevtype.left(ix).toLatin1();
60 QByteArray devType = subsysDevtype.mid(ix + 1).toLatin1();
61 udev_monitor_filter_add_match_subsystem_devtype(newM, subsystem.constData(), devType.constData());
62 } else {
63 udev_monitor_filter_add_match_subsystem_devtype(newM, subsysDevtype.toLatin1().constData(), nullptr);
64 }
65 }
66
67 // start the new monitor receiving
68 udev_monitor_enable_receiving(newM);
69 QSocketNotifier *sn = new QSocketNotifier(udev_monitor_get_fd(newM), QSocketNotifier::Read);
70 QObject::connect(sn, SIGNAL(activated(int)), q, SLOT(_uq_monitorReadyRead(int)));
71
72 // kill any previous monitor
73 delete monitorNotifier;
74 if (monitor) {
75 udev_monitor_unref(monitor);
76 }
77
78 // and save our new one
79 monitor = newM;
80 monitorNotifier = sn;
81 watchedSubsystems = subsystemList;
82}
83
84void ClientPrivate::_uq_monitorReadyRead(int fd)
85{
86 Q_UNUSED(fd);
87 monitorNotifier->setEnabled(false);
88 struct udev_device *dev = udev_monitor_receive_device(monitor);
89 monitorNotifier->setEnabled(true);
90
91 if (!dev) {
92 return;
93 }
94
95 Device device(new DevicePrivate(dev, false));
96
97 QByteArray action(udev_device_get_action(dev));
98 if (action == "add") {
99 Q_EMIT q->deviceAdded(device);
100 } else if (action == "remove") {
101 Q_EMIT q->deviceRemoved(device);
102 } else if (action == "change") {
103 Q_EMIT q->deviceChanged(device);
104 } else if (action == "online") {
105 Q_EMIT q->deviceOnlined(device);
106 } else if (action == "offline") {
107 Q_EMIT q->deviceOfflined(device);
108 } else if (action == "bind") {
109 Q_EMIT q->deviceBound(device);
110 } else if (action == "unbind") {
111 Q_EMIT q->deviceUnbound(device);
112 } else {
113 qCDebug(Solid::Frontend::DeviceManager::DEVICEMANAGER) << "UdevQt: unhandled action:" << action.constData() << "for device:" << device.sysfsPath();
114 }
115}
116
117DeviceList ClientPrivate::deviceListFromEnumerate(struct udev_enumerate *en)
118{
119 DeviceList ret;
120 struct udev_list_entry *list;
121 struct udev_list_entry *entry;
122
123 udev_enumerate_scan_devices(en);
124 list = udev_enumerate_get_list_entry(en);
125 udev_list_entry_foreach(entry, list)
126 {
127 struct udev_device *ud = udev_device_new_from_syspath(udev_enumerate_get_udev(en), udev_list_entry_get_name(entry));
128
129 if (!ud) {
130 continue;
131 }
132
133 ret << Device(new DevicePrivate(ud, false));
134 }
135
136 udev_enumerate_unref(en);
137
138 return ret;
139}
140
141Client::Client(QObject *parent)
142 : QObject(parent)
143 , d(new ClientPrivate(this))
144{
145 d->init(QStringList(), ClientPrivate::ListenToNone);
146}
147
148Client::Client(const QStringList &subsystemList, QObject *parent)
149 : QObject(parent)
150 , d(new ClientPrivate(this))
151{
152 d->init(subsystemList, ClientPrivate::ListenToList);
153}
154
155Client::~Client()
156{
157 delete d;
158}
159
160QStringList Client::watchedSubsystems() const
161{
162 // we're watching a specific list
163 if (!d->watchedSubsystems.isEmpty()) {
164 return d->watchedSubsystems;
165 }
166
167 // we're not watching anything
168 if (!d->monitor) {
169 return QStringList();
170 }
171
172 // we're watching everything: figure out what "everything" currently is
173 // we don't cache it, since it may be subject to change, depending on hotplug
174 struct udev_enumerate *en = udev_enumerate_new(d->udev);
175 udev_enumerate_scan_subsystems(en);
176 QStringList s = listFromListEntry(udev_enumerate_get_list_entry(en));
177 udev_enumerate_unref(en);
178 return s;
179}
180
181void Client::setWatchedSubsystems(const QStringList &subsystemList)
182{
183 d->setWatchedSubsystems(subsystemList);
184}
185
186DeviceList Client::devicesByProperty(const QString &property, const QVariant &value)
187{
188 struct udev_enumerate *en = udev_enumerate_new(d->udev);
189
190 if (value.isValid()) {
191 udev_enumerate_add_match_property(en, property.toLatin1().constData(), value.toString().toLatin1().constData());
192 } else {
193 udev_enumerate_add_match_property(en, property.toLatin1().constData(), nullptr);
194 }
195
196 return d->deviceListFromEnumerate(en);
197}
198
199DeviceList Client::allDevices()
200{
201 struct udev_enumerate *en = udev_enumerate_new(d->udev);
202 return d->deviceListFromEnumerate(en);
203}
204
205DeviceList Client::devicesBySubsystem(const QString &subsystem)
206{
207 struct udev_enumerate *en = udev_enumerate_new(d->udev);
208
209 udev_enumerate_add_match_subsystem(en, subsystem.toLatin1().constData());
210 return d->deviceListFromEnumerate(en);
211}
212
213DeviceList Client::devicesBySubsystemsAndProperties(const QStringList &subsystems, const QVariantMap &properties)
214{
215 struct udev_enumerate *en = udev_enumerate_new(d->udev);
216
217 for (const QString &subsystem : subsystems) {
218 udev_enumerate_add_match_subsystem(en, subsystem.toLatin1().constData());
219 }
220
221 for (auto it = properties.begin(), end = properties.end(); it != end; ++it) {
222 if (it.value().isValid()) {
223 udev_enumerate_add_match_property(en, it.key().toLatin1().constData(), it.value().toString().toLatin1().constData());
224 } else {
225 udev_enumerate_add_match_property(en, it.key().toLatin1().constData(), nullptr);
226 }
227 }
228
229 return d->deviceListFromEnumerate(en);
230}
231
232Device Client::deviceByDeviceFile(const QString &deviceFile)
233{
234 QT_STATBUF sb;
235
236 if (QT_STAT(deviceFile.toLatin1().constData(), &sb) != 0) {
237 return Device();
238 }
239
240 struct udev_device *ud = nullptr;
241
242 if (S_ISBLK(sb.st_mode)) {
243 ud = udev_device_new_from_devnum(d->udev, 'b', sb.st_rdev);
244 } else if (S_ISCHR(sb.st_mode)) {
245 ud = udev_device_new_from_devnum(d->udev, 'c', sb.st_rdev);
246 }
247
248 if (!ud) {
249 return Device();
250 }
251
252 return Device(new DevicePrivate(ud, false));
253}
254
255Device Client::deviceBySysfsPath(const QString &sysfsPath)
256{
257 struct udev_device *ud = udev_device_new_from_syspath(d->udev, sysfsPath.toLatin1().constData());
258
259 if (!ud) {
260 return Device();
261 }
262
263 return Device(new DevicePrivate(ud, false));
264}
265
266Device Client::deviceBySubsystemAndName(const QString &subsystem, const QString &name)
267{
268 struct udev_device *ud = udev_device_new_from_subsystem_sysname(d->udev, //
269 subsystem.toLatin1().constData(),
271
272 if (!ud) {
273 return Device();
274 }
275
276 return Device(new DevicePrivate(ud, false));
277}
278
279}
280
281#include "moc_udevqtclient.cpp"
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem properties()
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
const char * constData() const const
QByteArray left(qsizetype len) const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QByteArray toLatin1() const const
bool isValid() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:47:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.