NetworkInador/lib/handle.c

315 lines
8.9 KiB
C

/*
* main.c
* This file is part of Network-inador
*
* Copyright (C) 2024 - Félix Arreola Rodríguez
*
* Network-inador 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 2 of the License, or
* (at your option) any later version.
*
* Network-inador 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 Network-inador; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include "network-inador-private.h"
#include "netlink-events.h"
#include "interfaces.h"
#include "routes.h"
#include "resolv_manager.h"
struct _NetworkInadorWatchedPID {
unsigned int source;
pid_t child_pid;
NetworkInadorChildWatchFunc func;
void *data;
};
/* Variables si nosotros hacemos vigilancia de procesos */
static FList *network_inador_watched_childs = NULL;
static unsigned int network_inador_watched_child_source_counter = 1;
static int network_inador_pipes_child_notify[2] = {-1, -1};
static int network_inador_child_source_watcher = 0;
static unsigned int network_inador_add_watch_process (pid_t pid, NetworkInadorChildWatchFunc func, void *user_data) {
struct _NetworkInadorWatchedPID *watcher;
watcher = (struct _NetworkInadorWatchedPID *) malloc (sizeof (struct _NetworkInadorWatchedPID));
assert (watcher != NULL);
watcher->source = network_inador_watched_child_source_counter;
network_inador_watched_child_source_counter++;
watcher->child_pid = pid;
watcher->func = func;
watcher->data = user_data;
network_inador_watched_childs = f_list_append (network_inador_watched_childs, watcher);
return watcher->source;
}
static void network_inador_remove_watch_process (unsigned int source) {
/* Recorrer la lista para eliminar el watch, ignorar si no está en la lista */
struct _NetworkInadorWatchedPID *watcher;
FList *g, *next;
g = network_inador_watched_childs;
while (g != NULL) {
next = g->next;
watcher = (struct _NetworkInadorWatchedPID *) g->data;
if (watcher->source == source) {
network_inador_watched_childs = f_list_delete_link (network_inador_watched_childs, g);
free (watcher);
break;
}
g = next;
}
}
static void _network_inador_try_setup_route_events (NetworkInadorHandle *handle) {
struct nl_sock * sock_req;
sock_req = nl_socket_alloc ();
int fd;
if (nl_connect (sock_req, NETLINK_ROUTE) != 0) {
nl_socket_free (sock_req);
return;
}
nl_socket_set_nonblocking (sock_req);
nl_socket_disable_seq_check (sock_req);
fd = nl_socket_get_fd (sock_req);
/* Set close-on-exec */
fcntl (fd, F_SETFD, FD_CLOEXEC);
handle->route_events.nl_sock = sock_req;
handle->has_route_events = 1;
handle->route_events.source = handle->ops.input_add (fd, POLLIN, netlink_events_route_events_handle_read, handle);
nl_socket_add_memberships (sock_req, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_IFINFO, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0);
nl_socket_modify_cb (sock_req, NL_CB_VALID, NL_CB_CUSTOM, netlink_events_route_dispatcher, handle);
}
static void _network_inador_try_pipe_routes (NetworkInadorHandle *handle) {
int ret, flags;
int pipe_fds[2];
ret = pipe (pipe_fds);
if (ret < 0) {
return;
}
handle->pipe_routes[0] = pipe_fds[0];
handle->pipe_routes[1] = pipe_fds[1];
flags = fcntl (pipe_fds[0], F_GETFL);
fcntl (pipe_fds[0], F_SETFL, flags | O_NONBLOCK);
handle->source_pipe_routes = handle->ops.input_add (pipe_fds[0], POLLIN, netlink_events_pipe_route_handle_read, handle);
}
static void network_inador_signal_handler_child (int signum) {
//fprintf (stderr, "SIGCHLD session handler\n");
char byte = 1;
/* Avisar que un proceso hijo murió */
if (network_inador_pipes_child_notify[1] >= 0) {
write (network_inador_pipes_child_notify[1], &byte, 1);
}
}
static void network_inador_child_pipe_read (void *data, int fd, int condition) {
pid_t pid;
int status;
FList *g, *next;
char byte;
struct _NetworkInadorWatchedPID *watcher;
int n;
n = read (fd, &byte, 1);
if (n <= 0) return;
while ((pid = waitpid (-1, &status, WNOHANG)) > 0) {
g = network_inador_watched_childs;
fprintf (stderr, "El proceso hijo murió: %i\n", pid);
while (g != NULL) {
next = g->next;
watcher = (struct _NetworkInadorWatchedPID *) g->data;
if (watcher->child_pid == pid) {
/* Este es la vigilancia del proceso que buscamos */
watcher->func (watcher->data, pid, status);
/* Como ya murió el proceso, lo eliminamos de la lista de vigilancia */
network_inador_watched_childs = f_list_delete_link (network_inador_watched_childs, g);
free (watcher);
break;
}
g = next;
}
}
}
void network_inador_setup_child_handler (NetworkInadorHandle *handle) {
struct sigaction act;
sigset_t empty_mask;
if (handle->ops.input_add == NULL) {
/* No tenemos vigilancia de entrada, no podemos manejar las señales */
return;
}
if (pipe (network_inador_pipes_child_notify) != 0) {
return;
}
/* Queremos que estos pipes se cierren automáticamente */
fcntl (network_inador_pipes_child_notify[0], F_SETFD, FD_CLOEXEC);
fcntl (network_inador_pipes_child_notify[1], F_SETFD, FD_CLOEXEC);
/* Establecer un manejador de SIGCHLD para los procesos hijos que se generen */
sigemptyset (&empty_mask);
act.sa_mask = empty_mask;
act.sa_flags = 0;
act.sa_handler = &network_inador_signal_handler_child;
if (sigaction (SIGCHLD, &act, NULL) < 0) {
perror ("Failed to register SIGCHLD handler");
close (network_inador_pipes_child_notify[0]);
network_inador_pipes_child_notify[0] = -1;
close (network_inador_pipes_child_notify[1]);
network_inador_pipes_child_notify[1] = -1;
return;
}
network_inador_child_source_watcher = handle->ops.input_add (network_inador_pipes_child_notify[0], POLLIN, network_inador_child_pipe_read, handle);
handle->ops.process_watch = network_inador_add_watch_process;
handle->ops.process_unwatch = network_inador_remove_watch_process;
}
NetworkInadorHandle * network_inador_init_handle (struct NetworkInadorOps *network_inador_ops) {
NetworkInadorHandle *handle;
struct nl_sock * sock_req;
handle = (NetworkInadorHandle *) malloc (sizeof (NetworkInadorHandle));
assert (handle != NULL);
memset (handle, 0, sizeof (NetworkInadorHandle));
handle->pipe_routes[0] = -1;
handle->pipe_routes[1] = -1;
/* Copiar la información de eventos para vigilancia */
handle->ops = *network_inador_ops;
/* Crear el socket de peticiones */
sock_req = nl_socket_alloc ();
if (nl_connect (sock_req, NETLINK_ROUTE) != 0) {
perror ("Falló conectar netlink socket\n");
free (handle);
return NULL;
}
handle->nl_sock_route = sock_req;
/* Si tenemos una ops de configuración de eventos, entonces, configurar el otro socket de eventos async */
if (handle->ops.input_add != NULL) {
_network_inador_try_setup_route_events (handle);
_network_inador_try_pipe_routes (handle);
}
/* Inicializar las interfaces (y las direcciones IP) */
interfaces_init (handle);
/* Inicializar las rutas */
routes_init (handle);
/* Inicializar el resolv.conf */
resolv_manager_init (handle);
return handle;
}
void network_inador_destroy_handle (NetworkInadorHandle *handle) {
if (network_inador_pipes_child_notify[0] >= 0) {
if (handle->ops.input_remove != NULL) {
handle->ops.input_remove (network_inador_child_source_watcher);
network_inador_child_source_watcher = 0;
}
close (network_inador_pipes_child_notify[0]);
network_inador_pipes_child_notify[0] = -1;
}
if (network_inador_pipes_child_notify[1] >= 0) {
close (network_inador_pipes_child_notify[1]);
network_inador_pipes_child_notify[1] = -1;
}
if (handle->has_route_events) {
/* Quitar la vigilancia */
handle->ops.input_remove (handle->route_events.source);
handle->route_events.source = 0;
/* Cerrar el socket de eventos */
nl_socket_free (handle->route_events.nl_sock);
handle->route_events.nl_sock = NULL;
}
if (handle->source_pipe_routes != 0) {
handle->ops.input_remove (handle->source_pipe_routes);
handle->source_pipe_routes = 0;
}
if (handle->pipe_routes[0] >= 0) {
close (handle->pipe_routes[0]);
handle->pipe_routes[0] = -1;
}
if (handle->pipe_routes[1] >= 0) {
close (handle->pipe_routes[1]);
handle->pipe_routes[1] = -1;
}
/* TODO: Liberar las listas ligadas aquí */
routes_clean_up (handle);
interfaces_clean_up (handle);
nl_socket_free (handle->nl_sock_route);
handle->nl_sock_route = NULL;
free (handle);
}