Re-organized sub-directory by category
[staging/basesystem.git] / service / system / task_manager / server / src / tskm_svc.cpp
diff --git a/service/system/task_manager/server/src/tskm_svc.cpp b/service/system/task_manager/server/src/tskm_svc.cpp
new file mode 100755 (executable)
index 0000000..2f9abc1
--- /dev/null
@@ -0,0 +1,970 @@
+/*
+ * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "system_service/tskm_svc.h"
+#include <string.h>
+#include <stdlib.h>
+#include <sys/inotify.h>
+#include <errno.h>
+
+#include <native_service/ns_np_service_if.h>
+#include <string>
+
+#include "tskm_debug.h"
+#include "tskm_util.h"
+#include "tskm_port_pf.h"
+#include "tskm_port_subsys.h"
+#include "tskm_comm.h"
+
+
+/*********************************************************
+ *       Is the target event of the service handler?
+
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t isSvcEvent(const TSKM_EVENT_INFO_t* p_ev) {
+  switch (p_ev->event) {
+    case TSKM_EV_PRI_REP_CONNECT:
+    case TSKM_EV_PRI_REP_DISCONNECT:
+    case TSKM_EV_PRI_RES_WAKEUP:
+    case TSKM_EV_PRI_RES_DOWN:
+    case TSKM_EV_PRI_RES_DEBUGDUMP:
+    case TSKM_EV_PRI_REQ_EXIT:
+    case TSKM_EV_SVC_REP_TERM:
+      return TSKM_TRUE;
+      break;
+    default:
+      break;
+  }
+  return TSKM_FALSE;
+}
+
+/*********************************************************
+ *       Get service context
+ *********************************************************/
+TSKM_STATIC TSKM_SVC_CTX_t*
+getSvcCtxBySvcId(TSKM_SVCS_CTX_t* p_svcs, TSKM_SVCID_t svcId) {
+  uint32_t ii;
+
+  for (ii = 0; ii < p_svcs->svcNum; ii++) {
+    if (p_svcs->svcList[ii].attr->svcId == svcId) {
+      return &p_svcs->svcList[ii];
+    }
+  }
+  TSKM_ASSERT(0);
+  return NULL;
+}
+
+/*********************************************************
+ *       Get service context
+ *********************************************************/
+TSKM_STATIC TSKM_SVC_CTX_t*
+getSvcCtxByPid(TSKM_SVCS_CTX_t* p_svcs, pid_t pid) {
+  uint32_t ii;
+
+  for (ii = 0; ii < p_svcs->svcNum; ii++) {
+    if (p_svcs->svcList[ii].pid == pid) {
+      return &p_svcs->svcList[ii];
+    }
+  }
+  return NULL;
+}
+
+/*********************************************************
+ *       Get service context
+ *********************************************************/
+TSKM_STATIC TSKM_SVC_CTX_t*
+getSvcCtxByName(TSKM_SVCS_CTX_t* p_svcs, const char *p_name) {  // LCOV_EXCL_START 6: Because the condition cannot be set
+  AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+  uint32_t ii;
+
+  for (ii = 0; ii < p_svcs->svcNum; ii++) {
+    if (0
+        == strncmp(p_svcs->svcList[ii].attr->name, p_name,
+                   strlen(p_svcs->svcList[ii].attr->name))) {
+      return &p_svcs->svcList[ii];
+    }
+  }
+  return NULL;
+}
+// LCOV_EXCL_STOP
+/*********************************************************
+ *      Issuing a start request to the service
+ *********************************************************/
+TSKM_STATIC TSKM_ERR_t wakeupRequest(TSKM_SVC_CTX_t* p_svc,
+                                     TSKM_GSTEP_REQ_INFO_t* p_req) {
+  TSKM_EVENT_INFO_t ev;
+  int ret;
+  bzero(&ev, sizeof(ev));
+
+  ev.event = TSKM_EV_PRI_REQ_WAKEUP;
+  ev.errCode = TSKM_E_OK;
+  ev.prm.reqWakeup.svcId = p_svc->attr->svcId;
+  ev.prm.reqWakeup.localStep = p_req->localStep;
+  ev.prm.reqWakeup.isDynamic =
+      (p_svc->attr->lifeCycle == TSKM_SVC_LC_DYNAMIC) ? TSKM_TRUE : TSKM_FALSE;
+  // Since there is no startup in the P_CWORD72_ but only communication in the ALL/LAST,
+  // the startup information is optimized including the startup information in the communication of the WakeupRequest.
+  memcpy(&ev.prm.reqWakeup.bootInfo, &p_svc->bootInfo,
+         sizeof(ev.prm.reqWakeup.bootInfo));
+  memcpy(&ev.prm.reqWakeup.extBootInfo, &p_svc->extBootInfo,
+         sizeof(ev.prm.reqWakeup.extBootInfo));
+
+  ret = tskm_sockSend(p_svc->connFd, &ev);
+  if (ret <= 0) {
+    TSKM_ASSERT(0);
+    goto ERROR;
+  }
+  p_svc->waitResCnt++;
+  p_svc->state = TSKM_SVC_WAKEUP;
+
+  return TSKM_E_OK;
+  ERROR: return TSKM_E_NG;
+}
+
+/*********************************************************
+ *       Touch services and extend EXIT timeouts
+ *********************************************************/
+TSKM_STATIC TSKM_ERR_t reqTouch(TSKM_SVC_CTX_t* p_svc) {
+#define TSKM_BUF_LEN     ( 4 * ( sizeof(struct inotify_event) ) )  /* read size */
+  TSKM_ERR_t funcRet = TSKM_E_NG;
+  char touchFileName[32];
+  BOOL isNeedRetry = FALSE;
+
+  int iFd = p_svc->iFd;
+  int wd = 0;
+
+  TSKM_EVENT_INFO_t ev;
+  int ret;
+  bzero(&ev, sizeof(ev));
+
+  // Create monitoring files
+  tskm_pf_mkTouchFileName(p_svc->pid, touchFileName);
+  if (0 != tskm_pf_touch(touchFileName)) {
+    goto ERROR;
+  }
+
+  // Synchronize by iNortify
+  wd = inotify_add_watch(iFd, touchFileName, IN_DELETE_SELF);
+  if (wd == -1) {
+    TSKM_ASSERT_ERRNO(0);
+    goto ERROR;
+  }
+
+  // Send Touch request
+  ev.event = TSKM_EV_PRI_REQ_TOUCH;
+  ev.errCode = TSKM_E_OK;
+  ret = tskm_sockSend(p_svc->connFd, &ev);
+  if (ret <= 0) {
+    // Immediately after the nonresident service process terminates, since the TaskManager service status is other than DORMANT
+    // (mainly DOWN), the socket may be discarded and communication may fail, so retry
+    funcRet = TSKM_E_RETRY;
+    TSKM_PRINTF(TSKM_LOG_WARN, "ret = %d", ret);
+    goto ERROR;
+  }
+
+  // Wait for Touch completion
+  while (1) {
+    int maxFd = 0;
+    int ret;
+    fd_set fds;
+    FD_ZERO(&fds);
+    struct timeval timeout = { 0 };
+    timeout.tv_sec = TSKM_CFG_TOUCH_TIMEOUT;
+
+    FD_SET(iFd, &fds);
+    maxFd = iFd;
+
+    ret = select(maxFd + 1, &fds, NULL, NULL, &timeout);
+    if (ret == 0) {
+      TSKM_ASSERT_PRINT(0, "TIMEOUT:%s", touchFileName);  // Timeout occurs
+      isNeedRetry = TRUE;
+      break;
+    } else if (ret < 1) {
+      if (errno == EINTR) {
+        continue;
+      } else {
+        TSKM_ASSERT(0);
+        goto ERROR;
+      }
+    }
+
+    if (FD_ISSET(iFd, &fds)) {
+      int length;
+      uint8_t buf[TSKM_BUF_LEN];
+
+      length = static_cast<int>(read(iFd, buf, TSKM_BUF_LEN));
+      if (length < 0) {
+        TSKM_ASSERT_ERRNO(0);
+        goto ERROR;
+      }
+      struct inotify_event *event = (struct inotify_event *) buf;
+      if (event->mask & IN_DELETE_SELF) {
+        TSKM_PRINTF(TSKM_LOG_STATE, "TouchOK");
+        wd = 0;
+        // When a file is deleted, the association with the monitoring target is automatically released and inotify_rm_watch is no longer needed.
+        break;
+      } else {
+        TSKM_ASSERT_PRINT(0, "%x", event->mask);
+      }
+    }
+  }
+
+  if (isNeedRetry) {
+    funcRet = TSKM_E_RETRY;
+  } else {
+    funcRet = TSKM_E_OK;
+  }
+
+  ERROR: if (wd > 0) {
+    TSKM_ASSERT_ERRNO(0 == inotify_rm_watch(iFd, wd))
+  }
+
+  if (access(touchFileName, F_OK) == 0) {
+    // Timeout care
+    unlink(touchFileName);
+  }
+
+  return funcRet;
+}
+
+/*********************************************************
+ *       Availability Monitoring Callbacks for Services
+ *********************************************************/
+EFrameworkunifiedStatus OnSvcAvailability(HANDLE hApp) {  // LCOV_EXCL_START 6: Because the condition cannot be set
+  AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+  PCSTR availabilityName;
+  TSKM_MAIN_CTX_t* p_main = tskm_getMainCtx();
+  TSKM_SVC_CTX_t* p_svc;
+
+  availabilityName = FrameworkunifiedGetLastNotification(hApp);
+
+  std::string str = availabilityName;
+
+//  unsigned int position;
+//  if (std::string::npos != (position = static_cast<unsigned int>(str.find("/Availability", 0)))) {
+  ssize_t position;
+  if (0 <= (position = str.find("/Availability", 0))) {
+    str.erase(position, position + strlen("/Availability"));
+  }
+
+  p_svc = getSvcCtxByName(&p_main->svcs, str.c_str());
+  if (p_svc) {
+    p_svc->isAvailable = FrameworkunifiedIsServiceAvailable(hApp) ? TSKM_TRUE : TSKM_FALSE;
+    TSKM_PRINTF(TSKM_LOG_SVCSTATE, "%s Availability %s", p_svc->attr->name,
+                (p_svc->isAvailable == TSKM_TRUE) ? "TRUE" : "FALSE");
+  }
+
+  return eFrameworkunifiedStatusOK;
+}
+// LCOV_EXCL_STOP
+/*********************************************************
+ *       Starting Availability Monitoring of Services
+ *********************************************************/
+TSKM_STATIC TSKM_ERR_t startWatchAvailability(TSKM_SVC_CTX_t* p_svc) {
+  TSKM_ERR_t funcRet = TSKM_E_OK;
+
+  // LCOV_EXCL_BR_START 8: Since the condition is checked by the caller, it is not true.
+  if (p_svc->attr->type == TSKM_SVC_TYPE_UNKNONW) {
+  // LCOV_EXCL_BR_STOP
+    AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+    p_svc->isAvailable = TSKM_TRUE;  // LCOV_EXCL_LINE 8: Since the condition is checked by the caller, it is not true.
+  } else {
+    EFrameworkunifiedStatus taskmanagerStatus;
+    TSKM_MAIN_CTX_t* p_main = tskm_getMainCtx();
+    SS_String availabilityName = p_svc->attr->name;
+
+    availabilityName.append("/Availability");
+
+    taskmanagerStatus = FrameworkunifiedSubscribeNotificationWithCallback(p_main->hApp,
+                                                     availabilityName.c_str(),
+                                                     OnSvcAvailability);
+    if (eFrameworkunifiedStatusOK != taskmanagerStatus) {
+      TSKM_ASSERT(0);
+      funcRet = TSKM_E_NG;
+    }
+  }
+
+  return funcRet;
+}
+
+/*********************************************************
+ *       Service startup sub
+ *********************************************************/
+void svcExec_Process(TSKM_SVC_CTX_t* p_svc) {
+  TSKM_ERR_t funcRet;
+
+  if (p_svc->attr->type == TSKM_SVC_TYPE_NATIVE) {
+    p_svc->state = TSKM_SVC_WAITCONNECT;
+    TSKM_PRINTF(TSKM_LOG_DEBUG, "[ST:%d] WAIT_EXEC", p_svc->pid);
+    funcRet = startWatchAvailability(p_svc);
+    if (TSKM_E_OK != funcRet) {
+      TSKM_ASSERT(0);
+      p_svc->isAvailable = TSKM_TRUE;
+    }
+  } else {
+    p_svc->state = TSKM_SVC_RUNNING;
+    TSKM_PRINTF(TSKM_LOG_SVCSTATE, "[ST:%d] RUN", p_svc->pid);
+    p_svc->isAvailable = TSKM_TRUE;
+  }
+  return;
+}
+
+/*********************************************************
+ *       Service startup main
+ *********************************************************/
+TSKM_STATIC TSKM_ERR_t svcExec(TSKM_SVC_CTX_t* p_svc) {
+  TSKM_ERR_t funcRet = TSKM_E_NG;
+  pid_t pid;
+
+  if (p_svc == NULL) {  // LCOV_EXCL_BR_LINE 6:double check
+    // LCOV_EXCL_START 6: double check
+    AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+    TSKM_ASSERT(0);
+    goto ERROR;
+    // LCOV_EXCL_STOP
+  }
+
+  if (p_svc->state == TSKM_SVC_DISABLE) {
+    // Prohibited startup
+    TSKM_ASSERT(0);
+    return TSKM_E_STATE;
+  } else if (p_svc->state == TSKM_SVC_WAITCONNECT) {
+    return TSKM_E_OK;
+  } else if (p_svc->state != TSKM_SVC_DORMANT) {
+    // Already started
+    funcRet = reqTouch(p_svc);
+    if (TSKM_E_OK != funcRet) {
+      TSKM_PRINTF(TSKM_LOG_WARN, "funcRet = %d", funcRet);
+      goto ERROR;
+    }
+    return TSKM_E_OK;
+  }
+
+  pid = tskm_pf_createProc(p_svc->attr);
+  if (pid <= 0) {
+    TSKM_ASSERT(0);
+    goto ERROR;
+  }
+  TSKM_PRINTF(TSKM_LOG_STATE, "EXEC %s:%d", p_svc->attr->name, pid);
+
+  p_svc->pid = pid;
+  p_svc->waitReqCnt = 0;
+
+  // Startup service
+  svcExec_Process(p_svc);
+
+  funcRet = TSKM_E_OK;
+  ERROR: return funcRet;
+}
+
+/*********************************************************
+ *       Issuing a start request to the service
+ *********************************************************/
+TSKM_STATIC TSKM_ERR_t svcWakeupRequest(TSKM_SVC_CTX_t* p_svc,
+                                        TSKM_GSTEP_REQ_INFO_t* p_req) {
+  if (p_svc == NULL || p_svc->state == TSKM_SVC_DISABLE) {  // LCOV_EXCL_BR_LINE 6: Since this function has always been called after checking // NOLINT(whitespace/line_length)
+    AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+    return TSKM_E_STATE;  // LCOV_EXCL_LINE 6: Since this function has always been called after checking
+  } else if (p_svc->state == TSKM_SVC_WAITCONNECT) {
+    // Remember it once and issue a request when the CONNECT completes.
+    p_svc->request[p_svc->waitReqCnt] = *p_req;
+    p_svc->waitReqCnt++;
+  } else {
+    TSKM_ERR_t tskmRet;
+    tskmRet = wakeupRequest(p_svc, p_req);
+    TSKM_ERR_CHK_DFT;
+  }
+
+  return TSKM_E_OK;
+  ERROR: return TSKM_E_NG;
+}
+
+/*********************************************************
+ *      Service reboot processing
+ *********************************************************/
+TSKM_STATIC TSKM_ERR_t svcErrTermPost(TSKM_SVC_CTX_t* p_svc) {
+  TSKM_MAIN_CTX_t* p_main = tskm_getMainCtx();
+
+  TSKM_PRINTF(TSKM_LOG_SYSTEMDATA, "ERR TERM SVC(%s:%d)", p_svc->attr->name,
+              p_svc->pid);
+
+  if (p_svc->attr->lifeCycle == TSKM_SVC_LC_ALWAYS || (p_svc->errTermCnt > p_svc->attr->retryCnt && (uint32_t) -1 != p_svc->attr->retryCnt)) {  // NOLINT(whitespace/line_length)
+    TSKM_ERROR_REBOOT_t rebootInfo;
+
+    TSKM_ASSERT(0);
+    memset(&rebootInfo, 0, sizeof(TSKM_ERROR_REBOOT_t));
+    rebootInfo.type = TSKM_ERROR_REBOOT_NORMAL;
+    snprintf(rebootInfo.log.messageStr, TSKM_LOGGING_MSG_STR_SIZE,
+             "TaskManager:SVC ErrTerm");
+    tskm_sub_reboot(&rebootInfo);
+  } else {
+    TSKM_LOGGING_INFO_t logInfo;
+    logInfo.type = TSKM_LOGGING_TYPE_MODULE_LOGS;
+
+    snprintf(logInfo.messageStr, TSKM_LOGGING_MSG_STR_SIZE,
+             "TaskManager:SVC ErrTerm");
+    tskm_sub_logging(&logInfo);
+
+    if (p_svc->attr->lifeCycle == TSKM_SVC_LC_ALWAYS_RECOVERABLE) {
+      TSKM_ERR_t ret;
+      TSKM_GSTEP_REQ_INFO_t req = { 0 };
+
+      ret = svcExec(p_svc);
+
+      if (TSKM_E_OK != ret) {
+        TSKM_ASSERT_PRINT(0, "ret = %d", ret);
+        goto ERROR;
+      } else if (p_svc->state == TSKM_SVC_WAITCONNECT) {
+        // In the state waiting for execution
+        req.svcId = p_svc->attr->svcId;
+        req.localStep = TSKM_LSTEP_ALL;
+        ret = svcWakeupRequest(p_svc, &req);
+        if (TSKM_E_OK != ret) {
+          TSKM_ASSERT_PRINT(0, "ret = %d", ret);
+          goto ERROR;
+        }
+      }
+    }
+  }
+
+  return TSKM_E_OK;
+  ERROR: return TSKM_E_NG;
+}
+
+/*********************************************************
+ *      Connection handler
+ * ret:TRUE     SVC status changed
+ * ret:FALSE    SVC status not changed
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t connectHandle(TSKM_SVC_CTX_t* p_svc,
+                                      const TSKM_EVENT_INFO_t* p_inEv) {
+  uint32_t ii;
+
+  TSKM_ASSERT(p_svc->state == TSKM_SVC_WAITCONNECT);
+
+  p_svc->connFd = p_inEv->prm.repConnect.connFd;
+  p_svc->state = TSKM_SVC_WAKEUP;
+  TSKM_PRINTF(TSKM_LOG_DEBUG, "[ST:%d] WAKEUP", p_svc->pid);
+
+  p_svc->waitResCnt = 0;
+
+  if (p_svc->waitReqCnt) {
+    for (ii = 0; ii < p_svc->waitReqCnt; ii++) {
+      TSKM_ASSERT(TSKM_E_OK == wakeupRequest(p_svc, &p_svc->request[ii]));
+    }
+    p_svc->waitReqCnt = 0;
+  }
+
+  return TSKM_TRUE;
+}
+
+/*********************************************************
+ *      Disconnection handler
+ * ret:TRUE     SVC status changed
+ * ret:FALSE    SVC status not changed
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t disConnectHandle(TSKM_SVC_CTX_t* p_svc,
+                                         const TSKM_EVENT_INFO_t* p_inEv) {
+  // No status change
+
+  return TSKM_TRUE;
+}
+
+/*********************************************************
+ *      Response handler for the activation request
+ * ret:TRUE     SVC status changed
+ * ret:FALSE    SVC status not changed
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t resWakeupHandle(TSKM_SVC_CTX_t* p_svc,
+                                        const TSKM_EVENT_INFO_t* p_inEv) {
+  TSKM_ASSERT(p_svc->state == TSKM_SVC_WAKEUP);
+  TSKM_ASSERT(p_svc->waitResCnt > 0);
+  TSKM_BOOL_t isStateChg = TSKM_FALSE;
+
+  if (p_svc->isShmDone == TSKM_FALSE && p_inEv->prm.resWakeup.isShmDone) {
+    p_svc->isShmDone = TSKM_TRUE;
+    isStateChg = TSKM_TRUE;
+    TSKM_PRINTF(TSKM_LOG_SVCSTATE, "%s:SHM DONE", p_svc->attr->name);
+  }
+  if (p_svc->isStepDone == TSKM_FALSE && p_inEv->prm.resWakeup.isStepDone) {
+    p_svc->isStepDone = TSKM_TRUE;
+    isStateChg = TSKM_TRUE;
+    TSKM_PRINTF(TSKM_LOG_SVCSTATE, "%s:STEP DONE", p_svc->attr->name);
+  }
+
+  if (p_svc->waitResCnt > 0) {
+    p_svc->waitResCnt--;
+    if (p_svc->waitResCnt == 0) {  // Transition when the wait runs out
+      if (p_inEv->prm.resWakeup.isLast) {
+        p_svc->state = TSKM_SVC_RUNNING;  // Startup completed
+        TSKM_PRINTF(TSKM_LOG_SVCSTATE, "[ST:%d] RUN", p_svc->pid);
+      }
+      isStateChg = TSKM_TRUE;
+    }
+  }
+  return isStateChg;
+}
+
+/*********************************************************
+ *      Response handler for the termination request
+ * ret:TRUE     SVC state changed
+ * ret:FALSE    SVC state not changed
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t resDownHandle(TSKM_SVC_CTX_t* p_svc,
+                                      const TSKM_EVENT_INFO_t* p_inEv) {
+  TSKM_ASSERT(p_svc->state == TSKM_SVC_DOWN);
+  TSKM_ASSERT(p_svc->waitResCnt > 0);
+  TSKM_BOOL_t isStateChg = TSKM_FALSE;
+
+  TSKM_PRINTF(TSKM_LOG_DEBUG, "pid:%d waitCnt:%d", p_svc->pid,
+              p_svc->waitResCnt);
+  if (p_svc->waitResCnt > 0) {
+    p_svc->waitResCnt--;
+    if (p_svc->waitResCnt == 0) {  // Transition when the wait runs out
+      if (p_inEv->prm.resWakeup.isLast) {
+        p_svc->state = TSKM_SVC_FINDOWN;   // Termination complete
+        TSKM_PRINTF(TSKM_LOG_SVCSTATE, "[ST:%d] FIN_DOWN", p_svc->pid);
+      }
+      isStateChg = TSKM_TRUE;
+    }
+  }
+  return isStateChg;
+}
+
+/*********************************************************
+ *      Response Handlers for DebugDump Requests
+ * ret:TRUE     SVC state changed
+ * ret:FALSE    SVC state not changed
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t resDebugDumpHandle(TSKM_SVC_CTX_t* p_svc,
+                                           const TSKM_EVENT_INFO_t* p_inEv) {
+  TSKM_BOOL_t isStateChg = TSKM_FALSE;
+  TSKM_EV_PRI_EX_RES_DEBUGDUMP_PRM_t *p_prm;
+
+  if (!p_inEv->hasExtend || !p_inEv->extendPrm) {
+    TSKM_ASSERT(0);
+    goto ERROR;
+  }
+
+  p_prm = (TSKM_EV_PRI_EX_RES_DEBUGDUMP_PRM_t *) p_inEv->extendPrm;  // NOLINT (readability/casting)
+
+  // FALSE is specified here because the required service names have been added in the PrimaryLib.
+  tskm_sub_debugDumpRes(FALSE, p_prm->dumpMsg);
+
+  ERROR: return isStateChg;
+}
+
+/*********************************************************
+ *      Service termination request
+ * ret:TRUE     SVC state changed
+ * ret:FALSE    SVC state not changed
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t reqExit(TSKM_SVC_CTX_t* p_svc,
+                                const TSKM_EVENT_INFO_t* p_inEv) {
+  int ret;
+  TSKM_ERR_t tskmRet;
+  TSKM_ASSERT(p_svc->state == TSKM_SVC_RUNNING);
+  TSKM_ASSERT(p_svc->waitResCnt == 0);
+  TSKM_GSTEP_REQ_INFO_t req = { 0 };
+
+  req.svcId = p_svc->attr->svcId;
+  req.localStep = TSKM_LSTEP_ALL;
+  tskmRet = tskm_svcDownRequest(p_svc, &req);
+  TSKM_ERR_CHK_DFT;
+
+  return TSKM_TRUE;
+
+  // LCOV_EXCL_START 8: dead code
+  AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+  ERROR: ret = tskm_pf_terminateProcGroup(static_cast<uint16_t>(p_svc->pid));
+  if (ret != 0) {  // LCOV_EXCL_BR_LINE 8: dead code
+    TSKM_PRINTF(TSKM_LOG_WARN, "ret = %d", ret);
+  }
+
+  return TSKM_TRUE;
+  // LCOV_EXCL_STOP
+}
+
+/*********************************************************
+ *      Service termination handler
+ * ret:TRUE     SVC state changed
+ * ret:FALSE    SVC state not changed
+ *********************************************************/
+TSKM_STATIC TSKM_BOOL_t repTermHandle(TSKM_SVC_CTX_t* p_svc,
+                                      const TSKM_EVENT_INFO_t* p_inEv) {
+  int ret;
+
+  // Check error
+  if (p_svc->attr->lifeCycle == TSKM_SVC_LC_DYNAMIC) {
+    if (p_svc->state != TSKM_SVC_DOWN) {
+      // A STATE other than DOWN does not terminate.
+      TSKM_PRINTF(TSKM_LOG_ERROR, "ERR TERM %s(%d) waitCnt:%d",
+                  p_svc->attr->name, p_svc->pid, p_svc->waitResCnt);
+      TSKM_ASSERT(0);
+    }
+  } else {
+    // The resident service terminated.
+    TSKM_PRINTF(TSKM_LOG_ERROR, "ERR TERM %s(%d) waitCnt:%d", p_svc->attr->name,
+                p_svc->pid, p_svc->waitResCnt);
+    TSKM_ASSERT(0);
+  }
+
+  TSKM_PRINTF(TSKM_LOG_STATE, "[ST:%d] DORMANT", p_svc->pid);
+
+  p_svc->state = TSKM_SVC_DORMANT;
+  p_svc->waitResCnt = 0;
+  p_svc->pid = 0;
+
+  if (p_svc->attr->type == TSKM_SVC_TYPE_NATIVE) {
+    EFrameworkunifiedStatus taskmanagerStatus;
+    TSKM_MAIN_CTX_t* p_main = tskm_getMainCtx();
+    SS_String availabilityName = p_svc->attr->name;
+    availabilityName.append("/Availability");
+
+    taskmanagerStatus = FrameworkunifiedUnsubscribeNotificationWithCallback(
+        p_main->hApp, availabilityName.c_str());
+    if (eFrameworkunifiedStatusOK != taskmanagerStatus) {
+      TSKM_ASSERT(0);
+    }
+
+    p_svc->isAvailable = TSKM_FALSE;
+    p_svc->watchCnt = 0;
+
+    HANDLE hMq = McOpenSender(FRAMEWORKUNIFIED_NS_NPSERVICE);
+    if (NULL == hMq) {   // LCOV_EXCL_BR_LINE 4: NSFW error case.
+      AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+      TSKM_ASSERT(0);  // LCOV_EXCL_LINE 4: NSFW error case.
+    } else {
+      ServiceAvailability availInfo = { };
+
+      snprintf(availInfo.cServiceName, MAX_NAME_SIZE_APP, "%s",
+               p_svc->attr->name);
+      availInfo.eServiceAvailability = eFrameworkunifiedServiceNotAvailable;
+
+      taskmanagerStatus = NPPublishNotification(hMq, p_svc->attr->name,
+                                        availabilityName.c_str(), &availInfo,
+                                        sizeof(availInfo));
+      if (eFrameworkunifiedStatusOK != taskmanagerStatus) {
+        TSKM_ASSERT(0);
+      }
+
+      taskmanagerStatus = McClose(hMq);
+      if (eFrameworkunifiedStatusOK != taskmanagerStatus) {  // LCOV_EXCL_BR_LINE 4: NSFW error case.
+        AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
+        TSKM_ASSERT(0);  // LCOV_EXCL_LINE 4: NSFW error case.
+      }
+    }
+  }
+
+  if (p_inEv->errCode != TSKM_E_OK) {
+    p_svc->errTermCnt++;
+    // Notify 'NG' at Relaunch after abnormal termination
+    p_svc->bootInfo.resetStatus = e_SS_SM_RESET_STATUS_NG;
+
+    ret = svcErrTermPost(p_svc);
+    if (ret != TSKM_E_OK) {
+      TSKM_ASSERT(0);
+    }
+  } else {
+    // NONE is notified at Relaunch after normal completion.
+    p_svc->bootInfo.resetStatus = e_SS_SM_RESET_STATUS_NONE;
+  }
+
+  return TSKM_TRUE;
+}
+
+/*********************************************************
+ *       Get service context
+ *********************************************************/
+TSKM_SVC_CTX_t*
+tskm_svcsGetSvcBySvcId(TSKM_SVCS_CTX_t* p_svcs, TSKM_SVCID_t svcId) {
+  return getSvcCtxBySvcId(p_svcs, svcId);
+}
+
+/*********************************************************
+ *       Get service context
+ *********************************************************/
+TSKM_SVC_CTX_t*
+tskm_svcsGetSvcByPid(TSKM_SVCS_CTX_t* p_svcs, pid_t pid) {
+  return getSvcCtxByPid(p_svcs, pid);
+}
+
+/*********************************************************
+ *       Check for waiting services
+ *********************************************************/
+TSKM_BOOL_t tskm_svcsIsWaiting(TSKM_SVCS_CTX_t* p_svcs) {
+  uint32_t ii;
+  for (ii = 0; ii < p_svcs->svcNum; ii++) {
+    if (p_svcs->svcList[ii].waitResCnt > 0
+        || p_svcs->svcList[ii].state == TSKM_SVC_WAITCONNECT) {
+      return TSKM_TRUE;
+    }
+  }
+  return TSKM_FALSE;
+}
+
+/*********************************************************
+ *       Check if ShutdownWait services are terminated
+ *********************************************************/
+TSKM_SVC_WAIT_STATE_t tskm_svcsGetSvcTermWaitState(TSKM_SVCS_CTX_t* p_svcs) {
+  uint32_t ii;
+  TSKM_SVC_WAIT_STATE_t waitState = TSKM_SVC_WAIT_NONE;
+
+  for (ii = 0; ii < p_svcs->svcNum; ii++) {
+    if (p_svcs->svcList[ii].attr->shotdownWait) {
+      // Check shutdownWait SVC Only
+      if (p_svcs->svcList[ii].state != TSKM_SVC_DORMANT) {
+        // DORMANT is terminated or not started, so no checking is required
+        if (TSKM_SVC_LC_DYNAMIC != p_svcs->svcList[ii].attr->lifeCycle
+            && p_svcs->svcList[ii].state != TSKM_SVC_FINDOWN) {  // Check of termination of resident SVCs
+          waitState = TSKM_SVC_WAIT_BOTH;
+          break;
+        } else if (TSKM_SVC_LC_DYNAMIC == p_svcs->svcList[ii].attr->lifeCycle) {  // Check of termination of non-resident SVCs
+          waitState = TSKM_SVC_WAIT_TRANSIENT;
+        }
+      }
+    }
+  }
+  return waitState;
+}
+
+/*********************************************************
+ *       Update boot info of all services
+ *********************************************************/
+TSKM_ERR_t tskm_svcsSetBootInfo(TSKM_SVCS_CTX_t* p_svcs,
+                                T_SS_SM_START_DataStructType* p_info,
+                                T_SS_SM_START_ExtDataStructType *p_exInfo) {
+  uint32_t ii = 0;
+
+  for (ii = 0; ii < p_svcs->svcNum; ii++) {
+    p_svcs->svcList[ii].bootInfo = *p_info;
+    p_svcs->svcList[ii].extBootInfo = *p_exInfo;
+  }
+  return TSKM_E_OK;
+}
+
+/*********************************************************
+ *       Terminates a running non-resident services
+ *********************************************************/
+TSKM_ERR_t tskm_svcsAvtiveSvcTerm(TSKM_SVCS_CTX_t* p_svcs) {
+  uint32_t ii;
+
+  for (ii = 0; ii < p_svcs->svcNum; ii++) {
+    TSKM_SVC_CTX_t* p_svc = &p_svcs->svcList[ii];
+
+    if (TSKM_SVC_LC_DYNAMIC == p_svc->attr->lifeCycle &&              // Non-resident SVC
+        TSKM_SVC_RUNNING == p_svc->state && p_svc->waitResCnt == 0) {  // Running
+      TSKM_ERR_t tskmRet;
+      TSKM_GSTEP_REQ_INFO_t req = { 0 };
+
+      req.svcId = p_svc->attr->svcId;
+      req.localStep = TSKM_LSTEP_ALL;
+      tskmRet = tskm_svcDownRequest(p_svc, &req);
+      TSKM_ERR_CHK_DFT;
+    }
+  }
+
+  return TSKM_E_OK;
+
+  ERROR:
+  return TSKM_E_NG;
+}
+
+/*********************************************************
+ *   Calls back the DebugDump of the running services
+ *********************************************************/
+TSKM_ERR_t tskm_svcsCallDebugDump(TSKM_SVCS_CTX_t* p_svcs) {
+  for (uint32_t ii = 0; ii < p_svcs->svcNum; ii++) {
+    TSKM_SVC_CTX_t* p_svc = &p_svcs->svcList[ii];
+
+    if (TSKM_SVC_RUNNING == p_svc->state) {  // Running
+      TSKM_EVENT_INFO_t ev;
+      bzero(&ev, sizeof(ev));
+
+      // Send DebugDump request
+      ev.event = TSKM_EV_PRI_REQ_DEBUGDUMP;
+      ev.errCode = TSKM_E_OK;
+      if (0 >= tskm_sockSend(p_svc->connFd, &ev)) {
+        TSKM_ASSERT(0);
+        goto ERROR;
+      }
+    }
+  }
+
+  return TSKM_E_OK;
+  ERROR: return TSKM_E_NG;
+}
+
+/*********************************************************
+ *   Calls back the LowMemory of the running services
+ *********************************************************/
+TSKM_ERR_t tskm_svcsCallLowMem(TSKM_SVCS_CTX_t* p_svcs) {
+  for (uint32_t ii = 0; ii < p_svcs->svcNum; ii++) {
+    TSKM_SVC_CTX_t* p_svc = &p_svcs->svcList[ii];
+    if (TSKM_SVC_RUNNING == p_svc->state) {  // Running
+      TSKM_EVENT_INFO_t ev;
+      bzero(&ev, sizeof(ev));
+
+      // Send LowMemory detection notification
+      ev.event = TSKM_EV_PRI_REP_LOWMEM;
+      ev.errCode = TSKM_E_OK;
+      if (0 >= tskm_sockSend(p_svc->connFd, &ev)) {
+        TSKM_ASSERT(0);
+        goto ERROR;
+      }
+    }
+  }
+
+  return TSKM_E_OK;
+  ERROR: return TSKM_E_NG;
+}
+
+/*********************************************************
+ *      Event handler
+ *********************************************************/
+TSKM_ERR_t tskm_svcEventHandle(TSKM_SVC_CTX_t* p_svc, TSKM_EVENT_INFO_t* p_ev) {
+  TSKM_FUNC_IN();
+  TSKM_BOOL_t isStateChg = TSKM_FALSE;
+
+  if (isSvcEvent(p_ev) == TSKM_FALSE) {
+    // If it is not an event for the service, it will be sent as follows.
+    TSKM_FUNC_OUT();
+    return TSKM_E_OK;
+  }
+
+  switch (p_ev->event) {
+    case TSKM_EV_PRI_REP_CONNECT:   // Connection Registration from Service
+      isStateChg = connectHandle(p_svc, p_ev);
+      break;
+    case TSKM_EV_PRI_REP_DISCONNECT:   // Disconnection Registration from Service
+      isStateChg = disConnectHandle(p_svc, p_ev);
+      break;
+    case TSKM_EV_PRI_RES_WAKEUP:    // Response to a startup request from a service
+      isStateChg = resWakeupHandle(p_svc, p_ev);
+      break;
+    case TSKM_EV_PRI_RES_DOWN:
+      isStateChg = resDownHandle(p_svc, p_ev);
+      break;
+    case TSKM_EV_PRI_RES_DEBUGDUMP:
+      isStateChg = resDebugDumpHandle(p_svc, p_ev);
+      break;
+    case TSKM_EV_PRI_REQ_EXIT:
+      isStateChg = reqExit(p_svc, p_ev);
+      break;
+    case TSKM_EV_SVC_REP_TERM:      // Service termination
+      isStateChg = repTermHandle(p_svc, p_ev);
+      break;
+    default:
+      break;
+  }
+
+  // Overwrite service state change
+  if (isStateChg) {
+    p_ev->event = TSKM_EV_LCL_CHG_SVC_STATE;
+    p_ev->prm.chgSvc.svcId = p_svc->attr->svcId;
+  } else {
+    p_ev->event = TSKM_EV_NOP;
+  }
+
+  TSKM_FUNC_OUT();
+  return TSKM_E_OK;
+}
+/*********************************************************
+ *       Startup service
+ *********************************************************/
+TSKM_ERR_t tskm_svcExec(TSKM_SVC_CTX_t* p_svc) {
+  return svcExec(p_svc);
+}
+
+/*********************************************************
+ *       Issue a startup request to the service
+ *********************************************************/
+TSKM_ERR_t tskm_svcWakeupRequest(TSKM_SVC_CTX_t* p_svc,
+                                 TSKM_GSTEP_REQ_INFO_t* p_req) {
+  return svcWakeupRequest(p_svc, p_req);
+}
+
+/*********************************************************
+ *       Issue a termination request to the service
+ *********************************************************/
+TSKM_ERR_t tskm_svcDownRequest(TSKM_SVC_CTX_t* p_svc,
+                               TSKM_GSTEP_REQ_INFO_t* p_req) {
+  int ret;
+  TSKM_EVENT_INFO_t ev;
+
+  bzero(&ev, sizeof(ev));
+
+  if (tskm_svcIsCommunicatable(p_svc) == TSKM_FALSE) {
+    return TSKM_E_OK;
+  }
+
+  ev.event = TSKM_EV_PRI_REQ_DOWN;
+  ev.errCode = TSKM_E_OK;
+  ev.prm.reqDown.localStep = p_req->localStep;
+
+  ret = tskm_sockSend(p_svc->connFd, &ev);
+  if (ret <= 0) {
+    TSKM_ASSERT(0);
+    goto ERROR;
+  }
+  p_svc->waitResCnt++;
+  p_svc->state = TSKM_SVC_DOWN;
+
+  return TSKM_E_OK;
+  ERROR: return TSKM_E_NG;
+}
+
+/*********************************************************
+ *       Prohibit starting service
+ *********************************************************/
+TSKM_ERR_t tskm_svcDisableRequest(TSKM_SVC_CTX_t* p_svc) {
+  if (p_svc->state == TSKM_SVC_DORMANT || p_svc->state == TSKM_SVC_DISABLE) {
+    p_svc->state = TSKM_SVC_DISABLE;
+    return TSKM_E_OK;
+  }
+  return TSKM_E_STATE;
+}
+
+/*********************************************************
+ *       Allow starting service
+ *********************************************************/
+TSKM_ERR_t tskm_svcEnableRequest(TSKM_SVC_CTX_t* p_svc) {
+  if (p_svc->state == TSKM_SVC_DISABLE) {
+    p_svc->state = TSKM_SVC_DORMANT;
+  }
+  return TSKM_E_OK;
+}
+
+/*********************************************************
+ *      Queriy whether the service is ready for communication
+ *********************************************************/
+TSKM_BOOL_t tskm_svcIsCommunicatable(TSKM_SVC_CTX_t* p_svc) {
+  TSKM_BOOL_t ret = TSKM_FALSE;
+  if (p_svc == NULL || p_svc->attr->type == TSKM_SVC_TYPE_UNKNONW) {
+  } else {
+    switch (p_svc->state) {
+      case TSKM_SVC_WAKEUP:
+      case TSKM_SVC_RUNNING:
+      case TSKM_SVC_DOWN:
+        ret = TSKM_TRUE;
+        break;
+      default:
+        break;
+    }
+  }
+  return ret;
+}  // LCOV_EXCL_BR_LINE 10: Final line
+