/*
 * libstatgrab
 * https://libstatgrab.org
 * Copyright (C) 2003-2004 Peter Saunders
 * Copyright (C) 2003-2019 Tim Bishop
 * Copyright (C) 2003-2013 Adam Sampson
 * Copyright (C) 2012-2019 Jens Rehsack
 * 
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#ifndef STATGRAB_H
#define STATGRAB_H

#ifdef __cplusplus
extern "C" {
#endif

#include <sys/types.h>
#include <time.h>

#if 0
#define __sg_export 
#define __sg_import 
#define __sg_private 
#else
#define __sg_export
#define __sg_import
#define __sg_private
#endif

#ifdef BUILD_LIBSTATGRAB
#define __sg_public __sg_export
#else
#define __sg_public __sg_import
#endif

/* FIXME typedefs for 32/64-bit types */
/* FIXME maybe tidy up field names? */
/* FIXME comments for less obvious fields */

typedef enum {
	SG_ERROR_NONE = 0,
	SG_ERROR_INVALID_ARGUMENT,
	SG_ERROR_ASPRINTF,
	SG_ERROR_SPRINTF,
	SG_ERROR_DEVICES,
	SG_ERROR_DEVSTAT_GETDEVS,
	SG_ERROR_DEVSTAT_SELECTDEVS,
	SG_ERROR_DISKINFO,
	SG_ERROR_ENOENT,
	SG_ERROR_GETIFADDRS,
	SG_ERROR_GETMNTINFO,
	SG_ERROR_GETPAGESIZE,
	SG_ERROR_HOST,
	SG_ERROR_KSTAT_DATA_LOOKUP,
	SG_ERROR_KSTAT_LOOKUP,
	SG_ERROR_KSTAT_OPEN,
	SG_ERROR_KSTAT_READ,
	SG_ERROR_KVM_GETSWAPINFO,
	SG_ERROR_KVM_OPENFILES,
	SG_ERROR_MALLOC,
	SG_ERROR_MEMSTATUS,
	SG_ERROR_OPEN,
	SG_ERROR_OPENDIR,
	SG_ERROR_READDIR,
	SG_ERROR_PARSE,
	SG_ERROR_PDHADD,
	SG_ERROR_PDHCOLLECT,
	SG_ERROR_PDHOPEN,
	SG_ERROR_PDHREAD,
	SG_ERROR_PERMISSION,
	SG_ERROR_PSTAT,
	SG_ERROR_SETEGID,
	SG_ERROR_SETEUID,
	SG_ERROR_SETMNTENT,
	SG_ERROR_SOCKET,
	SG_ERROR_SWAPCTL,
	SG_ERROR_SYSCONF,
	SG_ERROR_SYSCTL,
	SG_ERROR_SYSCTLBYNAME,
	SG_ERROR_SYSCTLNAMETOMIB,
	SG_ERROR_SYSINFO,
	SG_ERROR_MACHCALL,
	SG_ERROR_IOKIT,
	SG_ERROR_UNAME,
	SG_ERROR_UNSUPPORTED,
	SG_ERROR_XSW_VER_MISMATCH,
	SG_ERROR_GETMSG,
	SG_ERROR_PUTMSG,
	SG_ERROR_INITIALISATION,
	SG_ERROR_MUTEX_LOCK,
	SG_ERROR_MUTEX_UNLOCK
} sg_error;

typedef struct sg_error_details {
	sg_error error;
	int errno_value;
	const char *error_arg;
} sg_error_details;

__sg_public sg_error sg_get_error(void);
__sg_public const char *sg_get_error_arg(void);
__sg_public int sg_get_error_errno(void);
__sg_public sg_error sg_get_error_details(sg_error_details *err_details);
__sg_public const char * sg_str_error(sg_error code);
__sg_public char *sg_strperror(char **buf, const sg_error_details * const err_details);

/*
 * initializes libstatgrab
 * @param ignore_init_errors - set to a true value to ignore initialization
 *   errors of component initializations (e.g. permission problems for kvm
 *   on freebsd)
 * XXX requires each component to provide a get_<comp>_init_error() and a
 *   reinit_<comp>()
 */
__sg_public sg_error sg_init(int ignore_init_errors);
__sg_public sg_error sg_snapshot(void);
__sg_public sg_error sg_shutdown(void);
__sg_public sg_error sg_drop_privileges(void);

__sg_public size_t sg_get_nelements(const void *data);
__sg_public sg_error sg_free_stats_buf(void *data);
#if 1
#define SG_SUPPORT_THREADS
__sg_public sg_error sg_lock_mutex(const char *mutex_name);
__sg_public sg_error sg_unlock_mutex(const char *mutex_name);
#else
#define sg_lock_mutex(name) SG_ERROR_NONE
#define sg_unlock_mutex(name) SG_ERROR_NONE
#endif

typedef enum {
	sg_unknown_configuration = 0,
	sg_physical_host,
	sg_virtual_machine,
	sg_paravirtual_machine,
	sg_hardware_virtualized
} sg_host_state;

typedef struct {
	char *os_name;
	char *os_release;
	char *os_version;
	char *platform;
	char *hostname;
	unsigned bitwidth;
	sg_host_state host_state;
	unsigned ncpus;
	unsigned maxcpus;
	time_t uptime;
	time_t systime;
} sg_host_info;

__sg_public sg_host_info *sg_get_host_info(size_t *entries);
__sg_public sg_host_info *sg_get_host_info_r(size_t *entries);
#define sg_free_host_info(data) sg_free_stats_buf(data);

typedef struct {
	unsigned long long user;
	unsigned long long kernel;
	unsigned long long idle;
	unsigned long long iowait;
	unsigned long long swap;
	unsigned long long nice;
	unsigned long long total;

	unsigned long long context_switches;
	unsigned long long voluntary_context_switches;
	unsigned long long involuntary_context_switches;
	unsigned long long syscalls;
	unsigned long long interrupts;
	unsigned long long soft_interrupts;

	time_t systime;
} sg_cpu_stats;

__sg_public sg_cpu_stats *sg_get_cpu_stats(size_t *entries);
__sg_public sg_cpu_stats *sg_get_cpu_stats_diff(size_t *entries);
__sg_public sg_cpu_stats *sg_get_cpu_stats_r(size_t *entries);
__sg_public sg_cpu_stats *sg_get_cpu_stats_diff_between(const sg_cpu_stats *cpu_now, const sg_cpu_stats *cpu_last, size_t *entries);
#define sg_free_cpu_stats(data) sg_free_stats_buf(data);

typedef struct {
	double user;
	double kernel;
	double idle;
	double iowait;
	double swap;
	double nice;
	time_t time_taken;
} sg_cpu_percents;

typedef enum sg_cpu_percent_source {
	sg_entire_cpu_percent,
	sg_last_diff_cpu_percent,
	sg_new_diff_cpu_percent
} sg_cpu_percent_source;

__sg_public sg_cpu_percents *sg_get_cpu_percents_of(sg_cpu_percent_source cps, size_t *entries);
__sg_public sg_cpu_percents *sg_get_cpu_percents_r(sg_cpu_stats const * whereof, size_t *entries);
#define sg_free_cpu_percents(data) sg_free_stats_buf(data);

#define sg_get_cpu_percents(entries) sg_get_cpu_percents_of(sg_new_diff_cpu_percent, entries)

typedef struct {
	unsigned long long total;
	unsigned long long free;
	unsigned long long used;
	unsigned long long cache;
	time_t systime;
} sg_mem_stats;

__sg_public sg_mem_stats *sg_get_mem_stats(size_t *entries);
__sg_public sg_mem_stats *sg_get_mem_stats_r(size_t *entries);
#define sg_free_mem_stats(data) sg_free_stats_buf(data);

typedef struct {
	double min1;
	double min5;
	double min15;
	time_t systime;
} sg_load_stats;

__sg_public sg_load_stats *sg_get_load_stats(size_t *entries);
__sg_public sg_load_stats *sg_get_load_stats_r(size_t *entries);
#define sg_free_load_stats(data) sg_free_stats_buf(data);

typedef struct {
	char *login_name;
	char *record_id;
	size_t record_id_size;
	char *device;
	char *hostname;
	pid_t pid; /* XXX sugesting to ad a uid here, too (padding + lookup-cache?) */
	time_t login_time;
	time_t systime;
} sg_user_stats;

__sg_public sg_user_stats *sg_get_user_stats(size_t *entries);
__sg_public sg_user_stats *sg_get_user_stats_r(size_t *entries);
#define sg_free_user_stats(data) sg_free_stats_buf(data);

typedef struct {
	unsigned long long total;
	unsigned long long used;
	unsigned long long free;
	time_t systime;
} sg_swap_stats;

__sg_public sg_swap_stats *sg_get_swap_stats(size_t *entries);
__sg_public sg_swap_stats *sg_get_swap_stats_r(size_t *entries);
#define sg_free_swap_stats(data) sg_free_stats_buf(data);

typedef enum {
	sg_fs_unknown  = 0,
	sg_fs_regular  = 1 << 0,
	sg_fs_special  = 1 << 1,
	sg_fs_loopback = 1 << 2,
	sg_fs_remote   = 1 << 3,
	sg_fs_local    = (sg_fs_regular | sg_fs_special),
	sg_fs_alltypes = (sg_fs_regular | sg_fs_special | sg_fs_loopback | sg_fs_remote)
} sg_fs_device_type;

typedef struct {
	char *device_name;
	char *device_canonical;
	char *fs_type;
	char *mnt_point;
	sg_fs_device_type device_type; /* XXX this isn't an enum, it's a bit-mask */
	unsigned long long size;
	unsigned long long used;
	unsigned long long free;
	unsigned long long avail;
	unsigned long long total_inodes;
	unsigned long long used_inodes;
	unsigned long long free_inodes;
	unsigned long long avail_inodes;
	unsigned long long io_size;
	unsigned long long block_size;
	unsigned long long total_blocks;
	unsigned long long free_blocks;
	unsigned long long used_blocks;
	unsigned long long avail_blocks;
	time_t systime;
} sg_fs_stats;

__sg_public const char **sg_get_valid_filesystems(size_t *entries);
__sg_public sg_error sg_set_valid_filesystems(const char *valid_fs[]);

__sg_public sg_fs_stats *sg_get_fs_stats(size_t *entries);
__sg_public sg_fs_stats *sg_get_fs_stats_r(size_t *entries);
__sg_public sg_fs_stats *sg_get_fs_stats_diff(size_t *entries);
__sg_public sg_fs_stats *sg_get_fs_stats_diff_between(const sg_fs_stats *cur, const sg_fs_stats *last, size_t *entries);
#define sg_free_fs_stats(data) sg_free_stats_buf(data);

__sg_public int sg_fs_compare_device_name(const void *va, const void *vb);
__sg_public int sg_fs_compare_mnt_point(const void *va, const void *vb);

typedef struct {
	char *disk_name;
	unsigned long long read_bytes;
	unsigned long long write_bytes;
	time_t systime;
} sg_disk_io_stats;

__sg_public sg_disk_io_stats *sg_get_disk_io_stats(size_t *entries);
__sg_public sg_disk_io_stats *sg_get_disk_io_stats_r(size_t *entries);
__sg_public sg_disk_io_stats *sg_get_disk_io_stats_diff(size_t *entries);
__sg_public sg_disk_io_stats *sg_get_disk_io_stats_diff_between(const sg_disk_io_stats *cur, const sg_disk_io_stats *last, size_t *entries);
#define sg_free_disk_io_stats(data) sg_free_stats_buf(data);

__sg_public int sg_disk_io_compare_name(const void *va, const void *vb);
__sg_public int sg_disk_io_compare_traffic(const void *va, const void *vb);

typedef struct {
	char *interface_name;
	unsigned long long tx;
	unsigned long long rx;
	unsigned long long ipackets;
	unsigned long long opackets;
	unsigned long long ierrors;
	unsigned long long oerrors;
	unsigned long long collisions;
	time_t systime;
} sg_network_io_stats;

__sg_public sg_network_io_stats *sg_get_network_io_stats(size_t *entries);
__sg_public sg_network_io_stats *sg_get_network_io_stats_r(size_t *entries);
__sg_public sg_network_io_stats *sg_get_network_io_stats_diff(size_t *entries);
__sg_public sg_network_io_stats *sg_get_network_io_stats_diff_between(const sg_network_io_stats *cur, const sg_network_io_stats *last, size_t *entries);
#define sg_free_network_io_stats(data) sg_free_stats_buf(data);

__sg_public int sg_network_io_compare_name(const void *va, const void *vb);

typedef enum {
	SG_IFACE_DUPLEX_FULL,
	SG_IFACE_DUPLEX_HALF,
	SG_IFACE_DUPLEX_UNKNOWN
} sg_iface_duplex;

typedef enum {
	SG_IFACE_DOWN = 0,
	SG_IFACE_UP
} sg_iface_updown;

typedef struct {
	char *interface_name;
	unsigned long long speed;	/* In factor/sec */
	unsigned long long factor;
	sg_iface_duplex duplex;
	sg_iface_updown up;
	time_t systime;
} sg_network_iface_stats;

__sg_public sg_network_iface_stats *sg_get_network_iface_stats(size_t *entries);
__sg_public sg_network_iface_stats *sg_get_network_iface_stats_r(size_t *entries);
#define sg_free_network_iface_stats(data) sg_free_stats_buf(data);

__sg_public int sg_network_iface_compare_name(const void *va, const void *vb);

typedef struct {
	unsigned long long pages_pagein;
	unsigned long long pages_pageout;
	time_t systime;
} sg_page_stats;

__sg_public sg_page_stats *sg_get_page_stats(size_t *entries);
__sg_public sg_page_stats *sg_get_page_stats_r(size_t *entries);
__sg_public sg_page_stats *sg_get_page_stats_diff(size_t *entries);
__sg_public sg_page_stats *sg_get_page_stats_diff_between(const sg_page_stats *now, const sg_page_stats *last, size_t *entries);
#define sg_free_page_stats(data) sg_free_stats_buf(data);

/* Recommend to add SG_PROCESS_STATE_IDLE */
typedef enum {
	SG_PROCESS_STATE_RUNNING,
	SG_PROCESS_STATE_SLEEPING,
	SG_PROCESS_STATE_STOPPED,
	SG_PROCESS_STATE_ZOMBIE,
	SG_PROCESS_STATE_UNKNOWN
} sg_process_state;

typedef struct {
	char *process_name;
	char *proctitle;

	pid_t pid;
	pid_t parent; /* Parent pid */
	pid_t pgid;   /* process id of process group leader */
	pid_t sessid; /* session id of the session the process belongs to */


/* Windows does not have uid_t or gid_t types */
#ifndef WIN32
	uid_t uid;
	uid_t euid;
	gid_t gid;
	gid_t egid;
#else
	int uid;
	int euid;
	int gid;
	int egid;
#endif

	unsigned long long context_switches;
	unsigned long long voluntary_context_switches;
	unsigned long long involuntary_context_switches;
	unsigned long long proc_size; /* in bytes */
	unsigned long long proc_resident; /* in bytes */
	time_t start_time; /* When was the process started */
	time_t time_spent; /* time running in seconds */
	double cpu_percent;
	int nice;
	sg_process_state state;

	time_t systime;
} sg_process_stats;

__sg_public sg_process_stats *sg_get_process_stats(size_t *entries);
__sg_public sg_process_stats *sg_get_process_stats_r(size_t *entries);
#define sg_free_process_stats(data) sg_free_stats_buf(data);

__sg_public int sg_process_compare_name(const void *va, const void *vb);
__sg_public int sg_process_compare_pid(const void *va, const void *vb);
__sg_public int sg_process_compare_uid(const void *va, const void *vb);
__sg_public int sg_process_compare_gid(const void *va, const void *vb);
__sg_public int sg_process_compare_size(const void *va, const void *vb);
__sg_public int sg_process_compare_res(const void *va, const void *vb);
__sg_public int sg_process_compare_cpu(const void *va, const void *vb);
__sg_public int sg_process_compare_time(const void *va, const void *vb);

typedef struct {
	unsigned long long total;
	unsigned long long running;
	unsigned long long sleeping;
	unsigned long long stopped;
	unsigned long long zombie;
	unsigned long long unknown;
	time_t systime;
} sg_process_count;

typedef enum sg_process_count_source {
	sg_entire_process_count,
	sg_last_process_count
} sg_process_count_source;

__sg_public sg_process_count *sg_get_process_count_of(sg_process_count_source pcs);
__sg_public sg_process_count *sg_get_process_count_r(sg_process_stats const * whereof);
#define sg_get_process_count() sg_get_process_count_of(sg_entire_process_count)
#define sg_free_process_count(data) sg_free_stats_buf(data);

#if 0
void sg_log_init(const char *properties_pfx, const char *env_name, const char *argv0);
#else
#define sg_log_init(properties_pfx, env_name, argv0) \
	do { (void)(properties_pfx); (void)(env_name); (void)(argv0); } while(0);
#endif

#ifdef __cplusplus
}
#endif

#endif
