/*
 * Copyright (c) Likewise Software.  All rights Reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library 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 Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Module Name:
 *
 *        lwsm.h
 *
 * Abstract:
 *
 *        Primary public header file
 *
 * Authors: Brian Koropoff (bkoropoff@likewise.com)
 *
 */

#ifndef __LWSM_H__
#define __LWSM_H__

/**
 * @file lwsm.h
 * @brief Likewise Service Manager API
 */

#include <lw/base.h>
#include <lwdef.h>

/**
 * @defgroup client Client API
 * @brief Client API
 *
 * This module contains the client-side API used to query and control services
 * in the Likewise Service Manager (lwsmd)
 */

/*@{*/

/**
 * @brief Opaque service handle
 *
 * A handle to a particular service that can be used to query or
 * perform operations upon it.
 */
typedef struct _LW_SERVICE_HANDLE *LW_SERVICE_HANDLE, **PLW_SERVICE_HANDLE;

/**
 * @brief Log level
 *
 * Describes the level of a log message in terms
 * of importance.
 */
typedef enum _LW_SM_LOG_LEVEL
{
    /**
     * @brief Indicates the default log level
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_DEFAULT = 0,
    /**
     * @brief Message that should always be logged
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_ALWAYS = LW_RTL_LOG_LEVEL_ALWAYS,
    /**
     * @brief Message that indicates an error condition
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_ERROR = LW_RTL_LOG_LEVEL_ERROR,
    /**
     * @brief Message that indicates a warning condition
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_WARNING = LW_RTL_LOG_LEVEL_WARNING,
    /**
     * @brief Message that informs the user of a normal
     * but significant condition or event
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_INFO = LW_RTL_LOG_LEVEL_INFO,
    /**
     * @brief Message that informs the user of a normal
     * and common event
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_VERBOSE = LW_RTL_LOG_LEVEL_VERBOSE,
    /**
     * @brief Message that is only of interest when attempting
     * to debug improper program behavior.
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_DEBUG = LW_RTL_LOG_LEVEL_DEBUG,
    /**
     * @brief Message that tracks low-level program execution
     * flow.
     * @hideinitializer
     */
    LW_SM_LOG_LEVEL_TRACE = LW_RTL_LOG_LEVEL_TRACE
} LW_SM_LOG_LEVEL, *PLW_SM_LOG_LEVEL;

/**
 * @brief Logger type
 *
 * Indicates the type of logging target
 */
typedef enum _LW_SM_LOGGER_TYPE
{
    /**
     * @brief Log to the default target
     * @hideinitializer
     */
    LW_SM_LOGGER_DEFAULT,
    /**
     * @brief Do not log
     * @hideinitializer
     */
    LW_SM_LOGGER_NONE,
    /**
     * @brief Log to file
     * @hideinitializer
     */
    LW_SM_LOGGER_FILE,
    /**
     * @brief Log to syslog
     * @hideinitializer
     */
    LW_SM_LOGGER_SYSLOG
} LW_SM_LOGGER_TYPE, *PLW_SM_LOGGER_TYPE;

/**
 * @brief State of a service
 *
 * Represents the state of a service (running, stopped, etc.)
 */
typedef enum _LW_SERVICE_STATE
{
    /**
     * @brief Service is running
     * @hideinitializer
     */
    LW_SERVICE_STATE_RUNNING = 0,
    /**
     * @brief Service is stopped
     * @hideinitializer
     */
    LW_SERVICE_STATE_STOPPED = 1,
    /**
     * @brief Service is starting
     * @hideinitializer
     */
    LW_SERVICE_STATE_STARTING = 2,
    /**
     * @brief Service is stopping
     * @hideinitializer
     */
    LW_SERVICE_STATE_STOPPING = 3,
    /**
     * @brief Service is pining for the fjords
     * @hideinitializer
     */
    LW_SERVICE_STATE_DEAD = 5,
    /**
     * @brief Service failed to start
     * @hideinitializer
     */
    LW_SERVICE_STATE_FAILED = 6,
    /**
     * @brief Service is stuck running
     * @hideinitializer
     */
    LW_SERVICE_STATE_STUCK = 7
} LW_SERVICE_STATE, *PLW_SERVICE_STATE;

/**
 * @brief Service information
 *
 * Describes the basic information about a service,
 * such as its path, command line arguments, etc.
 */
typedef struct _LW_SERVICE_INFO
{
    /** @brief Service short name */
    LW_PWSTR pwszName;
    /** @brief Service description */
    LW_PWSTR pwszDescription;
    /** @brief Path to service executable or module */
    LW_PWSTR pwszPath;
    /** @brief Arguments to service when started */
    LW_PWSTR* ppwszArgs;
    /** @brief Names of services on which this service depends */
    LW_PWSTR* ppwszDependencies;
    /** @brief Name of service group */
    LW_PWSTR pwszGroup;
    /** @brief Service categories */
    LW_PWSTR* ppCategories;
    /** @brief Desired file descriptor limit for the process */
    LW_DWORD dwFdLimit;
    /** @brief Default log type */
    LW_SM_LOGGER_TYPE DefaultLogType;
    /** @brief Default log target */
    LW_PWSTR pDefaultLogTarget;
    /** @brief Default log level */
    LW_SM_LOG_LEVEL DefaultLogLevel;
    /** @brief Check command */
    LW_PWSTR* ppCheckCommand;
} LW_SERVICE_INFO, *PLW_SERVICE_INFO;

typedef const LW_SERVICE_INFO* PCLW_SERVICE_INFO;

/**
 * @brief Service status
 *
 * Describes the runtime status of a service
 */
typedef struct _LW_SERVICE_STATUS
{
    /** Brief Service state (stopped, running, etc.) */
    LW_SERVICE_STATE state;
    /** Brief Error associated with current state */
    DWORD Error;
    /** Brief Process ID of service home */
    pid_t pid;
} LW_SERVICE_STATUS, *PLW_SERVICE_STATUS;

typedef enum _LW_SM_GLOBAL_SETTING
{
    /**
     * @brief No setting
     * @hideinitializer
     */
    LW_SM_GLOBAL_SETTING_NONE = 0,
    /**
     * @brief Control service watchdog
     *
     * (BOOLEAN) Turns service watchdog on or off globally
     * @hideinitializer
     */
    LW_SM_GLOBAL_SETTING_WATCHDOG,
    /**
     * @brief Timeout before considering a service stuck
     *
     * (DWORD) Sets the global timeout before a service is considered stuck
     * when starting or stopping.
     * @hideinitializer
     */
    LW_SM_GLOBAL_SETTING_STUCK_TIMEOUT,
    /**
     * @brief Restart limit
     *
     * (DWORD) Sets the maximum number of times the watchdog will
     * attempt to restart a service when it dies.  The count of
     * attempts resets after the #LW_SM_GLOBAL_SETTING_RESTART_PERIOD
     * has expired.
     * @hideinitializer
     */
    LW_SM_GLOBAL_SETTING_RESTART_LIMIT,
    /**
     * @brief Restart period
     *
     * (DWORD) The time in seconds after which the count of
     * restart attempts for a service is reset. The period is measured
     * from the first restart attempt.
     * @hideinitializer
     */
    LW_SM_GLOBAL_SETTING_RESTART_PERIOD
} LW_SM_GLOBAL_SETTING, *PLW_SM_GLOBAL_SETTING;

/**
 * @brief Acquire service handle
 *
 * Gets a handle to a known service by name
 *
 * @param[in] pwszServiceName the name of the service
 * @param[out] phHandle a service handle
 * @retval LW_ERROR_SUCCESS success
 * @retval LW_ERROR_NO_SUCH_SERVICE no service with the specified name exists
 */
DWORD
LwSmAcquireServiceHandle(
    LW_PCWSTR pwszServiceName,
    PLW_SERVICE_HANDLE phHandle
    );

/**
 * @brief Release a service handle
 *
 * Releases a handle previously acquired with #LwSmAcquireServiceHandle().
 *
 * @param[in] hHandle the service handle
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmReleaseServiceHandle(
    LW_SERVICE_HANDLE hHandle
    );

/**
 * @brief Enumerate available services
 *
 * Returns a NULL-terminated list of strings containing the names
 * of all known services.  The list should be freed with
 * #LwSmFreeServiceNameList() when done.
 *
 * @param[out] pppwszServiceNames the returned list of services
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmEnumerateServices(
    PWSTR** pppwszServiceNames
    );

/**
 * @brief Free service name list
 *
 * Frees a service name list as returned by e.g. #LwSmEnumerateServices()
 *
 * @param[in,out] ppwszServiceNames the list of services
 */
VOID
LwSmFreeServiceNameList(
    PWSTR* ppwszServiceNames
    );

/**
 * @brief Start a service
 *
 * Starts the service represented by the given service handle.  If the service
 * is already started, this function trivially succeeds.  If the service is not
 * started, it will attempt to start it and wait for it finish starting.
 * If the service is in the process of starting, it will wait for it to finish
 * starting.
 *
 * @param[in] hHandle the service handle
 * @retval ERROR_SUCCESS success
 * @retval ... the service failed to start
 */
DWORD
LwSmStartService(
    LW_SERVICE_HANDLE hHandle
    );
/**
 * @brief Stop a service
 *
 * Stops the service represented by the given service handle.  If the service
 * is already stopped, this function trivially succeeds.  If the service is not
 * stopped, it will attempt to stop it and wait for it finish stopping.
 * If the service is in the process of stopping, it will wait for it to finish
 * stopping.
 *
 * @param[in] hHandle the service handle
 * @retval ERROR_SUCCESS success
 * @retval .. the service refused to stop
 */
DWORD
LwSmStopService(
    LW_SERVICE_HANDLE hHandle
    );
   
/**
 * @brief Get service status
 *
 * Gets the current status of the service represented by the given service handle.
 * 
 * @param[in] hHandle the service handle
 * @param[out] pStatus the status of the service
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmQueryServiceStatus(
    LW_SERVICE_HANDLE hHandle,
    PLW_SERVICE_STATUS pStatus
    );

/**
 * @brief Wait for service state change
 *
 * Waits for the given service's state to change.  The last known
 * state of the service must be specified as a parameter, and the new
 * state will be returned when a change occurs.
 *
 * @note This function requires the last known state of the service
 * to be passed in to guard against a race condition in which the service
 * changes state after its status is queried but before this function is
 * called.  Passing in the last known state guarantees that this function
 * will return immediately if the service has changed state since the last
 * query.
 *
 * @param[in] hHandle the service handle
 * @param[in] currentState the last known state of the service
 * @param[out] pNewState the new state of the service
 */
DWORD
LwSmWaitService(
    LW_SERVICE_HANDLE hHandle,
    LW_SERVICE_STATE currentState,
    PLW_SERVICE_STATE pNewState
    );

/**
 * @brief Refresh service
 *
 * Refreshes the service represented by the given service handle, which typically
 * entails it reloading its configuration.
 *
 * @param[in] hHandle the service handle
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmRefreshService(
    LW_SERVICE_HANDLE hHandle
    );

/**
 * @brief Kill service
 *
 * Kills a service without waiting for it to stop normally.  Note that this
 * may affect other services running in the same process.  If the service
 * was already stopped or dead, this call will trivially succeed.
 */
DWORD
LwSmKillService(
    LW_SERVICE_HANDLE Service
    );

/**
 * @brief Sets log target for a service
 *
 * Sets the log target for a service.  If the service is running in a container,
 * all services in the same container will be affected.
 *
 * @param[in] hHandle the service handle, or NULL to specify lwsmd itself
 * @param[in] pFacility the facility to redirect, or NULL to set the default log target
 * @param[in] type the type of logging target
 * @param[in] pTarget the logging target, or NULL for target types that don't require it
 * @retval ERROR_SUCCESS success
 * @retval ERROR_NOT_SUPPORTED the specified service does not support setting log targets
 */
DWORD
LwSmSetServiceLogTarget(
    LW_SERVICE_HANDLE hHandle,
    LW_PCSTR pFacility,
    LW_SM_LOGGER_TYPE type,
    PCSTR pTarget
    );

/**
 * @brief Set maximum log level for a service
 *
 * Sets the maximum log level for a service.  If the service is running in
 * a container, all services in the same container will be affected.
 *
 * @param[in] hHandle the service handle, or NULL to specify lwsmd itself
 * @param[in] pFacility the facility to set the level for, or NULL to set the default log level
 * @param[in] level the maximum log level to set
 * @retval ERROR_SUCCESS success
 * @retval ERROR_NOT_SUPPORTED the specified service does not support setting log targets
 */
DWORD
LwSmSetServiceLogLevel(
    LW_SERVICE_HANDLE hHandle,
    LW_PCSTR pFacility,
    LW_SM_LOG_LEVEL level
    );

/**
 * @brief Get current logging state for a service
 *
 * Gets the current logging target and maximum log level for a service.
 * If the service is running in a container, the returned values apply
 * to all services in the same container.  The returned logging target
 * string can be freed with #LwSmFreeLogTarget().
 *
 * @param[in] hHandle the service handle, or NULL to specify lwsmd itself
 * @param[in] pFacility the facility to get state for, or NULL for the default state
 * @param[out] pType set to the logging target type
 * @param[out] ppTarget set to the logging target if applicable or NULL otherwise
 * @param[out] pLevel set to the maximum log level
 * @retval ERROR_SUCCESS
 * @retval ERROR_NOT_SUPPORTED the specified service does not support getting log state
 */
DWORD
LwSmGetServiceLogState(
    LW_SERVICE_HANDLE hHandle,
    LW_PCSTR pFacility,
    PLW_SM_LOGGER_TYPE pType,
    LW_PSTR* ppTarget,
    PLW_SM_LOG_LEVEL pLevel
    );

/**
 * @brief Free logging target
 *
 * Frees a logging target string returned by a previous call to
 * #LwSmGetServiceLogState().
 *
 * @param[in,out] pTarget
 */
VOID
LwSmFreeLogTarget(
    LW_PSTR pTarget
    );

/**
 * @brief Enumerate logging facilities for a service
 *
 * Enumerates all logging facilities for a service that have a non-default
 * configuration.
 *
 * @param[in] hHandle the service handle, or NULL to specify lwsmd itself
 * @param[out] pppFacilities set to the list of facilities
 * @retval ERROR_SUCCESS
 * @retval ERROR_NOT_SUPPORTED the specified service does not support getting log state
 */
DWORD
LwSmEnumerateServiceLogFacilities(
    LW_SERVICE_HANDLE hHandle,
    LW_PWSTR** pppFacilities
    );

/**
 * @brief Free log facility list
 *
 * Frees a list of logging facilities returned by
 * #LwSmEnumerateServiceLogFacilities().
 */
VOID
LwSmFreeLogFacilityList(
    LW_PWSTR* ppFacilities
    );

/**
 * @brief Get service info
 *
 * Gets the service info structore of the service represented by the given
 * service handle.  The structure should be freed with #LwSmFreeServiceInfo()
 * when done.
 * 
 * @param[in] hHandle the service handle
 * @param[out] ppInfo the info structure for the service
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmQueryServiceInfo(
    LW_SERVICE_HANDLE hHandle,
    PLW_SERVICE_INFO* ppInfo
    );

/**
 * @brief Get recursive dependency list
 *
 * Gets a list of all recursive dependencies of the service represented
 * by the given service handle -- that is, all services the given service
 * depends on directly or indirectly.  The entries in the list will be in the
 * order in which they would need to be started in order to start the
 * given service.  The list should be freed with #LwSmFreeServiceNameList()
 * when done.
 *
 * @param[in] hHandle the service handle
 * @param[out] pppwszServiceList the service dependency list
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmQueryServiceDependencyClosure(
    LW_SERVICE_HANDLE hHandle,
    PWSTR** pppwszServiceList
    );

/**
 * @brief Get recursive reverse dependency list
 *
 * Gets a list of all recursive reverse dependencies of the service represented
 * by the given service handle -- that is, all services which depend on the
 * given service directly or indirectly.  The entries in the list will be in the
 * order in which they would need to be stopped in order to stop the
 * given service.  The list should be freed with #LwSmFreeServiceNameList()
 * when done.
 *
 * @param[in] hHandle the service handle
 * @param[out] pppwszServiceList the service reverse dependency list
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmQueryServiceReverseDependencyClosure(
    LW_SERVICE_HANDLE hHandle,
    PWSTR** pppwszServiceList
    );

/**
 * @brief Free service info structure
 *
 * Frees a service info structure as returned by e.g.
 * #LwSmQueryServiceInfo()
 *
 * @param[in,out] pInfo the service info structure to free
 */
VOID
LwSmFreeServiceInfo(
    PLW_SERVICE_INFO pInfo
    );

/**
 * @brief Refresh service manager
 *
 * Causes the service manager to reread its own configuration.
 * To refresh the configuration of a particular service, use
 * #LwSmRefreshService().
 *
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmRefresh(
    VOID
    );

/**
 * @brief Shutdown
 *
 * Causes the service manager to shut down completely.
 *
 * @retval LW_ERROR_SUCCESS success
 */
DWORD
LwSmShutdown(
    VOID
    );

/**
 * @brief Change global setting
 *
 * Changes a global setting.  See the description of each
 * global setting for the type of parameters to pass as
 * the trailing arguments.
 *
 * @param[in] Setting the setting to change
 * @param[in] ... value or values for the given setting
 * @retval #ERROR_SUCCESS success
 * @retval #ERROR_INVALID_PARAMETER invalid setting or value
 */
DWORD
LwSmSetGlobal(
    LW_IN LW_SM_GLOBAL_SETTING Setting,
    ...
    );

/**
 * @brief Get global setting
 *
 * Gets the value of a global setting.  The
 * trailing parameters should be out pointers to
 * the appropriate types listed in the description
 * of the setting to change.
 *
 * @param[in] Setting the setting to get
 * @param[out] ... set to the value or values for the given setting
 */
DWORD
LwSmGetGlobal(
    LW_IN LW_SM_GLOBAL_SETTING Setting,
    ...
    );

/**
 * @brief Free global setting value
 *
 * Frees the value of a global setting previously
 * returned by #LwSmGetGlobal().
 *
 * @param[in] Setting the setting associated with the value to free
 * @param[in,out] ... the value or values to free
 */
VOID
LwSmFreeGlobal(
    LW_IN LW_SM_GLOBAL_SETTING Setting,
    ...
    );

/**
 * @brief Check that service is still functional
 *
 * Checks that the given service is still functional by running
 * its configured check command.
 *
 * @param[in] Service the service
 * @param[in] MillisecTimeout the timeout in milliseconds before
 * giving up and killing the check command
 * @retval ERROR_SUCCESS the check succeeded
 * @retval ERROR_NOT_SUPPORTED the service did not have a check command configured
 * @retval LW_ERROR_SERVICE_UNRESPONSIVE the check failed
 * @retval ... an error occurred communicating with the service manager
 */
LW_DWORD
LwSmCheckService(
    LW_IN LW_SERVICE_HANDLE Service,
    LW_IN LW_DWORD MillisecTimeout
    );

/*@}*/

#endif
