|  | // 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; | 
|  | } |