Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
Startup Init Lite
提交
3275e747
S
Startup Init Lite
项目概览
OpenHarmony
/
Startup Init Lite
1 年多 前同步成功
通知
3
Star
37
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
Startup Init Lite
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
3275e747
编写于
12月 27, 2021
作者:
X
xionglei6
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add beget control api
Signed-off-by:
N
xionglei6
<
xionglei6@huawei.com
>
上级
f5ed759c
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
378 addition
and
43 deletion
+378
-43
interfaces/innerkits/BUILD.gn
interfaces/innerkits/BUILD.gn
+25
-1
interfaces/innerkits/begetctl_utils/begetctl_utils.c
interfaces/innerkits/begetctl_utils/begetctl_utils.c
+196
-0
interfaces/innerkits/begetctl_watcher/begetctl_watcher.c
interfaces/innerkits/begetctl_watcher/begetctl_watcher.c
+42
-0
interfaces/innerkits/include/begetctl_utils.h
interfaces/innerkits/include/begetctl_utils.h
+42
-0
interfaces/innerkits/include/begetctl_watcher.h
interfaces/innerkits/include/begetctl_watcher.h
+34
-36
ohos.build
ohos.build
+11
-4
services/init/include/init_service.h
services/init/include/init_service.h
+2
-1
services/init/init_service_manager.c
services/init/init_service_manager.c
+26
-1
未找到文件。
interfaces/innerkits/BUILD.gn
浏览文件 @
3275e747
...
...
@@ -30,6 +30,7 @@ ohos_shared_library("libbegetutil") {
"file/init_file.c",
"reboot/init_reboot_innerkits.c",
"socket/init_socket.c",
"begetctl_utils/begetctl_utils.c",
]
sources += fs_manager_sources
...
...
@@ -39,7 +40,6 @@ ohos_shared_library("libbegetutil") {
"//third_party/bounds_checking_function/include",
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/services/include/param",
"//base/startup/syspara_lite/hals/parameter/include",
]
deps = [
"//base/startup/init_lite/services/log:init_log",
...
...
@@ -49,8 +49,32 @@ ohos_shared_library("libbegetutil") {
"//third_party/openssl:libcrypto_static",
"//utils/native/base:utils",
]
external_deps = [ "hiviewdfx_hilog_native:libhilog" ]
public_configs = [ ":exported_header_files" ]
part_name = "init"
install_images = [
"system",
"updater",
]
}
# watcher lib must separate compilation avoid interdependence.
ohos_shared_library("libbegetctl_watcher") {
sources = [ "begetctl_watcher/begetctl_watcher.c" ]
include_dirs = [
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/services/include/param",
"//third_party/bounds_checking_function/include",
]
deps = [
"//base/startup/init_lite/services/param/watcher:param_watcheragent",
"//base/startup/init_lite/services/utils:libinit_utils",
"//third_party/bounds_checking_function:libsec_shared",
]
public_configs = [ ":exported_header_files" ]
external_deps = [ "hiviewdfx_hilog_native:libhilog" ]
part_name = "init"
install_images = [
"system",
...
...
interfaces/innerkits/begetctl_utils/begetctl_utils.c
0 → 100755
浏览文件 @
3275e747
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* 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 "begetctl_utils.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hilog/log.h"
#include "init_utils.h"
#include "securec.h"
#include "sys_param.h"
static
int
StartDynamicProcess
(
const
char
*
name
,
const
char
*
extArgv
[],
int
extArgc
)
{
if
(
name
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Start dynamic service failed, service name is null."
);
return
-
1
;
}
int
extraArg
=
0
;
if
((
extArgv
!=
NULL
)
&&
(
extArgc
>
0
))
{
HILOG_INFO
(
LOG_CORE
,
"Start service by extra args"
);
extraArg
=
1
;
}
if
(
extraArg
==
1
)
{
unsigned
int
len
=
0
;
for
(
int
i
=
0
;
i
<
extArgc
;
i
++
)
{
len
+=
strlen
(
extArgv
[
i
]);
}
len
+=
strlen
(
name
)
+
extArgc
+
1
;
char
*
nameValue
=
(
char
*
)
calloc
(
len
,
sizeof
(
char
));
if
(
nameValue
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed calloc err=%{public}d"
,
errno
);
return
-
1
;
}
if
(
strncat_s
(
nameValue
,
len
,
name
,
strlen
(
name
))
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed strncat_s name err=%{public}d"
,
errno
);
return
-
1
;
}
for
(
int
j
=
0
;
j
<
extArgc
;
j
++
)
{
if
(
strncat_s
(
nameValue
,
len
,
"|"
,
1
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed strncat_s
\"
|
\"
err=%{public}d"
,
errno
);
return
-
1
;
}
if
(
strncat_s
(
nameValue
,
len
,
extArgv
[
j
],
strlen
(
extArgv
[
j
]))
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed strncat_s err=%{public}d"
,
errno
);
return
-
1
;
}
}
HILOG_INFO
(
LOG_CORE
,
"nameValue is %{public}s"
,
nameValue
);
if
(
SystemSetParameter
(
"ohos.ctl.start"
,
nameValue
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Set param for %{public}s failed.
\n
"
,
nameValue
);
free
(
nameValue
);
return
-
1
;
}
free
(
nameValue
);
}
else
{
if
(
SystemSetParameter
(
"ohos.ctl.start"
,
name
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Set param for %{public}s failed.
\n
"
,
name
);
return
-
1
;
}
}
return
0
;
}
static
int
StopDynamicProcess
(
const
char
*
serviceName
)
{
if
(
serviceName
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Stop dynamic service failed, service is null.
\n
"
);
return
-
1
;
}
if
(
SystemSetParameter
(
"ohos.ctl.stop"
,
serviceName
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Set param for %{public}s failed.
\n
"
,
serviceName
);
return
-
1
;
}
return
0
;
}
static
int
GetCurrentServiceStatus
(
const
char
*
serviceName
,
char
*
paramValue
,
unsigned
int
valueLen
)
{
char
paramName
[
PARAM_NAME_LEN_MAX
]
=
{
0
};
if
(
snprintf_s
(
paramName
,
PARAM_NAME_LEN_MAX
,
PARAM_NAME_LEN_MAX
-
1
,
"init.svc.%s"
,
serviceName
)
==
-
1
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed snprintf_s err=%{public}d"
,
errno
);
return
-
1
;
}
if
(
SystemGetParameter
(
paramName
,
paramValue
,
&
valueLen
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed get paramName."
);
return
-
1
;
}
return
0
;
}
static
int
RestartDynamicProcess
(
const
char
*
serviceName
,
const
char
*
extArgv
[],
int
extArgc
)
{
if
(
serviceName
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Restart dynamic service failed, service is null.
\n
"
);
return
-
1
;
}
char
paramValue
[
PARAM_VALUE_LEN_MAX
]
=
{
0
};
unsigned
int
valueLen
=
PARAM_VALUE_LEN_MAX
;
if
(
GetCurrentServiceStatus
(
serviceName
,
paramValue
,
valueLen
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Get service status failed.
\n
"
);
return
-
1
;
}
if
(
strcmp
(
paramValue
,
"running"
)
==
0
)
{
if
(
StopDynamicProcess
(
serviceName
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Stop service %{public}s failed"
,
serviceName
);
return
-
1
;
}
if
(
ServiceWaitForStatus
(
serviceName
,
"stopped"
,
DEFAULT_PARAM_WAIT_TIMEOUT
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed wait service %{public}s stopped"
,
serviceName
);
return
-
1
;
}
if
(
StartDynamicProcess
(
serviceName
,
extArgv
,
extArgc
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Start service %{public}s failed"
,
serviceName
);
return
-
1
;
}
}
else
if
(
strcmp
(
paramValue
,
"stopped"
)
==
0
)
{
if
(
StartDynamicProcess
(
serviceName
,
extArgv
,
extArgc
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Start service %{public}s failed"
,
serviceName
);
return
-
1
;
}
}
else
{
HILOG_ERROR
(
LOG_CORE
,
"Current service status: %{public}s is not support."
,
paramValue
);
}
return
0
;
}
int
ServiceControlWithExtra
(
const
char
*
serviceName
,
int
action
,
const
char
*
extArgv
[],
int
extArgc
)
{
if
(
serviceName
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Service wait failed, service is null.
\n
"
);
return
-
1
;
}
int
ret
=
0
;
switch
(
action
)
{
case
START
:
ret
=
StartDynamicProcess
(
serviceName
,
extArgv
,
extArgc
);
break
;
case
STOP
:
ret
=
StopDynamicProcess
(
serviceName
);
break
;
case
RESTART
:
ret
=
RestartDynamicProcess
(
serviceName
,
extArgv
,
extArgc
);
break
;
default:
HILOG_ERROR
(
LOG_CORE
,
"Set service %{public}s action %d error"
,
serviceName
,
action
);
ret
=
-
1
;
break
;
}
return
ret
;
}
int
ServiceControl
(
const
char
*
serviceName
,
int
action
)
{
if
(
serviceName
==
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Service getctl failed, service is null.
\n
"
);
return
-
1
;
}
int
ret
=
ServiceControlWithExtra
(
serviceName
,
action
,
NULL
,
0
);
return
ret
;
}
// Service status can set "running", "stopping", "stopped", "reseting". waitTimeout(s).
int
ServiceWaitForStatus
(
const
char
*
serviceName
,
const
char
*
status
,
int
waitTimeout
)
{
if
(
serviceName
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Service wait failed, service is null.
\n
"
);
return
-
1
;
}
char
paramName
[
PARAM_NAME_LEN_MAX
]
=
{
0
};
if
(
snprintf_s
(
paramName
,
PARAM_NAME_LEN_MAX
,
PARAM_NAME_LEN_MAX
-
1
,
"init.svc.%s"
,
serviceName
)
==
-
1
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed snprintf_s err=%{public}d"
,
errno
);
return
-
1
;
}
if
(
SystemWaitParameter
(
paramName
,
status
,
waitTimeout
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Wait param for %{public}s failed.
\n
"
,
paramName
);
return
-
1
;
}
HILOG_INFO
(
LOG_CORE
,
"Success wait"
);
return
0
;
}
interfaces/innerkits/
dynamic_service/dynamic_service
.c
→
interfaces/innerkits/
begetctl_watcher/begetctl_watcher
.c
浏览文件 @
3275e747
...
...
@@ -12,41 +12,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "begetctl_watcher.h"
#include "dynamic_service.h"
#include <errno.h>
#include <stdio.h>
#include
"hilog/log.h"
#include
"parameter.h"
#include
<stdlib.h>
#include
<string.h>
#undef LOG_TAG
#undef LOG_DOMAIN
#define LOG_TAG "Init"
#define LOG_DOMAIN 0xD000719
#include "hilog/log.h"
#include "init_utils.h"
#include "securec.h"
int
32_t
StartDynamicProcess
(
const
char
*
name
)
int
ServiceWatchForStatus
(
const
char
*
serviceName
,
void
*
context
,
ServiceStatusChangePtr
changeCallback
)
{
if
(
name
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Start dynamic service failed, service name is null."
);
return
-
1
;
}
if
(
SetParameter
(
"ohos.ctl.start"
,
name
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"Set param for %{public}s failed.
\n
"
,
name
);
if
(
serviceName
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Service wait failed, service is null.
\n
"
);
return
-
1
;
}
return
0
;
}
int32_t
StopDynamicProcess
(
const
char
*
name
)
{
if
(
name
==
NULL
)
{
HILOG_ERROR
(
LOG_CORE
,
"Stop dynamic service failed, service is null.
\n
"
);
char
paramName
[
PARAM_NAME_LEN_MAX
]
=
{
0
};
if
(
snprintf_s
(
paramName
,
PARAM_NAME_LEN_MAX
,
PARAM_NAME_LEN_MAX
-
1
,
"init.svc.%s"
,
serviceName
)
==
-
1
)
{
HILOG_ERROR
(
LOG_CORE
,
"Failed snprintf_s err=%{public}d"
,
errno
);
return
-
1
;
}
if
(
S
etParameter
(
"ohos.ctl.stop"
,
name
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"
Set param for %{public}s failed.
\n
"
,
n
ame
);
if
(
S
ystemWatchParameter
(
paramName
,
changeCallback
,
context
)
!=
0
)
{
HILOG_ERROR
(
LOG_CORE
,
"
Wait param for %{public}s failed.
\n
"
,
paramN
ame
);
return
-
1
;
}
return
0
;
}
interfaces/innerkits/include/begetctl_utils.h
0 → 100755
浏览文件 @
3275e747
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* 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.
*/
#ifndef BEGET_UTILS_API_H
#define BEGET_UTILS_API_H
#include <inttypes.h>
#ifdef __cplusplus
#if __cplusplus
extern
"C"
{
#endif
#endif
enum
ServiceAction
{
START
=
0
,
STOP
=
1
,
RESTART
=
2
,
};
int
ServiceControlWithExtra
(
const
char
*
serviceName
,
int
action
,
const
char
*
extArgv
[],
int
extArgc
);
int
ServiceControl
(
const
char
*
serviceName
,
int
action
);
int
ServiceWaitForStatus
(
const
char
*
serviceName
,
const
char
*
status
,
int
waitTimeout
);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif
interfaces/innerkits/include/
dynamic_service
.h
→
interfaces/innerkits/include/
begetctl_watcher
.h
浏览文件 @
3275e747
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* 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.
*/
#ifndef DYNAMIC_SERVICE_API_H
#define DYNAMIC_SERVICE_API_H
#include <inttypes.h>
#ifdef __cplusplus
#if __cplusplus
extern
"C"
{
#endif
#endif
int32_t
StartDynamicProcess
(
const
char
*
name
);
int32_t
StopDynamicProcess
(
const
char
*
name
);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // DYNAMIC_SERVICE_API_H
\ No newline at end of file
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* 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.
*/
#ifndef SERVICE_WATCH_API_H
#define SERVICE_WATCH_API_H
#include "sys_param.h"
#ifdef __cplusplus
#if __cplusplus
extern
"C"
{
#endif
#endif
typedef
ParameterChangePtr
ServiceStatusChangePtr
;
int
ServiceWatchForStatus
(
const
char
*
serviceName
,
void
*
context
,
ServiceStatusChangePtr
changeCallback
);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif
ohos.build
浏览文件 @
3275e747
...
...
@@ -8,22 +8,29 @@
"//base/startup/init_lite/watchdog:watchdog",
"//base/startup/init_lite/services/begetctl:begetctl",
"//base/startup/init_lite/interfaces/innerkits:libbegetutil",
"//base/startup/init_lite/interfaces/innerkits/file:libfile",
"//base/startup/init_lite/interfaces/innerkits/socket:libsocket"
"//base/startup/init_lite/interfaces/innerkits:libbegetctl_watcher"
],
"inner_kits": [{
"header": {
"header_files": [
"init_socket.h",
"dynamic_service.h",
"init_file.h",
"fs_manager/fs_manager_log.h",
"fs_manager/fs_manager.h",
"init_reboot.h"
"init_reboot.h",
"begetctl_utils.h"
],
"header_base": "//base/startup/init_lite/interfaces/innerkits/include/"
},
"name": "//base/startup/init_lite/interfaces/innerkits:libbegetutil"
}, {
"header": {
"header_files": [
"begetctl_watcher.h"
],
"header_base": "//base/startup/init_lite/interfaces/innerkits/include/"
},
"name": "//base/startup/init_lite/interfaces/innerkits:libbegetctl_watcher"
}
],
"test_list": [
...
...
services/init/include/init_service.h
浏览文件 @
3275e747
...
...
@@ -83,6 +83,7 @@ typedef struct {
int
importance
;
Perms
servPerm
;
ServiceArgs
pathArgs
;
ServiceArgs
extraArgs
;
ServiceArgs
writePidArgs
;
CmdLines
*
restartArg
;
ServiceSocket
*
socketCfg
;
...
...
@@ -98,7 +99,7 @@ void NotifyServiceChange(const char *serviceName, const char *change);
int
IsForbidden
(
const
char
*
fieldStr
);
int
SetImportantValue
(
Service
*
curServ
,
const
char
*
attrName
,
int
value
,
int
flag
);
int
GetServiceCaps
(
const
cJSON
*
curArrItem
,
Service
*
curServ
);
int
ServiceExec
(
const
Service
*
service
);
int
ServiceExec
(
Service
*
service
);
#ifdef __cplusplus
#if __cplusplus
...
...
services/init/init_service_manager.c
浏览文件 @
3275e747
...
...
@@ -617,6 +617,7 @@ void StartServiceByName(const char *servName, bool checkDynamic)
if
(
checkDynamic
&&
(
service
->
attribute
&
SERVICE_ATTR_DYNAMIC
))
{
INIT_LOGI
(
"%s is dynamic service."
,
servName
);
NotifyServiceChange
(
servName
,
"stopped"
);
return
;
}
if
(
ServiceStart
(
service
)
!=
SERVICE_SUCCESS
)
{
...
...
@@ -663,6 +664,25 @@ Service *GetServiceByPid(pid_t pid)
return
NULL
;
}
static
int
SetServiceExtraArgs
(
Service
*
service
,
const
char
*
fullServName
)
{
INIT_ERROR_CHECK
(
service
!=
NULL
&&
fullServName
!=
NULL
,
return
-
1
,
"Failed get parameters"
);
int
returnCount
=
0
;
char
*
tmpServName
=
strdup
(
fullServName
);
char
**
extArgv
=
SplitStringExt
(
tmpServName
,
"|"
,
&
returnCount
,
MAX_PATH_ARGS_CNT
);
free
(
tmpServName
);
INIT_ERROR_CHECK
(
extArgv
!=
NULL
&&
returnCount
>
0
,
return
-
1
,
"Split servName: %s failed"
,
fullServName
);
service
->
extraArgs
.
count
=
returnCount
-
1
;
service
->
extraArgs
.
argv
=
(
char
**
)
calloc
(
service
->
extraArgs
.
count
,
sizeof
(
char
*
));
INIT_ERROR_CHECK
(
service
->
extraArgs
.
argv
!=
NULL
,
FreeStringVector
(
extArgv
,
returnCount
);
return
-
1
,
"Failed calloc err=%d"
,
errno
);
for
(
int
i
=
0
;
i
<
service
->
extraArgs
.
count
;
i
++
)
{
service
->
extraArgs
.
argv
[
i
]
=
strdup
(
extArgv
[
i
+
1
]);
}
FreeStringVector
(
extArgv
,
returnCount
);
return
0
;
}
Service
*
GetServiceByName
(
const
char
*
servName
)
{
INIT_ERROR_CHECK
(
servName
!=
NULL
,
return
NULL
,
"Failed get servName"
);
...
...
@@ -670,7 +690,12 @@ Service *GetServiceByName(const char *servName)
while
(
node
!=
&
g_serviceSpace
.
services
)
{
Service
*
service
=
ListEntry
(
node
,
Service
,
node
);
if
(
service
!=
NULL
)
{
INIT_CHECK_RETURN_VALUE
(
strcmp
(
service
->
name
,
servName
)
!=
0
,
service
);
if
(
strcmp
(
service
->
name
,
servName
)
==
0
)
{
return
service
;
}
else
if
(
strncmp
(
service
->
name
,
servName
,
strlen
(
service
->
name
))
==
0
)
{
SetServiceExtraArgs
(
service
,
servName
);
return
service
;
}
}
node
=
node
->
next
;
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录