377 lines
9.7 KiB
C++
377 lines
9.7 KiB
C++
/*
|
|
* Vermont
|
|
* Copyright (C) 2009 Vermont Project
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
this is vermont.
|
|
released under GPL v2
|
|
|
|
(C) by Ronny T. Lampert
|
|
(C) by Lothar Braun <mail@lobraun.de>
|
|
|
|
*/
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
|
|
/* own systems */
|
|
#include "common/msg.h"
|
|
#include "common/VermontControl.h"
|
|
#include "common/defs.h"
|
|
#include "core/MainSignalHandler.h"
|
|
|
|
#include "modules/ConfigManager.hpp"
|
|
|
|
using namespace std;
|
|
|
|
struct parameters {
|
|
const char *config_file;
|
|
const char *pid_file;
|
|
uid_t uid;
|
|
gid_t gid;
|
|
bool daemon_mode;
|
|
};
|
|
|
|
|
|
static void
|
|
record_pid (const char *pidfile)
|
|
{
|
|
FILE *f = fopen(pidfile, "w");
|
|
if (!f) {
|
|
msg(LOG_CRIT, "Could not open pid-file %s for writing", pidfile);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
fprintf(f, "%d\n", getpid());
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_groups (uid_t uid, gid_t gid)
|
|
{
|
|
int groups_count = 0;
|
|
gid_t *groups = NULL;
|
|
|
|
struct passwd *pw = getpwuid(uid);
|
|
if (!pw) {
|
|
msg(LOG_CRIT, "could not getpwuid: %s", strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* With groups NULL and groups_count 0, getgrouplist will write the actual
|
|
* number of groups into groups_count.
|
|
*/
|
|
getgrouplist(pw->pw_name, gid, groups, &groups_count);
|
|
|
|
/*
|
|
* List must be on the stack.
|
|
*/
|
|
groups = (gid_t *)alloca(groups_count * sizeof(gid_t));
|
|
|
|
if (getgrouplist(pw->pw_name, gid, groups, &groups_count) <= 0) {
|
|
msg(LOG_CRIT, "could not getgrouplist: %s", strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (setgroups(groups_count, groups) < 0) {
|
|
msg(LOG_CRIT, "could not setgroups: %s", strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
daemonise (const char *pid_file, uid_t uid, gid_t gid)
|
|
{
|
|
/*
|
|
* 1 to stay in the current working dir, 0 to redirect stdin/out/err to
|
|
* dev/null
|
|
*/
|
|
if (daemon(1, 0) < 0) {
|
|
msg(LOG_CRIT, "daemon failed");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (pid_file) {
|
|
record_pid(pid_file);
|
|
}
|
|
|
|
if (gid) {
|
|
set_groups(uid, gid);
|
|
}
|
|
|
|
if (uid && setreuid(0, uid) < 0) {
|
|
msg(LOG_CRIT, "setreuid %u fail: %s", uid, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
usage (int status)
|
|
{
|
|
printf("VERsatile MONitoring Tool - VERMONT\n"
|
|
" MANDATORY OPTIONS:\n"
|
|
" -f, --config-file FILE Use configuration file\n"
|
|
"\n OTHER OPTIONS:\n"
|
|
" -h, --help Display this help and exit\n"
|
|
" -d, --debug Log verbosity:\n\n"
|
|
" -d NOTICE\n"
|
|
" -dd INFO\n"
|
|
" -ddd DEBUG\n"
|
|
" 'logging' attribute in the configuration\n"
|
|
" file takes precedence\n"
|
|
" -l, --log-level LEVEL Log level.\n"
|
|
" In increasing order:\n\n"
|
|
" debug\n"
|
|
" info\n"
|
|
" notice\n"
|
|
" warning\n"
|
|
" error\n"
|
|
" critical\n\n"
|
|
" Default: warning\n"
|
|
" 'logging' attribute in the configuration\n"
|
|
" file takes precedence\n"
|
|
" -q, --quiet Do not write output to console (implied by -b)\n"
|
|
" -b, --daemon Run in daemon mode (implies -q)\n"
|
|
" -p, --pid-file FILE Set process id filename (use with -b)\n"
|
|
" -u, --user USER Change user to USER (use with -b)\n"
|
|
" -g, --group GROUP Change group to GROUP (use with -b)\n"
|
|
" -s, --syslog Log to syslog\n"
|
|
#ifdef JOURNALD_SUPPORT_ENABLED
|
|
" -j, --journald Log to journald\n"
|
|
#endif
|
|
);
|
|
|
|
exit(status);
|
|
}
|
|
|
|
static int
|
|
parse_args (int argc, char **argv, struct parameters *params)
|
|
{
|
|
int opt, ret, option_index, log_bitask;
|
|
struct passwd *pw;
|
|
struct group *gr;
|
|
|
|
static const struct option long_opts[] = {
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "config-file", required_argument, NULL, 'f' },
|
|
{ "daemon", no_argument, NULL, 'b' },
|
|
{ "pid-file", required_argument, NULL, 'p' },
|
|
{ "user", required_argument, NULL, 'u' },
|
|
{ "group", required_argument, NULL, 'g' },
|
|
{ "quiet", no_argument, NULL, 'q' },
|
|
{ "debug", no_argument, NULL, 'd' },
|
|
{ "log-level", required_argument, NULL, 'l' },
|
|
#ifdef JOURNALD_SUPPORT_ENABLED
|
|
{ "journald", no_argument, NULL, 'j' },
|
|
#endif
|
|
{ "syslog", no_argument, NULL, 's' },
|
|
{ NULL, 0, NULL, 0}
|
|
};
|
|
|
|
while ((opt = getopt_long(argc, argv, "hbp:u:g:df:ql:js", long_opts,
|
|
&option_index)) != EOF) {
|
|
switch (opt) {
|
|
case 'b':
|
|
params->daemon_mode = true;
|
|
msg_setquiet(true);
|
|
break;
|
|
|
|
case 'f':
|
|
params->config_file = optarg;
|
|
break;
|
|
|
|
case 'p':
|
|
params->pid_file = optarg;
|
|
break;
|
|
|
|
case 'u':
|
|
// Drop privileges and run as this user after initialisation.
|
|
pw = getpwnam(optarg);
|
|
if (!pw) {
|
|
msg(LOG_CRIT, "unknown user '%s'", optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
params->uid = pw->pw_uid;
|
|
break;
|
|
|
|
case 'g':
|
|
// drop privileges and run as this group after initialisation.
|
|
gr = getgrnam(optarg);
|
|
if (!gr) {
|
|
msg(LOG_CRIT, "unknown group '%s'", optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
params->gid = gr->gr_gid;
|
|
break;
|
|
|
|
case 'h':
|
|
usage(EXIT_SUCCESS);
|
|
break;
|
|
|
|
case 'd':
|
|
/*
|
|
* Default log_level is LOG_WARNING (1 << 4). For each -d,
|
|
* bump log_level up to LOG_DEBUG (1 << 7).
|
|
*/
|
|
if (!(msg_getlevel() & LOG_MASK(LOG_DEBUG))) {
|
|
msg_setlevel(msg_getlevel() | (msg_getlevel() << 1));
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
log_bitask = parse_log_level(optarg);
|
|
if (log_bitask == -1) {
|
|
msg(LOG_CRIT, "ignoring unknown log level '%s'", optarg);
|
|
} else {
|
|
msg_setlevel(log_bitask);
|
|
}
|
|
break;
|
|
|
|
case 'q':
|
|
msg_setquiet(true);
|
|
break;
|
|
|
|
#ifdef JOURNALD_SUPPORT_ENABLED
|
|
case 'j':
|
|
msg_set_journald(true);
|
|
break;
|
|
#endif
|
|
|
|
case 's':
|
|
msg_set_syslog(true);
|
|
break;
|
|
|
|
default:
|
|
usage(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* reset getopt library
|
|
*/
|
|
ret = optind - 1;
|
|
optind = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main(int ac, char **dc)
|
|
{
|
|
/* manager is defined at the scope of the main function in order to
|
|
* steer clear of headaches which are related to the order of
|
|
* initialisation of static objects.
|
|
* Let's look at an example to better understand this requirement: The
|
|
* constructor of ConfigMananger indirectly uses the logging facility.
|
|
* As a result, we have to make sure that all static objects related to
|
|
* the logging facility are initialized before the c'tor of
|
|
* ConfigManager makes use of them. By defining manager inside main(),
|
|
* we ensure that all static objects have been initialized by the time
|
|
* the c'tor of ConfigManager is run. The same goes for the
|
|
* destructors: The d'tor of ConfigManager must be executed *before*
|
|
* the d'tors of the logging facility because the former makes use of
|
|
* the latter. */
|
|
ConfigManager *manager = new ConfigManager();
|
|
struct parameters parameters;
|
|
|
|
memset(¶meters, 0, sizeof(struct parameters));
|
|
msg_init();
|
|
|
|
/* parse command line */
|
|
if (parse_args (ac, dc, ¶meters) < 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (parameters.config_file == NULL) {
|
|
msg(LOG_CRIT, "no config file given, but mandatory");
|
|
usage(EXIT_FAILURE);
|
|
}
|
|
|
|
if (parameters.daemon_mode) {
|
|
daemonise(parameters.pid_file, parameters.uid, parameters.gid);
|
|
}
|
|
|
|
/**< Wrapper for the main thread's signal handlers*/
|
|
MainSignalHandler main_signal_handler;
|
|
|
|
#ifdef __APPLE__
|
|
if (semaphore_create(mach_task_self(), &mainSemaphore, SYNC_POLICY_FIFO, 0) != KERN_SUCCESS) {
|
|
#else
|
|
if (sem_init(&mainSemaphore, 0, 0) == -1) {
|
|
#endif
|
|
THROWEXCEPTION("failed to setup semaphore");
|
|
}
|
|
|
|
msg(LOG_WARNING, "starting up vermont config manager");
|
|
|
|
manager->parseConfig(string(parameters.config_file));
|
|
|
|
sigset_t sigmask;
|
|
sigemptyset(&sigmask);
|
|
|
|
msg(LOG_WARNING, "vermont is up and running");
|
|
|
|
while (run_program) {
|
|
// sleep until we get a signal
|
|
bool b;
|
|
while (((b=timeoutsem.wait(DELETER_PERIOD)) == true) && errno == EINTR) {}// restart when interrupted by handler
|
|
|
|
if (b == false){
|
|
manager->onTimeout2();
|
|
}
|
|
|
|
if (reload_config) {
|
|
msg(LOG_NOTICE, "reconfiguring vermont");
|
|
manager->parseConfig(string(parameters.config_file));
|
|
reload_config = false;
|
|
}
|
|
}
|
|
msg(LOG_CRIT, "got signal - shutting down manager");
|
|
manager->shutdown();
|
|
delete manager;
|
|
msg(LOG_CRIT, "manager shutdown complete");
|
|
|
|
msg_shutdown();
|
|
}
|
|
|
|
//static void __cplusplus_really_sucks_andPeopleUsingVeryStrangeNamingConventionsWithLongLongExplicitBlaBlaAndfUnNycasE()
|
|
//{
|
|
/*
|
|
The Paris crew at MOME Interop 2005
|
|
|
|
Christoph "C'est bon" "Das koennte beruehmt sein" Sommer
|
|
Lothar "Pragmatic" "Ai verdammt, das funktioniert nicht" Braun
|
|
Ronny T. "Agent Provocateur" "Le (SVN-)depot, c'est moi" Lampert
|
|
|
|
|
|
Thanks to
|
|
|
|
Falko "Ich hab da mal so ne Idee" Dressler
|
|
Gerhard "Man muesste mal" Muenz
|
|
|
|
who made it possible to fly to Paris.
|
|
*/
|
|
//};
|