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 */ 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 */ 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 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