MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CPCD.cpp
1 /*
2  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 
19 #include <ndb_global.h>
20 #include <NdbOut.hpp>
21 
22 #include "APIService.hpp"
23 #include "CPCD.hpp"
24 #include <NdbMutex.h>
25 
26 #include "common.hpp"
27 #ifdef _WIN32
28 #include <sys/types.h>
29 #include <direct.h>
30 #endif
31 
32 extern const ParserRow<CPCDAPISession> commands[];
33 
34 
36  loadingProcessList = false;
37  m_processes.clear();
38  m_monitor = NULL;
39  m_monitor = new Monitor(this);
40  m_procfile = "ndb_cpcd.db";
41 }
42 
44  if(m_monitor != NULL) {
45  delete m_monitor;
46  m_monitor = NULL;
47  }
48 }
49 
50 int
51 CPCD::findUniqueId() {
52  int id;
53  bool ok = false;
54  m_processes.lock();
55 
56  while(!ok) {
57  ok = true;
58  id = rand() % 8192; /* Don't want so big numbers */
59 
60  if(id == 0)
61  ok = false;
62 
63  for(size_t i = 0; i<m_processes.size(); i++) {
64  if(m_processes[i]->m_id == id)
65  ok = false;
66  }
67  }
68  m_processes.unlock();
69  return id;
70 }
71 
72 bool
74  if(arg->m_id == -1)
75  arg->m_id = findUniqueId();
76 
77  Guard tmp(m_processes);
78 
79  for(size_t i = 0; i<m_processes.size(); i++) {
80  Process * proc = m_processes[i];
81 
82  if((strcmp(arg->m_name.c_str(), proc->m_name.c_str()) == 0) &&
83  (strcmp(arg->m_group.c_str(), proc->m_group.c_str()) == 0)) {
84  /* Identical names in the same group */
85  rs->err(AlreadyExists, "Name already exists");
86  return false;
87  }
88 
89  if(arg->m_id == proc->m_id) {
90  /* Identical ID numbers */
91  rs->err(AlreadyExists, "Id already exists");
92  return false;
93  }
94  }
95 
96  m_processes.push_back(arg, false);
97 
98  notifyChanges();
99 
100  return true;
101 }
102 
103 bool
105 
106  Guard tmp(m_processes);
107 
108  Process * proc = 0;
109  size_t i;
110  for(i = 0; i < m_processes.size(); i++) {
111  if(m_processes[i]->m_id == id) {
112  proc = m_processes[i];
113  break;
114  }
115  }
116 
117  if(proc == 0){
118  rs->err(NotExists, "No such process");
119  return false;
120  }
121 
122  switch(proc->m_status){
123  case RUNNING:
124  case STOPPED:
125  case STOPPING:
126  case STARTING:
127  proc->stop();
128  m_processes.erase(i, false /* Already locked */);
129  }
130 
131 
132  notifyChanges();
133 
134  return true;
135 }
136 
137 bool
139 
140  Process * proc = 0;
141  {
142 
143  Guard tmp(m_processes);
144 
145  for(size_t i = 0; i < m_processes.size(); i++) {
146  if(m_processes[i]->m_id == id) {
147  proc = m_processes[i];
148  break;
149  }
150  }
151 
152  if(proc == 0){
153  rs->err(NotExists, "No such process");
154  return false;
155  }
156 
157  switch(proc->m_status){
158  case STOPPED:
159  proc->m_status = STARTING;
160  if(proc->start() != 0){
161  rs->err(Error, "Failed to start");
162  return false;
163  }
164  break;
165  case STARTING:
166  rs->err(Error, "Already starting");
167  return false;
168  case RUNNING:
169  rs->err(Error, "Already started");
170  return false;
171  case STOPPING:
172  rs->err(Error, "Currently stopping");
173  return false;
174  }
175 
176  notifyChanges();
177  }
178 
179  return true;
180 }
181 
182 bool
184 
185  Guard tmp(m_processes);
186 
187  Process * proc = 0;
188  for(size_t i = 0; i < m_processes.size(); i++) {
189  if(m_processes[i]->m_id == id) {
190  proc = m_processes[i];
191  break;
192  }
193  }
194 
195  if(proc == 0){
196  rs->err(NotExists, "No such process");
197  return false;
198  }
199 
200  switch(proc->m_status){
201  case STARTING:
202  case RUNNING:
203  proc->stop();
204  break;
205  case STOPPED:
206  rs->err(AlreadyStopped, "Already stopped");
207  return false;
208  break;
209  case STOPPING:
210  rs->err(Error, "Already stopping");
211  return false;
212  }
213 
214  notifyChanges();
215 
216  return true;
217 }
218 
219 bool
220 CPCD::notifyChanges() {
221  bool ret = true;
222  if(!loadingProcessList)
223  ret = saveProcessList();
224 
225  m_monitor->signal();
226 
227  return ret;
228 }
229 
230 
231 #ifdef _WIN32
232 static int link(const char* from_file, const char* to_file)
233 {
234  BOOL fail_if_exists = TRUE;
235  if (CopyFile(from_file, to_file, fail_if_exists) == 0)
236  {
237  /* "On error, -1 is returned" */
238  return -1;
239  }
240  /* "On success, zero is returned" */
241  return 0;
242 }
243 #endif
244 
245 
246 /* Must be called with m_processlist locked */
247 bool
249  char newfile[PATH_MAX+4];
250  char oldfile[PATH_MAX+4];
251  char curfile[PATH_MAX];
252  FILE *f;
253 
254  /* Create the filenames that we will use later */
255  BaseString::snprintf(newfile, sizeof(newfile), "%s.new", m_procfile.c_str());
256  BaseString::snprintf(oldfile, sizeof(oldfile), "%s.old", m_procfile.c_str());
257  BaseString::snprintf(curfile, sizeof(curfile), "%s", m_procfile.c_str());
258 
259  f = fopen(newfile, "w");
260 
261  if(f == NULL) {
262  /* XXX What should be done here? */
263  logger.critical("Cannot open `%s': %s\n", newfile, strerror(errno));
264  return false;
265  }
266 
267  for(size_t i = 0; i<m_processes.size(); i++){
268  m_processes[i]->print(f);
269  fprintf(f, "\n");
270 
271  if(m_processes[i]->m_processType == TEMPORARY){
275  continue;
276  }
277 
278  if(m_processes[i]->m_status == RUNNING ||
279  m_processes[i]->m_status == STARTING){
280  fprintf(f, "start process\nid: %d\n\n", m_processes[i]->m_id);
281  }
282  }
283 
284  fclose(f);
285  f = NULL;
286 
287  /* This will probably only work on reasonably Unix-like systems. You have
288  * been warned...
289  *
290  * The motivation behind all this link()ing is that the daemon might
291  * crash right in the middle of updating the configuration file, and in
292  * that case we want to be sure that the old file is around until we are
293  * guaranteed that there is always at least one copy of either the old or
294  * the new configuration file left.
295  */
296 
297  /* Remove an old config file if it exists */
298  unlink(oldfile);
299 
300  if(link(curfile, oldfile) != 0) /* make a backup of the running config */
301  logger.error("Cannot rename '%s' -> '%s'", curfile, oldfile);
302  else {
303  if(unlink(curfile) != 0) { /* remove the running config file */
304  logger.critical("Cannot remove file '%s'", curfile);
305  return false;
306  }
307  }
308 
309  if(link(newfile, curfile) != 0) { /* put the new config file in place */
310  printf("-->%d\n", __LINE__);
311 
312  logger.critical("Cannot rename '%s' -> '%s': %s",
313  curfile, newfile, strerror(errno));
314  return false;
315  }
316 
317  /* XXX Ideally we would fsync() the directory here, but I'm not sure if
318  * that actually works.
319  */
320 
321  unlink(newfile); /* remove the temporary file */
322  unlink(oldfile); /* remove the old file */
323 
324  logger.info("Process list saved as '%s'", curfile);
325 
326  return true;
327 }
328 
329 bool
331  BaseString secondfile;
332  FILE *f;
333 
334  loadingProcessList = true;
335 
336  secondfile.assfmt("%s.new", m_procfile.c_str());
337 
338  /* Try to open the config file */
339  f = fopen(m_procfile.c_str(), "r");
340 
341  /* If it did not exist, try to open the backup. See the saveProcessList()
342  * method for an explanation why it is done this way.
343  */
344  if(f == NULL) {
345  f = fopen(secondfile.c_str(), "r");
346 
347  if(f == NULL) {
348  /* XXX What to do here? */
349  logger.info("Configuration file `%s' not found",
350  m_procfile.c_str());
351  logger.info("Starting with empty configuration");
352  loadingProcessList = false;
353  return false;
354  } else {
355  logger.info("Configuration file `%s' missing",
356  m_procfile.c_str());
357  logger.info("Backup configuration file `%s' is used",
358  secondfile.c_str());
359  /* XXX Maybe we should just rename the backup file to the official
360  * name, and be done with it?
361  */
362  }
363  }
364 
365  CPCDAPISession sess(f, *this);
366  fclose(f);
367  sess.loadFile();
368  loadingProcessList = false;
369 
370  size_t i;
371  Vector<int> temporary;
372  for(i = 0; i<m_processes.size(); i++){
373  Process * proc = m_processes[i];
374  proc->readPid();
375  if(proc->m_processType == TEMPORARY){
376  temporary.push_back(proc->m_id);
377  }
378  }
379 
380  for(i = 0; i<temporary.size(); i++){
381  RequestStatus rs;
382  undefineProcess(&rs, temporary[i]);
383  }
384 
385  /* Don't call notifyChanges here, as that would save the file we just
386  loaded */
387  m_monitor->signal();
388  return true;
389 }
390 
393  return &m_processes;
394 }
395 
396 void
397 CPCD::RequestStatus::err(enum RequestStatusCode status, const char *msg) {
398  m_status = status;
399  BaseString::snprintf(m_errorstring, sizeof(m_errorstring), "%s", msg);
400 }