• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
safe_write(int fd,const char * buffer,int size)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.
transfer_to_fd(int fd_in,int fd_out)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.
transfer_to_buffer(int fd_in,char * bufffer,size_t size)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.
checked_open(const char * path)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 
transfer_measurement(int fd_in,int fd_out,bool last)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.
acquire_sample(int fd,const Context & context)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 
poll_content(const Context & context)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 
content_collection(int port)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 
main(int argc,char ** argv)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