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
27namespace UdevQt {
28
29ClientPrivate::ClientPrivate(Client *q_)
30 : udev(0), monitor(0), q(q_), monitorNotifier(0)
31{
32}
33
34ClientPrivate::~ClientPrivate()
35{
36 udev_unref(udev);
37 delete monitorNotifier;
38
39 if (monitor)
40 udev_monitor_unref(monitor);
41}
42
43void ClientPrivate::init(const QStringList &subsystemList, ListenToWhat what)
44{
45 udev = udev_new();
46
47 if (what != ListenToNone) {
48 setWatchedSubsystems(subsystemList);
49 }
50}
51
52void 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
91void 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
119DeviceList ClientPrivate::deviceListFromEnumerate(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
142Client::Client(QObject *parent)
143 : QObject(parent)
144 , d(new ClientPrivate(this))
145{
146 d->init(QStringList(), ClientPrivate::ListenToNone);
147}
148
149Client::Client(const QStringList& subsystemList, QObject *parent)
150 : QObject(parent)
151 , d(new ClientPrivate(this))
152{
153 d->init(subsystemList, ClientPrivate::ListenToList);
154}
155
156Client::~Client()
157{
158 delete d;
159}
160
161QStringList 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
180void Client::setWatchedSubsystems(const QStringList &subsystemList)
181{
182 d->setWatchedSubsystems(subsystemList);
183}
184
185DeviceList 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
198DeviceList Client::allDevices()
199{
200 struct udev_enumerate *en = udev_enumerate_new(d->udev);
201 return d->deviceListFromEnumerate(en);
202}
203
204DeviceList 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
212Device 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
232Device 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
242Device 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