/* Реализовать Web-Server использующий многопроцессный способ обработки клиентов через fork(). Веб-сервер должен работать в виде демона и обеспечивать базовую поддержку протокола HTTP, на уровне метода GET (по желанию методы HEAD и POST). Проверку Web-Server’а необходимо осуществлять на статических текстовых и двоичных данных, таких как изображения. Предусмотреть контроль и журналирование ошибок (либо в файл, либо через syslog). Обеспечить одновременную работу сервера с множественным числом клиентов. */ #include "daemon.h" #include #include #include #include #include #include #include #include #include static int stop = 0; static char www_root[512]; void signal_handler(int sig) { switch (sig) { case SIGTERM: case SIGINT: syslog(LOG_INFO, "Terminate signal catched. Stopping daemon..."); stop = 1; break; } } int create_server_socket(int port) { struct sockaddr_in addr; int listener = socket(AF_INET, SOCK_STREAM, 0); if (listener < 0) { syslog(LOG_ERR, "Unable to create server socket"); exit(1); } int opt = 1; if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { syslog(LOG_WARNING, "Unable to set SO_REUSEADDR"); } addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "Unable to bind socket to port %d", port); exit(1); } return listener; } const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); if (!ext) return "application/octet-stream"; if (strcmp(ext, ".html") == 0) return "text/html; charset=utf-8"; if (strcmp(ext, ".txt") == 0) return "text/plain; charset=utf-8"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".js") == 0) return "application/javascript"; return "application/octet-stream"; } void send_file(int client_sock, const char *filepath) { int fd = open(filepath, O_RDONLY); if (fd < 0) { /* Файл не найден - отправляем 404 */ const char *response = "HTTP/1.0 404 Not Found\r\n" "Content-Type: text/html\r\n\r\n" "

404 Not Found

"; write(client_sock, response, strlen(response)); syslog(LOG_INFO, "File not found: %s", filepath); return; } /* Получаем размер файла */ struct stat st; fstat(fd, &st); /* Отправляем заголовки HTTP */ char header[256]; snprintf(header, sizeof(header), "HTTP/1.0 200 OK\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n\r\n", get_content_type(filepath), st.st_size); write(client_sock, header, strlen(header)); /* Отправляем содержимое файла */ char buf[4096]; int bytes; while ((bytes = read(fd, buf, sizeof(buf))) > 0) { write(client_sock, buf, bytes); } close(fd); syslog(LOG_INFO, "Sent file: %s", filepath); } void handle_client(int client_sock) { char buf[1024]; int bytes_read = read(client_sock, buf, sizeof(buf) - 1); if (bytes_read <= 0) { syslog(LOG_ERR, "Unable to read from client"); return; } buf[bytes_read] = '\0'; /* Парсим первую строку: GET /path HTTP/1.x */ char method[16], path[256], protocol[16]; if (sscanf(buf, "%s %s %s", method, path, protocol) != 3) { syslog(LOG_ERR, "Invalid HTTP request"); return; } /* Поддерживаем только GET */ if (strcmp(method, "GET") != 0) { const char *response = "HTTP/1.0 501 Not Implemented\r\n\r\n"; write(client_sock, response, strlen(response)); syslog(LOG_INFO, "Unsupported method: %s", method); return; } /* Формируем путь к файлу */ char filepath[512]; snprintf(filepath, sizeof(filepath), "%s", www_root); /* Если запрашивают /, отдаём index.html */ if (strcmp(path, "/") == 0) { strcat(filepath, "/index.html"); } else { strcat(filepath, path); } syslog(LOG_INFO, "Request: %s %s -> %s", method, path, filepath); /* Отправляем файл */ send_file(client_sock, filepath); } int server(const char *root, int port) { snprintf(www_root, sizeof(www_root), "%s", root); openlog("MY-WEB-SERVER", 0, LOG_DAEMON); daemonize(); signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); /* Автоматическая очистка завершенных процессов */ signal(SIGCHLD, SIG_IGN); int client_sock; int server_socket = create_server_socket(port); listen(server_socket, 5); syslog(LOG_INFO, "Server started on port %d, serving %s", port, www_root); while (!stop) { client_sock = accept(server_socket, NULL, NULL); if (client_sock < 0) { if (stop) { break; } syslog(LOG_ERR, "Unable to accept client connection"); continue; } switch (fork()) { case -1: syslog(LOG_ERR, "Unable to fork"); close(client_sock); continue; case 0: close(server_socket); handle_client(client_sock); close(client_sock); exit(0); default: close(client_sock); } } close(server_socket); syslog(LOG_INFO, "Server stopped"); closelog(); return 0; }