/*****************************************************************************\
 *  sacctmgr.c - administration tool for slurm's accounting.
 *	         provides interface to read, write, update, and configure
 *               accounting.
 *****************************************************************************
 *  Copyright (C) 2008-2009 Lawrence Livermore National Security.
 *  Copyright (C) 2002-2007 The Regents of the University of California.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Danny Auble <da@llnl.gov>
 *  CODE-OCEC-09-009. All rights reserved.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm 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; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#include "config.h"

#include "src/common/data.h"
#include "src/common/proc_args.h"
#include "src/common/ref.h"
#include "src/common/sercli.h"
#include "src/common/strlcpy.h"
#include "src/common/xsignal.h"

#include "src/interfaces/data_parser.h"
#include "src/interfaces/serializer.h"

#include "src/sacctmgr/sacctmgr.h"

#define OPT_LONG_AUTOCOMP 0x100
#define OPT_LONG_JSON 0x101
#define OPT_LONG_YAML 0x102

char *command_name;
int exit_code;		/* sacctmgr's exit code, =1 on any error at any time */
int exit_flag;		/* program to terminate if =1 */
int one_liner;		/* one record per line if =1 */
int quiet_flag;		/* quiet=1, verbose=-1, normal=0 */
int readonly_flag;      /* make it so you can only run list commands */
int verbosity;		/* count of -v options */
int rollback_flag;       /* immediate execute=1, else = 0 */
int with_assoc_flag = 0;
void *db_conn = NULL;
uint32_t my_uid = 0;
char *my_user_name = NULL;
list_t *g_qos_list = NULL;
list_t *g_res_list = NULL;
list_t *g_tres_list = NULL;
const char *mime_type = NULL; /* mimetype if we are using data_parser */
const char *data_parser = NULL; /* data_parser args */

/* by default, normalize all usernames and resource names to lower case */
bool user_case_norm = true;
bool resource_case_norm = true;
bool tree_display = 0;
bool have_db_conn = false;

static void	_add_it(int argc, char **argv);
static void	_archive_it(int argc, char **argv);
static void	_clear_it(int argc, char **argv);
static void	_show_it(int argc, char **argv);
static void	_modify_it(int argc, char **argv);
static void	_delete_it(int argc, char **argv);
static int	_get_command(int *argc, char **argv);
static int	_ping(int argc, char **argv);
static void     _print_version(void);
static int	_process_command(int argc, char **argv);
static void	_usage(void);

decl_static_data(usage_txt);

int main(int argc, char **argv)
{
	int error_code = SLURM_SUCCESS, opt_char;
	log_options_t opts = LOG_OPTS_STDERR_ONLY ;
	int local_exit_code = 0;
	int option_index;
	uint16_t persist_conn_flags = 0;

	static struct option long_options[] = {
		{"autocomplete", required_argument, 0, OPT_LONG_AUTOCOMP},
		{"help",     0, 0, 'h'},
		{"usage",    0, 0, 'h'},
		{"immediate",0, 0, 'i'},
		{"noheader",0, 0, 'n'},
		{"oneliner", 0, 0, 'o'},
		{"parsable", 0, 0, 'p'},
		{"parsable2", 0, 0, 'P'},
		{"quiet",    0, 0, 'Q'},
		{"readonly", 0, 0, 'r'},
		{"associations", 0, 0, 's'},
		{"verbose",  0, 0, 'v'},
		{"version",  0, 0, 'V'},
		{"json", optional_argument, 0, OPT_LONG_JSON},
		{"yaml", optional_argument, 0, OPT_LONG_YAML},
		{NULL,       0, 0, 0}
	};

	command_name      = argv[0];
	rollback_flag     = 1;
	exit_code         = 0;
	exit_flag         = 0;
	quiet_flag        = 0;
	readonly_flag     = 0;
	verbosity         = 0;
	slurm_init(NULL);
	log_init("sacctmgr", opts, SYSLOG_FACILITY_DAEMON, NULL);

	while((opt_char = getopt_long(argc, argv, "hionpPQrsvV",
			long_options, &option_index)) != -1) {
		switch (opt_char) {
		case (int)'?':
			fprintf(stderr, "Try \"sacctmgr --help\" "
				"for more information\n");
			exit(1);
			break;
		case (int)'h':
			_usage ();
			exit(exit_code);
			break;
		case (int)'i':
			rollback_flag = 0;
			break;
		case (int)'o':
			one_liner = 1;
			break;
		case (int)'n':
			print_fields_have_header = 0;
			break;
		case (int)'p':
			print_fields_parsable_print =
			PRINT_FIELDS_PARSABLE_ENDING;
			break;
		case (int)'P':
			print_fields_parsable_print =
			PRINT_FIELDS_PARSABLE_NO_ENDING;
			break;
		case (int)'Q':
			if (quiet_flag == -1) {
				fprintf(stderr,
					"conflicting 'quiet' and 'verbose' options\n");
				exit(1);
			}
			quiet_flag = 1;
			break;
		case (int)'r':
			readonly_flag = 1;
			break;
		case (int)'s':
			with_assoc_flag = 1;
			break;
		case (int)'v':
			if (quiet_flag == 1) {
				fprintf(stderr,
					"conflicting 'quiet' and 'verbose' options\n");
				exit(1);
			}
			quiet_flag = -1;
			verbosity++;
			break;
		case (int)'V':
			_print_version();
			exit(exit_code);
			break;
		case OPT_LONG_AUTOCOMP:
			suggest_completion(long_options, optarg);
			exit(0);
			break;
		case OPT_LONG_JSON :
			mime_type = MIME_TYPE_JSON;
			data_parser = optarg;
			serializer_required(MIME_TYPE_JSON);
			break;
		case OPT_LONG_YAML :
			mime_type = MIME_TYPE_YAML;
			data_parser = optarg;
			serializer_required(MIME_TYPE_YAML);
			break;
		default:
			exit_code = 1;
			fprintf(stderr, "getopt error, returned %c\n",
				opt_char);
			exit(exit_code);
		}
	}

	if (verbosity) {
		opts.stderr_level += verbosity;
		opts.prefix_level = 1;
		log_alter(opts, 0, NULL);
	}

	if (quiet_flag == 1) {
		opts.stderr_level = LOG_LEVEL_ERROR;
		log_alter(opts, 0, NULL);
	}

	/* Check to see if we are running a supported accounting plugin */
	if (!slurm_with_slurmdbd()) {
		fprintf(stderr,
		        "You are not running a supported accounting_storage plugin\n"
		        "Only 'accounting_storage/slurmdbd' is supported.\n");
		exit(1);
	}

	errno = 0;
	db_conn = slurmdb_connection_get(&persist_conn_flags);

	if (!errno)
		have_db_conn = true;

	my_uid = getuid();
	my_user_name = uid_to_string_cached(my_uid);

	if (persist_conn_flags & PERSIST_FLAG_P_USER_CASE)
		user_case_norm = false;
	if (persist_conn_flags & PERSIST_FLAG_P_RESOURCE_CASE)
		resource_case_norm = false;

	/* We are only running a single command and exiting */
	if (optind < argc) {
		error_code = _process_command(argc - optind, argv + optind);
	} else if ((argc == 2) && (optind == argc) && mime_type &&
		   !xstrcmp(data_parser, "list")) {
		/*
		 * We are only listing the available data parser plugins.
		 * Calling DATA_DUMP_CLI_SINGLE() with a dummy type to get to
		 * "list".
		 * TODO: After Bug 18109 is fixed, replace this logic:
		 */
		DATA_DUMP_CLI_SINGLE(OPENAPI_PING_ARRAY_RESP, NULL, argc, argv,
				     NULL, mime_type, data_parser, exit_code);
	} else {
		/* We are running interactively multiple commands */
		int input_field_count = 0;
		char **input_fields = xcalloc(MAX_INPUT_FIELDS, sizeof(char *));
		while (error_code == SLURM_SUCCESS) {
			error_code = _get_command(
				&input_field_count, input_fields);
			if (error_code || exit_flag)
				break;

			error_code = _process_command(
				input_field_count, input_fields);
			/* This is here so if someone made a mistake we allow
			 * them to fix it and let the process happen since there
			 * are checks for global exit_code we need to reset it.
			 */
			if (exit_code) {
				local_exit_code = exit_code;
				exit_code = 0;
			}
			if (exit_flag)
				break;
		}
		xfree(input_fields);
	}

	/* readline library writes \n when echoes the input string, it does
	 * not when it sees the EOF, so in that case we have to print it to
	 * align the terminal prompt.
	 */
	if (exit_flag == 2)
		putchar('\n');
	if (local_exit_code)
		exit_code = local_exit_code;
	slurmdb_connection_close(&db_conn);

#ifdef MEMORY_LEAK_DEBUG
	log_fini();
	slurm_fini();
	uid_cache_clear();
	FREE_NULL_LIST(g_qos_list);
	FREE_NULL_LIST(g_res_list);
	FREE_NULL_LIST(g_tres_list);
#endif

	exit(exit_code);
}

#if !HAVE_READLINE
/*
 * Alternative to readline if readline is not available
 */
static char *_getline(const char *prompt)
{
	char buf[4096];
	char *line;
	int len;

	printf("%s", prompt);

	/* Set "line" here to avoid a warning, discard later */
	line = fgets(buf, 4096, stdin);
	if (line == NULL)
		return NULL;
	len = strlen(buf);
	if ((len == 0) || (len >= 4096))
		return NULL;
	if (buf[len-1] == '\n')
		buf[len-1] = '\0';
	else
		len++;
	line = malloc(len);
	if (!line)
		return NULL;
	strlcpy(line, buf, len);
	return line;
}
#endif

/*
 * _get_command - get a command from the user
 * OUT argc - location to store count of arguments
 * OUT argv - location to store the argument list
 */
static int _get_command (int *argc, char **argv)
{
	char *in_line;
	static char *last_in_line = NULL;
	int i, in_line_size;
	static int last_in_line_size = 0;

	*argc = 0;

#if HAVE_READLINE
	in_line = readline ("sacctmgr: ");
#else
	in_line = _getline("sacctmgr: ");
#endif
	if (in_line == NULL) {
		exit_flag = 2;
		return 0;
	} else if (xstrncmp (in_line, "#", 1) == 0) {
		free (in_line);
		return 0;
	} else if (xstrcmp (in_line, "!!") == 0) {
		free (in_line);
		in_line = last_in_line;
		in_line_size = last_in_line_size;
	} else {
		if (last_in_line)
			free (last_in_line);
		last_in_line = in_line;
		last_in_line_size = in_line_size = strlen (in_line);
	}

#if HAVE_READLINE
	add_history(in_line);
#endif

	/* break in_line into tokens */
	for (i = 0; i < in_line_size; i++) {
		bool double_quote = false, single_quote = false;
		if (in_line[i] == '\0')
			break;
		if (isspace ((int) in_line[i]))
			continue;
		if (((*argc) + 1) > MAX_INPUT_FIELDS) {	/* bogus input line */
			exit_code = 1;
			fprintf (stderr,
				 "%s: can not process over %d words\n",
				 command_name, MAX_INPUT_FIELDS-1);
			return E2BIG;
		}
		argv[(*argc)++] = &in_line[i];
		for (i++; i < in_line_size; i++) {
			if (in_line[i] == '\042') {
				double_quote = !double_quote;
				continue;
			}
			if (in_line[i] == '\047') {
				single_quote = !single_quote;
				continue;
			}
			if (in_line[i] == '\0')
				break;
			if (double_quote || single_quote)
				continue;
			if (isspace ((int) in_line[i])) {
				in_line[i] = '\0';
				break;
			}
		}
	}
	return 0;
}

static void _set_ping_exit_code(slurmdbd_ping_t *ping)
{
	static bool slurmdbd_up = false;

	if (ping->pinged) {
		exit_code = SLURM_SUCCESS;
		slurmdbd_up = true;
	} else if (!slurmdbd_up) {
		/* don't overwrite exit code if another slurmdbd is up */
		exit_code = 1;
	}
}

static void _print_db_ping(slurmdbd_ping_t *ping)
{
	char *state;
	char *mode;

	if (ping->pinged)
		state = "UP";
	else
		state = "DOWN";

	if (ping->offset == 0)
		mode = "primary";
	else
		mode = "backup";

	printf("slurmdbd(%s) at %s is %s\n", mode, ping->hostname, state);
}

static int _ping(int argc, char **argv)
{
	int rc = SLURM_SUCCESS;
	slurmdbd_ping_t *pings = NULL;

	if (!(pings = slurmdb_ping_all())) {
		error("Failed to perform slurmdbd pings");
		return SLURM_ERROR;
	}

	if (mime_type) {
		DATA_DUMP_CLI_SINGLE(OPENAPI_SLURMDBD_PING_RESP, pings, argc,
				     argv, db_conn, mime_type, data_parser, rc);
	} else  {
		for (int i = 0; pings[i].hostname; i++) {
			_print_db_ping(&pings[i]);
			_set_ping_exit_code(&pings[i]);
		}
	}

	xfree(pings);
	return rc;
}

static void _print_version(void)
{
	print_slurm_version();
	if (quiet_flag == -1) {
		long version = slurm_api_version();
		printf("slurm_api_version: %ld, %ld.%ld.%ld\n", version,
			SLURM_VERSION_MAJOR(version),
			SLURM_VERSION_MINOR(version),
			SLURM_VERSION_MICRO(version));
	}
}

/*
 * _process_command - process the user's command
 * IN argc - count of arguments
 * IN argv - the arguments
 * RET 0 or errno (only for errors fatal to sacctmgr)
 */
static int _process_command (int argc, char **argv)
{
	int command_len = 0, rc;

	if (argc < 1) {
		exit_code = 1;
		if (quiet_flag == -1)
			fprintf(stderr, "no input");
		return 0;
	}

	command_len = strlen(argv[0]);

	if (xstrncasecmp (argv[0], "associations",
			 MAX(command_len, 3)) == 0) {
		with_assoc_flag = 1;
	} else if (xstrncasecmp(argv[0], "dump", MAX(command_len, 3)) == 0) {
		sacctmgr_dump_cluster((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "help", MAX(command_len, 2)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for keyword:%s\n",
				 argv[0]);
		}
		_usage ();
	} else if (xstrncasecmp(argv[0], "load", MAX(command_len, 2)) == 0) {
		load_sacctmgr_cfg_file((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "oneliner",
				MAX(command_len, 1)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for keyword:%s\n",
				 argv[0]);
		}
		one_liner = 1;
	} else if (xstrncasecmp(argv[0], "quiet", MAX(command_len, 4)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr, "too many arguments for keyword:%s\n",
				 argv[0]);
		}
		log_options_t opts = LOG_OPTS_STDERR_ONLY;
		opts.stderr_level = LOG_LEVEL_ERROR;
		log_alter(opts, 0, NULL);
		quiet_flag = 1;
	} else if ((xstrncasecmp(argv[0], "exit", MAX(command_len, 4)) == 0) ||
		   (xstrncasecmp(argv[0], "\\q", MAX(command_len, 2)) == 0) ||
		   (xstrncasecmp(argv[0], "quit", MAX(command_len, 4)) == 0)) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for keyword:%s\n",
				 argv[0]);
		}
		exit_flag = 1;
	} else if ((xstrncasecmp(argv[0], "add", MAX(command_len, 3)) == 0) ||
		   (xstrncasecmp(argv[0], "create",
				 MAX(command_len, 3)) == 0)) {
		_add_it((argc - 1), &argv[1]);
	} else if ((xstrncasecmp(argv[0], "archive",
				 MAX(command_len, 3)) == 0)) {
		_archive_it((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "clear", MAX(command_len, 3)) == 0) {
		_clear_it((argc - 1), &argv[1]);
	} else if ((xstrncasecmp(argv[0], "show", MAX(command_len, 3)) == 0) ||
		   (xstrncasecmp(argv[0], "list", MAX(command_len, 3)) == 0)) {
		_show_it((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "modify", MAX(command_len, 1))
		   || !xstrncasecmp(argv[0], "update", MAX(command_len, 1))) {
		_modify_it((argc - 1), &argv[1]);
	} else if ((xstrncasecmp(argv[0], "delete",
				 MAX(command_len, 3)) == 0) ||
		   (xstrncasecmp(argv[0], "remove",
				 MAX(command_len, 3)) == 0)) {
		_delete_it((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "verbose", MAX(command_len, 4)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for %s keyword\n",
				 argv[0]);
		}
		log_options_t opts = LOG_OPTS_STDERR_ONLY;
		opts.stderr_level += verbosity;
		log_alter(opts, 0, NULL);
		quiet_flag = -1;
	} else if (xstrncasecmp(argv[0], "ping", MAX(command_len, 4)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf(stderr, "too many arguments for %s keyword\n",
				argv[0]);
		}

		if (_ping(argc, argv))
			fprintf(stderr, "unable to run ping\n");
	} else if (xstrncasecmp(argv[0], "readonly",
				MAX(command_len, 4)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for %s keyword\n",
				 argv[0]);
		}
		readonly_flag = 1;
	} else if (xstrncasecmp(argv[0], "reconfigure",
				MAX(command_len, 4)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for %s keyword\n",
				 argv[0]);
		}

		slurmdb_reconfig(db_conn);
	} else if (xstrncasecmp(argv[0], "rollup", MAX(command_len, 2)) == 0) {
		time_t my_start = 0;
		time_t my_end = 0;
		uint16_t archive_data = 0;
		if (argc > 4) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for %s keyword\n",
				 argv[0]);
		}

		if (argc > 1)
			my_start = parse_time(argv[1], 1);
		if (argc > 2)
			my_end = parse_time(argv[2], 1);
		if (argc > 3)
			archive_data = atoi(argv[3]);
		if (slurmdb_usage_roll(db_conn, my_start, my_end, archive_data,
				       NULL) == SLURM_SUCCESS) {
			if (commit_check("Would you like to commit rollup?")) {
				exit_code =
					slurmdb_connection_commit(db_conn, 1);
				if (exit_code != SLURM_SUCCESS)
					fprintf(stderr, " Error committing changes: %s\n",
						slurm_strerror(exit_code));
			} else {
				printf(" Rollup Discarded\n");
				exit_code =
					slurmdb_connection_commit(db_conn, 0);
				if (exit_code != SLURM_SUCCESS)
					fprintf(stderr, " Error rolling back changes: %s\n",
						slurm_strerror(exit_code));
			}
		}
	} else if (xstrncasecmp(argv[0], "shutdown", MAX(command_len, 4)) ==
		   0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for %s keyword\n",
				 argv[0]);
		}

		rc = slurmdb_shutdown(db_conn);
		if (rc != SLURM_SUCCESS) {
			fprintf(stderr, " Problem shutting down server: %s\n",
				slurm_strerror(rc));
			exit_code = 1;
		}
	} else if (xstrncasecmp(argv[0], "version", MAX(command_len, 4)) == 0) {
		if (argc > 1) {
			exit_code = 1;
			fprintf (stderr,
				 "too many arguments for %s keyword\n",
				 argv[0]);
		}
		_print_version();
	} else {
		exit_code = 1;
		fprintf (stderr, "invalid keyword: %s\n", argv[0]);
	}

	return 0;
}

/*
 * _add_it - add the entity per the supplied arguments
 * IN argc - count of arguments
 * IN argv - list of arguments
 */
static void _add_it(int argc, char **argv)
{
	int error_code = SLURM_SUCCESS;
	int command_len = 0;

	if (!have_db_conn) {
		exit_code = 1;
		return;
	}

	if (readonly_flag) {
		exit_code = 1;
		fprintf(stderr, "Can't run this command in readonly mode.\n");
		return;
	}

	if (!argv[0])
		goto helpme;

	command_len = strlen(argv[0]);
	/* reset the connection to get the most recent stuff */
	slurmdb_connection_commit(db_conn, 0);

	/* First identify the entity to add */
	if (!xstrncasecmp(argv[0], "Account", MAX(command_len, 1))
	    || !xstrncasecmp(argv[0], "Acct", MAX(command_len, 4))) {
		error_code = sacctmgr_add_account((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Cluster", MAX(command_len, 2))) {
		error_code = sacctmgr_add_cluster((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Coordinator", MAX(command_len, 2))) {
		error_code = sacctmgr_add_coord((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Federation", MAX(command_len, 1))) {
		error_code = sacctmgr_add_federation((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "QOS", MAX(command_len, 1))) {
		error_code = sacctmgr_add_qos((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Resource", MAX(command_len, 1))) {
		error_code = sacctmgr_add_res((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "User", MAX(command_len, 1))) {
		error_code = sacctmgr_add_user((argc - 1), &argv[1]);
	} else {
	helpme:
		exit_code = 1;
		fprintf(stderr, "No valid entity in add command\n");
		fprintf(stderr, "Input line must include ");
		fprintf(stderr, "\"Account\", \"Cluster\", \"Coordinator\", ");
		fprintf(stderr, "\"Federation\", \"QOS\", \"Resource\", ");
		fprintf(stderr, "or \"User\"\n");
	}

	if (error_code != SLURM_SUCCESS) {
		exit_code = 1;
	}
}

/*
 * _archive_it - archive the entity per the supplied arguments
 * IN argc - count of arguments
 * IN argv - list of arguments
 */
static void _archive_it(int argc, char **argv)
{
	int error_code = SLURM_SUCCESS;
	int command_len = 0;

	if (!have_db_conn) {
		exit_code = 1;
		return;
	}

	if (readonly_flag) {
		exit_code = 1;
		fprintf(stderr, "Can't run this command in readonly mode.\n");
		return;
	}

	if (!argv[0])
		goto helpme;

	command_len = strlen(argv[0]);
	/* reset the connection to get the most recent stuff */
	slurmdb_connection_commit(db_conn, 0);

	/* First identify the entity to add */
	if (xstrncasecmp(argv[0], "dump", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_archive_dump((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "load", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_archive_load((argc - 1), &argv[1]);
	} else {
	helpme:
		exit_code = 1;
		fprintf(stderr, "No valid entity in archive command\n");
		fprintf(stderr, "Input line must include, ");
		fprintf(stderr, "\"Dump\", or \"load\"\n");
	}

	if (error_code != SLURM_SUCCESS) {
		exit_code = 1;
	}
}

/*
 * _clear_it - Clear the slurm configuration per the supplied arguments
 * IN argc - count of arguments
 * IN argv - list of arguments
 */
static void _clear_it(int argc, char **argv)
{
	int error_code = SLURM_SUCCESS;
	int command_len = 0;

	if (!have_db_conn) {
		exit_code = 1;
		return;
	}

	if (!argv[0])
		goto helpme;

	command_len = strlen(argv[0]);

	/* First identify the entity to list */
	if (!xstrncasecmp(argv[0], "Stats", MAX(command_len, 1))) {
		error_code = slurmdb_clear_stats(db_conn);
	} else {
	helpme:
		exit_code = 1;
		fprintf(stderr, "No valid entity in list command\n");
		fprintf(stderr, "Input line must include ");
		fprintf(stderr, "\"Stats\"\n");
	}

	if (error_code != SLURM_SUCCESS) {
		exit_code = 1;
	}
}

/*
 * _dump_it - dump the slurmdbd configuration in the format specified
 * IN argc - count of arguments
 * IN argv - list of arguments
 */
static void _dump_it(int argc, char **argv)
{
	int rc = SLURM_SUCCESS;
	slurmdbd_conf_t *slurmdbd_conf = NULL;

	if (!(rc = slurmdb_config_get(db_conn, &slurmdbd_conf))) {
		openapi_resp_slurmdbd_conf_t resp = {
			.slurmdb_conf = slurmdbd_conf,
		};

		DATA_DUMP_CLI(OPENAPI_SLURMDBD_CONF_RESP, resp, argc, argv,
			      db_conn, mime_type, data_parser, rc);
	}

	slurmdbd_free_conf(slurmdbd_conf);
}

/*
 * _show_it - list the slurm configuration per the supplied arguments
 * IN argc - count of arguments
 * IN argv - list of arguments
 * undocumented association options wopi and wopl
 * without parent info and without parent limits
 */
static void _show_it(int argc, char **argv)
{
	int error_code = SLURM_SUCCESS;
	int command_len = 0;

	if (!argv[0])
		goto helpme;

	command_len = strlen(argv[0]);
	if (!have_db_conn &&
	    xstrncasecmp(argv[0], "Configuration",
			 MAX(command_len, 2))) {
		exit_code = 1;
		return;
	}


	/* reset the connection to get the most recent stuff */
	slurmdb_connection_commit(db_conn, 0);

	/* First identify the entity to list */
	if (xstrncasecmp(argv[0], "Accounts", MAX(command_len, 2)) == 0
	    || !xstrncasecmp(argv[0], "Acct", MAX(command_len, 4))) {
		error_code = sacctmgr_list_account((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Associations",
				MAX(command_len, 2)) == 0) {
		error_code = sacctmgr_list_assoc((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Clusters",
				MAX(command_len, 2)) == 0) {
		error_code = sacctmgr_list_cluster((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Configuration",
				MAX(command_len, 2)) == 0) {
		if (mime_type)
			_dump_it(argc, argv);
		else
			error_code = sacctmgr_list_config();
	} else if (xstrncasecmp(argv[0], "Events",
				MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_list_event((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Federation",
				MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_list_federation((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Instances",
				MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_list_instance((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Problems",
				MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_list_problem((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "RunawayJobs", MAX(command_len, 2)) ||
		   !xstrncasecmp(argv[0], "OrphanJobs", MAX(command_len, 1)) ||
		   !xstrncasecmp(argv[0], "LostJobs", MAX(command_len, 1))) {
		error_code = sacctmgr_list_runaway_jobs((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "QOS", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_list_qos((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Resource", MAX(command_len, 4))) {
		error_code = sacctmgr_list_res((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Reservations", MAX(command_len, 4))||
		   !xstrncasecmp(argv[0], "Resv", MAX(command_len, 4))) {
		error_code = sacctmgr_list_reservation((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Stats", MAX(command_len, 1))) {
		error_code = sacctmgr_list_stats((argc - 1), &argv[1]);
	} else if (!xstrncasecmp(argv[0], "Transactions", MAX(command_len, 1))
		   || !xstrncasecmp(argv[0], "Txn", MAX(command_len, 1))) {
		error_code = sacctmgr_list_txn((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Users", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_list_user((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "WCKeys", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_list_wckey((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "tres", MAX(command_len, 2)) == 0) {
		error_code = sacctmgr_list_tres(argc - 1, &argv[1]);
	} else {
	helpme:
		exit_code = 1;
		fprintf(stderr, "No valid entity in list command\n");
		fprintf(stderr, "Input line must include ");
		fprintf(stderr, "\"Account\", \"Association\", "
			"\"Cluster\", \"Configuration\",\n\"Event\", "
			"\"Federation\", \"Problem\", \"QOS\", \"Resource\", "
			"\"Reservation\",\n\"RunAwayJobs\", \"Stats\", "
			"\"Transaction\", \"TRES\", \"User\", or \"WCKey\"\n");
	}

	if (error_code != SLURM_SUCCESS) {
		exit_code = 1;
	}
}


/*
 * _modify_it - modify the slurm configuration per the supplied arguments
 * IN argc - count of arguments
 * IN argv - list of arguments
 */
static void _modify_it(int argc, char **argv)
{
	int error_code = SLURM_SUCCESS;
	int command_len = 0;

	if (!have_db_conn) {
		exit_code = 1;
		return;
	}

	if (readonly_flag) {
		exit_code = 1;
		fprintf(stderr, "Can't run this command in readonly mode.\n");
		return;
	}

	if (!argv[0])
		goto helpme;

	command_len = strlen(argv[0]);
	/* reset the connection to get the most recent stuff */
	slurmdb_connection_commit(db_conn, 0);

	/* First identify the entity to modify */
	if (xstrncasecmp(argv[0], "Accounts", MAX(command_len, 1)) == 0
	    || !xstrncasecmp(argv[0], "Acct", MAX(command_len, 4))) {
		error_code = sacctmgr_modify_account((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Clusters",
				MAX(command_len, 5)) == 0) {
		error_code = sacctmgr_modify_cluster((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Federation",
			       MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_modify_federation((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Job", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_modify_job((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "QOSs", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_modify_qos((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Resource", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_modify_res((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Users", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_modify_user((argc - 1), &argv[1]);
	} else {
	helpme:
		exit_code = 1;
		fprintf(stderr, "No valid entity in modify command\n");
		fprintf(stderr, "Input line must include ");
		fprintf(stderr, "\"Account\", \"Cluster\", \"Federation\", "
			"\"Job\", \"QOS\", \"Resource\" or \"User\"\n");
	}

	if (error_code != SLURM_SUCCESS) {
		exit_code = 1;
	}
}

/*
 * _delete_it - delete the slurm configuration per the supplied arguments
 * IN argc - count of arguments
 * IN argv - list of arguments
 */
static void _delete_it(int argc, char **argv)
{
	int error_code = SLURM_SUCCESS;
	int command_len = 0;

	if (!have_db_conn) {
		exit_code = 1;
		return;
	}

	if (readonly_flag) {
		exit_code = 1;
		fprintf(stderr, "Can't run this command in readonly mode.\n");
		return;
	}

	if (!argv[0])
		goto helpme;

	command_len = strlen(argv[0]);
	/* reset the connection to get the most recent stuff */
	slurmdb_connection_commit(db_conn, 0);

	/* First identify the entity to delete */
	if (xstrncasecmp(argv[0], "Accounts", MAX(command_len, 1)) == 0
	    || !xstrncasecmp(argv[0], "Acct", MAX(command_len, 4))) {
		error_code = sacctmgr_delete_account((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Clusters",
				MAX(command_len, 2)) == 0) {
		error_code = sacctmgr_delete_cluster((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Coordinators",
				MAX(command_len, 2)) == 0) {
		error_code = sacctmgr_delete_coord((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Federations",
				MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_delete_federation((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "QOS", MAX(command_len, 2)) == 0) {
		error_code = sacctmgr_delete_qos((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Resource", MAX(command_len, 1)) == 0){
		error_code = sacctmgr_delete_res((argc - 1), &argv[1]);
	} else if (xstrncasecmp(argv[0], "Users", MAX(command_len, 1)) == 0) {
		error_code = sacctmgr_delete_user((argc - 1), &argv[1]);
	} else {
	helpme:
		exit_code = 1;
		fprintf(stderr, "No valid entity in delete command\n");
		fprintf(stderr, "Input line must include ");
		fprintf(stderr, "\"Account\", \"Cluster\", \"Coordinator\", ");
		fprintf(stderr, "\"Federation\", \"QOS\", \"Resource\", or ");
		fprintf(stderr, "\"User\"\n");
	}

	if (error_code != SLURM_SUCCESS) {
		exit_code = 1;
	}
}

static void _usage(void)
{
        char *txt;
        static_ref_to_cstring(txt, usage_txt);
        printf("%s\n", txt);
        xfree(txt);
}
