/* COPYING ******************************************************************
For copyright and licensing terms, see the file named COPYING.
// **************************************************************************
*/

#include <vector>
#include <cstddef>
#include <cstdlib>
#include <csignal>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
#include "utils.h"
#include "fdutils.h"
#include "service-manager-client.h"
#include "popt.h"

/* Utilities ****************************************************************
// **************************************************************************
*/

static inline
std::vector<std::string>
lookup_bundle_directories (
	const char * prog,
	const std::vector<const char *> & args,
	const ProcessEnvironment & envs
) {
	std::vector<std::string> r;
	for (std::vector<const char *>::const_iterator i(args.begin()); args.end() != i; ++i) {
		std::string path, name, suffix;
		const int rc(open_bundle_directory(envs, "", *i, path, name, suffix));
		if (0 > rc) {
			die_errno(prog, envs, *i);
		}
		r.insert(r.end(), path + name);
		close(rc);
	}
	return r;
}

/* System control subcommands ***********************************************
// **************************************************************************
*/

namespace {

void
common_subcommand (
	const char * & next_prog,
	std::vector<const char *> & args,
	const ProcessEnvironment & envs,
	const char * command,
	const char * arg,
	bool show_log_lines = false
) {
	const char * prog(basename_of(args[0]));
	const char * log_lines(nullptr);
	try {
		popt::bool_definition user_option('u', "user", "Communicate with the per-user manager.", per_user_mode);
		popt::string_definition log_lines_option('\0', "log-lines", "number", "Control the number of log lines printed.", log_lines);
		popt::definition * main_table[] = {
			&user_option,
			&log_lines_option
		};
		popt::top_table_definition main_option(sizeof main_table/sizeof *main_table - (show_log_lines?0:1), main_table, "Main options", "{service(s)...}");

		std::vector<const char *> new_args;
		popt::arg_processor<const char **> p(args.data() + 1, args.data() + args.size(), prog, envs, main_option, new_args);
		p.process(true /* strictly options before arguments */);
		args = new_args;
		next_prog = arg0_of(args);
		if (p.stopped()) throw EXIT_SUCCESS;
	} catch (const popt::error & e) {
		die(prog, envs, e);
	}

	// This must have static storage duration as we are using it in args.
	static std::vector<std::string> args_storage;

	args_storage = lookup_bundle_directories(prog, args, envs);
	args = convert_args_storage(args_storage);
	args.insert(args.begin(), "--");
	if (arg)
		args.insert(args.begin(), arg);
	if (log_lines) {
		args.insert(args.begin(), log_lines);
		args.insert(args.begin(), "--log-lines");
	}
	args.insert(args.begin(), command);
	next_prog = arg0_of(args);
}

}

void
find (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "ls", "-1d");
}

void
show_json (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-show", "--json");
}

void
show (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-show", nullptr);
}

void
status (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-status", nullptr, true /* log-lines option */);
}

void
try_restart (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-control", "--terminate");
}

void
unload_when_stopped (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-control", "--exit");
}

void
hangup (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-control", "--hangup-main");
}

void
is_active (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-is-up", nullptr);
}

void
is_loaded (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-is-ok", nullptr);
}

void
is_enabled (
	const char * & next_prog,
	std::vector<const char *> & args,
	ProcessEnvironment & envs
) {
	common_subcommand(next_prog, args, envs, "service-is-enabled", nullptr);
}
