diff --git a/interfaces/innerkits/include/service_control.h b/interfaces/innerkits/include/service_control.h index 1b9635fc76c85212c024c0ae63361627e2f58150..61499406f7a5b9bf706cb0cfe86ca6477baadcac 100644 --- a/interfaces/innerkits/include/service_control.h +++ b/interfaces/innerkits/include/service_control.h @@ -34,7 +34,8 @@ int ServiceControlWithExtra(const char *serviceName, int action, const char *ext int ServiceControl(const char *serviceName, int action); // Service status can set "running", "stopping", "stopped", "reseting". waitTimeout(s). int ServiceWaitForStatus(const char *serviceName, const char *status, int waitTimeout); - +int StartServiceByTimer(const char *serviceName, uint64_t timeout); +int StopServiceTimer(const char *serviceName); #ifdef __cplusplus #if __cplusplus } diff --git a/interfaces/innerkits/service_control/service_control.c b/interfaces/innerkits/service_control/service_control.c index 42f8273fed0cf333967d1c5ee1a3688fbeaa6aeb..b61d0208a53a566fc60e9cff791829974f97dc21 100644 --- a/interfaces/innerkits/service_control/service_control.c +++ b/interfaces/innerkits/service_control/service_control.c @@ -198,3 +198,49 @@ int ServiceWaitForStatus(const char *serviceName, const char *status, int waitTi BEGET_LOGI("Success wait"); return 0; } + +int StartServiceByTimer(const char *serviceName, uint64_t timeout) +{ + if (serviceName == NULL) { + BEGET_LOGE("Request start serivce by timer with invalid service name"); + return -1; + } + + if (timeout == 0) { + // start service immediately. + return ServiceControl(serviceName, START); + } + // restrict timeout value, not too long. + char value[PARAM_VALUE_LEN_MAX] = {}; + if (snprintf_s(value, PARAM_NAME_LEN_MAX, PARAM_NAME_LEN_MAX - 1, "%s|%lld", serviceName, timeout) == -1) { + BEGET_LOGE("Failed to build parameter value"); + return -1; + } + + if (SystemSetParameter("ohos.servicectrl.timer_start", value) != 0) { + BEGET_LOGE("Failed to set parameter \' ohos.servicectrl.timer_start \' with value \' %s \'", value); + return -1; + } + return 0; +} + +int StopServiceTimer(const char *serviceName) +{ + if (serviceName == NULL) { + BEGET_LOGE("Request stop serivce timer with invalid service name"); + return -1; + } + + char value[PARAM_VALUE_LEN_MAX] = {}; + int ret = strncpy_s(value, PARAM_VALUE_LEN_MAX - 1, serviceName, strlen(serviceName)); + if (ret < 0) { + BEGET_LOGE("Failed to copy service name to parameter"); + return -1; + } + + if (SystemSetParameter("ohos.servicectrl", value) != 0) { + BEGET_LOGE("Failed to set parameter \' ohos.servicectrl.timer_stop \' with value \' %s \'", value); + return -1; + } + return 0; +} diff --git a/services/begetctl/service_control.c b/services/begetctl/service_control.c index e6580f206e67dda192102bfa1f2d68cacc1b09d8..13e25e1646ffded582f266146f6f191a387000eb 100755 --- a/services/begetctl/service_control.c +++ b/services/begetctl/service_control.c @@ -46,6 +46,19 @@ static int main_cmd(BShellHandle shell, int argc, char **argv) ServiceControlWithExtra(argv[1], 0, (const char **)argv + SERVICE_START_NUMBER, argc - SERVICE_START_NUMBER); } else if (strcmp(argv[0], "stop") == 0) { ServiceControlWithExtra(argv[1], 1, (const char **)argv + SERVICE_START_NUMBER, argc - SERVICE_START_NUMBER); + } else if (strcmp(argv[0], "timer_start") == 0) { + if (argc < SERVICE_START_NUMBER) { + return -1; + } + char *timeBuffer = argv[SERVICE_START_NUMBER]; + errno = 0; + uint64_t timeout = strtoull(timeBuffer, NULL, 10); + if (errno != 0) { + return -1; + } + StartServiceByTimer(argv[1], timeout); + } else if (strcmp(argv[0], "timer_stop") == 0) { + StopServiceTimer(argv[1]); } else { ServiceControlUsage(shell, argc, argv); } @@ -58,7 +71,9 @@ MODULE_CONSTRUCTOR(void) {"service_control", main_cmd, "stop service", "service_control stop servicename", "service_control stop"}, {"service_control", main_cmd, "start service", "service_control start servicename", "service_control start"}, {"stop_service", main_cmd, "stop service", "stop_service servicename", ""}, - {"start_service", main_cmd, "start service", "start_service servicename", ""} + {"start_service", main_cmd, "start service", "start_service servicename", ""}, + {"timer_start", main_cmd, "start service by timer", "timer_start servicename timeout", ""}, + {"timer_stop", main_cmd, "stop service timer", "timer_stop servicename", ""}, }; for (size_t i = 0; i < sizeof(infos) / sizeof(infos[0]); i++) { BShellEnvRegitsterCmd(GetShellHandle(), &infos[i]); diff --git a/services/init/include/init_service.h b/services/init/include/init_service.h index 0be9c004fc9945d52e1653e8f3774c454fceb70d..03bbefc527aba8197a66f91ad49680d57b218d4a 100755 --- a/services/init/include/init_service.h +++ b/services/init/include/init_service.h @@ -26,6 +26,7 @@ # include "init_selinux_param.h" #endif // WITH_SELINUX #include "list.h" +#include "loop_event.h" #ifdef __cplusplus #if __cplusplus extern "C" { @@ -47,6 +48,7 @@ extern "C" { #define SERVICE_ATTR_CONSOLE 0x080 // console #define SERVICE_ATTR_DYNAMIC 0x100 // dynamic service #define SERVICE_ATTR_ONDEMAND 0x200 // ondemand, manage socket by init +#define SERVICE_ATTR_TIMERSTART 0x400 // Mark a service will be started by timer #define MAX_SERVICE_NAME 32 #define MAX_APL_NAME 32 @@ -65,6 +67,15 @@ extern "C" { #define IsOnDemandService(service) \ (((service)->attribute & SERVICE_ATTR_ONDEMAND) == SERVICE_ATTR_ONDEMAND) +#define IsServiceWithTimerEnabled(service) \ + (((service)->attribute & SERVICE_ATTR_TIMERSTART) == SERVICE_ATTR_TIMERSTART) + +#define DisableServiceTimer(service) \ + ((service)->attribute &= ~SERVICE_ATTR_TIMERSTART) + +#define EnableServiceTimer(service) \ + ((service)->attribute |= SERVICE_ATTR_TIMERSTART) + typedef enum { START_MODE_CONDITION, START_MODE_BOOT, @@ -134,6 +145,7 @@ typedef struct Service_ { ServiceFile *fileCfg; int *fds; size_t fdCount; + TimerHandle timer; ServiceJobs serviceJobs; CpuArgs cpuInfo; } Service; @@ -152,6 +164,8 @@ void CloseServiceFds(Service *service, bool needFree); int UpdaterServiceFds(Service *service, int *fds, size_t fdCount); int SetAccessToken(const Service *service); void GetAccessToken(void); +void ServiceStopTimer(Service *service); +void ServiceStartTimer(Service *service, uint64_t timeout); #ifdef __cplusplus #if __cplusplus diff --git a/services/init/init_common_service.c b/services/init/init_common_service.c index 43e458e07dbc8ceeed0f05989f2ace310970fc89..7fb567c6d877eff568ac124735842446ba8733fe 100755 --- a/services/init/init_common_service.c +++ b/services/init/init_common_service.c @@ -40,6 +40,7 @@ #include "init_service_socket.h" #include "init_utils.h" #include "fd_holder_internal.h" +#include "loop_event.h" #include "securec.h" #ifdef WITH_SELINUX @@ -299,7 +300,7 @@ int ServiceStart(Service *service) DoJobNow(service->serviceJobs.jobsName[JOB_ON_START]); } - (void)ClearEnvironment(); + ClearEnvironment(); if (!IsOnDemandService(service)) { int ret = CreateServiceSocket(service); @@ -351,6 +352,10 @@ int ServiceStop(Service *service) if (service->fdCount != 0) { CloseServiceFds(service, true); } + + if (IsServiceWithTimerEnabled(service)) { + ServiceStopTimer(service); + } INIT_ERROR_CHECK(kill(service->pid, SIGKILL) == 0, return SERVICE_FAILURE, "stop service %s pid %d failed! err %d.", service->name, service->pid, errno); NotifyServiceChange(service->name, "stopping"); @@ -424,6 +429,11 @@ void ServiceReap(Service *service) return; } + // If the service set timer + // which means the timer handler will start the service + // Init should not start it automatically. + INIT_CHECK(IsServiceWithTimerEnabled(service) == 0, return); + if (!IsOnDemandService(service)) { CloseServiceSocket(service); } @@ -469,9 +479,7 @@ void ServiceReap(Service *service) INIT_CHECK_ONLY_ELOG(ret == SERVICE_SUCCESS, "Failed to exec restartArg for %s", service->name); } ret = ServiceStart(service); - if (ret != SERVICE_SUCCESS) { - INIT_LOGE("reap service %s start failed!", service->name); - } + INIT_CHECK_ONLY_ELOG(ret == SERVICE_SUCCESS, "reap service %s start failed!", service->name); service->attribute &= (~SERVICE_ATTR_NEED_RESTART); } @@ -535,3 +543,63 @@ int UpdaterServiceFds(Service *service, int *fds, size_t fdCount) INIT_LOGI("Hold fd for service \' %s \' done", service->name); return ret; } + +void ServiceStopTimer(Service *service) +{ + INIT_ERROR_CHECK(service != NULL, return, "Stop timer with invalid service"); + if (IsServiceWithTimerEnabled(service)) { + // Stop timer first + if (service->timer) { + LE_StopTimer(LE_GetDefaultLoop(), service->timer); + } + service->timer = NULL; + DisableServiceTimer(service); + } +} + +static void ServiceTimerStartProcess(const TimerHandle handler, void *context) +{ + UNUSED(handler); + Service *service = (Service *)context; + + if (service == NULL) { + INIT_LOGE("Service timer process with invalid service"); + return; + } + + // OK, service is ready to start. + // Before start the service, stop service timer. + // make sure it will not enter timer handler next time. + ServiceStopTimer(service); + int ret = ServiceStart(service); + if (ret != SERVICE_SUCCESS) { + INIT_LOGE("Start service \' %s \' in timer failed"); + } +} + +void ServiceStartTimer(Service *service, uint64_t timeout) +{ + bool oldTimerClean = false; + INIT_ERROR_CHECK(service != NULL, return, "Start timer with invalid service"); + // If the service already set a timer. + // And a new request coming. close it and create a new one. + if (IsServiceWithTimerEnabled(service)) { + ServiceStopTimer(service); + oldTimerClean = true; + } + LE_STATUS status = LE_CreateTimer(LE_GetDefaultLoop(), &service->timer, ServiceTimerStartProcess, + (void *)service); + if (status != LE_SUCCESS) { + INIT_LOGE("Create service timer for service \' %s \' failed, status = %d", service->name, status); + if (oldTimerClean) { + INIT_LOGE("previous timer is cleared"); + } + return; + } + status = LE_StartTimer(LE_GetDefaultLoop(), service->timer, timeout, 1); + if (status != LE_SUCCESS) { + INIT_LOGE("Start service timer for service \' %s \' failed, status = %d", service->name, status); + return; + } + EnableServiceTimer(service); +} diff --git a/services/init/standard/init_cmds.c b/services/init/standard/init_cmds.c index eaa1b935ec12de31b9e0c840573e4288f0a1e875..d6cab418725761e742af648b99df0f6896366fa7 100755 --- a/services/init/standard/init_cmds.c +++ b/services/init/standard/init_cmds.c @@ -282,6 +282,55 @@ static void DoLoadAccessTokenId(const struct CmdArgs *ctx) LoadAccessTokenId(); } +static void DoTimerStart(const struct CmdArgs*ctx) +{ + INIT_LOGI("Timer start service with arg = %s", ctx->argv[0]); + char *arg = ctx->argv[0]; + int count = 0; + int expectedCount = 2; + char **splitArgs = SplitStringExt(ctx->argv[0], "|", &count, expectedCount); + if (splitArgs == NULL) { + INIT_LOGE("Call timer_start with invalid arguments"); + return; + } + + if (count != expectedCount) { + INIT_LOGE("Call timer_start with unexpect arguments %s", arg); + FreeStringVector(splitArgs, count); + return; + } + + Service *service = GetServiceByName(splitArgs[0]); + if (service == NULL) { + INIT_LOGE("Cannot find service in timer_start command"); + FreeStringVector(splitArgs, count); + return; + } + + errno = 0; + uint64_t timeout = strtoull(splitArgs[1], NULL, DECIMAL_BASE); + if (errno != 0) { + INIT_LOGE("call timer_start with invalid timer"); + FreeStringVector(splitArgs, count); + return; + } + // not need this anymore , release memory. + FreeStringVector(splitArgs, count); + ServiceStartTimer(service, timeout); +} + +static void DoTimerStop(const struct CmdArgs*ctx) +{ + INIT_LOGI("Stop service timer with arg = %s", ctx->argv[0]); + const char *serviceName = ctx->argv[0]; + Service *service = GetServiceByName(serviceName); + if (service == NULL) { + INIT_LOGE("Cannot find service in timer_stop command"); + return; + } + ServiceStopTimer(service); +} + static const struct CmdTable g_cmdTable[] = { { "exec ", 1, 10, DoExec }, { "mknode ", 1, 5, DoMakeNode }, @@ -297,6 +346,8 @@ static const struct CmdTable g_cmdTable[] = { { "mount_fstab ", 1, 1, DoMountFstabFile }, { "umount_fstab ", 1, 1, DoUmountFstabFile }, { "restorecon ", 0, 1, DoRestorecon }, + { "timer_start", 1, 1, DoTimerStart }, + { "timer_stop", 1, 1, DoTimerStop }, }; const struct CmdTable *GetCmdTable(int *number) diff --git a/services/param/service/param_service.c b/services/param/service/param_service.c index 5e760dcf0551e125f244155a604d0efb969d3230..53d31570dd4d2d0f96bf09db3a9f76be77f1cc8a 100755 --- a/services/param/service/param_service.c +++ b/services/param/service/param_service.c @@ -182,7 +182,7 @@ static char *GetServiceCtrlName(ParamWorkSpace *workSpace, const char *name, con "ohos.ctl.stop" }; static char *installParam[] = { - "ohos.servicectrl" + "ohos.servicectrl." }; static char *powerCtrlArg[][2] = { {"reboot,shutdown", "reboot.shutdown"}, @@ -206,7 +206,7 @@ static char *GetServiceCtrlName(ParamWorkSpace *workSpace, const char *name, con } for (size_t i = 0; i < ARRAY_LENGTH(installParam); i++) { - if (strcmp(name, installParam[i]) == 0) { + if (strncmp(name, installParam[i], strlen(installParam[i])) == 0) { return BuildKey(workSpace, "%s.%s", name, value); } }