Blame src/ledmon.c

Packit 7e09eb
/*
Packit 7e09eb
 * Intel(R) Enclosure LED Utilities
Packit 7e09eb
 * Copyright (C) 2009-2019 Intel Corporation.
Packit 7e09eb
 *
Packit 7e09eb
 * This program is free software; you can redistribute it and/or modify it
Packit 7e09eb
 * under the terms and conditions of the GNU General Public License,
Packit 7e09eb
 * version 2, as published by the Free Software Foundation.
Packit 7e09eb
 *
Packit 7e09eb
 * This program is distributed in the hope it will be useful, but WITHOUT
Packit 7e09eb
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
Packit 7e09eb
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
Packit 7e09eb
 * more details.
Packit 7e09eb
 *
Packit 7e09eb
 * You should have received a copy of the GNU General Public License along with
Packit 7e09eb
 * this program; if not, write to the Free Software Foundation, Inc.,
Packit 7e09eb
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
Packit 7e09eb
 *
Packit 7e09eb
 */
Packit 7e09eb
Packit 7e09eb
#include <errno.h>
Packit 7e09eb
#include <fcntl.h>
Packit 7e09eb
#include <getopt.h>
Packit 7e09eb
#include <limits.h>
Packit 7e09eb
#include <signal.h>
Packit 7e09eb
#include <stdint.h>
Packit 7e09eb
#include <stdio.h>
Packit 7e09eb
#include <stdlib.h>
Packit 7e09eb
#include <string.h>
Packit 7e09eb
#include <sys/param.h>
Packit 7e09eb
#include <sys/select.h>
Packit 7e09eb
#include <sys/stat.h>
Packit 7e09eb
#include <sys/types.h>
Packit 7e09eb
#include <sys/wait.h>
Packit 7e09eb
#include <syslog.h>
Packit 7e09eb
#include <time.h>
Packit 7e09eb
#include <unistd.h>
Packit 7e09eb
Packit 7e09eb
#if _HAVE_DMALLOC_H
Packit 7e09eb
#include <dmalloc.h>
Packit 7e09eb
#endif
Packit 7e09eb
Packit 7e09eb
#include "ahci.h"
Packit 7e09eb
#include "block.h"
Packit 7e09eb
#include "cntrl.h"
Packit 7e09eb
#include "config.h"
Packit 7e09eb
#include "config_file.h"
Packit 7e09eb
#include "ibpi.h"
Packit 7e09eb
#include "list.h"
Packit 7e09eb
#include "pidfile.h"
Packit 7e09eb
#include "raid.h"
Packit 7e09eb
#include "scsi.h"
Packit 7e09eb
#include "slave.h"
Packit 7e09eb
#include "smp.h"
Packit 7e09eb
#include "status.h"
Packit 7e09eb
#include "sysfs.h"
Packit 7e09eb
#include "udev.h"
Packit 7e09eb
#include "utils.h"
Packit 7e09eb
#include "version.h"
Packit 7e09eb
#include "vmdssd.h"
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief List of active block devices.
Packit 7e09eb
 *
Packit 7e09eb
 * This list holds all block devices attached to supported storage controllers.
Packit 7e09eb
 * Only devices which have enclosure management feature enabled are on the
Packit 7e09eb
 * list, other devices are ignored (except protocol is forced).
Packit 7e09eb
 */
Packit 7e09eb
static struct list ledmon_block_list;
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Daemon process termination flag.
Packit 7e09eb
 *
Packit 7e09eb
 * This flag indicates that daemon process should terminate. User must send
Packit 7e09eb
 * SIGTERM to daemon in order to terminate the process gently.
Packit 7e09eb
 */
Packit 7e09eb
static sig_atomic_t terminate;
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Path to ledmon configuration file.
Packit 7e09eb
 *
Packit 7e09eb
 * This string contains path of the ledmon configuration file. The value is
Packit 7e09eb
 * set to LEDMON_DEF_CONF_FILE by default and it can be changed by command line
Packit 7e09eb
 * option.
Packit 7e09eb
 */
Packit 7e09eb
static char *ledmon_conf_path;
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Boolean flag whether to run foreground or not.
Packit 7e09eb
 *
Packit 7e09eb
 * This flag is turned on with --foreground option. Primary use of this option
Packit 7e09eb
 * is to use it in systemd service file.
Packit 7e09eb
 */
Packit 7e09eb
static int foreground;
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Name of IBPI patterns.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal array with names of IBPI patterns. Logging routines use this
Packit 7e09eb
 * entries to translate enumeration type into the string.
Packit 7e09eb
 */
Packit 7e09eb
const char *ibpi_str[] = {
Packit 7e09eb
	[IBPI_PATTERN_UNKNOWN]        = "None",
Packit 7e09eb
	[IBPI_PATTERN_NORMAL]         = "Off",
Packit 7e09eb
	[IBPI_PATTERN_ONESHOT_NORMAL] = "Oneshot Off",
Packit 7e09eb
	[IBPI_PATTERN_DEGRADED]       = "In a Critical Array",
Packit 7e09eb
	[IBPI_PATTERN_REBUILD]        = "Rebuild",
Packit 7e09eb
	[IBPI_PATTERN_FAILED_ARRAY]   = "In a Failed Array",
Packit 7e09eb
	[IBPI_PATTERN_HOTSPARE]       = "Hotspare",
Packit 7e09eb
	[IBPI_PATTERN_PFA]            = "Predicted Failure Analysis",
Packit 7e09eb
	[IBPI_PATTERN_FAILED_DRIVE]   = "Failure",
Packit 7e09eb
	[IBPI_PATTERN_LOCATE]         = "Locate",
Packit 7e09eb
	[IBPI_PATTERN_LOCATE_OFF]     = "Locate Off",
Packit 7e09eb
	[IBPI_PATTERN_ADDED]          = "Added",
Packit 7e09eb
	[IBPI_PATTERN_REMOVED]        = "Removed"
Packit 7e09eb
};
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * Internal variable of monitor service. It is the pattern used to print out
Packit 7e09eb
 * information about the version of monitor service.
Packit 7e09eb
 */
Packit 7e09eb
static char *ledmon_version = "Intel(R) Enclosure LED Monitor Service %d.%d %s\n"
Packit 7e09eb
			      "Copyright (C) 2009-2019 Intel Corporation.\n";
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * Internal variable of monitor service. It is used to help parse command line
Packit 7e09eb
 * short options.
Packit 7e09eb
 */
Packit 7e09eb
static char *shortopt;
Packit 7e09eb
Packit 7e09eb
struct option *longopt;
Packit 7e09eb
Packit 7e09eb
static int possible_params[] = {
Packit 7e09eb
	OPT_ALL,
Packit 7e09eb
	OPT_CONFIG,
Packit 7e09eb
	OPT_DEBUG,
Packit 7e09eb
	OPT_ERROR,
Packit 7e09eb
	OPT_HELP,
Packit 7e09eb
	OPT_INFO,
Packit 7e09eb
	OPT_INTERVAL,
Packit 7e09eb
	OPT_LOG,
Packit 7e09eb
	OPT_QUIET,
Packit 7e09eb
	OPT_VERSION,
Packit 7e09eb
	OPT_WARNING,
Packit 7e09eb
	OPT_LOG_LEVEL,
Packit 7e09eb
	OPT_FOREGROUND,
Packit 7e09eb
};
Packit 7e09eb
Packit 7e09eb
static int possible_params_size = sizeof(possible_params)
Packit 7e09eb
		/ sizeof(possible_params[0]);
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Monitor service finalize function.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. It is used to finalize daemon
Packit 7e09eb
 * process i.e. free allocated memory, unlock and remove pidfile and close log
Packit 7e09eb
 * file and syslog. The function is registered as on_exit() handler.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]     status          The function ignores this parameter.
Packit 7e09eb
 * @param[in]     program_name    The name of the binary file. This argument
Packit 7e09eb
 *                                is passed via on_exit() function.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_fini(int __attribute__ ((unused)) status, void *program_name)
Packit 7e09eb
{
Packit 7e09eb
	sysfs_reset();
Packit 7e09eb
	list_erase(&ledmon_block_list);
Packit 7e09eb
	log_close();
Packit 7e09eb
	pidfile_remove(program_name);
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Puts exit status to a log file.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. It is used to report an exit
Packit 7e09eb
 * status of the monitor service. The message is logged in to syslog and to log
Packit 7e09eb
 * file. The function is registered as on_exit() hander.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]     status            Status given in the last call to exit()
Packit 7e09eb
 *                                  function.
Packit 7e09eb
 * @param[in]     arg               Argument passed to on_exit().
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_status(int status, void *arg)
Packit 7e09eb
{
Packit 7e09eb
	int log_level;
Packit 7e09eb
	char message[4096];
Packit 7e09eb
	int ignore = *((int *)arg);
Packit 7e09eb
Packit 7e09eb
	if (ignore)
Packit 7e09eb
		return;
Packit 7e09eb
Packit 7e09eb
	if (status == STATUS_SUCCESS)
Packit 7e09eb
		log_level = LOG_LEVEL_INFO;
Packit 7e09eb
	else
Packit 7e09eb
		log_level = LOG_LEVEL_ERROR;
Packit 7e09eb
Packit 7e09eb
	snprintf(message, sizeof(message), "exit status is %s.",
Packit 7e09eb
		 strstatus(status));
Packit 7e09eb
Packit 7e09eb
	if (get_log_fd() >= 0)
Packit 7e09eb
		_log(log_level, message);
Packit 7e09eb
	else
Packit 7e09eb
		syslog(log_level_infos[log_level].priority, "%s", message);
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Displays the credits.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. It prints out the name and
Packit 7e09eb
 * version of the program. It displays the copyright notice and information
Packit 7e09eb
 * about the author and license, too.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_version(void)
Packit 7e09eb
{
Packit 7e09eb
	printf(ledmon_version, VERSION_MAJOR, VERSION_MINOR, BUILD_LABEL);
Packit 7e09eb
	printf("\nThis is free software; see the source for copying conditions."
Packit 7e09eb
	       " There is NO warranty;\nnot even for MERCHANTABILITY or FITNESS"
Packit 7e09eb
	       " FOR A PARTICULAR PURPOSE.\n\n");
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Displays the help.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. The function prints the name
Packit 7e09eb
 * and version of the program out. It displays the usage and available options
Packit 7e09eb
 * and its arguments (if any). Each option is described. This is an extract
Packit 7e09eb
 * from user manual page.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_help(void)
Packit 7e09eb
{
Packit 7e09eb
	printf(ledmon_version, VERSION_MAJOR, VERSION_MINOR, BUILD_LABEL);
Packit 7e09eb
	printf("\nUsage: %s [OPTIONS]\n\n", progname);
Packit 7e09eb
	printf("Mandatory arguments for long options are mandatory for short "
Packit 7e09eb
	       "options, too.\n\n");
Packit 7e09eb
	print_opt("--interval=VALUE", "-t VALUE",
Packit 7e09eb
			  "Set time interval to VALUE seconds.");
Packit 7e09eb
	print_opt("", "", "The smallest interval is 5 seconds.");
Packit 7e09eb
	print_opt("--config=PATH", "-c PATH",
Packit 7e09eb
			  "Use alternate configuration file.");
Packit 7e09eb
	print_opt("--log=PATH", "-l PATH",
Packit 7e09eb
			  "Use local log file instead /var/log/ledmon.log");
Packit 7e09eb
	print_opt("--log-level=VALUE", "-l VALUE",
Packit 7e09eb
			  "Allows user to set ledmon verbose level in logs.");
Packit 7e09eb
	print_opt("--foreground", "",
Packit 7e09eb
			  "Do not run as daemon.");
Packit 7e09eb
	print_opt("--help", "-h", "Displays this help text.");
Packit 7e09eb
	print_opt("--version", "-v",
Packit 7e09eb
			  "Displays version and license information.");
Packit 7e09eb
	printf("\nRefer to ledmon(8) man page for more detailed description.\n");
Packit 7e09eb
	printf("Bugs should be reported at: https://github.com/intel/ledmon/issues\n");
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Sets the path to configuration file.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. This function sets the path and
Packit 7e09eb
 * name of configuration file. The function is checking whether the given path
Packit 7e09eb
 * is valid or it is invalid and should be ignored.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]      path           the new location and name of config file.
Packit 7e09eb
 *
Packit 7e09eb
 * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code.
Packit 7e09eb
 */
Packit 7e09eb
static status_t _set_config_path(char **conf_path, const char *path)
Packit 7e09eb
{
Packit 7e09eb
	if (!path)
Packit 7e09eb
		path = LEDMON_DEF_CONF_FILE;
Packit 7e09eb
Packit 7e09eb
	if (*conf_path)
Packit 7e09eb
		free(*conf_path);
Packit 7e09eb
	*conf_path = str_dup(path);
Packit 7e09eb
Packit 7e09eb
	return STATUS_SUCCESS;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Sets the value of sleep interval.
Packit 7e09eb
 *
Packit 7e09eb
 * This function is used by command line handler to set new value of time
Packit 7e09eb
 * interval, @see time_interval for details.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]     optarg          String containing the new value of time
Packit 7e09eb
 *                                interval, given in command line option.
Packit 7e09eb
 *
Packit 7e09eb
 * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code.
Packit 7e09eb
 */
Packit 7e09eb
static status_t _set_sleep_interval(const char *optarg)
Packit 7e09eb
{
Packit 7e09eb
	conf.scan_interval = atoi(optarg);
Packit 7e09eb
	if (conf.scan_interval < LEDMON_MIN_SLEEP_INTERVAL) {
Packit 7e09eb
		log_warning("sleep interval too small... using default.");
Packit 7e09eb
		conf.scan_interval = LEDMON_DEF_SLEEP_INTERVAL;
Packit 7e09eb
	}
Packit 7e09eb
	return STATUS_SUCCESS;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Reads config file path and checks if command line input contains
Packit 7e09eb
 * options which don't require to run ledmon as daemon.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. This function looks for
Packit 7e09eb
 * config file path in command line options given to the program from
Packit 7e09eb
 * command line interface. It also handles options to print help and version.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]     argc            - number of arguments.
Packit 7e09eb
 * @param[in]     argv            - array of command line arguments.
Packit 7e09eb
 *
Packit 7e09eb
 * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code.
Packit 7e09eb
 */
Packit 7e09eb
static status_t _cmdline_parse_non_daemonise(int argc, char *argv[])
Packit 7e09eb
{
Packit 7e09eb
	int opt_index = -1;
Packit 7e09eb
	int opt = -1;
Packit 7e09eb
	status_t status = STATUS_SUCCESS;
Packit 7e09eb
Packit 7e09eb
	do {
Packit 7e09eb
		opt = getopt_long(argc, argv, shortopt, longopt, &opt_index);
Packit 7e09eb
		switch (opt) {
Packit 7e09eb
		case 'c':
Packit 7e09eb
			status = _set_config_path(&ledmon_conf_path, optarg);
Packit 7e09eb
			break;
Packit 7e09eb
		case 'h':
Packit 7e09eb
			_ledmon_help();
Packit 7e09eb
			exit(EXIT_SUCCESS);
Packit 7e09eb
		case 'v':
Packit 7e09eb
			_ledmon_version();
Packit 7e09eb
			exit(EXIT_SUCCESS);
Packit 7e09eb
		case ':':
Packit 7e09eb
		case '?':
Packit 7e09eb
			return STATUS_CMDLINE_ERROR;
Packit 7e09eb
		}
Packit 7e09eb
	} while (opt >= 0);
Packit 7e09eb
Packit 7e09eb
	return status;
Packit 7e09eb
}
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Command line interface handler function.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. This function interprets the
Packit 7e09eb
 * options and commands given to the program from command line interface.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]     argc            - number of arguments.
Packit 7e09eb
 * @param[in]     argv            - array of command line arguments.
Packit 7e09eb
 *
Packit 7e09eb
 * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code.
Packit 7e09eb
 */
Packit 7e09eb
static status_t _cmdline_parse(int argc, char *argv[])
Packit 7e09eb
{
Packit 7e09eb
	int opt, opt_index = -1;
Packit 7e09eb
	status_t status = STATUS_SUCCESS;
Packit 7e09eb
Packit 7e09eb
	optind = 1;
Packit 7e09eb
	do {
Packit 7e09eb
		opt = getopt_long(argc, argv, shortopt, longopt, &opt_index);
Packit 7e09eb
		if (opt == -1)
Packit 7e09eb
			break;
Packit 7e09eb
		if (opt == 'c')
Packit 7e09eb
			continue;
Packit 7e09eb
		switch (opt) {
Packit 7e09eb
		int log_level;
Packit 7e09eb
		case 0:
Packit 7e09eb
			switch (get_option_id(longopt[opt_index].name)) {
Packit 7e09eb
			case OPT_LOG_LEVEL:
Packit 7e09eb
				log_level = get_option_id(optarg);
Packit 7e09eb
				if (log_level != -1)
Packit 7e09eb
					status = set_verbose_level(log_level);
Packit 7e09eb
				else
Packit 7e09eb
					status = STATUS_CMDLINE_ERROR;
Packit 7e09eb
				break;
Packit 7e09eb
			case OPT_FOREGROUND:
Packit 7e09eb
				foreground = 1;
Packit 7e09eb
				break;
Packit 7e09eb
			default:
Packit 7e09eb
				status = set_verbose_level(
Packit 7e09eb
						possible_params[opt_index]);
Packit 7e09eb
			}
Packit 7e09eb
			break;
Packit 7e09eb
		case 'l':
Packit 7e09eb
			status = set_log_path(optarg);
Packit 7e09eb
			break;
Packit 7e09eb
		case 't':
Packit 7e09eb
			status = _set_sleep_interval(optarg);
Packit 7e09eb
			break;
Packit 7e09eb
		}
Packit 7e09eb
		opt_index = -1;
Packit 7e09eb
		if (status != STATUS_SUCCESS)
Packit 7e09eb
			return status;
Packit 7e09eb
	} while (1);
Packit 7e09eb
Packit 7e09eb
	return STATUS_SUCCESS;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief SIGTERM handler function.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]    signum          - the number of signal received.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_sig_term(int signum)
Packit 7e09eb
{
Packit 7e09eb
	if (signum == SIGTERM) {
Packit 7e09eb
		log_info("SIGTERM caught - terminating daemon process.");
Packit 7e09eb
		terminate = 1;
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Configures signal handlers.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor services. It sets to ignore SIGALRM,
Packit 7e09eb
 * SIGHUP and SIGPIPE signals. The function installs a handler for SIGTERM
Packit 7e09eb
 * signal. User must send SIGTERM to daemon process in order to shutdown the
Packit 7e09eb
 * daemon gently.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_setup_signals(void)
Packit 7e09eb
{
Packit 7e09eb
	struct sigaction act;
Packit 7e09eb
	sigset_t sigset;
Packit 7e09eb
Packit 7e09eb
	sigemptyset(&sigset);
Packit 7e09eb
	sigaddset(&sigset, SIGALRM);
Packit 7e09eb
	sigaddset(&sigset, SIGHUP);
Packit 7e09eb
	sigaddset(&sigset, SIGTERM);
Packit 7e09eb
	sigaddset(&sigset, SIGPIPE);
Packit 7e09eb
	sigaddset(&sigset, SIGUSR1);
Packit 7e09eb
	sigprocmask(SIG_BLOCK, &sigset, NULL);
Packit 7e09eb
Packit 7e09eb
	act.sa_handler = SIG_IGN;
Packit 7e09eb
	act.sa_flags = 0;
Packit 7e09eb
	sigemptyset(&act.sa_mask);
Packit 7e09eb
	sigaction(SIGALRM, &act, NULL);
Packit 7e09eb
	sigaction(SIGHUP, &act, NULL);
Packit 7e09eb
	sigaction(SIGPIPE, &act, NULL);
Packit 7e09eb
	act.sa_handler = _ledmon_sig_term;
Packit 7e09eb
	sigaction(SIGTERM, &act, NULL);
Packit 7e09eb
	sigaction(SIGUSR1, &act, NULL);
Packit 7e09eb
Packit 7e09eb
	sigprocmask(SIG_UNBLOCK, &sigset, NULL);
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Puts the calling process into sleep.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. The function puts the calling
Packit 7e09eb
 * process into a sleep for the given amount of time (expressed in seconds). The
Packit 7e09eb
 * function will give control back to the process as soon as time elapses or
Packit 7e09eb
 * SIGTERM occurs.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]    seconds         - the time interval given in seconds.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_wait(int seconds)
Packit 7e09eb
{
Packit 7e09eb
	int fd, udev_fd, max_fd, res;
Packit 7e09eb
	fd_set rdfds, exfds;
Packit 7e09eb
	struct timespec timeout;
Packit 7e09eb
	sigset_t sigset;
Packit 7e09eb
Packit 7e09eb
	sigprocmask(SIG_UNBLOCK, NULL, &sigset);
Packit 7e09eb
	sigdelset(&sigset, SIGTERM);
Packit 7e09eb
	timeout.tv_nsec = 0;
Packit 7e09eb
	timeout.tv_sec = seconds;
Packit 7e09eb
Packit 7e09eb
	fd = open("/proc/mdstat", O_RDONLY);
Packit 7e09eb
	udev_fd = get_udev_monitor();
Packit 7e09eb
	max_fd = MAX(fd, udev_fd) + 1;
Packit 7e09eb
	do {
Packit 7e09eb
		FD_ZERO(&rdfds);
Packit 7e09eb
		FD_ZERO(&exfds);
Packit 7e09eb
Packit 7e09eb
		if (fd > 0)
Packit 7e09eb
			FD_SET(fd, &exfds);
Packit 7e09eb
		if (udev_fd > 0)
Packit 7e09eb
			FD_SET(udev_fd, &rdfds);
Packit 7e09eb
Packit 7e09eb
		res = pselect(max_fd, &rdfds, NULL, &exfds, &timeout, &sigset);
Packit 7e09eb
		if (terminate || !FD_ISSET(udev_fd, &rdfds) ||
Packit 7e09eb
		    handle_udev_event(&ledmon_block_list) <= 0)
Packit 7e09eb
			break;
Packit 7e09eb
	} while (res > 0);
Packit 7e09eb
Packit 7e09eb
	if (fd >= 0)
Packit 7e09eb
		close(fd);
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Determine failed state by comparing saved block device with new
Packit 7e09eb
 * scanned.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. Due race conditions related
Packit 7e09eb
 * with removing files from /sys/block/md* when raid is stopped or disk is
Packit 7e09eb
 * failed, this function analyse state of every block device between scans.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]    block           Pointer to new (scanned) block device
Packit 7e09eb
 *				 structure.
Packit 7e09eb
 * @param[in]    temp            Pointer to previously saved state of block
Packit 7e09eb
 *				 device structure.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _handle_fail_state(struct block_device *block,
Packit 7e09eb
			       struct block_device *temp)
Packit 7e09eb
{
Packit 7e09eb
	struct raid_device *temp_raid_device = NULL;
Packit 7e09eb
Packit 7e09eb
	if (!temp->raid_dev)
Packit 7e09eb
		/*
Packit 7e09eb
		 * Device is a RAID member now, so keep information about
Packit 7e09eb
		 * related with device RAID.
Packit 7e09eb
		 */
Packit 7e09eb
		temp->raid_dev = raid_device_duplicate(block->raid_dev);
Packit 7e09eb
Packit 7e09eb
	if (!temp->raid_dev)
Packit 7e09eb
		return;
Packit 7e09eb
Packit 7e09eb
	temp_raid_device = find_raid_device(sysfs_get_volumes(),
Packit 7e09eb
					    temp->raid_dev->sysfs_path);
Packit 7e09eb
Packit 7e09eb
	if (!block->raid_dev) {
Packit 7e09eb
		if (temp->raid_dev->type == DEVICE_TYPE_VOLUME &&
Packit 7e09eb
		    temp_raid_device) {
Packit 7e09eb
			/*
Packit 7e09eb
			 * Device is outside of the volume, but related raid
Packit 7e09eb
			 * still exist, so disk has been removed from volume -
Packit 7e09eb
			 * blink fail LED. It is case when drive is removed
Packit 7e09eb
			 * by mdadm -If.
Packit 7e09eb
			 */
Packit 7e09eb
			temp->ibpi = IBPI_PATTERN_FAILED_DRIVE;
Packit 7e09eb
			/*
Packit 7e09eb
			 * Changing failed state to hotspare will be prevent by
Packit 7e09eb
			 * code from _add_block function. If disk come back to
Packit 7e09eb
			 * container failed state should be removed. By setting
Packit 7e09eb
			 * type to CONTAINER ledmon can react in this case.
Packit 7e09eb
			 */
Packit 7e09eb
			temp->raid_dev->type = DEVICE_TYPE_CONTAINER;
Packit 7e09eb
		} else {
Packit 7e09eb
			/*
Packit 7e09eb
			 * Device was RAID member and was failed (was outside
Packit 7e09eb
			 * of array and container). Now again is in container.
Packit 7e09eb
			 * Release object to perform hotspare state.
Packit 7e09eb
			 * Or:
Packit 7e09eb
			 * Device is outside of the volume, but related raid is
Packit 7e09eb
			 * removed (or stopped) so device is no longer a RAID
Packit 7e09eb
			 * member.
Packit 7e09eb
			 */
Packit 7e09eb
			raid_device_fini(temp->raid_dev);
Packit 7e09eb
			temp->raid_dev = NULL;
Packit 7e09eb
		}
Packit 7e09eb
	} else if (block->raid_dev) {
Packit 7e09eb
		if (temp->raid_dev->type == DEVICE_TYPE_VOLUME &&
Packit 7e09eb
		    block->raid_dev->type == DEVICE_TYPE_CONTAINER) {
Packit 7e09eb
			/*
Packit 7e09eb
			 * Drive is removed from volume, but still exist
Packit 7e09eb
			 * in container.
Packit 7e09eb
			 */
Packit 7e09eb
			enum raid_level new_level;
Packit 7e09eb
Packit 7e09eb
			if (!temp_raid_device)
Packit 7e09eb
				new_level = RAID_LEVEL_UNKNOWN;
Packit 7e09eb
			else
Packit 7e09eb
				new_level = temp_raid_device->level;
Packit 7e09eb
Packit 7e09eb
			if ((temp->raid_dev->level == RAID_LEVEL_10 ||
Packit 7e09eb
				 temp->raid_dev->level == RAID_LEVEL_1) &&
Packit 7e09eb
				 new_level == RAID_LEVEL_0) {
Packit 7e09eb
				/*
Packit 7e09eb
				 * Device is removed from volume due to
Packit 7e09eb
				 * migration to raid. State of this disk is
Packit 7e09eb
				 * hotspare now.
Packit 7e09eb
				 */
Packit 7e09eb
				temp->ibpi = IBPI_PATTERN_HOTSPARE;
Packit 7e09eb
			} else {
Packit 7e09eb
				/*
Packit 7e09eb
				 * Trasitions other than raid 0 migration.
Packit 7e09eb
				 * Like reshape, volume stopping etc.
Packit 7e09eb
				 */
Packit 7e09eb
				if (temp_raid_device) {
Packit 7e09eb
					/*
Packit 7e09eb
					 * Drive is removed from volume,
Packit 7e09eb
					 * but still exist in container. This
Packit 7e09eb
					 * situation can be caused by bad
Packit 7e09eb
					 * blocks or calling mdadm
Packit 7e09eb
					 * --set-faulty.
Packit 7e09eb
					 */
Packit 7e09eb
					temp->ibpi = IBPI_PATTERN_FAILED_DRIVE;
Packit 7e09eb
				}
Packit 7e09eb
			}
Packit 7e09eb
		} else if (temp->raid_dev->type == DEVICE_TYPE_CONTAINER &&
Packit 7e09eb
			   block->raid_dev->type == DEVICE_TYPE_VOLUME) {
Packit 7e09eb
			/*
Packit 7e09eb
			 * Disk was in container and is added to volume.
Packit 7e09eb
			 * Release object for recreating.
Packit 7e09eb
			 */
Packit 7e09eb
			raid_device_fini(temp->raid_dev);
Packit 7e09eb
			temp->raid_dev =
Packit 7e09eb
				raid_device_duplicate(block->raid_dev);
Packit 7e09eb
		}
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Adds the block device to list.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. The function adds a block
Packit 7e09eb
 * device to the ledmon_block_list list or if the device is already on the list
Packit 7e09eb
 * it updates the IBPI state of the given device. The function updates timestamp
Packit 7e09eb
 * value which indicates the time of last structure modification.  The function
Packit 7e09eb
 * is design to be used as 'action' parameter of list_for_each() function.
Packit 7e09eb
 * Each change of state is logged to the file and to the syslog.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]    block           Pointer to block device structure.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _add_block(struct block_device *block)
Packit 7e09eb
{
Packit 7e09eb
	struct block_device *temp = NULL;
Packit 7e09eb
Packit 7e09eb
	list_for_each(&ledmon_block_list, temp) {
Packit 7e09eb
		if (block_compare(temp, block))
Packit 7e09eb
			break;
Packit 7e09eb
		temp = NULL;
Packit 7e09eb
	}
Packit 7e09eb
	if (temp) {
Packit 7e09eb
		enum ibpi_pattern ibpi = temp->ibpi;
Packit 7e09eb
		temp->timestamp = block->timestamp;
Packit 7e09eb
		if (temp->ibpi == IBPI_PATTERN_ADDED) {
Packit 7e09eb
			temp->ibpi = IBPI_PATTERN_ONESHOT_NORMAL;
Packit 7e09eb
		} else if (temp->ibpi == IBPI_PATTERN_ONESHOT_NORMAL) {
Packit 7e09eb
			temp->ibpi = IBPI_PATTERN_UNKNOWN;
Packit 7e09eb
		} else if (temp->ibpi != IBPI_PATTERN_FAILED_DRIVE) {
Packit 7e09eb
			if (block->ibpi == IBPI_PATTERN_UNKNOWN) {
Packit 7e09eb
				if ((temp->ibpi != IBPI_PATTERN_UNKNOWN) &&
Packit 7e09eb
				    (temp->ibpi != IBPI_PATTERN_NORMAL)) {
Packit 7e09eb
					temp->ibpi =
Packit 7e09eb
					    IBPI_PATTERN_ONESHOT_NORMAL;
Packit 7e09eb
				} else {
Packit 7e09eb
					temp->ibpi = IBPI_PATTERN_UNKNOWN;
Packit 7e09eb
				}
Packit 7e09eb
			} else {
Packit 7e09eb
				temp->ibpi = block->ibpi;
Packit 7e09eb
			}
Packit 7e09eb
		} else if (!(temp->ibpi == IBPI_PATTERN_FAILED_DRIVE &&
Packit 7e09eb
			block->ibpi == IBPI_PATTERN_HOTSPARE) ||
Packit 7e09eb
			(temp->ibpi == IBPI_PATTERN_FAILED_DRIVE &&
Packit 7e09eb
			block->ibpi == IBPI_PATTERN_NONE)) {
Packit 7e09eb
			temp->ibpi = block->ibpi;
Packit 7e09eb
		}
Packit 7e09eb
Packit 7e09eb
		_handle_fail_state(block, temp);
Packit 7e09eb
Packit 7e09eb
		if (ibpi != temp->ibpi && ibpi <= IBPI_PATTERN_REMOVED) {
Packit 7e09eb
			log_info("CHANGE %s: from '%s' to '%s'.",
Packit 7e09eb
				 temp->sysfs_path, ibpi2str(ibpi),
Packit 7e09eb
				 ibpi2str(temp->ibpi));
Packit 7e09eb
		}
Packit 7e09eb
		/* Check if name of the device changed.*/
Packit 7e09eb
		if (strcmp(temp->sysfs_path, block->sysfs_path)) {
Packit 7e09eb
			log_info("NAME CHANGED %s to %s",
Packit 7e09eb
				 temp->sysfs_path, block->sysfs_path);
Packit 7e09eb
			free(temp->sysfs_path);
Packit 7e09eb
			temp->sysfs_path = str_dup(block->sysfs_path);
Packit 7e09eb
		}
Packit 7e09eb
	} else {
Packit 7e09eb
		/* Device not found, it's a new one! */
Packit 7e09eb
		temp = block_device_duplicate(block);
Packit 7e09eb
		if (temp != NULL) {
Packit 7e09eb
			log_info("NEW %s: state '%s'.", temp->sysfs_path,
Packit 7e09eb
				 ibpi2str(temp->ibpi));
Packit 7e09eb
			list_append(&ledmon_block_list, temp);
Packit 7e09eb
		}
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Sends LED control message.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. The function sends a LED
Packit 7e09eb
 * command to storage controller or enclosure device. The function checks
Packit 7e09eb
 * the time of last modification of block device structure. If the timestamp
Packit 7e09eb
 * is different then the current global timestamp this means the device is
Packit 7e09eb
 * missing due to hot-remove or hardware failure so it must be reported on
Packit 7e09eb
 * LEDs appropriately. Note that controller device and host attached to this
Packit 7e09eb
 * block device points to invalid pointer so it must be 'refreshed'.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]    block            Pointer to block device structure.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _send_msg(struct block_device *block)
Packit 7e09eb
{
Packit 7e09eb
	if (!block->cntrl) {
Packit 7e09eb
		log_debug("Missing cntrl for dev: %s. Not sending anything.",
Packit 7e09eb
			  strstr(block->sysfs_path, "host"));
Packit 7e09eb
		return;
Packit 7e09eb
	}
Packit 7e09eb
	if (block->timestamp != timestamp ||
Packit 7e09eb
	    block->ibpi == IBPI_PATTERN_REMOVED) {
Packit 7e09eb
		if (block->ibpi != IBPI_PATTERN_FAILED_DRIVE) {
Packit 7e09eb
			log_info("CHANGE %s: from '%s' to '%s'.",
Packit 7e09eb
				 block->sysfs_path, ibpi2str(block->ibpi),
Packit 7e09eb
				 ibpi2str(IBPI_PATTERN_FAILED_DRIVE));
Packit 7e09eb
			block->ibpi = IBPI_PATTERN_FAILED_DRIVE;
Packit 7e09eb
		} else {
Packit 7e09eb
			char *host = strstr(block->sysfs_path, "host");
Packit 7e09eb
			log_debug("DETACHED DEV '%s' in failed state",
Packit 7e09eb
				  host ? host : block->sysfs_path);
Packit 7e09eb
		}
Packit 7e09eb
	}
Packit 7e09eb
	block->send_fn(block, block->ibpi);
Packit 7e09eb
	block->ibpi_prev = block->ibpi;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static void _flush_msg(struct block_device *block)
Packit 7e09eb
{
Packit 7e09eb
	if (!block->cntrl)
Packit 7e09eb
		return;
Packit 7e09eb
	block->flush_fn(block);
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static void _revalidate_dev(struct block_device *block)
Packit 7e09eb
{
Packit 7e09eb
	/* Bring back controller and host to the device. */
Packit 7e09eb
	block->cntrl = block_get_controller(sysfs_get_cntrl_devices(),
Packit 7e09eb
					    block->cntrl_path);
Packit 7e09eb
	if (!block->cntrl) {
Packit 7e09eb
		/* It could be removed VMD drive */
Packit 7e09eb
		log_debug("Failed to get controller for dev: %s, ctrl path: %s",
Packit 7e09eb
			  block->sysfs_path, block->cntrl_path);
Packit 7e09eb
		return;
Packit 7e09eb
	}
Packit 7e09eb
	if (block->cntrl->cntrl_type == CNTRL_TYPE_SCSI) {
Packit 7e09eb
		block->host = block_get_host(block->cntrl, block->host_id);
Packit 7e09eb
		if (block->host) {
Packit 7e09eb
			if (dev_directly_attached(block->sysfs_path))
Packit 7e09eb
				cntrl_init_smp(NULL, block->cntrl);
Packit 7e09eb
			else
Packit 7e09eb
				scsi_get_enclosure(block);
Packit 7e09eb
		} else {
Packit 7e09eb
			log_debug("Failed to get host for dev: %s, hostId: %d",
Packit 7e09eb
				  block->sysfs_path, block->host_id);
Packit 7e09eb
			/* If failed, invalidate cntrl */
Packit 7e09eb
			block->cntrl = NULL;
Packit 7e09eb
		}
Packit 7e09eb
	}
Packit 7e09eb
	return;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static void _invalidate_dev(struct block_device *block)
Packit 7e09eb
{
Packit 7e09eb
	/* Those fields are valid only per 'session' - through single scan. */
Packit 7e09eb
	block->cntrl = NULL;
Packit 7e09eb
	block->host = NULL;
Packit 7e09eb
	block->enclosure = NULL;
Packit 7e09eb
	block->encl_index = -1;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static void _check_block_dev(struct block_device *block, int *restart)
Packit 7e09eb
{
Packit 7e09eb
	if (!block->cntrl) {
Packit 7e09eb
		(*restart)++;
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Sets a list of block devices and sends LED control messages.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of monitor service. Based on current layout of
Packit 7e09eb
 * sysfs tree the function extracts block devices and for each block device it
Packit 7e09eb
 * send LED control message to storage controller or enclosure. The message is
Packit 7e09eb
 * determine by appropriate field in block device's structure. See _add_block()
Packit 7e09eb
 * and _send_msg() functions description for more details.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The function does not return a value.
Packit 7e09eb
 */
Packit 7e09eb
static void _ledmon_execute(void)
Packit 7e09eb
{
Packit 7e09eb
	int restart = 0;	/* ledmon_block_list needs restart? */
Packit 7e09eb
	struct block_device *device;
Packit 7e09eb
Packit 7e09eb
	/* Revalidate each device in the list. Bring back controller and host */
Packit 7e09eb
	list_for_each(&ledmon_block_list, device)
Packit 7e09eb
		_revalidate_dev(device);
Packit 7e09eb
	/* Scan all devices and compare them against saved list */
Packit 7e09eb
	list_for_each(sysfs_get_block_devices(), device)
Packit 7e09eb
		_add_block(device);
Packit 7e09eb
	/* Send message to all devices in the list if needed. */
Packit 7e09eb
	list_for_each(&ledmon_block_list, device)
Packit 7e09eb
		_send_msg(device);
Packit 7e09eb
	/* Flush unsent messages from internal buffers. */
Packit 7e09eb
	list_for_each(&ledmon_block_list, device)
Packit 7e09eb
		_flush_msg(device);
Packit 7e09eb
	/* Check if there is any orphaned device. */
Packit 7e09eb
	list_for_each(&ledmon_block_list, device)
Packit 7e09eb
		_check_block_dev(device, &restart);
Packit 7e09eb
Packit 7e09eb
	if (restart) {
Packit 7e09eb
		/* there is at least one detached element in the list. */
Packit 7e09eb
		list_erase(&ledmon_block_list);
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static status_t _init_ledmon_conf(void)
Packit 7e09eb
{
Packit 7e09eb
	memset(&conf, 0, sizeof(struct ledmon_conf));
Packit 7e09eb
Packit 7e09eb
	/* initialize with default values */
Packit 7e09eb
	conf.blink_on_init = 1;
Packit 7e09eb
	conf.blink_on_migration = 1;
Packit 7e09eb
	conf.rebuild_blink_on_all = 0;
Packit 7e09eb
	conf.raid_members_only = 0;
Packit 7e09eb
	conf.log_level = LOG_LEVEL_WARNING;
Packit 7e09eb
	conf.scan_interval = LEDMON_DEF_SLEEP_INTERVAL;
Packit 7e09eb
	list_init(&conf.cntrls_whitelist, NULL);
Packit 7e09eb
	list_init(&conf.cntrls_blacklist, NULL);
Packit 7e09eb
	return set_log_path(LEDMON_DEF_LOG_FILE);
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static void _close_parent_fds(void)
Packit 7e09eb
{
Packit 7e09eb
	struct list dir;
Packit 7e09eb
Packit 7e09eb
	if (scan_dir("/proc/self/fd", &dir) == 0) {
Packit 7e09eb
		char *elem;
Packit 7e09eb
Packit 7e09eb
		list_for_each(&dir, elem) {
Packit 7e09eb
			int fd = (int)strtol(basename(elem), NULL, 10);
Packit 7e09eb
Packit 7e09eb
			if (fd != get_log_fd())
Packit 7e09eb
				close(fd);
Packit 7e09eb
		}
Packit 7e09eb
		list_erase(&dir;;
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 */
Packit 7e09eb
int main(int argc, char *argv[])
Packit 7e09eb
{
Packit 7e09eb
	status_t status = STATUS_SUCCESS;
Packit 7e09eb
	int ignore = 0;
Packit 7e09eb
Packit 7e09eb
	setup_options(&longopt, &shortopt, possible_params,
Packit 7e09eb
			possible_params_size);
Packit 7e09eb
	set_invocation_name(argv[0]);
Packit 7e09eb
	openlog(progname, LOG_PID | LOG_PERROR, LOG_DAEMON);
Packit 7e09eb
Packit 7e09eb
	if (on_exit(_ledmon_status, &ignore))
Packit 7e09eb
		return STATUS_ONEXIT_ERROR;
Packit 7e09eb
Packit 7e09eb
	if (_cmdline_parse_non_daemonise(argc, argv) != STATUS_SUCCESS)
Packit 7e09eb
		return STATUS_CMDLINE_ERROR;
Packit 7e09eb
Packit 7e09eb
	if (getuid() != 0) {
Packit 7e09eb
		fprintf(stderr, "Only root can run this application.\n");
Packit 7e09eb
		return STATUS_NOT_A_PRIVILEGED_USER;
Packit 7e09eb
	}
Packit 7e09eb
Packit 7e09eb
	status = _init_ledmon_conf();
Packit 7e09eb
	if (status != STATUS_SUCCESS)
Packit 7e09eb
		return status;
Packit 7e09eb
Packit 7e09eb
	status = ledmon_read_config(ledmon_conf_path);
Packit 7e09eb
	if (status != STATUS_SUCCESS)
Packit 7e09eb
		return status;
Packit 7e09eb
Packit 7e09eb
	if (_cmdline_parse(argc, argv) != STATUS_SUCCESS)
Packit 7e09eb
		return STATUS_CMDLINE_ERROR;
Packit 7e09eb
Packit 7e09eb
	ledmon_write_shared_conf();
Packit 7e09eb
Packit 7e09eb
	if (log_open(conf.log_path) != STATUS_SUCCESS)
Packit 7e09eb
		return STATUS_LOG_FILE_ERROR;
Packit 7e09eb
Packit 7e09eb
	free(shortopt);
Packit 7e09eb
	free(longopt);
Packit 7e09eb
	if (pidfile_check(progname, NULL) == 0) {
Packit 7e09eb
		log_warning("daemon is running...");
Packit 7e09eb
		return STATUS_LEDMON_RUNNING;
Packit 7e09eb
	}
Packit 7e09eb
	if (!foreground) {
Packit 7e09eb
		pid_t pid = fork();
Packit 7e09eb
Packit 7e09eb
		if (pid < 0) {
Packit 7e09eb
			log_debug("main(): fork() failed (errno=%d).", errno);
Packit 7e09eb
			exit(EXIT_FAILURE);
Packit 7e09eb
		}
Packit 7e09eb
		if (pid > 0) {
Packit 7e09eb
			ignore = 1; /* parent: don't print exit status */
Packit 7e09eb
			exit(EXIT_SUCCESS);
Packit 7e09eb
		}
Packit 7e09eb
Packit 7e09eb
		pid_t sid = setsid();
Packit 7e09eb
Packit 7e09eb
		if (sid < 0) {
Packit 7e09eb
			log_debug("main(): setsid() failed (errno=%d).", errno);
Packit 7e09eb
			exit(EXIT_FAILURE);
Packit 7e09eb
		}
Packit 7e09eb
Packit 7e09eb
		_close_parent_fds();
Packit 7e09eb
Packit 7e09eb
		int t = open("/dev/null", O_RDWR);
Packit 7e09eb
		UNUSED(dup(t));
Packit 7e09eb
		UNUSED(dup(t));
Packit 7e09eb
	}
Packit 7e09eb
Packit 7e09eb
	umask(027);
Packit 7e09eb
Packit 7e09eb
	if (chdir("/") < 0) {
Packit 7e09eb
		log_debug("main(): chdir() failed (errno=%d).", errno);
Packit 7e09eb
		exit(EXIT_FAILURE);
Packit 7e09eb
	}
Packit 7e09eb
	if (pidfile_create(progname)) {
Packit 7e09eb
		log_debug("main(): pidfile_creat() failed.");
Packit 7e09eb
		exit(EXIT_FAILURE);
Packit 7e09eb
	}
Packit 7e09eb
	_ledmon_setup_signals();
Packit 7e09eb
Packit 7e09eb
	if (on_exit(_ledmon_fini, progname))
Packit 7e09eb
		exit(STATUS_ONEXIT_ERROR);
Packit 7e09eb
	list_init(&ledmon_block_list, (item_free_t)block_device_fini);
Packit 7e09eb
	sysfs_init();
Packit 7e09eb
	log_info("monitor service has been started...");
Packit 7e09eb
	while (terminate == 0) {
Packit 7e09eb
		struct block_device *device;
Packit 7e09eb
Packit 7e09eb
		timestamp = time(NULL);
Packit 7e09eb
		sysfs_scan();
Packit 7e09eb
		_ledmon_execute();
Packit 7e09eb
		_ledmon_wait(conf.scan_interval);
Packit 7e09eb
		/* Invalidate each device in the list. Clear controller and host. */
Packit 7e09eb
		list_for_each(&ledmon_block_list, device)
Packit 7e09eb
			_invalidate_dev(device);
Packit 7e09eb
		sysfs_reset();
Packit 7e09eb
	}
Packit 7e09eb
	ledmon_remove_shared_conf();
Packit 7e09eb
	stop_udev_monitor();
Packit 7e09eb
	exit(EXIT_SUCCESS);
Packit 7e09eb
}