/*
 * $QNXLicenseC:  
 * Copyright 2007, QNX Software Systems. All Rights Reserved.
 *
 * You must obtain a written license from and pay applicable license fees to QNX 
 * Software Systems before you may reproduce, modify or distribute this software, 
 * or any work that includes all or part of this software.   Free development 
 * licenses are available for evaluation and non-commercial purposes.  For more 
 * information visit http://licensing.qnx.com or email licensing@qnx.com.
 *  
 * This file may contain contributions from others.  Please review this entire 
 * file for other proprietary rights or license notices, as well as the QNX 
 * Development Suite License Guide at http://licensing.qnx.com/license-guide/ 
 * for other information.
 * $
*/

#include <unistd.h>		// determines whether _POSIX_SPAWN is defined or not
#ifdef _POSIX_SPAWN

#include <spawn.h>
#include <sys/posix_spawn.h>

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/procmsg.h>
#include <sys/netmgr.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/part.h>
#include <pthread.h>
#include <gulliver.h>
#include <stddef.h>

#include <assert.h>


#ifndef ROUNDUP
#define ROUNDUP(x, a)		(((x) + ((a)-1)) & ~((a)-1))
#endif


/*
 * AKEY
 * 
 * see set_attrp()/get_attrp for description of use
*/
#define AKEY(_km_)	(0x031759f0 | (_km_))

/*
 * GET_ATTRP
 * SET_ATTRP
 * 
 * These macros wrap the get_attrp()/set_attrp() routines. For set we always use
 * KEY_MASK to enforce 8 byte alignment within this library however the get
 * operation will inspect the lower MIN_KEY_MASK bits of the incoming
 * 'posix_spawnattr_t' object pointer and if non zero, will extract those bits
 * for use as the key mask established during a set operation for the object.
 * This allows procmgr to use an alternate alignment constraint when processing
 * the _PROC_POSIX_SPAWN message. All user programs will however be forced to
 * the alignment established by KEY_MASK. 
*/
#define SET_ATTRP(a, b)		set_attrp((a), (b), KEY_MASK)
#define GET_ATTRP(a)		get_attrp(*(posix_spawnattr_t *)((unsigned int)(a) & ~MIN_KEY_MASK), \
									  ((unsigned int)(a) & MIN_KEY_MASK) ? (unsigned int)(a) & MIN_KEY_MASK : KEY_MASK)


/*
 * internal posix_spawnattr related types
*/

/*
 * _posix_spawnattr_partition_t
 * 
 * This structure is used internally to record the resource partitions a
 * posix_spawnattr_t attributes object has. These partition id's represent the
 * partitions that a spawned process which uses this object will be associated
 * with
*/
typedef struct
{
	uint32_t	size;				// size of the _posix_spawnattr_partition_t
//	uint32_t	dummy;				// for testing alignment of successive vsect structures (like example_t)
	part_list_t	part;				// list of partitions (sched and memory) to associate with
} _posix_spawnattr_partition_t;

/*
 * PARTITION_T_ADDR
 * 
 * This macro is used to get a pointer to the '_posix_spawnattr_partition_t' field of the
 * 'posix_spawnattr_t' object.
 * 
 * IMPORTANT: this macro IS NOT safe to use if 'attrp' == NULL
 */
#define PARTITION_T_ADDR(_attrp)	((_posix_spawnattr_partition_t *)&((SPAWNATTR_T_VSECT_ADDR(_attrp))->partition))

/*
 * PARTITION_T_SIZE
 * 
 * Specifies the 8 byte rounded up size of the _posix_spawnattr_parition_t structure
 * as allocated. If no partitions have been added, sizeof(_attrp->vsect.partition)
 * is returned. Once we actually allocate memory, it lays over the 'vsect.partition'
 * field and '_posix_spawnattr_partition_t.size' records the allocation.
 * 
 * IMPORTANT: this macros is safe to use if '_attrp' == NULL
*/
#define PARTITION_T_SIZE(_attrp)	ROUNDUP(_PARTITION_T_SIZE(((_posix_spawnattr_t *)_attrp)), 8)
#define _PARTITION_T_SIZE(_attrp)	(((_attrp != NULL) && ((PARTITION_T_ADDR(_attrp))->size > 0)) ? \
										(PARTITION_T_ADDR(_attrp))->size : \
										sizeof(_attrp->vsect.partition))

/*
 * the following example structure demonstrate how the variable size section can
 * be extended and is in fact used for testing.
 * 
 * When adding a new variable sized attribute, it should have a uint32_t 'size'
 * as the first field. Everything after that is attribute dependent but you will
 * need a reliable way of determining the number of entries in your structure.
 * The _posix_spawnattr_partition_t has an explicit field (in the part_list_t
 * that it uses) and this example structure calculates it from the size
 * 
 * Two macros are required for each new variable sized attribute, one to
 * calculate the 8 byte aligned size of the structure and one to calculate the
 * the address of the structure based on its location in the 'vsect' field.
 * 
 * This example shows how the addess of the _posix_spawnattr_example_t structure
 * is calculated from the address and size of the partition structure just before
 * it. By extension, a second example structure, say  _posix_spawnattr_example2_t
 * would use EXAMPLE2_T_ADDR() = EXAMPLE_T_ADDR() + EXAMPLE_T_SIZE().
 * 
 * The size returned should always be the 8 byte rounded up size so that we always
 * maintain 8 byte alignment of each successive variable size section. Within the
 * attribute structure itself, members will therefore be guaranteed to have their
 * structure defined alignment preserved (until be go 128 bit).
 * 
 * Since the vsect.example field costs only 8 bytes I leave it in for testing
 * purposes. There is no way to expose it to procnto so it is totally safe and
 * the support routines to add and get example values do not compile into proc.
 * 
 * Note that the first "real" variable sized attribute field to be added should
 * be placed BEFORE vsect.example
*/
typedef struct
{
	uint32_t size;
	uint64_t example[1];
} _posix_spawnattr_example_t;

/* the example field follows the partition field, hence we must consider the size of the partition section */
#define EXAMPLE_T_ADDR(_attrp)		((_posix_spawnattr_example_t *)(((uintptr_t)PARTITION_T_ADDR(_attrp)) + PARTITION_T_SIZE(_attrp)))

#define EXAMPLE_T_SIZE(_attrp)		ROUNDUP(_EXAMPLE_T_SIZE(((_posix_spawnattr_t *)_attrp)), 8)
#define _EXAMPLE_T_SIZE(_attrp)		(((_attrp != NULL) && ((EXAMPLE_T_ADDR(_attrp))->size > 0)) ? \
										(EXAMPLE_T_ADDR(_attrp))->size : \
										sizeof((_attrp)->vsect.example))


/*
 * _posix_spawnattr_t
 * 
 * Internal representation of the opaque posix_spawnattr_t.
 * A posix_spawnattr_t is simply a 'void *'. The static initializer
 * POSIX_SPAWNATTR_INIT as well as the posix_spawnattr_init() function will
 * set the contents of the user allocated posix_spawnattr_t object to NULL.
 * An attributes object initialized this way will have the same effect as
 * NULL for the posix_spawnattr_t parameter to posix_spawn() ... that is to
 * use default attributes.
 * 
 * Setting non default values for the posix_spawnattr_t object will cause
 * memory to be allocated on the very first _setxxx() call. The address of this
 * allocated memory will be stuffed into the caller provided posix_spawnattr_t
 * object for use in other set calls. This has the effect of making the
 * 'posix_spawnattr_t' object completely opaque to user application as well as
 * requiring only 4 bytes for the 'posix_spawnattr_t' object if default values
 * will be used.
 * 
 * There are 2 sections to the '_posix_spawnattr_t' structure. The first contains
 * "fixed size" attributes, the second contains "variable sized" attributes.
 * Both can be extended independently however each section must be "extended".
 * Attribute fields, once defined and released, can never be removed or have
 * their order within '_posix_spawnattr_t' changed.
 * 
 * 
 * Comments on the 'reserved' fields
 * 
 * The first release of libc for 6.4.0 did not have a separate variable size
 * section defined as it was expected that this structure would simply grow and
 * any new fields (fixed or variable sized) would be added after the original
 * location of '_posix_spawnattr_partition_t'. Our first test of this was the
 * addition of the 'credentials' field. For ease of coding, "it was decided" to
 * split the structure into 2 sections, one for "fixed size" attributes and
 * another for "variable sized" attributes. The 'vsect_off' field was added to
 * indicate the start of the "variable sized" section allowing the "fixed size"
 * section to grow independently (first for the credentials field) and the fields
 * within that section to be directly referenced (ie. no need to calculate the
 * offset of a fixed size field, like the credetials just because it follows the
 * variable sized 'partition' field).
 * 
 * This however means that for the 6.4.1 release, the "variable sized"
 * '_posix_spawnattr_partition_t' attribute would be relocated wrt to its
 * location in 6.4.0 libc version of '_posix_spawnattr_t'.
 * We have no intent of being backwards compatible wrt to memory partitioning
 * (since this feature has not been released and is therefore experimental)
 * however we must make sure that programs using a 6.4.0 libc (either via a
 * statically linked app, or just by choice) in combination with a 6.4.1 procnto
 * do not cause any issues (backwards compatibility). In addition, when cross
 * node posix_spawn() is eventually supported, we also need to make sure that a
 * message originating on a 6.4.1+ node sent to a 6.4.0 node does not cause any
 * issues (forwards compatibility).
 * 
 * To this end, the 'vsect_off' and 'reserved' fields have been defined. The
 * 'reserved' fields overlay the location of a 6.4.0 libc
 * '_posix_spawnattr_partition_t' and will protect backwards/forwards
 * compatibility as follows.
 * 
 * == Forward compatibility (6.4.x cross node spawn to 6.4.0 node) ==
 * The 'reserved[0]' is defined to be the same size as the 6.4.0 'partition' pointer
 * field as well as the _posix_spawnattr_partition_t.size field. This 'reserved[0]'
 * field will be initialized to '0' forever more and will only contain a non-zero
 * value if partitions have been added to a 6.4.0 _posix_spawnattr_t message.
 * This field protects forwards compatibility by ensuring that a remote node
 * that happend to be running a 6.4.0 procnto, will always see
 * _attrp->partition == NULL and/or partition_attr->size == 0 from a 6.4.x
 * originating message. This will cause 6.4.0 posix_spawnattr_getpartid() to
 * always return 0 for the number of partitions and, if POSIX_SPAWN_SETMPART or
 * POSIX_SPAWN_SETSPART flags are set, will result in the alternative inheritance
 * behaviour (which is defined to only inherit the parents sysram memory partition,
 * in this case that of qnet since that is the parent process of any remotely
 * spawned processes). It is not possible for a 6.4.0 originating message to send
 * a posix_spawn() message as the POSIX_SPAWN_SETNODE flag is not supported in
 * 6.4.0.
 * 
 * == Backward compatibility (local 6.4.0 application to 6.4.x procnto) ==
 * The 'reserved[1]' field will be initialized to '0' forever more and protects
 * backwards compatibility by detecting a non zero value as an indication of a
 * 6.4.0 originating message. This is possible because a 6.4.0 message will have
 * the following characteristics under 3 different scenarios
 * 
 * 1. the 6.4.0 message contains partitions.
 * 		The 'reserved[1]' field will overlay the
 * 		'_posix_spawnattr_partition_t.part.num_entries' field. This field will
 * 		be non-zero
 * 2. the 6.4.0 message does not contain partition but does contain file actions
 * 		The 'reserved[1]' field will overlay the
 * 		'_posix_spawn_file_actions_t.num_entries' field. This field will be non
 * 		zero
 * 3. the 6.4.0 message does not contain partitions or file actions data
 * 		The 'reserved[1]' field will overlay the 'path' to the executable to
 * 		spawn. At most there will be 1 '0' byte representing the '\0' path
 * 		terminating character but the uint32_t 'reserved[1]' field will be non
 * 		zero because a valid executable path is required before posix_spawn()
 * 		will send the message.
 * 
 * In all 3 possible scenarios, the 'reserved[1]' field will be non-zero for a
 * 6.4.0 originating message. For 6.4.x messages, it will be zero. A non-zero
 * value will trigger a 6.4.x procnto to abort the processing with an ENOTSUP
 * errno. Note that only posix_spawnattr_getpartid() (called by procnto) needs
 * to perform this check since any future extensions will require at least a
 * 6.4.1 libc. A 6.4.0 libc attempting to use a 6.4.1 procnto and partitions
 * will receive ENOTSUP from posix_spawn().
 * 
 * IMPORTANT: Using 6.4.0 libc with a 6.4.1 procnto is entirely compatible if
 * 			  partitioning is not used.
*/
typedef struct
{
	/* the following fields are POSIX mandated */
	uint32_t	flags;		// lower 16 bits are POSIX. Upper are QNX
	pid_t		pgrp;		// POSIX
	uint32_t	node;		// QNX (why isn't there a node descriptor type ?)

	struct {
		uint32_t	max;	// QNX
	} stack;

	struct {
		sigset_t	dflt;	// POSIX
		sigset_t	mask;	// POSIX
		sigset_t	ignore;	// QNX
	} sig;

	struct {
		struct sched_param	param;	// POSIX
		int32_t  policy;			// POSIX
		uint32_t runmask;			// QNX
	} sched;
	
	uint32_t	reserved[2];	// QNX - for backwards compatibility with previous structure
								//		 which did not have separate fixed and variable sized sections
								//		 see comments above for why this change was made and how it works
	uint16_t	vsect_off;	// QNX - always indicates the offset to 'struct vsect_s'
	
	struct {
		uid_t	uid;	// QNX
		gid_t	gid;	// QNX
	} cred;

	struct vsect_s {
		uint64_t	partition;	// QNX - partition information
		uint64_t	example;	// QNX - for extensibility testing
	} vsect;
} _posix_spawnattr_t;

/* preserve upper 6 bits for future use. This gives a practical limit of 1016 bytes for the fixed
 * size attribute region (to meet the 8 byte alignment required by the variable sized section). */
#define _POSIX_SPAWNATTR_RESERVED_1		0xFC00U
#define POSIX_SPAWNATTR_VSECT_OFF_MASK	((uint16_t)(~_POSIX_SPAWNATTR_RESERVED_1))

/*
 * SPAWNATTR_T_VSECT_ADDR
 * 
 * This macro is used to get a pointer to variable sized members of the spawnattr_t structure.
 * The variable sized portion of the structure will ALWAYS be found at 'vsect_off' bytes
 * from the start of the 'posix_spawnattr_t' object or it does not exist
 */
#define VSECT_OFFSET(_attrp)	((_attrp)->vsect_off & POSIX_SPAWNATTR_VSECT_OFF_MASK)
#define SPAWNATTR_T_VSECT_ADDR(_attrp)	((struct vsect_s *)((uintptr_t)_attrp + VSECT_OFFSET(_attrp)))

/*
 * SPAWNATTR_T_SIZE
 * The size of the '_posix_spawnattr_t' object
 * 
 * SPAWNATTR_T_VSECT_SIZE
 * The number of bytes in the variable section. It is a sum of all of the variable length sections
 * not the size of the 'struct vsect_s'. As sections are added to the variable length section, they
 * must be accounted for in this macro.
 * 
 * IMPORTANT: both of these macros are safe to use even if '_attrp' == NULL
*/
#define SPAWNATTR_T_VSECT_SIZE(_attrp)	( \
											PARTITION_T_SIZE(_attrp) + \
											EXAMPLE_T_SIZE(_attrp) + \
											0 \
										)
#define _SPAWNATTR_T_SIZE(_attrp)	(sizeof(*_attrp) - sizeof(struct vsect_s) + SPAWNATTR_T_VSECT_SIZE(_attrp))
#define SPAWNATTR_T_SIZE(_attrp)	_SPAWNATTR_T_SIZE(((_posix_spawnattr_t *)_attrp))

/*
 * internal prototypes
*/
extern pthread_once_t once;
extern posix_spawnattr_t default_posix_spawnattr_t;
extern const _posix_spawnattr_t _default_posix_spawnattr_t;

extern void posix_spawnattr_t_once_init(void);
extern _posix_spawnattr_t *_posix_spawnattr_t_extend(_posix_spawnattr_t *attrp, size_t size, off_t insert);
extern _posix_spawnattr_t *get_attrp(unsigned int x, unsigned int key_mask);
extern unsigned valid_attrp(const posix_spawnattr_t *_Restrict attrp);


#endif	/* _POSIX_SPAWN */

