/*
 * This file is part of Jehanne.
 *
 * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
 *
 * This is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, version 3 of the License.
 *
 * Jehanne 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 General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Jehanne.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * This API is designed to help porting other POSIX compliant C
 * libraries (such as newlib or musl) to Jehanne, so that they can be
 * used to port further software.
 *
 * POSIX_* functions provide a facade between the Jehanne's libc and
 * the POSIX 1-2008 semantics.
 *
 * libposix_* functions provide non-standard widely used functions
 * (eg. libposix_getdents) and configuration points.
 *
 * #include <u.h>
 * #include <posix.h>
 *
 * Defining _LIBPOSIX_H before the include allow you to just get
 * data structure definition.
 */

#ifndef _LIBPOSIX_DEF
#define _LIBPOSIX_DEF

struct timespec
{
	long	tv_sec;
	long	tv_nsec;
};

/* dirent alias of stat(5) message.
 * We (ab)use the fact that both 9P and Jehanne are little endian.
 * With the new file protocol this might change.
 */
struct __attribute__((__packed__)) dirent
{
	unsigned short	d_reclen;
	unsigned short	__pad1__;	/* don't use */
	unsigned int	__pad2__;	/* don't use */
	unsigned char	d_type;
	unsigned int	d_version;
	unsigned long	d_ino;
	unsigned int	d_mode;
	unsigned int	__pad3__;	/* don't use */
	unsigned int	__pad4__;	/* don't use */
	unsigned long	d_filesize;
	unsigned short	d_namlen;
	char		d_name[];
};

#define _DIRENT_HAVE_D_RECLEN
#define _DIRENT_HAVE_D_TYPE
#define _DIRENT_HAVE_D_VERSION
#define _DIRENT_HAVE_D_MODE
#define _DIRENT_HAVE_D_FILESIZE
#define _DIRENT_HAVE_D_NAMLEN
#undef _DIRENT_HAVE_D_OFF

#define	DT_UNKNOWN	 0
#define	DT_FIFO		 1
#define	DT_CHR		 2
#define	DT_DIR		 4
#define	DT_BLK		 6
#define	DT_REG		 8
#define	DT_LNK		10
#define	DT_SOCK		12
#define	DT_WHT		14

/* getrusage who */
typedef enum PosixRUsages
{
	PosixRUsageSelf = 0,
	PosixRUsageChildren = 1,

	PosixRUsageUnknown = -1
} PosixRUsages;

/* errno values */
#define _ERRNO_H	// skip the Posix part, we just need the enum
#include <apw/errno.h>

/* signals */
typedef unsigned long PosixSignalMask;


typedef enum PosixSigProcMaskAction
{
	PosixSPMSetMask	= 0,
	PosixSPMBlock	= 1,
	PosixSPMUnblock	= 2
} PosixSigProcMaskAction;

#define PosixNumberOfSignals (sizeof(PosixSignalMask)*7)
typedef enum PosixSignals
{
	PosixSIGABRT = 1,
	PosixSIGALRM,
	PosixSIGBUS,
	PosixSIGCHLD,
	PosixSIGCONT,
	PosixSIGFPE,
	PosixSIGHUP,
	PosixSIGILL,
	PosixSIGINT,
	PosixSIGKILL,
	PosixSIGPIPE,
	PosixSIGQUIT,
	PosixSIGSEGV,
	PosixSIGSTOP,
	PosixSIGTERM,
	PosixSIGTSTP,
	PosixSIGTTIN,
	PosixSIGTTOU,
	PosixSIGUSR1,
	PosixSIGUSR2,
	PosixSIGPOLL,
	PosixSIGPROF,
	PosixSIGSYS,
	PosixSIGTRAP,
	PosixSIGURG,
	PosixSIGVTALRM,
	PosixSIGXCPU,
	PosixSIGXFSZ,

	/* Optional Signals */
	PosixSIGIOT,
	PosixSIGEMT,
	PosixSIGSTKFLT,
	PosixSIGIO,
	PosixSIGPWR,
	PosixSIGINFO,
	PosixSIGLOST,
	PosixSIGWINCH,
	PosixSIGUNUSED,

	PosixSIGRTMIN,
	PosixSIGRTMAX = PosixNumberOfSignals
} PosixSignals;

typedef enum PosixSigActionFlags
{
	/* supported flags */
	PosixSAFSigInfo		= 1<<0,
	PosixSAFRestart		= 1<<1,
	PosixSAFNoChildrenStop	= 1<<2,
	PosixSAFResetHandler	= 1<<3,
	PosixSAFNoChildrenWait	= 1<<4,

	/* ignored flags */
	PosixSAFNoDefer		= 1<<16,	/* notes are not reentrant */
	PosixSAFOnStack		= 1<<17,
	PosixSAFDisable		= 1<<18
} PosixSigActionFlags;

typedef enum PosixSigInfoCodes
{
	PosixSIUser = 1,
	PosixSIQueue,
	PosixSITimer,
	PosixSIAsyncIO,
	PosixSIMsgQueued,

	PosixSIFaultMapError,
	PosixSIFaultAccessError,

	PosixSIChildExited,
	PosixSIChildKilled,
	PosixSIChildDumped,
	PosixSIChildTrapped,
	PosixSIChildStopped,
	PosixSIChildContinued
} PosixSigInfoCodes;

union sigval {
	int		sival_int;	/* Integer signal value */
	void*		sival_ptr;	/* Pointer signal value */
	void*		_si_addr;	/* Address of faulting address */
	int		_si_status;	/* Child exit status */
	uintptr_t	_sival_raw;	/* Raw value */
};

struct sigevent {
	int		sigev_notify;               /* Notification type */
	int		sigev_signo;                /* Signal number */
	union sigval	sigev_value;                /* Signal value */
	void		(*sigev_notify_function)( union sigval );
                                               /* Notification function */
	long		*sigev_notify_attributes;    /* Notification Attributes */
};

typedef struct {
	int		si_signo;	/* Signal number */
	int		si_code;	/* Cause of the signal */
	int		si_errno;
	int		si_pid;		/* Pid of sender */
	int		si_uid;		/* Uid of sender */
	union sigval	si_value;	/* Signal value */
} PosixSignalInfo;

#define si_addr si_value._si_addr
#define si_status si_value._si_status

typedef void (*PosixSigHandler)(int);
typedef void (*PosixSigAction)(int, PosixSignalInfo *, void * );

struct sigaction {
	int		sa_flags;       /* Special flags to affect behavior of signal */
	PosixSignalMask	sa_mask;        /* Additional set of signals to be blocked */
					/*   during execution of signal-catching */
					/*   function. */
	union {
		PosixSigHandler	_handler;	/* SIG_DFL, SIG_IGN, or pointer to a function */
		PosixSigAction	_sigaction;
	} _signal_handlers;
};

#define sa_handler    _signal_handlers._handler
#define sa_sigaction  _signal_handlers._sigaction

typedef enum PosixFDCmds
{
	PosixFDCDupFD = 1,
	PosixFDCDupFDCloseOnExec,
	PosixFDCGetFD,
	PosixFDCSetFD,
	PosixFDCGetFL,
	PosixFDCSetFL
} PosixFDCmds;

#endif /* _LIBPOSIX_DEF */

#ifndef _LIBPOSIX_H
#define _LIBPOSIX_H

typedef unsigned long clock_t;

#define __POSIX_EXIT_PREFIX "posix error "
#define __POSIX_EXIT_SIGNAL_PREFIX "terminated by posix signal "
#define __POSIX_SIGNAL_PREFIX "posix: "

extern unsigned int POSIX_alarm(int *errnop, unsigned int seconds);
extern int POSIX_access(int *errnop, const char *path, int amode);
extern int POSIX_dup(int *errnop, int fildes);
extern int POSIX_dup2(int *errnop, int fildes, int fildes2);
extern void POSIX_exit(int code) __attribute__((noreturn));
extern int POSIX_chmod(int *errnop, const char *path, int mode);
extern int POSIX_fchmodat(int *errnop, int fd, const char *path, long mode, int flag);
extern int POSIX_chown(int *errnop, const char *pathname, int owner, int group);
extern int POSIX_fchownat(int *errnop, int fd, const char *path, int owner, int group, int flag);
extern int POSIX_lchown(int *errnop, const char *path, int owner, int group);
extern int POSIX_chdir(int *errnop, const char *path);
extern int POSIX_fchdir(int *errnop, int fd);
extern int POSIX_mkdir(int *errnop, const char *path, int mode);
extern int POSIX_close(int *errnop, int file);
extern int POSIX_execve(int *errnop, const char *name, char * const*argv, char * const*env);
extern int POSIX_fork(int *errnop);
extern int POSIX_getrusage(int *errnop, PosixRUsages who, void *r_usage);
extern char* POSIX_getcwd(int *errnop, char *buf, int size);
extern char* POSIX_getlogin(int *errnop);
extern int POSIX_getlogin_r(int *errnop, char *name, int namesize);
extern char* POSIX_getpass(int *errnop, const char *prompt);
extern int POSIX_getpid(int *errnop);
extern int POSIX_getppid(int *errnop);
extern int POSIX_isatty(int *errnop, int file);
extern int POSIX_kill(int *errnop, int pid, int sig);
extern int POSIX_link(int *errnop, const char *old, const char *new);
extern off_t POSIX_lseek(int *errnop, int fd, off_t pos, int whence);
extern int POSIX_open(int *errnop, const char *name, int flags, int mode);
extern long POSIX_pread(int *errnop, int fd, char *buf, size_t len, long offset);
extern long POSIX_pwrite(int *errnop, int fd, const char *buf, size_t len, long offset);
extern long POSIX_read(int *errnop, int fd, char *buf, size_t len);
extern int POSIX_stat(int *errnop, const char *file, void *stat);
extern int POSIX_fstat(int *errnop, int file, void *stat);
extern int POSIX_lstat(int *errnop, const char *file, void *stat);
extern clock_t POSIX_times(int *errnop, void *tms);
extern int POSIX_unlink(int *errnop, const char *name);
extern int POSIX_wait(int *errnop, int *status);
extern int POSIX_waitpid(int *errnop, int pid, int *status, int options);
extern long POSIX_write(int *errnop, int fd, const void *buf, size_t len);
extern int POSIX_gettimeofday(int *errnop, void *timeval, void *timezone);
extern char* POSIX_getenv(int *errnop, const char *name);
extern int POSIX_setenv(int *errno, const char *name, const char *value, int overwrite);
extern int POSIX_unsetenv(int *errnop, const char *name);
extern void *POSIX_sbrk(int *errnop, ptrdiff_t incr);
extern void * POSIX_malloc(int *errnop, size_t size);
extern void *POSIX_realloc(int *errnop, void *ptr, size_t size);
extern void *POSIX_calloc(int *errnop, size_t nelem, size_t size);
extern void POSIX_free(void *ptr);
extern unsigned int POSIX_sleep(unsigned int seconds);
extern int POSIX_pipe(int *errnop, int fildes[2]);
extern int POSIX_umask(int *errnop, int mask);
extern int POSIX_fcntl(int *errnop, int fd, PosixFDCmds cmd, uintptr_t arg);

extern int POSIX_sigaddset(int *errnop, PosixSignalMask *set, int signo);
extern int POSIX_sigdelset(int *errnop, PosixSignalMask *set, int signo);
extern int POSIX_sigismember(int *errnop, const PosixSignalMask *set, int signo);
extern int POSIX_sigfillset(int *errnop, PosixSignalMask *set);
extern int POSIX_sigemptyset(int *errnop, PosixSignalMask *set);
extern int POSIX_sigprocmask(int *errnop, PosixSigProcMaskAction how, const PosixSignalMask *set, PosixSignalMask *oset);
extern int POSIX_sigpending(int *errnop, PosixSignalMask *set);
extern int POSIX_sigsuspend(int *errnop, const PosixSignalMask *mask);
extern int POSIX_sigaction(int *errnop, int signo, const struct sigaction *act, struct sigaction *old);
extern int POSIX_sigtimedwait(int *errnop, const PosixSignalMask *set, PosixSignalInfo *info, const struct timespec *timeout);
extern int POSIX_sigqueue(int *errnop, int pid, int signo, const union sigval value);

extern int POSIX_getuid(int *errnop);
extern int POSIX_geteuid(int *errnop);
extern int POSIX_setuid(int *errnop, int uid);
extern int POSIX_seteuid(int *errnop, int euid);
extern int POSIX_setreuid(int *errnop, int ruid, int euid);
extern int POSIX_getgid(int *errnop);
extern int POSIX_getegid(int *errnop);
extern int POSIX_setgid(int *errnop, int gid);
extern int POSIX_setegid(int *errnop, int egid);
extern int POSIX_setregid(int *errnop, int rgid, int egid);
extern int POSIX_getpgrp(int *errnop);
extern int POSIX_getpgid(int *errnop, int pid);
extern int POSIX_setpgid(int *errnop, int pid, int pgid);
extern int POSIX_getsid(int *errnop, int pid);
extern int POSIX_setsid(int *errnop);

extern int POSIX_tcgetpgrp(int *errnop, int fd);
extern int POSIX_tcsetpgrp(int *errnop, int fd, int pgrp);

extern int libposix_getdents(int *errnop, int fd, char *buf, int buf_bytes);
extern PosixError libposix_translate_kernel_errors(const char *msg);

/* Library initialization
 */

/* Initialize libposix. Should call
 *
 *	libposix_define_errno to set the value of each PosixError
 *	libposix_define_at_fdcwd to set the value of AT_FDCWD (for fchmodat)
 *	libposix_translate_error to translate error strings to PosixError
 *	libposix_set_stat_reader
 *	libposix_set_tms_reader
 *	libposix_set_timeval_reader
 *	libposix_set_timezone_reader
 */
typedef void (*PosixInit)(int argc, char *argv[]);
extern void libposix_init(int argc, char *argv[], PosixInit init) __attribute__((noreturn));

/* Translate an error string to a PosixError, in the context of the
 * calling function (typically a pointer to a POSIX_* function).
 *
 * Must return 0 if unable to identify a PosixError.
 *
 * Translators are tried in reverse registration order.
 */
typedef PosixError (*PosixErrorTranslator)(char* error, uintptr_t caller);

/* Register a translation between Jehanne error strings and PosixErrors.
 * If caller is not zero, it is a pointer to the calling
 * functions to consider. Otherwise the translation will be tried whenever
 * for any caller, after the specialized ones.
 */
extern int libposix_translate_error(PosixErrorTranslator translation, uintptr_t caller);

/* define the value of AT_FDCWD according to the library headers */
extern int libposix_define_at_fdcwd(int AT_FDCWD);

/* define the value of a specific PosixError according to the library headers */
extern int libposix_define_errno(PosixError e, int errno);

/* Map a Dir to the stat structure expected by the library.
 *
 * Must return 0 on success or a PosixError on failure.
 */
typedef PosixError (*PosixStatReader)(void *statp, const Dir *dir);

extern int libposix_set_stat_reader(PosixStatReader reader);

/* Map a time provided by times() tms structure
 * expected by the library.
 *
 * Must return 0 on success or a PosixError on failure.
 */
typedef PosixError (*PosixTMSReader)(void *tms, 
	unsigned int proc_userms, unsigned int proc_sysms,
	unsigned int children_userms, unsigned int children_sysms);

extern int libposix_set_tms_reader(PosixTMSReader reader);

/* Map a time provided by gmtime() or localtime() to a timeval
 * expected by the library.
 *
 * Must return 0 on success or a PosixError on failure.
 */
typedef PosixError (*PosixTimevalReader)(void *timeval, const Tm *time);

extern int libposix_set_timeval_reader(PosixTimevalReader reader);

/* Map a time provided by gmtime() or localtime() to a timezone
 * expected by the library.
 *
 * Must return 0 on success or a PosixError on failure.
 */
typedef PosixError (*PosixTimezoneReader)(void *timezone, const Tm *time);

extern int libposix_set_timezone_reader(PosixTimezoneReader reader);

/* Map the POSIX_open flag and mode to Jehanne's arguments to open
 * or create. It's also used in chmod to translate mode.
 *
 * omode is a pointer to the open/create omode argument
 * cperm is a pointer to the create perm argument that must be filled
 * only if omode is nil or O_CREATE has been set in mode.
 *
 * Must return 0 on success or a PosixError on failure.
 *
 * In flag:
 * - O_CLOEXEC must be mapped to OCEXEC in omode
 * - O_TRUNC must be mapped to OTRUNC in omode
 * - O_APPEND|O_CREAT must be mapped to DMAPPEND in cperm
 * - O_APPEND without O_CREAT must return PosixENOTSUP
 * - O_EXCL|O_CREAT must be mapped to DMEXCL in cperm
 * - O_EXCL without O_CREAT it must return PosixENOTSUP
 * - O_SEARCH must be mapped to OEXEC|DMDIR in omode
 * - O_DIRECTORY|O_CREAT must return PosixEINVAL
 * - O_DIRECTORY without O_CREAT must be mapped to DMDIR in omode
 * - O_DSYNC, O_NOCTTY, O_NOFOLLOW, O_NONBLOCK, O_RSYNC, O_SYNC
 *   and O_TTY_INIT must be ignored
 * - S_ISUID, S_ISGID, S_ISVTX must be ignored
 */
typedef PosixError (*PosixOpenTranslator)(int flag, int mode, long *omode, long *cperm);

extern int libposix_translate_open(PosixOpenTranslator translation);

extern int libposix_translate_seek_whence(int seek_set, int seek_cur, int seek_end);

extern int libposix_translate_access_mode(int f_ok, int r_ok, int w_ok, int x_ok);

/* Map the main exit status received by POSIX_exit to the exit status
 * string for Jehanne.
 *
 * Must return nil if unable to translate the status, so that the
 * default translation strategy will be adopted.
 */
typedef char* (*PosixExitStatusTranslator)(int status);

extern int libposix_translate_exit_status(PosixExitStatusTranslator translator);

/* Execute process disposition (executed when main returns)
 */
typedef void (*PosixProcessDisposer)(int status);

extern int libposix_on_process_disposition(PosixProcessDisposer dispose);

/* Enable SIGCHLD emulation
 */
extern int libposix_emulate_SIGCHLD(void);

/* Define of WCONTINUED, WNOHANG and WUNTRACED bit flags.
 *
 * Note that WCONTINUED and WUNTRACED are not yet supported by libposix
 * and thus defining them cause an error.
 */
int libposix_set_wait_options(int wcontinued, int wnohang, int wuntraced);

#endif /* _LIBPOSIX_H */
