/*
 * Copyright (c) 2019,24 Andrew G. Morgan <morgan@kernel.org>
 *
 * This header, and the -lpsx library, provide a number of things to
 * support POSIX semantics for syscalls associated with the pthread
 * library. For any code that references the psx_syscall{3,6}
 * functions, linking this code is as follows:
 *
 *     ld ... -lpsx -lpthread
 * or,
 *     gcc ... -lpsx -lpthread
 *
 * NOTE: linking with -lcap to make it silently aware of the psx
 *    mechanism is tricky. You have to envelope the -lpsx part with
 *    the following linker flags:
 *
 *     ld ... --no-as-needed --whole-archive -lpsx --no-whole-archive --as-needed -lpthread
 * or,
 *     gcc ... -Wl,--no-as-needed -Wl,--whole-archive -lpsx -Wl,--no-whole-archive -Wl,--as-needed -lpthread
 *
 *    These options are provided in the
 *    https://en.wikipedia.org/wiki/Pkg-config libpsx.pc file
 *    distributed with the library.
 *
 *    FYI Earlier versions of libpsx relied on gcc
 *    ... -Wl,--wrap=pthread_create linkage in all cases, but since
 *    libpsx-2.72 the library can work with non pthread threads (LWP)
 *    under Linux and such wrapping is no longer needed. That being
 *    said, for compatibility reasons such linking is still supported.
 *
 * glibc provides a subset of this functionality natively through the
 * nptl:setxid mechanism and could implement psx_syscall() directly
 * using that style of functionality but, as of 2019-11-30, the setxid
 * mechanism is limited to 9 specific set*() syscalls that do not
 * support the syscall6 API (needed for prctl functions and the ambient
 * capabilities set for example).
 */

#ifndef _SYS_PSX_SYSCALL_H
#define _SYS_PSX_SYSCALL_H

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Programmatic way to recognize feature set.
 */
#define LIBPSX_MAJOR 2
#define LIBPSX_MINOR 75

/*
 * psx_syscall performs the specified syscall on all psx registered
 * threads. The mechanism by which this occurs is much less efficient
 * than a standard system call on Linux, so it should only be used
 * when POSIX semantics are required to change process relevant
 * security state.
 *
 * Glibc has native support for POSIX semantics on setgroups() and the
 * 8 set*[gu]id() functions. So, there is no need to use psx_syscall()
 * for these calls. This call exists for all the other system calls
 * that need to maintain parity on all pthreads of a program.
 *
 * Some macrology is used to allow the caller to provide only as many
 * arguments as needed, thus psx_syscall() cannot be used as a
 * function pointer. For those situations, we define psx_syscall3()
 * and psx_syscall6().
 */
#define psx_syscall(syscall_nr, ...) \
    __psx_syscall(syscall_nr, __VA_ARGS__, (long int) 6, (long int) 5, \
		  (long int) 4, (long int) 3, (long int) 2, \
		  (long int) 1, (long int) 0)
long int __psx_syscall(long int syscall_nr, ...);
long int psx_syscall3(long int syscall_nr,
		      long int arg1, long int arg2, long int arg3);
long int psx_syscall6(long int syscall_nr,
		      long int arg1, long int arg2, long int arg3,
		      long int arg4, long int arg5, long int arg6);

/*
 * This function should be used by systems to obtain pointers to the
 * two syscall functions provided by the PSX library. A linkage trick
 * is to define this function as weak in a library that can optionally
 * use libpsx and then, should the caller link -lpsx, that library can
 * implicitly use these POSIX semantics syscalls. See libcap for an
 * example of this usage.
 */
void psx_load_syscalls(long int (**syscall_fn)(long int,
					       long int, long int, long int),
		       long int (**syscall6_fn)(long int,
						long int, long int, long int,
						long int, long int, long int));

/*
 * psx_sensitivity_t holds the level of paranoia for non-POSIX syscall
 * behavior. The default is PSX_IGNORE: which is best effort - no
 * enforcement; PSX_WARNING will dump to stderr a warning when a
 * syscall's results differ; PSX_ERROR will dump info as per
 * PSX_WARNING and generate a SIGSYS. The current mode can be set with
 * psx_set_sensitivity().
 */
typedef enum {
    PSX_IGNORE = 0,
    PSX_WARNING = 1,
    PSX_ERROR = 2,
} psx_sensitivity_t;

/*
 * psx_set_sensitivity sets the current sensitivity of the PSX
 * mechanism.  The function returns 0 on success and -1 if the
 * requested level is invalid.
 */
int psx_set_sensitivity(psx_sensitivity_t level);

#ifdef __cplusplus
}
#endif

#endif /* _SYS_PSX_SYSCALL_H */
