/*
 * Copyright (C) Tildeslash Ltd. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * 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 Affero General Public License in all respects
 * for all of the code used other than OpenSSL.
 */


#ifndef COMMAND_INCLUDED
#define COMMAND_INCLUDED
#include "system/Process.h"
#include "util/List.h"


/**
 * A <b>Command</b> creates operating system processes. Each Command instance
 * manages a collection of process attributes. The Command_execute() method
 * creates a new sub-process with those attributes and the method can be invoked
 * repeatedly from the same instance to create new sub-processes with identical
 * or related attributes.
 *
 * Modifying a Command's attributes will affect processes subsequently created,
 * but will not affect previously created processes nor the calling process
 * itself.
 *
 * This class is reentrant but not thread-safe
 *
 * @see Process.h
 * @author https://tildeslash.com
 * @see https://mmonit.com
 * @file
 */


#define T Command_T
typedef struct T *T;


/**
 * Create a new Command object and set the operating system program with
 * arguments to execute. The <code>...</code> variable argument list denotes
 * an optional sequence of arguments to the program. The arguments list can
 * be thought of as arg0, arg1, ..., argn. Together they describe a list of
 * zero or more pointers to null-terminated strings that represent the argument
 * list available to the executed program specified in <code>path</code>. Example:
 * <pre>
 * Command_new("/bin/ls")
 * Command_new("/bin/ls", "-lrt")
 * Command_new("/bin/sh", "-c", "ps -aef|egrep mmonit")
 * </pre>
 * @param path The program to execute, _without_ any arguments
 * @param ... An optional sequence of arguments to the program given in path
 * @exception AssertException if the program given in path does not exist.
 * @return A Command object
 */
#define Command_new(path, ...) _Command_new((path), ##__VA_ARGS__, NULL)
/** Internal function. Use Command_new() */
T _Command_new(const char *path, ...) __attribute__((sentinel));


/**
 * Destroy A Command object and release allocated resources. Call this
 * method to release a Command object allocated with Command_new()
 * @param C A Command object reference
 */
void Command_free(T *C);


/** @name Properties */
//@{

/**
 * Append an argument to the command that should be used to launch this executable.
 * @param C A Command object
 * @param argument A string argument
 */
void Command_appendArgument(T C, const char *argument);


/**
 * Set the user id the sub-process should switch to on exec. If not set, the
 * uid of the calling process is used. Note that this process must run with
 * super-user privileges for the sub-process to be able to switch uid. In fact
 * it is a checked runtime error to call this method if this process does not
 * run with super-user privileges
 * @param C A Command object
 * @param uid The user id the sub-process should switch to when executed
 * @exception AssertException If this process is not run by the super-user or
 * with super-user privileges.
 */
void Command_setUid(T C, uid_t uid);


/**
 * Returns the uid the sub-process should switch to on exec.
 * @param C A Command object
 * @return The user id the sub-process should use. Returns 0
 * if not set, meaning the sub-process will run with the same
 * uid as this process.
 */
uid_t Command_uid(T C);


/**
 * Set the group id the sub-process should switch to on exec. If not set,
 * the gid of the calling process is used. Note that this process must run
 * with super-user privileges for the sub-process to be able to switch gid
 * @param C A Command object
 * @param gid The group id the sub-process should switch to when executed
 * @exception AssertException If this process is not run by the super-user or
 * with super-user privileges.
 */
void Command_setGid(T C, gid_t gid);


/**
 * Returns the group id the Command should switch to on exec.
 * @param C A Command object
 * @return The group id the sub-process should use. Returns 0
 * if not set, meaning the sub-process will run with the same
 * gid as this process.
 */
gid_t Command_gid(T C);


/**
 * Set the umask value for the sub-process. The default value if not set
 * is 022. See also https://en.wikipedia.org/wiki/Umask and man umask(2)
 * @param C A Command object
 * @param umask The new umask value, as a 3 digit octal number, e.g. 002
 */
void Command_setUmask(T C, mode_t umask);


/**
 * Returns the umask value for the sub-process. The default value is 022
 * unless changed with Command_setUmask().
 * @param C A Command object
 * @return The umask value for the sub-process
 */
mode_t Command_umask(T C);


/**
 * Set the working directory for the sub-process. If the directory cannot
 * be changed to, the sub-process will exit with an error
 * @param C A Command object
 * @param dir The working directory for the sub-process
 * @exception AssertException if the directory does not exist or is not
 * accessible
 */
void Command_setDir(T C, const char *dir);


/**
 * Returns the working directory for the sub-process. Unless previously
 * set, the returned value is NULL, meaning the calling process's current
 * directory
 * @param C A Command object
 * @return The working directory for the sub-process or NULL meaning the
 * calling process's current directory
 */
const char *Command_dir(T C);


/**
 * Set or replace the environment variable identified by <code>name</code>.
 * The sub-process initially inherits the environment from the calling
 * process. Environment variables set with this method does not affect the
 * parent process and only apply to the sub-process.
 * @param C A Command object
 * @param name The environment variable to set or replace
 * @param value The value. A NULL value is converted to the empty string ""
 */
void Command_setEnv(T C, const char *name, const char *value);


/**
 * Set or replace the environment variable identified by <code>name</code>.
 * The sub-process initially inherits the environment from the calling
 * process. Environment variables set with this method does not affect the
 * parent process and only apply to the sub-process. Example:
 *<pre>
 * Command_vSetEnv(C, "PID", "%ld", getpid()) -> PID=1234
 * </pre>
 * @param C A Command object
 * @param name The environment variable to set or replace
 * @param value The value. A NULL value is converted to the empty string ""
 */
void Command_vSetEnv(T C, const char *name, const char *value, ...) __attribute__((format (printf, 3, 4)));


/**
 * Returns the value of the environment variable identified by
 * <code>name</code>.
 * @param C A Command object
 * @param name The environment variable to get the value of
 * @return The value for name or NULL if name was not found
 */
const char *Command_env(T C, const char *name);


/**
 * Returns the operating system program with arguments to be executed by
 * this Command. The first element in the list is the path to the program
 * and subsequent elements are arguments to the program. Elements in the
 * list are C-strings.
 * @param C A Command object
 * @return A list with the operating system program with arguments to
 * execute. The first element in the list is the program.
 */
List_T Command_command(T C);

//@}


/**
 * Create a new sub-process using the attributes of this Command object.
 * The new sub-process will execute the command and arguments given in
 * Command_new(). The caller is responsible for releasing the returned
 * Process_T object by calling Process_free(). If creating the new
 * sub-process failed, NULL is returned and errno is set to indicate the
 * error. Use e.g. System_lastError() to get a description of the error
 * that occurred.
 * @param C A Command object
 * @return A new Process_T object representing the sub-process or NULL
 * if execute failed.
 */
Process_T Command_execute(T C);


#undef T
#endif

