1
/* Copyright (C) 2003 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
#include <ndb_global.h>
20
#include "APIService.hpp"
26
extern const ParserRow<CPCDAPISession> commands[];
30
loadingProcessList = false;
33
m_monitor = new Monitor(this);
34
m_procfile = "ndb_cpcd.db";
38
if(m_monitor != NULL) {
45
CPCD::findUniqueId() {
52
id = random() % 8192; /* Don't want so big numbers */
57
for(size_t i = 0; i<m_processes.size(); i++) {
58
if(m_processes[i]->m_id == id)
67
CPCD::defineProcess(RequestStatus * rs, Process * arg){
69
arg->m_id = findUniqueId();
71
Guard tmp(m_processes);
73
for(size_t i = 0; i<m_processes.size(); i++) {
74
Process * proc = m_processes[i];
76
if((strcmp(arg->m_name.c_str(), proc->m_name.c_str()) == 0) &&
77
(strcmp(arg->m_group.c_str(), proc->m_group.c_str()) == 0)) {
78
/* Identical names in the same group */
79
rs->err(AlreadyExists, "Name already exists");
83
if(arg->m_id == proc->m_id) {
84
/* Identical ID numbers */
85
rs->err(AlreadyExists, "Id already exists");
90
m_processes.push_back(arg, false);
93
report(arg->m_id, CPCEvent::ET_PROC_USER_DEFINE);
99
CPCD::undefineProcess(CPCD::RequestStatus *rs, int id) {
101
Guard tmp(m_processes);
105
for(i = 0; i < m_processes.size(); i++) {
106
if(m_processes[i]->m_id == id) {
107
proc = m_processes[i];
113
rs->err(NotExists, "No such process");
117
switch(proc->m_status){
123
m_processes.erase(i, false /* Already locked */);
129
report(id, CPCEvent::ET_PROC_USER_UNDEFINE);
135
CPCD::startProcess(CPCD::RequestStatus *rs, int id) {
140
Guard tmp(m_processes);
142
for(size_t i = 0; i < m_processes.size(); i++) {
143
if(m_processes[i]->m_id == id) {
144
proc = m_processes[i];
150
rs->err(NotExists, "No such process");
154
switch(proc->m_status){
156
proc->m_status = STARTING;
157
if(proc->start() != 0){
158
rs->err(Error, "Failed to start");
163
rs->err(Error, "Already starting");
166
rs->err(Error, "Already started");
169
rs->err(Error, "Currently stopping");
175
report(id, CPCEvent::ET_PROC_USER_START);
181
CPCD::stopProcess(CPCD::RequestStatus *rs, int id) {
183
Guard tmp(m_processes);
186
for(size_t i = 0; i < m_processes.size(); i++) {
187
if(m_processes[i]->m_id == id) {
188
proc = m_processes[i];
194
rs->err(NotExists, "No such process");
198
switch(proc->m_status){
204
rs->err(AlreadyStopped, "Already stopped");
208
rs->err(Error, "Already stopping");
214
report(id, CPCEvent::ET_PROC_USER_START);
220
CPCD::notifyChanges() {
222
if(!loadingProcessList)
223
ret = saveProcessList();
230
/* Must be called with m_processlist locked */
232
CPCD::saveProcessList(){
233
char newfile[PATH_MAX+4];
234
char oldfile[PATH_MAX+4];
235
char curfile[PATH_MAX];
238
/* Create the filenames that we will use later */
239
BaseString::snprintf(newfile, sizeof(newfile), "%s.new", m_procfile.c_str());
240
BaseString::snprintf(oldfile, sizeof(oldfile), "%s.old", m_procfile.c_str());
241
BaseString::snprintf(curfile, sizeof(curfile), "%s", m_procfile.c_str());
243
f = fopen(newfile, "w");
246
/* XXX What should be done here? */
247
logger.critical("Cannot open `%s': %s\n", newfile, strerror(errno));
251
for(size_t i = 0; i<m_processes.size(); i++){
252
m_processes[i]->print(f);
255
if(m_processes[i]->m_processType == TEMPORARY){
257
* Interactive process should never be "restarted" on cpcd restart
262
if(m_processes[i]->m_status == RUNNING ||
263
m_processes[i]->m_status == STARTING){
264
fprintf(f, "start process\nid: %d\n\n", m_processes[i]->m_id);
271
/* This will probably only work on reasonably Unix-like systems. You have
274
* The motivation behind all this link()ing is that the daemon might
275
* crash right in the middle of updating the configuration file, and in
276
* that case we want to be sure that the old file is around until we are
277
* guaranteed that there is always at least one copy of either the old or
278
* the new configuration file left.
281
/* Remove an old config file if it exists */
284
if(link(curfile, oldfile) != 0) /* make a backup of the running config */
285
logger.error("Cannot rename '%s' -> '%s'", curfile, oldfile);
287
if(unlink(curfile) != 0) { /* remove the running config file */
288
logger.critical("Cannot remove file '%s'", curfile);
293
if(link(newfile, curfile) != 0) { /* put the new config file in place */
294
printf("-->%d\n", __LINE__);
296
logger.critical("Cannot rename '%s' -> '%s': %s",
297
curfile, newfile, strerror(errno));
301
/* XXX Ideally we would fsync() the directory here, but I'm not sure if
302
* that actually works.
305
unlink(newfile); /* remove the temporary file */
306
unlink(oldfile); /* remove the old file */
308
logger.info("Process list saved as '%s'", curfile);
314
CPCD::loadProcessList(){
315
BaseString secondfile;
318
loadingProcessList = true;
320
secondfile.assfmt("%s.new", m_procfile.c_str());
322
/* Try to open the config file */
323
f = fopen(m_procfile.c_str(), "r");
325
/* If it did not exist, try to open the backup. See the saveProcessList()
326
* method for an explanation why it is done this way.
329
f = fopen(secondfile.c_str(), "r");
332
/* XXX What to do here? */
333
logger.info("Configuration file `%s' not found",
335
logger.info("Starting with empty configuration");
336
loadingProcessList = false;
339
logger.info("Configuration file `%s' missing",
341
logger.info("Backup configuration file `%s' is used",
343
/* XXX Maybe we should just rename the backup file to the official
344
* name, and be done with it?
349
CPCDAPISession sess(f, *this);
351
loadingProcessList = false;
354
Vector<int> temporary;
355
for(i = 0; i<m_processes.size(); i++){
356
Process * proc = m_processes[i];
358
if(proc->m_processType == TEMPORARY){
359
temporary.push_back(proc->m_id);
363
for(i = 0; i<temporary.size(); i++){
365
undefineProcess(&rs, temporary[i]);
368
/* Don't call notifyChanges here, as that would save the file we just
374
MutexVector<CPCD::Process *> *
375
CPCD::getProcessList() {
380
CPCD::RequestStatus::err(enum RequestStatusCode status, const char *msg) {
382
BaseString::snprintf(m_errorstring, sizeof(m_errorstring), "%s", msg);
387
CPCD::sigchild(int pid){
389
for(size_t i = 0; i<m_processes.size(); i++){
390
if(m_processes[i].m_pid == pid){
397
/** Register event subscriber */
399
CPCD::do_register(EventSubscriber * sub){
400
m_subscribers.lock();
401
m_subscribers.push_back(sub, false);
402
m_subscribers.unlock();
406
CPCD::do_unregister(EventSubscriber * sub){
407
m_subscribers.lock();
409
for(size_t i = 0; i<m_subscribers.size(); i++){
410
if(m_subscribers[i] == sub){
411
m_subscribers.erase(i);
412
m_subscribers.unlock();
417
m_subscribers.unlock();
422
CPCD::report(int id, CPCEvent::EventType t){
427
m_subscribers.lock();
428
for(size_t i = 0; i<m_subscribers.size(); i++){
429
(* m_subscribers[i]).report(e);
431
m_subscribers.unlock();
434
template class MutexVector<EventSubscriber*>;