/*------------------------------------------------------------------------- * * pgxc_ctl.c * * Main module of Postgres-XC configuration and operation tool. * * Copyright (c) 2013 Postgres-XC Development Group * *------------------------------------------------------------------------- */ /* * PXC_CTL Postgres-XC configurator and operation tool * * * Command line options * * -c --configuration file : configuration file. Rerative path * start at $HOME/.pgxc_ctl or homedir if * specified by --home option * --home homedir : home directory of pgxc_ctl. Default is * $HOME/.pgxc_ctl. You can override this * with PGXC_CTL_HOME environment or option. * Command argument has the highest priority. * * -v | --verbose: verbose mode. You can set your default in * pgxc_ctl_rc file at home. * * --silent: Opposite to --verbose. * * -V | --version: prints out the version * * -l | --logdir dir: Log directory. Default is $home/pgxc_log * * -L | --logfile file: log file. Default is the timestamp. * Relative path starts with --logdir. * */ #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "pg_config.h" #include "variables.h" #include "pgxc_ctl.h" #include "bash_handler.h" #include "signature.h" #include "pgxc_ctl_log.h" #include "varnames.h" #include "do_command.h" #include "utils.h" /* * Common global variable */ char pgxc_ctl_home[MAXPATH+1]; char pgxc_ctl_bash_path[MAXPATH+1]; char pgxc_ctl_config_path[MAXPATH+1]; char progname[MAXPATH+1]; char *myName; char *defaultDatabase; FILE *inF; FILE *outF; static void build_pgxc_ctl_home(char *home); static void trim_trailing_slash(char *path); static void startLog(char *path, char *logFileNam); static void print_version(void); static void print_help(void); static void trim_trailing_slash(char *path) { char *curr = path; char *last = path; while (*curr) { last = curr; curr++; } while (last != path) { if (*last == '/') { last = 0; last--; continue; } else return; } } static void build_pgxc_ctl_home(char *home) { char *env_pgxc_ctl_home = getenv(PGXC_CTL_HOME); char *env_home = getenv(HOME); /* We assume this is always available */ if (home) { if (home[0] == '/') { /* Absolute path */ strncpy(pgxc_ctl_home, home, MAXPATH); goto set_bash; } else { /* Relative path */ trim_trailing_slash(home); snprintf(pgxc_ctl_home, MAXPATH, "%s/%s", env_home, home); goto set_bash; } } if ((env_pgxc_ctl_home = getenv(PGXC_CTL_HOME)) == NULL) { snprintf(pgxc_ctl_home, MAXPATH, "%s/%s", env_home, pgxc_ctl_home_def); goto set_bash; } if (env_pgxc_ctl_home[0] == '/') /* Absoute path */ { strncpy(pgxc_ctl_home, env_pgxc_ctl_home, MAXPATH); goto set_bash; } trim_trailing_slash(env_pgxc_ctl_home); if (env_pgxc_ctl_home[0] == '\0' || env_pgxc_ctl_home[0] == ' ' || env_pgxc_ctl_home[0] == '\t') { /* Null environment */ snprintf(pgxc_ctl_home, MAXPATH, "%s/%s", env_home, pgxc_ctl_home_def); goto set_bash; } snprintf(pgxc_ctl_home, MAXPATH, "%s/%s", env_home, home); goto set_bash; set_bash: snprintf(pgxc_ctl_bash_path, MAXPATH, "%s/%s", pgxc_ctl_home, PGXC_CTL_BASH); /* * Create home dir if necessary and change current directory to it. */ { struct stat buf; char cmd[MAXLINE+1]; if (stat(pgxc_ctl_home, &buf) ==0) { if (S_ISDIR(buf.st_mode)) { Chdir(pgxc_ctl_home, TRUE); return; } else { fprintf(stderr, "%s is not directory. Check your configurfation\n", pgxc_ctl_home); exit(1); } } snprintf(cmd, MAXLINE, "mkdir -p %s", pgxc_ctl_home); system(cmd); if (stat(pgxc_ctl_home, &buf) ==0) { if (S_ISDIR(buf.st_mode)) { Chdir(pgxc_ctl_home, TRUE); return; } else { fprintf(stderr, "Creating %s directory failed. Check your configuration\n", pgxc_ctl_home); exit(1); } } fprintf(stderr, "Creating directory %s failed. %s\n", pgxc_ctl_home, strerror(errno)); exit(1); } return; } static void build_configuration_path(char *path) { struct stat statbuf; int rr; if (path) reset_var_val(VAR_configFile, path); if (!find_var(VAR_configFile) || !sval(VAR_configFile) || (sval(VAR_configFile)[0] == 0)) { /* Default */ snprintf(pgxc_ctl_config_path, MAXPATH, "%s/%s", pgxc_ctl_home, DEFAULT_CONF_FILE_NAME); rr = stat(pgxc_ctl_config_path, &statbuf); if (rr || !S_ISREG(statbuf.st_mode)) { /* No configuration specified and the default does not apply --> simply ignore */ elog(ERROR, "ERROR: Default configuration file \"%s\" was not found while no configuration file was specified\n", pgxc_ctl_config_path); pgxc_ctl_config_path[0] = 0; /* Read prototype config file if no default config file found */ install_pgxc_ctl_bash(pgxc_ctl_bash_path, true); return; } } else if (sval(VAR_configFile)[0] == '/') { /* Absolute path */ strncpy(pgxc_ctl_config_path, sval(VAR_configFile), MAXPATH); } else { /* Relative path from $pgxc_ctl_home */ snprintf(pgxc_ctl_config_path, MAXPATH, "%s/%s", pgxc_ctl_home, sval(VAR_configFile)); } rr = stat(pgxc_ctl_config_path, &statbuf); if (rr || !S_ISREG(statbuf.st_mode)) { if (rr) elog(ERROR, "ERROR: File \"%s\" not found or not a regular file. %s\n", pgxc_ctl_config_path, strerror(errno)); else elog(ERROR, "ERROR: File \"%s\" not found or not a regular file", pgxc_ctl_config_path); /* Read prototype config file if no config file found */ install_pgxc_ctl_bash(pgxc_ctl_bash_path, true); } else { /* * Since we found a valid config file, don't read the prototype config * file as it may conflict with the user conf file */ install_pgxc_ctl_bash(pgxc_ctl_bash_path, false); } return; } static void read_configuration(void) { FILE *conf; char cmd[MAXPATH+1]; if (pgxc_ctl_config_path[0]) snprintf(cmd, MAXPATH, "%s --home %s --configuration %s", pgxc_ctl_bash_path, pgxc_ctl_home, pgxc_ctl_config_path); else snprintf(cmd, MAXPATH, "%s --home %s", pgxc_ctl_bash_path, pgxc_ctl_home); elog(NOTICE, "Reading configuration using %s\n", cmd); conf = popen(cmd, "r"); if (conf == NULL) { elog(ERROR, "ERROR: Cannot execute %s, %s", cmd, strerror(errno)); return; } read_vars(conf); pclose(conf); uninstall_pgxc_ctl_bash(pgxc_ctl_bash_path); elog(INFO, "Finished reading configuration.\n"); } static void prepare_pgxc_ctl_bash(char *path) { struct stat buf; int rc; rc = stat(path, &buf); if (rc) install_pgxc_ctl_bash(path, false); else if (S_ISREG(buf.st_mode)) return; rc = stat(path, &buf); if (S_ISREG(buf.st_mode)) return; fprintf(stderr, "Error: caould not install bash script %s\n", path); exit(1); } static void pgxcCtlMkdir(char *path) { char cmd[MAXPATH+1]; snprintf(cmd, MAXPATH, "mkdir -p %s", path); system(cmd); } static void startLog(char *path, char *logFileNam) { char logFilePath[MAXPATH+1]; if(path) { trim_trailing_slash(path); pgxcCtlMkdir(path); if(logFileNam) { if (logFileNam[0] == '/') { fprintf(stderr, "ERROR: both --logdir and --logfile are specified and logfile was abosolute path.\n"); exit(1); } if (path[0] == '/') snprintf(logFilePath, MAXPATH, "%s/%s", path, logFileNam); else snprintf(logFilePath, MAXPATH, "%s/%s/%s", pgxc_ctl_home, path, logFileNam); initLog(NULL, logFilePath); } else { if (path[0] == '/') initLog(path, NULL); else { snprintf(logFilePath, MAXPATH, "%s/%s", pgxc_ctl_home, path); initLog(logFilePath, NULL); } } } else { if (logFileNam && logFileNam[0] == '/') { /* This is used as log file path */ initLog(NULL, logFileNam); return; } else { snprintf(logFilePath, MAXPATH, "%s/pgxc_log", pgxc_ctl_home); pgxcCtlMkdir(logFilePath); initLog(logFilePath, NULL); } } return; } static void setDefaultIfNeeded(char *name, char *val) { if (!find_var(name) || !sval(name)) { if (val) reset_var_val(name, val); else reset_var(name); } } static void setup_my_env(void) { char path[MAXPATH+1]; char *home; FILE *ini_env; char *selectVarList[] = { VAR_pgxc_ctl_home, VAR_xc_prompt, VAR_verbose, VAR_logDir, VAR_logFile, VAR_tmpDir, VAR_localTmpDir, VAR_configFile, VAR_echoAll, VAR_debug, VAR_printMessage, VAR_logMessage, VAR_defaultDatabase, VAR_pgxcCtlName, VAR_printLocation, VAR_logLocation, NULL }; ini_env = fopen("/etc/pgxc_ctl", "r"); if (ini_env) { read_selected_vars(ini_env, selectVarList); fclose(ini_env); } if ((home = getenv("HOME"))) { snprintf(path, MAXPATH, "%s/.pgxc_ctl", getenv("HOME")); if ((ini_env = fopen(path, "r"))) { read_selected_vars(ini_env, selectVarList); fclose(ini_env); } } /* * Setup defaults */ snprintf(path, MAXPATH, "%s/pgxc_ctl", getenv("HOME")); setDefaultIfNeeded(VAR_pgxc_ctl_home, path); setDefaultIfNeeded(VAR_xc_prompt, "PGXC "); snprintf(path, MAXPATH, "%s/pgxc_ctl/pgxc_log", getenv("HOME")); setDefaultIfNeeded(VAR_logDir, path); setDefaultIfNeeded(VAR_logFile, NULL); setDefaultIfNeeded(VAR_tmpDir, "/tmp"); setDefaultIfNeeded(VAR_localTmpDir, "/tmp"); setDefaultIfNeeded(VAR_configFile, "pgxc_ctl.conf"); setDefaultIfNeeded(VAR_echoAll, "n"); setDefaultIfNeeded(VAR_debug, "n"); setDefaultIfNeeded(VAR_printMessage, "info"); setDefaultIfNeeded(VAR_logMessage, "info"); setDefaultIfNeeded(VAR_pgxcCtlName, DefaultName); myName = Strdup(sval(VAR_pgxcCtlName)); setDefaultIfNeeded(VAR_defaultDatabase, DefaultDatabase); defaultDatabase = Strdup(sval(VAR_defaultDatabase)); setDefaultIfNeeded(VAR_printLocation, "n"); setDefaultIfNeeded(VAR_logLocation, "n"); } int main(int argc, char *argv[]) { char *configuration = NULL; char *infile = NULL; char *outfile = NULL; char *verbose = NULL; int version_opt = 0; char *logdir = NULL; char *logfile = NULL; char *home = NULL; int help_opt = 0; int c; static struct option long_options[] = { {"configuration", required_argument, 0, 'c'}, {"silent", no_argument, 0, 1}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"logdir", required_argument, 0, 'l'}, {"logfile", required_argument, 0, 'L'}, {"home", required_argument, 0, 2}, {"infile", required_argument, 0, 'i'}, {"outfile", required_argument, 0, 'o'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; int is_bash_exist = system("command -v bash"); if ( is_bash_exist != 0 ) { fprintf(stderr, "Cannot find bash. Please ensure that bash is " "installed and available in the PATH\n"); exit(2); } strcpy(progname, argv[0]); init_var_hash(); while(1) { int option_index = 0; c = getopt_long(argc, argv, "m:i:o:c:vVl:L:h", long_options, &option_index); if (c == -1) break; switch(c) { case 1: verbose = "n"; break; case 2: if (home) free (home); home = strdup(optarg); break; case 'i': if (infile) free(infile); infile = strdup(optarg); break; case 'o': if (outfile) free(outfile); outfile = strdup(optarg); break; case 'v': verbose = "y"; break; case 'V': version_opt = 1; break; case 'l': if (logdir) free(logdir); logdir = strdup(optarg); break; case 'L': if (logfile) free(logfile); logfile = strdup(optarg); break; case 'c': if (configuration) free(configuration); configuration = strdup(optarg); break; case 'h': help_opt = 1; break; case 'm': break; default: fprintf(stderr, "Invalid optin value, received code 0%o\n", c); exit(1); } } if (version_opt || help_opt) { if (version_opt) print_version(); if (help_opt) print_help(); exit(0); } setup_my_env(); /* Read $HOME/.pgxc_ctl */ build_pgxc_ctl_home(home); if (infile) reset_var_val(VAR_configFile, infile); if (logdir) reset_var_val(VAR_logDir, logdir); if (logfile) reset_var_val(VAR_logFile, logfile); startLog(sval(VAR_logDir), sval(VAR_logFile)); prepare_pgxc_ctl_bash(pgxc_ctl_bash_path); build_configuration_path(configuration); read_configuration(); check_configuration(); /* * Setop output */ if (outfile) { elog(INFO, "Output file: %s\n", outfile); if ((outF = fopen(outfile, "w"))) dup2(fileno(outF),2); else elog(ERROR, "ERROR: Cannot open output file %s, %s\n", outfile, strerror(errno)); } else outF = stdout; /* * Startup Message */ elog(NOTICE, " ******** PGXC_CTL START ***************\n\n"); elog(NOTICE, "Current directory: %s\n", pgxc_ctl_home); /* * Setup input */ if (infile) { elog(INFO, "Input file: %s\n", infile); inF = fopen(infile, "r"); if(inF == NULL) { elog(ERROR, "ERROR: Cannot open input file %s, %s\n", infile, strerror(errno)); exit(1); } } else inF = stdin; /* * If we have remaing arguments, they will be treated as a command to do. Do this * first, then handle the input from input file specified by -i option. * If it is not found, then exit. */ #if 0 print_vars(); #endif if (optind < argc) { char orgBuf[MAXLINE + 1]; char wkBuf[MAXLINE + 1]; orgBuf[0] = 0; while (optind < argc) { strncat(orgBuf, argv[optind++], MAXLINE); strncat(orgBuf, " ", MAXLINE); } strncpy(wkBuf, orgBuf, MAXLINE); do_singleLine(orgBuf, wkBuf); if (infile) do_command(inF, outF); } else do_command(inF, outF); exit(0); } static void print_version(void) { printf("Pgxc_ctl %s\n", PG_VERSION); } static void print_help(void) { printf( "pgxc_ctl [option ...] [command]\n" "option:\n" " -c or --configuration conf_file: Specify configruration file.\n" " -v or --verbose: Specify verbose output.\n" " -V or --version: Print version and exit.\n" " -l or --logdir log_directory: specifies what directory to write logs.\n" " -L or --logfile log_file: Specifies log file.\n" " --home home_direcotry: Specifies pgxc_ctl work director.\n" " -i or --infile input_file: Specifies inptut file.\n" " -o or --outfile output_file: Specifies output file.\n" " -h or --help: Prints this message and exits.\n" "For more deatils, refer to pgxc_ctl reference manual included in\n" "postgres-xc reference manual.\n"); }