diff -r 000000000000 -r 6474c204b198 hal/gonk/UeventPoller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hal/gonk/UeventPoller.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* Copyright 2012 Mozilla Foundation and Mozilla contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "nsDebug.h" +#include "base/message_loop.h" +#include "mozilla/FileUtils.h" +#include "nsAutoPtr.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" + +#include "UeventPoller.h" + +namespace mozilla { +namespace hal_impl { + +static void ShutdownUevent(); + +class NetlinkPoller : public MessageLoopForIO::Watcher +{ +public: + NetlinkPoller() : mSocket(-1), + mIOLoop(MessageLoopForIO::current()) + { + } + + virtual ~NetlinkPoller() {} + + bool OpenSocket(); + + virtual void OnFileCanReadWithoutBlocking(int fd); + + // no writing to the netlink socket + virtual void OnFileCanWriteWithoutBlocking(int fd) + { + MOZ_CRASH("Must not write to netlink socket"); + } + + MessageLoopForIO *GetIOLoop () const { return mIOLoop; } + void RegisterObserver(IUeventObserver *aObserver) + { + mUeventObserverList.AddObserver(aObserver); + } + + void UnregisterObserver(IUeventObserver *aObserver) + { + mUeventObserverList.RemoveObserver(aObserver); + if (mUeventObserverList.Length() == 0) + ShutdownUevent(); // this will destroy self + } + +private: + ScopedClose mSocket; + MessageLoopForIO* mIOLoop; + MessageLoopForIO::FileDescriptorWatcher mReadWatcher; + + const static int kBuffsize = 64 * 1024; + uint8_t mBuffer [kBuffsize]; + + typedef ObserverList UeventObserverList; + UeventObserverList mUeventObserverList; +}; + +bool +NetlinkPoller::OpenSocket() +{ + mSocket.rwget() = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (mSocket.get() < 0) + return false; + + int sz = kBuffsize; + + if (setsockopt(mSocket.get(), SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) + return false; + + // add FD_CLOEXEC flag + int flags = fcntl(mSocket.get(), F_GETFD); + if (flags == -1) { + return false; + } + flags |= FD_CLOEXEC; + if (fcntl(mSocket.get(), F_SETFD, flags) == -1) + return false; + + // set non-blocking + if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) + return false; + + struct sockaddr_nl saddr; + bzero(&saddr, sizeof(saddr)); + saddr.nl_family = AF_NETLINK; + saddr.nl_groups = 1; + saddr.nl_pid = gettid(); + + do { + if (bind(mSocket.get(), (struct sockaddr *)&saddr, sizeof(saddr)) == 0) { + break; + } + + if (errno != EADDRINUSE) { + return false; + } + + if (saddr.nl_pid == 0) { + return false; + } + + // Once there was any other place in the same process assigning saddr.nl_pid by + // gettid(), we can detect it and print warning message. + printf_stderr("The netlink socket address saddr.nl_pid=%u is in use. Let the kernel re-assign.\n", saddr.nl_pid); + saddr.nl_pid = 0; + } while (true); + + if (!mIOLoop->WatchFileDescriptor(mSocket.get(), + true, + MessageLoopForIO::WATCH_READ, + &mReadWatcher, + this)) { + return false; + } + + return true; +} + +static nsAutoPtr sPoller; + +class UeventInitTask : public Task +{ + virtual void Run() + { + if (!sPoller) { + return; + } + if (sPoller->OpenSocket()) { + return; + } + sPoller->GetIOLoop()->PostDelayedTask(FROM_HERE, new UeventInitTask(), 1000); + } +}; + +void +NetlinkPoller::OnFileCanReadWithoutBlocking(int fd) +{ + MOZ_ASSERT(fd == mSocket.get()); + while (true) { + int ret = read(fd, mBuffer, kBuffsize); + if (ret == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } + if (errno == EINTR) { + continue; + } + } + if (ret <= 0) { + // fatal error on netlink socket which should not happen + _exit(1); + } + NetlinkEvent netlinkEvent; + netlinkEvent.decode(reinterpret_cast(mBuffer), ret); + mUeventObserverList.Broadcast(netlinkEvent); + } +} + +static void +InitializeUevent() +{ + MOZ_ASSERT(!sPoller); + sPoller = new NetlinkPoller(); + sPoller->GetIOLoop()->PostTask(FROM_HERE, new UeventInitTask()); + +} + +static void +ShutdownUevent() +{ + sPoller = nullptr; +} + +void +RegisterUeventListener(IUeventObserver *aObserver) +{ + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + + if (!sPoller) + InitializeUevent(); + sPoller->RegisterObserver(aObserver); +} + +void +UnregisterUeventListener(IUeventObserver *aObserver) +{ + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + + sPoller->UnregisterObserver(aObserver); +} + +} // hal_impl +} // mozilla +