OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |