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"