1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
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"
43 static void ShutdownUevent();
45 class NetlinkPoller
: public MessageLoopForIO::Watcher
48 NetlinkPoller() : mSocket(-1),
49 mIOLoop(MessageLoopForIO::current())
53 virtual ~NetlinkPoller() {}
57 virtual void OnFileCanReadWithoutBlocking(int fd
);
59 // no writing to the netlink socket
60 virtual void OnFileCanWriteWithoutBlocking(int fd
)
62 MOZ_CRASH("Must not write to netlink socket");
65 MessageLoopForIO
*GetIOLoop () const { return mIOLoop
; }
66 void RegisterObserver(IUeventObserver
*aObserver
)
68 mUeventObserverList
.AddObserver(aObserver
);
71 void UnregisterObserver(IUeventObserver
*aObserver
)
73 mUeventObserverList
.RemoveObserver(aObserver
);
74 if (mUeventObserverList
.Length() == 0)
75 ShutdownUevent(); // this will destroy self
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
;
91 NetlinkPoller::OpenSocket()
93 mSocket
.rwget() = socket(PF_NETLINK
, SOCK_DGRAM
, NETLINK_KOBJECT_UEVENT
);
94 if (mSocket
.get() < 0)
99 if (setsockopt(mSocket
.get(), SOL_SOCKET
, SO_RCVBUFFORCE
, &sz
, sizeof(sz
)) < 0)
102 // add FD_CLOEXEC flag
103 int flags
= fcntl(mSocket
.get(), F_GETFD
);
108 if (fcntl(mSocket
.get(), F_SETFD
, flags
) == -1)
112 if (fcntl(mSocket
.get(), F_SETFL
, O_NONBLOCK
) == -1)
115 struct sockaddr_nl saddr
;
116 bzero(&saddr
, sizeof(saddr
));
117 saddr
.nl_family
= AF_NETLINK
;
119 saddr
.nl_pid
= gettid();
122 if (bind(mSocket
.get(), (struct sockaddr
*)&saddr
, sizeof(saddr
)) == 0) {
126 if (errno
!= EADDRINUSE
) {
130 if (saddr
.nl_pid
== 0) {
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
);
140 if (!mIOLoop
->WatchFileDescriptor(mSocket
.get(),
142 MessageLoopForIO::WATCH_READ
,
151 static nsAutoPtr
<NetlinkPoller
> sPoller
;
153 class UeventInitTask
: public Task
160 if (sPoller
->OpenSocket()) {
163 sPoller
->GetIOLoop()->PostDelayedTask(FROM_HERE
, new UeventInitTask(), 1000);
168 NetlinkPoller::OnFileCanReadWithoutBlocking(int fd
)
170 MOZ_ASSERT(fd
== mSocket
.get());
172 int ret
= read(fd
, mBuffer
, kBuffsize
);
174 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
177 if (errno
== EINTR
) {
182 // fatal error on netlink socket which should not happen
185 NetlinkEvent netlinkEvent
;
186 netlinkEvent
.decode(reinterpret_cast<char*>(mBuffer
), ret
);
187 mUeventObserverList
.Broadcast(netlinkEvent
);
194 MOZ_ASSERT(!sPoller
);
195 sPoller
= new NetlinkPoller();
196 sPoller
->GetIOLoop()->PostTask(FROM_HERE
, new UeventInitTask());
207 RegisterUeventListener(IUeventObserver
*aObserver
)
209 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
213 sPoller
->RegisterObserver(aObserver
);
217 UnregisterUeventListener(IUeventObserver
*aObserver
)
219 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
221 sPoller
->UnregisterObserver(aObserver
);