1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qunixsocketserver_p.h"
43 
44 // #define QUNIXSOCKETSERVER_DEBUG
45 
46 #ifdef QUNIXSOCKETSERVER_DEBUG
47 #include <QDebug>
48 #endif
49 
50 #include <QtCore/qsocketnotifier.h>
51 
52 extern "C" {
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <sys/un.h>
56 #include <unistd.h>
57 #include <errno.h>
58 };
59 
60 #define UNIX_PATH_MAX 108 // From unix(7)
61 
62 QT_BEGIN_NAMESPACE
63 
64 class QUnixSocketServerPrivate : public QObject
65 {
66 Q_OBJECT
67 public:
QUnixSocketServerPrivate(QUnixSocketServer * parent)68     QUnixSocketServerPrivate(QUnixSocketServer * parent)
69     : QObject(), me(parent), fd(-1), maxConns(30),
70       error(QUnixSocketServer::NoError), acceptNotifier(0)
71     {}
72 
73     QUnixSocketServer * me;
74     int fd;
75     int maxConns;
76     QByteArray address;
77     QUnixSocketServer::ServerError error;
78     QSocketNotifier * acceptNotifier;
79 public slots:
80     void acceptActivated();
81 };
82 
83 /*!
84   \class QUnixSocketServer
85   \internal
86 
87   \brief The QUnixSocketServer class provides a Unix domain socket based server.
88   \omit
89   \ingroup Platform::DeviceSpecific
90   \ingroup Platform::OS
91   \ingroup Platform::Communications
92   \endomit
93   \ingroup qws
94 
95   This class makes it possible to accept incoming Unix domain socket
96   connections.  Call \l QUnixSocketServer::listen() to have the server listen
97   for incoming connections on a specified path.  The pure virtual
98   \l QUnixSocketServer::incomingConnection() is called each time a new
99   connection is established.  Users must inherit from QUnixSocketServer and
100   implement this method.
101 
102   If an error occurs, \l QUnixSocketServer::serverError() returns the type of
103   error.  Errors can only occur during server establishment - that is, during a
104   call to \l QUnixSocketServer::listen().  Calling \l QUnixSocketServer::close()
105   causes QUnixSocketServer to stop listening for connections and reset its
106   state.
107 
108   QUnixSocketServer is often used in conjunction with the \l QUnixSocket class.
109 
110   \sa QUnixSocket
111 */
112 
113 /*!
114   \enum QUnixSocketServer::ServerError
115 
116   The ServerError enumeration represents the errors that can occur during server
117   establishment.  The most recent error can be retrieved through a call to
118   \l QUnixSocketServer::serverError().
119 
120   \value NoError No error has occurred.
121   \value InvalidPath An invalid path endpoint was passed to
122          \l QUnixSocketServer::listen().  As defined by unix(7), invalid paths
123          include an empty path, or what more than 107 characters long.
124   \value ResourceError An error acquiring or manipulating the system's socket
125          resources occurred.  For example, if the process runs out of available
126          socket descriptors, a ResourceError will occur.
127   \value BindError The server was unable to bind to the specified path.
128   \value ListenError The server was unable to listen on the specified path for
129          incoming connections.
130   */
131 
132 /*!
133   Create a new Unix socket server with the given \a parent.
134   */
QUnixSocketServer(QObject * parent)135 QUnixSocketServer::QUnixSocketServer(QObject *parent)
136 : QObject(parent), d(0)
137 {
138 }
139 
140 /*!
141   Stops listening for incoming connection and destroys the Unix socket server.
142   */
~QUnixSocketServer()143 QUnixSocketServer::~QUnixSocketServer()
144 {
145     close();
146     if(d)
147         delete d;
148 }
149 
150 /*!
151   Stop listening for incoming connections and resets the Unix socket server's
152   state.  Calling this method while \l {QUnixSocketServer::isListening()}{not listening } for incoming connections is a no-op.
153 
154   \sa QUnixSocketServer::listen()
155   */
close()156 void QUnixSocketServer::close()
157 {
158     if(!d)
159         return;
160 
161     if(d->acceptNotifier) {
162         d->acceptNotifier->setEnabled(false);
163         delete d->acceptNotifier;
164     }
165     d->acceptNotifier = 0;
166 
167     if(-1 != d->fd) {
168 #ifdef QUNIXSOCKET_DEBUG
169         int closerv =
170 #endif
171             ::close(d->fd);
172 #ifdef QUNIXSOCKET_DEBUG
173         if(0 != closerv) {
174             qDebug() << "QUnixSocketServer: Unable to close socket ("
175                      << strerror(errno) << ')';
176         }
177 #endif
178     }
179     d->fd = -1;
180     d->address = QByteArray();
181     d->error = NoError;
182 }
183 
184 /*!
185   Returns the last server error.  Errors may only occur within a call to
186   \l QUnixSocketServer::listen(), and only when such a call fails.
187 
188   This method is not destructive, so multiple calls to
189   QUnixSocketServer::serverError() will return the same value.  The error is
190   only reset by an explicit call to \l QUnixSocketServer::close() or
191   by further calls to \l QUnixSocketServer::listen().
192   */
serverError() const193 QUnixSocketServer::ServerError QUnixSocketServer::serverError() const
194 {
195     if(!d)
196         return NoError;
197 
198     return d->error;
199 }
200 
201 /*!
202   Returns true if this server is listening for incoming connections, false
203   otherwise.
204 
205   \sa QUnixSocketServer::listen()
206   */
isListening() const207 bool QUnixSocketServer::isListening() const
208 {
209     if(!d)
210         return false;
211 
212     return (-1 != d->fd);
213 }
214 
215 /*!
216   Tells the server to listen for incoming connections on \a path.  Returns true
217   if it successfully initializes, false otherwise.  In the case of failure, the
218   \l QUnixSocketServer::serverError() error status is set accordingly.
219 
220   Calling this method while the server is already running will result in the
221   server begin reset, and then attempting to listen on \a path.  This will not
222   affect connections established prior to the server being reset, but further
223   incoming connections on the previous path will be refused.
224 
225   The server can be explicitly reset by a call to \l QUnixSocketServer::close().
226 
227   \sa QUnixSocketServer::close()
228   */
listen(const QByteArray & path)229 bool QUnixSocketServer::listen(const QByteArray & path)
230 {
231     if(d) {
232         close(); // Any existing server is destroyed
233     } else {
234         d = new QUnixSocketServerPrivate(this);
235     }
236 
237     if(path.isEmpty() || path.size() > UNIX_PATH_MAX) {
238         d->error = InvalidPath;
239         return false;
240     }
241     unlink( path );  // ok if this fails
242 
243     // Create the socket
244     d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
245     if(-1 == d->fd) {
246 #ifdef QUNIXSOCKETSERVER_DEBUG
247         qDebug() << "QUnixSocketServer: Unable to create socket ("
248                  << strerror(errno) << ')';
249 #endif
250         close();
251         d->error = ResourceError;
252         return false;
253     }
254 
255     // Construct our unix address
256     struct ::sockaddr_un addr;
257     addr.sun_family = AF_UNIX;
258     ::memcpy(addr.sun_path, path.data(), path.size());
259     if(path.size() < UNIX_PATH_MAX)
260         addr.sun_path[path.size()] = '\0';
261 
262     // Attempt to bind
263     if(-1 == ::bind(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un))) {
264 #ifdef QUNIXSOCKETSERVER_DEBUG
265         qDebug() << "QUnixSocketServer: Unable to bind socket ("
266                  << strerror(errno) << ')';
267 #endif
268         close();
269         d->error = BindError;
270         return false;
271     }
272 
273     // Listen to socket
274     if(-1 == ::listen(d->fd, d->maxConns)) {
275 #ifdef QUNIXSOCKETSERVER_DEBUG
276         qDebug() << "QUnixSocketServer: Unable to listen socket ("
277                  << strerror(errno) << ')';
278 #endif
279         close();
280         d->error = ListenError;
281         return false;
282     }
283 
284     // Success!
285     d->address = path;
286     d->acceptNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d);
287     d->acceptNotifier->setEnabled(true);
288     QObject::connect(d->acceptNotifier, SIGNAL(activated(int)),
289                      d, SLOT(acceptActivated()));
290 
291     return true;
292 }
293 
294 /*!
295   Returns the Unix path on which this server is listening.  If this server is
296   not listening, and empty address will be returned.
297   */
serverAddress() const298 QByteArray QUnixSocketServer::serverAddress() const
299 {
300     if(!d)
301         return QByteArray();
302     return d->address;
303 }
304 
socketDescriptor() const305 int QUnixSocketServer::socketDescriptor() const
306 {
307     if (!d)
308         return -1;
309     return d->fd;
310 }
311 
312 
313 /*!
314   Returns the maximum length the queue of pending connections may grow to.  That
315   is, the maximum number of clients attempting to connect for which the Unix
316   socket server has not yet accepted and passed to
317   \l QUnixSocketServer::incomingConnection().  If a connection request arrives
318   with the queue full, the client may receive a connection refused notification.
319 
320   By default a queue length of 30 is used.
321 
322   \sa QUnixSocketServer::setMaxPendingConnections()
323   */
maxPendingConnections() const324 int QUnixSocketServer::maxPendingConnections() const
325 {
326     if(!d)
327         return 30;
328 
329     return d->maxConns;
330 }
331 
332 /*!
333   Sets the maximum length the queue of pending connections may grow to
334   \a numConnections.  This value will only apply to
335   \l QUnixSocketServer::listen() calls made following the value change - it will
336   not be retroactively applied.
337 
338   \sa QUnixSocketServer::maxPendingConnections()
339   */
setMaxPendingConnections(int numConnections)340 void QUnixSocketServer::setMaxPendingConnections(int numConnections)
341 {
342     Q_ASSERT(numConnections >= 1);
343     if(!d)
344         d = new QUnixSocketServerPrivate(this);
345 
346     d->maxConns = numConnections;
347 }
348 
349 /*!
350   \fn void QUnixSocketServer::incomingConnection(int socketDescriptor)
351 
352   This method is invoked each time a new incoming connection is established with
353   the server.  Clients must reimplement this function in their QUnixSocketServer
354   derived class to handle the connection.
355 
356   A common approach to handling the connection is to pass \a socketDescriptor to
357   a QUnixSocket instance.
358 
359   \sa QUnixSocket
360   */
361 
acceptActivated()362 void QUnixSocketServerPrivate::acceptActivated()
363 {
364     ::sockaddr_un r;
365     socklen_t len = sizeof(sockaddr_un);
366     int connsock = ::accept(fd, (sockaddr *)&r, &len);
367 #ifdef QUNIXSOCKETSERVER_DEBUG
368     qDebug() << "QUnixSocketServer: Accept connection " << connsock;
369 #endif
370     if(-1 != connsock)
371         me->incomingConnection(connsock);
372 }
373 
374 QT_END_NAMESPACE
375 
376 #include "qunixsocketserver.moc"
377