Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <pthread.h>
20 #include <signal.h>
21 #include <string.h>
22 #include <strings.h>
23 #include <unistd.h>
25 #include <arpa/inet.h>
26 #include <linux/types.h>
27 #include <linux/netlink.h>
28 #include <netinet/in.h>
29 #include <sys/socket.h>
31 #include "nsDebug.h"
32 #include "base/message_loop.h"
33 #include "mozilla/FileUtils.h"
34 #include "nsAutoPtr.h"
35 #include "nsThreadUtils.h"
36 #include "nsXULAppAPI.h"
38 #include "UeventPoller.h"
40 namespace mozilla {
41 namespace hal_impl {
43 static void ShutdownUevent();
45 class NetlinkPoller : public MessageLoopForIO::Watcher
46 {
47 public:
48 NetlinkPoller() : mSocket(-1),
49 mIOLoop(MessageLoopForIO::current())
50 {
51 }
53 virtual ~NetlinkPoller() {}
55 bool OpenSocket();
57 virtual void OnFileCanReadWithoutBlocking(int fd);
59 // no writing to the netlink socket
60 virtual void OnFileCanWriteWithoutBlocking(int fd)
61 {
62 MOZ_CRASH("Must not write to netlink socket");
63 }
65 MessageLoopForIO *GetIOLoop () const { return mIOLoop; }
66 void RegisterObserver(IUeventObserver *aObserver)
67 {
68 mUeventObserverList.AddObserver(aObserver);
69 }
71 void UnregisterObserver(IUeventObserver *aObserver)
72 {
73 mUeventObserverList.RemoveObserver(aObserver);
74 if (mUeventObserverList.Length() == 0)
75 ShutdownUevent(); // this will destroy self
76 }
78 private:
79 ScopedClose mSocket;
80 MessageLoopForIO* mIOLoop;
81 MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
83 const static int kBuffsize = 64 * 1024;
84 uint8_t mBuffer [kBuffsize];
86 typedef ObserverList<NetlinkEvent> UeventObserverList;
87 UeventObserverList mUeventObserverList;
88 };
90 bool
91 NetlinkPoller::OpenSocket()
92 {
93 mSocket.rwget() = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
94 if (mSocket.get() < 0)
95 return false;
97 int sz = kBuffsize;
99 if (setsockopt(mSocket.get(), SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0)
100 return false;
102 // add FD_CLOEXEC flag
103 int flags = fcntl(mSocket.get(), F_GETFD);
104 if (flags == -1) {
105 return false;
106 }
107 flags |= FD_CLOEXEC;
108 if (fcntl(mSocket.get(), F_SETFD, flags) == -1)
109 return false;
111 // set non-blocking
112 if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1)
113 return false;
115 struct sockaddr_nl saddr;
116 bzero(&saddr, sizeof(saddr));
117 saddr.nl_family = AF_NETLINK;
118 saddr.nl_groups = 1;
119 saddr.nl_pid = gettid();
121 do {
122 if (bind(mSocket.get(), (struct sockaddr *)&saddr, sizeof(saddr)) == 0) {
123 break;
124 }
126 if (errno != EADDRINUSE) {
127 return false;
128 }
130 if (saddr.nl_pid == 0) {
131 return false;
132 }
134 // Once there was any other place in the same process assigning saddr.nl_pid by
135 // gettid(), we can detect it and print warning message.
136 printf_stderr("The netlink socket address saddr.nl_pid=%u is in use. Let the kernel re-assign.\n", saddr.nl_pid);
137 saddr.nl_pid = 0;
138 } while (true);
140 if (!mIOLoop->WatchFileDescriptor(mSocket.get(),
141 true,
142 MessageLoopForIO::WATCH_READ,
143 &mReadWatcher,
144 this)) {
145 return false;
146 }
148 return true;
149 }
151 static nsAutoPtr<NetlinkPoller> sPoller;
153 class UeventInitTask : public Task
154 {
155 virtual void Run()
156 {
157 if (!sPoller) {
158 return;
159 }
160 if (sPoller->OpenSocket()) {
161 return;
162 }
163 sPoller->GetIOLoop()->PostDelayedTask(FROM_HERE, new UeventInitTask(), 1000);
164 }
165 };
167 void
168 NetlinkPoller::OnFileCanReadWithoutBlocking(int fd)
169 {
170 MOZ_ASSERT(fd == mSocket.get());
171 while (true) {
172 int ret = read(fd, mBuffer, kBuffsize);
173 if (ret == -1) {
174 if (errno == EAGAIN || errno == EWOULDBLOCK) {
175 return;
176 }
177 if (errno == EINTR) {
178 continue;
179 }
180 }
181 if (ret <= 0) {
182 // fatal error on netlink socket which should not happen
183 _exit(1);
184 }
185 NetlinkEvent netlinkEvent;
186 netlinkEvent.decode(reinterpret_cast<char*>(mBuffer), ret);
187 mUeventObserverList.Broadcast(netlinkEvent);
188 }
189 }
191 static void
192 InitializeUevent()
193 {
194 MOZ_ASSERT(!sPoller);
195 sPoller = new NetlinkPoller();
196 sPoller->GetIOLoop()->PostTask(FROM_HERE, new UeventInitTask());
198 }
200 static void
201 ShutdownUevent()
202 {
203 sPoller = nullptr;
204 }
206 void
207 RegisterUeventListener(IUeventObserver *aObserver)
208 {
209 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
211 if (!sPoller)
212 InitializeUevent();
213 sPoller->RegisterObserver(aObserver);
214 }
216 void
217 UnregisterUeventListener(IUeventObserver *aObserver)
218 {
219 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
221 sPoller->UnregisterObserver(aObserver);
222 }
224 } // hal_impl
225 } // mozilla