diff --git a/src/main.c b/src/main.c index 45642a6..5787f5f 100644 --- a/src/main.c +++ b/src/main.c @@ -203,7 +203,7 @@ int read_safe_integer() { // Speicher freigeben und mit 0 überschreiben (Prävention von use-after-free-Schwachstelle) void cleanup_memory() { if (all_entries != NULL) { - printf("%lu Bytes Speicher werden freigegeben\n", (unsigned long)(max_entries * sizeof(struct log_entry))); + printf("\nDEBUG: %lu Bytes Speicher werden freigegeben\n", (unsigned long)(max_entries * sizeof(struct log_entry))); free(all_entries); all_entries = NULL; } @@ -225,7 +225,7 @@ void mem_expand_dynamically() { int old_max = max_entries; max_entries = max_entries * GROWTH_FACTOR; - printf("Dynamische Speichererweiterung von %d auf %d Einträge um Faktor %f\n", old_max, max_entries, GROWTH_FACTOR); + printf("DEBUG: Dynamische Speichererweiterung von %d auf %d Einträge um Faktor %f\n", old_max, max_entries, GROWTH_FACTOR); struct log_entry *new_ptr = realloc(all_entries, max_entries * sizeof(struct log_entry)); @@ -236,7 +236,7 @@ void mem_expand_dynamically() { } all_entries = new_ptr; - printf("Speicher erfolgreich erweitert auf %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); + printf("DEBUG: Speicher erfolgreich erweitert auf %lu Bytes\n", (unsigned long)(max_entries * sizeof(struct log_entry))); } } @@ -250,7 +250,7 @@ void allocate_initial_memory() { exit(1); // cleanup_and_exit() nicht nötig, da der Speicherbereich nicht beschrieben wurde - use-after-free unproblematisch } - printf("Speicher erfolgreich alloziert für %d Log-Einträge (%lu Bytes)\n", max_entries, (unsigned long)(max_entries * sizeof(struct log_entry))); + printf("DEBUG: Speicher erfolgreich alloziert für %d Log-Einträge (%lu Bytes)\n", max_entries, (unsigned long)(max_entries * sizeof(struct log_entry))); } void get_current_timestamp(char* buffer, int buffer_size) { @@ -540,7 +540,7 @@ void load_regular_file(char* filename) { printf("ERROR: Kann Datei '%s' nicht öffnen!\n", filename); return; } - printf("Lade Datei: %s\n", filename); + printf("INFO: Lade Datei: %s\n", filename); char line[MAX_LINE_LENGTH_BUF]; int loaded_from_this_file = 0; while (fgets(line, sizeof(line), file) != NULL) { @@ -558,8 +558,8 @@ void load_log_file(char* path) { total_entries = 0; if (is_directory(path)) { - printf("Verzeichnis erkannt: %s\n", path); - printf("Suche nach .log Dateien...\n"); + printf("DEBUG: Verzeichnis erkannt: %s\n", path); + printf("DEBUG: Suche nach .log Dateien...\n"); DIR* dir = opendir(path); if (dir == NULL) { @@ -585,17 +585,17 @@ void load_log_file(char* path) { files_found++; } else if (strstr(filename, ".gz") != NULL) { printf("INFO: .gz Datei '%s' übersprungen (nicht unterstützt in dieser Version)\n", filename); - printf(" Tipp: Dekomprimieren Sie mit 'gunzip %s'\n", filename); + printf(" Tipp: Dekomprimieren Sie mit 'gunzip %s'\n", filename); } } closedir(dir); if (files_found == 0) { - printf("Keine .log Dateien im Verzeichnis gefunden.\n"); - printf("Tipp: Für .gz Dateien verwenden Sie 'gunzip *.gz' zum Dekomprimieren\n"); + printf("WARNING: Keine .log Dateien im Verzeichnis gefunden.\n"); + printf(" Tipp: Für .gz Dateien verwenden Sie 'gunzip *.gz' zum Dekomprimieren\n"); } else { - printf("Insgesamt %d .log Dateien verarbeitet.\n", files_found); + printf("INFO: Insgesamt %d .log Dateien verarbeitet.\n", files_found); } } else { @@ -603,17 +603,17 @@ void load_log_file(char* path) { if (strstr(path, ".gz") != NULL) { printf("FEHLER: .gz Dateien werden in dieser Version nicht unterstützt!\n"); - printf("Lösung: Dekomprimieren Sie die Datei zuerst:\n"); - printf(" gunzip %s\n", path); - printf(" Dann: %s %.*s\n", "PROGRAMM", (int)(strlen(path)-3), path); + printf(" Lösung: Dekomprimieren Sie die Datei zuerst:\n"); + printf(" gunzip %s\n", path); + printf(" Dann: %s %.*s\n", "PROGRAMM", (int)(strlen(path)-3), path); return; } else { load_regular_file(path); } } - printf("Erfolgreich %d Einträge insgesamt geladen.\n", total_entries); - printf("Aktueller Speicherverbrauch: %lu Bytes für %d Einträge\n", (unsigned long)(max_entries * sizeof(struct log_entry)), max_entries); + printf("INFO: Erfolgreich %d Einträge insgesamt geladen.\n", total_entries); + printf("DEBUG: Aktueller Speicherverbrauch: %lu Bytes für %d Einträge\n", (unsigned long)(max_entries * sizeof(struct log_entry)), max_entries); } // Funktion zum suchen eines Suchbegriffs innerhalb eines Strings (lowercase) @@ -981,19 +981,23 @@ void format_iso8601_time(struct simple_time time, char* buffer, int buffer_size) } //Export in Timesketch-kompatiblem Format -void export_filtered_entries() { - printf("Dateiname für Timesketch-Export eingeben (ohne .csv): "); +void export_filtered_entries(char *filepath) { //90 chars + delimiter char filename[91]; - if (scanf("%90s", filename) != 1) { - printf("FEHLER: Ungültiger Dateiname!\n"); - clear_input_buffer(); - return; + if (filepath == NULL) { + printf("Dateiname für Timesketch-Export eingeben (ohne .csv): "); + if (scanf("%90s", filename) != 1) { + printf("FEHLER: Ungültiger Dateiname!\n"); + clear_input_buffer(); + return; + } + } else { + strncpy(filename, filepath, sizeof(filename) - 1); + filename[sizeof(filename) - 1] = '\0'; } - clear_input_buffer(); // Dateiendung strcat(filename, ".csv"); - + printf("\nINFO: Schreibe Datei %s...\n", filename); FILE* file = fopen(filename, "w"); if (file == NULL) { printf("ERROR: Kann Datei '%s' nicht erstellen!\n", filename); @@ -1028,7 +1032,7 @@ void export_filtered_entries() { } fclose(file); - printf("%d Logeinträge erfolgreich als Timesketch-kompatible CSV-Datei nach '%s' exportiert.\n", exported_count, filename); + printf("INFO: %d Logeinträge erfolgreich als Timesketch-kompatible CSV-Datei nach '%s' exportiert.\n", exported_count, filename); } struct ip_stat { @@ -2150,7 +2154,7 @@ void menu_show_entries() { supress_preview = 1; show_filtered_entries(0); } else if (choice == 2) { - export_filtered_entries(); + export_filtered_entries(0); } else if (choice == 3) { show_top_x_ips(); } else if (choice == 4) { @@ -2165,66 +2169,118 @@ void menu_show_entries() { } } -int main(int argc, char* argv[]) { - if (argc != 2) { - printf("Verwendung: %s \n", argv[0]); - printf("Unterstützte Formate: .log Dateien, sowie für rotierte Logs: .log.1 etc.\n"); - printf("Beispiele:\n"); - printf(" %s /var/log/nginx/access.log (einzelne .log Datei)\n", argv[0]); - printf(" %s /var/log/nginx/ (Verzeichnis mit .log Dateien)\n", argv[0]); - printf("\nRotierte nginx-Logs (.gz-Archive) müssen manuell dekomprimiert werden:\n"); - printf(" 1. Dekomprimieren: gunzip /var/log/nginx/*.gz\n"); - printf(" 2. Dann ausführen: %s /var/log/nginx/\n", argv[0]); - return 1; - } else if (argc == 3) { - - } - +void print_help(char* binary) { printf("\nNGINX EXAMINATOR\n"); - - allocate_initial_memory(); - load_log_file(argv[1]); - - if (total_entries == 0) { - printf("Keine gültigen Log-Einträge gefunden. Überprüfen Sie den Pfad und die Dateiformate.\n"); - cleanup_memory(); + printf("Verwendung:\n"); + printf(" %s -i Interaktiver Modus (Filter/Analyse/Export)\n", binary); + printf(" %s -e Export generieren\n", binary); + printf(" %s -h Diese Hilfe anzeigen\n", binary); + + printf("\nArgumente:\n"); + printf(" Pfad zu einer einzelnen *.log Datei ODER zu einem Ordner,\n"); + printf(" der eine oder mehrere *.log / *.log.N Dateien enthält.\n"); + + printf("\nFlags:\n"); + printf(" -i Startet interaktiven Modus mit Filtern (Status, Methode, IP, Zeitraum,\n"); + printf(" User-Agent, URL-Teilstring), Vorschau, Statistiken und CSV-Export (Timesketch).\n"); + printf(" -e Generiert Timesketch-kompatible CSV-Datei, nimmt optional Dateinamen entgegen.\n"); + printf(" -h Zeigt diese Hilfe an.\n"); + + printf("\nUnterstützte Eingaben:\n"); + printf(" - Normale NGINX-Access-Logs: *.log\n"); + printf(" - Rotierte Logs: *.log.1, *.log.2, ... (rein textbasiert)\n"); + printf(" - GZIP-Archive (*.gz) werden NICHT direkt unterstützt.\n"); + printf(" Tipp: Dekomprimieren Sie vorher, z.B.:\n"); + printf(" gunzip /var/log/nginx/*.gz\n"); + printf(" %s /var/log/nginx/ -i\n", binary); + + printf("\nErwartetes NGINX-Logformat (default `log_format main`):\n"); + printf(" log_format main '$remote_addr - $remote_user [$time_local] \"$request\" '\n"); + printf(" '$status $body_bytes_sent \"$http_referer\" '\n"); + printf(" '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n"); + + printf("\nBeispielzeile (eine Zeile pro Request):\n"); + printf(" 0.0.0.0 - - [31/Aug/2025:00:11:42 +0000] \"GET /.git/config HTTP/1.1\" 400 255 \"-\" \"Mozilla/5.0\" \"-\"\n"); + + printf("\nBeispiele:\n"); + printf(" - Einzeldatei analysieren:\n"); + printf(" %s /var/log/nginx/access.log -i\n", binary); + printf(" - Verzeichnis mit mehreren .log / .log.N Dateien:\n"); + printf(" %s /var/log/nginx/ -i\n", binary); + + printf("\nCSV-Export (Timesketch-kompatibel):\n"); + printf(" Spalten: datetime, timestamp_desc, ip_address, method, url_path, status_code,\n"); + printf(" bytes_sent, user_agent, source_file, parsing_timestamp\n"); + + printf("\nHinweise & Einschränkungen:\n"); + printf(" - Parser erwartet das obige Standardformat. Abweichungen können zum Abbruch führen.\n"); + printf(" - Zeitzone aus dem Log wird gelesen, aber intern als einfache Felder verarbeitet.\n"); + printf(" - ATYPICAL-Methode: für fehlerhafte/binäre Requests innerhalb der \"request\"-Spalte.\n"); +} + + +int main(int argc, char* argv[]) { + if (argc <= 3) { + print_help(argv[0]); return 1; } - - int choice = 0; - int stats_show = 0; - while (choice != 4 && choice != -4) { - show_status(); - if (stats_show == 1) { - show_stats(); - int stats_show = 0; + + printf("\nNGINX EXAMINATOR\n"); + + allocate_initial_memory(); + + if (strcmp(argv[2], "-i") == 0) { + load_log_file(argv[1]); + + if (total_entries == 0) { + printf("Keine gültigen Log-Einträge gefunden. Überprüfen Sie den Pfad und die Dateiformate.\n"); + cleanup_memory(); + return 1; } - show_main_menu(); - - choice = read_menu_input(); - choice = handle_menu_shortcuts(choice); - - if (choice == -1) { - printf("FEHLER: Bitte geben Sie eine gültige Zahl oder Navigation ein (1-4, b, m, q)!\n"); - continue; + int choice = 0; + int stats_show = 0; + while (choice != 4 && choice != -4) { + show_status(); + if (stats_show == 1) { + show_stats(); + stats_show = 0; + } + show_main_menu(); + + choice = read_menu_input(); + choice = handle_menu_shortcuts(choice); + + if (choice == -1) { + printf("FEHLER: Bitte geben Sie eine gültige Zahl oder Navigation ein (1-4, b, m, q)!\n"); + continue; + } + + if (choice == 1) { + menu_filter_management(); + } else if (choice == 2) { + menu_show_entries(); + } else if (choice == 3) { + stats_show = 1; + } else if (choice == 4) { + printf("Programmende\n"); + break; + } else if (choice == -4) { + break; + } else { + printf("FEHLER: Ungültige Auswahl! Bitte wählen Sie 1-4 oder b/m/q.\n"); + } } - - if (choice == 1) { - menu_filter_management(); - } else if (choice == 2) { - menu_show_entries(); - } else if (choice == 3) { - stats_show =1; - } else if (choice == 4) { - printf("Programmende\n"); - break; - } else if (choice == -4) { - break; + } else if (strcmp(argv[2], "-e") == 0){ + load_log_file(argv[1]); + if (argc == 4) { + export_filtered_entries(argv[3]); } else { - printf("FEHLER: Ungültige Auswahl! Bitte wählen Sie 1-4 oder b/m/q.\n"); + export_filtered_entries(NULL); } + } else if (strcmp(argv[2], "-h") == 0){ + print_help(argv[0]); + return 1; } - cleanup_memory(); return 0; } \ No newline at end of file