AJ_Status AJS_ServiceIO(duk_context* ctx) { AJS_IO_PinTriggerCondition condition; int32_t trigId; trigId = AJS_TargetIO_PinTrigId(&condition); if (trigId != AJS_IO_PIN_NO_TRIGGER) { AJ_InfoPrintf(("triggered on id %d\n", trigId)); /* * Lookup the pin object in the triggers array */ duk_get_global_string(ctx, AJS_IOObjectName); duk_get_prop_string(ctx, -1, AJS_HIDDEN_PROP("trigs")); do { duk_get_prop_index(ctx, -1, trigId); if (duk_is_object(ctx, -1)) { /* * Call the trigger function passing the pin object and value as the argument */ duk_get_prop_string(ctx, -1, "trigger"); if (duk_is_callable(ctx, -1)) { /* * Pin object is the "this" object */ duk_dup(ctx, -2); duk_push_int(ctx, condition); if (duk_pcall_method(ctx, 1) != DUK_EXEC_SUCCESS) { AJS_ConsoleSignalError(ctx); } } /* * Pop pin object */ duk_pop(ctx); } else { AJ_ErrPrintf(("Expected a pin object trigId = %d\n", trigId)); } duk_pop(ctx); trigId = AJS_TargetIO_PinTrigId(&condition); } while (trigId != AJS_IO_PIN_NO_TRIGGER); duk_pop_2(ctx); } return AJ_OK; }
AJ_Status AJS_HandleAcceptSession(duk_context* ctx, AJ_Message* msg, uint16_t port, uint32_t sessionId, const char* joiner) { uint32_t accept = TRUE; SessionInfo* sessionInfo; /* * Create an entry in the sessions table so we can track this peer */ AJS_GetGlobalStashObject(ctx, "sessions"); sessionInfo = AllocSessionObject(ctx, joiner); /* * If there is no handler automatically accept the connection */ AJS_GetAllJoynProperty(ctx, "onPeerConnected"); if (duk_is_callable(ctx, -1)) { /* Empty interface array */ duk_push_array(ctx); AddServiceObject(ctx, sessionInfo, "/", joiner); if (AJS_DebuggerIsAttached()) { msg = AJS_CloneAndCloseMessage(ctx, msg); } if (duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS) { AJS_ConsoleSignalError(ctx); accept = FALSE; } else { accept = duk_get_boolean(ctx, -1); } } duk_pop_2(ctx); /* * It is possible that we already have an outbound session to this peer so if we are not * accepting the session we can only delete the entry if the refCount is zero. */ if (accept) { ++sessionInfo->refCount; sessionInfo->port = port; sessionInfo->sessionId = sessionId; } else if (sessionInfo->refCount == 0) { duk_del_prop_string(ctx, -1, joiner); } /* Pop sessions object */ duk_pop(ctx); return AJ_BusReplyAcceptSession(msg, accept); }
/* * Delete session info object pointed to by sessionId. If sessionId * is zero then delete all session info objects. */ static AJ_Status RemoveSessions(duk_context* ctx, uint32_t sessionId) { AJ_Status status = AJ_OK; AJS_GetGlobalStashObject(ctx, "sessions"); duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); while (duk_next(ctx, -1, 1)) { SessionInfo* sessionInfo; const char* peer = duk_get_string(ctx, -2); duk_get_prop_string(ctx, -1, "info"); sessionInfo = duk_get_buffer(ctx, -1, NULL); duk_pop_3(ctx); if (sessionId == 0) { AJ_InfoPrintf(("RemoveSessions(): Leaving session: %u\n", sessionInfo->sessionId)); status = AJ_BusLeaveSession(AJS_GetBusAttachment(), sessionInfo->sessionId); } else if (sessionInfo->sessionId == sessionId) { status = AJ_BusLeaveSession(AJS_GetBusAttachment(), sessionInfo->sessionId); duk_del_prop_string(ctx, -2, peer); break; } } duk_pop_2(ctx); /* * TODO - this is not all that useful because it only indicates that a peer has gone away * without being able to specify exactly which services are affected. The problem is we cannot * hold a reference to the service object because we a relying on the service object finalizer * to clean up sessions that are no longer in use. If we hold a reference the finalizer will * never get called. */ if (sessionId != 0) { AJS_GetAllJoynProperty(ctx, "onPeerDisconnected"); if (duk_is_callable(ctx, -1)) { if (duk_pcall(ctx, 0) != DUK_EXEC_SUCCESS) { AJS_ConsoleSignalError(ctx); } } duk_pop(ctx); } else { AJS_ClearGlobalStashObject(ctx, "sessions"); } return status; }
/* * Called with "sessions" at the top of the stack. * * Iterate over the accumulated announcents and make callbacks into JavaScript */ static void AnnouncementCallbacks(duk_context* ctx, const char* peer, SessionInfo* sessionInfo) { size_t i; size_t numSvcs; AJ_ASSERT(sessionInfo->sessionId); /* * Get the session object (indexed by the peer string) and from this get the announcements array */ duk_get_prop_string(ctx, -1, peer); duk_get_prop_string(ctx, -1, "anno"); /* * Iterate over the services implemented by this peer */ numSvcs = duk_get_length(ctx, -1); for (i = 0; i < numSvcs; ++i) { size_t j; size_t numIfaces; duk_idx_t svcIdx; duk_get_prop_index(ctx, -1, i); if (duk_is_undefined(ctx, -1)) { /* * Undefined means the iface has been deleted so pop "interfaces" */ duk_pop(ctx); /* * Delete the announcement entry and continue */ duk_del_prop_index(ctx, -1, i); continue; } svcIdx = duk_get_top_index(ctx); /* * Iterate over the interfaces for this service */ duk_get_prop_string(ctx, svcIdx, "interfaces"); numIfaces = duk_get_length(ctx, -1); for (j = 0; j < numIfaces; ++j) { const char* iface; duk_get_prop_index(ctx, -1, j); iface = duk_get_string(ctx, -1); duk_pop(ctx); /* * If there is a callback registered for this interface call it * * TODO - should we really make a callback for each interface or just call the first one * that has an callback referenced. If the same callback function has been registered * for multiple interfaces we definitely don't want to call it multiple times. */ if (PushServiceCallback(ctx, iface)) { /* * Set the session information on the service */ duk_push_int(ctx, sessionInfo->sessionId); duk_put_prop_string(ctx, svcIdx, "session"); duk_push_string(ctx, peer); duk_put_prop_string(ctx, svcIdx, "dest"); /* * Call the callback function */ duk_dup(ctx, svcIdx); if (duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS) { AJS_ConsoleSignalError(ctx); } duk_pop(ctx); } } /* * Pop "interfaces" and the service object */ duk_pop_2(ctx); /* * Delete the announcement entry */ duk_del_prop_index(ctx, -1, i); } /* Pop "anno" and the session object */ duk_pop_2(ctx); }