/* Cleopatre project {{{ * * Copyright (C) 2013 MStar Semiconductor * * <<>> * * }}} */ /** * \file devkit/plcd/src/plcd_daemon.c * \ingroup plcd * * This PLC daemon is responsible for the initialization of * the AV / EoC stack: start, parameter sending, status watching. * It is also responsible for any configuration change from * the ethernet / plc sides through MME-s. * */ #include "plcd_daemon.h" #include "plcd_stack.h" #include "plcd_autoswitch.h" #include "plcd_files.h" #include "plcd_ctx.h" #include "libspid.h" #include /* for ifreq */ #include #include #include /* for getpid() */ #include #include "ioctl.h" #include "nvram_utils.h" /* "utest_override" include must be the last include. */ #include "utest_override.h" /* Interface name */ #define PLC_IFNAME "plc0" static plcd_nvram_t g_nvram; volatile sig_atomic_t is_process_signal_needed; volatile sig_atomic_t exit_requested; static int nvram_init (plcd_nvram_t *nvram) { int status = libspid_get_plc_address (&(nvram->plc_address), 6); status |= libspid_get_device_password (&(nvram->device_password), 32); status |= libspid_get_product_name (&(nvram->product_name), 64); status |= libspid_get_tonemask (&(nvram->tonemask), 192); return status; } /** * Handle signals reception. * * \param signal_nb signal identifier */ static void plcd_daemon_signal_handler (int signal_nb) { /* Check received signal. */ if (SIGHUP == signal_nb) is_process_signal_needed = 1; else if (SIGTERM == signal_nb) exit_requested = 1; } /** * Opposite function of plcd_daemon_open * * This function is called after a successful open, or not. * * \param ctx plcd context */ static void plcd_daemon_close (plcd_ctx_t *ctx) { PLCD_ASSERT (ctx); /* TODO: Unregister PID into plcdrv. */ /* Close the socket if opened. */ if (0 < ctx->plc_sock) { close (ctx->plc_sock); ctx->plc_sock = 0; } /* Ignore SIGTERM now. */ signal (SIGTERM, SIG_IGN); } /** * Open socket to plcdrv, set signals, etc... * * \param ctx plcd context * \return -1 on error, 0 otherwise */ static int plcd_daemon_open (plcd_ctx_t *ctx) { struct plcdrv_setpid user_data; struct ifreq ifr; enum { PLCD_DAEMON_OPEN__SUCCESS, PLCD_DAEMON_OPEN__SOCKET_FAIL, PLCD_DAEMON_OPEN__BIND_FAIL, PLCD_DAEMON_OPEN__IOCTL_FAIL, PLCD_DAEMON_OPEN__NVRAM_FAIL, } result; PLCD_ASSERT (ctx); /* Bind signal before send PID to plcdrv. */ signal (SIGTERM, plcd_daemon_signal_handler); memset (&user_data, 0, sizeof (struct plcdrv_setpid)); memset (&ifr, 0, sizeof (struct ifreq)); result = PLCD_DAEMON_OPEN__SUCCESS; /* open netlink socket */ ctx->plc_sock = socket (PF_NETLINK, SOCK_RAW, NETLINK_PLC_DRV); if (0 < ctx->plc_sock) { ctx->plcd_addr.nl_family = AF_NETLINK; ctx->plcd_addr.nl_pid = ctx->pid; ctx->plcd_addr.nl_groups = 0; /* not in mcast group */ if (0 <= bind (ctx->plc_sock, (struct sockaddr *) &ctx->plcd_addr, sizeof (ctx->plcd_addr))) { /* Set plcd pid for reception on drv netlink */ user_data.nl = NETLINK_PLC_DRV; user_data.pid = ctx->pid; ifr.ifr_data = (void *) &user_data; strncpy (ifr.ifr_name, PLC_IFNAME, IFNAMSIZ); if (0 <= ioctl (ctx->plc_sock, PLCDRV_IOCTL_SETPID, &ifr)) { ctx->nvram = &g_nvram; if (nvram_init (ctx->nvram)) { syslog (LOG_ERR, "libspid system get nvram failed"); result = PLCD_DAEMON_OPEN__NVRAM_FAIL; } } else { syslog (LOG_ERR, "ioctl set PID failed (%s)", strerror (errno)); result = PLCD_DAEMON_OPEN__IOCTL_FAIL; } } else { syslog (LOG_ERR, "socket bind failed"); result = PLCD_DAEMON_OPEN__BIND_FAIL; } } else { syslog (LOG_ERR, "socket open failed"); result = PLCD_DAEMON_OPEN__SOCKET_FAIL; } if (result == PLCD_DAEMON_OPEN__SUCCESS) return 0; else { if (result > PLCD_DAEMON_OPEN__IOCTL_FAIL) { /* TODO: Unregister the plcd into plcdrv */ } if (result > PLCD_DAEMON_OPEN__SOCKET_FAIL) { close (ctx->plc_sock); ctx->plc_sock = 0; } return -1; } } /** * PLC daemon. * * \param ctx plcd context */ void plcd_daemon_main (plcd_ctx_t *ctx) { PLCD_ASSERT (ctx); if (0 > plcd_files_read_hpavconf (ctx) || 0 > plcd_files_write_default_hpavinfo (ctx)) return; int init_nb, init_ret = -1; is_process_signal_needed = 0; if (0 <= plcd_daemon_open (ctx)) { /* Show PLCD version. */ syslog (LOG_NOTICE, "PLC Daemon (%s) Running\n", VERSION); /* initialize the AV / EoC stack * and loop (PLCD_INIT_RETRIES maximum) until successful init */ for (init_nb = 0; init_nb < PLCD_INIT_RETRIES; init_nb++) { if (0 == (init_ret = plcd_stack_init (ctx))) { break; } } if (0 != init_ret) syslog (LOG_ERR, "stack init failed\n"); else { if (LIBSPID_SUCCESS != libspid_system_file_update_register ( ctx->pid, LIBSPID_HPAV_CONF_PATH, plcd_daemon_signal_handler)) syslog (LOG_ERR, "libspid register hpav.conf failed\n"); else { if (LIBSPID_SUCCESS != libspid_system_file_update_register ( ctx->pid, LIBSPID_HPAV_INFO_PATH, plcd_daemon_signal_handler)) syslog (LOG_ERR, "libspid register hpav.info failed\n"); else { if (LIBSPID_SUCCESS != libspid_system_file_update_register ( ctx->pid, LIBSPID_MULTICAST_INFO_PATH, plcd_daemon_signal_handler)) syslog (LOG_ERR, "libspid register multicast.info failed\n"); else { /* Initialize autoswitch timer. */ plcd_autoswitch_timer_init (); /* now get all events from plcdrv (i.e. DRV MME-s) */ while (!exit_requested) { if (0 > plcd_stack_event_dispatch (ctx)) { syslog (LOG_ERR, "plcd_stack_event_dispatch failed\n"); break; } } libspid_system_file_update_unregister ( ctx->pid, LIBSPID_MULTICAST_INFO_PATH); } libspid_system_file_update_unregister ( ctx->pid, LIBSPID_HPAV_CONF_PATH); } libspid_system_file_update_unregister ( ctx->pid, LIBSPID_HPAV_INFO_PATH); } /* XXX no plcd_stack_unit. */ } plcd_daemon_close (ctx); } }