~jan-kneschke/mysql-proxy/packet-tracking-assertions

« back to all changes in this revision

Viewing changes to trunk/src/chassis.c

  • Committer: Kay Roepke
  • Author(s): Jan Kneschke
  • Date: 2008-01-23 22:00:28 UTC
  • Revision ID: kay@mysql.com-20080123220028-hq2xqb69apa75fnx
first round on mysql-shell based on the proxy code

this is mostly a verification if the proxy-code is flexible enough to handle 
all three scenarios of: client, server and forwarding (proxy)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $%BEGINLICENSE%$
2
 
 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3
 
 
4
 
 This program is free software; you can redistribute it and/or
5
 
 modify it under the terms of the GNU General Public License as
6
 
 published by the Free Software Foundation; version 2 of the
7
 
 License.
8
 
 
9
 
 This program is distributed in the hope that it will be useful,
10
 
 but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 
 GNU General Public License for more details.
13
 
 
14
 
 You should have received a copy of the GNU General Public License
15
 
 along with this program; if not, write to the Free Software
16
 
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17
 
 02110-1301  USA
18
 
 
19
 
 $%ENDLICENSE%$ */
20
 
 
 
1
/* Copyright (C) 2007 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
 * \mainpage
 
18
 *
 
19
 * \section Architecture
 
20
 *
 
21
 * MySQL Proxy is based around the C10k problem as described by http://kegel.com/c10k.html
 
22
 *
 
23
 * This leads to some basic features
 
24
 * - 10.000 concurrent connections in one program
 
25
 * - spreading the load over several backends
 
26
 * - each backend might be able to only handle 100 connections (max_connections)
 
27
 * 
 
28
 * We can implement 
 
29
 * - reusing idling backend connections 
 
30
 * - splitting client connections into several backend connections
 
31
 *
 
32
 * Most of the magic is happening in the scripting layer provided by lua (http://lua.org/) which
 
33
 * was picked as it:
 
34
 *
 
35
 * - is very easy to embed
 
36
 * - is small (200kb stripped) and efficient (see http://shootout.alioth.debian.org/gp4/benchmark.php?test=all&lang=all)
 
37
 * - is easy to read and write
 
38
 *
 
39
 * \section a walk through the code
 
40
 *
 
41
 * To understand the code you basicly only have to know about the three files documented below:
 
42
 *
 
43
 * - mysql-proxy.c
 
44
 *   - main()
 
45
 *     -# command-line handling
 
46
 *     -# plugin loading
 
47
 *     -# logging
 
48
 * - network-mysqld.c
 
49
 *   - network_mysqld_thread() (supposed be called as thread)
 
50
 *     -# registers event-halders (event_set(..., network_mysqld_con_accept, ...))
 
51
 *     -# calls event_base_dispatch() [libevent] in the mainloop 
 
52
 *   - network_mysqld_con_accept()
 
53
 *     -# is called when the listen()ing socket gets a incoming connection
 
54
 *     -# sets the event-handler for the established connection (e.g. network_mysqld_proxy_connection_init())
 
55
 *     -# calls network_mysqld_con_handle() on the connection 
 
56
 *   - network_mysqld_con_handle() is the state-machine
 
57
 *     -# implements the states of the \ref protocol "MySQL Protocol"
 
58
 *     -# calls plugin functions (registered by e.g. network_mysqld_proxy_connection_init()) 
 
59
 * - network-mysqld-proxy.c
 
60
 *   - implements the \ref proxy_states "proxy specific states"
 
61
 *
 
62
 * The other files only help those based main modules to do their job:
 
63
 *
 
64
 * - network-mysqld-proto.c
 
65
 *   - the byte functions around the \ref proto "MySQL protocol"
 
66
 * - network-socket.c
 
67
 *   - basic socket struct 
 
68
 * - network-mysqld-table.c
 
69
 *   - internal tables to select from on the admin interface (to be removed) 
 
70
 * - \ref sql-tokenizer.h "sql-tokenizer.y"
 
71
 *   - a flex based tokenizer for MySQL's SQL dialect (no parser) 
 
72
 * - network-conn-pool.c
 
73
 *   - a connection pool for server connections 
 
74
 */
 
75
 
 
76
 
21
77
/** @file
22
78
 * the user-interface for the MySQL Proxy @see main()
23
79
 *
30
86
 * @todo move the SQL based help out into a lua script
31
87
 */
32
88
 
 
89
 
 
90
#define SVN_REVISION "$Rev$"
 
91
 
33
92
#ifdef HAVE_CONFIG_H
34
93
#include "config.h"
35
94
#endif
51
110
#include <io.h>      /* open() */
52
111
#else
53
112
#include <unistd.h>
54
 
#include <sys/wait.h>
55
 
#include <sys/resource.h> /* for rusage in wait() */
56
113
#endif
57
114
 
58
115
#include <glib.h>
59
116
#include <gmodule.h>
60
117
 
61
 
#ifdef HAVE_LUA_H
62
118
#include <lua.h>
63
 
#include <stdio.h>
64
 
#endif
65
 
 
66
 
#ifdef HAVE_VALGRIND_VALGRIND_H
67
 
#include <valgrind/valgrind.h>
68
 
#endif
69
 
 
70
 
#ifndef HAVE_VALGRIND_VALGRIND_H
71
 
#define RUNNING_ON_VALGRIND 0
72
 
#endif
73
 
 
74
119
 
75
120
#include "network-mysqld.h"
76
121
#include "network-mysqld-proto.h"
79
124
#include "chassis-log.h"
80
125
#include "chassis-keyfile.h"
81
126
#include "chassis-mainloop.h"
82
 
#include "chassis-path.h"
83
 
#include "chassis-limits.h"
84
 
#include "chassis-filemode.h"
85
 
#include "chassis-win32-service.h"
86
 
#include "chassis-unix-daemon.h"
87
 
#include "chassis-frontend.h"
88
 
#include "chassis-options.h"
89
 
 
90
 
#ifdef WIN32
91
 
#define CHASSIS_NEWLINE "\r\n"
92
 
#else
93
 
#define CHASSIS_NEWLINE "\n"
94
 
#endif
 
127
 
 
128
/**
 
129
 * turn a GTimeVal into string
 
130
 *
 
131
 * @return string in ISO notation with micro-seconds
 
132
 */
 
133
static gchar * g_timeval_string(GTimeVal *t1, GString *str) {
 
134
        size_t used_len;
 
135
        
 
136
        g_string_set_size(str, 63);
 
137
 
 
138
        used_len = strftime(str->str, str->allocated_len, "%Y-%m-%dT%H:%M:%S", gmtime(&t1->tv_sec));
 
139
 
 
140
        g_assert(used_len < str->allocated_len);
 
141
        str->len = used_len;
 
142
 
 
143
        g_string_append_printf(str, ".%06ld", t1->tv_usec);
 
144
 
 
145
        return str->str;
 
146
}
 
147
 
 
148
 
 
149
 
 
150
#ifndef _WIN32
 
151
/**
 
152
 * start the app in the background 
 
153
 * 
 
154
 * UNIX-version
 
155
 */
 
156
static void daemonize(void) {
 
157
#ifdef SIGTTOU
 
158
        signal(SIGTTOU, SIG_IGN);
 
159
#endif
 
160
#ifdef SIGTTIN
 
161
        signal(SIGTTIN, SIG_IGN);
 
162
#endif
 
163
#ifdef SIGTSTP
 
164
        signal(SIGTSTP, SIG_IGN);
 
165
#endif
 
166
        if (fork() != 0) exit(0);
 
167
        
 
168
        if (setsid() == -1) exit(0);
 
169
 
 
170
        signal(SIGHUP, SIG_IGN);
 
171
 
 
172
        if (fork() != 0) exit(0);
 
173
        
 
174
        chdir("/");
 
175
        
 
176
        umask(0);
 
177
}
 
178
#endif
 
179
 
95
180
 
96
181
#define GETTEXT_PACKAGE "mysql-proxy"
97
182
 
98
 
/**
99
 
 * options of the MySQL Proxy frontend
100
 
 */
101
 
typedef struct {
102
 
        int print_version;
103
 
        int verbose_shutdown;
104
 
 
105
 
        int daemon_mode;
106
 
        gchar *user;
107
 
 
108
 
        gchar *base_dir;
109
 
        int auto_base_dir;
110
 
 
111
 
        gchar *default_file;
112
 
        GKeyFile *keyfile;
113
 
 
114
 
        chassis_plugin *p;
115
 
        GOptionEntry *config_entries;
116
 
 
117
 
        gchar *pid_file;
118
 
 
119
 
        gchar *plugin_dir;
120
 
        gchar **plugin_names;
121
 
 
122
 
        guint invoke_dbg_on_crash;
123
 
#ifndef _WIN32
124
 
        /* the --keepalive option isn't available on Unix */
125
 
        guint auto_restart;
126
 
#endif
127
 
 
128
 
        gint max_files_number;
129
 
 
130
 
        gint event_thread_count;
131
 
 
132
 
        gchar *log_level;
133
 
        gchar *log_filename;
134
 
        int    use_syslog;
135
 
 
136
 
        char *lua_path;
137
 
        char *lua_cpath;
138
 
        char **lua_subdirs;
139
 
} chassis_frontend_t;
140
 
 
141
 
/**
142
 
 * create a new the frontend for the chassis
143
 
 */
144
 
chassis_frontend_t *chassis_frontend_new(void) {
145
 
        chassis_frontend_t *frontend;
146
 
 
147
 
        frontend = g_slice_new0(chassis_frontend_t);
148
 
        frontend->event_thread_count = 1;
149
 
        frontend->max_files_number = 0;
150
 
 
151
 
        return frontend;
152
 
}
153
 
 
154
 
/**
155
 
 * free the frontend of the chassis
156
 
 */
157
 
void chassis_frontend_free(chassis_frontend_t *frontend) {
158
 
        if (!frontend) return;
159
 
 
160
 
        if (frontend->keyfile) g_key_file_free(frontend->keyfile);
161
 
        if (frontend->default_file) g_free(frontend->default_file);
162
 
 
163
 
 
164
 
        if (frontend->base_dir) g_free(frontend->base_dir);
165
 
        if (frontend->user) g_free(frontend->user);
166
 
        if (frontend->pid_file) g_free(frontend->pid_file);
167
 
        if (frontend->log_level) g_free(frontend->log_level);
168
 
        if (frontend->plugin_dir) g_free(frontend->plugin_dir);
169
 
 
170
 
        if (frontend->plugin_names) {
171
 
                g_strfreev(frontend->plugin_names);
172
 
        }
173
 
 
174
 
        if (frontend->lua_path) g_free(frontend->lua_path);
175
 
        if (frontend->lua_cpath) g_free(frontend->lua_cpath);
176
 
        if (frontend->lua_subdirs) g_strfreev(frontend->lua_subdirs);
177
 
 
178
 
        g_slice_free(chassis_frontend_t, frontend);
179
 
}
180
 
 
181
 
/**
182
 
 * setup the options of the chassis
183
 
 */
184
 
int chassis_frontend_set_chassis_options(chassis_frontend_t *frontend, chassis_options_t *opts) {
185
 
        chassis_options_add(opts,
186
 
                "verbose-shutdown",         0, 0, G_OPTION_ARG_NONE, &(frontend->verbose_shutdown), "Always log the exit code when shutting down", NULL);
187
 
 
188
 
        chassis_options_add(opts,
189
 
                "daemon",                   0, 0, G_OPTION_ARG_NONE, &(frontend->daemon_mode), "Start in daemon-mode", NULL);
190
 
 
191
 
#ifndef _WIN32
192
 
        chassis_options_add(opts,
193
 
                "user",                     0, 0, G_OPTION_ARG_STRING, &(frontend->user), "Run mysql-proxy as user", "<user>");
194
 
#endif
195
 
 
196
 
        chassis_options_add(opts,
197
 
                "basedir",                  0, 0, G_OPTION_ARG_STRING, &(frontend->base_dir), "Base directory to prepend to relative paths in the config", "<absolute path>");
198
 
 
199
 
        chassis_options_add(opts,
200
 
                "pid-file",                 0, 0, G_OPTION_ARG_STRING, &(frontend->pid_file), "PID file in case we are started as daemon", "<file>");
201
 
 
202
 
        chassis_options_add(opts,
203
 
                "plugin-dir",               0, 0, G_OPTION_ARG_STRING, &(frontend->plugin_dir), "path to the plugins", "<path>");
204
 
 
205
 
        chassis_options_add(opts,
206
 
                "plugins",                  0, 0, G_OPTION_ARG_STRING_ARRAY, &(frontend->plugin_names), "plugins to load", "<name>");
207
 
 
208
 
        chassis_options_add(opts,
209
 
                "log-level",                0, 0, G_OPTION_ARG_STRING, &(frontend->log_level), "log all messages of level ... or higher", "(error|warning|info|message|debug)");
210
 
 
211
 
        chassis_options_add(opts,
212
 
                "log-file",                 0, 0, G_OPTION_ARG_STRING, &(frontend->log_filename), "log all messages in a file", "<file>");
213
 
 
214
 
        chassis_options_add(opts,
215
 
                "log-use-syslog",           0, 0, G_OPTION_ARG_NONE, &(frontend->use_syslog), "log all messages to syslog", NULL);
216
 
 
217
 
        chassis_options_add(opts,
218
 
                "log-backtrace-on-crash",   0, 0, G_OPTION_ARG_NONE, &(frontend->invoke_dbg_on_crash), "try to invoke debugger on crash", NULL);
219
 
 
220
 
#ifndef _WIN32
221
 
        chassis_options_add(opts,
222
 
                "keepalive",                0, 0, G_OPTION_ARG_NONE, &(frontend->auto_restart), "try to restart the proxy if it crashed", NULL);
223
 
#endif
224
 
 
225
 
        chassis_options_add(opts,
226
 
                "max-open-files",           0, 0, G_OPTION_ARG_INT, &(frontend->max_files_number), "maximum number of open files (ulimit -n)", NULL);
227
 
 
228
 
        chassis_options_add(opts,
229
 
                "event-threads",            0, 0, G_OPTION_ARG_INT, &(frontend->event_thread_count), "number of event-handling threads (default: 1)", NULL);
230
 
 
231
 
        chassis_options_add(opts,
232
 
                "lua-path",                 0, 0, G_OPTION_ARG_STRING, &(frontend->lua_path), "set the LUA_PATH", "<...>");
233
 
 
234
 
        chassis_options_add(opts,
235
 
                "lua-cpath",                0, 0, G_OPTION_ARG_STRING, &(frontend->lua_cpath), "set the LUA_CPATH", "<...>");
236
 
 
237
 
        return 0;       
238
 
}
239
 
 
240
 
 
241
 
static void sigsegv_handler(int G_GNUC_UNUSED signum) {
242
 
        g_on_error_stack_trace(g_get_prgname());
243
 
 
244
 
        abort(); /* trigger a SIGABRT instead of just exiting */
245
 
}
246
 
 
247
 
/**
248
 
 * This is the "real" main which is called both on Windows and UNIX platforms.
249
 
 * For the Windows service case, this will also handle the notifications and set
250
 
 * up the logging support appropriately.
251
 
 */
252
 
int main_cmdline(int argc, char **argv) {
253
 
        chassis *srv = NULL;
254
 
#ifdef HAVE_SIGACTION
255
 
        static struct sigaction sigsegv_sa;
256
 
#endif
 
183
int main(int argc, char **argv) {
 
184
        chassis *srv;
 
185
        
257
186
        /* read the command-line options */
258
 
        GOptionContext *option_ctx = NULL;
259
 
        GOptionEntry *main_entries = NULL;
260
 
        chassis_frontend_t *frontend = NULL;
261
 
        chassis_options_t *opts = NULL;
262
 
 
 
187
        GOptionContext *option_ctx;
263
188
        GError *gerr = NULL;
264
 
        chassis_log *log = NULL;
265
 
 
266
 
        /* a little helper macro to set the src-location that we stepped out at to exit */
267
 
#define GOTO_EXIT(status) \
268
 
        exit_code = status; \
269
 
        exit_location = G_STRLOC; \
270
 
        goto exit_nicely;
271
 
 
 
189
        guint i;
272
190
        int exit_code = EXIT_SUCCESS;
273
 
        const gchar *exit_location = G_STRLOC;
274
 
 
275
 
        if (chassis_frontend_init_glib()) { /* init the thread, module, ... system */
276
 
                GOTO_EXIT(EXIT_FAILURE);
277
 
        }
278
 
 
279
 
        /* start the logging ... to stderr */
280
 
        log = chassis_log_new();
281
 
        log->min_lvl = G_LOG_LEVEL_MESSAGE; /* display messages while parsing or loading plugins */
 
191
        int print_version = 0;
 
192
        int daemon_mode = 0;
 
193
        const gchar *check_str = NULL;
 
194
        chassis_plugin *p;
 
195
        gchar *pid_file = NULL;
 
196
        gchar *plugin_dir = NULL;
 
197
        gchar *default_file = NULL;
 
198
        GOptionEntry *config_entries;
 
199
        gchar **plugin_names = NULL;
 
200
 
 
201
        gchar *log_level = NULL;
 
202
 
 
203
        GKeyFile *keyfile = NULL;
 
204
        chassis_log *log;
 
205
 
 
206
        /* can't appear in the configfile */
 
207
        GOptionEntry base_main_entries[] = 
 
208
        {
 
209
                { "version",                 'V', 0, G_OPTION_ARG_NONE, NULL, "Show version", NULL },
 
210
                { "defaults-file",            0, 0, G_OPTION_ARG_STRING, NULL, "configuration file", "<file>" },
 
211
                
 
212
                { NULL,                       0, 0, G_OPTION_ARG_NONE,   NULL, NULL, NULL }
 
213
        };
 
214
 
 
215
        GOptionEntry main_entries[] = 
 
216
        {
 
217
                { "daemon",                   0, 0, G_OPTION_ARG_NONE, NULL, "Start in daemon-mode", NULL },
 
218
                { "pid-file",                 0, 0, G_OPTION_ARG_STRING, NULL, "PID file in case we are started as daemon", "<file>" },
 
219
                { "plugin-dir",               0, 0, G_OPTION_ARG_STRING, NULL, "path to the plugins", "<path>" },
 
220
                { "plugins",                  0, 0, G_OPTION_ARG_STRING_ARRAY, NULL, "plugins to load", "<name>" },
 
221
                { "log-level",                0, 0, G_OPTION_ARG_STRING, NULL, "log all messages of level ... or higer", "(error|warning|info|message|debug)" },
 
222
                { "log-file",                 0, 0, G_OPTION_ARG_STRING, NULL, "log all messages in a file", "<file>" },
 
223
                { "log-use-syslog",           0, 0, G_OPTION_ARG_NONE, NULL, "send all log-messages to syslog", NULL },
 
224
                
 
225
                { NULL,                       0, 0, G_OPTION_ARG_NONE,   NULL, NULL, NULL }
 
226
        };
 
227
 
 
228
        if (!GLIB_CHECK_VERSION(2, 6, 0)) {
 
229
                g_error("the glib header are too old, need at least 2.6.0, got: %d.%d.%d", 
 
230
                                GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
 
231
        }
 
232
 
 
233
        check_str = glib_check_version(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
 
234
 
 
235
        if (check_str) {
 
236
                g_error("%s, got: lib=%d.%d.%d, headers=%d.%d.%d", 
 
237
                        check_str,
 
238
                        glib_major_version, glib_minor_version, glib_micro_version,
 
239
                        GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
 
240
        }
 
241
 
 
242
        if (!g_module_supported()) {
 
243
                g_error("loading modules is not supported on this platform");
 
244
        }
 
245
 
 
246
#ifdef HAVE_GTHREAD     
 
247
        g_thread_init(NULL);
 
248
#endif
 
249
 
 
250
        log = chassis_log_init();
 
251
        
282
252
        g_log_set_default_handler(chassis_log_func, log);
283
253
 
284
 
#ifdef _WIN32
285
 
        if (chassis_win32_is_service() && chassis_log_set_event_log(log, g_get_prgname())) {
286
 
                GOTO_EXIT(EXIT_FAILURE);
287
 
        }
288
 
 
289
 
        if (chassis_frontend_init_win32()) { /* setup winsock */
290
 
                GOTO_EXIT(EXIT_FAILURE);
291
 
        }
292
 
#endif
293
 
 
294
 
        /* may fail on library mismatch */
295
 
        if (NULL == (srv = chassis_new())) {
296
 
                GOTO_EXIT(EXIT_FAILURE);
297
 
        }
298
 
 
299
 
        srv->log = log; /* we need the log structure for the log-rotation */
300
 
 
301
 
        frontend = chassis_frontend_new();
302
 
        option_ctx = g_option_context_new("- MySQL Proxy");
 
254
        srv = chassis_init();
 
255
        /* assign the mysqld part to the */
 
256
        network_mysqld_init(srv);
 
257
 
 
258
        i = 0;
 
259
        base_main_entries[i++].arg_data  = &(print_version);
 
260
        base_main_entries[i++].arg_data  = &(default_file);
 
261
 
 
262
        i = 0;
 
263
        main_entries[i++].arg_data  = &(daemon_mode);
 
264
        main_entries[i++].arg_data  = &(pid_file);
 
265
        main_entries[i++].arg_data  = &(plugin_dir);
 
266
        main_entries[i++].arg_data  = &(plugin_names);
 
267
 
 
268
        main_entries[i++].arg_data  = &(log_level);
 
269
        main_entries[i++].arg_data  = &(log->log_filename);
 
270
        main_entries[i++].arg_data  = &(log->use_syslog);
 
271
 
 
272
        option_ctx = g_option_context_new("- MySQL App Shell");
 
273
        g_option_context_add_main_entries(option_ctx, base_main_entries, GETTEXT_PACKAGE);
 
274
        g_option_context_set_help_enabled(option_ctx, FALSE);
 
275
        g_option_context_set_ignore_unknown_options(option_ctx, TRUE);
 
276
 
303
277
        /**
304
278
         * parse once to get the basic options like --defaults-file and --version
305
279
         *
306
280
         * leave the unknown options in the list
307
281
         */
308
 
        if (chassis_frontend_init_base_options(option_ctx,
309
 
                                &argc, &argv,
310
 
                                &(frontend->print_version),
311
 
                                &(frontend->default_file),
312
 
                                &gerr)) {
313
 
                g_critical("%s: %s",
314
 
                                G_STRLOC,
315
 
                                gerr->message);
316
 
                g_clear_error(&gerr);
317
 
 
318
 
                GOTO_EXIT(EXIT_FAILURE);
 
282
        if (FALSE == g_option_context_parse(option_ctx, &argc, &argv, &gerr)) {
 
283
                g_critical("%s", gerr->message);
 
284
                
 
285
                exit_code = EXIT_FAILURE;
 
286
                goto exit_nicely;
319
287
        }
320
288
 
321
 
        if (frontend->default_file) {
322
 
                if (!(frontend->keyfile = chassis_frontend_open_config_file(frontend->default_file, &gerr))) {
323
 
                        g_critical("%s: loading config from '%s' failed: %s",
324
 
                                        G_STRLOC,
325
 
                                        frontend->default_file,
 
289
        if (default_file) {
 
290
                keyfile = g_key_file_new();
 
291
                g_key_file_set_list_separator(keyfile, ',');
 
292
 
 
293
                if (FALSE == g_key_file_load_from_file(keyfile, default_file, G_KEY_FILE_NONE, &gerr)) {
 
294
                        g_critical("loading configuration from %s failed: %s", 
 
295
                                        default_file,
326
296
                                        gerr->message);
327
 
                        g_clear_error(&gerr);
328
 
                        GOTO_EXIT(EXIT_FAILURE);
 
297
 
 
298
                        exit_code = EXIT_FAILURE;
 
299
                        goto exit_nicely;
329
300
                }
330
301
        }
331
302
 
332
 
        /* print the main version number here, but don't exit
333
 
         * we check for print_version again, after loading the plugins (if any)
334
 
         * and print their version numbers, too. then we exit cleanly.
335
 
         */
336
 
        if (frontend->print_version) {
337
 
#ifndef CHASSIS_BUILD_TAG
338
 
#define CHASSIS_BUILD_TAG PACKAGE_STRING
 
303
        if (print_version) {
 
304
                printf("%s\r\n", PACKAGE_STRING); 
 
305
                printf("  glib2: %d.%d.%d\r\n", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
 
306
#ifdef HAVE_EVENT_H
 
307
                printf("  libevent: %s\r\n", event_get_version());
339
308
#endif
340
 
                g_print("%s" CHASSIS_NEWLINE, CHASSIS_BUILD_TAG); 
341
 
                chassis_frontend_print_version();
 
309
 
 
310
                exit_code = EXIT_SUCCESS;
 
311
                goto exit_nicely;
342
312
        }
343
 
        
 
313
 
 
314
 
344
315
        /* add the other options which can also appear in the configfile */
345
 
        opts = chassis_options_new();
346
 
        chassis_frontend_set_chassis_options(frontend, opts);
347
 
        main_entries = chassis_options_to_g_option_entries(opts);
348
 
        g_option_context_add_main_entries(option_ctx, main_entries, NULL);
 
316
        g_option_context_add_main_entries(option_ctx, main_entries, GETTEXT_PACKAGE);
349
317
 
350
318
        /**
351
319
         * parse once to get the basic options 
355
323
        if (FALSE == g_option_context_parse(option_ctx, &argc, &argv, &gerr)) {
356
324
                g_critical("%s", gerr->message);
357
325
 
358
 
                GOTO_EXIT(EXIT_FAILURE);
359
 
        }
360
 
 
361
 
        if (frontend->keyfile) {
362
 
                if (chassis_keyfile_to_options(frontend->keyfile, "mysql-proxy", main_entries)) {
363
 
                        GOTO_EXIT(EXIT_FAILURE);
364
 
                }
365
 
        }
366
 
 
367
 
 
368
 
        if (chassis_frontend_init_basedir(argv[0], &(frontend->base_dir))) {
369
 
                GOTO_EXIT(EXIT_FAILURE);
370
 
        }
371
 
 
372
 
        /* basic setup is done, base-dir is known, ... */
373
 
        frontend->lua_subdirs = g_new(char *, 2);
374
 
        frontend->lua_subdirs[0] = g_strdup("mysql-proxy");
375
 
        frontend->lua_subdirs[1] = NULL;
376
 
 
377
 
        if (chassis_frontend_init_lua_path(frontend->lua_path, frontend->base_dir, frontend->lua_subdirs)) {
378
 
                GOTO_EXIT(EXIT_FAILURE);
379
 
        }
380
 
        
381
 
        if (chassis_frontend_init_lua_cpath(frontend->lua_cpath, frontend->base_dir, frontend->lua_subdirs)) {
382
 
                GOTO_EXIT(EXIT_FAILURE);
383
 
        }
384
 
 
385
 
        /* assign the mysqld part to the */
386
 
        network_mysqld_init(srv); /* starts the also the lua-scope, LUA_PATH and LUA_CPATH have to be set before this being called */
387
 
 
388
 
 
389
 
#ifdef HAVE_SIGACTION
390
 
        /* register the sigsegv interceptor */
391
 
 
392
 
        memset(&sigsegv_sa, 0, sizeof(sigsegv_sa));
393
 
        sigsegv_sa.sa_handler = sigsegv_handler;
394
 
        sigemptyset(&sigsegv_sa.sa_mask);
395
 
 
396
 
        if (frontend->invoke_dbg_on_crash && !(RUNNING_ON_VALGRIND)) {
397
 
                sigaction(SIGSEGV, &sigsegv_sa, NULL);
398
 
        }
399
 
#endif
400
 
 
401
 
        /*
402
 
         * some plugins cannot see the chassis struct from the point
403
 
         * where they open files, hence we must make it available
404
 
         */
405
 
        srv->base_dir = g_strdup(frontend->base_dir);
406
 
 
407
 
        chassis_frontend_init_plugin_dir(&frontend->plugin_dir, srv->base_dir);
408
 
        
409
 
        /* 
410
 
         * these are used before we gathered all the options
411
 
         * from the plugins, thus we need to fix them up before
412
 
         * dealing with all the rest.
413
 
         */
414
 
        chassis_resolve_path(srv->base_dir, &frontend->log_filename);
415
 
        chassis_resolve_path(srv->base_dir, &frontend->pid_file);
416
 
        chassis_resolve_path(srv->base_dir, &frontend->plugin_dir);
417
 
 
418
 
        /*
419
 
         * start the logging
420
 
         */
421
 
        if (frontend->log_filename) {
422
 
                log->log_filename = g_strdup(frontend->log_filename);
423
 
        }
424
 
 
425
 
        log->use_syslog = frontend->use_syslog;
426
 
 
427
 
        if (log->log_filename && log->use_syslog) {
428
 
                g_critical("%s: log-file and log-use-syslog were given, but only one is allowed",
429
 
                                G_STRLOC);
430
 
                GOTO_EXIT(EXIT_FAILURE);
431
 
        }
432
 
 
433
 
        if (log->log_filename && FALSE == chassis_log_open(log)) {
434
 
                g_critical("can't open log-file '%s': %s", log->log_filename, g_strerror(errno));
435
 
 
436
 
                GOTO_EXIT(EXIT_FAILURE);
437
 
        }
438
 
 
439
 
        /* handle log-level after the config-file is read, just in case it is specified in the file */
440
 
        if (frontend->log_level) {
441
 
                if (0 != chassis_log_set_level(log, frontend->log_level)) {
442
 
                        g_critical("--log-level=... failed, level '%s' is unknown ",
443
 
                                        frontend->log_level);
444
 
 
445
 
                        GOTO_EXIT(EXIT_FAILURE);
446
 
                }
447
 
        } else {
448
 
                /* if it is not set, use "critical" as default */
449
 
                log->min_lvl = G_LOG_LEVEL_CRITICAL;
450
 
        }
451
 
 
452
 
        /*
453
 
         * the MySQL Proxy should load 'admin' and 'proxy' plugins
454
 
         */
455
 
        if (!frontend->plugin_names) {
456
 
                frontend->plugin_names = g_new(char *, 3);
457
 
 
458
 
                frontend->plugin_names[0] = g_strdup("admin");
459
 
                frontend->plugin_names[1] = g_strdup("proxy");
460
 
                frontend->plugin_names[2] = NULL;
461
 
        }
462
 
 
463
 
        if (chassis_frontend_load_plugins(srv->modules,
464
 
                                frontend->plugin_dir,
465
 
                                frontend->plugin_names)) {
466
 
                GOTO_EXIT(EXIT_FAILURE);
467
 
        }
468
 
 
469
 
        if (chassis_frontend_init_plugins(srv->modules,
470
 
                                option_ctx,
471
 
                                &argc, &argv,
472
 
                                frontend->keyfile,
473
 
                                "mysql-proxy",
474
 
                                srv->base_dir,
475
 
                                &gerr)) {
476
 
                g_critical("%s: %s",
477
 
                                G_STRLOC, 
478
 
                                gerr->message);
479
 
                g_clear_error(&gerr);
480
 
 
481
 
                GOTO_EXIT(EXIT_FAILURE);
482
 
        }
483
 
 
484
 
 
485
 
        /* if we only print the version numbers, exit and don't do any more work */
486
 
        if (frontend->print_version) {
487
 
                chassis_frontend_print_lua_version();
488
 
                chassis_frontend_print_plugin_versions(srv->modules);
489
 
                GOTO_EXIT(EXIT_SUCCESS);
 
326
                exit_code = EXIT_FAILURE;
 
327
                goto exit_nicely;
 
328
        }
 
329
 
 
330
        if (log->log_filename) {
 
331
                if (0 != chassis_log_open(log)) {
 
332
                        g_critical("can't open log-file '%s': %s", log->log_filename, g_strerror(errno));
 
333
 
 
334
                        exit_code = EXIT_FAILURE;
 
335
                        goto exit_nicely;
 
336
                }
 
337
        }
 
338
 
 
339
        if (log_level) {
 
340
                if (0 != chassis_log_set_level(log, log_level)) {
 
341
                        g_critical("--log-level=... failed, level '%s' is unknown ", log_level);
 
342
 
 
343
                        exit_code = EXIT_FAILURE;
 
344
                        goto exit_nicely;
 
345
                }
 
346
        }
 
347
 
 
348
        if (keyfile) {
 
349
                if (chassis_keyfile_to_options(keyfile, "mysql-proxy", main_entries)) {
 
350
                        exit_code = EXIT_FAILURE;
 
351
                        goto exit_nicely;
 
352
                }
 
353
        }
 
354
 
 
355
        if (!plugin_dir) plugin_dir = g_strdup(LIBDIR);
 
356
 
 
357
        /* if not plugins are specified, load admin and proxy */
 
358
        if (!plugin_names) {
 
359
                plugin_names = g_new0(char *, 3);
 
360
 
 
361
#define IS_PNAME(pname) \
 
362
                ((strlen(argv[0]) > sizeof(pname) - 1) && \
 
363
                 0 == strcmp(argv[0] + strlen(argv[0]) - (sizeof(pname) - 1), pname) \
 
364
                )
 
365
 
 
366
                /* check what we are called as */
 
367
                if (IS_PNAME("mysql-proxy")) {
 
368
                        plugin_names[0] = g_strdup("admin");
 
369
                        plugin_names[1] = g_strdup("proxy");
 
370
                        plugin_names[2] = NULL;
 
371
                }
 
372
        }
 
373
 
 
374
        /* load the plugins */
 
375
        for (i = 0; plugin_names && plugin_names[i]; i++) {
 
376
                char *plugin_filename = g_strdup_printf("lib%s.la", plugin_names[i]);
 
377
 
 
378
                p = chassis_plugin_load(plugin_dir, plugin_filename);
 
379
                g_free(plugin_filename);
 
380
                
 
381
                if (NULL == p) {
 
382
                        g_critical("setting --plugins-dir=<dir> might help");
 
383
                        exit_code = EXIT_FAILURE;
 
384
                        goto exit_nicely;
 
385
                }
 
386
 
 
387
                g_ptr_array_add(srv->modules, p);
 
388
 
 
389
                if (NULL != (config_entries = chassis_plugin_get_options(p))) {
 
390
                        gchar *group_desc = g_strdup_printf("%s-module", plugin_names[i]);
 
391
                        gchar *help_msg = g_strdup_printf("Show options for the %s-module", plugin_names[i]);
 
392
                        const gchar *group_name = plugin_names[i];
 
393
 
 
394
                        GOptionGroup *option_grp = g_option_group_new(group_name, group_desc, help_msg, NULL, NULL);
 
395
                        g_option_group_add_entries(option_grp, config_entries);
 
396
                        g_option_context_add_group(option_ctx, option_grp);
 
397
 
 
398
                        g_free(help_msg);
 
399
                        g_free(group_desc);
 
400
 
 
401
                        /* parse the new options */
 
402
                        if (FALSE == g_option_context_parse(option_ctx, &argc, &argv, &gerr)) {
 
403
                                g_critical("%s", gerr->message);
 
404
                
 
405
                                exit_code = EXIT_FAILURE;
 
406
                                goto exit_nicely;
 
407
                        }
 
408
        
 
409
                        if (keyfile) {
 
410
                                if (chassis_keyfile_to_options(keyfile, "mysql-proxy", config_entries)) {
 
411
                                        exit_code = EXIT_FAILURE;
 
412
                                        goto exit_nicely;
 
413
                                }
 
414
                        }
 
415
                }
490
416
        }
491
417
 
492
418
        /* we know about the options now, lets parse them */
495
421
 
496
422
        /* handle unknown options */
497
423
        if (FALSE == g_option_context_parse(option_ctx, &argc, &argv, &gerr)) {
498
 
                if (gerr->domain == G_OPTION_ERROR &&
499
 
                    gerr->code == G_OPTION_ERROR_UNKNOWN_OPTION) {
500
 
                        g_critical("%s: %s (use --help to show all options)", 
501
 
                                        G_STRLOC, 
502
 
                                        gerr->message);
503
 
                } else {
504
 
                        g_critical("%s: %s (code = %d, domain = %s)", 
505
 
                                        G_STRLOC, 
506
 
                                        gerr->message,
507
 
                                        gerr->code,
508
 
                                        g_quark_to_string(gerr->domain)
509
 
                                        );
510
 
                }
 
424
                g_critical("%s", gerr->message);
511
425
                
512
 
                GOTO_EXIT(EXIT_FAILURE);
 
426
                exit_code = EXIT_FAILURE;
 
427
                goto exit_nicely;
513
428
        }
514
429
 
515
430
        g_option_context_free(option_ctx);
519
434
        if (argc > 1) {
520
435
                g_critical("unknown option: %s", argv[1]);
521
436
 
522
 
                GOTO_EXIT(EXIT_FAILURE);
523
 
        }
524
 
 
525
 
        /* make sure that he max-thread-count isn't negative */
526
 
        if (frontend->event_thread_count < 1) {
527
 
                g_critical("--event-threads has to be >= 1, is %d", frontend->event_thread_count);
528
 
 
529
 
                GOTO_EXIT(EXIT_FAILURE);
530
 
        }
531
 
 
532
 
        srv->event_thread_count = frontend->event_thread_count;
533
 
        
 
437
                exit_code = EXIT_FAILURE;
 
438
                goto exit_nicely;
 
439
        }
 
440
 
 
441
 
 
442
#if defined(HAVE_LUA_H) && defined(DATADIR)
 
443
        /**
 
444
         * if the LUA_PATH is not set, set a good default 
 
445
         */
 
446
        if (!g_getenv("LUA_PATH")) {
 
447
                g_setenv("LUA_PATH", LUA_PATHSEP LUA_PATHSEP DATADIR "/?.lua", 1);
 
448
        }
 
449
#endif
 
450
 
534
451
#ifndef _WIN32  
535
452
        signal(SIGPIPE, SIG_IGN);
536
453
 
537
 
        if (frontend->daemon_mode) {
538
 
                chassis_unix_daemonize();
 
454
        if (daemon_mode) {
 
455
                daemonize();
539
456
        }
540
 
 
541
 
        if (frontend->auto_restart) {
542
 
                int child_exit_status = EXIT_SUCCESS; /* forward the exit-status of the child */
543
 
                int ret = chassis_unix_proc_keepalive(&child_exit_status);
544
 
 
545
 
                if (ret > 0) {
546
 
                        /* the agent stopped */
547
 
                
548
 
                        exit_code = child_exit_status;
 
457
#endif
 
458
        if (pid_file) {
 
459
                int fd;
 
460
                gchar *pid_str;
 
461
 
 
462
                /**
 
463
                 * write the PID file
 
464
                 */
 
465
 
 
466
                if (-1 == (fd = open(pid_file, O_WRONLY|O_TRUNC|O_CREAT, 0600))) {
 
467
                        g_critical("%s.%d: open(%s) failed: %s", 
 
468
                                        __FILE__, __LINE__,
 
469
                                        pid_file,
 
470
                                        strerror(errno));
 
471
 
 
472
                        exit_code = EXIT_FAILURE;
549
473
                        goto exit_nicely;
550
 
                } else if (ret < 0) {
551
 
                        GOTO_EXIT(EXIT_FAILURE);
552
 
                } else {
553
 
                        /* we are the child, go on */
554
 
                }
555
 
        }
556
 
#endif
557
 
        if (frontend->pid_file) {
558
 
                if (0 != chassis_frontend_write_pidfile(frontend->pid_file, &gerr)) {
559
 
                        g_critical("%s", gerr->message);
560
 
                        g_clear_error(&gerr);
561
 
 
562
 
                        GOTO_EXIT(EXIT_FAILURE);
563
 
                }
564
 
        }
565
 
 
566
 
        /* the message has to be _after_ the g_option_content_parse() to 
567
 
         * hide from the output if the --help is asked for
568
 
         */
569
 
        g_message("%s started", PACKAGE_STRING); /* add tag to the logfile (after we opened the logfile) */
570
 
 
571
 
#ifdef _WIN32
572
 
        if (chassis_win32_is_service()) chassis_win32_service_set_state(SERVICE_RUNNING, 0);
573
 
#endif
574
 
 
575
 
        /*
576
 
         * we have to drop root privileges in chassis_mainloop() after
577
 
         * the plugins opened the ports, so we need the user there
578
 
         */
579
 
        srv->user = g_strdup(frontend->user);
580
 
 
581
 
        if (frontend->max_files_number) {
582
 
                if (0 != chassis_fdlimit_set(frontend->max_files_number)) {
583
 
                        g_critical("%s: setting fdlimit = %d failed: %s (%d)",
584
 
                                        G_STRLOC,
585
 
                                        frontend->max_files_number,
586
 
                                        g_strerror(errno),
587
 
                                        errno);
588
 
                        GOTO_EXIT(EXIT_FAILURE);
589
 
                }
590
 
        }
591
 
        g_debug("max open file-descriptors = %"G_GINT64_FORMAT,
592
 
                        chassis_fdlimit_get());
 
474
                }
 
475
 
 
476
                pid_str = g_strdup_printf("%d", getpid());
 
477
 
 
478
                write(fd, pid_str, strlen(pid_str));
 
479
                g_free(pid_str);
 
480
 
 
481
                close(fd);
 
482
        }
593
483
 
594
484
        if (chassis_mainloop(srv)) {
595
485
                /* looks like we failed */
596
 
                g_critical("%s: Failure from chassis_mainloop. Shutting down.", G_STRLOC);
597
 
                GOTO_EXIT(EXIT_FAILURE);
 
486
 
 
487
                exit_code = EXIT_FAILURE;
 
488
                goto exit_nicely;
598
489
        }
599
490
 
600
491
exit_nicely:
601
 
        /* necessary to set the shutdown flag, because the monitor will continue
602
 
         * to schedule timers otherwise, causing an infinite loop in cleanup
603
 
         */
604
 
        if (!exit_code) {
605
 
                exit_location = G_STRLOC;
606
 
        }
607
 
        chassis_set_shutdown_location(exit_location);
608
 
 
609
 
        if (!frontend->print_version) {
610
 
                g_log(G_LOG_DOMAIN, (frontend->verbose_shutdown ? G_LOG_LEVEL_CRITICAL : G_LOG_LEVEL_MESSAGE),
611
 
                                "shutting down normally, exit code is: %d", exit_code); /* add a tag to the logfile */
612
 
        }
613
 
 
614
 
#ifdef _WIN32
615
 
        if (chassis_win32_is_service()) chassis_win32_service_set_state(SERVICE_STOP_PENDING, 0);
616
 
#endif
617
 
 
618
 
        chassis_frontend_free(frontend);        
619
 
        
 
492
        if (keyfile) g_key_file_free(keyfile);
 
493
        if (default_file) g_free(default_file);
 
494
 
620
495
        if (gerr) g_error_free(gerr);
621
496
        if (option_ctx) g_option_context_free(option_ctx);
622
497
        if (srv) chassis_free(srv);
623
 
        if (opts) chassis_options_free(opts);
624
 
        if (main_entries) g_free(main_entries);
 
498
 
 
499
        if (pid_file) g_free(pid_file);
 
500
        if (log_level) g_free(log_level);
 
501
        if (plugin_dir) g_free(plugin_dir);
 
502
 
 
503
        if (plugin_names) {
 
504
                for (i = 0; plugin_names[i]; i++) {
 
505
                        g_free(plugin_names[i]);
 
506
                }
 
507
                g_free(plugin_names);
 
508
        }
625
509
 
626
510
        chassis_log_free(log);
627
 
        
628
 
#ifdef _WIN32
629
 
        if (chassis_win32_is_service()) chassis_win32_service_set_state(SERVICE_STOPPED, 0);
630
 
#endif
631
 
 
632
 
#ifdef HAVE_SIGACTION
633
 
        /* reset the handler */
634
 
        sigsegv_sa.sa_handler = SIG_DFL;
635
 
        if (frontend->invoke_dbg_on_crash && !(RUNNING_ON_VALGRIND)) {
636
 
                sigaction(SIGSEGV, &sigsegv_sa, NULL);
637
 
        }
638
 
#endif
639
511
 
640
512
        return exit_code;
641
513
}
642
514
 
643
 
/**
644
 
 * On Windows we first look if we are started as a service and 
645
 
 * set that up if appropriate.
646
 
 * We eventually fall down through to main_cmdline, even on Windows.
647
 
 */
648
 
int main(int argc, char **argv) {
649
 
#ifdef WIN32_AS_SERVICE
650
 
        return main_win32(argc, argv, main_cmdline);
651
 
#else
652
 
        return main_cmdline(argc, argv);
653
 
#endif
654
 
}
655