1*c8d645caSAndroid Build Coastguard Worker /* This is a simple TCP server that listens on port 1234 and provides lists
2*c8d645caSAndroid Build Coastguard Worker * of files to clients, using a protocol defined in file_server.proto.
3*c8d645caSAndroid Build Coastguard Worker *
4*c8d645caSAndroid Build Coastguard Worker * It directly deserializes and serializes messages from network, minimizing
5*c8d645caSAndroid Build Coastguard Worker * memory use.
6*c8d645caSAndroid Build Coastguard Worker *
7*c8d645caSAndroid Build Coastguard Worker * For flexibility, this example is implemented using posix api.
8*c8d645caSAndroid Build Coastguard Worker * In a real embedded system you would typically use some other kind of
9*c8d645caSAndroid Build Coastguard Worker * a communication and filesystem layer.
10*c8d645caSAndroid Build Coastguard Worker */
11*c8d645caSAndroid Build Coastguard Worker
12*c8d645caSAndroid Build Coastguard Worker #include <sys/socket.h>
13*c8d645caSAndroid Build Coastguard Worker #include <sys/types.h>
14*c8d645caSAndroid Build Coastguard Worker #include <netinet/in.h>
15*c8d645caSAndroid Build Coastguard Worker #include <unistd.h>
16*c8d645caSAndroid Build Coastguard Worker #include <dirent.h>
17*c8d645caSAndroid Build Coastguard Worker #include <stdio.h>
18*c8d645caSAndroid Build Coastguard Worker #include <string.h>
19*c8d645caSAndroid Build Coastguard Worker
20*c8d645caSAndroid Build Coastguard Worker #include <pb_encode.h>
21*c8d645caSAndroid Build Coastguard Worker #include <pb_decode.h>
22*c8d645caSAndroid Build Coastguard Worker
23*c8d645caSAndroid Build Coastguard Worker #include "fileproto.pb.h"
24*c8d645caSAndroid Build Coastguard Worker #include "common.h"
25*c8d645caSAndroid Build Coastguard Worker
26*c8d645caSAndroid Build Coastguard Worker /* This callback function will be called once during the encoding.
27*c8d645caSAndroid Build Coastguard Worker * It will write out any number of FileInfo entries, without consuming unnecessary memory.
28*c8d645caSAndroid Build Coastguard Worker * This is accomplished by fetching the filenames one at a time and encoding them
29*c8d645caSAndroid Build Coastguard Worker * immediately.
30*c8d645caSAndroid Build Coastguard Worker */
listdir_callback(pb_ostream_t * stream,const pb_field_t * field,void * const * arg)31*c8d645caSAndroid Build Coastguard Worker bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
32*c8d645caSAndroid Build Coastguard Worker {
33*c8d645caSAndroid Build Coastguard Worker DIR *dir = (DIR*) *arg;
34*c8d645caSAndroid Build Coastguard Worker struct dirent *file;
35*c8d645caSAndroid Build Coastguard Worker FileInfo fileinfo = {};
36*c8d645caSAndroid Build Coastguard Worker
37*c8d645caSAndroid Build Coastguard Worker while ((file = readdir(dir)) != NULL)
38*c8d645caSAndroid Build Coastguard Worker {
39*c8d645caSAndroid Build Coastguard Worker fileinfo.inode = file->d_ino;
40*c8d645caSAndroid Build Coastguard Worker strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name));
41*c8d645caSAndroid Build Coastguard Worker fileinfo.name[sizeof(fileinfo.name) - 1] = '\0';
42*c8d645caSAndroid Build Coastguard Worker
43*c8d645caSAndroid Build Coastguard Worker /* This encodes the header for the field, based on the constant info
44*c8d645caSAndroid Build Coastguard Worker * from pb_field_t. */
45*c8d645caSAndroid Build Coastguard Worker if (!pb_encode_tag_for_field(stream, field))
46*c8d645caSAndroid Build Coastguard Worker return false;
47*c8d645caSAndroid Build Coastguard Worker
48*c8d645caSAndroid Build Coastguard Worker /* This encodes the data for the field, based on our FileInfo structure. */
49*c8d645caSAndroid Build Coastguard Worker if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
50*c8d645caSAndroid Build Coastguard Worker return false;
51*c8d645caSAndroid Build Coastguard Worker }
52*c8d645caSAndroid Build Coastguard Worker
53*c8d645caSAndroid Build Coastguard Worker /* Because the main program uses pb_encode_delimited(), this callback will be
54*c8d645caSAndroid Build Coastguard Worker * called twice. Rewind the directory for the next call. */
55*c8d645caSAndroid Build Coastguard Worker rewinddir(dir);
56*c8d645caSAndroid Build Coastguard Worker
57*c8d645caSAndroid Build Coastguard Worker return true;
58*c8d645caSAndroid Build Coastguard Worker }
59*c8d645caSAndroid Build Coastguard Worker
60*c8d645caSAndroid Build Coastguard Worker /* Handle one arriving client connection.
61*c8d645caSAndroid Build Coastguard Worker * Clients are expected to send a ListFilesRequest, terminated by a '0'.
62*c8d645caSAndroid Build Coastguard Worker * Server will respond with a ListFilesResponse message.
63*c8d645caSAndroid Build Coastguard Worker */
handle_connection(int connfd)64*c8d645caSAndroid Build Coastguard Worker void handle_connection(int connfd)
65*c8d645caSAndroid Build Coastguard Worker {
66*c8d645caSAndroid Build Coastguard Worker DIR *directory = NULL;
67*c8d645caSAndroid Build Coastguard Worker
68*c8d645caSAndroid Build Coastguard Worker /* Decode the message from the client and open the requested directory. */
69*c8d645caSAndroid Build Coastguard Worker {
70*c8d645caSAndroid Build Coastguard Worker ListFilesRequest request = {};
71*c8d645caSAndroid Build Coastguard Worker pb_istream_t input = pb_istream_from_socket(connfd);
72*c8d645caSAndroid Build Coastguard Worker
73*c8d645caSAndroid Build Coastguard Worker if (!pb_decode_delimited(&input, ListFilesRequest_fields, &request))
74*c8d645caSAndroid Build Coastguard Worker {
75*c8d645caSAndroid Build Coastguard Worker printf("Decode failed: %s\n", PB_GET_ERROR(&input));
76*c8d645caSAndroid Build Coastguard Worker return;
77*c8d645caSAndroid Build Coastguard Worker }
78*c8d645caSAndroid Build Coastguard Worker
79*c8d645caSAndroid Build Coastguard Worker directory = opendir(request.path);
80*c8d645caSAndroid Build Coastguard Worker printf("Listing directory: %s\n", request.path);
81*c8d645caSAndroid Build Coastguard Worker }
82*c8d645caSAndroid Build Coastguard Worker
83*c8d645caSAndroid Build Coastguard Worker /* List the files in the directory and transmit the response to client */
84*c8d645caSAndroid Build Coastguard Worker {
85*c8d645caSAndroid Build Coastguard Worker ListFilesResponse response = {};
86*c8d645caSAndroid Build Coastguard Worker pb_ostream_t output = pb_ostream_from_socket(connfd);
87*c8d645caSAndroid Build Coastguard Worker
88*c8d645caSAndroid Build Coastguard Worker if (directory == NULL)
89*c8d645caSAndroid Build Coastguard Worker {
90*c8d645caSAndroid Build Coastguard Worker perror("opendir");
91*c8d645caSAndroid Build Coastguard Worker
92*c8d645caSAndroid Build Coastguard Worker /* Directory was not found, transmit error status */
93*c8d645caSAndroid Build Coastguard Worker response.has_path_error = true;
94*c8d645caSAndroid Build Coastguard Worker response.path_error = true;
95*c8d645caSAndroid Build Coastguard Worker response.file.funcs.encode = NULL;
96*c8d645caSAndroid Build Coastguard Worker }
97*c8d645caSAndroid Build Coastguard Worker else
98*c8d645caSAndroid Build Coastguard Worker {
99*c8d645caSAndroid Build Coastguard Worker /* Directory was found, transmit filenames */
100*c8d645caSAndroid Build Coastguard Worker response.has_path_error = false;
101*c8d645caSAndroid Build Coastguard Worker response.file.funcs.encode = &listdir_callback;
102*c8d645caSAndroid Build Coastguard Worker response.file.arg = directory;
103*c8d645caSAndroid Build Coastguard Worker }
104*c8d645caSAndroid Build Coastguard Worker
105*c8d645caSAndroid Build Coastguard Worker if (!pb_encode_delimited(&output, ListFilesResponse_fields, &response))
106*c8d645caSAndroid Build Coastguard Worker {
107*c8d645caSAndroid Build Coastguard Worker printf("Encoding failed: %s\n", PB_GET_ERROR(&output));
108*c8d645caSAndroid Build Coastguard Worker }
109*c8d645caSAndroid Build Coastguard Worker }
110*c8d645caSAndroid Build Coastguard Worker
111*c8d645caSAndroid Build Coastguard Worker if (directory != NULL)
112*c8d645caSAndroid Build Coastguard Worker closedir(directory);
113*c8d645caSAndroid Build Coastguard Worker }
114*c8d645caSAndroid Build Coastguard Worker
main(int argc,char ** argv)115*c8d645caSAndroid Build Coastguard Worker int main(int argc, char **argv)
116*c8d645caSAndroid Build Coastguard Worker {
117*c8d645caSAndroid Build Coastguard Worker int listenfd, connfd;
118*c8d645caSAndroid Build Coastguard Worker struct sockaddr_in servaddr;
119*c8d645caSAndroid Build Coastguard Worker int reuse = 1;
120*c8d645caSAndroid Build Coastguard Worker
121*c8d645caSAndroid Build Coastguard Worker /* Listen on localhost:1234 for TCP connections */
122*c8d645caSAndroid Build Coastguard Worker listenfd = socket(AF_INET, SOCK_STREAM, 0);
123*c8d645caSAndroid Build Coastguard Worker setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
124*c8d645caSAndroid Build Coastguard Worker
125*c8d645caSAndroid Build Coastguard Worker memset(&servaddr, 0, sizeof(servaddr));
126*c8d645caSAndroid Build Coastguard Worker servaddr.sin_family = AF_INET;
127*c8d645caSAndroid Build Coastguard Worker servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
128*c8d645caSAndroid Build Coastguard Worker servaddr.sin_port = htons(1234);
129*c8d645caSAndroid Build Coastguard Worker if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0)
130*c8d645caSAndroid Build Coastguard Worker {
131*c8d645caSAndroid Build Coastguard Worker perror("bind");
132*c8d645caSAndroid Build Coastguard Worker return 1;
133*c8d645caSAndroid Build Coastguard Worker }
134*c8d645caSAndroid Build Coastguard Worker
135*c8d645caSAndroid Build Coastguard Worker if (listen(listenfd, 5) != 0)
136*c8d645caSAndroid Build Coastguard Worker {
137*c8d645caSAndroid Build Coastguard Worker perror("listen");
138*c8d645caSAndroid Build Coastguard Worker return 1;
139*c8d645caSAndroid Build Coastguard Worker }
140*c8d645caSAndroid Build Coastguard Worker
141*c8d645caSAndroid Build Coastguard Worker for(;;)
142*c8d645caSAndroid Build Coastguard Worker {
143*c8d645caSAndroid Build Coastguard Worker /* Wait for a client */
144*c8d645caSAndroid Build Coastguard Worker connfd = accept(listenfd, NULL, NULL);
145*c8d645caSAndroid Build Coastguard Worker
146*c8d645caSAndroid Build Coastguard Worker if (connfd < 0)
147*c8d645caSAndroid Build Coastguard Worker {
148*c8d645caSAndroid Build Coastguard Worker perror("accept");
149*c8d645caSAndroid Build Coastguard Worker return 1;
150*c8d645caSAndroid Build Coastguard Worker }
151*c8d645caSAndroid Build Coastguard Worker
152*c8d645caSAndroid Build Coastguard Worker printf("Got connection.\n");
153*c8d645caSAndroid Build Coastguard Worker
154*c8d645caSAndroid Build Coastguard Worker handle_connection(connfd);
155*c8d645caSAndroid Build Coastguard Worker
156*c8d645caSAndroid Build Coastguard Worker printf("Closing connection.\n");
157*c8d645caSAndroid Build Coastguard Worker
158*c8d645caSAndroid Build Coastguard Worker close(connfd);
159*c8d645caSAndroid Build Coastguard Worker }
160*c8d645caSAndroid Build Coastguard Worker
161*c8d645caSAndroid Build Coastguard Worker return 0;
162*c8d645caSAndroid Build Coastguard Worker }
163