From 0b3303a173617f0ddefd9833f4d7d4362daa94c4 Mon Sep 17 00:00:00 2001 From: Azreyo <58790873+Azreyo@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:58:48 +0100 Subject: [PATCH] Add files via upload --- config_parser.c | 90 +++++++++++++++++ server.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++ server.json | 8 ++ server.log | 0 server_config.c | 11 ++ server_config.h | 17 ++++ 6 files changed, 390 insertions(+) create mode 100644 config_parser.c create mode 100644 server.c create mode 100644 server.json create mode 100644 server.log create mode 100644 server_config.c create mode 100644 server_config.h diff --git a/config_parser.c b/config_parser.c new file mode 100644 index 0000000..e6c6609 --- /dev/null +++ b/config_parser.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include "server_config.h" +#include + +int load_config(const char *filename, ServerConfig *config) { + FILE *fp = fopen(filename, "r"); + if (!fp) { + perror("Error opening config file"); + return 1; + } + + fseek(fp, 0, SEEK_END); + long file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + char *buffer = malloc(file_size + 1); + if (!buffer) { + perror("Error allocating memory for config file"); + fclose(fp); + return 1; + } + + fread(buffer, file_size, 1, fp); + buffer[file_size] = '\0'; + fclose(fp); + + cJSON *root = cJSON_Parse(buffer); + free(buffer); + + if (!root) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) { + fprintf(stderr, "Error before: %s\n", error_ptr); + } + goto end; + } + + cJSON *port = cJSON_GetObjectItemCaseSensitive(root, "port"); + if (cJSON_IsNumber(port)) { + config->port = port->valueint; + printf("load_config: port = %d\n", config->port); // debug + } else { + fprintf(stderr, "load_config: port not found or not a number. Using default.\n"); + config->port = 8080; // Default value + } + + cJSON *use_https = cJSON_GetObjectItemCaseSensitive(root, "use_https"); + if (cJSON_IsBool(use_https)) { + config->use_https = cJSON_IsTrue(use_https); + printf("load_config: use_https = %d\n", config->use_https); // debug + } else { + fprintf(stderr, "load_config: use_https not found or not a boolean. Using default.\n"); + config->use_https = false; // Default value + } + + cJSON *log_file = cJSON_GetObjectItemCaseSensitive(root, "log_file"); + if (cJSON_IsString(log_file) && (log_file->valuestring != NULL)) { + strncpy(config->log_file, log_file->valuestring, sizeof(config->log_file) - 1); + config->log_file[sizeof(config->log_file) - 1] = '\0'; // Ensure null termination + printf("load_config: log_file = %s\n", config->log_file); // debug + } else { + fprintf(stderr, "load_config: log_file not found or not a string. Using default.\n"); + strcpy(config->log_file, "server.log"); // Default value + } + + cJSON *max_threads = cJSON_GetObjectItemCaseSensitive(root, "max_threads"); + if (cJSON_IsNumber(max_threads)) { + config->max_threads = max_threads->valueint; + printf("load_config: max_threads = %d\n", config->max_threads); // debug + } else { + fprintf(stderr, "load_config: max_threads not found or not a number. Using default.\n"); + config->max_threads = 4; // Default value + } + + cJSON *running = cJSON_GetObjectItemCaseSensitive(root, "running"); + if (cJSON_IsBool(running)) { + config->running = cJSON_IsTrue(running); + printf("load_config: running = %d\n", config->running); // debug + } else { + fprintf(stderr, "load_config: running not found or not a boolean. Using default.\n"); + config->running = true; // Default value + } + +end: + cJSON_Delete(root); + return 0; +} \ No newline at end of file diff --git a/server.c b/server.c new file mode 100644 index 0000000..1bf2990 --- /dev/null +++ b/server.c @@ -0,0 +1,264 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "server_config.h" + +#define MAX_REQUEST_SIZE 8192 +#define MAX_LOG_SIZE 2048 +#define MAX_CLIENT_THREADS 100 + +// Global variables +ServerConfig config; +char server_log[MAX_LOG_SIZE]; +pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_t client_threads[MAX_CLIENT_THREADS]; +int num_client_threads = 0; + +// Function declarations +void *handle_client(void *arg); +void log_event(const char *message); +void display_menu(); +void handle_menu_option(int option); + +void shutdown_server() { + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + config.running = 0; + close(server_socket); + for (int i = 0; i < num_client_threads; i++) { + pthread_cancel(client_threads[i]); + } +} + + +int main() { + ServerConfig config; + + if (load_config("server.json", &config) != 0) { + printf("Using default configuration.\n"); + } + + config.running = 1; + + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket < 0) { + perror("Error creating socket"); + exit(1); + } + + struct sockaddr_in server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = INADDR_ANY; + server_address.sin_port = htons(config.port); + + if (bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { + perror("Error binding socket"); + exit(1); + } + + if (listen(server_socket, 5) < 0) { + perror("Error listening for connections"); + exit(1); + } + + log_event("Server started."); + + + + + while (config.running) { + int client_socket; + struct sockaddr_in client_address; + socklen_t client_address_len = sizeof(client_address); + pthread_mutex_t client_count_mutex = PTHREAD_MUTEX_INITIALIZER; + + client_socket = accept(server_socket, (struct sockaddr *)&client_address, &client_address_len); + if (client_socket < 0) { + perror("Error accepting connection"); + continue; + } + log_event("Client connected."); + setbuf(stdin, NULL); + pthread_t client_thread; + int *client_socket_ptr = malloc(sizeof(int)); + *client_socket_ptr = client_socket; + + if (pthread_create(&client_thread, NULL, handle_client, (void *)client_socket_ptr) != 0) { + perror("Error creating thread"); + close(client_socket); + free(client_socket_ptr); + continue; + } + + pthread_mutex_lock(&client_count_mutex); + if (num_client_threads < MAX_CLIENT_THREADS) { + client_threads[num_client_threads++] = client_thread; + } else { + fprintf(stderr, "Maximum number of client threads reached.\n"); + close(client_socket); + free(client_socket_ptr); + } + pthread_mutex_unlock(&client_count_mutex); + } + + for(int i = 0; i < num_client_threads; i++) { + pthread_join(client_threads[i], NULL); + } + + close(server_socket); + pthread_mutex_destroy(&log_mutex); + log_event("Server stopped."); + return 0; +} + + + +void cleanup_handler(void *arg) { + int client_socket = *((int *)arg); + close(client_socket); + printf("handle_client: Client socket closed.\n"); // debug +} + +void *handle_client(void *arg) { + int client_socket = *((int *)arg); + free(arg); + + printf("handle_client: Client connected. client_socket = %d\n", client_socket); // debug + + char request_buffer[MAX_REQUEST_SIZE]; + ssize_t bytes_received = recv(client_socket, request_buffer, MAX_REQUEST_SIZE - 1, 0); + + printf("handle_client: bytes_received = %ld\n", bytes_received); // debug + + // Push cleanup handler with the correct argument + pthread_cleanup_push(cleanup_handler, (void *)&client_socket); // Pass the address of client_socket + + if (bytes_received < 0) { + perror("handle_client: Error receiving data"); + log_event("Error receiving data"); // Log the error + } else if (bytes_received == 0) { + log_event("Client disconnected."); + } else { + request_buffer[bytes_received] = '\0'; + log_event("Received request:"); + log_event(request_buffer); + + char filepath[256]; + strcpy(filepath, "www/"); + strcat(filepath, "index.html"); // Default file + + int fd = open(filepath, O_RDONLY); + printf("handle_client: File descriptor (fd) = %d\n", fd); + + if (fd == -1) { + const char *not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"; + send(client_socket, not_found_response, strlen(not_found_response), 0); + } else { + struct stat st; + fstat(fd, &st); + off_t file_size = st.st_size; + + printf("handle_client: File size = %ld\n", file_size); // debug + + char headers[512]; + snprintf(headers, sizeof(headers), "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n\r\n", file_size); + send(client_socket, headers, strlen(headers), 0); + + char buffer[1024]; + ssize_t bytes_read; + while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { + printf("handle_client: bytes_read = %ld\n", bytes_read); // debug + send(client_socket, buffer, bytes_read, 0); + } + + close(fd); + } + } + + pthread_cleanup_pop(1); // Pop and execute cleanup handler + pthread_exit(NULL); +} + +void log_event(const char *message) { + pthread_mutex_lock(&log_mutex); + + time_t t = time(NULL); + struct tm tm = *localtime(&t); + char timestamp[64]; + + // Format timestamp + strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &tm); + + // Append new log entry safely + size_t remaining_size = MAX_LOG_SIZE - strlen(server_log) - 2; + if (remaining_size > 0) { + snprintf(server_log + strlen(server_log), remaining_size, "%s: %s\n", timestamp, message); + } + + pthread_mutex_unlock(&log_mutex); +} + +void print_log() { + pthread_mutex_lock(&log_mutex); + printf("Server Log:\n%s", server_log); + pthread_mutex_unlock(&log_mutex); +} + + + +void display_menu() { + printf("\nServer Menu:\n"); + printf("1. Status\n"); + printf("2. Logging\n"); + printf("3. Config\n"); + printf("4. Switch HTTP/HTTPS\n"); + printf("5. Troubleshooting\n"); + printf("6. Exit\n"); + printf("Enter your choice: "); + + char input[10]; + if (fgets(input, sizeof(input), stdin) != NULL) { + int choice = atoi(input); + handle_menu_option(choice); + } +} + + +void handle_menu_option(int option) { + switch (option) { + case 1: + printf("Server Status:\n"); + printf("Running: %s\n", config.running ? "Yes" : "No"); + printf("Port: %d\n", config.port); + break; + case 2: + pthread_mutex_lock(&log_mutex); + printf("Server Log:\n%s\n", server_log); + pthread_mutex_unlock(&log_mutex); + break; + case 3: + printf("Config Options:\n"); + break; + case 4: + printf("Switching HTTP/HTTPS:\n"); + break; + case 5: + printf("Troubleshooting:\n"); + break; + case 6: + printf("Exiting...\n"); + shutdown_server(); + break; + default: + printf("Invalid option.\n"); + } +} diff --git a/server.json b/server.json new file mode 100644 index 0000000..5f3473c --- /dev/null +++ b/server.json @@ -0,0 +1,8 @@ +{ + "port": 8080, + "use_https": false, + "log_file": "server.log", + "max_threads": 4, + "running": true, + "automatic_startup": false + } \ No newline at end of file diff --git a/server.log b/server.log new file mode 100644 index 0000000..e69de29 diff --git a/server_config.c b/server_config.c new file mode 100644 index 0000000..bf2fa86 --- /dev/null +++ b/server_config.c @@ -0,0 +1,11 @@ +#include +#include +#include "server_config.h" + +void init_config(ServerConfig *config) { + config->port = 8080; + config->use_https = false; + strcpy(config->log_file, "server.log"); + config->max_threads = 4; + config->running = true; +} \ No newline at end of file diff --git a/server_config.h b/server_config.h new file mode 100644 index 0000000..0aa7b82 --- /dev/null +++ b/server_config.h @@ -0,0 +1,17 @@ +#ifndef SERVER_CONFIG_H +#define SERVER_CONFIG_H + +#include + +typedef struct { + int port; + bool use_https; + char log_file[256]; + int max_threads; + bool running; +} ServerConfig; + +int load_config(const char *filename, ServerConfig *config); +void init_config(ServerConfig *config); + +#endif \ No newline at end of file