/* eurephiadm.c -- CLI based admin program for eurephia * * GPLv2 only - Copyright (C) 2008 - 2012 * David Sommerseth * * This program 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 * of the License. * * 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. * */ /** * @file eurephiadm.c * @author David Sommerseth * @date 2008-11-28 * * @brief Main part of the eurephiadm command line administration utility for eurephia * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MODULE "eurephiadm" /**< Need to define the active module before including argparser.h */ #include "argparser.h" #include "client_context.h" #include "client_config.h" #include "client_session.h" #include "get_console_input.h" #include "commands.h" /** * Prints version and copyright info to stdout * * @param fprg Full path to binary * * @return Returns the basename part of the binary file name */ char *print_version(char *fprg) { char *prg = basename(fprg); fprintf(stdout, "%s (v%s) - eurephia administration utility\n" "Copyright (C) 2008-2012 David Sommerseth \n", prg, EUREPHIAVERSION); return prg; } /** * Wrapper function to be used by the command API in eurephiadm * */ void help_Help() { char *argv[] = { MODULE, NULL }; cmd_Help(NULL, NULL, NULL, 1, argv); } /** * Displays a generic help screen, if no arguments where given, or else the * the help screen for the eurephiadm command if it was found. * * @param ctx eurephiaCTX * @param sess eurephiaSESSION of the current logged in user * @param cfg eurephiaVALUES struct of the current configuration * @param argc argument count for the eurephiadm command * @param argv argument table for the eurephiadm command * * @return returns 0 on success, otherwise 1. */ int cmd_Help(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int argc, char **argv) { eurephiadm_functions *func = NULL; int i; char *prg = NULL; // Print the modules help screen if module name is given if( ((argc == 2) && (strcmp(argv[0], "help") == 0)) || ((argc == 3) && (*argv[1] == '-')) ) { char *help = (argc == 2 ? argv[1] : argv[2]); for( i = 0; cmdline_functions[i].command != NULL; i++ ) { func = (eurephiadm_functions *)&cmdline_functions[i]; if( (strcmp(func->command, help) == 0) && (func->helpfunc != NULL) ) { func->helpfunc(); return 0; } } fprintf(stderr, "%s: ERROR :: Could not find help for module '%s'\n", MODULE , help); return 1; } prg = print_version((strcmp(argv[0], "help") == 0) ? MODULE : argv[0]); fprintf(stdout, "\n Usage: %s [global options] [command options]\n\n", prg); fprintf(stdout," %-20.20s %-20.20s %-30.30s\n", "-V | --version", "", "Print version of eurephiadm"); fprintf(stdout," %-20.20s %-20.20s %-30.30s\n", "-l | --log", "", "Log to file"); fprintf(stdout," %-20.20s %-20.20s %-30.30s\n", "-L | --log-level", "", "Set log verbosity"); fprintf(stdout,"\n %-20.20s %-20.20s %-30.30s\n", "Command","Arguments","Description"); fprintf(stdout, " ------------------------------------------------------------------------\n"); for( i = 0; cmdline_functions[i].command != NULL; i++ ) { func = (eurephiadm_functions *)&cmdline_functions[i]; fprintf(stdout," %-20.20s %-20.20s %-30.30s\n", func->command, (func->arghint != NULL ? func->arghint : ""), func->helpinfo); } fprintf(stdout, "\n"); return 0; } /** * eurephiadm logout command. Logs out the current active session. * * @param ctx eurephiaCTX * @param sess eurephiaSESSION of the current logged in user * @param cfg eurephiaVALUES struct of the current configuration * @param argc argument count for the logout command * @param argv argument table for the logout command * * @return returns 0 on success, otherwise 1. */ int cmd_Logout(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int argc, char **argv) { xmlDoc *logoutxml = NULL, *resxml = NULL; xmlNode *logout_n = NULL; eurephiaRESULT *res = NULL; int rc = 0; eurephiaXML_CreateDoc(ctx, 1, "Register", &logoutxml, &logout_n); assert( (logoutxml != NULL) && (logout_n != NULL) ); xmlNewProp(logout_n, (xmlChar *) "mode", (xmlChar *) "logout"); xmlAddChild(logout_n, xmlNewText((xmlChar *) argv[0])); // A dirty hack, but the sesskey is here resxml = eDBadminAuthenticate(ctx, logoutxml); xmlFreeDoc(logoutxml); if( !eurephiaXML_IsResultMsg(ctx, resxml) ) { eurephia_log(ctx, LOG_FATAL, 0, "Failed to logout session"); rc = 1; } else { res = eurephiaXML_ParseResultMsg(ctx, resxml); if( res->resultType == exmlERROR ) { fprintf(stderr, "%s: %s\n", MODULE, res->message); rc = 1; } else { fprintf(stdout, "%s: %s\n", MODULE, res->message); rc = 0; } } xmlFreeDoc(resxml); free_nullsafe(ctx, res); return rc; } /** * eurephiadm show-config command. Dumps configuration to stdout. If the user is logged in, the * configuration saved in the database is also dumped. * * @param ctx eurephiaCTX * @param sess eurephiaSESSION of the current logged in user * @param cfg eurephiaVALUES struct of the current configuration * @param argc argument count for the show-config command * @param argv argument table for the show-config command * * @return returns 0 on success, otherwise 1. */ int cmd_ShowCfg(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int argc, char **argv) { void dump_values(eurephiaVALUES *vls) { eurephiaVALUES *ptr = NULL; fprintf(stdout, "----------------------------------------------------------------------\n"); for( ptr = vls; ptr != NULL; ptr = ptr->next) { if( ptr->key != NULL ) { fprintf(stdout, "%20s = %s\n", ptr->key, ptr->val); } } fprintf(stdout, "----------------------------------------------------------------------\n"); } if( cfg != NULL ) { fprintf(stdout, "\n** eurephiadm configuration settings (config file) **\n"); dump_values(cfg); fprintf(stdout, "\n"); } if( ctx != NULL ) { fprintf(stdout, "** eurephia configuration settings (set in database) **\n"); dump_values(ctx->dbc->config); fprintf(stdout, "\n"); } return 0; } /** * Establish a connection to the database and register the database connection in the eurephiaCTX * * @param ctx eurephiaCTX * @param argstr argument string for the database connection * * @return Returns 1 on success, otherwise 0. */ int eurephia_ConnectDB(eurephiaCTX *ctx, const char *argstr) { char *delims = " "; char *cp = NULL; const char *dbargv[MAX_ARGUMENTS]; int dbargc = 0; if( (argstr == NULL) || (strlen(argstr) < 1) ) { eurephia_log(ctx, LOG_FATAL, 0, "No database connection string given"); return 0; } // Split up argstr into separate arguments cp = strdup(argstr); assert(cp != NULL); dbargc = 0; dbargv[dbargc] = strtok(cp, delims); while( (dbargc <= MAX_ARGUMENTS) && (dbargv[dbargc] != NULL) ) { dbargv[++dbargc] = strtok(NULL, delims); } if( !eDBconnect(ctx, dbargc, dbargv) ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not connect to the database"); eDBlink_close(ctx); return 0; } free_nullsafe(ctx, cp); return 1; } /** * Log in a user. The function will ask for user name and password and authenticate the user against * the eurephia database and make sure the user is granted access to the requested access level. * * @param ctx eurephiaCTX * @param cfg eurephiaVALUES with configuration settings * @param req_access String (const char *) containing the requested access level. * * @return Returns a eurephiaSESSION pointer to the authenticated user session on success, otherwise NULL. */ static eurephiaSESSION *do_login(eurephiaCTX *ctx, eurephiaVALUES *cfg, const char *req_access) { xmlDoc *loginxml = NULL, *resxml = NULL; xmlNode *login_n = NULL; eurephiaRESULT *res = NULL; eurephiaSESSION *session = NULL; char username[33], password[33], *tmp = NULL; memset(&username, 0, 33); memset(&password, 0, 33); if( (tmp = eGet_value(cfg, "username")) == NULL ) { memset(username, 0, 33); get_console_input(username, 32, "User:", 0); } else { strncpy(username, tmp, 32); } get_console_input(password, 32, "Password:", 1); eurephiaXML_CreateDoc(ctx, 1, "Authenticate", &loginxml, &login_n); assert( (loginxml != NULL) && (login_n != NULL) ); xmlNewProp(login_n, (xmlChar *) "mode", (xmlChar *) "user"); xmlNewChild(login_n, NULL, (xmlChar *) "username", (xmlChar *) username); xmlNewChild(login_n, NULL, (xmlChar *) "password", (xmlChar *) password); xmlNewChild(login_n, NULL, (xmlChar *) "accesslevel", (xmlChar *) req_access); resxml = eDBadminAuthenticate(ctx, loginxml); xmlFreeDoc(loginxml); memset(username, 0, 33); memset(password, 0, 33); if( !eurephiaXML_IsResultMsg(ctx, resxml) ) { return NULL; } res = eurephiaXML_ParseResultMsg(ctx, resxml); if( res->resultType == exmlERROR ) { fprintf(stderr, "%s: %s\n", MODULE, res->message); free_nullsafe(ctx, res); xmlFreeDoc(resxml); return NULL; } session = create_session(ctx, NULL); login_n = xmlFindNode(res->details, "UserAccount"); // Borrow username for converting uid to a string snprintf(username, 30, "%s", xmlGetAttrValue(login_n->properties, "uid")); xmlFreeDoc(resxml); free_nullsafe(ctx, res); if( !eDBset_session_value(ctx, session, "uid", username) ) { eurephia_log(ctx, LOG_FATAL, 0, "Could not update session variables (uid)"); eDBfree_session(ctx, session); return NULL; }; // Register the session as logged in eurephiaXML_CreateDoc(ctx, 1, "Register", &loginxml, &login_n); assert( (loginxml != NULL) && (login_n != NULL) ); xmlNewProp(login_n, (xmlChar *) "mode", (xmlChar *) "login"); xmlNewProp(login_n, (xmlChar *) "uid", (xmlChar *) username); xmlAddChild(login_n, xmlNewText((xmlChar *) session->sessionkey)); resxml = eDBadminAuthenticate(ctx, loginxml); xmlFreeDoc(loginxml); if( !eurephiaXML_IsResultMsg(ctx, resxml) ) { eurephia_log(ctx, LOG_FATAL, 0, "Could not register login"); eDBfree_session(ctx, session); xmlFreeDoc(resxml); return NULL; } res = eurephiaXML_ParseResultMsg(ctx, resxml); if( res->resultType == exmlERROR ) { fprintf(stderr, "%s: %s\n", MODULE, res->message); free_nullsafe(ctx, res); xmlFreeDoc(resxml); eDBfree_session(ctx, session); return NULL; } xmlFreeDoc(resxml); free_nullsafe(ctx, res); return session; } /** * Validates an exisiting session key against a requested access level * * @param ctx eurephiaCTX * @param sesskey String containing the session key * @param accesslvl String containing the requested access level * * @return Returns 1 if session is valid and access level is granted to the session, otherwise 0 */ static int validate_session(eurephiaCTX *ctx, const char *sesskey, const char *accesslvl) { xmlDoc *sessxml = NULL, *resxml = NULL; xmlNode *sess_n = NULL; int ret = 0; eurephiaXML_CreateDoc(ctx, 1, "Authenticate", &sessxml, &sess_n); assert( (sessxml != NULL) && (sess_n != NULL) ); xmlNewProp(sess_n, (xmlChar *) "mode", (xmlChar *) "session"); xmlNewChild(sess_n, NULL, (xmlChar *) "sessionkey", (xmlChar *) sesskey); xmlNewChild(sess_n, NULL, (xmlChar *) "accesslevel", (xmlChar *) accesslvl); resxml = eDBadminAuthenticate(ctx, sessxml); xmlFreeDoc(sessxml); if( !eurephiaXML_IsResultMsg(ctx, resxml) ) { ret = 0; } else { eurephiaRESULT *res = NULL; res = eurephiaXML_ParseResultMsg(ctx, resxml); if( res->resultType == exmlERROR ) { fprintf(stderr, "%s: %s\n", MODULE, res->message); ret = 0; } else { ret = 1; } free_nullsafe(ctx, res); } if( resxml ) { xmlFreeDoc(resxml); } return ret; } /** * Converts a argument table (char **) into a space separated string (char *) * * @param argc argument counter * @param argv argument array * * @return String (char *) of all arguments, separated by space. */ char *args2string(int argc, char **argv) { char *res = NULL; int len = 0, i = 0; for( i = 0; i < argc; i++ ) { len += strlen_nullsafe(argv[i]) + 1; } res = (char *) malloc_nullsafe(NULL, len+2); assert( res != NULL ); for( i = 0; i < argc; i++ ) { strcat(res, argv[i]); strcat(res, " "); } return res; } /** * eurephiadm main function * * @param argc argument counter * @param argv argument table * * @return returns 0 on success, otherwise a value > 0 */ int main(int argc, char **argv) { char *logfile = NULL; int loglevel = 0; eurephiaCTX *ctx = NULL; eurephiaSESSION *session = NULL; eurephiaVALUES *cfg = NULL; char *sesskey_file = NULL; char *dbparams = NULL, *cmdargv[MAX_ARGUMENTS]; int rc = 0, i = 0, found = 0, cmdargc = 0, argi = 0; eurephiadm_functions *call_fnc; // Global arguments we accept static e_options argopts[] = { {"--version", "-V", 0}, {"--help", "-h", 0}, {"--log", "-l", 1}, {"--log-level", "-L", 1}, {NULL, NULL, 0} }; if( argc < 2 ) { cmd_Help(NULL, NULL, NULL, argc, argv); return 1; } // Parse argument line argi = 1; for( argi = 1; argi < argc; argi++ ) { if( *argv[argi] != '-' ) { break; } switch( eurephia_getopt(&argi, argc, argv, argopts) ) { case 'V': print_version(argv[0]); return 0; case 'h': cmd_Help(NULL, NULL, NULL, argc, argv); return 0; case 'l': logfile = optargs[0]; break; case 'L': loglevel = atoi_nullsafe(optargs[0]); break; default: return 1; break; } } if( argi >= argc ) { fprintf(stderr, "%s: ERROR :: No command given\n", basename(argv[0])); return 1; } else { for( i = argi; ((i < argc) && (i < MAX_ARGUMENTS)); i++ ) { cmdargv[cmdargc++] = argv[i]; } } // Find the command requested and save a pointer to that commands C function for( i = 0; cmdline_functions[i].command != NULL; i++ ) { call_fnc = (eurephiadm_functions *)&cmdline_functions[i]; if( strcmp(call_fnc->command, cmdargv[0]) == 0 ) { found = 1; break; } } // Exit with error if command is not found if( found == 0 ) { fprintf(stderr, "%s: No such command '%s'\n", basename(argv[0]), argv[argi]); return 5; } // Read configuration file cfg = ReadConfig("EUREPHIADM_CONFIG", "eurephiadm.cfg"); if( cfg == NULL ) { fprintf(stderr, "No configuration file found.\n"); return 2; } // If function do not need a logged in session, go a head process it now and exit if( call_fnc->need_session == 0 ) { rc = call_fnc->function(NULL, NULL, cfg, cmdargc, cmdargv); eFree_values(NULL, cfg); return rc; } // // Load database driver, setup a context and create or reuse an open session // and then call the commando's function // // Create a eurephia context and load database driver ctx = eurephiaCTX_init("eurephiadm", logfile, loglevel, cfg); if( ctx == NULL ) { fprintf(stderr, "Could not initialise a eurephia context.\n"); return 3; } // Connect to the database dbparams = eGet_value(cfg, "database_params"); if( !eurephia_ConnectDB(ctx, dbparams) ) { return 4; } // Check if we have XSLT path available, if not setup a default path if( eGet_value(ctx->dbc->config, "eurephiadm_xslt_path") ) { eAdd_value(ctx, cfg, "eurephiadm_xslt_path", eGet_value(ctx->dbc->config, "eurephiadm_xslt_path")); } else { eAdd_value(ctx, cfg, "eurephiadm_xslt_path", EUREPHIADM_XSLT_PATH); } // Load session file, if it exists. (reuse open session) if( (sesskey_file = read_session_file(ctx)) == NULL ) { // No session file - go straight to login if( strcmp(call_fnc->command, "logout") == 0) { fprintf(stderr, "You are already logged out\n"); rc = 0; goto exit; } session = do_login(ctx, cfg, call_fnc->accesslvl); } else { // If we are logging out, do so here - without checking the session if( strcmp(call_fnc->command, "logout") == 0) { char *logout_argv[] = { sesskey_file }; rc = cmd_Logout(ctx, NULL, cfg, 0, logout_argv); remove_session_file(ctx); free_nullsafe(ctx, sesskey_file); goto exit; } // Session file found, check if it still is a valid session if( validate_session(ctx, sesskey_file, call_fnc->accesslvl) ) {; // If valid, load this session session = create_session(ctx, sesskey_file); } else { // If not valid, remove session file and go to login remove_session_file(ctx); session = do_login(ctx, cfg, call_fnc->accesslvl); } } free_nullsafe(ctx, sesskey_file); // If we have an open session now, call the requested command if(session != NULL ) { char *argstr = NULL; // Write session info to a file if( !write_session_file(ctx, session) ) { eurephia_log(ctx, LOG_ERROR, 0, "Could not write session file. Login needed for actions"); }; // Execute the requested command rc = call_fnc->function(ctx, session, cfg, cmdargc, cmdargv); argstr = args2string(cmdargc, cmdargv); eurephia_log(ctx, LOG_INFO, 0, "Command: %s (result: %i)", argstr, rc); free_nullsafe(ctx, argstr); // Remove session info from memory eDBfree_session(ctx, session); } else { // If not an open session, make sure the session file is gone remove_session_file(ctx); } exit: // Disconnect from the database, and remove config and our context before exiting eFree_values(ctx, cfg); eDBdisconnect(ctx); eurephiaCTX_destroy(ctx); xmlCleanupParser(); return rc; }