TTKMusicPlayer  4.2.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  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  if (!buffer.open(QIODevice::ReadOnly))
93  return;
94  while(!buffer.atEnd()) { //buffer.canReadLine() always false?
95  parseLine(buffer.readLine().trimmed());
96  }
97  buffer.close();
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  if (buffer.open(QIODevice::ReadOnly))
119  return;
120  QByteArray line = buffer.readLine();
121  while(!line.isNull()) {
122  parseLine(line.trimmed());
123  line = buffer.readLine();
124  }
125  buffer.close();
126  }
127 }
128 #endif //CONFIG_THREAD
129 
148 {
149  struct sockaddr_nl snl;
150  const int buffersize = 16 * 1024 * 1024;
151  int retval;
152 
153  memset(&snl, 0x00, sizeof(struct sockaddr_nl));
154  snl.nl_family = AF_NETLINK;
155  snl.nl_groups = UDEV_MONITOR_KERNEL;
156 
157  netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
158  //netlink_socket = socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT); //SOCK_CLOEXEC may be not available
159  if (netlink_socket == -1) {
160  qWarning("error getting socket: %s", strerror(errno));
161  return false;
162  }
163 
164  /* set receive buffersize */
165  setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
166  retval = bind(netlink_socket, (struct sockaddr*) &snl, sizeof(struct sockaddr_nl));
167  if (retval < 0) {
168  qWarning("bind failed: %s", strerror(errno));
169  close(netlink_socket);
170  netlink_socket = -1;
171  return false;
172  } else if (retval == 0) {
173  //from libudev-monitor.c
174  struct sockaddr_nl _snl;
175  socklen_t _addrlen;
176 
177  /*
178  * get the address the kernel has assigned us
179  * it is usually, but not necessarily the pid
180  */
181  _addrlen = sizeof(struct sockaddr_nl);
182  retval = getsockname(netlink_socket, (struct sockaddr *)&_snl, &_addrlen);
183  if (retval == 0)
184  snl.nl_pid = _snl.nl_pid;
185  }
186 
187 #if CONFIG_SOCKETNOTIFIER
188  socket_notifier = new QSocketNotifier(netlink_socket, QSocketNotifier::Read, this);
189  connect(socket_notifier, SIGNAL(activated(int)), SLOT(parseDeviceInfo())); //will always active
190  socket_notifier->setEnabled(false);
191 #elif CONFIG_TCPSOCKET
192  //QAbstractSocket *socket = new QAbstractSocket(QAbstractSocket::UnknownSocketType, this); //will not detect "remove", why?
193  tcp_socket = new QTcpSocket(this); //works too
194  if (!tcp_socket->setSocketDescriptor(netlink_socket, QAbstractSocket::ConnectedState)) {
195  qWarning("Failed to assign native socket to QAbstractSocket: %s", qPrintable(tcp_socket->errorString()));
196  delete tcp_socket;
197  return false;
198  }
199 #endif
200  return true;
201 }
202 
203 void QDeviceWatcherPrivate::parseLine(const QByteArray &line)
204 {
205  qDebug("%s", line.constData());
206 #define USE_REGEXP 0
207 #if USE_REGEXP
208  const QRegExp rx("(\\w+)(?:@/.*/block/.*/)(\\w+)\\W*");
209  //const QRegExp rx("(add|remove|change)@/.*/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)