1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <fcntl.h>
8 #include "UnixSocketWatcher.h"
9 
10 namespace mozilla {
11 namespace ipc {
12 
~UnixSocketWatcher()13 UnixSocketWatcher::~UnixSocketWatcher()
14 {
15 }
16 
Close()17 void UnixSocketWatcher::Close()
18 {
19   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
20 
21   mConnectionStatus = SOCKET_IS_DISCONNECTED;
22   UnixFdWatcher::Close();
23 }
24 
25 nsresult
Connect(const struct sockaddr * aAddr,socklen_t aAddrLen)26 UnixSocketWatcher::Connect(const struct sockaddr* aAddr, socklen_t aAddrLen)
27 {
28   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
29   MOZ_ASSERT(IsOpen());
30   MOZ_ASSERT(aAddr || !aAddrLen);
31 
32   if (TEMP_FAILURE_RETRY(connect(GetFd(), aAddr, aAddrLen) < 0)) {
33     if (errno == EINPROGRESS) {
34       mConnectionStatus = SOCKET_IS_CONNECTING;
35       // Set up a write watch to receive the connect signal
36       AddWatchers(WRITE_WATCHER, false);
37       return NS_OK;
38     }
39     OnError("connect", errno);
40     return NS_ERROR_FAILURE;
41   }
42 
43   mConnectionStatus = SOCKET_IS_CONNECTED;
44   OnConnected();
45 
46   return NS_OK;
47 }
48 
49 nsresult
Listen(const struct sockaddr * aAddr,socklen_t aAddrLen)50 UnixSocketWatcher::Listen(const struct sockaddr* aAddr, socklen_t aAddrLen)
51 {
52   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
53   MOZ_ASSERT(IsOpen());
54   MOZ_ASSERT(aAddr || !aAddrLen);
55 
56   if (mConnectionStatus == SOCKET_IS_DISCONNECTED) {
57     // We init the socket descriptor when we listen for the first time.
58     if (bind(GetFd(), aAddr, aAddrLen) < 0) {
59       OnError("bind", errno);
60       return NS_ERROR_FAILURE;
61     }
62     if (listen(GetFd(), 1) < 0) {
63       OnError("listen", errno);
64       return NS_ERROR_FAILURE;
65     }
66   }
67   mConnectionStatus = SOCKET_IS_LISTENING;
68   OnListening();
69 
70   return NS_OK;
71 }
72 
UnixSocketWatcher(MessageLoop * aIOLoop)73 UnixSocketWatcher::UnixSocketWatcher(MessageLoop* aIOLoop)
74 : UnixFdWatcher(aIOLoop)
75 , mConnectionStatus(SOCKET_IS_DISCONNECTED)
76 {
77 }
78 
UnixSocketWatcher(MessageLoop * aIOLoop,int aFd,ConnectionStatus aConnectionStatus)79 UnixSocketWatcher::UnixSocketWatcher(MessageLoop* aIOLoop, int aFd,
80                                      ConnectionStatus aConnectionStatus)
81 : UnixFdWatcher(aIOLoop, aFd)
82 , mConnectionStatus(aConnectionStatus)
83 {
84 }
85 
86 void
SetSocket(int aFd,ConnectionStatus aConnectionStatus)87 UnixSocketWatcher::SetSocket(int aFd, ConnectionStatus aConnectionStatus)
88 {
89   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
90 
91   SetFd(aFd);
92   mConnectionStatus = aConnectionStatus;
93 }
94 
95 void
OnFileCanReadWithoutBlocking(int aFd)96 UnixSocketWatcher::OnFileCanReadWithoutBlocking(int aFd)
97 {
98   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
99   MOZ_ASSERT(aFd == GetFd());
100 
101   if (mConnectionStatus == SOCKET_IS_CONNECTED) {
102     OnSocketCanReceiveWithoutBlocking();
103   } else if (mConnectionStatus == SOCKET_IS_LISTENING) {
104     OnSocketCanAcceptWithoutBlocking();
105   } else {
106     NS_NOTREACHED("invalid connection state for reading");
107   }
108 }
109 
110 void
OnFileCanWriteWithoutBlocking(int aFd)111 UnixSocketWatcher::OnFileCanWriteWithoutBlocking(int aFd)
112 {
113   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
114   MOZ_ASSERT(aFd == GetFd());
115 
116   if (mConnectionStatus == SOCKET_IS_CONNECTED) {
117     OnSocketCanSendWithoutBlocking();
118   } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
119     RemoveWatchers(WRITE_WATCHER);
120     int error = 0;
121     socklen_t len = sizeof(error);
122     if (getsockopt(GetFd(), SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
123       OnError("getsockopt", errno);
124     } else if (error) {
125       OnError("connect", error);
126     } else {
127       mConnectionStatus = SOCKET_IS_CONNECTED;
128       OnConnected();
129     }
130   } else {
131     NS_NOTREACHED("invalid connection state for writing");
132   }
133 }
134 
135 }
136 }
137