~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to mysql-test/lib/My/SafeProcess/safe_process.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2008 MySQL AB
 
2
 
 
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.
 
6
 
 
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.
 
11
 
 
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 */
 
15
 
 
16
 
 
17
/*
 
18
  Utility program that encapsulates process creation, monitoring
 
19
  and bulletproof process cleanup
 
20
 
 
21
  Usage:
 
22
    safe_process [options to safe_process] -- progname arg1 ... argn
 
23
 
 
24
  To safeguard mysqld you would invoke safe_process with a few options
 
25
  for safe_process itself followed by a double dash to indicate start
 
26
  of the command line for the program you really want to start
 
27
 
 
28
  $> safe_process --output=output.log -- mysqld --datadir=var/data1 ...
 
29
 
 
30
  This would redirect output to output.log and then start mysqld,
 
31
  once it has done that it will continue to monitor the child as well
 
32
  as the parent.
 
33
 
 
34
  The safe_process then checks the follwing things:
 
35
  1. Child exits, propagate the childs return code to the parent
 
36
     by exiting with the same return code as the child.
 
37
 
 
38
  2. Parent dies, immediately kill the child and exit, thus the
 
39
     parent does not need to properly cleanup any child, it is handled
 
40
     automatically.
 
41
 
 
42
  3. Signal's recieced by the process will trigger same action as 2)
 
43
 
 
44
*/
 
45
 
 
46
#include <sys/types.h>
 
47
#include <sys/wait.h>
 
48
#include <sys/time.h>
 
49
#include <sys/resource.h>
 
50
#include <unistd.h>
 
51
#include <stdarg.h>
 
52
#include <stdio.h>
 
53
#include <stdlib.h>
 
54
#include <signal.h>
 
55
#include <string.h>
 
56
#include <errno.h>
 
57
 
 
58
int verbose= 0;
 
59
int terminated= 0;
 
60
pid_t child_pid= -1;
 
61
char safe_process_name[32]= {0};
 
62
 
 
63
 
 
64
static void message(const char* fmt, ...)
 
65
{
 
66
  if (!verbose)
 
67
    return;
 
68
  va_list args;
 
69
  fprintf(stderr, "%s: ", safe_process_name);
 
70
  va_start(args, fmt);
 
71
  vfprintf(stderr, fmt, args);
 
72
  fprintf(stderr, "\n");
 
73
  va_end(args);
 
74
  fflush(stderr);
 
75
}
 
76
 
 
77
 
 
78
static void die(const char* fmt, ...)
 
79
{
 
80
  va_list args;
 
81
  fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name);
 
82
  va_start(args, fmt);
 
83
  vfprintf(stderr, fmt, args);
 
84
  fprintf(stderr, "\n");
 
85
  va_end(args);
 
86
  if (int last_err= errno)
 
87
    fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err));
 
88
  exit(1);
 
89
}
 
90
 
 
91
 
 
92
static void kill_child(void)
 
93
{
 
94
  int status= 0;
 
95
 
 
96
  message("Killing child: %d", child_pid);
 
97
  // Terminate whole process group
 
98
  kill(-child_pid, SIGKILL);
 
99
 
 
100
  pid_t ret_pid= waitpid(child_pid, &status, 0);
 
101
  if (ret_pid == child_pid)
 
102
  {
 
103
    int exit_code= 1;
 
104
    if (WIFEXITED(status))
 
105
    {
 
106
      // Process has exited, collect return status
 
107
      exit_code= WEXITSTATUS(status);
 
108
      message("Child exit: %d", exit_code);
 
109
      // Exit with exit status of the child
 
110
      exit(exit_code);
 
111
    }
 
112
 
 
113
    if (WIFSIGNALED(status))
 
114
      message("Child killed by signal: %d", WTERMSIG(status));
 
115
 
 
116
    exit(exit_code);
 
117
  }
 
118
  exit(1);
 
119
}
 
120
 
 
121
 
 
122
extern "C" void handle_abort(int sig)
 
123
{
 
124
    message("Got signal %d, child_pid: %d, sending ABRT", sig, child_pid);
 
125
 
 
126
    if (child_pid > 0) {
 
127
        kill (-child_pid, SIGABRT);     // Don't wait for it to terminate
 
128
    }
 
129
}
 
130
 
 
131
 
 
132
extern "C" void handle_signal(int sig)
 
133
{
 
134
  message("Got signal %d, child_pid: %d", sig, child_pid);
 
135
  terminated= 1;
 
136
 
 
137
  if (child_pid > 0)
 
138
    kill_child();
 
139
 
 
140
  // Ignore further signals
 
141
  signal(SIGTERM, SIG_IGN);
 
142
  signal(SIGINT,  SIG_IGN);
 
143
 
 
144
  // Continune execution, allow the child to be started and
 
145
  // finally terminated by monitor loop
 
146
}
 
147
 
 
148
 
 
149
int main(int argc, char* const argv[] )
 
150
{
 
151
  char* const* child_argv= 0;
 
152
  pid_t own_pid= getpid();
 
153
  pid_t parent_pid= getppid();
 
154
  bool nocore = false;
 
155
 
 
156
  /* Install signal handlers */
 
157
  signal(SIGTERM, handle_signal);
 
158
  signal(SIGINT,  handle_signal);
 
159
  signal(SIGCHLD, handle_signal);
 
160
  signal(SIGABRT, handle_abort);
 
161
 
 
162
  sprintf(safe_process_name, "safe_process[%d]", own_pid);
 
163
 
 
164
  message("Started");
 
165
 
 
166
  /* Parse arguments */
 
167
  for (int i= 1; i < argc; i++) {
 
168
    const char* arg= argv[i];
 
169
    if (strcmp(arg, "--") == 0 && strlen(arg) == 2) {
 
170
      /* Got the "--" delimiter */
 
171
      if (i >= argc)
 
172
        die("No real args -> nothing to do");
 
173
      child_argv= &argv[i+1];
 
174
      break;
 
175
    } else {
 
176
      if ( strcmp(arg, "--verbose") == 0 )
 
177
        verbose++;
 
178
      else if ( strncmp(arg, "--parent-pid", 10) == 0 )
 
179
      {
 
180
        /* Override parent_pid with a value provided by user */
 
181
        const char* start;
 
182
        if ((start= strstr(arg, "=")) == NULL)
 
183
          die("Could not find start of option value in '%s'", arg);
 
184
        start++; /* Step past = */
 
185
        if ((parent_pid= atoi(start)) == 0)
 
186
          die("Invalid value '%s' passed to --parent-id", start);
 
187
      } else if ( strcmp(arg, "--nocore") == 0 )
 
188
      {
 
189
        nocore = true;  // Don't allow the process to dump core
 
190
      }
 
191
      else
 
192
        die("Unknown option: %s", arg);
 
193
    }
 
194
  }
 
195
  if (!child_argv || *child_argv == 0)
 
196
    die("nothing to do");
 
197
 
 
198
  message("parent_pid: %d", parent_pid);
 
199
  if (parent_pid == own_pid)
 
200
    die("parent_pid is equal to own pid!");
 
201
 
 
202
  char buf;
 
203
  int pfd[2];
 
204
  if (pipe(pfd) == -1)
 
205
    die("Failed to create pipe");
 
206
 
 
207
  /* Create the child process */
 
208
  while((child_pid= fork()) == -1)
 
209
  {
 
210
    message("fork failed");
 
211
    sleep(1);
 
212
  }
 
213
 
 
214
  if (child_pid == 0)
 
215
  {
 
216
    close(pfd[0]); // Close unused read end
 
217
 
 
218
    // Use default signal handlers in child
 
219
    signal(SIGTERM, SIG_DFL);
 
220
    signal(SIGINT,  SIG_DFL);
 
221
    signal(SIGCHLD, SIG_DFL);
 
222
 
 
223
    // Make this process it's own process group to be able to kill
 
224
    // it and any childs(that hasn't changed group themself)
 
225
    setpgid(0, 0);
 
226
 
 
227
    if (nocore)
 
228
    {
 
229
      struct rlimit corelim = { 0, 0 };
 
230
      if (setrlimit (RLIMIT_CORE, &corelim) < 0)
 
231
      {
 
232
        message("setrlimit failed, errno=%d", errno);
 
233
      }
 
234
    }
 
235
 
 
236
    // Signal that child is ready
 
237
    buf= 37;
 
238
    if ((write(pfd[1], &buf, 1)) < 1)
 
239
      die("Failed to signal that child is ready");
 
240
    // Close write end
 
241
    close(pfd[1]);
 
242
 
 
243
    if (execvp(child_argv[0], child_argv) < 0)
 
244
      die("Failed to exec child");
 
245
  }
 
246
 
 
247
  close(pfd[1]); // Close unused write end
 
248
 
 
249
  // Wait for child to signal it's ready
 
250
  if ((read(pfd[0], &buf, 1)) < 1)
 
251
    die("Failed to read signal from child");
 
252
 
 
253
  if (buf != 37)
 
254
    die("Didn't get 37 from pipe");
 
255
  close(pfd[0]); // Close read end
 
256
 
 
257
  /* Monitor loop */
 
258
  message("Started child %d, terminated: %d", child_pid, terminated);
 
259
 
 
260
  while(!terminated)
 
261
  {
 
262
    // Check if parent is still alive
 
263
    if (kill(parent_pid, 0) != 0){
 
264
      message("Parent is not alive anymore");
 
265
      break;
 
266
    }
 
267
 
 
268
    // Check if child has exited, normally this will be
 
269
    // detected immediately with SIGCHLD handler
 
270
    int status= 0;
 
271
    pid_t ret_pid= waitpid(child_pid, &status, WNOHANG);
 
272
    if (ret_pid == child_pid)
 
273
    {
 
274
      int ret_code= 2;
 
275
      if (WIFEXITED(status))
 
276
      {
 
277
        // Process has exited, collect return status
 
278
        ret_code= WEXITSTATUS(status);
 
279
        message("Child exit: %d", ret_code);
 
280
        // Exit with exit status of the child
 
281
        exit(ret_code);
 
282
      }
 
283
 
 
284
      if (WIFSIGNALED(status))
 
285
        message("Child killed by signal: %d", WTERMSIG(status));
 
286
 
 
287
      exit(ret_code);
 
288
    }
 
289
    sleep(1);
 
290
  }
 
291
  kill_child();
 
292
 
 
293
  return 1;
 
294
}
 
295