/* Copyright (C) 2004 MySQL AB 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef __GNUC__ #pragma implementation #endif #include "instance.h" #include "mysql_manager_error.h" #include "log.h" #include "instance_map.h" #include #include #include #include C_MODE_START pthread_handler_decl(proxy, arg) { Instance *instance= (Instance *) arg; instance->fork_and_monitor(); return 0; } C_MODE_END /* The method starts an instance. SYNOPSYS start() RETURN 0 ok ER_CANNOT_START_INSTANCE Cannot start instance ER_INSTANCE_ALREADY_STARTED The instance on the specified port/socket is already started */ int Instance::start() { pid_t pid; /* clear crash flag */ pthread_mutex_lock(&LOCK_instance); crashed= 0; pthread_mutex_unlock(&LOCK_instance); if (!is_running()) { if ((pid= options.get_pid()) != 0) /* check the pidfile */ if (options.unlink_pidfile()) /* remove stalled pidfile */ log_error("cannot remove pidfile for instance %i, this might be \ since IM lacks permmissions or hasn't found the pidifle", options.instance_name); /* No need to monitor this thread in the Thread_registry, as all instances are to be stopped during shutdown. */ pthread_t proxy_thd_id; pthread_attr_t proxy_thd_attr; int rc; pthread_attr_init(&proxy_thd_attr); pthread_attr_setdetachstate(&proxy_thd_attr, PTHREAD_CREATE_DETACHED); rc= pthread_create(&proxy_thd_id, &proxy_thd_attr, proxy, this); pthread_attr_destroy(&proxy_thd_attr); if (rc) { log_error("Instance::start(): pthread_create(proxy) failed"); return ER_CANNOT_START_INSTANCE; } return 0; } /* the instance is started already */ return ER_INSTANCE_ALREADY_STARTED; } void Instance::fork_and_monitor() { pid_t pid; log_info("starting instance %s", options.instance_name); switch (pid= fork()) { case 0: execv(options.mysqld_path, options.argv); /* exec never returns */ exit(1); case -1: log_info("cannot fork() to start instance %s", options.instance_name); return; default: wait(NULL); /* set instance state to crashed */ pthread_mutex_lock(&LOCK_instance); crashed= 1; pthread_mutex_unlock(&LOCK_instance); /* Wake connection threads waiting for an instance to stop. This is needed if a user issued command to stop an instance via mysql connection. This is not the case if Guardian stop the thread. */ pthread_cond_signal(&COND_instance_restarted); /* wake guardian */ pthread_cond_signal(&instance_map->guardian->COND_guardian); /* thread exits */ return; } /* we should never end up here */ DBUG_ASSERT(0); } Instance::Instance(): crashed(0) { pthread_mutex_init(&LOCK_instance, 0); pthread_cond_init(&COND_instance_restarted, 0); } Instance::~Instance() { pthread_mutex_destroy(&LOCK_instance); pthread_cond_destroy(&COND_instance_restarted); } int Instance::is_crashed() { int val; pthread_mutex_lock(&LOCK_instance); val= crashed; pthread_mutex_unlock(&LOCK_instance); return val; } bool Instance::is_running() { MYSQL mysql; uint port= 0; const char *socket= NULL; const char *password= "321rarepassword213"; const char *username= "645rareusername945"; const char *access_denied_message= "Access denied for user"; bool return_val; if (options.mysqld_port) port= atoi(strchr(options.mysqld_port, '=') + 1); if (options.mysqld_socket) socket= strchr(options.mysqld_socket, '=') + 1; pthread_mutex_lock(&LOCK_instance); mysql_init(&mysql); /* try to connect to a server with a fake username/password pair */ if (mysql_real_connect(&mysql, LOCAL_HOST, username, password, NullS, port, socket, 0)) { /* We have successfully connected to the server using fake username/password. Write a warning to the logfile. */ log_info("The Instance Manager was able to log into you server \ with faked compiled-in password while checking server status. \ Looks like something is wrong."); pthread_mutex_unlock(&LOCK_instance); return_val= TRUE; /* server is alive */ } else { if (!strncmp(access_denied_message, mysql_error(&mysql), sizeof(access_denied_message)-1)) { return_val= TRUE; } else return_val= FALSE; } mysql_close(&mysql); pthread_mutex_unlock(&LOCK_instance); return return_val; } /* Stop an instance. SYNOPSYS stop() RETURN: 0 ok ER_INSTANCE_IS_NOT_STARTED Looks like the instance it is not started ER_STOP_INSTANCE mysql_shutdown reported an error */ int Instance::stop() { pid_t pid; struct timespec timeout; int waitchild= DEFAULT_SHUTDOWN_DELAY; if (options.shutdown_delay != NULL) waitchild= atoi(options.shutdown_delay); kill_instance(SIGTERM); /* sleep on condition to wait for SIGCHLD */ timeout.tv_sec= time(NULL) + waitchild; timeout.tv_nsec= 0; if (pthread_mutex_lock(&LOCK_instance)) goto err; while (options.get_pid() != 0) /* while server isn't stopped */ { int status; status= pthread_cond_timedwait(&COND_instance_restarted, &LOCK_instance, &timeout); if (status == ETIMEDOUT) break; } pthread_mutex_unlock(&LOCK_instance); kill_instance(SIGKILL); return 0; return ER_INSTANCE_IS_NOT_STARTED; err: return ER_STOP_INSTANCE; } void Instance::kill_instance(int signum) { pid_t pid; /* if there are no pid, everything seems to be fine */ if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ { /* If we cannot kill mysqld, then it has propably crashed. Let us try to remove staled pidfile and return successfully as mysqld is probably stopped. */ if (!kill(pid, signum)) options.unlink_pidfile(); else if (signum == SIGKILL) /* really killed instance with SIGKILL */ log_error("The instance %s is being stopped forsibly. Normally \ it should not happed. Probably the instance has been \ hanging. You should also check your IM setup", options.instance_name); } return; } /* We execute this function to initialize instance parameters. Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY. */ int Instance::init(const char *name_arg) { return options.init(name_arg); } int Instance::complete_initialization(Instance_map *instance_map_arg, const char *mysqld_path) { instance_map= instance_map_arg; return options.complete_initialization(mysqld_path); }