root/tools/android/file_poller/file_poller.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. safe_write
  2. transfer_to_fd
  3. transfer_to_buffer
  4. checked_open
  5. transfer_measurement
  6. acquire_sample
  7. poll_content
  8. content_collection
  9. main

// Copyright 2014 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.

// When run with 2 or more arguments the file_poller tool will open a port on
// the device, print it on its standard output and then start collect file
// contents.  The first argument is the polling rate in Hz, and the following
// arguments are file to poll.
// When run with the port of an already running file_poller, the tool will
// contact the first instance, retrieve the sample and print those on its
// standard output. This will also terminate the first instance.

#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include "base/logging.h"

// Context containing the files to poll and the polling rate.
struct Context {
  size_t nb_files;
  int* file_fds;
  int poll_rate;
};

// Write from the buffer to the given file descriptor.
void safe_write(int fd, const char* buffer, int size) {
  const char* index = buffer;
  size_t to_write = size;
  while (to_write > 0) {
    int written = write(fd, index, to_write);
    if (written < 0)
      PLOG(FATAL);
    index += written;
    to_write -= written;
  }
}

// Transfer the content of a file descriptor to another.
void transfer_to_fd(int fd_in, int fd_out) {
  char buffer[1024];
  int n;
  while ((n = read(fd_in, buffer, sizeof(buffer))) > 0)
    safe_write(fd_out, buffer, n);
}

// Transfer the content of a file descriptor to a buffer.
int transfer_to_buffer(int fd_in, char* bufffer, size_t size) {
  char* index = bufffer;
  size_t to_read = size;
  int n;
  while (to_read > 0 && ((n = read(fd_in, index, to_read)) > 0)) {
    index += n;
    to_read -= n;
  }
  if (n < 0)
    PLOG(FATAL);
  return size - to_read;
}

// Try to open the file at the given path for reading. Exit in case of failure.
int checked_open(const char* path) {
  int fd = open(path, O_RDONLY);
  if (fd < 0)
    PLOG(FATAL);
  return fd;
}

void transfer_measurement(int fd_in, int fd_out, bool last) {
  char buffer[1024];
  if (lseek(fd_in, 0, SEEK_SET) < 0)
    PLOG(FATAL);
  int n = transfer_to_buffer(fd_in, buffer, sizeof(buffer));
  safe_write(fd_out, buffer, n - 1);
  safe_write(fd_out, last ? "\n" : " ", 1);
}

// Acquire a sample and save it to the given file descriptor.
void acquire_sample(int fd, const Context& context) {
  struct timeval tv;
  gettimeofday(&tv, NULL);
  char buffer[1024];
  int n = snprintf(buffer, sizeof(buffer), "%d.%06d ", tv.tv_sec, tv.tv_usec);
  safe_write(fd, buffer, n);

  for (int i = 0; i < context.nb_files; ++i)
    transfer_measurement(context.file_fds[i], fd, i == (context.nb_files - 1));
}

void poll_content(const Context& context) {
  // Create and bind the socket so that the port can be written to stdout.
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  struct sockaddr_in socket_info;
  socket_info.sin_family = AF_INET;
  socket_info.sin_addr.s_addr = htonl(INADDR_ANY);
  socket_info.sin_port = htons(0);
  if (bind(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) < 0)
    PLOG(FATAL);
  socklen_t size = sizeof(socket_info);
  getsockname(sockfd, (struct sockaddr*)&socket_info, &size);
  printf("%d\n", ntohs(socket_info.sin_port));
  // Using a pipe to ensure child is diconnected from the terminal before
  // quitting.
  int pipes[2];
  pipe(pipes);
  pid_t pid = fork();
  if (pid < 0)
    PLOG(FATAL);
  if (pid != 0) {
    close(pipes[1]);
    // Not expecting any data to be received.
    read(pipes[0], NULL, 1);
    signal(SIGCHLD, SIG_IGN);
    return;
  }

  // Detach from terminal.
  setsid();
  close(STDIN_FILENO);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);
  close(pipes[0]);

  // Start listening for incoming connection.
  if (listen(sockfd, 1) < 0)
    PLOG(FATAL);

  // Signal the parent that it can now safely exit.
  close(pipes[1]);

  // Prepare file to store the samples.
  int fd;
  char filename[] = "/data/local/tmp/fileXXXXXX";
  fd = mkstemp(filename);
  unlink(filename);

  // Collect samples until a client connect on the socket.
  fd_set rfds;
  struct timeval timeout;
  do {
    acquire_sample(fd, context);
    timeout.tv_sec = 0;
    timeout.tv_usec = 1000000 / context.poll_rate;
    FD_ZERO(&rfds);
    FD_SET(sockfd, &rfds);
  } while (select(sockfd + 1, &rfds, NULL, NULL, &timeout) == 0);

  // Collect a final sample.
  acquire_sample(fd, context);

  // Send the result back.
  struct sockaddr_in remote_socket_info;
  int rfd = accept(sockfd, (struct sockaddr*)&remote_socket_info, &size);
  if (rfd < 0)
    PLOG(FATAL);
  if (lseek(fd, 0, SEEK_SET) < 0)
    PLOG(FATAL);
  transfer_to_fd(fd, rfd);
}

void content_collection(int port) {
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  // Connect to localhost.
  struct sockaddr_in socket_info;
  socket_info.sin_family = AF_INET;
  socket_info.sin_addr.s_addr = htonl(0x7f000001);
  socket_info.sin_port = htons(port);
  if (connect(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) <
      0) {
    PLOG(FATAL);
  }
  transfer_to_fd(sockfd, STDOUT_FILENO);
}

int main(int argc, char** argv) {
  if (argc == 1) {
    fprintf(stderr,
            "Usage: \n"
            " %s port\n"
            " %s rate FILE...\n",
            argv[0],
            argv[0]);
    exit(EXIT_FAILURE);
  }
  if (argc == 2) {
    // Argument is the port to connect to.
    content_collection(atoi(argv[1]));
  } else {
    // First argument is the poll frequency, in Hz, following arguments are the
    // file to poll.
    Context context;
    context.poll_rate = atoi(argv[1]);
    context.nb_files = argc - 2;
    context.file_fds = new int[context.nb_files];
    for (int i = 2; i < argc; ++i)
      context.file_fds[i - 2] = checked_open(argv[i]);
    poll_content(context);
  }
  return EXIT_SUCCESS;
}

/* [<][>][^][v][top][bottom][index][help] */