/* * aldad.c - Part of AFD, an automatic file distribution program. * Copyright (c) 2009 - 2023 Holger Kiehl * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "afddefs.h" DESCR__S_M3 /* ** NAME ** aldad - AFD log data analyser daemon ** ** SYNOPSIS ** aldad [options] ** ** DESCRIPTION ** ** RETURN VALUES ** ** AUTHOR ** H.Kiehl ** ** HISTORY ** 17.01.2009 H.Kiehl Created ** 22.04.2023 H.Kiehl Added option --afdmon to read configuration ** from MON_CONFIG_FILE. ** 09.05.2023 H.Kiehl If all entries are removed and realloc() returns ** NULL, evaluate errno to check if this is the ** case. ** */ DESCR__E_M3 #include #include /* realloc(), free(), atexit() */ #include /* time() */ #include #ifdef HAVE_STATX # include /* Definition of AT_* constants */ #endif #include /* stat() */ #include /* waitpid() */ #include /* signal(), kill() */ #include /* fork() */ #include #include "aldadefs.h" #include "version.h" /* Global variables. */ int no_of_process, sys_log_fd = STDERR_FILENO; char config_file[MAX_PATH_LENGTH + ETC_DIR_LENGTH + AFD_CONFIG_FILE_LENGTH + 1], *p_work_dir; struct aldad_proc_list *apl = NULL; const char *sys_log_name = SYSTEM_LOG_FIFO; /* Local function prototypes. */ #ifdef WITH_AFD_MON static pid_t make_process(char *, int); #else static pid_t make_process(char *); #endif static void aldad_exit(void), sig_segv(int), sig_bus(int), sig_exit(int), zombie_check(void); /*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ aldad $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*/ int main(int argc, char *argv[]) { #ifdef WITH_AFD_MON int remote_log_data; #endif time_t next_stat_time, now, old_st_mtime; char work_dir[MAX_PATH_LENGTH]; /* Evaluate input arguments. */ CHECK_FOR_VERSION(argc, argv); #ifdef WITH_AFD_MON if (get_arg(&argc, argv, "--afdmon", NULL, 0) == SUCCESS) { if (get_mon_path(&argc, argv, work_dir) < 0) { exit(INCORRECT); } (void)sprintf(config_file, "%s%s%s", work_dir, ETC_DIR, MON_CONFIG_FILE); remote_log_data = YES; } else { #endif if (get_afd_path(&argc, argv, work_dir) < 0) { exit(INCORRECT); } (void)sprintf(config_file, "%s%s%s", work_dir, ETC_DIR, AFD_CONFIG_FILE); #ifdef WITH_AFD_MON remote_log_data = NO; } #endif /* Initialize variables. */ p_work_dir = work_dir; next_stat_time = 0L; old_st_mtime = 0L; no_of_process = 0; /* Do some cleanups when we exit. */ if (atexit(aldad_exit) != 0) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Could not register exit function : %s", strerror(errno)); exit(INCORRECT); } if ((signal(SIGINT, sig_exit) == SIG_ERR) || (signal(SIGQUIT, sig_exit) == SIG_ERR) || (signal(SIGTERM, sig_exit) == SIG_ERR) || (signal(SIGSEGV, sig_segv) == SIG_ERR) || (signal(SIGBUS, sig_bus) == SIG_ERR) || (signal(SIGHUP, SIG_IGN) == SIG_ERR)) { system_log(FATAL_SIGN, __FILE__, __LINE__, _("Could not set signal handler : %s"), strerror(errno)); exit(INCORRECT); } system_log(INFO_SIGN, NULL, 0, "Started %s.", ALDAD); /* Lets determine what log files we need to search. */ for (;;) { now = time(NULL); if (next_stat_time < now) { #ifdef HAVE_STATX struct statx stat_buf; #else struct stat stat_buf; #endif next_stat_time = now + STAT_INTERVAL; #ifdef HAVE_STATX if (statx(0, config_file, AT_STATX_SYNC_AS_STAT, STATX_MTIME, &stat_buf) == 0) #else if (stat(config_file, &stat_buf) == 0) #endif { #ifdef HAVE_STATX if (stat_buf.stx_mtime.tv_sec != old_st_mtime) #else if (stat_buf.st_mtime != old_st_mtime) #endif { int i; char *buffer, tmp_aldad[MAX_PATH_LENGTH + 1]; for (i = 0; i < no_of_process; i++) { apl[i].in_list = NO; } #ifdef HAVE_STATX old_st_mtime = stat_buf.stx_mtime.tv_sec; #else old_st_mtime = stat_buf.st_mtime; #endif if ((eaccess(config_file, F_OK) == 0) && (read_file_no_cr(config_file, &buffer, YES, __FILE__, __LINE__) != INCORRECT)) { int gotcha; char *ptr = buffer; system_log(DEBUG_SIGN, NULL, 0, "ALDAD read %s", config_file); /* * Read all alda daemons entries. */ do { if ((ptr = get_definition(ptr, ALDA_DAEMON_DEF, tmp_aldad, MAX_PATH_LENGTH)) != NULL) { /* First check if we already have this in our list. */ gotcha = NO; for (i = 0; i < no_of_process; i++) { if (my_strcmp(apl[i].parameters, tmp_aldad) == 0) { apl[i].in_list = YES; gotcha = YES; break; } } if (gotcha == NO) { no_of_process++; if ((apl = realloc(apl, (no_of_process * sizeof(struct aldad_proc_list)))) == NULL) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Failed to realloc() memory : %s", strerror(errno)); exit(INCORRECT); } if ((apl[no_of_process - 1].parameters = malloc((strlen(tmp_aldad) + 1))) == NULL) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Failed to malloc() memory : %s", strerror(errno)); exit(INCORRECT); } (void)strcpy(apl[no_of_process - 1].parameters, tmp_aldad); apl[no_of_process - 1].in_list = YES; #ifdef WITH_AFD_MON if ((apl[no_of_process - 1].pid = make_process(apl[no_of_process - 1].parameters, remote_log_data)) == 0) #else if ((apl[no_of_process - 1].pid = make_process(apl[no_of_process - 1].parameters)) == 0) #endif { system_log(ERROR_SIGN, __FILE__, __LINE__, "Failed to start aldad process with the following parameters : %s", apl[no_of_process - 1].parameters); free(apl[no_of_process - 1].parameters); no_of_process--; if ((apl = realloc(apl, (no_of_process * sizeof(struct aldad_proc_list)))) == NULL) { if (errno != 0) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Failed to realloc() memory : %s", strerror(errno)); exit(INCORRECT); } } } } } } while (ptr != NULL); for (i = 0; i < no_of_process; i++) { if (apl[i].in_list == NO) { if (kill(apl[i].pid, SIGINT) == -1) { system_log(WARN_SIGN, __FILE__, __LINE__, #if SIZEOF_PID_T == 4 "Failed to kill() process %d with parameters %s", #else "Failed to kill() process %lld with parameters %s", #endif (pri_pid_t)apl[i].pid, apl[i].parameters); } else { free(apl[i].parameters); if (i < no_of_process) { (void)memmove(&apl[i], &apl[i + 1], ((no_of_process - (i + 1)) * sizeof(struct aldad_proc_list))); } no_of_process--; i--; if ((apl = realloc(apl, (no_of_process * sizeof(struct aldad_proc_list)))) == NULL) { if (errno != 0) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Failed to realloc() memory : %s", strerror(errno)); exit(INCORRECT); } } } } } free(buffer); if (no_of_process > 0) { system_log(INFO_SIGN, NULL, 0, "ALDAD %d process running.", no_of_process); } else { system_log(DEBUG_SIGN, NULL, 0, "ALDAD no definitions found."); } } } } } zombie_check(); sleep(5L); } exit(SUCCESS); } /*++++++++++++++++++++++++++++ make_process() +++++++++++++++++++++++++++*/ static pid_t #ifdef WITH_AFD_MON make_process(char *parameters, int remote_log_data) #else make_process(char *parameters) #endif { pid_t proc_id; switch (proc_id = fork()) { case -1: /* Could not generate process. */ system_log(FATAL_SIGN, __FILE__, __LINE__, "Could not create a new process : %s", strerror(errno)); exit(INCORRECT); case 0: /* Child process. */ { int length, work_dir_length; char *cmd; work_dir_length = strlen(p_work_dir); length = 5 + 3 + work_dir_length + 1 + 6 + strlen(parameters) + 1; if ((cmd = malloc(length)) == NULL) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Failed to malloc() %d bytes : %s", length, strerror(errno)); exit(INCORRECT); } cmd[0] = 'a'; cmd[1] = 'l'; cmd[2] = 'd'; cmd[3] = 'a'; cmd[4] = ' '; cmd[5] = '-'; cmd[6] = 'w'; cmd[7] = ' '; (void)strcpy(&cmd[8], p_work_dir); cmd[8 + work_dir_length] = ' '; cmd[9 + work_dir_length] = '-'; cmd[10 + work_dir_length] = 'C'; cmd[11 + work_dir_length] = ' '; #ifdef WITH_AFD_MON if (remote_log_data == YES) { cmd[12 + work_dir_length] = '-'; cmd[13 + work_dir_length] = 'r'; cmd[14 + work_dir_length] = ' '; } else { #endif cmd[12 + work_dir_length] = '-'; cmd[13 + work_dir_length] = 'l'; cmd[14 + work_dir_length] = ' '; #ifdef WITH_AFD_MON } #endif (void)strcpy(&cmd[15 + work_dir_length], parameters); system_log(DEBUG_SIGN, NULL, 0, "aldad: %s", cmd); (void)execl("/bin/sh", "sh", "-c", cmd, (char *)0); } exit(SUCCESS); default: /* Parent process. */ return(proc_id); } return(INCORRECT); } /*++++++++++++++++++++++++++++ zombie_check() +++++++++++++++++++++++++++*/ static void zombie_check(void) { int exit_status, i, status; for (i = 0; i < no_of_process; i++) { if (waitpid(apl[i].pid, &status, WNOHANG) > 0) { if (WIFEXITED(status)) { exit_status = WEXITSTATUS(status); if (exit_status != 0) { system_log(WARN_SIGN, __FILE__, __LINE__, "Alda log process (%s) died, return code is %d", apl[i].parameters, exit_status); } free(apl[i].parameters); if (i < (no_of_process - 1)) { (void)memmove(&apl[i], &apl[i + 1], ((no_of_process - i) * sizeof(struct aldad_proc_list))); } no_of_process--; i--; if (no_of_process == 0) { free(apl); apl = NULL; } else { if ((apl = realloc(apl, (no_of_process * sizeof(struct aldad_proc_list)))) == NULL) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Failed to realloc() memory : %s", strerror(errno)); exit(INCORRECT); } } } else if (WIFSIGNALED(status)) { /* Termination caused by signal. */ system_log(WARN_SIGN, __FILE__, __LINE__, "Alda log process (%s) terminated by signal %d.", apl[i].parameters, WTERMSIG(status)); free(apl[i].parameters); if (i < no_of_process) { (void)memmove(&apl[i], &apl[i + 1], ((no_of_process - i) * sizeof(struct aldad_proc_list))); } no_of_process--; i--; if (no_of_process == 0) { free(apl); apl = NULL; } else { if ((apl = realloc(apl, (no_of_process * sizeof(struct aldad_proc_list)))) == NULL) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Failed to realloc() memory : %s", strerror(errno)); exit(INCORRECT); } } } else if (WIFSTOPPED(status)) { /* Child has been stopped. */ system_log(WARN_SIGN, __FILE__, __LINE__, "Alda log process (%s) received STOP signal.", apl[i].parameters); } } } return; } /*+++++++++++++++++++++++++++++ aldad_exit() ++++++++++++++++++++++++++++*/ static void aldad_exit(void) { int i; system_log(INFO_SIGN, NULL, 0, "Stopped %s.", ALDAD); /* Kill all jobs that where started. */ for (i = 0; i < no_of_process; i++) { if (kill(apl[i].pid, SIGINT) < 0) { if (errno != ESRCH) { system_log(ERROR_SIGN, __FILE__, __LINE__, #if SIZEOF_PID_T == 4 "Failed to kill process alda with pid %d : %s", #else "Failed to kill process alda with pid %lld : %s", #endif (pri_pid_t)apl[i].pid, strerror(errno)); } } } return; } /*++++++++++++++++++++++++++++++ sig_segv() +++++++++++++++++++++++++++++*/ static void sig_segv(int signo) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Aaarrrggh! Received SIGSEGV."); aldad_exit(); abort(); } /*++++++++++++++++++++++++++++++ sig_bus() ++++++++++++++++++++++++++++++*/ static void sig_bus(int signo) { system_log(FATAL_SIGN, __FILE__, __LINE__, "Uuurrrggh! Received SIGBUS."); aldad_exit(); abort(); } /*++++++++++++++++++++++++++++++ sig_exit() +++++++++++++++++++++++++++++*/ static void sig_exit(int signo) { int ret; (void)fprintf(stderr, #if SIZEOF_PID_T == 4 "%s terminated by signal %d (%d)\n", #else "%s terminated by signal %d (%lld)\n", #endif ALDAD, signo, (pri_pid_t)getpid()); if ((signo == SIGINT) || (signo == SIGTERM)) { ret = SUCCESS; } else { ret = INCORRECT; } exit(ret); }