Пример #1
0
 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());
     }
   }
 }
Пример #2
0
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);
  }
}
Пример #3
0
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);
    }
  }
}
Пример #4
0
 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
 }
Пример #5
0
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
}
Пример #6
0
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);
  }
}
Пример #7
0
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);
    }
  }
}
Пример #8
0
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);
}
Пример #9
0
 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
 }
Пример #10
0
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());
    }
  }
}
Пример #11
0
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;
}
Пример #12
0
 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();
   }
 }
Пример #13
0
 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());
   }
 }
Пример #14
0
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());
}
Пример #15
0
 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);
   }
 }
Пример #16
0
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;
}
Пример #17
0
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());
  }
}
Пример #18
0
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;
}
Пример #19
0
 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();
   }
 }
Пример #20
0
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;
}
Пример #21
0
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();
  }
}
Пример #22
0
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;
}
Пример #23
0
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);
  }
}
Пример #24
0
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;
}
Пример #25
0
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;
}