1 /*
2    Copyright (C) 2003-2007 MySQL AB, 2009, 2010 Sun Microsystems, Inc.
3     All rights reserved. Use is subject to license terms.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License, version 2.0,
7    as published by the Free Software Foundation.
8 
9    This program is also distributed with certain software (including
10    but not limited to OpenSSL) that is licensed under separate terms,
11    as designated in a particular file or component or in included license
12    documentation.  The authors of MySQL hereby grant you an additional
13    permission to link the program and your derivative works with the
14    separately licensed software that they have included with MySQL.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License, version 2.0, for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 */
25 
26 #ifndef CPCD_HPP
27 #define CPCD_HPP
28 
29 #include <Vector.hpp>
30 #include <Properties.hpp>
31 #include <NdbOut.hpp>
32 #include <NdbThread.h>
33 #include <NdbCondition.h>
34 #include <BaseString.hpp>
35 
36 #ifdef _WIN32
37 typedef DWORD pid_t;
38 #endif
39 const pid_t bad_pid = -1;
40 
is_bad_pid(pid_t pid)41 inline bool is_bad_pid(pid_t pid)
42 {
43 #ifdef _WIN32
44   return pid == bad_pid;
45 #else
46   return pid <= 1;
47 #endif
48 }
49 
50 #define CPCD_DEFAULT_PROC_FILE    	"ndb_cpcd.conf"
51 #define CPCD_DEFAULT_TCP_PORT		1234
52 #define CPCD_DEFAULT_POLLING_INTERVAL	5 /* seconds */
53 #ifndef _WIN32
54 #define CPCD_DEFAULT_WORK_DIR		"/var/run/ndb_cpcd"
55 #define CPCD_DEFAULT_CONFIG_FILE        "/etc/ndb_cpcd.conf"
56 
57 #else
58 #define CPCD_DEFAULT_WORK_DIR		"c:\\ndb_cpcd"
59 #define CPCD_DEFAULT_CONFIG_FILE        "c:\\ndb_cpcd\\ndb_cpcd.conf"
60 #endif
61 enum ProcessStatus {
62   STOPPED  = 0,
63   STARTING = 1,
64   RUNNING  = 2,
65   STOPPING = 3
66 };
67 
68 enum ProcessType {
69   PERMANENT = 0,
70   TEMPORARY = 1
71 };
72 
73 /**
74  *  @brief Error codes for CPCD requests
75  */
76 enum RequestStatusCode {
77   OK = 0,            ///< Everything OK
78   Error = 1,	     ///< Generic error
79   AlreadyExists = 2, ///< Entry already exists in list
80   NotExists = 3,     ///< Entry does not exist in list
81   AlreadyStopped = 4
82 };
83 
84 /**
85  *  @class CPCD
86  *  @brief Manages processes, letting them be controlled with a TCP connection.
87  *
88  *  The class implementing the Cluster Process Control Daemon
89  */
90 class CPCD {
91 public:
92   /** @brief Describes the status of a client request */
93   class RequestStatus {
94   public:
95     /** @brief Constructs an empty RequestStatus */
RequestStatus()96     RequestStatus() { m_status = OK; m_errorstring[0] = '\0'; };
97 
98     /** @brief Sets an errorcode and a printable message */
99     void err(enum RequestStatusCode, const char *);
100 
101     /** @brief Returns the error message */
getErrMsg()102     char *getErrMsg() { return m_errorstring; };
103 
104     /** @brief Returns the error code */
getStatus()105     enum RequestStatusCode getStatus() { return m_status; };
106   private:
107     enum RequestStatusCode m_status;
108     char m_errorstring[256];
109   };
110   /**
111    *  @brief Manages a process
112    */
113   class Process {
114     pid_t m_pid;
115 #ifdef _WIN32
116     HANDLE m_job;
117 #endif
118   public:
119     /**
120      * @brief Constructs and empty Process
121      */
122     Process(const Properties & props, class CPCD *cpcd);
123     /**
124      *  @brief Monitors the process
125      *
126      *  The process is started or stopped as needed.
127      */
128     void monitor();
129 
130     /**
131      *  @brief Checks if the process is running or not
132      *
133      *  @return
134      *          - true if the process is running,
135      *          - false if the process is not running
136      */
137     bool isRunning();
138 
139     /** @brief Starts the process */
140     int start();
141 
142     /** @brief Stops the process */
143     void stop();
144 
145     /**
146      *  @brief Reads the pid from stable storage
147      *
148      *  @return The pid number
149      */
150     int readPid();
151 
152     /**
153      *  @brief Writes the pid from stable storage
154      *
155      *  @return
156      *          - 0 if successful
157                 - -1 and sets errno if an error occured
158      */
159     int writePid(int pid);
160 
161     /**
162      *  @brief Prints a textual description of the process on a file
163      */
164     void print(FILE *);
165 
166     /** Id number of the Process.
167      *
168      *  @note This is not the same as a pid. This number is used in the
169      *        protocol, and will not be changed if a processes is restarted.
170      */
171     int m_id;
172 
173     /** @brief The name shown to the user */
174     BaseString m_name;
175 
176     /** @brief Used to group a number of processes */
177     BaseString m_group;
178 
179     /** @brief Environment variables
180      *
181      *  Environmentvariables to add for the process.
182      *
183      *  @note
184      *       - The environment cpcd started with is preserved
185      *       - There is no way to delete variables
186      */
187     BaseString m_env;
188 
189     /** @brief Path to the binary to run */
190     BaseString m_path;
191 
192     /** @brief Arguments to the process.
193      *
194      *  @note
195      *        - This includes argv[0].
196      *        - If no argv[0] is given, argv[0] will be set to m_path.
197      */
198     BaseString m_args;
199 
200     /**
201      * @brief Type of process
202      *
203      *  Either set to "interactive" or "permanent".
204      */
205     BaseString m_type;
206     ProcessType m_processType;
207 
208     /**
209      *  @brief Working directory
210      *
211      * Working directory the process will start in.
212      */
213     BaseString m_cwd;
214 
215     /**
216      *  @brief Owner of the process.
217      *
218      *  @note This will not affect the process' uid or gid;
219      *        it is only used for managemental purposes.
220      *  @see m_runas
221      */
222     BaseString m_owner;
223 
224     /**
225      * @bried Run as
226      * @note This affects uid
227      * @see m_owner
228      */
229     BaseString m_runas;
230 
231     /**
232      * @brief redirection for stdin
233      */
234     BaseString m_stdin;
235 
236     /**
237      * @brief redirection for stdout
238      */
239     BaseString m_stdout;
240 
241     /**
242      * @brief redirection for stderr
243      */
244     BaseString m_stderr;
245 
246     /** @brief Status of the process */
247     enum ProcessStatus m_status;
248 
249     /**
250      * @brief ulimits for process
251      * @desc Format c:unlimited d:0 ...
252      */
253     BaseString m_ulimit;
254 
255     /**
256      * @brief shutdown options
257      */
258     BaseString m_shutdown_options;
259 
260   private:
261     class CPCD *m_cpcd;
262     void do_exec();
263   };
264 
265   /**
266    *  @brief Starts and stops processes as needed
267    *
268    *  At a specified interval (default 5 seconds) calls the monitor function
269    *  of all the processes in the CPCDs list, causing the to start or
270    *  stop, depending on the configuration.
271    */
272   class Monitor {
273   public:
274     /** Creates a new CPCD::Monitor object, connected to the specified
275      *	CPCD.
276      *  A new thread will be created, which will poll the processes of
277      *  the CPCD at the specifed interval.
278      */
279     Monitor(CPCD *cpcd, int poll = CPCD_DEFAULT_POLLING_INTERVAL);
280 
281     /** Stops the monitor, but does not stop the processes */
282     ~Monitor();
283 
284     /** Runs the monitor thread. */
285     void run();
286 
287     /** Signals configuration changes to the monitor thread, causing it to
288      *  do the check without waiting for the timeout */
289     void signal();
290   private:
291     class CPCD *m_cpcd;
292     struct NdbThread *m_monitorThread;
293     bool m_monitorThreadQuitFlag;
294     struct NdbCondition *m_changeCondition;
295     NdbMutex *m_changeMutex;
296     int m_pollingInterval; /* seconds */
297   };
298 
299   /** @brief Constructs a CPCD object */
300   CPCD();
301 
302   /**
303    * @brief Destroys a CPCD object,
304    * but does not stop the processes it manages
305    */
306   ~CPCD();
307 
308   /** Adds a Process to the CPCDs list of managed Processes.
309    *
310    *  @note The process will not be started until it is explicitly
311    *        marked as running with CPCD::startProcess().
312    *
313    *  @return
314    *          - true if the addition was successful,
315    *          - false if not
316    *          - RequestStatus will be filled in with a suitable error
317    *            if an error occured.
318    */
319   bool defineProcess(RequestStatus *rs, Process * arg);
320 
321   /** Removes a Process from the CPCD.
322    *
323    *  @note A Process that is running cannot be removed.
324    *
325    *  @return
326    *          - true if the removal was successful,
327    *          - false if not
328    *          - The RequestStatus will be filled in with a suitable error
329    *            if an error occured.
330    */
331   bool undefineProcess(RequestStatus *rs, int id);
332 
333   /** Marks a Process for starting.
334    *
335    *  @note The fact that a process has started does not mean it will actually
336    *        start properly. This command only makes sure the CPCD will
337    *        try to start it.
338    *
339    *  @return
340    *          - true if the marking was successful
341    *          - false if not
342    *          - RequestStatus will be filled in with a suitable error
343    *            if an error occured.
344    */
345   bool startProcess(RequestStatus *rs, int id);
346 
347   /** Marks a Process for stopping.
348    *
349    *  @return
350    *          - true if the marking was successful
351    *          - false if not
352    *          - The RequestStatus will be filled in with a suitable error
353    *            if an error occured.
354    */
355   bool stopProcess(RequestStatus *rs, int id);
356 
357   /** Generates a list of processes, and sends them to the CPCD client */
358   bool listProcesses(RequestStatus *rs, MutexVector<const char *> &);
359 
360   /** Set to true while the CPCD is reading the configuration file */
361   bool loadingProcessList;
362 
363   /** Saves the list of Processes and their status to the configuration file.
364    *  Called whenever the configuration is changed.
365    */
366   bool saveProcessList();
367 
368   /** Loads the list of Processes and their status from the configuration
369    *  file.
370    *  @note This function should only be called when the CPCD is starting,
371    *        calling it at other times will cause unspecified behaviour.
372    */
373   bool loadProcessList();
374 
375   /** Returns the list of processes */
376   MutexVector<Process *> *getProcessList();
377 
378   /** The list of processes. Should not be used directly */
379   MutexVector<Process *> m_processes;
380 
381 private:
382   friend class Process;
383   bool notifyChanges();
384   int findUniqueId();
385   BaseString m_procfile;
386   Monitor *m_monitor;
387 };
388 
389 #endif
390