 /*
  * main.c - Monitor-netlink
  * 
  * This file makes use of the following monitor-netlink example:
  * 
  * https://github.com/lihongguang/monitor-netlink
  * 
  * All modifications to the original source file are:
  * Copyright (C) 2026 Aitor Cuadrado Zubizarreta <aitor@genuen.org>
  * 
  * simple-netaid is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  * 
  * simple-netaid is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  * See the GNU General Public License for more details.
  * 
  * You should have received a copy of the GNU General Public License along
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  * 
  * See the COPYING file.
  */
   
  
#include <stdio.h>
#include <stdlib.h> 
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>   // dirname() and basename()
#include <time.h>
#include <signal.h>
#include <memory.h>
#include <net/if.h>
#include <asm/types.h>
#include <arpa/inet.h>
#include <sys/un.h>      /*  UNIX socket  */
#include <sys/types.h>  
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <netinet/in.h>
#include <assert.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <ctype.h>      // isdigit()

#include <sys/wait.h>

#include <simple-netaid/iproute.h>
#include <simple-netaid/interfaces.h>
#include <simple-netaid/ethlink.h>

#include "ini.h"
#include "def.h"
#include "notify.h"
#include "config.h"
#include "util.h"
#include "policy.h"
#include "netlink_monitor.h"

#define MAX_SIZE 1024

int DEBUG = 1;

int _DEBUG_MESSAGES = 0;
int _INFO_MESSAGES = 0;
int _WARN_MESSAGES = 1;
int _ERROR_MESSAGES = 1; 

// Global
char *progname = NULL;

config_t *m_config = NULL;
const char *libpath;
char arg0[512] = {0};
int pid_fd = -1;      // lock file
struct ubus_context *ctx;
const char *ubus_socket = "/var/run/ubus/ubus.sock";
const char *default_lockfile = "/run/snetaid/snetaid.lock";

static volatile sig_atomic_t shutdown_requested = 0;

static void periodic_cb(struct uloop_timeout *t)
{
    const char *curr_ifname = iproute();

    if (shutdown_requested) {
        uloop_end();
        return;
    }

    if (curr_ifname && curr_ifname[0] != '\0') {
		uloop_timeout_cancel(t);
		return;
	} else if (m_config->wired_interface && m_config->wired_interface[0] != '\0') {
		interface_up(m_config->wired_interface);
		if (ethlink(m_config->wired_interface) && 
			get_interface_status(m_config->wired_interface)) {
				char *const up_args[] = { "ifup", m_config->wired_interface, NULL };
				safe_execute("/dev/null", up_args);
		}
    }
	uloop_timeout_set(t, 1000);
}

static void signal_handler(int sig)
{
    (void)sig;
    shutdown_requested = 1;
}

int main(int argc, char **argv)
{ 
    int rc = 0; 
    char *cad = NULL;
    char *lockdir, *tmp;
	struct uloop_timeout periodic_timer;
	const char *curr_ifname = NULL;

    if ((cad = strrchr(argv[0], '/')))
        cad++;
    else
        cad = argv[0];

    progname = strdup(cad);
    if (!progname) {
        fprintf(stderr, "Out of memory\n");
        return EXIT_FAILURE;
    }

    openlog(progname, LOG_PID|LOG_CONS, LOG_DAEMON);
    log_info("Starting service %s...", progname);
    
    signal(SIGINT, signal_handler);    
    signal(SIGTERM, signal_handler);
         
    m_config = (config_t*)calloc(sizeof(config_t), 1);
    if (m_config == NULL)
        return -ENOMEM;
   
    if (config_init(m_config) != 0) {
        log_error("config_init failed: %m");
        exit(EXIT_FAILURE);
    }
   
    if(config_load_from_args(m_config, argc, argv) != 0) {
        config_usage(argv[0]);
        goto cleanup;
    }

    if (m_config->config_path) {
        int cfg_rc = config_load(m_config->config_path, m_config);
        if (cfg_rc != 0 && cfg_rc != -ENOENT)
            log_warn("Could not load config file %s: %d",
                    m_config->config_path, cfg_rc);
    } else {
        int cfg_rc = config_load(DEFAULT_CONFIG_FILE, m_config);
        if (cfg_rc != 0 && cfg_rc != -ENOENT)
            log_warn("Could not load default config %s: %d",
                    DEFAULT_CONFIG_FILE, cfg_rc);
    }

    if(config_load_from_args(m_config, argc, argv) != 0) {
        config_usage(argv[0]);
        goto cleanup;
    }

    if (!m_config->lockfile_path)
        m_config->lockfile_path = strdup(default_lockfile);

    if (m_config->action_kill) {
        if (!m_config->pidfile_path) {
            fprintf(stderr, "Error: pidfile (-p) is required\n");
            rc = EXIT_FAILURE;
            goto cleanup;
        }
        rc = (wait_on_kill(SIGTERM, 5) == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
        goto cleanup;
    }

    if (m_config->action_check) {
        pid_t pid = get_pid_t();

        if (pid == (pid_t)-1 || pid == 0)
            rc = EXIT_FAILURE;
        else {
            printf("%s process running as pid %u.\n", progname, (unsigned int)pid);
            rc = EXIT_SUCCESS;
        }
        goto cleanup;
    }
    
    lockdir = strdup(m_config->lockfile_path);
    tmp = strrchr(lockdir, '/');
    if (tmp) {
        *tmp = '\0';
        mkdir(lockdir, 0755);
    }
    free(lockdir);  

    // Daemonize
    if (!m_config->foreground) {
        daemonize();
    }
            
    uloop_init();
    netaid_ui_notify_init();

    ctx = ubus_connect(ubus_socket);
    if (!ctx) {
        log_error("Failed to connect to ubusd: %m");
        goto cleanup;
    }
 
    ubus_add_uloop(ctx);
        
    if (ubus_add_object(ctx, &netaid_obj) != 0) {
        log_error("Ubus object registration failure: %m"); 
        goto cleanup;
    }
    
    curr_ifname = iproute();
    
    if (!curr_ifname || curr_ifname[0] == '\0') {
		periodic_timer.cb = periodic_cb;
		uloop_timeout_set(&periodic_timer, 1000);
	}

    log_info("Starting network engine...");
    if (kernel_init() < 0) {
        log_error("Netlink monitor failed to initialize");
        goto cleanup;
    }

    uloop_run();
    
    kernel_finish();

    netaid_ui_notify_fini();
    uloop_done();

cleanup:
    if (ctx)
        ubus_free(ctx);
    
    if (progname)
        free(progname);
    
    if (m_config && m_config->pidfile_path && access(m_config->pidfile_path, F_OK) == 0 ) {
        if (pid_fd != -1) {
            (void)!lockf(pid_fd, F_ULOCK, 0);
            close(pid_fd);
        }
        if (m_config->pidfile_path)
            unlink(m_config->pidfile_path);
    }
    
    if (m_config) {
        config_free(m_config); 
        free(m_config);
    }
    
    closelog();

    return rc;   
}
