/* * Copyright (c) 2013-2014, Christian Ferrari * All rights reserved. * * This file is part of FLOM. * * FLOM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * FLOM 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 FLOM. If not, see . */ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_GLIB_H # include #endif #define FLOM_TRACE_MODULE FLOM_TRACE_MOD_GENERIC #include "flom_config.h" #include "flom_client.h" #include "flom_conns.h" #include "flom_errors.h" #include "flom_exec.h" #include "flom_rsrc.h" #include "flom_trace.h" static gboolean print_version = FALSE; static gboolean verbose = FALSE; static char *config_file = NULL; static gchar *socket_name = NULL; static gchar *resource_name = NULL; static gchar *resource_wait = NULL; static gint resource_timeout = FLOM_NETWORK_WAIT_TIMEOUT; static gchar *lock_mode = NULL; static gint daemon_lifespan = _DEFAULT_DAEMON_LIFESPAN; static gchar *unicast_address = NULL; static gint unicast_port = _DEFAULT_DAEMON_PORT; static gchar *multicast_address = NULL; static gint multicast_port = _DEFAULT_DAEMON_PORT; static gint discovery_attempts = _DEFAULT_DISCOVERY_ATTEMPTS; static gint discovery_timeout = _DEFAULT_DISCOVERY_TIMEOUT; static gint discovery_ttl = _DEFAULT_DISCOVERY_TTL; static gint tcp_keepalive_time = _DEFAULT_TCP_KEEPALIVE_TIME; static gint tcp_keepalive_intvl = _DEFAULT_TCP_KEEPALIVE_INTVL; static gint tcp_keepalive_probes = _DEFAULT_TCP_KEEPALIVE_PROBES; static gchar *command_trace_file = NULL; static gchar *daemon_trace_file = NULL; static gchar **command_argv = NULL; /* command line options */ static GOptionEntry entries[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &print_version, "Print package info and exit", NULL }, { "verbose", 'V', 0, G_OPTION_ARG_NONE, &verbose, "Activate verbose messages", NULL }, { "config-file", 'c', 0, G_OPTION_ARG_STRING, &config_file, "User configuration file name", NULL }, { "resource-name", 'r', 0, G_OPTION_ARG_STRING, &resource_name, "Specify the name of the resource to be locked", NULL }, { "resource-wait", 'w', 0, G_OPTION_ARG_STRING, &resource_wait, "Specify if the command enques when the resource is already locked (accepted values 'yes', 'no')", NULL }, { "resource-timeout", 'o', 0, G_OPTION_ARG_INT, &resource_timeout, "Specify maximum wait time (milliseconds) if a resource is already locked", NULL }, { "lock-mode", 'l', 0, G_OPTION_ARG_STRING, &lock_mode, "Resource lock mode ('NL', 'CR', 'CW', 'PR', 'PW', 'EX')", NULL }, { "socket-name", 's', 0, G_OPTION_ARG_STRING, &socket_name, "Daemon/command communication socket name", NULL }, { "daemon-lifespan", 'd', 0, G_OPTION_ARG_INT, &daemon_lifespan, "Specify minimum lifespan of the flom daemon (if activated)", NULL }, { "unicast-address", 'a', 0, G_OPTION_ARG_STRING, &unicast_address, "Daemon TCP/IP address", NULL }, { "unicast-port", 'p', 0, G_OPTION_ARG_INT, &unicast_port, "Daemon TCP/IP port", NULL }, { "multicast-address", 'A', 0, G_OPTION_ARG_STRING, &multicast_address, "Daemon UDP/IP (multicast) address", NULL }, { "multicast-port", 'P', 0, G_OPTION_ARG_INT, &multicast_port, "Daemon UDP/IP (multicast) port", NULL }, { "discovery-attempts", 'D', 0, G_OPTION_ARG_INT, &discovery_attempts, "UDP/IP (multicast) max number of requests", NULL }, { "discovery-timeout", 'I', 0, G_OPTION_ARG_INT, &discovery_timeout, "UDP/IP (multicast) request timeout", NULL }, { "discovery-ttl", 0, 0, G_OPTION_ARG_INT, &discovery_ttl, "UDP/IP (multicast) hop limit", NULL }, { "tcp-keepalive-time", 0, 0, G_OPTION_ARG_INT, &tcp_keepalive_time, "Local override for SO_KEEPALIVE feature", NULL }, { "tcp-keepalive-intvl", 0, 0, G_OPTION_ARG_INT, &tcp_keepalive_intvl, "Local override for SO_KEEPALIVE feature", NULL }, { "tcp-keepalive-probes", 0, 0, G_OPTION_ARG_INT, &tcp_keepalive_probes, "Local override for SO_KEEPALIVE feature", NULL }, { "daemon-trace-file", 't', 0, G_OPTION_ARG_STRING, &daemon_trace_file, "Specify daemon (background process) trace file name (absolute path required)", NULL }, { "command-trace-file", 'T', 0, G_OPTION_ARG_STRING, &command_trace_file, "Specify command (foreground process) trace file name (absolute path required)", NULL }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &command_argv, "Command must be executed under flom control" }, { NULL } }; int main (int argc, char *argv[]) { GError *error = NULL; GOptionContext *option_context; int child_status = 0; int ret_cod = FLOM_RC_INTERNAL_ERROR; struct flom_conn_data_s cd; option_context = g_option_context_new("-- command to execute"); g_option_context_add_main_entries(option_context, entries, NULL); if (!g_option_context_parse(option_context, &argc, &argv, &error)) { g_print("option parsing failed: %s\n", error->message); exit(FLOM_ES_GENERIC_ERROR); } g_option_context_free(option_context); if (print_version) { g_print("FLOM: Free LOck Manager\n" "Copyright (c) 2013-2014, Christian Ferrari; " "all rights reserved.\n" "License: GPL (GNU Public License) version 2\n" "Package name: %s; package version: %s; release date: %s\n" "Access http://sourceforge.net/projects/flom/ for " "project community activities\n", FLOM_PACKAGE_NAME, FLOM_PACKAGE_VERSION, FLOM_PACKAGE_DATE); exit(FLOM_ES_OK); } /* initialize trace destination if necessary */ FLOM_TRACE_INIT; /* initialize regular expression table */ if (FLOM_RC_OK != (ret_cod = global_res_name_preg_init())) { g_print("global_res_name_preg_init: ret_cod=%d\n", ret_cod); exit(FLOM_ES_GENERIC_ERROR); } /* reset global configuration */ flom_config_reset(); /* initialize configuration with standard system, statndard user and user customized config files */ if (FLOM_RC_OK != (ret_cod = flom_config_init(config_file))) { g_print("flom_config_init: ret_cod=%d\n", ret_cod); exit(FLOM_ES_GENERIC_ERROR); } /* overrides configuration with command line passed arguments */ if (NULL != resource_name) if (FLOM_RC_OK != (ret_cod = flom_config_set_resource_name( resource_name))) { g_print("flom_config_set_resource_name: ret_cod=%d\n", ret_cod); exit(FLOM_ES_GENERIC_ERROR); } if (NULL != resource_wait) { flom_bool_value_t fbv; if (FLOM_BOOL_INVALID == ( fbv = flom_bool_value_retrieve(resource_wait))) { g_print("resource-wait: '%s' is an invalid value\n", resource_wait); exit(FLOM_ES_GENERIC_ERROR); } flom_config_set_resource_wait(fbv); } if (FLOM_NETWORK_WAIT_TIMEOUT != resource_timeout) { /* timeout is useless if no wait was specified */ if (FLOM_BOOL_NO == flom_config_get_resource_wait()) g_warning("Timeout ignored because 'no wait' behavior was " "specified\n"); else flom_config_set_resource_timeout(resource_timeout); } if (NULL != lock_mode) { flom_lock_mode_t flm; if (FLOM_LOCK_MODE_INVALID == ( flm = flom_lock_mode_retrieve(lock_mode))) { g_print("lock-mode: '%s' is an invalid value\n", lock_mode); exit(FLOM_ES_GENERIC_ERROR); } flom_config_set_lock_mode(flm); } if (NULL != socket_name) { if (FLOM_RC_OK != (ret_cod = flom_config_set_socket_name( socket_name))) { g_print("socket-name: '%s' is an invalid value\n", socket_name); g_print("flom_client_connect: ret_cod=%d (%s)\n", ret_cod, flom_strerror(ret_cod)); exit(FLOM_ES_GENERIC_ERROR); } } if (_DEFAULT_DAEMON_LIFESPAN != daemon_lifespan) { flom_config_set_lifespan(daemon_lifespan); } if (NULL != unicast_address) { flom_config_set_unicast_address(unicast_address); } if (_DEFAULT_DAEMON_PORT != unicast_port) { flom_config_set_unicast_port(unicast_port); } if (NULL != multicast_address) { flom_config_set_multicast_address(multicast_address); } if (_DEFAULT_DAEMON_PORT != multicast_port) { flom_config_set_multicast_port(multicast_port); } if (_DEFAULT_DISCOVERY_ATTEMPTS != discovery_attempts) { flom_config_set_discovery_attempts(discovery_attempts); } if (_DEFAULT_DISCOVERY_TIMEOUT != discovery_timeout) { flom_config_set_discovery_timeout(discovery_timeout); } if (_DEFAULT_DISCOVERY_TTL != discovery_ttl) { flom_config_set_discovery_ttl(discovery_ttl); } if (_DEFAULT_TCP_KEEPALIVE_TIME != tcp_keepalive_time) { flom_config_set_tcp_keepalive_time(tcp_keepalive_time); } if (_DEFAULT_TCP_KEEPALIVE_INTVL != tcp_keepalive_intvl) { flom_config_set_tcp_keepalive_intvl(tcp_keepalive_intvl); } if (_DEFAULT_TCP_KEEPALIVE_PROBES != tcp_keepalive_probes) { flom_config_set_tcp_keepalive_probes(tcp_keepalive_probes); } if (NULL != daemon_trace_file) flom_config_set_daemon_trace_file(daemon_trace_file); if (NULL != command_trace_file) { flom_config_set_command_trace_file(command_trace_file); /* change trace destination if necessary */ FLOM_TRACE_REOPEN(flom_config_get_command_trace_file()); } /* print configuration */ if (verbose) flom_config_print(); /* check configuration */ if (FLOM_RC_OK != (ret_cod = flom_config_check())) { g_warning("Configuration is not valid, cannot go on!\n"); exit(FLOM_ES_GENERIC_ERROR); } /* check the command is not null */ if (NULL == command_argv) { g_warning("No command to execute!\n"); exit(FLOM_ES_UNABLE_TO_EXECUTE_COMMAND); } /* open connection to a valid flom lock manager... */ if (FLOM_RC_OK != (ret_cod = flom_client_connect(&cd))) { g_print("flom_client_connect: ret_cod=%d (%s)\n", ret_cod, flom_strerror(ret_cod)); exit(FLOM_ES_GENERIC_ERROR); } /* sending lock command */ ret_cod = flom_client_lock(&cd, flom_config_get_resource_timeout()); switch (ret_cod) { case FLOM_RC_OK: /* OK, go on */ break; case FLOM_RC_LOCK_BUSY: /* busy */ g_print("Resource already locked, the lock cannot be obtained\n"); /* gracefully disconnect from daemon */ if (FLOM_RC_OK != (ret_cod = flom_client_disconnect(&cd))) { g_print("flom_client_unlock: ret_cod=%d (%s)\n", ret_cod, flom_strerror(ret_cod)); } exit(FLOM_ES_RESOURCE_BUSY); break; case FLOM_RC_NETWORK_TIMEOUT: /* timeout expired, busy resource */ g_print("Resource already locked, the lock was not obtained " "because timeout (%d milliseconds) expired\n", flom_config_get_resource_timeout()); /* gracefully disconnect from daemon */ if (FLOM_RC_OK != (ret_cod = flom_client_disconnect(&cd))) { g_print("flom_client_unlock: ret_cod=%d (%s)\n", ret_cod, flom_strerror(ret_cod)); } exit(FLOM_ES_RESOURCE_BUSY); break; default: g_print("flom_client_lock: ret_cod=%d (%s)\n", ret_cod, flom_strerror(ret_cod)); exit(FLOM_ES_GENERIC_ERROR); } /* switch (ret_cod) */ /* execute the command */ if (FLOM_RC_OK != (ret_cod = flom_exec(command_argv, &child_status))) { guint i, num; g_print("Unable to execute command: '"); num = g_strv_length(command_argv); for (i=0; i