/*
 * $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 "posix_spawnattr.h"


/*
 * =============================================================================
 * 
 * 			The following are NON PUBLIC QNX posix_spawnattr_t extensions
 * 				used by proc to parse a _PROC_POSIX_SPAWN message
 * 					(DO NOT DOCUMENT in library reference guide)
 * 
 * =============================================================================
*/

/*
 * default_posix_spawnattr_t
 * 
 * The default 'posix_spawnattr_t' object will be used in the following cases...
 * 	1.	to return default value for any _getxxx() for which a user 'posix_spawnattr_t'
 * 		object has not yet been created. A user object will only be created
 * 		(memory will only be allocated) if a call to one of the _setxxx() functions
 * 		is made. This avoids unnecessary allocations for objects which will only
 * 		have default values.
 * 	2.	to provide the 'posix_spawnattr_t' values in a posix_spawn() call if
 * 		the 'attrp' argument is NULL
 * 
 * Note re __attribute__((aligned(8))) for _default_posix_spawnattr_t
 * 
 * We need this variable to be 8 byte aligned for the same reason we expect
 * any allocated posix_spawnattr_t memory within libc to be 8 byte aligned
 * (see KEY_MASK). The WATCOM (x86 only) compiler will does not understand the
 * __attribute__ keyword and so it potentially will not align
 * _default_posix_spawnattr_t to 8 bytes. If this is the case, then posix_spawn()
 * will assert in posix_spawnattr_t_once_init(). In this case I will have to
 * either reduce the KEY_MASK to 4 bytes alignment for a WATCOM compile or use
 * the same technique used within procnto, again for a WATCOM compile only.
 * Until I actually see this problem, I am not going to code a specific WATCOM
 * solution.
*/
#if defined(__WATCOMC__)
const _posix_spawnattr_t _default_posix_spawnattr_t =
#else	/* defined(__WATCOMC__) */
const _posix_spawnattr_t _default_posix_spawnattr_t __attribute__((aligned(8))) =
#endif	/* defined(__WATCOMC__) */

{
	0,				// flags - POSIX says no flags set is the default
	0,				// process group - POSIX says default value is 0
	ND_LOCAL_NODE,	// node on which to spawn - non POSIX
	{	// struct {} stack;
		0,			// maximum stack size - non POSIX. default is no maximum
	},
	{	// struct {} sig;
		{{0, 0}},	// signals which should be forced to use default signal handling - POSIX says empty set
		{{0, 0}},	// signal mask of the new process - POSIX says unspecified. Leave as empty set
		{{0, 0}},	// signals to be ignored - non POSIX. Leave as empty set
	},
	{	// struct {} sched;
		{NULL},		// sched_param - POSIX says default is unspecified. Leave NULL so inherited
		0,			// scheduling policy - POSIX says default is unspecified. Leave 0 so inherited
		(~0),		// runmask - non POSIX. Default is any available CPU
	},
	{0, 0},			// reserved[] - must always be initialized to 0 (NEVER CHANGE THIS)
	(uint16_t)offsetof(_posix_spawnattr_t, vsect),	// offset to the variable length data
	{	// struct {} cred;
		0,			// uid - non POSIX, default is root
		0,			// gid - non POSIX, default is root
	},
	{	// struct for variable sized objects
		0,			// partitioning attributes - non POSIX. Default is no partitions specified
		0,			// example (for demonstrating and testing extensibility of the variable sized section)
	}
};

/* default_posix_spawnattr_t is available externally to posix_spawn() */
posix_spawnattr_t default_posix_spawnattr_t = ((unsigned)0);



/*
 * I want a compile time bailout and #if sizeof() does not work. I am absolutely willing to entertain
 * anything better but in the meantime ...
 * If __expression__ is false, we get a negative array index which won't compile else we get
 * an unused '0' sized array which consumes no space.
*/
#define COMPILE_TIME_ASSERT(__expression__)		__attribute((unused)) char x[(__expression__) - 1]

/*******************************************************************************
 * posix_spawnattr_endswap
 * 
 * Do an endian swap on the '_posix_spawnattr_t' pointed to by <p>.
 * This is an internal routine used by procmgr_pspawn()
 * Perhaps it should only compile when procnto is being built ?
*/
void posix_spawnattr_endswap(void *p)
{
	_posix_spawnattr_t *_attrp = p;
	_posix_spawnattr_partition_t  *partition_attr;

	/* make sure that none of these "messaged" types (some of unspecified size) change behind our back */
	COMPILE_TIME_ASSERT((sizeof(_attrp->pgrp) == 4) &&
						(sizeof(_attrp->sig.mask.__bits[0]) == 4) && (sizeof(_attrp->sig.mask.__bits[1]) == 4) &&
						(sizeof(_attrp->sig.dflt.__bits[0]) == 4) && (sizeof(_attrp->sig.dflt.__bits[1]) == 4) &&
						(sizeof(_attrp->sig.ignore.__bits[0]) == 4) && (sizeof(_attrp->sig.ignore.__bits[1]) == 4) &&
						(sizeof(_attrp->sched.param.__sched_ss_repl_period.tv_sec) == 4) &&
						(sizeof(_attrp->sched.param.__sched_ss_repl_period.tv_nsec) == 4) &&
						(sizeof(_attrp->sched.param.__sched_ss_init_budget.tv_sec) == 4) &&
						(sizeof(_attrp->sched.param.__sched_ss_init_budget.tv_nsec) == 4) &&
						(sizeof(_attrp->cred.uid) == 4) && (sizeof(_attrp->cred.gid) == 4));

	if (_attrp == NULL) return;

	/* swap flags */
	ENDIAN_SWAP32(&_attrp->flags);
	
	/* swap process group attribute */
	ENDIAN_SWAP32(&_attrp->pgrp);
	
	/* swap node attribute */
	ENDIAN_SWAP32(&_attrp->node);
	
	/* swap stack attributes */
	ENDIAN_SWAP32(&_attrp->stack.max);

	/* swap signal handling attributes */
	ENDIAN_SWAP32(&_attrp->sig.mask.__bits[0]);
	ENDIAN_SWAP32(&_attrp->sig.mask.__bits[1]);
	ENDIAN_SWAP32(&_attrp->sig.dflt.__bits[0]);
	ENDIAN_SWAP32(&_attrp->sig.dflt.__bits[1]);
	ENDIAN_SWAP32(&_attrp->sig.ignore.__bits[0]);
	ENDIAN_SWAP32(&_attrp->sig.ignore.__bits[1]);

	/* swap scheduling attributes */
	ENDIAN_SWAP32(&_attrp->sched.policy);
	ENDIAN_SWAP32(&_attrp->sched.runmask);
	ENDIAN_SWAP32(&_attrp->sched.param.sched_priority);
	ENDIAN_SWAP32(&_attrp->sched.param.sched_curpriority);
	ENDIAN_SWAP32(&_attrp->sched.param.__sched_ss_low_priority);
	ENDIAN_SWAP32(&_attrp->sched.param.__sched_ss_max_repl);
	ENDIAN_SWAP32(&_attrp->sched.param.__sched_ss_repl_period.tv_sec);
	ENDIAN_SWAP32(&_attrp->sched.param.__sched_ss_repl_period.tv_nsec);
	ENDIAN_SWAP32(&_attrp->sched.param.__sched_ss_init_budget.tv_nsec);
	ENDIAN_SWAP32(&_attrp->sched.param.__sched_ss_init_budget.tv_nsec);

	/* swap the credentials */
	ENDIAN_SWAP32(&_attrp->cred.uid);
	ENDIAN_SWAP32(&_attrp->cred.gid);

	/* swap variable sized partition attributes */
	partition_attr = PARTITION_T_ADDR(_attrp);
	ENDIAN_SWAP32(&partition_attr->size);
	if (partition_attr->size > 0)
	{
		unsigned i;

		ENDIAN_SWAP32(&partition_attr->part.num_entries);
		for (i=0; i<partition_attr->part.num_entries; i++) {
			ENDIAN_SWAP32(&partition_attr->part.i[i].id);
			ENDIAN_SWAP32(&partition_attr->part.i[i].flags);
		}
	}
}


/*******************************************************************************
 * get_attrp
 * set_attrp
 * 
 * These 2 routine translate between a real allocated '_posix_spawnattr_t *' and
 * the token stored in the 'posix_spawnattr_t *'.
 * 
 * A valid, initialized 'posix_spawnattr_t' object has 2 possible values.
 * 		- NULL
 * 		- a valid, allocated address
 * All other values are considered invalid. These 2 routines attempt to catch
 * uninitialized 'posix_spawnattr_t' objects from being used. The example is
 * an automatic 'posix_spawnattr_t' which contains stack garbage. It is non NULL
 * but does not represent valid, allocated memory.
 * 
 * Because we know that allocated memory is always aligned on given boundary
 * we use a key that will always turn a properly allocated memory address into
 * a token which always has the lower 3 bits set (8 byte alignment). This works
 * for all bogus values that do not have 7 as the LSB 
*/
void set_attrp(posix_spawnattr_t *pp, void *_p, unsigned key_mask)
{
	_posix_spawnattr_t *p = (_posix_spawnattr_t *)_p;
	assert(pp != NULL);
	assert(((unsigned)p == AKEY(key_mask)) || (((unsigned)p & key_mask) == 0)); // must be aligned or keying will fail
	*pp = ((unsigned)p ^ AKEY(key_mask));
}

_posix_spawnattr_t *get_attrp(unsigned int x, unsigned key_mask)
{
	if (x != 0) x ^= AKEY(key_mask);
	return (x & key_mask) ? NULL : (_posix_spawnattr_t *)x;
}

/*******************************************************************************
 * valid_attrp
 * 
 * Return true (!0) or FALSE (0) depending on whether <attrp> represents a valid
 * initialized 'posix_spawnattr_t' object.
 * 
 * A valid object is as follows
 * 	- <attrp> is non NULL
 * 	- <*attrp> is NULL or represents allocated memory
 * 
 * In order to differentiate between allocated memory and a non 0 value, any
 * memory actually allocated will be translated. The get_attrp() and set_attrp()
 * routines will be used to translate back and forth 
*/
unsigned valid_attrp(const posix_spawnattr_t *_Restrict attrp)
{
	if (attrp == NULL) return 0;				// not valid
	if (*(posix_spawnattr_t *)((unsigned int)attrp & ~MIN_KEY_MASK) == ~0U) return 0;			// not valid
	if (*(posix_spawnattr_t *)((unsigned int)attrp & ~MIN_KEY_MASK) == 0) return !0;			// valid
	if (GET_ATTRP(attrp) != NULL) return !0;	// valid
	return 0;
}

#endif	/* _POSIX_SPAWN */

