Blob Blame History Raw
/*
 * Soft:        Keepalived is a failover program for the LVS project
 *              <www.linuxvirtualserver.org>. It monitor & manipulate
 *              a loadbalanced server pool using multi-layer checks.
 *
 * Part:        FILE CHECK. Monitor contents if a file
 *
 * Authors:     Quentin Armitage, <quentin@armitage.org.uk>
 *
 *              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.
 *
 *              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.
 *
 * Copyright (C) 2020-2020 Alexandre Cassen, <acassen@gmail.com>
 */

#include "config.h"

#include <stdio.h>

#include "check_file.h"
#include "check_data.h"
#include "track_file.h"
#include "parser.h"
#include "logger.h"
#include "check_data.h"
#include "logger.h"
#include "main.h"


static void
free_file_check(checker_t *checker)
{
	FREE(checker);
}

static void
dump_file_check(FILE *fp, const checker_t *checker)
{
	tracked_file_t *tfp = checker->data;

	conf_write(fp, "   Keepalive method = FILE_CHECK");
	conf_write(fp, "     Tracked file = %s", tfp->fname);
	conf_write(fp, "     Reloaded = %s", tfp->reloaded ? "Yes" : "No");
}

static bool
file_check_compare(const checker_t *old_c, checker_t *new_c)
{
	const tracked_file_t *old = old_c->data;
	tracked_file_t *new = new_c->data;

	if (strcmp(old->file_path, new->file_path))
		return false;
	if (old->weight != new->weight)
		return false;
	if (old->weight_reverse != new->weight_reverse)
		return false;

	new->reloaded = true;

	return true;
}

static void
track_file_handler(const vector_t *strvec)
{
	virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
	real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
	tracked_file_monitor_t *tfile;
	tracked_file_t *vsf;

	tfile = list_last_entry(&rs->track_files, tracked_file_monitor_t, e_list);

	vsf = find_tracked_file_by_name(strvec_slot(strvec, 1), &check_data->track_files);
	if (!vsf) {
		report_config_error(CONFIG_GENERAL_ERROR, "track_file %s not found", strvec_slot(strvec, 1));
		return;
	}

	tfile->file = vsf;
}

static void
file_check_handler(__attribute__((unused)) const vector_t *strvec)
{
	virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
	real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
	tracked_file_monitor_t *tfile;

	PMALLOC(tfile);
	INIT_LIST_HEAD(&tfile->e_list);
	list_add_tail(&tfile->e_list, &rs->track_files);
}

static void
track_file_weight_handler(const vector_t *strvec)
{
	virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
	real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
	tracked_file_monitor_t *tfile;
	int weight;
	bool reverse = false;

	tfile = list_last_entry(&rs->track_files, tracked_file_monitor_t, e_list);

	if (vector_size(strvec) < 2) {
		report_config_error(CONFIG_GENERAL_ERROR, "track file weight missing");
		return;
	}

	if (!read_int_strvec(strvec, 1, &weight, -IPVS_WEIGHT_MAX, IPVS_WEIGHT_MAX, true)) {
		report_config_error(CONFIG_GENERAL_ERROR, "weight for track file must be in "
				 "[-IPVS_WEIGHT_MAX..IPVS_WEIGHT_MAX] inclusive. Ignoring...");
		return;
	}

	if (vector_size(strvec) >= 3) {
		if (!strcmp(strvec_slot(strvec, 2), "reverse"))
			reverse = true;
		else if (!strcmp(strvec_slot(strvec, 2), "noreverse"))
			reverse = false;
		else {
			report_config_error(CONFIG_GENERAL_ERROR, "unknown track file weight option %s - ignoring",
					 strvec_slot(strvec, 2));
			return;
		}
	}

	tfile->weight = weight;
	tfile->weight_reverse = reverse;
}

static void
file_end_handler(void)
{
	virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
	real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
	tracked_file_monitor_t *tfile;

	tfile = list_last_entry(&rs->track_files, tracked_file_monitor_t, e_list);

	if (!tfile->file) {
		report_config_error(CONFIG_GENERAL_ERROR, "FILE_CHECK has no track_file specified - ignoring");
		free_track_file_monitor(tfile);
		return;
	}

	if (!tfile->weight) {
		tfile->weight = tfile->file->weight;
		tfile->weight_reverse = tfile->file->weight_reverse;
	}
}

void
install_file_check_keyword(void)
{
	install_keyword("FILE_CHECK", &file_check_handler);
	install_sublevel();
	install_keyword("track_file", &track_file_handler);
	install_keyword("weight", &track_file_weight_handler);
	install_sublevel_end_handler(&file_end_handler);
	install_sublevel_end();
}

void
add_rs_to_track_files(void)
{
	virtual_server_t *vs;
	real_server_t *rs;
	tracked_file_monitor_t *tfl;
	checker_t *new_checker;

	list_for_each_entry(vs, &check_data->vs, e_list) {
		list_for_each_entry(rs, &vs->rs, e_list) {
			list_for_each_entry(tfl, &rs->track_files, e_list) {
				/* queue new checker */
				new_checker = queue_checker(free_file_check, dump_file_check, NULL, file_check_compare, tfl->file, NULL, false);
				new_checker->vs = vs;
				new_checker->rs = rs;

				/* There is no concept of the checker running, but we will have
				 * checked the file, so mark it as run. */
				new_checker->has_run = true;

				add_obj_to_track_file(new_checker, tfl, FMT_RS(rs, vs), dump_tracking_rs);
			}
		}
	}
}

void
set_track_file_checkers_down(void)
{
	tracked_file_t *tfl;
	tracking_obj_t *top;
	int status;

	list_for_each_entry(tfl, &check_data->track_files, e_list) {
		if (tfl->last_status) {
			list_for_each_entry(top, &tfl->tracking_obj, e_list) {
				checker_t *checker = top->obj.checker;

				if (!top->weight) {
					if (reload && !tfl->reloaded) {
						/* This is pretty horrible. At some stage this should
						 * be tidied up so that it works without having to
						 * fudge the values to make update_track_file_status()
						 * work for us. */
						status = tfl->last_status;
						tfl->last_status = 0;
						process_update_checker_track_file_status(tfl, !!status == (top->weight_multiplier == 1) ? INT_MIN : 0, top);
						tfl->last_status = status;
					} else
						checker->is_up = false;
				}
			}
		}
	}
}

#ifdef THREAD_DUMP
void
register_check_file_addresses(void)
{
	register_track_file_inotify_addresses();
}
#endif