File indexing completed on 2024-05-12 05:38:44

0001 /*
0002     SPDX-FileCopyrightText: 2009 Benjamin K. Stuhl <bks24@cornell.edu>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "udevqtclient.h"
0008 #include "udevqt_p.h"
0009 
0010 #include <powerdevil_debug.h>
0011 
0012 #include <QSocketNotifier>
0013 #include <qplatformdefs.h>
0014 
0015 namespace UdevQt
0016 {
0017 ClientPrivate::ClientPrivate(Client *q_)
0018     : udev(nullptr)
0019     , monitor(nullptr)
0020     , q(q_)
0021     , monitorNotifier(nullptr)
0022 {
0023 }
0024 
0025 ClientPrivate::~ClientPrivate()
0026 {
0027     udev_unref(udev);
0028     delete monitorNotifier;
0029 
0030     if (monitor)
0031         udev_monitor_unref(monitor);
0032 }
0033 
0034 void ClientPrivate::init(const QStringList &subsystemList, ListenToWhat what)
0035 {
0036     udev = udev_new();
0037 
0038     if (what != ListenToNone) {
0039         setWatchedSubsystems(subsystemList);
0040     }
0041 }
0042 
0043 void ClientPrivate::setWatchedSubsystems(const QStringList &subsystemList)
0044 {
0045     // create a listener
0046     struct udev_monitor *newM = udev_monitor_new_from_netlink(udev, "udev");
0047 
0048     if (!newM) {
0049         qCWarning(POWERDEVIL, "UdevQt: unable to create udev monitor connection");
0050         return;
0051     }
0052 
0053     // apply our filters; an empty list means listen to everything
0054     for (const QString &subsysDevtype : subsystemList) {
0055         int ix = subsysDevtype.indexOf(u'/');
0056 
0057         if (ix > 0) {
0058             QByteArray subsystem = QStringView(subsysDevtype).left(ix).toLatin1();
0059             QByteArray devType = QStringView(subsysDevtype).mid(ix + 1).toLatin1();
0060             udev_monitor_filter_add_match_subsystem_devtype(newM, subsystem.constData(), devType.constData());
0061         } else {
0062             udev_monitor_filter_add_match_subsystem_devtype(newM, subsysDevtype.toLatin1().constData(), nullptr);
0063         }
0064     }
0065 
0066     // start the new monitor receiving
0067     udev_monitor_enable_receiving(newM);
0068     QSocketNotifier *sn = new QSocketNotifier(udev_monitor_get_fd(newM), QSocketNotifier::Read);
0069     QObject::connect(sn, SIGNAL(activated(int)), q, SLOT(_uq_monitorReadyRead(int)));
0070 
0071     // kill any previous monitor
0072     delete monitorNotifier;
0073     if (monitor)
0074         udev_monitor_unref(monitor);
0075 
0076     // and save our new one
0077     monitor = newM;
0078     monitorNotifier = sn;
0079     watchedSubsystems = subsystemList;
0080 }
0081 
0082 void ClientPrivate::_uq_monitorReadyRead(int fd)
0083 {
0084     Q_UNUSED(fd);
0085     monitorNotifier->setEnabled(false);
0086     struct udev_device *dev = udev_monitor_receive_device(monitor);
0087     monitorNotifier->setEnabled(true);
0088 
0089     if (!dev)
0090         return;
0091 
0092     Device device(new DevicePrivate(dev, false));
0093 
0094     QByteArray action(udev_device_get_action(dev));
0095     if (action == "add") {
0096         Q_EMIT q->deviceAdded(device);
0097     } else if (action == "remove") {
0098         Q_EMIT q->deviceRemoved(device);
0099     } else if (action == "change") {
0100         Q_EMIT q->deviceChanged(device);
0101     } else if (action == "online") {
0102         Q_EMIT q->deviceOnlined(device);
0103     } else if (action == "offline") {
0104         Q_EMIT q->deviceOfflined(device);
0105     } else {
0106         qCWarning(POWERDEVIL, "UdevQt: unhandled device action \"%s\"", action.constData());
0107     }
0108 }
0109 
0110 DeviceList ClientPrivate::deviceListFromEnumerate(struct udev_enumerate *en)
0111 {
0112     DeviceList ret;
0113     struct udev_list_entry *list, *entry;
0114 
0115     udev_enumerate_scan_devices(en);
0116     list = udev_enumerate_get_list_entry(en);
0117     udev_list_entry_foreach(entry, list)
0118     {
0119         struct udev_device *ud = udev_device_new_from_syspath(udev_enumerate_get_udev(en), udev_list_entry_get_name(entry));
0120 
0121         if (!ud)
0122             continue;
0123 
0124         ret << Device(new DevicePrivate(ud, false));
0125     }
0126 
0127     udev_enumerate_unref(en);
0128 
0129     return ret;
0130 }
0131 
0132 Client::Client(QObject *parent)
0133     : QObject(parent)
0134     , d(new ClientPrivate(this))
0135 {
0136     d->init(QStringList(), ClientPrivate::ListenToNone);
0137 }
0138 
0139 Client::Client(const QStringList &subsystemList, QObject *parent)
0140     : QObject(parent)
0141     , d(new ClientPrivate(this))
0142 {
0143     d->init(subsystemList, ClientPrivate::ListenToList);
0144 }
0145 
0146 Client::~Client()
0147 {
0148     delete d;
0149 }
0150 
0151 QStringList Client::watchedSubsystems() const
0152 {
0153     // we're watching a specific list
0154     if (!d->watchedSubsystems.isEmpty())
0155         return d->watchedSubsystems;
0156 
0157     // we're not watching anything
0158     if (!d->monitor)
0159         return QStringList();
0160 
0161     // we're watching everything: figure out what "everything" currently is
0162     // we don't cache it, since it may be subject to change, depending on hotplug
0163     struct udev_enumerate *en = udev_enumerate_new(d->udev);
0164     udev_enumerate_scan_subsystems(en);
0165     QStringList s = listFromListEntry(udev_enumerate_get_list_entry(en));
0166     udev_enumerate_unref(en);
0167     return s;
0168 }
0169 
0170 void Client::setWatchedSubsystems(const QStringList &subsystemList)
0171 {
0172     d->setWatchedSubsystems(subsystemList);
0173 }
0174 
0175 DeviceList Client::devicesByProperty(const QString &property, const QVariant &value)
0176 {
0177     struct udev_enumerate *en = udev_enumerate_new(d->udev);
0178 
0179     if (value.isValid()) {
0180         udev_enumerate_add_match_property(en, property.toLatin1().constData(), value.toString().toLatin1().constData());
0181     } else {
0182         udev_enumerate_add_match_property(en, property.toLatin1().constData(), nullptr);
0183     }
0184 
0185     return d->deviceListFromEnumerate(en);
0186 }
0187 
0188 DeviceList Client::allDevices()
0189 {
0190     struct udev_enumerate *en = udev_enumerate_new(d->udev);
0191     return d->deviceListFromEnumerate(en);
0192 }
0193 
0194 DeviceList Client::devicesBySubsystem(const QString &subsystem)
0195 {
0196     struct udev_enumerate *en = udev_enumerate_new(d->udev);
0197 
0198     udev_enumerate_add_match_subsystem(en, subsystem.toLatin1().constData());
0199     return d->deviceListFromEnumerate(en);
0200 }
0201 
0202 Device Client::deviceByDeviceFile(const QString &deviceFile)
0203 {
0204     QT_STATBUF sb;
0205 
0206     if (QT_STAT(deviceFile.toLatin1().constData(), &sb) != 0)
0207         return Device();
0208 
0209     struct udev_device *ud = nullptr;
0210 
0211     if (S_ISBLK(sb.st_mode))
0212         ud = udev_device_new_from_devnum(d->udev, 'b', sb.st_rdev);
0213     else if (S_ISCHR(sb.st_mode))
0214         ud = udev_device_new_from_devnum(d->udev, 'c', sb.st_rdev);
0215 
0216     if (!ud)
0217         return Device();
0218 
0219     return Device(new DevicePrivate(ud, false));
0220 }
0221 
0222 Device Client::deviceBySysfsPath(const QString &sysfsPath)
0223 {
0224     struct udev_device *ud = udev_device_new_from_syspath(d->udev, sysfsPath.toLatin1().constData());
0225 
0226     if (!ud)
0227         return Device();
0228 
0229     return Device(new DevicePrivate(ud, false));
0230 }
0231 
0232 Device Client::deviceBySubsystemAndName(const QString &subsystem, const QString &name)
0233 {
0234     struct udev_device *ud = udev_device_new_from_subsystem_sysname(d->udev, subsystem.toLatin1().constData(), name.toLatin1().constData());
0235 
0236     if (!ud)
0237         return Device();
0238 
0239     return Device(new DevicePrivate(ud, false));
0240 }
0241 
0242 }
0243 
0244 #include "moc_udevqtclient.cpp"