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