TTKMusicPlayer  4.3.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
qdevicewatcher_linux.cpp
Go to the documentation of this file.
1 #include "qdevicewatcher.h"
2 #include "qdevicewatcher_p.h"
3 
4 #ifdef Q_OS_LINUX
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 
10 #include <sys/un.h>
11 #include <sys/ioctl.h>
12 #include <sys/socket.h>
13 #include <linux/types.h>
14 #include <linux/netlink.h>
15 #include <errno.h>
16 #include <unistd.h>
17 
18 #if USE_REGEXP
19 # include <QRegExp>
20 #endif
21 #include <QCoreApplication>
22 #if CONFIG_SOCKETNOTIFIER
23 # include <QSocketNotifier>
24 #elif CONFIG_TCPSOCKET
25 # include <QtNetwork/QTcpSocket>
26 #endif
27 
28 
29 #define UEVENT_BUFFER_SIZE 2048
30 
31 enum udev_monitor_netlink_group {
32  UDEV_MONITOR_NONE,
33  UDEV_MONITOR_KERNEL,
34  UDEV_MONITOR_UDEV
35 };
36 
38 {
39  stop();
40  close(netlink_socket);
41  netlink_socket = -1;
42 }
43 
45 {
46  if (!initialize())
47  return false;
48 #if CONFIG_SOCKETNOTIFIER
49  socket_notifier->setEnabled(true);
50 #elif CONFIG_TCPSOCKET
51  connect(tcp_socket, SIGNAL(readyRead()), SLOT(parseDeviceInfo()));
52 #else
53  QThread::start();
54 #endif
55  return true;
56 }
57 
59 {
60  if (netlink_socket!=-1) {
61 #if CONFIG_SOCKETNOTIFIER
62  socket_notifier->setEnabled(false);
63 #elif CONFIG_TCPSOCKET
64  //tcp_socket->close(); //how to restart?
65  disconnect(this, SLOT(parseDeviceInfo()));
66 #else
67  this->quit();
68 #endif
69  close(netlink_socket);
70  netlink_socket = -1;
71  }
72  return true;
73 }
74 
75 
77 {
78  QByteArray data;
79 #if CONFIG_SOCKETNOTIFIER
80  //socket_notifier->setEnabled(false); //for win
81  data.resize(UEVENT_BUFFER_SIZE * 2);
82  data.fill(0);
83  size_t len = read(socket_notifier->socket(), data.data(), UEVENT_BUFFER_SIZE * 2);
84  qDebug("read fro socket %ld bytes", len);
85  data.resize(len);
86  //socket_notifier->setEnabled(true); //for win
87 #elif CONFIG_TCPSOCKET
88  data = tcp_socket->readAll();
89 #endif
90  data = data.replace(0, '\n').trimmed(); //In the original line each information is seperated by 0
91  if (buffer.isOpen())
92  buffer.close();
93  buffer.setBuffer(&data);
94  if (!buffer.open(QIODevice::ReadOnly))
95  return;
96  while(!buffer.atEnd()) { //buffer.canReadLine() always false?
97  parseLine(buffer.readLine().trimmed());
98  }
99  buffer.close();
100 }
101 
102 #if CONFIG_THREAD
103 //another thread
104 void QDeviceWatcherPrivate::run()
105 {
106  QByteArray data;
107  //loop only when event happens. because of recv() block the function?
108  while (1) {
109  //char buf[UEVENT_BUFFER_SIZE * 2] = {0};
110  //recv(d->netlink_socket, &buf, sizeof(buf), 0);
111  data.resize(UEVENT_BUFFER_SIZE * 2);
112  data.fill(0);
113  size_t len = recv(netlink_socket, data.data(), data.length(), 0);
114  qDebug("read fro socket %ld bytes", len);
115  data.resize(len);
116  data = data.replace(0, '\n').trimmed();
117  if (buffer.isOpen())
118  buffer.close();
119  buffer.setBuffer(&data);
120  if (!buffer.open(QIODevice::ReadOnly))
121  return;
122  QByteArray line = buffer.readLine();
123  while(!line.isNull()) {
124  parseLine(line.trimmed());
125  line = buffer.readLine();
126  }
127  buffer.close();
128  }
129 }
130 #endif //CONFIG_THREAD
131 
150 {
151  struct sockaddr_nl snl;
152  const int buffersize = 16 * 1024 * 1024;
153  int retval;
154 
155  memset(&snl, 0x00, sizeof(struct sockaddr_nl));
156  snl.nl_family = AF_NETLINK;
157  snl.nl_groups = UDEV_MONITOR_KERNEL;
158 
159  netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
160  //netlink_socket = socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); //SOCK_CLOEXEC may be not available
161  if (netlink_socket == -1) {
162  qWarning("error getting socket: %s", strerror(errno));
163  return false;
164  }
165 
166  /* set receive buffersize */
167  setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
168  retval = bind(netlink_socket, (struct sockaddr*) &snl, sizeof(struct sockaddr_nl));
169  if (retval < 0) {
170  qWarning("bind failed: %s", strerror(errno));
171  close(netlink_socket);
172  netlink_socket = -1;
173  return false;
174  } else if (retval == 0) {
175  //from libudev-monitor.c
176  struct sockaddr_nl _snl;
177  socklen_t _addrlen;
178 
179  /*
180  * get the address the kernel has assigned us
181  * it is usually, but not necessarily the pid
182  */
183  _addrlen = sizeof(struct sockaddr_nl);
184  retval = getsockname(netlink_socket, (struct sockaddr *)&_snl, &_addrlen);
185  if (retval == 0)
186  snl.nl_pid = _snl.nl_pid;
187  }
188 
189 #if CONFIG_SOCKETNOTIFIER
190  socket_notifier = new QSocketNotifier(netlink_socket, QSocketNotifier::Read, this);
191  connect(socket_notifier, SIGNAL(activated(int)), SLOT(parseDeviceInfo())); //will always active
192  socket_notifier->setEnabled(false);
193 #elif CONFIG_TCPSOCKET
194  //QAbstractSocket *socket = new QAbstractSocket(QAbstractSocket::UnknownSocketType, this); //will not detect "remove", why?
195  tcp_socket = new QTcpSocket(this); //works too
196  if (!tcp_socket->setSocketDescriptor(netlink_socket, QAbstractSocket::ConnectedState)) {
197  qWarning("Failed to assign native socket to QAbstractSocket: %s", qPrintable(tcp_socket->errorString()));
198  delete tcp_socket;
199  return false;
200  }
201 #endif
202  return true;
203 }
204 
205 void QDeviceWatcherPrivate::parseLine(const QByteArray &line)
206 {
207  qDebug("%s", line.constData());
208 #if USE_REGEXP
209  const QRegExp rx("(\\w+)(?:@/.*/block/.*/)(\\w+)\\W*");
210  if (rx.indexIn(line) == -1)
211  return;
212  QString action_str = rx.cap(1).toLower();
213  QString dev = "/dev/" + rx.cap(2);
214 #else
215  if (!line.contains("/block/")) //hotplug
216  return;
217  QString action_str = line.left(line.indexOf('@')).toLower();
218  QString dev = "/dev/" + line.right(line.length() - line.lastIndexOf('/') - 1);
219 #endif //USE_REGEXP
220  QDeviceChangeEvent *event = 0;
221 
222  if (action_str==QLatin1String("add")) {
223  emitDeviceAdded(dev);
225  } else if (action_str==QLatin1String("remove")) {
226  emitDeviceRemoved(dev);
228  } else if (action_str==QLatin1String("change")) {
229  emitDeviceChanged(dev);
231  }
232 
233  qDebug("%s %s", qPrintable(action_str), qPrintable(dev));
234 
235  if (event != 0 && !event_receivers.isEmpty()) {
236  for(QObject *obj : qAsConst(event_receivers)) {
237  QCoreApplication::postEvent(obj, event, Qt::HighEventPriority);
238  }
239  }
240 }
241 
242 #endif //Q_OS_LINUX
void emitDeviceAdded(const QString &dev)
#define qPrintable(s)
Definition: ttkqtglobal.h:35
#define qAsConst
Definition: ttkqtglobal.h:57
void emitDeviceChanged(const QString &dev)
The class of the device watcher event.
void emitDeviceRemoved(const QString &dev)