/// Routine to free/destroy/finalize our ChildProcess objects.
FSTATIC void
_childprocess_finalize(AssimObj* aself)
{
	ChildProcess*	self = CASTTOCLASS(ChildProcess, aself);
	if (self->stdout_src) {
		g_source_destroy(&self->stdout_src->baseclass);
		g_source_unref(&self->stdout_src->baseclass);
		//self->stdout_src->finalize(self->stdout_src);
		self->stdout_src = NULL;
	}
	if (self->stderr_src) {
		g_source_destroy(&self->stderr_src->baseclass.baseclass);
		g_source_unref(&self->stderr_src->baseclass.baseclass);
		//self->stderr_src->baseclass.finalize(&self->stderr_src->baseclass);
		self->stderr_src = NULL;
	}
	if (self->loggingname) {
		g_free(self->loggingname);
		self->loggingname = NULL;
	}
	if (self->timeoutsrc_id > 0)  {
		g_source_remove(self->timeoutsrc_id);
		DEBUGMSG3("%s:%d: Removed timeout for process with user_data = %p"
		,	__FUNCTION__, __LINE__, self);
		self->timeoutsrc_id = 0;
	}
	_assimobj_finalize(aself);
	self = NULL;
	aself = NULL;
}
Esempio n. 2
0
/// Finalize (free) a RscQElem object
FSTATIC void
_resource_queue_qelem_finalize(RscQElem* self)
{
	DEBUGMSG3("%s.%d: UNREF(self->cmd, refcount=%d)"
	,	__FUNCTION__, __LINE__,	self->cmd->baseclass._refcount);
	UNREF(self->cmd);
	FREECLASSOBJ(self);
}
Esempio n. 3
0
/// Examine our queues and run anything that needs running.
/// (this code is more expensive than it could be, but in practice it may not matter)
FSTATIC gboolean
_resource_queue_runqueue(gpointer pself)
{
	ResourceQueue* self = CASTTOCLASS(ResourceQueue, pself);
	GHashTableIter	iter;
	gpointer	key;
	gpointer	value;
	gint64		now = g_get_monotonic_time();
	gboolean	anyelems = FALSE;

	DEBUGMSG3("%s.%d: Examining queues", __FUNCTION__, __LINE__);
	g_hash_table_iter_init(&iter, self->resources);

	while(g_hash_table_iter_next(&iter, &key, &value)) {
		GQueue*	rsc_q = (GQueue*)value;
		GList*	qelem;
		gboolean	any_running = FALSE;
		for (qelem=rsc_q->head; NULL != qelem; qelem=qelem->next) {
			RscQElem*	qe = CASTTOCLASS(RscQElem, qelem->data);
			anyelems = TRUE;
			if (qe->cmd->is_running) {
				any_running = TRUE;
				break;
			}
		}
		if (any_running) {
			continue;
		}
		DEBUGMSG4("%s.%d: No resource jobs are running.", __FUNCTION__, __LINE__);
		for (qelem=rsc_q->head; NULL != qelem; qelem=qelem->next) {
			RscQElem*	qe = CASTTOCLASS(RscQElem, qelem->data);
			if (now >= qe->cmd->starttime) {
				REF(self);	// We undo this when process exits
				self->activechildcnt += 1;
				qe->cmd->execute(qe->cmd);
				break;
			}
		}
	}
	if (!anyelems && self->timerid >= 0) {
		g_source_remove(self->timerid);
		self->timerid = -1;
	}
	return anyelems;
}
Esempio n. 4
0
///
/// We update the data in the packet from our CryptCurve25519 object with the
/// side-effect of encrypting all the frames already put into the packet.  Note that
/// this only works because we always construct the packet from the end back to the
/// beginning.  We do this in-place - fortunately the algorithms allow that...
/// We effectively suck all the remaining frames into a single encrypted frame...
FSTATIC void	
_cryptcurve25519_updatedata(Frame* f,			///< Frame to marshall
			    gpointer tlvstart,		///< Start of our Frame in the packet
			    gconstpointer pktend,	///< Last byte in the allocated packet
			    FrameSet* unused_fs)	///< Pointer to our containing frameset
{
	CryptCurve25519*self		= CASTTOCLASS(CryptCurve25519, f);
	const guint8*	pktend8		= pktend;
	//guint8*	tlvstart8	= tlvstart;
	guint8*		tlvval;
	guint8*		valptr;
	guint32		plaintextoffset;
	guint32		plaintextsize;
	guint32		cyphertextoffset;
	guint32		nonceoffset;
	guint32		tlvsize;
	unsigned char*	nonce;
	int		j;

	(void)unused_fs;
	
	// [key1, key2, nonce, MAC, plaintext]

	DUMP3(__FUNCTION__, &f->baseclass, " is CryptCurve25519 Frame being processed.");
	DEBUGMSG3("%s.%d: tlvstart:%p, pktend:%p", __FUNCTION__, __LINE__, tlvstart, pktend);
	// The plain text starts immediately after our (incoming) frame
	plaintextoffset = f->length;					// Plain text starts here
	cyphertextoffset = plaintextoffset - crypto_box_MACBYTES;	// Preceded by MAC
	nonceoffset = cyphertextoffset - crypto_box_NONCEBYTES;		// Preceded by nonce
	// Our (outgoing) frame consists of the original incoming frame plus all other frames after ours
	tlvval = get_generic_tlv_nonconst_value(tlvstart, pktend);
	tlvsize = pktend8 - tlvval;
	plaintextsize = (tlvsize - plaintextoffset);

	// Generate a "nonce" as part of the packet - make known plaintext attacks harder
	// ... lots of our plaintext is easy to figure out ...
	nonce = tlvval + nonceoffset;
	DEBUGMSG3("%s.%d: generating random nonce (%p, %d, %p)", __FUNCTION__, __LINE__
	,	nonce, (int)crypto_box_NONCEBYTES, nonce+crypto_box_NONCEBYTES);
	randombytes_buf(nonce, crypto_box_NONCEBYTES);
	DEBUGMSG3("%s.%d: random nonce generated.", __FUNCTION__, __LINE__);

	DEBUGMSG3("%s.%d: public->key_id: [%s], private_key->key_id: [%s]", __FUNCTION__, __LINE__
	,	self->public_key->key_id, self->private_key->key_id);
	DEBUGMSG3("%s.%d: calling crypto_box_easy(%p,%p,%d,%p,%p,%p)", __FUNCTION__, __LINE__
	,	tlvval+cyphertextoffset, tlvval+plaintextoffset, plaintextsize
	,	nonce, self->public_key->public_key, self->private_key->private_key);
	DEBUGCKSUM4("plain text cksum:", tlvval+plaintextoffset, plaintextsize);
	DEBUGCKSUM4("receiver public key cksum:",self->public_key -> public_key, crypto_box_PUBLICKEYBYTES);
	DEBUGCKSUM4("sender  private key cksum:", self->private_key->private_key, crypto_box_SECRETKEYBYTES);
	DEBUGCKSUM4("nonce cksum:", nonce, crypto_box_NONCEBYTES);
	// Encrypt in-place [we previously allocated enough space for authentication info]
	crypto_box_easy(tlvval+cyphertextoffset, tlvval+plaintextoffset, plaintextsize
	,	nonce, self->public_key->public_key, self->private_key->private_key);
	DEBUGMSG4("cypher offset versus tlvstart: %ld", (long)(tlvval+cyphertextoffset-(guint8*)tlvstart));
	DEBUGCKSUM4("cypher text checksum:", tlvval+cyphertextoffset, plaintextsize+crypto_box_MACBYTES);
	set_generic_tlv_type(tlvstart, self->baseclass.baseclass.type, pktend);
	set_generic_tlv_len(tlvstart, tlvsize, pktend);
	// Put in the frame type, length, key name length, and key name for both keys
	// We're the sender - our [private] key name goes first, then the receiver's [public] key name
	valptr = get_generic_tlv_nonconst_value(tlvstart, pktend);
	for (j=0; j < 2; ++j) {
		char *	key_id = (j == 0 ? self->baseclass.sender_key_id : self->baseclass.receiver_key_id);
		int	keylen = strlen(key_id)+1;
		tlv_set_guint8(valptr, keylen, pktend);
		valptr += 1;
		g_strlcpy((char *)valptr, key_id, keylen);
		valptr += keylen;
	}
	DEBUGMSG3("%s.%d: returning after next assert (tlvval:%p, tlvsize%d, pktend:%p"
	,	__FUNCTION__, __LINE__, tlvval, (int)tlvsize, pktend);
	g_assert((tlvval + tlvsize) == pktend);
	DEBUGMSG3("%s.%d: returning (assert passed).", __FUNCTION__, __LINE__);
}
/**
 * @ref ChildProcess constructor.
 * Here's what we're going to do:
 * 1) Create child process using g_spawn_async_with_pipes()
 * 2) ...In child process become our own process group
 * 3) Create LogSourceFd object for stderr
 * 4) Create LogSourceFd or GMainFd object for stdout
 * 5) Set timer (if any)
 * 6) Initialize the child state to running
 * 7) Return.
 */
WINEXPORT ChildProcess*
childprocess_new(gsize cpsize		///< Size of created ChildProcess object
,		char** argv		///< NULL-terminated argv for the ChildProcess
,		const char** envp	///< Environment for the ChildProcess
,		ConfigContext* envmod	///< Modifications to the ChildProcess environment
,		const char* curdir	///< Current directory to start the child in
,		void (*notify)(ChildProcess*, enum HowDied, int rc, int signal, gboolean core_dumped)
					///< Function to call if/when the child terminates
,		gboolean save_stdout	///< TRUE to save stdout, FALSE to log it
,		const char*logdomain	///< Glib log domain
,		const char*logprefix	///< Prefix to prepend to log entries
,		GLogLevelFlags loglevel	///< Glib Log level
,		guint32 timeout_seconds	///< How long to wait before killing it - zero for no timeout
,		gpointer user_data	///< Data our user wants us to keep
,		enum ChildErrLogMode logmode ///< How to log child exits
,		const char* logname)	///< Name to use when logging child exits as requested
{
	AssimObj*	aself;
	ChildProcess*	self;
	gint		stdoutfd;
	gint		stderrfd;
	GError*		failcode = NULL;
	gchar**		childenv = NULL;

	BINDDEBUG(ChildProcess);
	g_return_val_if_fail(logprefix != NULL, NULL);
	if (cpsize < sizeof(ChildProcess)) {
		cpsize = sizeof(ChildProcess);
	}
	aself = assimobj_new(cpsize);
	g_return_val_if_fail(aself != NULL, NULL);
	self = NEWSUBCLASS(ChildProcess, aself);
	childenv = assim_merge_environ(envp, envmod);
	if (!g_spawn_async_with_pipes(
		curdir,				// Current directory
		argv,				// Arguments
		childenv,			// environment
		G_SPAWN_DO_NOT_REAP_CHILD,	// GSpawnFlags flags,
		_childprocess_setup_child,	// GSpawnChildSetupFunc child_setup,
		self,				// gpointer user_data,
		&self->child_pid,		// GPid *child_pid,
		NULL,				// gint *standard_input,
		&stdoutfd,			// gint *standard_output,
		&stderrfd,			// gint *standard_error,
		&failcode)) {			// GError **error

		// OOPS!  Failed!
		const char *	msg = "unknown exec error";
		if (failcode && failcode->message) {
			msg = failcode->message;
		}
		g_critical("%s.%d: %s", __FUNCTION__, __LINE__, msg);
		if (failcode) {
			g_clear_error(&failcode);	// sets failcode back to NULL
		}
		assim_free_environ(childenv); childenv = NULL;
		UNREF(self);
		aself = NULL;
		return NULL;
	}
	DEBUGMSG2("%s.%d: Spawned process with user_data = %p", __FUNCTION__, __LINE__, self);

	aself->_finalize = _childprocess_finalize;
	aself->toString = _childprocess_toString;
	self->stderr_src = logsourcefd_new(0, stderrfd, G_PRIORITY_HIGH, g_main_context_default()
	,		                   logdomain, loglevel, logprefix);
	self->user_data = user_data;
	self->logmode = logmode;
	if (NULL == logname) {
		logname = argv[0];
	}
	self->loggingname = g_strdup(logname);
	assim_free_environ(childenv);
	childenv = NULL;

	if (!save_stdout) {
		LogSourceFd*	logsrc;
		logsrc = logsourcefd_new(0, stdoutfd, G_PRIORITY_HIGH
		,		g_main_context_default(), logdomain, loglevel, logprefix);
		self->stdout_src = &logsrc->baseclass;
	}else{
		self->stdout_src = gmainfd_new(0, stdoutfd, G_PRIORITY_HIGH, g_main_context_default());
	}
	self->childsrc_id = g_child_watch_add(self->child_pid, _childprocess_childexit, self);

	self->notify = notify;

	if (0 == timeout_seconds) {
		DEBUGMSG2("No timeout for process with user_data = %p", self);
		self->timeoutsrc_id = 0;
	}else{
		self->timeoutsrc_id = g_timeout_add_seconds(timeout_seconds
		,			                   _childprocess_timeout, self);
		DEBUGMSG3("%s.%d: Set %d second timeout %d for process with user_data = %p"
		,	__FUNCTION__, __LINE__, timeout_seconds, self->timeoutsrc_id, self);
	}
	self->child_state = CHILDSTATE_RUNNING;
	DEBUGMSG5("%s.%d: REF child: %p", __FUNCTION__,__LINE__, self);
	REF(self);	// We do this because we need to still be here when the process exits
	return self;
}
/// Function called when the child (finally) exits...
FSTATIC void
_childprocess_childexit(GPid pid, gint status, gpointer childprocess_object)
{
	ChildProcess*	self = CASTTOCLASS(ChildProcess, childprocess_object);
	gboolean	signalled = WIFSIGNALED(status);
	int		exitrc = 0;
	int		signal = 0;
	gboolean	logexit = FALSE;
	enum HowDied	howwedied = NOT_EXITED;
	(void)pid;

	if (self->timeoutsrc_id > 0)  {
		g_source_remove(self->timeoutsrc_id);
		DEBUGMSG3("%s.%d: Removed timeout %d for process with user_data = %p"
		,	__FUNCTION__, __LINE__, self->timeoutsrc_id, self);
		self->timeoutsrc_id = 0;
	}
	// If it refused to die, then the status is invalid
	if ((guint)(self->child_state) >= DIMOF(signalmap)) {
		howwedied = EXITED_HUNG;
	}else if ((guint)self->child_state != CHILDSTATE_RUNNING) {
 		// Then we tried to kill it...
		howwedied = EXITED_TIMEOUT;
		signal = signalled ? WTERMSIG(status) : 0;
	}else{
		if (signalled) {
			signal = WTERMSIG(status);
			howwedied = EXITED_SIGNAL;
		}else{
			exitrc = WEXITSTATUS(status);
			howwedied = (exitrc == 0 ? EXITED_ZERO : EXITED_NONZERO);
		}
	}
	switch (howwedied) {
		case EXITED_SIGNAL:	/*FALLTHROUGH*/
		case EXITED_TIMEOUT:	/*FALLTHROUGH*/
		case EXITED_HUNG:
			logexit = self->logmode  > CHILD_NOLOG;
			break;
		case EXITED_NONZERO:
			logexit = self->logmode  >= CHILD_LOGERRS;
			break;
		case EXITED_ZERO:
			logexit = self->logmode  >= CHILD_LOGALL;
			break;
		default:
			// We'll never produce any other values above
			/*NOTREACHED*/
			logexit = TRUE;
			break;
	}
	if (logexit) {
		switch (howwedied) {
		case EXITED_SIGNAL:
			g_warning("Child process [%s] died from signal %d%s."
			,	self->loggingname, signal, WCOREDUMP(status) ? " (core dumped)" : "");
			break;
		case EXITED_TIMEOUT:
			if (signalled) {
				g_warning("Child process [%s] timed out after %d seconds [signal %d%s]."
				,	self->loggingname, self->timeout, signal
				,	WCOREDUMP(status) ? " (core dumped)" : "");
			}else{
				g_warning("Child process [%s] timed out after %d seconds."
				,	self->loggingname, self->timeout);
			}
			break;
		case EXITED_HUNG:
			g_warning("Child process [%s] timed out after %d seconds and could not be killed."
			,	self->loggingname, self->timeout);
			break;
		case EXITED_NONZERO:
			g_message("Child process [%s] exited with return code %d."
			,	self->loggingname, exitrc);
			break;
		case EXITED_ZERO:
			g_message("Child process [%s] exited normally.", self->loggingname);
			break;
		default:/*NOTREACHED*/
			break;
		}
	}

	DEBUGMSG2("%s.%d: Exit happened howwedied:%d", __FUNCTION__, __LINE__
	,	howwedied);
	if (!self->stdout_src->atEOF) {
		//DEBUGMSG3("Child %d [%s] EXITED but output is not at EOF [fd%d]", pid
		//,	self->loggingname, self->stdout_src->gfd.fd);
		self->stdout_src->readmore(self->stdout_src);
	}
	if (!self->stderr_src->baseclass.atEOF) {
		self->stderr_src->baseclass.readmore(&self->stderr_src->baseclass);
	}
	self->notify(self, howwedied, exitrc, signal, WCOREDUMP(status));
	self->child_state = -1;
	DEBUGMSG5("%s.%d: UNREF child: %p", __FUNCTION__,__LINE__, self);
	UNREF(self);	// Undo the REF(self) in our constructor
}