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 return true;
54 }
55
56 /* Handle one arriving client connection.
57 * Clients are expected to send a ListFilesRequest, terminated by a '0'.
58 * Server will respond with a ListFilesResponse message.
59 */
handle_connection(int connfd)60 void handle_connection(int connfd)
61 {
62 DIR *directory = NULL;
63
64 /* Decode the message from the client and open the requested directory. */
65 {
66 ListFilesRequest request = {};
67 pb_istream_t input = pb_istream_from_socket(connfd);
68
69 if (!pb_decode(&input, ListFilesRequest_fields, &request))
70 {
71 printf("Decode failed: %s\n", PB_GET_ERROR(&input));
72 return;
73 }
74
75 directory = opendir(request.path);
76 printf("Listing directory: %s\n", request.path);
77 }
78
79 /* List the files in the directory and transmit the response to client */
80 {
81 ListFilesResponse response = {};
82 pb_ostream_t output = pb_ostream_from_socket(connfd);
83
84 if (directory == NULL)
85 {
86 perror("opendir");
87
88 /* Directory was not found, transmit error status */
89 response.has_path_error = true;
90 response.path_error = true;
91 response.file.funcs.encode = NULL;
92 }
93 else
94 {
95 /* Directory was found, transmit filenames */
96 response.has_path_error = false;
97 response.file.funcs.encode = &listdir_callback;
98 response.file.arg = directory;
99 }
100
101 if (!pb_encode(&output, ListFilesResponse_fields, &response))
102 {
103 printf("Encoding failed: %s\n", PB_GET_ERROR(&output));
104 }
105 }
106
107 if (directory != NULL)
108 closedir(directory);
109 }
110
main(int argc,char ** argv)111 int main(int argc, char **argv)
112 {
113 int listenfd, connfd;
114 struct sockaddr_in servaddr;
115 int reuse = 1;
116
117 /* Listen on localhost:1234 for TCP connections */
118 listenfd = socket(AF_INET, SOCK_STREAM, 0);
119 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
120
121 memset(&servaddr, 0, sizeof(servaddr));
122 servaddr.sin_family = AF_INET;
123 servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
124 servaddr.sin_port = htons(1234);
125 if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0)
126 {
127 perror("bind");
128 return 1;
129 }
130
131 if (listen(listenfd, 5) != 0)
132 {
133 perror("listen");
134 return 1;
135 }
136
137 for(;;)
138 {
139 /* Wait for a client */
140 connfd = accept(listenfd, NULL, NULL);
141
142 if (connfd < 0)
143 {
144 perror("accept");
145 return 1;
146 }
147
148 printf("Got connection.\n");
149
150 handle_connection(connfd);
151
152 printf("Closing connection.\n");
153
154 close(connfd);
155 }
156
157 return 0;
158 }
159