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