version 1.2, 2015/04/20 18:10:11
|
version 1.3, 2015/04/20 18:19:51
|
|
|
**============================================================================== | **============================================================================== |
*/ | */ |
| |
#include <base/strings.h> |
#include <pal/strings.h> |
#include <base/messages.h> | #include <base/messages.h> |
#include <base/log.h> | #include <base/log.h> |
#include <base/strings.h> |
#include <pal/strings.h> |
#include <base/paths.h> | #include <base/paths.h> |
#include <base/io.h> |
#include <pal/format.h> |
|
#include <base/Strand.h> |
#include <protocol/protocol.h> | #include <protocol/protocol.h> |
#include "agentmgr.h" | #include "agentmgr.h" |
|
#include <omi_error/errorutil.h> |
| |
#if defined(CONFIG_POSIX) | #if defined(CONFIG_POSIX) |
# include <unistd.h> | # include <unistd.h> |
|
|
# include <sys/wait.h> | # include <sys/wait.h> |
#endif | #endif |
| |
#define T MI_T |
|
|
|
/* | /* |
**============================================================================== | **============================================================================== |
** | ** |
|
|
| |
#if defined(CONFIG_POSIX) | #if defined(CONFIG_POSIX) |
| |
typedef struct _RequestItem RequestItem; |
|
/* | /* |
RequestItem - stores information about request sent to the agent/provider; | RequestItem - stores information about request sent to the agent/provider; |
this item stores original request's msgID (has to be substituted) and request pointer |
this item stores original request's operationId (has to be substituted) and request pointer |
In case of agent disconnection, agent-mgr uses this list to send error responses to | In case of agent disconnection, agent-mgr uses this list to send error responses to |
outstanding requests. | outstanding requests. |
*/ | */ |
struct _RequestItem |
|
|
typedef enum _RequestItemFinishState |
{ | { |
/* Linked-list support */ |
RequestItemFinishState_None = 0, |
RequestItem* next; |
RequestItemFinishState_PendingFinishOnError, |
RequestItem* prev; |
RequestItemFinishState_ProcessedFinishOnError |
|
} RequestItemFinishState; |
| |
Message* request; |
|
MI_Uint64 originalMsgID; |
typedef struct _RequestItem |
}; |
{ |
|
// managing original interaction coming from dispatcher |
|
StrandEntry strand; |
|
|
|
MI_Boolean isIdleRequest; |
|
MI_Boolean pendingCancel; |
|
RequestItemFinishState finishOnErrorState; |
|
|
|
Message* request; // Request received from the left |
|
MI_Uint64 originalOperationId; |
|
MI_Uint64 key; // OperationId of the outogoing request; for now RequestItem address (as it was before) |
|
} |
|
RequestItem; |
| |
/* | /* |
AgentElem - stores information about single running agent | AgentElem - stores information about single running agent |
|
(this will be become multiplexer eventually) |
*/ | */ |
struct _AgentElem | struct _AgentElem |
{ | { |
/* Linked-list support */ |
StrandMany strand; |
AgentElem* next; |
|
AgentElem* prev; |
|
| |
/* outstanding requests */ |
/* Linked-list support */ |
RequestItem* headRequests; |
ListElem* next; |
RequestItem* tailRequests; |
ListElem* prev; |
| |
/* hosting context */ | /* hosting context */ |
uid_t uid; | uid_t uid; |
gid_t gid; | gid_t gid; |
| |
/* connection to the agent */ | /* connection to the agent */ |
Protocol* protocol; |
ProtocolSocketAndBase* protocol; |
| |
/* link to manager */ | /* link to manager */ |
AgentMgr* agentMgr; | AgentMgr* agentMgr; |
|
|
| |
/* | /* |
**============================================================================== | **============================================================================== |
|
*/ |
|
|
|
#define AGENTELEM_STRANDAUX_CLOSEAGENTITEM 0 |
|
#define AGENTELEM_STRANDAUX_ENTRYACK 1 |
|
|
|
STRAND_DEBUGNAME2( AgentElem, CloseAgentItem, EntryAck ); |
|
|
|
#define IDLEREQUESTITEM_STRANDAUX_READYTOFINISH 0 |
|
|
|
STRAND_DEBUGNAME1( IdleRequestItem, ReadyToFinish ); |
|
|
|
#define REQUESTITEM_STRANDAUX_PREPARETOFINISHONERROR 0 |
|
|
|
STRAND_DEBUGNAME1( RequestItem, PrepareToFinishOnError ); |
|
|
|
|
|
/* |
|
**============================================================================== |
|
*/ |
|
|
|
void _AgentElem_InitiateClose( _In_ AgentElem* self ) |
|
{ |
|
// remove agent from Mgr's list |
|
// Do this first so no new request entries are added after this |
|
ReadWriteLock_AcquireWrite(&self->agentMgr->lock); |
|
List_Remove( |
|
&self->agentMgr->headAgents, |
|
&self->agentMgr->tailAgents, |
|
(ListElem*)&(self->next)); |
|
ReadWriteLock_ReleaseWrite(&self->agentMgr->lock); |
|
|
|
StrandMany_ScheduleAux( &self->strand, AGENTELEM_STRANDAUX_CLOSEAGENTITEM ); |
|
} |
|
|
|
void _AgentElem_Post( _In_ Strand* self_, _In_ Message* msg) |
|
{ |
|
AgentElem* self = (AgentElem*)StrandMany_FromStrand(self_); |
|
DEBUG_ASSERT( NULL != self_ ); |
|
trace_AgentElemPostingMessage(&self->strand.strand.info.interaction, self->strand.strand.info.interaction.other); |
|
|
|
/* ATTN: verify unload message */ |
|
|
|
if (BinProtocolNotificationTag == msg->tag) |
|
{ |
|
BinProtocolNotification* notification = (BinProtocolNotification*)msg; |
|
if (BinNotificationAgentIdle == notification->type) |
|
{ |
|
// Check if this agent has outstanding requests - |
|
// if agent is really idle (only idle notification request) |
|
// then initiate the close |
|
if( 1 == self->strand.numEntries ) |
|
{ |
|
_AgentElem_InitiateClose( self ); |
|
} |
|
} |
|
/* ignore service messages */ |
|
Strand_Ack( &self->strand.strand ); |
|
return; |
|
} |
|
|
|
if( !StrandMany_PostFindEntry( &self->strand, msg ) ) |
|
{ |
|
trace_StrandMany_CannotFindItem( Uint64ToPtr(msg->operationId), (int)self->uid ); |
|
_AgentElem_InitiateClose( self ); |
|
} |
|
|
|
// For now ack immediately |
|
//TODO eventually multiplexer should take care of flow control here |
|
// For now, we are short circuiting ACK from RequstItem |
|
} |
|
|
|
void _AgentElem_PostControl( _In_ Strand* self_, _In_ Message* msg) |
|
{ |
|
DEBUG_ASSERT( MI_FALSE ); // not used yet |
|
} |
|
|
|
void _AgentElem_Ack( _In_ Strand* self_) |
|
{ |
|
trace_AgentElemAck( &self_->info.interaction, self_->info.interaction.other ); |
|
} |
|
|
|
void _AgentElem_Cancel( _In_ Strand* self_) |
|
{ |
|
DEBUG_ASSERT( MI_FALSE ); // not used yet |
|
} |
|
|
|
void _AgentElem_Close( _In_ Strand* self_) |
|
{ |
|
AgentElem* self = (AgentElem*)StrandMany_FromStrand(self_); |
|
|
|
trace_AgentClosedConnection((int)self->uid); |
|
// lost connection to the agent ( within 'CloseAgentItem' call): |
|
// - send error repsonses to all outstanding requests |
|
// - remove agent form the list |
|
|
|
_AgentElem_InitiateClose( self ); |
|
} |
|
|
|
void _AgentElem_Finish( _In_ Strand* self_) |
|
{ |
|
AgentElem* self = (AgentElem*)StrandMany_FromStrand(self_); |
|
DEBUG_ASSERT( NULL != self_ ); |
|
|
|
// It is ok now for the protocol object to go away |
|
ProtocolSocketAndBase_ReadyToFinish(self->protocol); |
|
|
|
StrandMany_Delete(&self->strand); |
|
} |
|
|
|
// AGENTELEM_STRANDAUX_ENTRYACK |
|
void _AgentElem_EntryAck( _In_ Strand* self_) |
|
{ |
|
//Temporary until we have flow control with multiplexing buffers |
|
Strand_Ack( self_ ); |
|
|
|
} |
|
|
|
// AGENTELEM_STRANDAUX_CLOSEAGENTITEM |
|
static void _AgentElem_CloseAgentItem(Strand* self_); |
|
|
|
/* |
|
Object that implements a single connection going thru binary protocol |
|
to one agent. It uses that one-to-many interface to channel thru multiple |
|
operations on the same connection. |
|
|
|
Behavior: |
|
- Post checks if the message is a idle notification, and if it is and there is |
|
only one remaining operation (the idle notification one itself) then initiates |
|
a close. Otherwise it just post the message to the pertinent operation that is |
|
find using the buildin hash map searching by the operationId field |
|
in the message. |
|
- Ack does nothing currently as there is not an explicit in-the-wire flow control |
|
protocol implemented yet. |
|
- PostControl and Cancel are not currently implemented |
|
- Close initiates the closing of all entries and sending the corresponding |
|
error messages to each one (see _AgentElem_CloseAgentItem below) |
|
- Shutdown: |
|
The objects are deleted thru the normal Strand logic. That is, |
|
once the interaction is closed on both sides and there are no |
|
entries the object is auto-deleted. |
|
|
|
Unique features and special Behavour: |
|
- _AgentElem_CloseAgentItem is called at any time is there is an unrecoverable |
|
error or the connection has been lost and it will iterate thru the existing |
|
operations/requests sending an appropiate error message to each one. |
|
- _AgentElem_EntryAck is schedule by each entry (RequestItem) when it |
|
receives and Ack so that Ack can be simply passed thru to the connection |
|
(since we dont have yet a more sophisticated on-the-wire flow control |
|
mechanism). |
|
*/ |
|
static StrandFT _AgentElem_FT = { |
|
_AgentElem_Post, |
|
_AgentElem_PostControl, |
|
_AgentElem_Ack, |
|
_AgentElem_Cancel, |
|
_AgentElem_Close, |
|
_AgentElem_Finish, |
|
NULL, |
|
_AgentElem_CloseAgentItem, |
|
_AgentElem_EntryAck, |
|
NULL, |
|
NULL, |
|
NULL }; |
|
|
|
/* |
|
**============================================================================== |
|
*/ |
|
|
|
static MI_Result _PrepareMessageForAgent( |
|
MI_Uint64 operationId, |
|
_In_ Message* msg, |
|
_Out_ Message** msgOut) |
|
{ |
|
MI_Result result; |
|
|
|
/* clone message (may be incoming message in some cases) */ |
|
result = MessagePackCloneForBinarySending(msg, msgOut); |
|
if( MI_RESULT_OK == result ) |
|
{ |
|
/* substitute message-id on time server->agent call; |
|
operationId has to be restored once first response is received */ |
|
(*msgOut)->operationId = operationId; |
|
(*msgOut)->flags = msg->flags; |
|
} |
|
else |
|
{ |
|
trace_SendRequestToAgent_MessageCloneFailed(result); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
static void _SendErrorResponseAndClose( |
|
RequestItem* requestItem, |
|
MI_Result r) |
|
{ |
|
PostResultMsg* resp; |
|
trace_AgentMgr_SendErrorResponse( requestItem ); |
|
resp = PostResultMsg_NewAndSerialize( requestItem->request, NULL, NULL, MI_RESULT_TYPE_MI, r); |
|
|
|
//TODO - do a force close |
|
if (!resp) |
|
return; |
|
|
|
requestItem->request->operationId = requestItem->originalOperationId; |
|
|
|
requestItem->finishOnErrorState = RequestItemFinishState_ProcessedFinishOnError; |
|
Strand_Post(&requestItem->strand.strand, &resp->base); |
|
|
|
PostResultMsg_Release(resp); |
|
|
|
Strand_Close(&requestItem->strand.strand); |
|
} |
|
|
|
// not used much yet (no secondary "semantic" messages at the time) |
|
// currently used for unsubscribe message |
|
void _RequestItem_Post( _In_ Strand* self_, _In_ Message* msg) |
|
{ |
|
RequestItem* self = (RequestItem*)StrandEntry_FromStrand(self_); |
|
MI_Result result; |
|
Message* msgOut = NULL; |
|
|
|
DEBUG_ASSERT( NULL != self_ ); |
|
trace_RequestItemPostingMessage( self_, &self->strand.strand.info.interaction, self->strand.strand.info.interaction.other ); |
|
|
|
result = _PrepareMessageForAgent( self->key, msg, &msgOut ); |
|
if( MI_RESULT_OK == result ) |
|
{ |
|
StrandEntry_PostParentPassthru(&self->strand, msgOut); |
|
Message_Release(msgOut); |
|
} |
|
else |
|
{ |
|
trace_RequestItem_PostFailed(result); |
|
} |
|
} |
|
|
|
void _RequestItem_PostControl( _In_ Strand* self_, _In_ Message* msg) |
|
{ |
|
DEBUG_ASSERT( MI_FALSE ); // not used yet |
|
} |
|
|
|
void _RequestItem_Ack( _In_ Strand* self_) |
|
{ |
|
RequestItem* self = (RequestItem*)StrandEntry_FromStrand(self_); |
|
DEBUG_ASSERT( NULL != self_ ); |
|
trace_RequestItemAck( &self_->info.interaction, self_->info.interaction.other ); |
|
|
|
if (self->finishOnErrorState != RequestItemFinishState_ProcessedFinishOnError) |
|
{ |
|
StrandEntry_ScheduleAuxParent( &self->strand, AGENTELEM_STRANDAUX_ENTRYACK ); |
|
} |
|
|
|
if (self->finishOnErrorState == RequestItemFinishState_PendingFinishOnError) |
|
{ |
|
//this is going to set finishOnErrorState to processed state |
|
_SendErrorResponseAndClose(self, MI_RESULT_FAILED); |
|
} |
|
|
|
|
|
//TODO eventually multiplexer "WITH FC Buffers" should take care of flow control here |
|
} |
|
|
|
void _RequestItem_SendCancel( _In_ RequestItem* self ) |
|
{ |
|
CancelMsg* msg = CancelMsg_New(self->originalOperationId); // this will get transformed on _SendMessageToAgent |
|
MI_Result result; |
|
Message* msgOut = NULL; |
|
|
|
DEBUG_ASSERT( NULL != self ); |
|
|
|
trace_RequestItemCancel( &self->strand.strand ); |
|
|
|
|
|
if( NULL != msg ) |
|
{ |
|
result = _PrepareMessageForAgent( self->key, &msg->base, &msgOut ); |
|
if( MI_RESULT_OK == result ) |
|
{ |
|
StrandEntry_PostParent(&self->strand, msgOut); |
|
Message_Release(msgOut); |
|
} |
|
else |
|
{ |
|
trace_RequestItem_Cancel_PrepMessageFailed(result); |
|
} |
|
CancelMsg_Release(msg); |
|
} |
|
else |
|
{ |
|
trace_RequestItem_Cancel_CancelMsg_NewFailed(); |
|
} |
|
} |
|
|
|
void _RequestItem_Cancel( _In_ Strand* self_ ) |
|
{ |
|
RequestItem* self = (RequestItem*)StrandEntry_FromStrand(self_); |
|
|
|
if( self->strand.strand.info.otherAckPending ) |
|
{ |
|
DEBUG_ASSERT( self->strand.ackPassthru ); |
|
// Do nothing now, when the Ack from parent arrives we will send the message |
|
self->pendingCancel = MI_TRUE; |
|
} |
|
else |
|
{ |
|
_RequestItem_SendCancel( self ); |
|
} |
|
} |
|
|
|
void _RequestItem_Finish( _In_ Strand* self_) |
|
{ |
|
RequestItem* self = (RequestItem*)StrandEntry_FromStrand(self_); |
|
|
|
// release request before we delete the entry |
|
if( NULL != self->request ) |
|
{ |
|
Message_Release(self->request); |
|
self->request = NULL; |
|
} |
|
|
|
StrandEntry_Delete( &self->strand ); |
|
} |
|
|
|
// REQUESTITEM_STRANDAUX_PREPARETOFINISHONERROR |
|
void _RequestItem_PrepareToFinishOnError( _In_ Strand* self_) |
|
{ |
|
RequestItem* self = (RequestItem*)StrandEntry_FromStrand(self_); |
|
|
|
//if we already closed other |
|
if (self_->info.thisClosedOther) |
|
return; |
|
|
|
//if there isn't an ack pending on a previous post, send the final response |
|
if (!self_->info.thisAckPending) |
|
{ |
|
_SendErrorResponseAndClose(self, MI_RESULT_FAILED); |
|
return; |
|
} |
|
|
|
//else mark for finish when the next ack arrives |
|
self->finishOnErrorState = RequestItemFinishState_PendingFinishOnError; |
|
} |
|
|
|
/* |
|
Object that implements a single operation/request going to an agent thru |
|
a binary protocol connection. Uses that one-to-many interface to multiplex multiple |
|
operations in a single connection (AgentElem). |
|
|
|
Behavior: |
|
- Post calls _PrepareMessageForAgent to adapt the message to be send on the |
|
wire the uses StrandEntry_PostParentPassthru to post to the parent |
|
using the default many-to-one post implementation that enques the message |
|
on the AgentElem |
|
- Ack checks on the state of finishOnErrorState. On the normal case the ack is |
|
just passed thru to the parent by using AGENTELEM_STRANDAUX_ENTRYACK |
|
but if the state is RequestItemFinishState_PendingFinishOnError then an |
|
error is send instead and if the state is RequestItemFinishState_ProcessedFinishOnError |
|
it means nothing else is needed. |
|
- PostControl is not currently implemented |
|
- Cancel check if there is an Ack pending on the other side of the interaction (dispatcher) |
|
what means that in that case the cancel cannot be sent immediately and just |
|
set pendingCancel to true (see _RequestItem_ParentAck below). Otherwise sends |
|
the cancel immediately. |
|
- Close uses the default implementation |
|
- Shutdown: |
|
The objects are deleted thru the normal Strand logic. That is, |
|
once the interaction is closed on both sides the object is auto-deleted. |
|
Note that the interaction is closed once the final message is received as |
|
noted in _RequestItem_ParentPost below |
|
|
|
Unique features and special Behavour: |
|
- _RequestItem_PrepareToFinishOnError is scheduled when the parent |
|
_AgentElem_CloseAgentItem execute, that is, when for some reason |
|
the connection to the agent needs to be closed. In that case it checks |
|
if the Interaction has been already closed (so it doesnt need to do anything), |
|
or if there is an Ack pending on the other side of the interaction (dispatcher) |
|
what means that in that case the cancel cannot be sent immediately and just |
|
sets finishOnErrorState RequestItemFinishState_PendingFinishOnError. |
|
Otherwise it will directly send the error response and close the interaction |
|
(finishOnErrorState will be set to RequestItemFinishState_ProcessedFinishOnError |
|
in that case). |
|
- _RequestItem_AddedToParent is used to deliver the initial request ONCE |
|
the Item has been added to the parent AgentElem connection |
|
- _RequestItem_ParentPost just post the message from the parent back to |
|
the left interaction (typically dispatcher) unless the connection has already being |
|
closed for some reason. It restores the original operationId that is replaced in |
|
the connection to the agent and it also checks if the message is a final message, |
|
in which case closes the interaction. |
|
- _RequestItem_ParentAck checks if the corresponding Post was PassThru |
|
and in that case sends the Ack passThru. It also checks if there was a pending |
|
cancel to be send (see Cancel above) and send its now that is possible. |
|
*/ |
|
static StrandFT _RequestItem_FT = { |
|
_RequestItem_Post, |
|
_RequestItem_PostControl, |
|
_RequestItem_Ack, |
|
_RequestItem_Cancel, |
|
NULL, |
|
_RequestItem_Finish, |
|
NULL, |
|
_RequestItem_PrepareToFinishOnError, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL }; |
|
|
|
|
|
void _IdleRequestItem_Post( _In_ Strand* self_, _In_ Message* msg) |
|
{ |
|
DEBUG_ASSERT( MI_FALSE ); // not used |
|
} |
|
|
|
void _IdleRequestItem_PostControl( _In_ Strand* self_, _In_ Message* msg) |
|
{ |
|
DEBUG_ASSERT( MI_FALSE ); // not used |
|
} |
|
|
|
void _IdleRequestItem_Ack( _In_ Strand* self_ ) |
|
{ |
|
DEBUG_ASSERT( MI_FALSE ); // not used |
|
} |
|
|
|
void _IdleRequestItem_Finish( _In_ Strand* self_) |
|
{ |
|
RequestItem* self = (RequestItem*)StrandEntry_FromStrand(self_); |
|
|
|
DEBUG_ASSERT( NULL == self->request ); |
|
|
|
StrandEntry_Delete( &self->strand ); |
|
} |
|
|
|
// IDLEREQUESTITEM_STRANDAUX_READYTOFINISH |
|
void _IdleRequestItem_ReadyToFinish( _In_ Strand* self_) |
|
{ |
|
RequestItem* self = (RequestItem*)StrandEntry_FromStrand(self_); |
|
|
|
Strand_ResetDelayFinish(&self->strand.strand); |
|
} |
|
|
|
/* |
|
Object that implements the especific request needed to receive the Idle notification |
|
from the agent. It is attached as one Entries on the one-to-many interface with the |
|
agent connection (AgentElem). |
|
|
|
Behavior: |
|
- Post, PostControl and Ack are never used |
|
- Shutdown: |
|
The object is not deleted thru the normal Strand logic but only |
|
when the connection is finished. For that SetDelayFinish is set, |
|
and only reset when _IdleRequestItem_ReadyToFinish is called, |
|
what would happen when the parent agentElem is closed. |
|
Aalso note the the idle notification is processed directly by the |
|
AgentElem as never reaches the IdleRequestItem. |
|
IdleRequestItem is only used to initiate the corresponding object, |
|
IdleNotification, in the agent ) |
|
*/ |
|
static StrandFT _IdleRequestItem_FT = { |
|
_IdleRequestItem_Post, |
|
_IdleRequestItem_PostControl, |
|
_IdleRequestItem_Ack, |
|
NULL, |
|
NULL, |
|
_IdleRequestItem_Finish, |
|
NULL, |
|
_IdleRequestItem_ReadyToFinish, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL }; |
|
|
|
/* |
|
**============================================================================== |
|
*/ |
|
|
|
void _AgentElem_NewEntry( _In_ StrandMany* self, _In_ StrandEntry* newEntry, _In_opt_ Message* msg, _Inout_ MI_Boolean* failed ) |
|
{ |
|
DEBUG_ASSERT( NULL != failed ); |
|
DEBUG_ASSERT( !(*failed) ); //TODO process this properly |
|
|
|
// Nothing to do here. The entry will post its initial message once it receives AddedToParent |
|
} |
|
|
|
void _RequestItem_AddedToParent( _In_ StrandEntry* self, _In_ Message* msg ) |
|
{ |
|
RequestItem* requestItem = (RequestItem*)self; |
|
|
|
DEBUG_ASSERT( NULL != msg ); |
|
|
|
if( requestItem->isIdleRequest ) |
|
{ |
|
// There is no Interaction to deliver an Ack in this case |
|
StrandEntry_PostParent( &requestItem->strand, msg ); |
|
} |
|
else |
|
{ |
|
StrandEntry_PostParentPassthru( &requestItem->strand, msg ); |
|
} |
|
} |
|
|
|
// Message back from protocol |
|
void _RequestItem_ParentPost( _In_ StrandEntry* self, _In_ Message* msg) |
|
{ |
|
RequestItem* requestItem = (RequestItem*)self; |
|
//should never receive a parent post when the parent previously told us to wrap up things on error |
|
DEBUG_ASSERT(requestItem->finishOnErrorState == RequestItemFinishState_None); |
|
|
|
if( self->strand.info.thisClosedOther ) |
|
{ |
|
trace_RequestItem_ParentPost_AfterClose( requestItem, msg ); |
|
} |
|
else |
|
{ |
|
trace_RequestItemParentPost( requestItem, msg ); |
|
|
|
/* restore operationId */ |
|
requestItem->request->operationId = requestItem->originalOperationId; |
|
msg->operationId = requestItem->originalOperationId; |
|
|
|
Strand_Post( &requestItem->strand.strand, msg ); |
|
|
|
/* remove item if result received */ |
|
if( Message_IsFinalResponse(msg) ) |
|
{ |
|
// Now we can close interaction (nothing else is going to be posted) |
|
Strand_Close( &requestItem->strand.strand ); |
|
} |
|
} |
|
} |
|
|
|
void _RequestItem_ParentAck(_In_ StrandEntry* self) |
|
{ |
|
RequestItem* requestItem = (RequestItem*)self; |
|
|
|
if( !requestItem->isIdleRequest ) |
|
{ |
|
if( requestItem->strand.ackPassthru ) |
|
{ |
|
|
|
if( requestItem->pendingCancel ) |
|
{ |
|
requestItem->pendingCancel = MI_FALSE; |
|
_RequestItem_SendCancel( requestItem ); |
|
} |
|
|
|
Strand_Ack( &self->strand ); |
|
} |
|
else |
|
{ |
|
//only case we do a Parent Post without a need to ackPassthru is when |
|
//posting CancelMsg (See _RequestItem_SendCancel). The strand should have been in canceled state by then |
|
DEBUG_ASSERT(self->strand.canceled); |
|
} |
|
} |
|
else |
|
{ |
|
DEBUG_ASSERT( !requestItem->strand.ackPassthru ); |
|
} |
|
} |
|
|
|
static StrandManyInternalFT _AgentElem_InternalFT = { |
|
_AgentElem_NewEntry, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL, |
|
_RequestItem_AddedToParent, |
|
_RequestItem_ParentPost, |
|
NULL, |
|
_RequestItem_ParentAck, |
|
NULL }; |
|
/* |
|
**============================================================================== |
** | ** |
** Local functions | ** Local functions |
** | ** |
**============================================================================== | **============================================================================== |
*/ | */ |
|
static MI_Uint64 _NextOperationId() |
|
{ |
|
static ptrdiff_t _operationId = 0; |
|
return (MI_Uint64) Atomic_Inc(&_operationId); |
|
} |
|
|
|
|
|
// Called with AgentMgr lock acquired |
static AgentElem* _FindAgent( | static AgentElem* _FindAgent( |
AgentMgr* self, | AgentMgr* self, |
uid_t uid, | uid_t uid, |
gid_t gid) | gid_t gid) |
{ | { |
AgentElem* agent; | AgentElem* agent; |
|
ListElem* elem; |
| |
agent = self->headAgents; |
elem = self->headAgents; |
| |
while (agent) |
while (elem) |
{ | { |
|
agent = FromOffset(AgentElem,next,elem); |
|
|
if (uid == agent->uid && gid == agent->gid) | if (uid == agent->uid && gid == agent->gid) |
|
{ |
return agent; | return agent; |
|
} |
| |
agent = agent->next; |
elem = elem->next; |
} | } |
|
|
return 0; | return 0; |
} | } |
| |
|
|
char param_sock[32]; | char param_sock[32]; |
char param_logfd[32]; | char param_logfd[32]; |
char param_idletimeout[32]; | char param_idletimeout[32]; |
|
const char* agentProgram = OMI_GetPath(ID_AGENTPROGRAM); |
| |
child = fork(); | child = fork(); |
| |
|
|
Snprintf(param_logfd, sizeof(param_logfd), "%d", (int)logfd); | Snprintf(param_logfd, sizeof(param_logfd), "%d", (int)logfd); |
Snprintf(param_idletimeout, sizeof(param_idletimeout), "%d", (int)idletimeout); | Snprintf(param_idletimeout, sizeof(param_idletimeout), "%d", (int)idletimeout); |
| |
execl(GetPath(ID_AGENTPROGRAM), |
execl(agentProgram, |
GetPath(ID_AGENTPROGRAM), |
agentProgram, |
param_sock, | param_sock, |
param_logfd, | param_logfd, |
"--destdir", | "--destdir", |
GetPath(ID_DESTDIR), |
OMI_GetPath(ID_DESTDIR), |
"--providerdir", | "--providerdir", |
provDir, | provDir, |
"--idletimeout", | "--idletimeout", |
|
|
Log_GetLevelString(Log_GetLevel()), | Log_GetLevelString(Log_GetLevel()), |
NULL); | NULL); |
| |
LOGW_CHAR(("agent launching: failed to exec %s, errno %d\n", |
trace_AgentLaunch_Failed(scs(agentProgram), errno); |
GetPath(ID_AGENTPROGRAM), |
|
errno)); |
|
_exit(1); | _exit(1); |
return -1; /* never get here */ | return -1; /* never get here */ |
} | } |
| |
static void _SendErrorResponse( |
static void _AgentElem_CloseAgentItem( Strand* self_ ) |
RequestItem* requestItem, |
|
MI_Result r) |
|
{ | { |
PostResultMsg* resp; |
AgentElem* agent = (AgentElem*)StrandMany_FromStrand(self_); |
|
RequestItem* requestItem; |
resp = PostResultMsg_New( requestItem->originalMsgID ); |
|
|
|
if (!resp) |
|
return; |
|
|
|
requestItem->request->msgID = requestItem->originalMsgID; |
|
resp->base.clientID = requestItem->request->clientID; |
|
|
|
resp->result = r; |
|
Message_SetRequest(&resp->base,requestItem->request); |
|
(*requestItem->request->callback)(&resp->base, requestItem->request->callbackData); |
|
| |
PostResultMsg_Release(resp); |
StrandMany_BeginIteration( &agent->strand ); |
} |
|
| |
static void _FreeItem( |
|
AgentElem*agent) |
|
{ |
|
/* send error repsonses to all outstanding requests */ | /* send error repsonses to all outstanding requests */ |
while (agent->headRequests) |
while( NULL != (requestItem = (RequestItem*)StrandMany_Iterate( &agent->strand )) ) |
{ | { |
RequestItem* requestItem = agent->headRequests; |
if( requestItem->isIdleRequest ) |
|
{ |
_SendErrorResponse(requestItem, MI_RESULT_FAILED); |
StrandEntry_ScheduleAux(&requestItem->strand, IDLEREQUESTITEM_STRANDAUX_READYTOFINISH ); |
|
} |
/* free item from list */ |
else |
List_Remove( |
{ |
(ListElem**)&agent->headRequests, |
StrandEntry_ScheduleAux(&requestItem->strand, REQUESTITEM_STRANDAUX_PREPARETOFINISHONERROR ); |
(ListElem**)&agent->tailRequests, |
} |
(ListElem*)requestItem); |
// No need to call StrandMany_DeleteEntry(&requestItem->strand) to delete requestItem (it would do so itself ) |
|
|
/* Note: |
|
request item was allocated form message's batch so no need to free it directly; |
|
Message's release may free batch and invalidate requestItem */ |
|
Message_Release(requestItem->request); |
|
} | } |
| |
if (agent->protocol) | if (agent->protocol) |
Protocol_Delete(agent->protocol); |
{ |
|
// We can now close the interaction with protocol if not done already |
|
if( !agent->strand.strand.info.thisClosedOther ) |
|
Strand_Close( &agent->strand.strand ); |
|
} |
|
else |
|
{ |
|
agent->strand.strand.info.thisClosedOther = agent->strand.strand.info.otherClosedThis = MI_TRUE; |
|
} |
| |
/* SIGCHILD HANDLER will take care of pid waiting */ | /* SIGCHILD HANDLER will take care of pid waiting */ |
/*if (agent->agentPID > 0) | /*if (agent->agentPID > 0) |
|
|
} | } |
}*/ | }*/ |
| |
free(agent); |
// the AgentElem should delete itself on Finish |
} | } |
| |
static void _EventCallback( |
size_t _AgentElem_HashMapHashProc(const HashBucket* bucket) |
Protocol* protocol, |
|
ProtocolEvent event, |
|
void* data) |
|
{ | { |
AgentElem* agent = (AgentElem*)data; |
const RequestItem* self = (const RequestItem*)StrandEntry_FromBucketConst(bucket); |
|
return (size_t)self->key; |
MI_UNUSED(protocol); |
|
MI_UNUSED(event); |
|
|
|
DEBUG_ASSERT(PROTOCOLEVENT_DISCONNECT == event); |
|
|
|
|
|
LOGW_CHAR(("lost connection to agent running as [%d]", (int)agent->uid)); |
|
/* lost connection to the agent: |
|
- send error repsonses to all outstanding requests ( within 'FreeItem' call) |
|
- remove agent form the list |
|
*/ |
|
|
|
/* remove agent from Mgr's list */ |
|
List_Remove( |
|
(ListElem**)&agent->agentMgr->headAgents, |
|
(ListElem**)&agent->agentMgr->tailAgents, |
|
(ListElem*)agent); |
|
|
|
_FreeItem(agent); |
|
} | } |
| |
static MI_Boolean _RequestCallback( |
int _AgentElem_HashMapEqualProc(_In_ const HashBucket* bucket1, _In_ const HashBucket* bucket2) |
Protocol* protocol_, |
|
Message* msg, |
|
void* data) |
|
{ | { |
AgentElem* self = (AgentElem*)data; |
const RequestItem* entry1 = (const RequestItem*)StrandEntry_FromBucketConst(bucket1); |
RequestItem* requestItem; |
const RequestItem* entry2 = (const RequestItem*)StrandEntry_FromBucketConst(bucket2); |
|
return entry1->key == entry2->key; |
MI_UNUSED(protocol_); |
|
|
|
/* ATTN: verify unload message */ |
|
if (BinProtocolNotificationTag == msg->tag) |
|
{ |
|
BinProtocolNotification* notification = (BinProtocolNotification*)msg; |
|
if (BinNotificationAgentIdle == notification->type) |
|
{ |
|
/* Check if this agnet has outstanding requests - |
|
if agent is idle, return false form callback |
|
to close connection to it. |
|
It will forces protocl to close conneciton and send |
|
'disconnect' event to event-callback |
|
*/ |
|
if (self->headRequests) |
|
return MI_TRUE; |
|
else |
|
return MI_FALSE; |
|
} |
|
/* ignore service messages */ |
|
return MI_TRUE; |
|
} | } |
| |
|
StrandEntry* _AgentElem_FindRequest(_In_ const StrandMany* parent, _In_ const Message* msg) |
/* get/verify request-item */ |
|
requestItem = (RequestItem*)Uint64ToPtr(msg->msgID); |
|
|
|
/* ATTN: add verification if element is in list to prevent incorect pointer attack */ |
|
{ | { |
RequestItem* cur = self->headRequests; |
AgentElem* agent = (AgentElem*)parent; |
|
RequestItem forSearch; |
|
HashBucket* bucket; |
| |
while (cur) |
forSearch.key = msg->operationId; |
{ |
|
if (cur == requestItem) |
|
break; |
|
| |
cur = cur->next; |
bucket = HashMap_Find(&agent->strand.many,&forSearch.strand.bucket); |
} |
|
| |
if (cur != requestItem) |
if( NULL == bucket ) |
{ | { |
LOGE_CHAR(("cannot find request item %p; agent for user %d!\n", requestItem, (int)self->uid)); |
trace_AgentElem_FindRequest_CannotFindKey( agent, &agent->strand.strand, forSearch.key ); |
return MI_FALSE; |
return NULL; |
} |
|
} | } |
|
else |
/* restore msgID */ |
|
requestItem->request->msgID = requestItem->originalMsgID; |
|
msg->msgID = requestItem->originalMsgID; |
|
msg->clientID = requestItem->request->clientID; |
|
|
|
/* Attach request and post repsonse */ |
|
Message_SetRequest(msg,requestItem->request); |
|
(*requestItem->request->callback)(msg, requestItem->request->callbackData); |
|
|
|
/* remove item if result received */ |
|
if (msg->tag == PostResultMsgTag) |
|
{ | { |
List_Remove( |
RequestItem* self = (RequestItem*)StrandEntry_FromBucket(bucket); |
(ListElem**)&self->headRequests, |
trace_AgentElemFoundKey( agent, &agent->strand.strand, forSearch.key, self, &self->strand.strand ); |
(ListElem**)&self->tailRequests, |
return (StrandEntry*)self; |
(ListElem*)requestItem); |
|
|
|
/* Note: |
|
request item was allocated form message's batch so no need to free it directly; |
|
Message's release may free batch and invalidate requestItem */ |
|
Message_Release(requestItem->request); |
|
} | } |
|
|
return MI_TRUE; |
|
} | } |
| |
|
// Called with AgentMgr lock acquired |
static AgentElem* _CreateAgent( | static AgentElem* _CreateAgent( |
AgentMgr* self, |
_In_ AgentMgr* self, |
uid_t uid, | uid_t uid, |
gid_t gid) | gid_t gid) |
{ | { |
AgentElem* agent = 0; | AgentElem* agent = 0; |
Sock s[2]; | Sock s[2]; |
int logfd = -1; | int logfd = -1; |
|
InteractionOpenParams interactionParams; |
| |
/* create communication pipe */ | /* create communication pipe */ |
if(0 != socketpair(AF_UNIX, SOCK_STREAM, 0, s)) | if(0 != socketpair(AF_UNIX, SOCK_STREAM, 0, s)) |
{ | { |
LOGW_CHAR(("socketpair() failed\n")); |
trace_SocketPair_Failed(); |
return 0; | return 0; |
} | } |
| |
if (MI_RESULT_OK != Sock_SetBlocking(s[0], MI_FALSE) || | if (MI_RESULT_OK != Sock_SetBlocking(s[0], MI_FALSE) || |
MI_RESULT_OK != Sock_SetBlocking(s[1], MI_FALSE)) | MI_RESULT_OK != Sock_SetBlocking(s[1], MI_FALSE)) |
{ | { |
LOGW_CHAR(("set non-blocking failed\n")); |
trace_SetNonBlocking_Failed(); |
goto failed; | goto failed; |
} | } |
| |
/* create/open log file for agent */ | /* create/open log file for agent */ |
{ | { |
char path[MAX_PATH_SIZE]; |
char path[PAL_MAX_PATH_SIZE]; |
| |
if (0 != FormatLogFileName(uid, gid, path)) | if (0 != FormatLogFileName(uid, gid, path)) |
{ | { |
LOGW_CHAR(("cannot format log file name\n")); |
trace_CannotFormatLogFilename(); |
goto failed; | goto failed; |
} | } |
| |
|
|
logfd = open(path, O_WRONLY|O_CREAT|O_APPEND, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); | logfd = open(path, O_WRONLY|O_CREAT|O_APPEND, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); |
if (logfd == -1) | if (logfd == -1) |
{ | { |
LOGW_CHAR(("failed to create log file %s; errno %d", path, (int)errno)); |
trace_CreateLogFile_Failed(scs(path), (int)errno); |
goto failed; | goto failed; |
} | } |
} | } |
| |
agent = (AgentElem*)calloc(1, sizeof(*agent)); |
agent = (AgentElem*)StrandMany_New( |
|
STRAND_DEBUG( AgentElem ) |
|
&_AgentElem_FT, |
|
&_AgentElem_InternalFT, |
|
sizeof(AgentElem), |
|
STRAND_FLAG_ENTERSTRAND, |
|
NULL, |
|
100, |
|
_AgentElem_HashMapHashProc, |
|
_AgentElem_HashMapEqualProc, |
|
_AgentElem_FindRequest ); |
| |
if (!agent) | if (!agent) |
goto failed; | goto failed; |
|
|
self->provDir, | self->provDir, |
(MI_Uint32)(self->provmgr.idleTimeoutUsec / 1000000))) < 0) | (MI_Uint32)(self->provmgr.idleTimeoutUsec / 1000000))) < 0) |
{ | { |
LOGW_CHAR(("cannot spawn a child process\n")); |
trace_CannotSpawnChildProcess(); |
goto failed; | goto failed; |
} | } |
| |
|
|
Sock_Close(s[0]); | Sock_Close(s[0]); |
s[0] = INVALID_SOCK; | s[0] = INVALID_SOCK; |
| |
if (MI_RESULT_OK != Protocol_New_From_Socket( |
Strand_OpenPrepare(&agent->strand.strand,&interactionParams,NULL,NULL,MI_TRUE); |
|
|
|
if( MI_RESULT_OK != ProtocolSocketAndBase_New_AgentConnector( |
&agent->protocol, | &agent->protocol, |
self->selector, | self->selector, |
s[1], | s[1], |
MI_TRUE, |
&interactionParams ) ) |
_RequestCallback, |
|
agent, |
|
_EventCallback, |
|
agent)) |
|
goto failed; | goto failed; |
| |
s[1] = INVALID_SOCK; | s[1] = INVALID_SOCK; |
| |
|
trace_AgentItemCreated(agent); |
List_Append( | List_Append( |
(ListElem**)&self->headAgents, |
&self->headAgents, |
(ListElem**)&self->tailAgents, |
&self->tailAgents, |
(ListElem*)agent); |
(ListElem*)&(agent->next)); |
| |
return agent; | return agent; |
| |
|
|
close(logfd); | close(logfd); |
| |
if (agent) | if (agent) |
_FreeItem(agent); |
{ |
|
_AgentElem_InitiateClose(agent); |
|
} |
| |
return 0; | return 0; |
} | } |
| |
static AgentElem* _FindOrCreateAgent( |
// Called with AgentMgr lock acquired |
AgentMgr* self, |
static MI_Result _SendRequestToAgent_Common( |
uid_t uid, |
_In_ RequestItem* requestItem, |
gid_t gid) |
_In_ Message* msg, |
|
_In_opt_ const ProvRegEntry* proventry) |
{ | { |
AgentElem* agent = _FindAgent(self, uid, gid); |
MI_Result result; |
|
Message* req = NULL; |
|
MI_Uint64 operationId; |
| |
if (!agent) |
operationId = _NextOperationId(); |
agent = _CreateAgent(self, uid, gid); |
requestItem->key = operationId; |
| |
return agent; |
result = _PrepareMessageForAgent( operationId, msg, &req ); |
|
if( MI_RESULT_OK != result ) |
|
{ |
|
trace_PrepareMessageForAgent_Failed(result); |
|
StrandEntry_DeleteNoAdded( &requestItem->strand ); |
|
return result; |
} | } |
| |
static MI_Result _SendMessageToAgent( |
if( NULL != proventry ) |
AgentElem* agent, |
{ |
Message* msg, |
DEBUG_ASSERT( Message_IsRequest(req) ); |
const ProvRegEntry* proventry) |
{ |
{ |
RequestMsg* request = (RequestMsg*)req; |
RequestItem* newRequestItem; |
request->libraryName = Batch_Strdup(req->batch, proventry->libraryName); |
Message* req = 0; |
request->instanceLifetimeContext = proventry->instanceLifetimeContext; |
MI_Result r; |
|
|
|
/* Allocate item form incoming message batch */ |
|
newRequestItem = (RequestItem*) Batch_GetClear(msg->batch, sizeof(*newRequestItem)); |
|
| |
if (!newRequestItem) |
if (!request->libraryName) |
|
{ |
|
trace_SendRequestToAgent_Batch_Strdup_Failed(); |
|
StrandEntry_DeleteNoAdded( &requestItem->strand ); |
|
Message_Release(req); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
|
} |
|
} |
|
} |
| |
newRequestItem->request = msg; |
trace_AgentMgr_SendRequestToAgent( |
|
req, |
|
req->tag, |
|
MessageName(req->tag), |
|
req->operationId, |
|
requestItem->originalOperationId, |
|
requestItem->key ); |
| |
/* clone message (may be incoming message in some cases) */ |
StrandEntry_ScheduleAdd( &requestItem->strand, req); |
if (MI_RESULT_OK != MessagePackCloneForBinarySending(msg, &req)) |
|
|
Message_Release(req); |
|
|
|
return MI_RESULT_OK; |
|
} |
|
|
|
// Called with AgentMgr lock acquired |
|
static MI_Result _SendIdleRequestToAgent( |
|
_Inout_ AgentElem* agent ) |
|
{ |
|
RequestItem* requestItem; |
|
MI_Result result; |
|
BinProtocolNotification* notification; |
|
|
|
notification = BinProtocolNotification_New( BinNotificationAgentIdle ); |
|
|
|
if( !notification ) |
{ | { |
LOGW_CHAR(("message clone failed\n")); |
trace_SendRequestToAgent_StrandNewFailed(); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
req->libraryName = Batch_Strdup2(req->batch, proventry->libraryName); |
requestItem = (RequestItem*) StrandEntry_New( |
|
STRAND_DEBUG( IdleRequestItem ) |
if (!req->libraryName) |
&agent->strand, |
|
&_IdleRequestItem_FT, |
|
sizeof(RequestItem), |
|
STRAND_FLAG_ENTERSTRAND|STRAND_FLAG_NOINTERACTION, |
|
NULL ); |
|
if( NULL == requestItem ) |
{ | { |
Message_Release(req); |
trace_SendRequestToAgent_StrandNewFailed(); |
|
BinProtocolNotification_Release(notification); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
| |
/* Add ref to keep request around until Result received */ |
requestItem->isIdleRequest = MI_TRUE; |
Message_AddRef(msg); |
requestItem->pendingCancel = MI_FALSE; |
|
requestItem->finishOnErrorState = RequestItemFinishState_None; |
|
Strand_SetDelayFinish(&requestItem->strand.strand); |
|
Strand_Leave(&requestItem->strand.strand); |
| |
newRequestItem->originalMsgID = msg->msgID; |
result = _SendRequestToAgent_Common( requestItem, ¬ification->base, NULL ); |
| |
/* substitute message-id on time server->agent call; |
BinProtocolNotification_Release(notification); |
msgID has to be restored once first response is received */ |
|
req->msgID = PtrToUint64(newRequestItem); |
|
| |
List_Append( |
return result; |
(ListElem**)&agent->headRequests, |
} |
(ListElem**)&agent->tailRequests, |
|
(ListElem*)newRequestItem); |
|
| |
r = Protocol_Send(agent->protocol, req); |
// Called with AgentMgr lock acquired |
Message_Release(req); |
static MI_Result _SendRequestToAgent( |
|
_Inout_ AgentElem* agent, |
|
_In_ InteractionOpenParams* interactionParams, |
|
_In_ Message* msg, |
|
_In_ const ProvRegEntry* proventry) |
|
{ |
|
RequestItem* requestItem; |
| |
return r; |
DEBUG_ASSERT( NULL != interactionParams ); |
|
trace_SendMessageToAgent( msg->tag ); |
|
|
|
requestItem = (RequestItem*) StrandEntry_New( |
|
STRAND_DEBUG( RequestItem ) |
|
&agent->strand, |
|
&_RequestItem_FT, |
|
sizeof(RequestItem), |
|
0, |
|
interactionParams ); |
|
if( NULL == requestItem ) |
|
{ |
|
trace_SendRequestToAgent_StrandNewFailed(); |
|
return MI_RESULT_FAILED; |
|
} |
|
|
|
requestItem->originalOperationId = msg->operationId; |
|
requestItem->request = msg; |
|
requestItem->isIdleRequest = MI_FALSE; |
|
requestItem->pendingCancel = MI_FALSE; |
|
requestItem->finishOnErrorState = RequestItemFinishState_None; |
|
|
|
/* Add ref to keep request around until Result received */ |
|
Message_AddRef(msg); |
|
|
|
return _SendRequestToAgent_Common( requestItem, msg, proventry ); |
} | } |
| |
#endif | #endif |
|
|
memset(self, 0, sizeof(*self)); | memset(self, 0, sizeof(*self)); |
| |
/* Initialize the provider manager */ | /* Initialize the provider manager */ |
MI_RETURN_ERR(ProvMgr_Init(&self->provmgr, selector, NULL, NULL, GetPath(ID_PROVIDERDIR))); |
MI_RETURN_ERR(ProvMgr_Init(&self->provmgr, selector, NULL, NULL, OMI_GetPath(ID_PROVIDERDIR))); |
| |
self->home = Strdup(GetPath(ID_PREFIX)); |
self->home = PAL_Strdup(OMI_GetPath(ID_PREFIX)); |
self->provDir = Strdup(GetPath(ID_PROVIDERDIR)); |
self->provDir = PAL_Strdup(OMI_GetPath(ID_PROVIDERDIR)); |
| |
self->selector = selector; | self->selector = selector; |
| |
|
ReadWriteLock_Init(&self->lock); |
|
|
|
#if defined(CONFIG_ENABLE_PREEXEC) |
|
PreExec_Construct(&self->preexec); |
|
#endif /* defined(CONFIG_ENABLE_PREEXEC) */ |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
MI_Result AgentMgr_Destroy( | MI_Result AgentMgr_Destroy( |
AgentMgr* self) | AgentMgr* self) |
{ | { |
|
ListElem* listElem; |
|
|
ProvMgr_Destroy(&self->provmgr); | ProvMgr_Destroy(&self->provmgr); |
free(self->home); |
PAL_Free(self->home); |
free(self->provDir); |
PAL_Free(self->provDir); |
|
|
|
#if defined(CONFIG_ENABLE_PREEXEC) |
|
PreExec_Destruct(&self->preexec); |
|
#endif /* defined(CONFIG_ENABLE_PREEXEC) */ |
| |
#if defined(CONFIG_POSIX) | #if defined(CONFIG_POSIX) |
/* | /* |
Free all outstanding agents | Free all outstanding agents |
*/ | */ |
while (self->headAgents) |
ReadWriteLock_AcquireWrite(&self->lock); |
|
listElem = self->headAgents; |
|
while (listElem) |
{ | { |
AgentElem* agent = self->headAgents; |
AgentElem* agent = FromOffset(AgentElem,next,listElem); |
| |
List_Remove( |
StrandMany_ScheduleAux( &agent->strand, AGENTELEM_STRANDAUX_CLOSEAGENTITEM ); |
(ListElem**)&self->headAgents, |
|
(ListElem**)&self->tailAgents, |
|
(ListElem*)agent); |
|
| |
_FreeItem(agent); |
listElem = listElem->next; |
} | } |
|
ReadWriteLock_ReleaseWrite(&self->lock); |
#endif | #endif |
| |
/* Invalidate self */ | /* Invalidate self */ |
|
|
return MI_RESULT_OK; | return MI_RESULT_OK; |
} | } |
| |
|
void AgentMgr_OpenCallback( |
|
_Inout_ InteractionOpenParams* params ) |
|
{ |
|
MI_Result result; |
|
AgentMgr_OpenCallbackData* callbackData = (AgentMgr_OpenCallbackData*)params->callbackData; |
|
|
|
result = AgentMgr_HandleRequest( callbackData->self, params, callbackData->proventry ); |
|
if( MI_RESULT_OK != result ) |
|
{ |
|
Strand_FailOpenWithResult(params, result, PostResultMsg_NewAndSerialize); |
|
} |
|
} |
|
|
MI_Result AgentMgr_HandleRequest( | MI_Result AgentMgr_HandleRequest( |
AgentMgr* self, |
_In_ AgentMgr* self, |
Message* msg, |
_Inout_ InteractionOpenParams* params, |
const ProvRegEntry* proventry) |
_In_ const ProvRegEntry* proventry) |
{ | { |
|
MI_Result result = MI_RESULT_OK; |
AgentElem* agent; | AgentElem* agent; |
uid_t uid; | uid_t uid; |
gid_t gid; | gid_t gid; |
|
RequestMsg* msg = (RequestMsg*)params->msg; |
|
|
|
trace_AgentMgrHandleRequest(msg, msg->base.tag); |
|
|
|
DEBUG_ASSERT( Message_IsRequest(&msg->base) ); |
| |
if (proventry->hosting == PROV_HOSTING_INPROC) | if (proventry->hosting == PROV_HOSTING_INPROC) |
return ProvMgr_PostMessage( |
{ |
|
#if defined(CONFIG_POSIX) |
|
/* For in proc provider, following checks if an incoming |
|
* request from non-root user, and omiserver is running |
|
* under root user, then return access denied error, otherwise |
|
* it could cause a problem that non-root user runs code under root |
|
*/ |
|
if (IsAuthCallsIgnored() == 0) |
|
{ |
|
/* Reject in-proc provider requests for non-root client users */ |
|
if (IsRoot() == 0 && msg->authInfo.uid != 0) |
|
{ |
|
/* user name */ |
|
char name[USERNAME_SIZE]; |
|
char* uname = (char*)name; |
|
if (0 != GetUserName(msg->authInfo.uid, name)) |
|
uname = "unknown user"; |
|
trace_NonRootUserAccessInprocProvider(uname, proventry->className, proventry->nameSpace); |
|
return MI_RESULT_ACCESS_DENIED; |
|
} |
|
} |
|
#endif /* defined(CONFIG_POSIX) */ |
|
|
|
return ProvMgr_NewRequest( |
&self->provmgr, | &self->provmgr, |
proventry->libraryName, |
proventry, |
msg); |
params ); |
|
} |
| |
if (proventry->hosting == PROV_HOSTING_USER) | if (proventry->hosting == PROV_HOSTING_USER) |
{ | { |
if (0 != LookupUser(proventry->user, &uid, &gid)) | if (0 != LookupUser(proventry->user, &uid, &gid)) |
{ | { |
LOGW_CHAR(("get user [%s] uid/gid", proventry->user )); |
trace_GetUserUidGid_Failed(scs(proventry->user)); |
return MI_RESULT_FAILED; | return MI_RESULT_FAILED; |
} | } |
} | } |
else | else |
{ | { |
uid = msg->uid; |
uid = msg->authInfo.uid; |
gid = msg->gid; |
gid = msg->authInfo.gid; |
|
MI_UNREFERENCED_PARAMETER(uid); |
|
MI_UNREFERENCED_PARAMETER(gid); |
} | } |
| |
|
#if defined(CONFIG_ENABLE_PREEXEC) |
|
if (PreExec_Exec(&self->preexec, proventry->preexec, uid, gid) != 0) |
|
return MI_RESULT_FAILED; |
|
#endif /* defined(CONFIG_ENABLE_PREEXEC) */ |
|
|
#if defined(CONFIG_POSIX) | #if defined(CONFIG_POSIX) |
| |
agent = _FindOrCreateAgent(self, uid, gid); |
// We cannot use ReadWriteLock_AcquireRead(&self->lock); |
|
// as we may need to create the object here |
|
// (and there is no option to upgrade from read to write acquisition) |
|
ReadWriteLock_AcquireWrite(&self->lock); |
|
|
|
agent = _FindAgent(self, uid, gid); |
| |
if (!agent) | if (!agent) |
return MI_RESULT_FAILED; |
{ |
|
agent = _CreateAgent(self, uid, gid ); |
|
|
|
if (!agent) |
|
{ |
|
trace_FailedLoadProviderAgent(); |
|
result = MI_RESULT_FAILED; |
|
} |
|
else |
|
{ |
|
result = _SendIdleRequestToAgent( agent ); |
|
} |
|
} |
|
|
|
if( MI_RESULT_OK == result ) |
|
{ |
|
result = _SendRequestToAgent(agent, params, &msg->base, proventry); |
|
} |
|
|
|
ReadWriteLock_ReleaseWrite(&self->lock); |
|
|
|
return result; |
| |
return _SendMessageToAgent(agent, msg, proventry); |
|
#else | #else |
MI_UNUSED(agent); | MI_UNUSED(agent); |
/* windows version hosts all providers as 'in-proc' */ | /* windows version hosts all providers as 'in-proc' */ |
return ProvMgr_PostMessage( |
return ProvMgr_NewRequest( |
&self->provmgr, | &self->provmgr, |
proventry->libraryName, |
proventry, |
msg); |
params ); |
#endif | #endif |
} | } |