/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "monitor.h"
#include "net.h"
#include "socket.h"
#include "event.h"
/**
* Methods for controlling services managed by monit.
*
* @author Jan-Henrik Haukeland, <hauk@tildeslash.com>
* @author Rory Toma, <rory@digeo.com>
* @author Martin Pala, <martinp@tildeslash.com>
*
* @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;
}
syntax highlighted by Code2HTML, v. 0.9.1