Commit 23828c6a authored by Lucas Russo's avatar Lucas Russo

dev_mngr: add Device Manager subsytem

This was not commited as the .gitignore
file was wrong
parent 03f54145
/*
* Copyright (C) 2014 LNLS (www.lnls.br)
* Author: Lucas Russo <lucas.russo@lnls.br>
*
* Released according to the GNU LGPL, version 3 or any later version.
*/
#include <unistd.h>
#include <signal.h> /* sigaction */
//#include <asm/siginfo.h> /* siginfo_t */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h> /* perror */
#include <sys/types.h>
#include <sys/wait.h> /* waitpid */
#include <stdarg.h>
#include <sys/stat.h> /* chmod */
#include "dev_mngr.h"
#include "debug_print.h"
#define DFLT_BIND_FOLDER "/tmp/bpm"
#define DFLT_BIND_ADDR "0"
#define IPC_FILE_PERM 0777
/* Global variable for testing an asynchronous event */
volatile sig_atomic_t __new_dev = 0;
volatile sig_atomic_t __dev_nums = 0;
/* This signal handler emulates some device being plugged */
static void dmngr_sigusr1_h (int sig, siginfo_t *siginfo, void *context)
{
(void) sig;
(void) context;
(void) siginfo;
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Recv'ed signal: %d\n",
siginfo->si_signo);
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] New PCIe device detected!\n");
/* Simplistic, inefficient method of handling a new_dev */
__new_dev = 1;
__dev_nums++;
}
int dmngr_wait_chld_f (void)
{
int chld_status;
pid_t chld_pid = waitpid (-1, &chld_status, WNOHANG);
/* Error or no child exists */
if (chld_pid == (pid_t) -1) {
/* Not actually an error if ECHILD. Do nothing... */
if (errno == ECHILD) {
/* DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_INFO, "[dev_mngr] no child to wait for\n"); */
return 0;
}
return -1;
}
/* Child exists but have not changed its state */
if (chld_pid == (pid_t) 0) {
/* DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_INFO, "[dev_mngr] Child has not changed its state\n"); */
return 0;
}
/* Child exists and has changed its state. Check fior the return status */
if (WIFEXITED (chld_status)) {
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_WARN, "[dev_mngr] Child exited%s with status %d\n",
WCOREDUMP(chld_status) ? " and dumped core" : "",
WEXITSTATUS(chld_status));
__dev_nums--;
}
if (WIFSTOPPED (chld_status)) {
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_WARN, "[dev_mngr] Child stopped by signal %d\n",
WSTOPSIG(chld_status));
}
if (WIFSIGNALED (chld_status)) {
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_WARN, "[dev_mngr] Child signalled by signal %d\n",
WTERMSIG(chld_status));
__dev_nums--;
}
return 0;
}
int dmngr_spawn_chld_f (const char *program, char *const argv[])
{
/* Fill the devio strucutre and create a new process
* in charge of handling it */
pid_t child = fork ();
if (child == -1) {
perror ("[dev_mngr] fork");
/* What to do in case of error? retry ? */
return -1;
}
else if (child == 0) { /* Child */
int err = execv (program, argv);
if (err < 0) {
perror ("[dev_mngr] execl");
return -1;
}
}
else { /* Parent */
}
return 0; /* Success */
}
void print_help (char *program_name)
{
printf( "Usage: %s [options]\n"
"\t-h This help message\n"
"\t-d Daemon mode.\n"
"\t-v Verbose output\n"
"\t-b <broker_endpoint> Broker endpoint\n", program_name);
}
int main (int argc, char *argv[])
{
int verbose = 0;
int daemonize = 0;
char *broker_endp = NULL;
char **str_p;
int i;
if (argc < 2) {
print_help (argv[0]);
exit (1);
}
/* FIXME: This is rather buggy! */
/* Simple handling of command-line options. This should be done
* with getopt, for instance*/
for (i = 1; i < argc; i++)
{
if (streq(argv[i], "-v")) {
verbose = 1;
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Verbose mode set\n");
}
else if (streq(argv[i], "-d")) {
daemonize = 1;
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Demonize mode set\n");
}
else if (streq(argv[i], "-h"))
{
print_help (argv [0]);
exit (1);
}
else if (streq (argv[i], "-b")) {
str_p = &broker_endp;
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Going to set broker_endp parameter\n");
}
/* Fallout for options with parameters */
else {
*str_p = strdup (argv[i]);
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Parameter set to \"%s\"\n", *str_p);
}
}
/* Set default broker address */
if (broker_endp == NULL) {
broker_endp = DFLT_BIND_FOLDER"/"DFLT_BIND_ADDR;
}
/* Daemonize dev_mngr */
if (daemonize != 0) {
int rc = daemon(0, 0);
if (rc != 0) {
perror ("[dev_mngr] daemon");
goto err_daemonize;
}
}
/* See the fake promiscuous endpoint tcp*:*. To be changed soon! */
dmngr_t *dmngr = dmngr_new ("dev_mngr", "tcp://*:*", verbose);
if (dmngr == NULL) {
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_FATAL, "[dev_mngr] Fail to allocate dev_mngr instance\n");
goto err_dmngr_alloc;
}
dmngr_sig_handler_t dmngr_sigusr1_handler =
{.signal = SIGUSR1,
.dmngr_sig_h = dmngr_sigusr1_h};
/* dmngr_sigusr1_handler_t dmngr_sigkill_handler =
{.signal = SIGKILL,
.dmngr_sig_h = dmngr_sigusr1_handler};
dmngr_sigusr1_handler_t dmngr_sigterm_handler =
{.signal = SIGTERM,
.dmngr_sig_h = dmngr_sigusr1_handler}; */
dmngr_err_e err = dmngr_set_sig_handler (dmngr, &dmngr_sigusr1_handler);
if (err != DMNGR_SUCCESS) {
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_FATAL, "[dev_mngr] dmngr_set_sig_handler error!\n");
goto err_exit;
}
dmngr_set_wait_clhd_handler (dmngr, &dmngr_wait_chld_f);
dmngr_set_spawn_clhd_handler (dmngr, &dmngr_spawn_chld_f);
err = dmngr_register_sig_handlers (dmngr);
if (err != DMNGR_SUCCESS) {
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_FATAL, "[dev_mngr] dmngr_register_sig_handler error!\n");
goto err_exit;
}
/* Here we should monitor the plug of new devices, such as
* PCIe devices, by means of a udev event (or some other) and
* Ethernet ones, by using a discovery protocol based on zeroMQ
* (zbeacon should provide a sufficient infrastrucutre for that)
*/
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Waiting for new device\n");
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] PID: %d\n", getpid());
/* Do until a C^c is pressed (daemon mode unset) or SIGTERM signal arrives */
while (!zctx_interrupted) {
/* DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] ., PID: %d\n", getpid()); */
if (__new_dev) {
__new_dev = 0;
/* Verify if the Broker is running. If not, spawn it */
if (!dmngr_is_broker_running (dmngr)) {
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Spawing Broker\n");
char *argv_exec[] = {"mdp_broker", broker_endp, NULL};
/* char *argv_exec[] = {"mdp_broker", "-v", NULL}; */
int spawn_err = dmngr_spawn_chld (dmngr, "mdp_broker", argv_exec);
/* Just fail miserably, for now */
if (spawn_err < 0) {
perror ("spwan");
goto err_exit;
}
}
/* Argument options are "process name", "device type" and
*"dev entry" */
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE, "[dev_mngr] Spawing DEVIO worker\n");
char *argv_exec[] = {"dev_io", "-t", "pcie", "-e", "/dev/fpga0",
"-b", broker_endp, NULL};
int spawn_err = dmngr_spawn_chld (dmngr, "./dev_io", argv_exec);
/* Just fail miserably, for now */
if (spawn_err < 0) {
perror ("spwan");
goto err_exit;
}
}
/* Check the status of child processes */
/* DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_TRACE,
* "[dev_mngr] PID: %d will wait for child. # = %d\n", getpid(), __dev_nums); */
err = dmngr_wait_chld (dmngr);
if (err != DMNGR_SUCCESS) {
//perror("[dev_mngr] waitpid");
goto err_exit;
exit (1);
}
/* Do some monitoring activities */
sleep(1);
}
err_exit:
dmngr_destroy (&dmngr);
err_dmngr_alloc:
err_daemonize:
str_p = &broker_endp;
free (*str_p);
broker_endp = NULL;
return 0;
}
/*
* Copyright (C) 2014 LNLS (www.lnls.br)
* Author: Lucas Russo <lucas.russo@lnls.br>
*
* Released according to the GNU LGPL, version 3 or any later version.
*/
#ifndef _DEV_MNGR_H_
#define _DEV_MNGR_H_
#include "dev_mngr_core.h"
#endif
dev_mngr_DIR = hal/dev_mngr
# Here we call <output_name>_core_OBJS as we need to add
# more objects to this target. This is done in the hal.mk
# makefile
dev_mngr_core_OBJS = $(dev_mngr_DIR)/dev_mngr.o \
$(dev_mngr_DIR)/dev_mngr_core.o \
$(dev_mngr_DIR)/dev_mngr_err.o
dev_mngr_INCLUDE_DIRS = $(dev_mngr_DIR)
dev_mngr_OUT = dev_mngr
/*
* Copyright (C) 2014 LNLS (www.lnls.br)
* Author: Lucas Russo <lucas.russo@lnls.br>
*
* Released according to the GNU LGPL, version 3 or any later version.
*/
#include <string.h>
#include "dev_mngr_core.h"
#include "hal_assert.h"
/* Some global variables for use with signal handlers */
extern volatile sig_atomic_t __dev_nums;
/* Undef ASSERT_ALLOC to avoid conflicting with other ASSERT_ALLOC */
#ifdef ASSERT_TEST
#undef ASSERT_TEST
#endif
#define ASSERT_TEST(test_boolean, err_str, err_goto_label) \
ASSERT_HAL_TEST(test_boolean, DEV_MNGR, "dev_mngr_core",\
err_str, err_goto_label)
#ifdef ASSERT_ALLOC
#undef ASSERT_ALLOC
#endif
#define ASSERT_ALLOC(ptr, err_goto_label) \
ASSERT_HAL_ALLOC(ptr, DEV_MNGR, "dev_mngr_core", \
dmngr_err_str(DMNGR_ERR_ALLOC), \
err_goto_label)
#ifdef CHECK_ERR
#undef CHECK_ERR
#endif
#define CHECK_ERR(err, err_type) \
CHECK_HAL_ERR(err, DEV_MNGR, "dev_mngr_core", \
dmngr_err_str (err_type))
/* Creates a new instance of the Device Manager */
dmngr_t * dmngr_new (char *name, char *endpoint, int verbose)
{
assert (name);
assert (endpoint);
dmngr_t *self = (dmngr_t *) zmalloc (sizeof *self);
ASSERT_ALLOC(self, err_self_alloc);
/* Initialize the Device Manager */
self->ctx = zctx_new ();
ASSERT_ALLOC(self->ctx, err_ctx_alloc);
/* Create Dealer for use with zbeacon */
self->dealer = zsocket_new (self->ctx, ZMQ_DEALER);
ASSERT_ALLOC(self->dealer, err_dealer_alloc);
zsocket_bind (self->dealer, endpoint);
self->name = strdup (name);
ASSERT_ALLOC(self->name, err_name_alloc);
self->endpoint = strdup (endpoint);
ASSERT_ALLOC(self->endpoint, err_endpoint_alloc);
self->verbose = verbose;
self->ops = (dmngr_ops_t *) zmalloc (sizeof *self->ops);
ASSERT_ALLOC(self->ops, err_ops_alloc);
self->ops->sig_ops = zlist_new ();
ASSERT_ALLOC(self->ops->sig_ops, err_list_alloc);
self->broker_running = false;
return self;
err_list_alloc:
free (self->ops);
err_ops_alloc:
free (self->endpoint);
err_endpoint_alloc:
free (self->name);
err_name_alloc:
zsocket_destroy (self->ctx, self->dealer);
err_dealer_alloc:
zctx_destroy (&self->ctx);
err_ctx_alloc:
free (self);
err_self_alloc:
return NULL;
}
/* Destroy an instance of the Device Manager */
dmngr_err_e dmngr_destroy (dmngr_t **self_p)
{
assert (self_p);
if (*self_p) {
dmngr_t *self = *self_p;
/* Starting destructing by the last resource */
zlist_destroy (&self->ops->sig_ops);
free (self->ops);
free (self->endpoint);
free (self->name);
zsocket_destroy (self->ctx, self->dealer);
zctx_destroy (&self->ctx);
free (self);
*self_p = NULL;
}
return DMNGR_SUCCESS;
}
dmngr_err_e dmngr_set_sig_handler (dmngr_t *self, dmngr_sig_handler_t *sig_handler)
{
assert (self);
int err = zlist_append (self->ops->sig_ops, sig_handler);
return (err == -1) ? DMNGR_ERR_ALLOC : DMNGR_SUCCESS;
}
dmngr_err_e dmngr_register_sig_handlers (dmngr_t *self)
{
assert (self);
dmngr_sig_handler_t *sig_handler =
(dmngr_sig_handler_t *) zlist_first (self->ops->sig_ops);
/* Register all signal handlers in list*/
while (sig_handler) {
struct sigaction act;
memset (&act, 0, sizeof(act));
act.sa_sigaction = sig_handler->dmngr_sig_h;
act.sa_flags = SA_SIGINFO;
int err = sigaction (sig_handler->signal, &act, NULL);
CHECK_ERR(err, DMNGR_ERR_SIGACTION);
DBE_DEBUG (DBG_DEV_MNGR | DBG_LVL_INFO, "[dev_mngr_core] registered signal %d\n", sig_handler->signal);
sig_handler = (dmngr_sig_handler_t *)
zlist_next (self->ops->sig_ops);
}
return DMNGR_SUCCESS;
}
/* Declare wrapper for all DMNGR functions API */
#define DMNGR_FUNC_WRAPPER(func_name, ops_err, ...) \
{ \
assert (self); \
CHECK_ERR(((self->ops->func_name == NULL) ? -1 : 0),\
DMNGR_ERR_FUNC_NOT_IMPL); \
int err = self->ops->func_name (##__VA_ARGS__); \
CHECK_ERR (err, ops_err); \
return DMNGR_SUCCESS; \
}
dmngr_err_e dmngr_set_wait_clhd_handler (dmngr_t *self, wait_chld_handler_fp fp)
{
assert (self);
self->ops->dmngr_wait_chld = fp;
return DMNGR_SUCCESS;
}
dmngr_err_e dmngr_wait_chld (dmngr_t *self)
DMNGR_FUNC_WRAPPER(dmngr_wait_chld, DMNGR_ERR_WAITCHLD)
/*{
assert (self);
int err = self->ops->dmngr_wait_chld ();
CHECK_ERR (err, DMNGR_ERR_WAITCHLD);
return DMNGR_SUCCESS;
}*/
/* FIXME: Use the above wrapper!!!! */
dmngr_err_e dmngr_set_spawn_clhd_handler (dmngr_t *self, spawn_chld_handler_fp fp)
{
assert (self);
self->ops->dmngr_spawn_chld = fp;
return DMNGR_SUCCESS;
}
dmngr_err_e dmngr_spawn_chld (dmngr_t *self, const char *program, char *const argv[])
{
assert (self);
int err = self->ops->dmngr_spawn_chld (program, argv);
CHECK_ERR (err, DMNGR_ERR_SPAWNCHLD);
return DMNGR_SUCCESS;
}
/* Execute function to spawn a broker process */
dmngr_err_e dmngr_spawn_broker (dmngr_t *self, const char *program, char *const argv[])
{
assert (self);
if (self->broker_running) {
return DMNGR_ERR_BROK_RUNN;
}
int err = self->ops->dmngr_spawn_chld (program, argv);
CHECK_ERR (err, DMNGR_ERR_SPAWNCHLD);
return DMNGR_SUCCESS;
}
dmngr_err_e dmngr_set_ops (dmngr_t *self, dmngr_ops_t *dmngr_ops)
{
assert (self);
assert (dmngr_ops);
self->ops = dmngr_ops;
return DMNGR_SUCCESS;
}
bool dmngr_is_broker_running (dmngr_t *self)
{
assert (self);
return self->broker_running;
}
/*
* Copyright (C) 2014 LNLS (www.lnls.br)
* Author: Lucas Russo <lucas.russo@lnls.br>
*
* Released according to the GNU LGPL, version 3 or any later version.
*/
#ifndef _DEV_MNGR_CORE_H_
#define _DEV_MNGR_CORE_H_
#include "czmq.h"
#include "dev_mngr_err.h"
/* Signal handler function pointer */
typedef void (*sig_handler_fp)(int sig, siginfo_t *siginfo, void *context);
/* Wait child handler function pointer */
typedef int (*wait_chld_handler_fp)(void);
/* Spawn child handler function pointer */
typedef int (*spawn_chld_handler_fp)(const char *program, char *const argv[]);
/* Spawn broker handler function pointer */
typedef int (*spawn_broker_handler_fp)(const char *program, char *const argv[]);
/* Node of sig_ops list */
struct _dmngr_sig_handler_t {
int signal; /* Signal identifier, e.g., SIGINT, SIGKILL, etc... */
sig_handler_fp dmngr_sig_h;
};
struct _dmngr_ops_t {
wait_chld_handler_fp dmngr_wait_chld; /* Called to wait a all child process */
spawn_chld_handler_fp dmngr_spawn_chld; /* Called to spawn a new process to handle device */
spawn_broker_handler_fp dmngr_spawn_broker; /* Called to spawn (or respawn a zeroMQ broker */
/* List of dmngr_sig_handler_t */
zlist_t *sig_ops;
};
struct _dmngr_t {
/* General information */
zctx_t *ctx; /* zeroMQ context */
void *dealer; /* zeroMQ Dealer socket */
char *name; /* Identification of this dmngr instance */
char *endpoint; /* Endpoint to connect to */
int verbose; /* Print activity to stdout */
/* General management operations */
struct _dmngr_ops_t *ops;
/* zeroMQ broker management */
bool broker_running; /* true if broker is already running */
/* Device managment */
};
/* Opaque class signal handler structure */
typedef struct _dmngr_sig_handler_t dmngr_sig_handler_t;
/* Opaque operations handler structure */
typedef struct _dmngr_ops_t dmngr_ops_t;
/* Opaque class structure */
typedef struct _dmngr_t dmngr_t;
/***************** Our methods *****************/
/* Creates a new instance of the Device Manager */
dmngr_t * dmngr_new (char *name, char * endpoint, int verbose);
/* Destroy an instance of the Device Manager */
dmngr_err_e dmngr_destroy (dmngr_t **self_p);
/* Register signals to Device Manager instance */
dmngr_err_e dmngr_set_sig_handler (dmngr_t *self, dmngr_sig_handler_t *sig_handler);
/* Register all signal handlers previously set */
dmngr_err_e dmngr_register_sig_handlers (dmngr_t *self);
/* Register function to wait a all child process */
dmngr_err_e dmngr_set_wait_clhd_handler (dmngr_t *self, wait_chld_handler_fp fp);
/* Execute function to wait a all child process */
dmngr_err_e dmngr_wait_chld (dmngr_t *self);
/* Register function to spawn a all child process */
dmngr_err_e dmngr_set_spawn_clhd_handler (dmngr_t *self, spawn_chld_handler_fp fp);
/* Execute function to spawn a all child process */
dmngr_err_e dmngr_spawn_chld (dmngr_t *self, const char *program, char *const argv[]);
/* Execute function to spawn a broker process */
dmngr_err_e dmngr_spawn_broker (dmngr_t *self, const char *program, char *const argv[]);
/* Setting all operations at once */
dmngr_err_e dmngr_set_ops (dmngr_t *self, dmngr_ops_t *dmngr_ops);
/* Is broker Running? */
bool dmngr_is_broker_running (dmngr_t *self);
#endif
/*
* Copyright (C) 2014 LNLS (www.lnls.br)
* Author: Lucas Russo <lucas.russo@lnls.br>
*
* Released according to the GNU LGPL, version 3 or any later version.
*/
/* Error definitions and output stringification based on the work available
* at the libsllp project repository: https://github.com/brunoseivam/libsllp */
#include "dev_mngr_err.h"
static const char *dmngr_err [DMNGR_ERR_END] =
{
[DMNGR_SUCCESS] = "Success",
[DMNGR_ERR_ALLOC] = "Could not allocate memory",
[DMNGR_ERR_FUNC_NOT_IMPL] = "Function not implemented",
[DMNGR_ERR_SIGACTION] = "Signal registration error",
[DMNGR_ERR_WAITCHLD] = "Could not complete wait child routine",
[DMNGR_ERR_SPAWNCHLD] = "Could not complete spawn child routine",
[DMNGR_ERR_BROK_RUNN] = "Broker already running"
};
/* Convert enumeration type to string */
const char * dmngr_err_str (dmngr_err_e err)
{
return dmngr_err [err];
}
/*
* Copyright (C) 2014 LNLS (www.lnls.br)
* Author: Lucas Russo <lucas.russo@lnls.br>
*
* Released according to the GNU LGPL, version 3 or any later version.
*/
/* Error definitions and output stringification based on the work available
* at the libsllp project repository: https://github.com/brunoseivam/libsllp */
#ifndef _DEV_MNGR_CORE_ERR_H_
#define _DEV_MNGR_CORE_ERR_H_
enum _dmngr_err_e
{
DMNGR_SUCCESS = 0, /* No error */
DMNGR_ERR_ALLOC, /* Could not allocate memory */
DMNGR_ERR_FUNC_NOT_IMPL, /* Function not implemented */
DMNGR_ERR_SIGACTION, /* Could not register signal */
DMNGR_ERR_WAITCHLD, /* Wait child routine error */
DMNGR_ERR_SPAWNCHLD, /* Spawn child routine error */
DMNGR_ERR_BROK_RUNN, /* Broker already running error */
DMNGR_ERR_END /* End of enum marker */
};
typedef enum _dmngr_err_e dmngr_err_e;
/* Convert enumeration type to string */
const char * dmngr_err_str (dmngr_err_e err);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment