/* * Copyright (C), 2000-2007 by the monit project group. * All Rights Reserved. * * 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 3 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, see . */ #include #ifdef HAVE_STDIO_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "monitor.h" #include "net.h" #include "socket.h" #include "event.h" /** * Methods for controlling services managed by monit. * * @author Jan-Henrik Haukeland, * @author Rory Toma, * @author Martin Pala, * * @version \$Id: control.c,v 1.107 2007/07/25 12:54:28 hauk Exp $ * * @file */ /* -------------------------------------------------------------- Prototypes */ static void do_start(Service_T); static int do_stop(Service_T); static void do_monitor(Service_T); static void do_unmonitor(Service_T); static void *wait_start(void *); static int wait_stop(Service_T); static void do_depend(Service_T, int); /* ------------------------------------------------------------------ Public */ /** * Pass on to methods in http/cervlet.c to start/stop services * @param S A service name as stated in the config file * @param action A string describing the action to execute */ void control_service_daemon(const char *S, const char *action) { Socket_T s; ASSERT(S); ASSERT(action); if(Util_getAction(action) == ACTION_IGNORE) { LogError("%s: Cannot %s service '%s' -- invalid action %s\n", prog, action, S, action); return; } s= socket_new(Run.bind_addr?Run.bind_addr:"localhost", Run.httpdport, SOCKET_TCP, Run.httpdssl, NET_TIMEOUT); if(!s) { LogError("%s: Cannot connect to the monit daemon." " Did you start it with http support?\n", prog); } else { char *auth = Util_getBasicAuthHeader(); if(socket_print(s, "POST /%s HTTP/1.0\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %d\r\n" "%s" "\r\n" "action=%s", S, strlen("action=") + strlen(action), auth?auth:"", action) < 0) { LogError("%s: Cannot send the command '%s' to the monit daemon -- %s", prog, action?action:"null", STRERROR); } FREE(auth); socket_free(&s); } } /** * Check to see if we should try to start/stop service * @param S A service name as stated in the config file * @param A A string describing the action to execute */ void control_service_string(const char *S, const char *A) { int a; ASSERT(S); ASSERT(A); if((a = Util_getAction(A)) == ACTION_IGNORE) { LogError("%s: service '%s' -- invalid action %s\n", prog, S, A); return; } control_service(S, a); } /** * Check to see if we should try to start/stop service * @param S A service name as stated in the config file * @param A An action id describing the action to execute */ void control_service(const char *S, int A) { Service_T s = NULL; ASSERT(S); if(!(s= Util_getService(S))) { LogError("%s: service '%s' -- doesn't exist\n", prog, S); return; } switch(A) { case ACTION_START: if(s->type==TYPE_PROCESS) { if(Util_isProcessRunning(s)) { DEBUG("%s: Process already running -- process %s\n", prog, S); Util_monitorSet(s); return; } if(!s->start) { DEBUG("%s: Start method not defined -- process %s\n", prog, S); Util_monitorSet(s); return; } } do_depend(s, ACTION_STOP); do_start(s); do_depend(s, ACTION_START); break; case ACTION_STOP: if(s->type==TYPE_PROCESS && !s->stop) { DEBUG("%s: Stop method not defined -- process %s\n", prog, S); Util_monitorUnset(s); return; } /* soft unmonitor and stop: */ do_depend(s, ACTION_STOP); do_stop(s); /* hard unmonitor - will reset all counters and flags: */ do_depend(s, ACTION_UNMONITOR); do_unmonitor(s); break; case ACTION_RESTART: if(s->type==TYPE_PROCESS && (!s->start || !s->stop)) { DEBUG("%s: Start or stop method not defined -- process %s\n", prog, S); Util_monitorSet(s); return; } LogInfo("'%s' trying to restart\n", s->name); do_depend(s, ACTION_STOP); if(do_stop(s)) { /* Only start if stop succeeded */ do_start(s); do_depend(s, ACTION_START); } else { /* enable monitoring of this service again to allow the restart retry * in the next cycle up to timeout limit */ Util_monitorSet(s); } break; case ACTION_MONITOR: /* We only enable monitoring of this service and all prerequisite * services. Chain of services which depends on this service keep * its state */ do_monitor(s); break; case ACTION_UNMONITOR: /* We disable monitoring of this service and all services which * depends on it */ do_depend(s, ACTION_UNMONITOR); do_unmonitor(s); break; default: LogError("%s: service '%s' -- invalid action %s\n", prog, S, A); break; } } /* * Reset the visited flags used when handling dependencies */ void reset_depend() { Service_T s; for(s= servicelist; s; s= s->next) { s->visited= FALSE; s->depend_visited= FALSE; } } /* ----------------------------------------------------------------- Private */ /* * This is a post- fix recursive function for starting every service * that s depends on before starting s. * @param s A Service_T object */ static void do_start(Service_T s) { ASSERT(s); if(s->visited) return; s->visited= TRUE; if(s->dependantlist) { Dependant_T d; for(d= s->dependantlist; d; d= d->next ) { Service_T parent= Util_getService(d->dependant); ASSERT(parent); do_start(parent); } } if(s->start && (s->type!=TYPE_PROCESS || !Util_isProcessRunning(s))) { int status; pthread_t thread; LogInfo("'%s' start: %s\n", s->name, s->start->arg[0]); spawn(s, s->start, "Started"); if(s->type==TYPE_PROCESS) { /* We only wait for a process type, other service types does not * have a pid file to watch */ LOCK(Run.mutex) Run.wait_start++; END_LOCK; status= pthread_create(&thread, NULL, wait_start, s); if(status != 0) { LOCK(Run.mutex) Run.wait_start--; END_LOCK; LogError("Warning: Failed to create the start controller thread. " "Thread error -- %s.\n", strerror(status)); } } } Util_monitorSet(s); } /* * This function simply stops the service p. * @param s A Service_T object * @return TRUE if the service was stopped otherwise FALSE */ static int do_stop(Service_T s) { ASSERT(s); if(s->depend_visited) return TRUE; s->depend_visited= TRUE; /* do soft unmonitor - start counter and error state is kept */ if(s->monitor != MONITOR_NOT) { s->monitor= MONITOR_NOT; DEBUG("Monitoring disabled -- service %s\n", s->name); } if(s->stop && (s->type!=TYPE_PROCESS || Util_isProcessRunning(s))) { LogInfo("'%s' stop: %s\n", s->name, s->stop->arg[0]); spawn(s, s->stop, "Stopped"); if(s->type==TYPE_PROCESS) { /* Only wait for process service types */ if(!wait_stop(s)) return FALSE; } } Util_resetInfo(s); return TRUE; } /* * This is a post- fix recursive function for enabling monitoring every service * that s depends on before monitor s. * @param s A Service_T object */ static void do_monitor(Service_T s) { ASSERT(s); if(s->visited) return; s->visited= TRUE; if(s->dependantlist) { Dependant_T d; for(d= s->dependantlist; d; d= d->next ) { Service_T parent= Util_getService(d->dependant); ASSERT(parent); do_monitor(parent); } } Util_monitorSet(s); } /* * This is a function for disabling monitoring * @param s A Service_T object */ static void do_unmonitor(Service_T s) { ASSERT(s); if(s->depend_visited) return; s->depend_visited= TRUE; Util_monitorUnset(s); } /* * This is an in-fix recursive function called before s is started to * stop every service that depends on s, in reverse order *or* after s * was started to start again every service that depends on s. The * action parametere controls if this function should start or stop * the procceses that depends on s. * @param s A Service_T object * @param action An action to do on the dependant services */ static void do_depend(Service_T s, int action) { Service_T child; ASSERT(s); for(child= servicelist; child; child= child->next) { if(child->dependantlist) { Dependant_T d; for(d= child->dependantlist; d; d= d->next) { if(IS(d->dependant, s->name)) { if(action == ACTION_START) do_start(child); else if(action == ACTION_MONITOR) do_monitor(child); do_depend(child, action); if(action == ACTION_STOP) do_stop(child); else if(action == ACTION_UNMONITOR) do_unmonitor(child); break; } } } } } /* * This function runs in its own thread and waits for the service to * start running. If the service did not start a failed event is * posted to notify the user. * @param service A Service to wait for */ static void *wait_start(void *service) { Service_T s= service; int max_tries= Run.polltime; ASSERT(s); pthread_detach(pthread_self()); while(max_tries-- && !Run.stopped) { if(Util_isProcessRunning(s)) break; sleep(1); } if(!Util_isProcessRunning(s)) { Event_post(s, EVENT_EXEC, STATE_FAILED, s->action_EXEC, "'%s' failed to start", s->name); } else { Event_post(s, EVENT_EXEC, STATE_PASSED, s->action_EXEC, "'%s' started", s->name); } LOCK(Run.mutex) Run.wait_start--; END_LOCK; return NULL; } /* * This function waits for the service to stop running. If the service * did not stop a failed event is posted to notify the user. This * function does purposefully not run in its own thread because, if we * did a restart we need to know if we successfully managed to stop * the service first before we can do a start. * @param service A Service to wait for * @return TRUE if the service was stopped otherwise FALSE */ static int wait_stop(Service_T s) { int max_tries= Run.polltime; ASSERT(s); while(max_tries-- && !Run.stopped) { if(!Util_isProcessRunning(s)) break; sleep(1); } if(Util_isProcessRunning(s)) { Event_post(s, EVENT_EXEC, STATE_FAILED, s->action_EXEC, "'%s' failed to stop", s->name); return FALSE; } else { Event_post(s, EVENT_EXEC, STATE_PASSED, s->action_EXEC, "'%s' stopped", s->name); } return TRUE; }