Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

async_network_alive_linux.cc

// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/notifier/base/async_network_alive.h"

#include <sys/socket.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

#include "base/logging.h"
#include "base/platform_file.h"
#include "talk/base/physicalsocketserver.h"

using base::kInvalidPlatformFileValue;

namespace notifier {

class AsyncNetworkAliveLinux : public AsyncNetworkAlive {
 public:
  AsyncNetworkAliveLinux() {
    if (pipe(exit_pipe_) == -1) {
      PLOG(ERROR) << "Could not create pipe for exit signal.";
      exit_pipe_[0] = kInvalidPlatformFileValue;
      exit_pipe_[1] = kInvalidPlatformFileValue;
    }
  }

  virtual ~AsyncNetworkAliveLinux() {
    if (exit_pipe_[1] != kInvalidPlatformFileValue) {
      // Ensure that we've signalled the thread to quit.
      char data = 0;
      if (write(exit_pipe_[1], &data, 1) == -1) {
        PLOG(WARNING) << "Error sending error signal to AsyncNetworkAliveLinux";
      }
      close(exit_pipe_[1]);
      exit_pipe_[1] = kInvalidPlatformFileValue;
    }
    if (exit_pipe_[0] != kInvalidPlatformFileValue) {
      close(exit_pipe_[0]);
      exit_pipe_[0] = kInvalidPlatformFileValue;
    }
  }

 protected:
  // SignalThread Interface
  virtual void DoWork() {
    if (exit_pipe_[0] == kInvalidPlatformFileValue) {
      PLOG(ERROR) << "No exit flag to listen for.";
      // If we don't have an exit flag to listen for, set the error flag and
      // abort.
      error_ = true;
      return;
    }

    // This function listens for changes to network interfaces, and link state.
    // It's copied from syncapi.cc.
    struct sockaddr_nl socket_address;

    memset(&socket_address, 0, sizeof(socket_address));
    socket_address.nl_family = AF_NETLINK;
    socket_address.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;

    // NETLINK_ROUTE is the protocol used to update the kernel routing table.
    int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    bind(fd, (struct sockaddr *) &socket_address, sizeof(socket_address));

    fd_set rdfs;
    FD_ZERO(&rdfs);
    FD_SET(fd, &rdfs);
    FD_SET(exit_pipe_[0], &rdfs);

    int max_fd = fd > exit_pipe_[0] ? fd : exit_pipe_[0];

    int result = select(max_fd + 1, &rdfs, NULL, NULL, NULL);

    if (result <= 0) {
      error_ = true;
      PLOG(ERROR) << "select() returned unexpected result " << result;
      close(fd);
      close(exit_pipe_[0]);
      exit_pipe_[0] = kInvalidPlatformFileValue;
      return;
    }

    // Since we received a change from the socket, read the change in.
    if (FD_ISSET(fd, &rdfs)) {
      char buf[4096];
      struct iovec iov = { buf, sizeof(buf) };
      struct sockaddr_nl sa;

      struct msghdr msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
      recvmsg(fd, &msg, 0);
    }

    close(fd);

    // If exit_pipe was written to, we must be shutting down.
    if (exit_pipe_[0] == kInvalidPlatformFileValue ||
        FD_ISSET(exit_pipe_[0], &rdfs)) {
      alive_ = false;
      error_ = true;
      close(exit_pipe_[0]);
      exit_pipe_[0] = kInvalidPlatformFileValue;
      return;
    }

    // If there is an active connection, check that talk.google.com:5222
    // is reachable.
    talk_base::PhysicalSocketServer physical;
    scoped_ptr<talk_base::Socket> socket(physical.CreateSocket(SOCK_STREAM));
    if (socket->Connect(talk_base::SocketAddress("talk.google.com", 5222))) {
      alive_ = false;
    } else {
      alive_ = true;
    }

    close(exit_pipe_[0]);
    exit_pipe_[0] = kInvalidPlatformFileValue;
  }

  virtual void OnWorkStop() {
    if (exit_pipe_[1] != kInvalidPlatformFileValue) {
      char data = 0;
      // We can't ignore the return value on write(), since that generates a
      // compile warning.  However, since we're exiting, there's nothing we can
      // do if this fails except to log it.
      if (write(exit_pipe_[1], &data, 1) == -1) {
        PLOG(WARNING) << "Error sending error signal to AsyncNetworkAliveLinux";
      }
    }
  }

 private:
  int exit_pipe_[2];
  DISALLOW_COPY_AND_ASSIGN(AsyncNetworkAliveLinux);
};

AsyncNetworkAlive* AsyncNetworkAlive::Create() {
  return new AsyncNetworkAliveLinux();
}

}  // namespace notifier

Generated by  Doxygen 1.6.0   Back to index