/* * This file is distributed as part of the SkySQL Gateway. It is free * software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, * version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright SkySQL Ab 2013 * */ /** * @file gateway.c - The gateway entry point. * * @verbatim * Revision History * * Date Who Description * 23-05-2013 Massimiliano Pinto epoll loop test * 12-06-2013 Mark Riddoch Add the -p option to set the * listening port * and bind addr is 0.0.0.0 * 19/06/13 Mark Riddoch Extract the epoll functionality * 21/06/13 Mark Riddoch Added initial config support * 27/06/13 * 28/06/13 Vilho Raatikka Added necessary headers, example functions and * calls to log manager and to query classifier. * Put example code behind SS_DEBUG macros. * * @endverbatim */ #define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include # include # include /** for procname */ #define _GNU_SOURCE extern char *program_invocation_name; extern char *program_invocation_short_name; /** * Variable holding the enabled logfiles information. * Used from log users to check enabled logs prior calling * actual library calls such as skygw_log_write. */ extern int lm_enabled_logfiles_bitmask; /* * Server options are passed to the mysql_server_init. Each gateway must have a unique * data directory that is passed to the mysql_server_init, therefore the data directory * is not fixed here and will be updated elsewhere. */ static char* server_options[] = { "SkySQL Gateway", "--datadir=", "--language=", "--skip-innodb", "--default-storage-engine=myisam", NULL }; const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1; const char* default_cnf_fname = "etc/MaxScale.cnf"; static char* server_groups[] = { "embedded", "server", "embedded", "server", "server", NULL }; /* The data directory we created for this gateway instance */ static char datadir[PATH_MAX+1] = ""; /** * exit flag for log flusher. */ static bool do_exit = FALSE; /** * Flag to indicate whether libmysqld is successfully initialized. */ static bool libmysqld_started = FALSE; /** * If MaxScale is started to run in daemon process the value is true. */ static bool daemon_mode = true; static void log_flush_shutdown(void); static void log_flush_cb(void* arg); static void libmysqld_done(void); static bool file_write_header(FILE* outfile); static bool file_write_footer(FILE* outfile); static void write_footer(void); static int ntfw_cb(const char*, const struct stat*, int, struct FTW*); static bool file_is_readable(char* absolute_pathname); static bool file_is_writable(char* absolute_pathname); static void usage(void); static char* get_expanded_pathname( char** abs_path, char* input_path, char* fname); static void print_log_n_stderr( bool do_log, bool do_stderr, char* logstr, char* fprstr, int eno); static bool resolve_maxscale_conf_fname( char** cnf_full_path, char* home_dir, char* cnf_file_arg); static bool resolve_maxscale_homedir( char** p_home_dir); /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. */ static void sighup_handler (int i) { skygw_log_write( LOGFILE_MESSAGE, "Refreshing configuration following SIGHUP\n"); config_reload(); } static void sigterm_handler (int i) { extern void shutdown_server(); skygw_log_write_flush( LOGFILE_ERROR, "MaxScale received signal SIGTERM. Exiting."); shutdown_server(); } static void sigint_handler (int i) { extern void shutdown_server(); skygw_log_write_flush( LOGFILE_ERROR, "MaxScale received signal SIGINT. Shutting down."); shutdown_server(); fprintf(stderr, "\n\nShutting down MaxScale\n\n"); } /** * @node Wraps sigaction calls * * Parameters: * @param sig Signal to set * @param void Handler function for signal * * * @return 0 in success, 1 otherwise * * * @details (write detailed description here) * */ static int signal_set (int sig, void (*handler)(int)) { static struct sigaction sigact; static int err; int rc = 0; memset(&sigact, 0, sizeof(struct sigaction)); sigact.sa_handler = handler; GW_NOINTR_CALL(err = sigaction(sig, &sigact, NULL)); if (err < 0) { int eno = errno; errno = 0; skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed call sigaction() in %s due to %d, %s.", program_invocation_short_name, eno, strerror(eno)); rc = 1; } return rc; } /** * Cleanup the temporary data directory we created for the gateway */ int ntfw_cb( const char* filename, const struct stat* filestat, int fileflags, struct FTW* pfwt) { int rc = remove(filename); if (rc != 0) { int eno = errno; errno = 0; skygw_log_write( LOGFILE_ERROR, "Error : Failed to remove the data directory %s of " "MaxScale due to %d, %s.", datadir, eno, strerror(eno)); } return rc; } void datadir_cleanup() { int depth = 1; int flags = FTW_CHDIR|FTW_DEPTH|FTW_MOUNT; int rc; if (datadir[0] != 0 && access(datadir, F_OK) == 0) { rc = nftw(datadir, ntfw_cb, depth, flags); } } static void libmysqld_done(void) { if (libmysqld_started) { mysql_library_end(); } } static void write_footer(void) { file_write_footer(stdout); } static bool file_write_footer( FILE* outfile) { bool succp = false; size_t wbytes1; size_t len1; const char* header_buf1; header_buf1 = "------------------------------------------------------" "\n\n"; len1 = strlen(header_buf1); wbytes1=fwrite((void*)header_buf1, len1, 1, outfile); succp = true; return succp; } static bool file_write_header( FILE* outfile) { bool succp = false; size_t wbytes1; size_t wbytes2; size_t wbytes3; size_t len1; size_t len2; size_t len3; const char* header_buf1; char* header_buf2 = NULL; const char* header_buf3; time_t* t = NULL; struct tm* tm = NULL; if ((t = (time_t *)malloc(sizeof(time_t))) == NULL) { goto return_succp; } if ((tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) { goto return_succp; } *t = time(NULL); *tm = *localtime(t); header_buf1 = "\n\nSkySQL MaxScale\t"; header_buf2 = strdup(asctime(tm)); if (header_buf2 == NULL) { goto return_succp; } header_buf3 = "------------------------------------------------------\n"; len1 = strlen(header_buf1); len2 = strlen(header_buf2); len3 = strlen(header_buf3); #if defined(LAPTOP_TEST) usleep(DISKWRITE_LATENCY); #else wbytes1=fwrite((void*)header_buf1, len1, 1, outfile); wbytes2=fwrite((void*)header_buf2, len2, 1, outfile); wbytes3=fwrite((void*)header_buf3, len3, 1, outfile); #endif succp = true; return_succp: if (tm != NULL) { free(tm); } if (t != NULL) { free(t); } if (header_buf2 != NULL) { free(header_buf2); } return succp; } static bool resolve_maxscale_conf_fname( char** cnf_full_path, char* home_dir, char* cnf_file_arg) { bool succp = false; if (cnf_file_arg != NULL) { char* home_etc_dir; /** * 1. argument is valid full pathname * '- /home/jdoe/MaxScale/myconf.cnf' */ if (file_is_readable(cnf_file_arg)) { *cnf_full_path = cnf_file_arg; succp = true; goto return_succp; } /** * 2. argument is file name only and file is located in home * directory. * '-f MaxScale.cnf' */ home_etc_dir = (char*)malloc(strlen(home_dir)+strlen("/etc")+1); snprintf(home_etc_dir, strlen(home_dir)+strlen("/etc")+1, "%s/etc", home_dir); *cnf_full_path = get_expanded_pathname(NULL, home_etc_dir, cnf_file_arg); free(home_etc_dir); if (*cnf_full_path != NULL) { if (file_is_readable(*cnf_full_path)) { succp = true; goto return_succp; } else { char* logstr = "Found config file but wasn't " "able to read it."; int eno = errno; print_log_n_stderr(true, true, logstr, logstr, eno); goto return_succp; } } /** * 3. argument is valid relative pathname * '-f ../myconf.cnf' */ if (realpath(cnf_file_arg, *cnf_full_path) != NULL) { if (file_is_readable(*cnf_full_path)) { succp = true; goto return_succp; } else { char* logstr = "Failed to open read access to " "config file."; int eno = errno; print_log_n_stderr(true, true, logstr, logstr, eno); goto return_succp; } } else { char* logstr = "Failed to expand config file name to " "complete path."; int eno = errno; errno = 0; print_log_n_stderr(true, true, logstr, logstr, eno); goto return_succp; } } else /**] [-f ]\n* where:\n* " "-h help\n* -d enable running in terminal process (default:disabled)\n* " "-c relative|absolute MaxScale home directory\n* " "-f relative|absolute pathname of MaxScale configuration file (default:MAXSCALE_HOME/etc/MaxScale.cnf)\n*\n"); } /** * The main entry point into the gateway * * @param argc The argument count * @param argv The array of arguments themselves * * @return 0 in success, 1 otherwise * * * @details Logging and error printing: * --- * What is printed to the terminal is something that the user can understand, * and/or something what the user can do for. For example, fix configuration. * More detailed messages are printed to error log, and optionally to trace * and debug log. * * As soon as process switches to daemon process, stderr printing is stopped - * except when it comes to command-line arguments processing. * This is not obvious solution because stderr is often directed to somewhere, * but currently this is the case. * * The configuration file is by default /etc/MaxScale.cnf * The name of configuration file and its location can be specified by * command-line argument. * * is resolved in the following order: * 1. from '-c ' command-line argument * 2. from MAXSCALE_HOME environment variable * 3. /etc/ if MaxScale.cnf is found from there * 4. current working directory if MaxScale.cnf is found from there * * is resolved in the following order: * 1. from '-f ' command-line argument * 2. by using default value "MaxScale.cnf" * * vraa 25.11.13 * */ int main(int argc, char **argv) { int rc = 0; int l; int i; int n; int n_threads; /**