void initDiscovery() { // get discovery params // - host name string s; string hostname; if (!getStringOption("hostname", hostname)) { // none specified, create default hostname = string_format("plan44-vdcd-%s", p44VdcHost->getDsUid().getString().c_str()); } // start DS advertising if not disabled if (!getOption("nodiscovery")) { // start the basic service ErrorPtr err = DiscoveryManager::sharedDiscoveryManager().start( hostname.c_str() ); if (Error::isOK(err)) { // started ok, set discovery params int sshPort = 0; getIntOption("sshport", sshPort); // start discovery manager DiscoveryManager::sharedDiscoveryManager().advertiseDS( p44VdcHost, getOption("noauto"), p44VdcHost->webUiPort, p44VdcHost->webUiPath, sshPort ); } else { LOG(LOG_ERR, "**** Cannot start discovery manager: %s", err->description().c_str()); } } }
void DaliDeviceContainer::testRWResponse(CompletedCB aCompletedCB, DaliAddress aShortAddr, uint8_t aTestByte, bool aNoOrTimeout, uint8_t aResponse, ErrorPtr aError) { if (Error::isOK(aError) && !aNoOrTimeout && aResponse==aTestByte) { LOG(LOG_NOTICE," - sent 0x%02X, received 0x%02X\n",aTestByte, aResponse, aNoOrTimeout); // successfully read back same value from DTR as sent before // - check if more tests switch (aTestByte) { case 0x55: aTestByte = 0xAA; break; // next test: inverse case 0xAA: aTestByte = 0x00; break; // next test: all 0 case 0x00: aTestByte = 0xFF; break; // next test: all 1 case 0xFF: aTestByte = 0xF0; break; // next test: half / half case 0xF0: aTestByte = 0x0F; break; // next test: half / half inverse default: // all tests done aCompletedCB(aError); // turn off lights daliComm->daliSendDirectPower(DaliBroadcast, 0); // off return; } // launch next test testRW(aCompletedCB, aShortAddr, aTestByte); } else { // not ok if (Error::isOK(aError) && aNoOrTimeout) aError = ErrorPtr(new DaliCommError(DaliCommErrorMissingData)); // report LOG(LOG_ERR,"DALI self test error: sent 0x%02X, error: %s\n",aTestByte, aError->description().c_str()); aCompletedCB(aError); } }
void SsdpSearch::socketStatusHandler(ErrorPtr aError) { FOCUSLOG("SSDP socket status: %s", aError ? aError->description().c_str() : "<no error>"); if (Error::isOK(aError)) { FOCUSLOG("### sending UDP M-SEARCH"); // unregister socket status handler (or we'll get called when connection closes) setConnectionStatusHandler(NULL); // send search request string ssdpSearch = string_format( "M-SEARCH * HTTP/1.1\n" "HOST: %s:%s\n" "MAN: \"ssdp:discover\"\n" "MX: %d\n" "ST: %s\n", SSDP_BROADCAST_ADDR, SSDP_PORT, SSDP_MX, searchTarget.c_str() ); transmitString(ssdpSearch); // start timer (wait 1.5 the MX for answers) timeoutTicket = MainLoop::currentMainLoop().executeOnce(boost::bind(&SsdpSearch::searchTimedOut, this), SSDP_MX*1500*MilliSecond); } else { // error starting search if (searchResultHandler) { searchResultHandler(this, aError); } } }
void handleCreateUserAnswer(JsonObjectPtr aJsonResponse, ErrorPtr aError) { if (Error::isOK(aError)) { FOCUSLOG("Received success answer:\n%s", aJsonResponse->json_c_str()); JsonObjectPtr s = HueComm::getSuccessItem(aJsonResponse); // apparently successful, extract user name if (s) { JsonObjectPtr u = s->get("username"); if (u) { hueComm.userName = u->stringValue(); hueComm.uuid = currentAuthCandidate->first; hueComm.baseURL = currentAuthCandidate->second; hueComm.apiReady = true; // can use API now FOCUSLOG("hue Bridge %s @ %s: successfully registered as user %s", hueComm.uuid.c_str(), hueComm.baseURL.c_str(), hueComm.userName.c_str()); // successfully registered with hue bridge, let caller know callback(ErrorPtr()); // done! keepAlive.reset(); // will delete object if nobody else keeps it return; } } } else { LOG(LOG_ERR, "hue Bridge: Error creating user: %s", aError->description().c_str()); } // try next ++currentAuthCandidate; processCurrentAuthCandidate(); // process next, if any }
bool FdComm::transmitString(const string &aString) { ErrorPtr err; size_t res = transmitBytes(aString.length(), (uint8_t *)aString.c_str(), err); if (!Error::isOK(err)) { FOCUSLOG("FdComm: Error sending data: %s", err->description().c_str()); } return Error::isOK(err) && res==aString.length(); // ok if no error and all bytes sent }
void Application::terminateAppWith(ErrorPtr aError) { if (Error::isOK(aError)) { mainLoop.terminate(EXIT_SUCCESS); } else { LOG(LOG_ERR, "Terminating because of error: %s", aError->description().c_str()); mainLoop.terminate(EXIT_FAILURE); } }
void EnoceanDeviceContainer::handleRadioPacket(Esp3PacketPtr aEsp3PacketPtr, ErrorPtr aError) { if (aError) { LOG(LOG_INFO, "Radio packet error: %s\n", aError->description().c_str()); return; } // check learning mode if (learningMode) { // no learn/unlearn actions detected so far // - check if we know that device address already. If so, it is a learn-out bool learnIn = enoceanDevices.find(aEsp3PacketPtr->radioSender())==enoceanDevices.end(); // now add/remove the device (if the action is a valid learn/unlearn) // detect implicit (RPS) learn in only with sufficient radio strength (or explicit override of that check), // explicit ones are always recognized if (aEsp3PacketPtr->eepHasTeachInfo(disableProximityCheck ? 0 : MIN_LEARN_DBM, false)) { LOG(LOG_NOTICE, "Received EnOcean learn packet while learn mode enabled: %s\n", aEsp3PacketPtr->description().c_str()); // This is actually a valid learn action ErrorPtr learnStatus; if (learnIn) { // new device learned in, add logical devices for it int numNewDevices = EnoceanDevice::createDevicesFromEEP(this, aEsp3PacketPtr->radioSender(), aEsp3PacketPtr->eepProfile(), aEsp3PacketPtr->eepManufacturer()); if (numNewDevices>0) { // successfully learned at least one device // - update learn status (device learned) getDeviceContainer().reportLearnEvent(true, ErrorPtr()); } } else { // device learned out, un-pair all logical dS devices it has represented // but keep dS level config in case it is reconnected unpairDevicesByAddress(aEsp3PacketPtr->radioSender(), false); getDeviceContainer().reportLearnEvent(false, ErrorPtr()); } // - only allow one learn action (to prevent learning out device when // button is released or other repetition of radio packet) learningMode = false; } // learn action } else { // not learning mode, dispatch packet to all devices known for that address for (EnoceanDeviceMap::iterator pos = enoceanDevices.lower_bound(aEsp3PacketPtr->radioSender()); pos!=enoceanDevices.upper_bound(aEsp3PacketPtr->radioSender()); ++pos) { if (aEsp3PacketPtr->eepHasTeachInfo(MIN_LEARN_DBM, false) && aEsp3PacketPtr->eepRorg()!=rorg_RPS) { // learning packet in non-learn mode -> report as non-regular user action, may be attempt to identify a device // Note: RPS devices are excluded because for these all telegrams are regular regular user actions. signalDeviceUserAction() will be called // from button if (getDeviceContainer().signalDeviceUserAction(*(pos->second), false)) { // consumed for device identification purposes, suppress further processing break; } } // handle regularily (might be RPS switch which does not have separate learn/action packets pos->second->handleRadioPacket(aEsp3PacketPtr); } } }
void MainLoop::childAnswerCollected(ExecCB aCallback, FdStringCollectorPtr aAnswerCollector, ErrorPtr aError) { LOG(LOG_DEBUG, "childAnswerCollected: error = %s", Error::isOK(aError) ? "none" : aError->description().c_str()); // close my end of the pipe aAnswerCollector->stopMonitoringAndClose(); // now get answer string answer = aAnswerCollector->collectedData; LOG(LOG_DEBUG, "- Answer = %s", answer.c_str()); // call back directly aCallback(cycleStartTime, aError, answer); }
void handleServiceDescriptionAnswer(const string &aResponse, ErrorPtr aError) { if (Error::isOK(aError)) { // show //FOCUSLOG("Received bridge description:\n%s", aResponse.c_str()); FOCUSLOG("Received service description XML"); // TODO: this is poor man's XML scanning, use some real XML parser eventually // do some basic checking for model size_t i = aResponse.find("<manufacturer>Royal Philips Electronics</manufacturer>"); if (i!=string::npos) { // is from Philips // - check model number i = aResponse.find("<modelNumber>929000226503</modelNumber>"); // original 2012 hue bridge, FreeRTOS if (i==string::npos) i = aResponse.find("<modelNumber>BSB002</modelNumber>"); // hue bridge 2015 with homekit, Linux if (i!=string::npos) { // is the right model // - get base URL string token = "<URLBase>"; i = aResponse.find(token); if (i!=string::npos) { i += token.size(); size_t e = aResponse.find("</URLBase>", i); if (e!=string::npos) { // create the base address for the API string url = aResponse.substr(i,e-i) + "api"; if (refind) { // that's my known hue bridge, save the URL and report success hueComm.baseURL = url; // save it hueComm.apiReady = true; // can use API now FOCUSLOG("pre-known hue Bridge %s found at %s", hueComm.uuid.c_str(), hueComm.baseURL.c_str()); callback(ErrorPtr()); // success keepAlive.reset(); // will delete object if nobody else keeps it return; // done } else { // that's a hue bridge, remember it for trying to authorize FOCUSLOG("- Seems to be a hue bridge at %s", url.c_str()); authCandidates[currentBridgeCandidate->first] = url; } } } } } } else { FOCUSLOG("Error accessing bridge description: %s", aError->description().c_str()); } // try next ++currentBridgeCandidate; processCurrentBridgeCandidate(); // process next, if any }
void SerialComm::reconnectHandler() { if (reconnecting) { ErrorPtr err = establishConnection(); if (!Error::isOK(err)) { LOG(LOG_ERR, "SerialComm: re-connect failed: %s -> retry again later", err->description().c_str()); reconnecting = true; MainLoop::currentMainLoop().executeOnce(boost::bind(&SerialComm::reconnectHandler, this), 15*Second); } else { LOG(LOG_NOTICE, "SerialComm: successfully reconnected to %s", connectionPath.c_str()); } } }
bool SocketComm::connectionMonitorHandler(MLMicroSeconds aCycleStartTime, int aFd, int aPollFlags) { ErrorPtr err; if ((aPollFlags & POLLOUT) && isConnecting) { // became writable, check status err = socketError(aFd); } else if (aPollFlags & POLLHUP) { err = ErrorPtr(new SocketCommError(SocketCommErrorHungUp, "Connection HUP while opening (= connection rejected)")); } else if (aPollFlags & POLLERR) { err = socketError(aFd); } // now check if successful if (Error::isOK(err)) { // successfully connected connectionOpen = true; isConnecting = false; currentAddressInfo = NULL; // no more addresses to check freeAddressInfo(); LOG(LOG_DEBUG, "Connection to %s:%s established", hostNameOrAddress.c_str(), serviceOrPortOrSocket.c_str()); // call handler if defined if (connectionStatusHandler) { // connection ok connectionStatusHandler(this, ErrorPtr()); } // let FdComm base class operate open connection (will install handlers) setFd(aFd); } else { // this attempt has failed, try next (if any) LOG(LOG_DEBUG, "- Connection attempt failed: %s", err->description().c_str()); // this will return no error if we have another address to try err = connectNextAddress(); if (err) { // no next attempt started, report error LOG(LOG_WARNING, "Connection to %s:%s failed: %s", hostNameOrAddress.c_str(), serviceOrPortOrSocket.c_str(), err->description().c_str()); if (connectionStatusHandler) { connectionStatusHandler(this, err); } freeAddressInfo(); internalCloseConnection(); } } // handled return true; }
void bridgeDiscoveryHandler(SsdpSearchPtr aSsdpSearch, ErrorPtr aError) { if (Error::isOK(aError)) { // check device for possibility of being a hue bridge if (aSsdpSearch->server.find("IpBridge")!=string::npos) { LOG(LOG_INFO, "hue bridge candidate device found at %s, server=%s, uuid=%s", aSsdpSearch->locationURL.c_str(), aSsdpSearch->server.c_str(), aSsdpSearch->uuid.c_str()); // put into map bridgeCandiates[aSsdpSearch->uuid.c_str()] = aSsdpSearch->locationURL.c_str(); } } else { FOCUSLOG("discovery ended, error = %s (usually: timeout)", aError->description().c_str()); aSsdpSearch->stopSearch(); // now process the results currentBridgeCandidate = bridgeCandiates.begin(); processCurrentBridgeCandidate(); } }
void deviceLearnHandler(bool aLearnIn, ErrorPtr aError) { // back to normal... stopLearning(false); // ...but as we acknowledge the learning with the LEDs, schedule a update for afterwards shutDownTicket.executeOnce(boost::bind(&P44Vdcd::showAppStatus, this), 2*Second); // acknowledge the learning (if any, can also be timeout or manual abort) if (Error::isOK(aError)) { if (aLearnIn) { // show device learned indicateTempStatus(tempstatus_success); } else { // show device unlearned indicateTempStatus(tempstatus_failure); } } else { LOG(LOG_ERR, "Learning error: %s", aError->description().c_str()); } }
void DaliDeviceContainer::deviceInfoReceived(DaliBusDeviceListPtr aBusDevices, DaliBusDeviceList::iterator aNextDev, CompletedCB aCompletedCB, DaliComm::DaliDeviceInfoPtr aDaliDeviceInfoPtr, ErrorPtr aError) { bool missingData = aError && aError->isError(DaliCommError::domain(), DaliCommErrorMissingData); bool badData = aError && (aError->isError(DaliCommError::domain(), DaliCommErrorBadChecksum) || aError->isError(DaliCommError::domain(), DaliCommErrorBadDeviceInfo)); if (!aError || missingData || badData) { // no error, or error but due to missing or bad data -> device exists if (missingData) { LOG(LOG_INFO,"Device at shortAddress %d does not have device info\n",aDaliDeviceInfoPtr->shortAddress); } if (badData) { LOG(LOG_INFO,"Device at shortAddress %d does not have valid device info\n",aDaliDeviceInfoPtr->shortAddress); } // update device info entry in dali bus device (*aNextDev)->setDeviceInfo(*aDaliDeviceInfoPtr); } else { LOG(LOG_ERR,"Error reading device info: %s\n",aError->description().c_str()); return aCompletedCB(aError); } // check next ++aNextDev; queryNextDev(aBusDevices, aNextDev, aCompletedCB, ErrorPtr()); }
virtual void initialized(ErrorPtr aError) { #if SELFTESTING_ENABLED if (selfTesting) { // self test mode if (Error::isOK(aError)) { // start self testing (which might do some collecting if needed for testing) p44VdcHost->selfTest(boost::bind(&P44Vdcd::selfTestDone, this, _1), button, redLED, greenLED); // do the self test } else { // - init already unsuccessful, consider test failed, call test end routine directly selfTestDone(aError); } } else #endif // SELFTESTING_ENABLED if (!Error::isOK(aError)) { // cannot initialize, this is a fatal error setAppStatus(status_fatalerror); // exit in 15 seconds LOG(LOG_ALERT, "****** Fatal error - vdc host initialisation failed: %s", aError->description().c_str()); shutDownTicket.executeOnce(boost::bind(&P44Vdcd::terminateAppWith, this, aError), 15*Second); return; } else { // Initialized ok and not testing #if !DISABLE_DISCOVERY // - initialize discovery initDiscovery(); #endif // - start running normally p44VdcHost->startRunning(); // - collect devices collectDevices(rescanmode_normal); } }
ErrorPtr SocketComm::initiateConnection() { int res; ErrorPtr err; if (!connectionOpen && !isConnecting && !serverConnection) { freeAddressInfo(); // check for protocolfamily auto-choice if (protocolFamily==PF_UNSPEC) { // not specified, choose local socket if service spec begins with slash if (serviceOrPortOrSocket.size()>1 && serviceOrPortOrSocket[0]=='/') protocolFamily = PF_LOCAL; // absolute paths are considered local sockets } if (protocolFamily==PF_LOCAL) { // local socket -> just connect, no lists to try LOG(LOG_DEBUG, "Initiating local socket %s connection", serviceOrPortOrSocket.c_str()); hostNameOrAddress = "local"; // set it for log display // synthesize address info for unix socket, because standard UN*X getaddrinfo() call usually does not handle PF_LOCAL addressInfoList = new struct addrinfo; memset(addressInfoList, 0, sizeof(addrinfo)); addressInfoList->ai_family = protocolFamily; addressInfoList->ai_socktype = socketType; addressInfoList->ai_protocol = protocol; struct sockaddr_un *sunP = new struct sockaddr_un; addressInfoList->ai_addr = (struct sockaddr *)sunP; addressInfoList->ai_addrlen = sizeof(struct sockaddr_un); memset((char *)sunP, 0, addressInfoList->ai_addrlen); sunP->sun_family = (sa_family_t)protocolFamily; strncpy(sunP->sun_path, serviceOrPortOrSocket.c_str(), sizeof (sunP->sun_path)); sunP->sun_path[sizeof (sunP->sun_path) - 1] = '\0'; // emergency terminator } else { // assume internet connection -> get list of possible addresses and try them if (hostNameOrAddress.empty()) { err = ErrorPtr(new SocketCommError(SocketCommErrorNoParams,"Missing connection parameters")); goto done; } // try to resolve host name struct addrinfo hint; memset(&hint, 0, sizeof(addrinfo)); hint.ai_flags = 0; // no flags hint.ai_family = protocolFamily; hint.ai_socktype = socketType; hint.ai_protocol = protocol; res = getaddrinfo(hostNameOrAddress.c_str(), serviceOrPortOrSocket.c_str(), &hint, &addressInfoList); if (res!=0) { // error err = ErrorPtr(new SocketCommError(SocketCommErrorCannotResolve, string_format("getaddrinfo error %d: %s", res, gai_strerror(res)))); DBGLOG(LOG_DEBUG, "SocketComm: getaddrinfo failed: %s", err->description().c_str()); goto done; } } // now try all addresses in the list // - init iterator pointer currentAddressInfo = addressInfoList; // - try connecting first address LOG(LOG_DEBUG, "Initiating connection to %s:%s", hostNameOrAddress.c_str(), serviceOrPortOrSocket.c_str()); err = connectNextAddress(); } done: if (!Error::isOK(err) && connectionStatusHandler) { connectionStatusHandler(this, err); } // return it return err; }
void JsonRpcComm::gotJson(ErrorPtr aError, JsonObjectPtr aJsonObject) { JsonRpcCommPtr keepMeAlive(this); // make sure this object lives until routine terminates ErrorPtr respErr; bool safeError = false; // set when reporting error is safe (i.e. not error possibly generated by malformed error, to prevent error loops) JsonObjectPtr idObj; const char *idString = NULL; if (Error::isOK(aError)) { // received proper JSON, now check JSON-RPC specifics FOCUSLOG("Received JSON message:\n %s\n", aJsonObject->c_strValue()); if (aJsonObject->isType(json_type_array)) { respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - batch mode not supported by this implementation")); } else if (!aJsonObject->isType(json_type_object)) { respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - request must be JSON object")); } else { // check request object fields const char *method = NULL; JsonObjectPtr o = aJsonObject->get("jsonrpc"); if (!o) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - missing 'jsonrpc'")); else if (o->stringValue()!="2.0") respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - wrong version in 'jsonrpc'")); else { // get ID param (must be present for all messages except notification) idObj = aJsonObject->get("id"); if (idObj) idString = idObj->c_strValue(); JsonObjectPtr paramsObj = aJsonObject->get("params"); // JSON-RPC version is correct, check other params method = aJsonObject->getCString("method"); if (method) { // this is a request (responses don't have the method member) safeError = idObj!=NULL; // reporting error is safe if this is a method call. Other errors are reported only when reportAllErrors is set if (*method==0) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - empty 'method'")); else { // looks like a valid method or notification call if (!jsonRequestHandler) { // no handler -> method cannot be executed respErr = ErrorPtr(new JsonRpcError(JSONRPC_METHOD_NOT_FOUND, "Method not found")); } else { if (paramsObj && !paramsObj->isType(json_type_array) && !paramsObj->isType(json_type_object)) { // invalid param object respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - 'params' must be object or array")); } else { // call handler to execute method or notification jsonRequestHandler(method, idString, paramsObj); } } } } else { // this is a response (requests always have a method member) // - check if result or error JsonObjectPtr respObj; if (!aJsonObject->get("result", respObj)) { // must be error, need further decoding respObj = aJsonObject->get("error"); if (!respObj) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INTERNAL_ERROR, "Internal JSON-RPC error - response with neither 'result' nor 'error'")); else { // dissect error object ErrorCode errCode = JSONRPC_INTERNAL_ERROR; // Internal RPC error const char *errMsg = "malformed Error response"; // - try to get error code JsonObjectPtr o = respObj->get("code"); if (o) errCode = o->int32Value(); // - try to get error message o = respObj->get("message"); if (o) errMsg = o->c_strValue(); // compose error object from this respErr = ErrorPtr(new JsonRpcError(errCode, errMsg)); // also get optional data element respObj = respObj->get("data"); } } // Now we have either result or error.data in respObj, and respErr is Ok or contains the error code + message if (!idObj) { // errors without ID cannot be associated with calls made earlier, so just log the error LOG(LOG_WARNING,"JSON-RPC 2.0 warning: Received response with no or NULL 'id' that cannot be dispatched:\n %s\n", aJsonObject->c_strValue()); } else { // dispatch by ID uint32_t requestId = idObj->int32Value(); PendingAnswerMap::iterator pos = pendingAnswers.find(requestId); if (pos==pendingAnswers.end()) { // errors without ID cannot be associated with calls made earlier, so just log the error LOG(LOG_WARNING,"JSON-RPC 2.0 error: Received response with unknown 'id'=%d : %s\n", requestId, aJsonObject->c_strValue()); } else { // found callback JsonRpcResponseCB cb = pos->second; pendingAnswers.erase(pos); // erase cb(requestId, respErr, respObj); // call } respErr.reset(); // handled } } } } } else { // no proper JSON received, create error response if (aError->isDomain(JsonError::domain())) { // some kind of parsing error respErr = ErrorPtr(new JsonRpcError(JSONRPC_PARSE_ERROR, aError->description())); } else { // some other type of server error respErr = ErrorPtr(new JsonRpcError(JSONRPC_SERVER_ERROR, aError->description())); } } // auto-generate error response for internally created errors if (!Error::isOK(respErr)) { if (safeError || reportAllErrors) sendError(idString, respErr); else LOG(LOG_WARNING,"Received data that generated error which can't be sent back: Code=%d, Message='%s'\n", respErr->getErrorCode(), respErr->description().c_str()); } }
ErrorPtr PropertyContainer::accessProperty(PropertyAccessMode aMode, ApiValuePtr aQueryObject, ApiValuePtr aResultObject, int aDomain, PropertyDescriptorPtr aParentDescriptor) { ErrorPtr err; #if DEBUGFOCUSLOGGING FOCUSLOG("\naccessProperty: entered with query = %s\n", aQueryObject->description().c_str()); if (aParentDescriptor) { FOCUSLOG("- parentDescriptor '%s' (%s), fieldKey=%u, objectKey=%u\n", aParentDescriptor->name(), aParentDescriptor->isStructured() ? "structured" : "scalar", aParentDescriptor->fieldKey(), aParentDescriptor->objectKey()); } #endif // for reading, NULL query is like query { "":NULL } if (aQueryObject->isNull() && aMode==access_read) { aQueryObject->setType(apivalue_object); aQueryObject->add("", aQueryObject->newValue(apivalue_null)); } // aApiObject must be of type apivalue_object if (!aQueryObject->isType(apivalue_object)) return ErrorPtr(new VdcApiError(415, "Query or Value written must be object")); if (aMode==access_read) { if (!aResultObject) return ErrorPtr(new VdcApiError(415, "accessing property for read must provide result object")); aResultObject->setType(apivalue_object); // must be object } // Iterate trough elements of query object aQueryObject->resetKeyIteration(); string queryName; ApiValuePtr queryValue; string errorMsg; while (aQueryObject->nextKeyValue(queryName, queryValue)) { FOCUSLOG("- starting to process query element named '%s' : %s\n", queryName.c_str(), queryValue->description().c_str()); if (aMode==access_read && queryName=="#") { // asking for number of elements at this level -> generate and return int value queryValue = queryValue->newValue(apivalue_int64); // integer queryValue->setInt32Value(numProps(aDomain, aParentDescriptor)); aResultObject->add(queryName, queryValue); } else { // accessing an element or series of elements at this level bool wildcard = isMatchAll(queryName); // - find all descriptor(s) for this queryName PropertyDescriptorPtr propDesc; int propIndex = 0; bool foundone = false; do { propDesc = getDescriptorByName(queryName, propIndex, aDomain, aParentDescriptor); if (propDesc) { foundone = true; // found at least one descriptor for this query element FOCUSLOG(" - processing descriptor '%s' (%s), fieldKey=%u, objectKey=%u\n", propDesc->name(), propDesc->isStructured() ? "structured" : "scalar", propDesc->fieldKey(), propDesc->objectKey()); // actually access by descriptor if (propDesc->isStructured()) { ApiValuePtr subQuery; // property is a container. Now check the value if (queryValue->isType(apivalue_object)) { subQuery = queryValue; // query specifies next level, just use it } else if (queryName!="*" && (!wildcard || propDesc->isWildcardAddressable())) { // don't recurse deeper when query name is "*" or property is not wildcard-adressable // special case is "*" as leaf in query - only recurse if it is not present // - autocreate subquery subQuery = queryValue->newValue(apivalue_object); subQuery->add("", queryValue->newValue(apivalue_null)); } if (subQuery) { // addressed property is a container by itself -> recurse // - get the PropertyContainer int containerDomain = aDomain; // default to same, but getContainer may modify it PropertyDescriptorPtr containerPropDesc = propDesc; PropertyContainerPtr container = getContainer(containerPropDesc, containerDomain); if (container) { FOCUSLOG(" - container for '%s' is 0x%p\n", propDesc->name(), container.get()); FOCUSLOG(" >>>> RECURSING into accessProperty()\n"); if (aMode==access_read) { // read needs a result object ApiValuePtr resultValue = queryValue->newValue(apivalue_object); err = container->accessProperty(aMode, subQuery, resultValue, containerDomain, containerPropDesc); if (Error::isOK(err)) { // add to result with actual name (from descriptor) FOCUSLOG("\n <<<< RETURNED from accessProperty() recursion\n"); FOCUSLOG(" - accessProperty of container for '%s' returns %s\n", propDesc->name(), resultValue->description().c_str()); aResultObject->add(propDesc->name(), resultValue); } } else { // for write, just pass the query value err = container->accessProperty(aMode, subQuery, ApiValuePtr(), containerDomain, containerPropDesc); FOCUSLOG(" <<<< RETURNED from accessProperty() recursion\n", propDesc->name(), container.get()); } if ((aMode!=access_read) && Error::isOK(err)) { // give this container a chance to post-process write access err = writtenProperty(aMode, propDesc, aDomain, container); } // 404 errors are collected, but dont abort the query if (Error::isError(err, VdcApiError::domain(), 404)) { if (!errorMsg.empty()) errorMsg += "; "; errorMsg += string_format("Error(s) accessing subproperties of '%s' : { %s }", queryName.c_str(), err->description().c_str()); err.reset(); // forget the error on this level } } } } else { // addressed (and known by descriptor!) property is a simple value field -> access it if (aMode==access_read) { // read access: create a new apiValue and have it filled ApiValuePtr fieldValue = queryValue->newValue(propDesc->type()); // create a value of correct type to get filled bool accessOk = accessField(aMode, fieldValue, propDesc); // read // for read, not getting an OK from accessField means: property does not exist (even if known per descriptor), // so it will not be added to the result if (accessOk) { // add to result with actual name (from descriptor) aResultObject->add(propDesc->name(), fieldValue); } FOCUSLOG(" - accessField for '%s' returns %s\n", propDesc->name(), fieldValue->description().c_str()); } else { // write access: just pass the value if (!accessField(aMode, queryValue, propDesc)) { // write err = ErrorPtr(new VdcApiError(403,string_format("Write access to '%s' denied", propDesc->name()))); } } } } else { // no descriptor found for this query element // Note: this means that property is not KNOWN, which IS not the same as getting false from accessField // (latter means that property IS known, but has no value in the context it was queried) // HOWEVER, for the vdc API it was decided that for reading, these cases should NOT be // distinguished for getProperty. If a property does not exist for either reason, the return tree just does // no contain that property. // Also note that not having a property is not the same as not having a property VALUE: // latter case will explicitly return a NULL value if (!wildcard && !foundone && aMode!=access_read) { // query did address a specific property but it is unknown -> report as error (for read AND write!) // - collect error message, but do not abort query processing if (!errorMsg.empty()) errorMsg += "; "; errorMsg += string_format("Unknown property '%s' -> ignored", queryName.c_str()); } } } while (Error::isOK(err) && propIndex!=PROPINDEX_NONE); } // now generate error if we have collected a non-empty error message if (!errorMsg.empty()) { err = ErrorPtr(new VdcApiError(404,errorMsg)); } #if DEBUGLOGGING if (aMode==access_read) { FOCUSLOG("- query element named '%s' now has result object: %s\n", queryName.c_str(), aResultObject->description().c_str()); } #endif } return err; }
void containerTested(ErrorPtr aError) { if (!Error::isOK(aError)) { // test failed LOG(LOG_ERR, "****** Test of '%s' FAILED with error: %s", nextVdc->second->vdcClassIdentifier(), aError->description().c_str()); // remember globalError = aError; // morse out tag number of vDC failing self test until button is pressed greenLED->steadyOff(); int numBlinks = nextVdc->second->getTag(); redLED->blinkFor(300*MilliSecond*numBlinks, 300*MilliSecond, 50); // call myself again later errorReportTicket = MainLoop::currentMainLoop().executeOnce(boost::bind(&SelfTestRunner::containerTested, this, aError), 300*MilliSecond*numBlinks+2*Second); // also install button responder button->setButtonHandler(boost::bind(&SelfTestRunner::errorAcknowledged, this), false); // report only release } else { // test was ok LOG(LOG_ERR, "------ Test of '%s' OK", nextVdc->second->vdcClassIdentifier()); // check next ++nextVdc; testNextContainer(); } }
ErrorPtr PersistentParams::saveToStore(const char *aParentIdentifier, bool aMultipleChildrenAllowed) { ErrorPtr err; if (dirty) { sqlite3pp::command cmd(paramStore); string sql; // cleanup: remove all previous records for that parent if not multiple children allowed if (!aMultipleChildrenAllowed) { sql = string_format("DELETE FROM %s WHERE %s='%s'", tableName(), getKeyDef(0)->fieldName, aParentIdentifier); if (rowid!=0) { string_format_append(sql, " AND ROWID!=%lld", rowid); } FOCUSLOG("- cleanup before save: %s\n", sql.c_str()); if (paramStore.executef(sql.c_str()) != SQLITE_OK) { LOG(LOG_ERR, "- cleanup error (ignored): %s\n", sql.c_str(), paramStore.error()->description().c_str()); } } // now save if (rowid!=0) { // already exists in the DB, just update sql = string_format("UPDATE %s SET ", tableName()); // - update all fields, even key fields may change (as long as they don't collide with another entry) appendfieldList(sql, true, false, true); appendfieldList(sql, false, true, true); string_format_append(sql, " WHERE ROWID=%lld", rowid); // now execute command FOCUSLOG("saveToStore: update existing row for parent='%s': %s\n", aParentIdentifier, sql.c_str()); if (cmd.prepare(sql.c_str())!=SQLITE_OK) { // error on update is always a real error - if we loaded the params from the DB, schema IS ok! err = paramStore.error(); } if (Error::isOK(err)) { // bind the values int index = 1; // SQLite parameter indexes are 1-based! bindToStatement(cmd, index, aParentIdentifier, 0); // no flags yet, class hierarchy will collect them // now execute command if (cmd.execute()==SQLITE_OK) { // ok, updated ok dirty = false; } else { // failed err = paramStore.error(); } } } else { // seems new, insert. But use INSERT OR REPLACE to make sure key constraints are enforced sql = string_format("INSERT OR REPLACE INTO %s (", tableName());; size_t numFields = appendfieldList(sql, true, false, false); numFields += appendfieldList(sql, false, true, false); sql += ") VALUES ("; bool first = true; for (int i=0; i<numFields; i++) { if (!first) sql += ", "; sql += "?"; first = false; } sql += ")"; // prepare FOCUSLOG("saveToStore: insert new row for parent='%s': %s\n", aParentIdentifier, sql.c_str()); if (cmd.prepare(sql.c_str())!=SQLITE_OK) { FOCUSLOG("- insert not successful - assume wrong schema -> calling checkAndUpdateSchema()\n"); // - error on INSERT could mean schema is not up to date cmd.reset(); checkAndUpdateSchema(); FOCUSLOG("saveToStore: retrying insert after schema update: %s\n", sql.c_str()); if (cmd.prepare(sql.c_str())!=SQLITE_OK) { // error now means something is really wrong err = paramStore.error(); } } if (Error::isOK(err)) { // bind the values int index = 1; // SQLite parameter indexes are 1-based! bindToStatement(cmd, index, aParentIdentifier, 0); // no flags yet, class hierarchy will collect them // now execute command if (cmd.execute()==SQLITE_OK) { // get the new ROWID rowid = paramStore.last_insert_rowid(); dirty = false; } else { // failed err = paramStore.error(); } } } if (!Error::isOK(err)) { LOG(LOG_ERR, "saveToStore: %s - failed: %s\n", sql.c_str(), err->description().c_str()); } } // anyway, have children checked if (Error::isOK(err)) { err = saveChildren(); } return err; }
void SocketComm::dataExceptionHandler(int aFd, int aPollFlags) { SocketCommPtr keepMyselfAlive(this); DBGLOG(LOG_DEBUG, "SocketComm::dataExceptionHandler(fd==%d, pollflags==0x%X)", aFd, aPollFlags); if (!isClosing) { if (aPollFlags & POLLHUP) { // other end has closed connection // - report if (connectionStatusHandler) { // report reason for closing connectionStatusHandler(this, ErrorPtr(new SocketCommError(SocketCommErrorHungUp,"Connection closed (HUP)"))); } } else if (aPollFlags & POLLIN) { // Note: on linux a socket closed server side does not return POLLHUP, but POLLIN with no data // alerted for read, but nothing to read any more: assume connection closed ErrorPtr err = socketError(aFd); if (Error::isOK(err)) err = ErrorPtr(new SocketCommError(SocketCommErrorHungUp,"Connection closed (POLLIN but no data -> interpreted as HUP)")); DBGLOG(LOG_DEBUG, "Connection to %s:%s has POLLIN but no data; error: %s", hostNameOrAddress.c_str(), serviceOrPortOrSocket.c_str(), err->description().c_str()); // - report if (connectionStatusHandler) { // report reason for closing connectionStatusHandler(this, err); } } else if (aPollFlags & POLLERR) { // error ErrorPtr err = socketError(aFd); LOG(LOG_WARNING, "Connection to %s:%s reported error: %s", hostNameOrAddress.c_str(), serviceOrPortOrSocket.c_str(), err->description().c_str()); // - report if (connectionStatusHandler) { // report reason for closing connectionStatusHandler(this, err); } } else { // NOP return; } // - shut down (Note: if nobody else retains the connection except the server SocketComm, this will delete the connection) internalCloseConnection(); } }
ErrorPtr PersistentParams::deleteFromStore() { ErrorPtr err; dirty = false; // forget any unstored changes if (rowid!=0) { FOCUSLOG("deleteFromStore: deleting row %lld in table %s\n", rowid, tableName()); if (paramStore.executef("DELETE FROM %s WHERE ROWID=%lld", tableName(), rowid) != SQLITE_OK) { err = paramStore.error(); } // deleted, forget rowid = 0; } // anyway, remove children if (Error::isOK(err)) { err = deleteChildren(); } if (!Error::isOK(err)) { LOG(LOG_ERR, "deleteFromStore: table=%s, ROWID=%lld - failed: %s\n", tableName(), rowid, err->description().c_str()); } return err; }
void VdcJsonApiConnection::jsonResponseHandler(VdcApiResponseCB aResponseHandler, int32_t aResponseId, ErrorPtr &aError, JsonObjectPtr aResultOrErrorData) { if (aResponseHandler) { // create request object just to hold the response ID string respId = string_format("%d", aResponseId); ApiValuePtr resultOrErrorData = JsonApiValue::newValueFromJson(aResultOrErrorData); VdcApiRequestPtr request = VdcJsonApiRequestPtr(new VdcJsonApiRequest(VdcJsonApiConnectionPtr(this), respId.c_str())); if (Error::isOK(aError)) { LOG(LOG_INFO,"vdSM -> vDC (JSON) result received: id='%s', result=%s\n", request->requestId().c_str(), resultOrErrorData ? resultOrErrorData->description().c_str() : "<none>"); } else { LOG(LOG_INFO,"vdSM -> vDC (JSON) error received: id='%s', error=%s, errordata=%s\n", request->requestId().c_str(), aError->description().c_str(), resultOrErrorData ? resultOrErrorData->description().c_str() : "<none>"); } aResponseHandler(VdcApiConnectionPtr(this), request, aError, resultOrErrorData); } }
ErrorPtr SocketComm::connectNextAddress() { int res; ErrorPtr err; const int one = 1; // close possibly not fully open connection FD internalCloseConnection(); // try to create a socket int socketFD = -1; // as long as we have more addresses to check and not already connecting bool startedConnecting = false; while (currentAddressInfo && !startedConnecting) { err.reset(); socketFD = socket(currentAddressInfo->ai_family, currentAddressInfo->ai_socktype, currentAddressInfo->ai_protocol); if (socketFD==-1) { err = SysError::errNo("Cannot create client socket: "); } else { // usable address found, socket created // - make socket non-blocking makeNonBlocking(socketFD); // Now we have a socket if (connectionLess) { // UDP: no connect phase // - enable for broadcast if requested if (broadcast) { // needs SO_BROADCAST if (setsockopt(socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&one, (int)sizeof(one)) == -1) { err = SysError::errNo("Cannot setsockopt(SO_BROADCAST): "); } else { // to receive answers, we also need to bind to INADDR_ANY // - get port number char sbuf[NI_MAXSERV]; int s = getnameinfo( currentAddressInfo->ai_addr, currentAddressInfo->ai_addrlen, NULL, 0, // no host address sbuf, sizeof sbuf, // only service/port NI_NUMERICSERV ); if (s==0) { // convert to numeric port number int port; if (sscanf(sbuf, "%d", &port)==1) { // bind connectionless socket to INADDR_ANY to receive broadcasts at all struct sockaddr_in recvaddr; memset(&recvaddr, 0, sizeof recvaddr); recvaddr.sin_family = AF_INET; recvaddr.sin_port = htons(port); recvaddr.sin_addr.s_addr = INADDR_ANY; if (::bind(socketFD, (struct sockaddr*)&recvaddr, sizeof recvaddr) == -1) { err = SysError::errNo("Cannot bind to INADDR_ANY: "); } } } } } if (Error::isOK(err)) { startedConnecting = true; // save valid address info for later use (UDP needs it to send datagrams) if (currentSockAddrP) free(currentSockAddrP); currentSockAddrLen = currentAddressInfo->ai_addrlen; currentSockAddrP = (sockaddr *)malloc(currentSockAddrLen); memcpy(currentSockAddrP, currentAddressInfo->ai_addr, currentAddressInfo->ai_addrlen); } } else { // TCP: initiate connection res = connect(socketFD, currentAddressInfo->ai_addr, currentAddressInfo->ai_addrlen); LOG(LOG_DEBUG, "- Attempting connection with address family = %d, protocol = %d", currentAddressInfo->ai_family, currentAddressInfo->ai_protocol); if (res==0 || errno==EINPROGRESS) { // connection initiated (or already open, but connectionMonitorHandler will take care in both cases) startedConnecting = true; } else { // immediate error connecting err = SysError::errNo("Cannot connect: "); } } } // advance to next address currentAddressInfo = currentAddressInfo->ai_next; } if (!startedConnecting) { // exhausted addresses without starting to connect if (!err) err = ErrorPtr(new SocketCommError(SocketCommErrorNoConnection, "No connection could be established")); LOG(LOG_DEBUG, "Cannot initiate connection to %s:%s - %s", hostNameOrAddress.c_str(), serviceOrPortOrSocket.c_str(), err->description().c_str()); } else { if (!connectionLess) { // connection in progress isConnecting = true; // - save FD connectionFd = socketFD; // - install callback for when FD becomes writable (or errors out) mainLoop.registerPollHandler( connectionFd, POLLOUT, boost::bind(&SocketComm::connectionMonitorHandler, this, _1, _2, _3) ); } else { // UDP socket successfully created LOG(LOG_DEBUG, "Connectionless socket ready for address family = %d, protocol = %d", protocolFamily, protocol); connectionOpen = true; isConnecting = false; currentAddressInfo = NULL; // no more addresses to check // immediately use socket for I/O setFd(socketFD); // call handler if defined if (connectionStatusHandler) { // connection ok connectionStatusHandler(this, ErrorPtr()); } } } // clean up if list processed freeAddressInfo(); // return status return err; }
bool SerialComm::requestConnection() { ErrorPtr err = establishConnection(); if (!Error::isOK(err)) { if (!reconnecting) { LOG(LOG_ERR, "SerialComm: requestConnection() could not open connection now: %s -> entering background retry mode", err->description().c_str()); reconnecting = true; MainLoop::currentMainLoop().executeOnce(boost::bind(&SerialComm::reconnectHandler, this), 5*Second); } return false; } return true; }