// Returns total number of CPUs (including down CPUs)
Lng32 NAClusterInfo::getTotalNumberOfCPUs()
{
  Lng32 cpuCount = cpuArray_.entries();

#ifndef NDEBUG
  if ( inTestMode() ) {
    NADefaults & defs = ActiveSchemaDB()->getDefaults();
    cpuCount = (Int32)(defs.getAsLong(POS_TEST_NUM_NODES));
  }
#endif
  // 
  return cpuCount;
}
/**
 * @brief decrypt the file specified at parentKey
 * @param pluginConfig holds the plugin configuration
 * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure.
 * @param state holds the plugin state
 * @retval 1 on success
 * @retval -1 on error, errorKey holds an error description
 */
static int fcryptDecrypt (KeySet * pluginConfig, Key * parentKey, fcryptState * state)
{
	int tmpFileFd = -1;
	char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd);
	if (!tmpFile)
	{
		ELEKTRA_SET_ERROR (87, parentKey, "Memory allocation failed");
		return -1;
	}

	const size_t testMode = inTestMode (pluginConfig);

	// prepare argument vector for gpg call
	// 8 static arguments (magic number below) are:
	//   1. path to the binary
	//   2. --batch
	//   3. -o
	//   4. path to tmp file
	//   5. yes
	//   6. -d
	//   7. file to be encrypted
	//   8. NULL terminator
	int argc = 8 + (2 * testMode);
	char * argv[argc];
	int i = 0;

	argv[i++] = NULL;
	argv[i++] = "--batch";
	argv[i++] = "--yes";

	// if we are in test mode we add the trust model
	if (testMode)
	{
		argv[i++] = "--trust-model";
		argv[i++] = "always";
	}

	argv[i++] = "-o";
	argv[i++] = tmpFile;
	argv[i++] = "-d";
	// safely discarding const from keyString() return value
	argv[i++] = (char *) keyString (parentKey);
	argv[i++] = NULL;

	// NOTE the decryption process works like this:
	// gpg2 --batch --yes -o tmpfile -d configFile
	int result = ELEKTRA_PLUGIN_FUNCTION (gpgCall) (pluginConfig, parentKey, NULL, argv, argc);
	if (result == 1)
	{
		state->originalFilePath = elektraStrDup (keyString (parentKey));
		state->tmpFilePath = tmpFile;
		state->tmpFileFd = tmpFileFd;
		keySetString (parentKey, tmpFile);
	}
	else
	{
		// if anything went wrong above the temporary file is shredded and removed
		shredTemporaryFile (tmpFileFd, parentKey);
		if (unlink (tmpFile))
		{
			ELEKTRA_ADD_WARNINGF (ELEKTRA_WARNING_FCRYPT_UNLINK, parentKey, "Affected file: %s, error description: %s", tmpFile,
					      strerror (errno));
		}
		if (close (tmpFileFd))
		{
			ELEKTRA_ADD_WARNINGF (ELEKTRA_WARNING_FCRYPT_CLOSE, parentKey, "%s", strerror (errno));
		}
		elektraFree (tmpFile);
	}
	return result;
}
/**
 * @brief encrypt or sign the file specified at parentKey
 * @param pluginConfig holds the plugin configuration
 * @param parentKey holds the path to the file to be encrypted. Will hold an error description in case of failure.
 * @retval 1 on success
 * @retval -1 on error, errorKey holds an error description
 */
static int fcryptEncrypt (KeySet * pluginConfig, Key * parentKey)
{
	Key * k;
	const size_t recipientCount = getRecipientCount (pluginConfig, ELEKTRA_RECIPIENT_KEY);
	const size_t signatureCount = getRecipientCount (pluginConfig, ELEKTRA_SIGNATURE_KEY);

	if (recipientCount == 0 && signatureCount == 0)
	{
		ELEKTRA_SET_ERRORF (
			ELEKTRA_ERROR_NO_GPG_RECIPIENTS, parentKey,
			"Missing GPG recipient key (specified as %s) or GPG signature key (specified as %s) in plugin configuration.",
			ELEKTRA_RECIPIENT_KEY, ELEKTRA_SIGNATURE_KEY);
		return -1;
	}

	int tmpFileFd = -1;
	char * tmpFile = getTemporaryFileName (pluginConfig, keyString (parentKey), &tmpFileFd);
	if (!tmpFile)
	{
		ELEKTRA_SET_ERROR (87, parentKey, "Memory allocation failed");
		return -1;
	}

	const size_t testMode = inTestMode (pluginConfig);
	const size_t textMode = inTextMode (pluginConfig);

	// prepare argument vector for gpg call
	// 7 static arguments (magic number below) are:
	//   1. path to the binary
	//   2. --batch
	//   3. -o
	//   4. path to tmp file
	//   5. yes
	//   6. file to be encrypted
	//   7. NULL terminator
	int argc = 7 + (2 * recipientCount) + (2 * signatureCount) + (2 * testMode) + textMode + (recipientCount > 0 ? 1 : 0) +
		   (signatureCount > 0 ? 1 : 0);
	kdb_unsigned_short_t i = 0;
	char * argv[argc];
	argv[i++] = NULL;
	argv[i++] = "--batch";
	argv[i++] = "-o";
	argv[i++] = tmpFile;
	argv[i++] = "--yes"; // overwrite files if they exist

	// add recipients
	Key * gpgRecipientRoot = ksLookupByName (pluginConfig, ELEKTRA_RECIPIENT_KEY, 0);

	// append root (gpg/key) as gpg recipient
	if (gpgRecipientRoot && strlen (keyString (gpgRecipientRoot)) > 0)
	{
		argv[i++] = "-r";
		// NOTE argv[] values will not be modified, so const can be discarded safely
		argv[i++] = (char *) keyString (gpgRecipientRoot);
	}

	// append keys beneath root (crypto/key/#_) as gpg recipients
	if (gpgRecipientRoot)
	{
		ksRewind (pluginConfig);
		while ((k = ksNext (pluginConfig)) != 0)
		{
			const char * kStringVal = keyString (k);
			if (keyIsBelow (k, gpgRecipientRoot) && strlen (kStringVal) > 0)
			{
				argv[i++] = "-r";
				// NOTE argv[] values will not be modified, so const can be discarded safely
				argv[i++] = (char *) kStringVal;
			}
		}
	}


	// add signature keys
	Key * gpgSignatureRoot = ksLookupByName (pluginConfig, ELEKTRA_SIGNATURE_KEY, 0);

	// append root signature key
	if (gpgSignatureRoot && strlen (keyString (gpgSignatureRoot)) > 0)
	{
		argv[i++] = "-u";
		// NOTE argv[] values will not be modified, so const can be discarded safely
		argv[i++] = (char *) keyString (gpgSignatureRoot);
	}

	// append keys beneath root (fcrypt/sign/#_) as gpg signature keys
	if (gpgSignatureRoot)
	{
		ksRewind (pluginConfig);
		while ((k = ksNext (pluginConfig)) != 0)
		{
			const char * kStringVal = keyString (k);
			if (keyIsBelow (k, gpgSignatureRoot) && strlen (kStringVal) > 0)
			{
				argv[i++] = "-u";
				// NOTE argv[] values will not be modified, so const can be discarded safely
				argv[i++] = (char *) kStringVal;
			}
		}
	}

	// if we are in test mode we add the trust model
	if (testMode > 0)
	{
		argv[i++] = "--trust-model";
		argv[i++] = "always";
	}

	// ASCII armor in text mode
	if (textMode)
	{
		argv[i++] = "--armor";
	}

	// prepare rest of the argument vector
	if (recipientCount > 0)
	{
		// encrypt the file
		argv[i++] = "-e";
	}

	if (signatureCount > 0)
	{
		if (textMode && recipientCount == 0)
		{
			// clear-sign the file
			argv[i++] = "--clearsign";
		}
		else
		{
			// sign the file
			argv[i++] = "-s";
		}
	}

	argv[i++] = (char *) keyString (parentKey);
	argv[i++] = NULL;

	// NOTE the encryption process works like this:
	// gpg2 --batch --yes -o encryptedFile -r keyID -e configFile
	// mv encryptedFile configFile

	return fcryptGpgCallAndCleanup (parentKey, pluginConfig, argv, argc, tmpFileFd, tmpFile);
}