// File: src/error_handler.c
#include "error_handler.h"
#include "logger.h"
#include "recovery.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

void handle_error(ErrorType type, const char *message, int error_code) {
    // Log the error
    printf("Error for debugging purpose: %s\n", message);
    log_error(type, message, error_code);

    pid_t pid = fork();
    if (pid == 0) {
        // Child process

        // Prepare arguments
        char type_str[10];
        char error_code_str[10];
        snprintf(type_str, sizeof(type_str), "%d", type);
        snprintf(error_code_str, sizeof(error_code_str), "%d", error_code);

        // Ensure the path to the Python script is correct.
        // Here, it's assumed that the executable is run from the project root.
        char *args[] = {
            "python3",
            "./dashboard/report_error.py", // Updated path to the Python script
            type_str,
            (char *)message,
            error_code_str,
            NULL
        };
        
        // Execute the Python script
        execvp("python3", args);
        
        // If execvp returns, an error occurred
        perror("execvp failed");
        exit(EXIT_FAILURE);
    } else if (pid < 0) {
        // Fork failed
        perror("fork failed");
        fprintf(stderr, "Failed to report error using Python script.\n");
    } else {
        // Parent process: wait for child if necessary
        int status;
        if (waitpid(pid, &status, 0) == -1) {
            perror("waitpid failed");
        } else {
            if (WIFEXITED(status)) {
                if (WEXITSTATUS(status) != 0) {
                    fprintf(stderr, "Python script exited with status %d.\n", WEXITSTATUS(status));
                }
            } else if (WIFSIGNALED(status)) {
                fprintf(stderr, "Python script terminated by signal %d.\n", WTERMSIG(status));
            } else {
                fprintf(stderr, "Python script did not terminate normally.\n");
            }
        }
    }

    // Attempt recovery
    recover_from_error(type);
}

// File: src/error_handler.h
#ifndef ERROR_HANDLER_H
#define ERROR_HANDLER_H

typedef enum {
    MEMORY_ERROR,
    FILE_ACCESS_ERROR,
    DEVICE_ERROR,
    NULL_ERROR,       // Added NULL_ERROR for null pointer dereference
    UNKNOWN_ERROR
} ErrorType;

// Function to handle errors
void handle_error(ErrorType type, const char *message, int error_code);

#endif // ERROR_HANDLER_H

// File: src/logger.c
#include "logger.h"
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <sys/stat.h>

#define LOG_FILE "logs/error_log.log"
#define MAX_LOG_SIZE 5242880 // 5MB

pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;

// Function to get current timestamp
const char* current_timestamp() {
    static char buffer[20];
    time_t now = time(NULL);
    struct tm *t = localtime(&now);
    strftime(buffer, sizeof(buffer)-1, "%Y-%m-%d %H:%M:%S", t);
    return buffer;
}

// Function to convert ErrorType to string
const char* error_type_to_string(ErrorType type) {
    switch(type) {
        case MEMORY_ERROR:
            return "MEMORY_ERROR";
        case FILE_ACCESS_ERROR:
            return "FILE_ACCESS_ERROR";
        case DEVICE_ERROR:
            return "DEVICE_ERROR";
        default:
            return "UNKNOWN_ERROR";
    }
}

// Function to rotate logs if needed
void rotate_logs_if_needed() {
    struct stat st;
    if (stat(LOG_FILE, &st) == 0) {
        if (st.st_size >= MAX_LOG_SIZE) {
            // Rename the current log file with a timestamp
            char new_name[256];
            time_t now = time(NULL);
            struct tm *t = localtime(&now);
            strftime(new_name, sizeof(new_name), "logs/error_log_%Y%m%d%H%M%S.log", t);
            rename(LOG_FILE, new_name);
        }
    }
}

void log_error(ErrorType type, const char *message, int error_code) {
    pthread_mutex_lock(&log_mutex);
    rotate_logs_if_needed();
    FILE *file = fopen(LOG_FILE, "a");
    if (!file) {
        pthread_mutex_unlock(&log_mutex);
        return;
    }

    fprintf(file, "[%s] %s: %s (Error Code: %d)\n", current_timestamp(), error_type_to_string(type), message, error_code);
    fclose(file);
    pthread_mutex_unlock(&log_mutex);
}

// File: src/logger.h
#ifndef LOGGER_H
#define LOGGER_H

#include "error_handler.h"

void log_error(ErrorType type, const char *message, int error_code);

#endif // LOGGER_H


// File: src/recovery.c
#include "recovery.h"
#include "logger.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// Define maximum retries and delay
#define MAX_RETRIES 3
#define RETRY_DELAY 2 // seconds

void recover_from_file_access_error(const char *filepath) {
    for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
        printf("Retrying file access (Attempt %d)...\n", attempt);
        FILE *file = fopen(filepath, "r");
        if (file != NULL) {
            printf("File accessed successfully on attempt %d.\n", attempt);
            fclose(file);
            return;
        }
        sleep(RETRY_DELAY);
    }
    printf("Failed to recover from FILE_ACCESS_ERROR after %d attempts.\n", MAX_RETRIES);
    // Further actions, like using fallback
}

void recover_from_error(ErrorType type) {
    switch(type) {
        case MEMORY_ERROR:
            printf("Attempting to recover from MEMORY_ERROR...\n");
            // Implement recovery logic, e.g., free memory, restart process
            // For demonstration, we'll simply exit
            exit(EXIT_FAILURE);
            break;
        case FILE_ACCESS_ERROR:
            printf("Attempting to recover from FILE_ACCESS_ERROR...\n");
            // Example: Retry file access
            recover_from_file_access_error("/path/to/nonexistent/file.txt");
            break;
        case DEVICE_ERROR:
            printf("Attempting to recover from DEVICE_ERROR...\n");
            // Attempt to reinitialize the device or switch to a backup
            // For demonstration, we'll log and continue
            break;
        default:
            printf("Unknown error type. Unable to recover.\n");
            exit(EXIT_FAILURE);
    }
}

// File: src/recovery.h
#ifndef RECOVERY_H
#define RECOVERY_H

#include "error_handler.h"

void recover_from_error(ErrorType type);

#endif // RECOVERY_H

#!/usr/bin/env python3
# src/report_error.py

import os
import smtplib
import sys
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

ERROR_TYPE_MAPPING = {
    "0": "MEMORY_ERROR",
    "1": "FILE_ACCESS_ERROR",
    "2": "DEVICE_ERROR",
    "3": "NULL_ERROR",
    "4": "UNKNOWN_ERROR"
}

def send_email(subject, body):
    # Configuration from environment variables
    SMTP_SERVER = os.getenv('SMTP_SERVER', 'smtp.gmail.com')
    SMTP_PORT = int(os.getenv('SMTP_PORT', '587'))
    SENDER_EMAIL = os.getenv('SENDER_EMAIL')
    SENDER_PASSWORD = os.getenv('SENDER_PASSWORD')
    RECIPIENT_EMAIL = os.getenv('RECIPIENT_EMAIL', 'b22cs004@iitj.ac.in')

    if not SENDER_EMAIL or not SENDER_PASSWORD:
        print("Sender email or password not set in environment variables.")
        return

    # Create the email content
    message = MIMEMultipart()
    message['From'] = SENDER_EMAIL
    message['To'] = RECIPIENT_EMAIL
    message['Subject'] = subject

    message.attach(MIMEText(body, 'plain'))

    try:
        # Connect to the SMTP server
        server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
        server.starttls()
        server.login(SENDER_EMAIL, SENDER_PASSWORD)

        # Send the email
        # server.send_message(message)
        server.quit()
        print("Email sent successfully.")
    except Exception as e:
        print(f"Failed to send email: {e}")

def main():
    if len(sys.argv) != 4:
        print("Usage: report_error.py <ERROR_TYPE> <MESSAGE> <ERROR_CODE>")
        sys.exit(1)

    error_type_num = sys.argv[1]
    message = sys.argv[2]
    error_code = sys.argv[3]

    error_type = ERROR_TYPE_MAPPING.get(error_type_num, "UNKNOWN_ERROR")

    subject = f"OS Error Report: {error_type}"
    body = f"An error of type {error_type} occurred.\nDetails: {message}\nError Code: {error_code}"

    send_email(subject, body)

if __name__ == "__main__":
    main()

# File: dashboard/app.py
from flask import Flask, render_template
import collections
import os

app = Flask(__name__)

LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../logs/error_log.log')

def read_logs():
    try:
        with open(LOG_FILE, 'r') as file:
            return file.readlines()
    except FileNotFoundError:
        return []

@app.route('/')
def index():
    logs = read_logs()[-100:]  # Display the last 100 log entries
    return render_template('index.html', logs=logs)

@app.route('/stats')
def stats():
    stats_counter = collections.Counter()
    logs = read_logs()
    for line in logs:
        if "MEMORY_ERROR" in line:
            stats_counter['MEMORY_ERROR'] += 1
        elif "FILE_ACCESS_ERROR" in line:
            stats_counter['FILE_ACCESS_ERROR'] += 1
        elif "DEVICE_ERROR" in line:
            stats_counter['DEVICE_ERROR'] += 1
        else:
            stats_counter['UNKNOWN_ERROR'] += 1
    return render_template('stats.html', stats=stats_counter)

if __name__ == '__main__':
    app.run(debug=True)


now everything goes smooth what i want is to store logs when i call build/simulate_memory_error <segfault/leak/null> so that it must be stored in logs/error_log.log files ....so just give me updated files that can doo that dont explain anything to me