Home | History | Annotate | Download | only in file_poller
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // When run with 2 or more arguments the file_poller tool will open a port on
      6 // the device, print it on its standard output and then start collect file
      7 // contents.  The first argument is the polling rate in Hz, and the following
      8 // arguments are file to poll.
      9 // When run with the port of an already running file_poller, the tool will
     10 // contact the first instance, retrieve the sample and print those on its
     11 // standard output. This will also terminate the first instance.
     12 
     13 #include <errno.h>
     14 #include <fcntl.h>
     15 #include <netinet/in.h>
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 #include <sys/socket.h>
     19 #include <sys/stat.h>
     20 #include <sys/time.h>
     21 #include <sys/types.h>
     22 #include <unistd.h>
     23 
     24 #include "base/logging.h"
     25 
     26 // Context containing the files to poll and the polling rate.
     27 struct Context {
     28   size_t nb_files;
     29   int* file_fds;
     30   int poll_rate;
     31 };
     32 
     33 // Write from the buffer to the given file descriptor.
     34 void safe_write(int fd, const char* buffer, int size) {
     35   const char* index = buffer;
     36   size_t to_write = size;
     37   while (to_write > 0) {
     38     int written = write(fd, index, to_write);
     39     if (written < 0)
     40       PLOG(FATAL);
     41     index += written;
     42     to_write -= written;
     43   }
     44 }
     45 
     46 // Transfer the content of a file descriptor to another.
     47 void transfer_to_fd(int fd_in, int fd_out) {
     48   char buffer[1024];
     49   int n;
     50   while ((n = read(fd_in, buffer, sizeof(buffer))) > 0)
     51     safe_write(fd_out, buffer, n);
     52 }
     53 
     54 // Transfer the content of a file descriptor to a buffer.
     55 int transfer_to_buffer(int fd_in, char* bufffer, size_t size) {
     56   char* index = bufffer;
     57   size_t to_read = size;
     58   int n;
     59   while (to_read > 0 && ((n = read(fd_in, index, to_read)) > 0)) {
     60     index += n;
     61     to_read -= n;
     62   }
     63   if (n < 0)
     64     PLOG(FATAL);
     65   return size - to_read;
     66 }
     67 
     68 // Try to open the file at the given path for reading. Exit in case of failure.
     69 int checked_open(const char* path) {
     70   int fd = open(path, O_RDONLY);
     71   if (fd < 0)
     72     PLOG(FATAL);
     73   return fd;
     74 }
     75 
     76 void transfer_measurement(int fd_in, int fd_out, bool last) {
     77   char buffer[1024];
     78   if (lseek(fd_in, 0, SEEK_SET) < 0)
     79     PLOG(FATAL);
     80   int n = transfer_to_buffer(fd_in, buffer, sizeof(buffer));
     81   safe_write(fd_out, buffer, n - 1);
     82   safe_write(fd_out, last ? "\n" : " ", 1);
     83 }
     84 
     85 // Acquire a sample and save it to the given file descriptor.
     86 void acquire_sample(int fd, const Context& context) {
     87   struct timeval tv;
     88   gettimeofday(&tv, NULL);
     89   char buffer[1024];
     90   int n = snprintf(buffer, sizeof(buffer), "%d.%06d ", tv.tv_sec, tv.tv_usec);
     91   safe_write(fd, buffer, n);
     92 
     93   for (int i = 0; i < context.nb_files; ++i)
     94     transfer_measurement(context.file_fds[i], fd, i == (context.nb_files - 1));
     95 }
     96 
     97 void poll_content(const Context& context) {
     98   // Create and bind the socket so that the port can be written to stdout.
     99   int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    100   struct sockaddr_in socket_info;
    101   socket_info.sin_family = AF_INET;
    102   socket_info.sin_addr.s_addr = htonl(INADDR_ANY);
    103   socket_info.sin_port = htons(0);
    104   if (bind(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) < 0)
    105     PLOG(FATAL);
    106   socklen_t size = sizeof(socket_info);
    107   getsockname(sockfd, (struct sockaddr*)&socket_info, &size);
    108   printf("%d\n", ntohs(socket_info.sin_port));
    109   // Using a pipe to ensure child is diconnected from the terminal before
    110   // quitting.
    111   int pipes[2];
    112   pipe(pipes);
    113   pid_t pid = fork();
    114   if (pid < 0)
    115     PLOG(FATAL);
    116   if (pid != 0) {
    117     close(pipes[1]);
    118     // Not expecting any data to be received.
    119     read(pipes[0], NULL, 1);
    120     signal(SIGCHLD, SIG_IGN);
    121     return;
    122   }
    123 
    124   // Detach from terminal.
    125   setsid();
    126   close(STDIN_FILENO);
    127   close(STDOUT_FILENO);
    128   close(STDERR_FILENO);
    129   close(pipes[0]);
    130 
    131   // Start listening for incoming connection.
    132   if (listen(sockfd, 1) < 0)
    133     PLOG(FATAL);
    134 
    135   // Signal the parent that it can now safely exit.
    136   close(pipes[1]);
    137 
    138   // Prepare file to store the samples.
    139   int fd;
    140   char filename[] = "/data/local/tmp/fileXXXXXX";
    141   fd = mkstemp(filename);
    142   unlink(filename);
    143 
    144   // Collect samples until a client connect on the socket.
    145   fd_set rfds;
    146   struct timeval timeout;
    147   do {
    148     acquire_sample(fd, context);
    149     timeout.tv_sec = 0;
    150     timeout.tv_usec = 1000000 / context.poll_rate;
    151     FD_ZERO(&rfds);
    152     FD_SET(sockfd, &rfds);
    153   } while (select(sockfd + 1, &rfds, NULL, NULL, &timeout) == 0);
    154 
    155   // Collect a final sample.
    156   acquire_sample(fd, context);
    157 
    158   // Send the result back.
    159   struct sockaddr_in remote_socket_info;
    160   int rfd = accept(sockfd, (struct sockaddr*)&remote_socket_info, &size);
    161   if (rfd < 0)
    162     PLOG(FATAL);
    163   if (lseek(fd, 0, SEEK_SET) < 0)
    164     PLOG(FATAL);
    165   transfer_to_fd(fd, rfd);
    166 }
    167 
    168 void content_collection(int port) {
    169   int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    170   // Connect to localhost.
    171   struct sockaddr_in socket_info;
    172   socket_info.sin_family = AF_INET;
    173   socket_info.sin_addr.s_addr = htonl(0x7f000001);
    174   socket_info.sin_port = htons(port);
    175   if (connect(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) <
    176       0) {
    177     PLOG(FATAL);
    178   }
    179   transfer_to_fd(sockfd, STDOUT_FILENO);
    180 }
    181 
    182 int main(int argc, char** argv) {
    183   if (argc == 1) {
    184     fprintf(stderr,
    185             "Usage: \n"
    186             " %s port\n"
    187             " %s rate FILE...\n",
    188             argv[0],
    189             argv[0]);
    190     exit(EXIT_FAILURE);
    191   }
    192   if (argc == 2) {
    193     // Argument is the port to connect to.
    194     content_collection(atoi(argv[1]));
    195   } else {
    196     // First argument is the poll frequency, in Hz, following arguments are the
    197     // file to poll.
    198     Context context;
    199     context.poll_rate = atoi(argv[1]);
    200     context.nb_files = argc - 2;
    201     context.file_fds = new int[context.nb_files];
    202     for (int i = 2; i < argc; ++i)
    203       context.file_fds[i - 2] = checked_open(argv[i]);
    204     poll_content(context);
    205   }
    206   return EXIT_SUCCESS;
    207 }
    208