/* This is a simple TCP server that listens on port 1234 and provides lists * of files to clients, using a protocol defined in file_server.proto. * * It directly deserializes and serializes messages from network, minimizing * memory use. * * For flexibility, this example is implemented using posix api. * In a real embedded system you would typically use some other kind of * a communication and filesystem layer. */ #include #include #include #include #include #include #include #include #include #include "fileproto.h" bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) { int fd = *(int*)stream->state; return send(fd, buf, count, 0) == count; } bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count) { int fd = *(int*)stream->state; if (buf == NULL) { /* Well, this is a really inefficient way to skip input. */ /* It is only used when there are unknown fields. */ char dummy; while (count-- && recv(fd, &dummy, 1, 0) == 1); return count == 0; } return recv(fd, buf, count, MSG_WAITALL) == count; } bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, const void *arg) { DIR *dir = (DIR*) arg; struct dirent *file; FileInfo fileinfo; while ((file = readdir(dir)) != NULL) { fileinfo.inode = file->d_ino; strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name)); fileinfo.name[sizeof(fileinfo.name) - 1] = '\0'; if (!pb_encode_tag_for_field(stream, field)) return false; if (!pb_enc_submessage(stream, field, &fileinfo)) return false; } return true; } void handle_connection(int connfd) { ListFilesRequest request; ListFilesResponse response; pb_istream_t input = {&read_callback, &connfd, SIZE_MAX}; pb_ostream_t output = {&write_callback, &connfd, SIZE_MAX, 0}; DIR *directory; if (!pb_decode(&input, ListFilesRequest_fields, &request)) { printf("Decoding failed.\n"); return; } directory = opendir(request.path); printf("Listing directory: %s\n", request.path); if (directory == NULL) { perror("opendir"); response.has_path_error = true; response.path_error = true; response.file.funcs.encode = NULL; } else { response.has_path_error = false; response.file.funcs.encode = &listdir_callback; response.file.arg = directory; } if (!pb_encode(&output, ListFilesResponse_fields, &response)) { printf("Encoding failed.\n"); } } int main(int argc, char **argv) { int listenfd, connfd; struct sockaddr_in servaddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); servaddr.sin_port = htons(1234); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) { perror("bind"); return 1; } if (listen(listenfd, 5) != 0) { perror("listen"); return 1; } for(;;) { connfd = accept(listenfd, NULL, NULL); if (connfd < 0) { perror("accept"); return 1; } printf("Got connection.\n"); handle_connection(connfd); printf("Closing connection.\n"); close(connfd); } }