/*
 * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
 * Copyright (c) 2009-2013 Cyril Hrubis chrubis@suse.cz
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 *
 * http://www.sgi.com
 *
 * For further information regarding this notice, see:
 *
 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
 */

#ifndef __TEST_H__
#define __TEST_H__

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include "compiler.h"

#include "safe_file_ops.h"
#include "tst_checkpoint.h"
#include "tst_process_state.h"
#include "tst_resource.h"
#include "tst_res_flags.h"

/* virt types for tst_is_virt() */
#define VIRT_XEN	1	/* xen dom0/domU */

/*
 * Ensure that NUMSIGS is defined.
 * It should be defined in signal.h or sys/signal.h on
 * UNICOS/mk and IRIX systems.   On UNICOS systems,
 * it is not defined, thus it is being set to UNICOS's NSIG.
 * Note:  IRIX's NSIG (signals are 1-(NSIG-1))
 *      is not same meaning as UNICOS/UMK's NSIG  (signals 1-NSIG)
 */
#ifndef NUMSIGS
#define NUMSIGS NSIG
#endif


/* defines for unexpected signal setup routine (set_usig.c) */
#define FORK    1		/* SIGCLD is to be ignored */
#define NOFORK  0		/* SIGCLD is to be caught */
#define DEF_HANDLER SIG_ERR	/* tells set_usig() to use default signal handler */

/*
 * The following defines are used to control tst_res and t_result reporting.
 */

#define TOUTPUT	   "TOUTPUT"		/* The name of the environment variable */
					/* that can be set to one of the following */
					/* strings to control tst_res output */
					/* If not set, TOUT_VERBOSE_S is assumed */

/*
 * fork() can't be used on uClinux systems, so use FORK_OR_VFORK instead,
 * which will run vfork() on uClinux.
 * mmap() doesn't support MAP_PRIVATE on uClinux systems, so use
 * MAP_PRIVATE_EXCEPT_UCLINUX instead, which will skip the option on uClinux.
 * If MAP_PRIVATE really is required, the test can not be run on uClinux.
 */
#ifdef UCLINUX
# define FORK_OR_VFORK			tst_vfork
# define MAP_PRIVATE_EXCEPT_UCLINUX	0
/* tst_flush() + vfork() */
pid_t tst_vfork(void);
#else
# define FORK_OR_VFORK			tst_fork
# define MAP_PRIVATE_EXCEPT_UCLINUX	MAP_PRIVATE
#endif

/*
 * Macro to use for making functions called only once in
 * multi-threaded tests such as init or cleanup function.
 * The first call to @name_fn function by any thread shall
 * call the @exec_fn. Subsequent calls shall not call @exec_fn.
 * *_fn functions must not take any arguments.
 */
#define TST_DECLARE_ONCE_FN(name_fn, exec_fn)				\
	void name_fn(void)						\
	{								\
		static pthread_once_t ltp_once = PTHREAD_ONCE_INIT;	\
		pthread_once(&ltp_once, exec_fn);			\
	}

/*
 * lib/forker.c
 */
extern int Forker_pids[];
extern int Forker_npids;

/* lib/tst_res.c */
const char *strttype(int ttype);
void tst_res_(const char *file, const int lineno, int ttype,
	const char *fname, const char *arg_fmt, ...)
	__attribute__ ((format (printf, 5, 6)));

#define tst_res(ttype, fname, arg_fmt, ...) \
	tst_res_(__FILE__, __LINE__, (ttype), (fname), \
		 (arg_fmt), ##__VA_ARGS__)

void tst_resm_(const char *file, const int lineno, int ttype,
	const char *arg_fmt, ...)
	__attribute__ ((format (printf, 4, 5)));
#define tst_resm(ttype, arg_fmt, ...) \
	tst_resm_(__FILE__, __LINE__, (ttype), \
		  (arg_fmt), ##__VA_ARGS__)

void tst_resm_hexd_(const char *file, const int lineno, int ttype,
	const void *buf, size_t size, const char *arg_fmt, ...)
	__attribute__ ((format (printf, 6, 7)));
#define tst_resm_hexd(ttype, buf, size, arg_fmt, ...) \
	tst_resm_hexd_(__FILE__, __LINE__, (ttype), (buf), (size), \
		       (arg_fmt), ##__VA_ARGS__)

void tst_brk_(const char *file, const int lineno, int ttype,
	const char *fname, void (*func)(void), const char *arg_fmt, ...)
	__attribute__ ((format (printf, 6, 7)));
#define tst_brk(ttype, fname, func, arg_fmt, ...) \
	tst_brk_(__FILE__, __LINE__, (ttype), (fname), (func), \
		 (arg_fmt), ##__VA_ARGS__)

void tst_brkm_(const char *file, const int lineno, int ttype,
	void (*func)(void), const char *arg_fmt, ...)
	__attribute__ ((format (printf, 5, 6))) LTP_ATTRIBUTE_NORETURN;
#define tst_brkm(ttype, func, arg_fmt, ...) \
	tst_brkm_(__FILE__, __LINE__, (ttype), (func), \
		  (arg_fmt), ##__VA_ARGS__)

void tst_require_root(void (*func)(void));
int  tst_environ(void);
void tst_exit(void) LTP_ATTRIBUTE_NORETURN;
void tst_flush(void);

/*
 * tst_flush() + fork
 * NOTE: tst_fork() will reset T_exitval to 0 for child process.
 */
pid_t tst_fork(void);

/* lib/tst_res.c */
/*
 * In case we need do real test work in child process parent process can use
 * tst_record_childstatus() to make child process's test results propagated to
 * parent process correctly.
 *
 * The child can use tst_resm(), tst_brkm() followed by the tst_exit() or
 * plain old exit() (with TPASS, TFAIL and TBROK).
 *
 * WARNING: Be wary that the child cleanup function passed to tst_brkm()
 *          must clean only resources the child has allocated. E.g. the
 *          child cleanup is different function from the parent cleanup.
 */
void tst_record_childstatus(void (*cleanup)(void), pid_t child);

extern int tst_count;

/* lib/tst_sig.c */
void tst_sig(int fork_flag, void (*handler)(), void (*cleanup)());

/* lib/tst_tmpdir.c */

/* tst_tmpdir()
 *
 * Create a unique temporary directory and chdir() to it. It expects the caller
 * to have defined/initialized the TCID/TST_TOTAL global variables.
 * The TESTDIR global variable will be set to the directory that gets used
 * as the testing directory.
 *
 * NOTE: This function must be called BEFORE any activity that would require
 * CLEANUP.  If tst_tmpdir() fails, it cleans up afer itself and calls
 * tst_exit() (i.e. does not return).
 */
void tst_tmpdir(void);
/* tst_rmdir()
 *
 * Recursively remove the temporary directory created by tst_tmpdir().
 * This function is intended ONLY as a companion to tst_tmpdir().
 */
void tst_rmdir(void);
/* tst_get_tmpdir()
 *
 * Return a copy of the test temp directory as seen by LTP. This is for
 * path-oriented tests like chroot, etc, that may munge the path a bit.
 *
 * FREE VARIABLE AFTER USE IF IT IS REUSED!
 */
char *tst_get_tmpdir(void);
/*
 * Returns 1 if temp directory was created.
 */
int tst_tmpdir_created(void);

/* lib/get_high_address.c */
char *get_high_address(void);

/* lib/tst_kvercmp.c */
void tst_getkver(int *k1, int *k2, int *k3);
int tst_kvercmp(int r1, int r2, int r3);

struct tst_kern_exv {
	char *dist_name;
	char *extra_ver;
};

int tst_kvercmp2(int r1, int r2, int r3, struct tst_kern_exv *vers);

enum {
	TST_BYTES = 1,
	TST_KB = 1024,
	TST_MB = 1048576,
	TST_GB = 1073741824,
};

/* lib/tst_fs_has_free.c
 *
 * @path: path is the pathname of any file within the mounted file system
 * @mult: mult should be TST_KB, TST_MB or TST_GB
 * the required free space is calculated by @size * @mult
 */
int tst_fs_has_free(void (*cleanup)(void), const char *path,
		    unsigned int size, unsigned int mult);

int tst_is_virt(int virt_type);

/* lib/self_exec.c */
void maybe_run_child(void (*child)(), const char *fmt, ...);
int self_exec(const char *argv0, const char *fmt, ...);

/* Functions from lib/cloner.c */
int ltp_clone(unsigned long flags, int (*fn)(void *arg), void *arg,
		size_t stack_size, void *stack);
int ltp_clone7(unsigned long flags, int (*fn)(void *arg), void *arg,
		size_t stack_size, void *stack, ...);
int ltp_clone_malloc(unsigned long clone_flags, int (*fn)(void *arg),
		void *arg, size_t stacksize);
int ltp_clone_quick(unsigned long clone_flags, int (*fn)(void *arg),
		void *arg);
#define clone(...) use_the_ltp_clone_functions,do_not_use_clone

/* Function from lib/get_path.c */
int tst_get_path(const char *prog_name, char *buf, size_t buf_len);

/* lib/tst_cpu.c */
long tst_ncpus(void);
long tst_ncpus_max(void);

/* lib/tst_run_cmd.c
 *
 * vfork() + execvp() specified program.
 * @argv: a list of two (at least program name + NULL) or more pointers that
 * represent the argument list to the new program. The array of pointers
 * must be terminated by a NULL pointer.
 * @stdout_fd: file descriptor where to redirect stdout. Set -1 if
 * redirection is not needed.
 * @stderr_fd: file descriptor where to redirect stderr. Set -1 if
 * redirection is not needed.
 */
void tst_run_cmd_fds(void (cleanup_fn)(void),
			const char *const argv[],
			int stdout_fd,
			int stderr_fd);

/* Executes tst_run_cmd_fds() and redirects its output to a file
 * @stdout_path: path where to redirect stdout. Set NULL if redirection is
 * not needed.
 * @stderr_path: path where to redirect stderr. Set NULL if redirection is
 * not needed.
 */
void tst_run_cmd(void (cleanup_fn)(void),
		const char *const argv[],
		const char *stdout_path,
		const char *stderr_path);

/* lib/tst_mkfs.c
 *
 * @dev: path to a device
 * @fs_type: filesystem type
 * @fs_opts: NULL or NULL terminated array of extra mkfs options
 */
void tst_mkfs(void (cleanup_fn)(void), const char *dev,
	      const char *fs_type, const char *const fs_opts[]);

/*
 * Returns filesystem type to be used for the testing. Unless your test is
 * designed for specific filesystem you should use this function to the tested
 * filesytem.
 *
 * If TST_DEV_FS_TYPE is set the function returns it's content,
 * otherwise default fs type hardcoded in the library is returned.
 */
const char *tst_dev_fs_type(void);

/* lib/tst_device.c
 *
 * Acquires test device.
 *
 * Can be used only once, i.e. you cannot get two different devices.
 *
 * Looks for LTP_DEV env variable first (which may be passed by the test
 * driver or by a user) and returns just it's value if found.
 *
 * Otherwise creates a temp file and loop device.
 *
 * Note that you have to call tst_tmpdir() beforehand.
 *
 * Returns path to the device or NULL if it cannot be created.
 */
const char *tst_acquire_device(void (cleanup_fn)(void));

/* lib/tst_device.c
 * @dev: device path returned by the tst_acquire_device()
 */
void tst_release_device(void (cleanup_fn)(void), const char *dev);

/* lib/tst_fill_file.c
 *
 * Creates/ovewrites a file with specified pattern
 * @path: path to file
 * @pattern: pattern
 * @bs: block size
 * @bcount: blocks amount
 */
int tst_fill_file(const char *path, char pattern, size_t bs, size_t bcount);

/* lib/tst_net.c
 *
 * Return unused port
 */
unsigned short tst_get_unused_port(void (cleanup_fn)(void),
	unsigned short family, int type);

/* lib/tst_res.c
 * tst_strsig converts signal's value to corresponding string.
 * tst_strerrno converts errno to corresponding string.
 */
const char *tst_strsig(int sig);
const char *tst_strerrno(int err);

/* lib/tst_path_has_mnt_flags.c
 *
 * Check whether a path is on a filesystem that is mounted with
 * specified flags
 * @path: path to file, if path is NULL tst_tmpdir is used.
 * @flags: NULL or NULL terminated array of mount flags
 *
 * Return: 0..n - number of flags matched
 */
int tst_path_has_mnt_flags(void (cleanup_fn)(void),
		const char *path, const char *flags[]);

/*
 * lib/tst_fs_link_count.c
 *
 * Try to get maximum number of hard links to a regular file inside the @dir.
 *
 * Note: This number depends on the filesystem @dir is on.
 *
 * The code uses link(2) to create hard links to a single file until it gets
 * EMLINK or creates 65535 links.
 *
 * If limit is hit maximal number of hardlinks is returned and the the @dir is
 * filled with hardlinks in format "testfile%i" where i belongs to [0, limit)
 * interval.
 *
 * If no limit is hit (succed to create 65535 without error) or if link()
 * failed with ENOSPC or EDQUOT zero is returned previously created files are
 * removed.
 */
int tst_fs_fill_hardlinks(void (*cleanup) (void), const char *dir);

/*
 * lib/tst_fs_link_count.c
 *
 * Try to get maximum number of subdirectories in directory.
 *
 * Note: This number depends on the filesystem @dir is on.
 *
 * The code uses mkdir(2) to create directories in @dir until it gets EMLINK
 * or creates 65535 directories.
 *
 * If limit is hit the maximal number of subdirectories is returned and the
 * @dir is filled with subdirectories in format "testdir%i" where i belongs to
 * [0, limit - 2) interval (because each newly created dir has two links
 * allready the '.' and link from parent dir).
 *
 * If no limit is hit or mkdir() failed with ENOSPC or EDQUOT zero is returned
 * previously created directories are removed.
 *
 */
int tst_fs_fill_subdirs(void (*cleanup) (void), const char *dir);

/*
 * lib/tst_pid.c
 *
 * Get a pid value not used by the OS
 */
pid_t tst_get_unused_pid(void (*cleanup_fn) (void));

/*
 * lib/tst_pid.c
 *
 * Returns number of free pids by substarction of the number of pids
 * currently used ('ps -eT') from max_pids
 */
int tst_get_free_pids(void (*cleanup_fn) (void));

#ifdef TST_USE_COMPAT16_SYSCALL
#define TCID_BIT_SUFFIX "_16"
#elif  TST_USE_NEWER64_SYSCALL
#define TCID_BIT_SUFFIX "_64"
#else
#define TCID_BIT_SUFFIX ""
#endif
#define TCID_DEFINE(ID) char *TCID = (#ID TCID_BIT_SUFFIX)

#endif	/* __TEST_H__ */
