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