1 /*
2   Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
3 
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 
27 #ifndef NDB_PROCESS_HPP
28 #define NDB_PROCESS_HPP
29 
30 #include <portlib/NdbSleep.h>
31 
32 class NdbProcess
33 {
34 #ifdef _WIN32
35   typedef DWORD pid_t;
36 #endif
37   pid_t m_pid;
38   BaseString m_name;
39 public:
40 
getpid()41   static pid_t getpid()
42   {
43 #ifdef _WIN32
44     return GetCurrentProcessid();
45 #else
46     return ::getpid();
47 #endif
48   }
49 
50   class Args
51   {
52     Vector<BaseString> m_args;
53   public:
54 
add(const char * str)55     void add(const char* str)
56     {
57       m_args.push_back(str);
58     }
59 
add(const char * str,const char * str2)60     void add(const char* str, const char* str2)
61     {
62       BaseString tmp;
63       tmp.assfmt("%s%s", str, str2);
64       m_args.push_back(tmp);
65     }
66 
add(const char * str,int val)67     void add(const char* str, int val)
68     {
69       BaseString tmp;
70       tmp.assfmt("%s%d", str, val);
71       m_args.push_back(tmp);
72     }
73 
add(const Args & args)74     void add(const Args & args)
75     {
76       for (unsigned i = 0; i < args.m_args.size(); i++)
77         add(args.m_args[i].c_str());
78     }
79 
args(void) const80     const Vector<BaseString>& args(void) const
81     {
82       return m_args;
83     }
84 
85   };
86 
create(const BaseString & name,const BaseString & path,const BaseString & cwd,const Args & args)87   static NdbProcess* create(const BaseString& name,
88                             const BaseString& path,
89                             const BaseString& cwd,
90                             const Args& args)
91   {
92     NdbProcess* proc = new NdbProcess(name);
93     if (!proc)
94     {
95       fprintf(stderr, "Failed to allocate memory for new process\n");
96       return NULL;
97     }
98 
99     // Check existence of cwd
100     if (cwd.c_str() && access(cwd.c_str(), F_OK) != 0)
101     {
102       fprintf(stderr,
103               "The specified working directory '%s' does not exist\n",
104               cwd.c_str());
105       delete proc;
106       return NULL;
107     }
108 
109     if (!start_process(proc->m_pid, path.c_str(), cwd.c_str(), args))
110     {
111       fprintf(stderr,
112               "Failed to create process '%s'\n", name.c_str());
113       delete proc;
114       return NULL;
115     }
116     return proc;
117   }
118 
stop(void)119   bool stop(void)
120   {
121     int ret = kill(m_pid, 9);
122     if (ret != 0)
123     {
124       fprintf(stderr,
125               "Failed to kill process %d, ret: %d, errno: %d\n",
126               m_pid, ret, errno);
127       return false;
128     }
129     printf("Stopped process %d\n", m_pid);
130     return true;
131   }
132 
wait(int & ret,int timeout=0)133   bool wait(int& ret, int timeout = 0)
134   {
135     int retries = 0;
136     int status;
137     while (true)
138     {
139       pid_t ret_pid = waitpid(m_pid, &status, WNOHANG);
140       if (ret_pid == -1)
141       {
142         fprintf(stderr,
143                 "Error occurred when waiting for process %d, ret: %d, errno: %d\n",
144                 m_pid, status, errno);
145         return false;
146       }
147 
148       if (ret_pid == m_pid)
149       {
150         if (WIFEXITED(status))
151           ret = WEXITSTATUS(status);
152         else if (WIFSIGNALED(status))
153           ret = WTERMSIG(status);
154         else
155           ret = 37; // Unknown exit status
156 
157         printf("Got process %d, status: %d, ret: %d\n", m_pid, status, ret);
158         return true;
159       }
160 
161       if (timeout == 0)
162         return false;
163 
164       if (retries++ > timeout*10)
165       {
166         fprintf(stderr,
167                 "Timeout when waiting for process %d\n", m_pid);
168         return false;
169       }
170       NdbSleep_MilliSleep(10);
171     }
172     require(false); // Never reached
173   }
174 
175 private:
176 
NdbProcess(BaseString name)177   NdbProcess(BaseString name) :
178   m_name(name)
179   {
180     m_pid = -1;
181   }
182 
start_process(pid_t & pid,const char * path,const char * cwd,const Args & args)183   static bool start_process(pid_t& pid, const char* path,
184                             const char* cwd,
185                             const Args& args)
186   {
187 #ifdef _WIN32
188 #else
189     int retries = 5;
190     pid_t tmp;
191     while ((tmp = fork()) == -1)
192     {
193       fprintf(stderr, "Warning: 'fork' failed, errno: %d - ", errno);
194       if (retries--)
195       {
196         fprintf(stderr, "retrying in 1 second...\n");
197         NdbSleep_SecSleep(1);
198         continue;
199       }
200       fprintf(stderr, "giving up...\n");
201       return false;
202     }
203 
204     if (tmp)
205     {
206       pid = tmp;
207       printf("Started process: %d\n", pid);
208       return true;
209     }
210     require(tmp == 0);
211 
212     if (cwd && chdir(cwd) != 0)
213     {
214       fprintf(stderr, "Failed to change directory to '%s', errno: %d\n", cwd, errno);
215       exit(1);
216     }
217 
218     // Concatenate arguments
219     BaseString args_str;
220     args_str.assign(args.args(), " ");
221 
222     char **argv = BaseString::argify(path, args_str.c_str());
223     //printf("name: %s\n", path);
224     execv(path, argv);
225 
226     fprintf(stderr, "execv failed, errno: %d\n", errno);
227     exit(1);
228 #endif
229   }
230 };
231 
232 #endif
233