mirror of
https://github.com/openharmony/ability_ability_runtime.git
synced 2026-08-24 22:21:36 -04:00
!19167 merge master0501 into master
hassubcommand 与sdk保持一致 Created-by: dsz2025 Commit-by: duansizhao Merged-by: openharmony_ci Description: **IssueNo**:https://gitcode.com/openharmony/build/issues/4508 **Description**: **稳定性自检:** | 自检项 | 自检结果 | | ------------------------------------------------------------ | -------- | | 涉及跨进程调用的相关操作需要抛至主线程或加锁防止并发 | | | 成员变量进行赋值或创建需要排查并发 | | | 谨慎在lambda表达式中使用引用捕获 | | | 谨慎在未经拷贝的情况下使用外部传入的string、C字符串 | | | map\vector\list\set等stl模板类使用时需要排查并发 | | | 谨慎考虑加锁范围 | | | 在IPC通信中谨慎使用同步通信方式 | | | 禁止传递this指针至其他模块或线程(特别是eventhandler任务) | | | 禁止将外部传入的裸指针在内部直接构造智能指针 | | | 禁止多个独立创建的智能指针管理同一地址 | | | 禁止在析构函数中抛异步任务 | | | 禁止js对象在非js线程(例如在IPC线程)创建、使用或销毁 | | | 禁止在对外接口中未经判空直接使用外部传入的指针 | | | 禁止接口返回局部变量引用 | | | 禁止在信号函数中加锁 | | | 禁止在关键流程(SA启动、应用启动等主流程)执行耗时的操作 | | | 禁止将同一个cpp编译在不同的so中 | | **安全编码自检:** | 自检项 | 自检结果 | | -------------------------------------------------------------- | -------- | | 裸指针避免通过隐式转换构造为sptr | | | json对象在取值之前必须先判断类型,避免类型不匹配 | | | 序列化时必须对传入的数组大小进行校验,避免出现超大数组 | | | 避免使用未明确位宽的整型,选择使用int8_t、uint8_t等类型 | | | 外部传入的路径要做规范化校验,对路径中的.、..、../等特殊字符严格校验 | | | 指针变量、表示资源描述符的变量、bool变量必须赋初值 | | | readParcelable获取的对象使用前需要判空 | | | 分配和释放内存的函数需要成对出现 | | | 申请内存后异常退出前需要及时进行内存释放 | | | 内存申请前必须对内存大小进行合法性校验 | | | 内存分配后必须判断是否成功 | | | 禁止使用realloc、alloca函数 | | | 禁止打印文件路径、口令等敏感信息,如有需要,使用private修饰 | | | 禁止打印内存地址 | | | 整数之间运算时必须严格检查,确保不会出现溢出、反转、除0 | | | 禁止对有符号整数进行位操作符运算 | | | 禁止对指针进行逻辑或位运算 | | | 循环次数如果收外部数据控制,需要检验其合法性 | | | 禁止使用内存操作类危险函数,需要使用安全函数 | | | 谨慎使用不可重入函数 | | | 必须检查安全函数的返回值,并进行正确处理 | | | 禁止仅通过TokenType类型判断绕过权限校验 | | **TDD Result**: **XTS Result**: ### 是否已执行L0用例 - [ ] 已验证 - [ ] 不涉及。如不涉及,请写明理由 See merge request: openharmony/ability_ability_runtime!19167
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"description": "Example CLI tool, demonstrates CLI tool specification implementation (with subcommand format)",
|
||||
"executablePath": "/system/bin/cli_tool/executable/ohos-example",
|
||||
"hasSubcommands": true,
|
||||
"hasSubCommand": true,
|
||||
"requirePermissions": [],
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
@@ -56,7 +56,7 @@
|
||||
"description": "Current status"
|
||||
}
|
||||
},
|
||||
"required": ["percentage", "status"]
|
||||
"required": ["type", "percentage", "status"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -65,12 +65,7 @@
|
||||
"requirePermissions": [],
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reserved": {
|
||||
"type": "string",
|
||||
"description": "Reserved placeholder parameter for future use, not required for normal calls"
|
||||
}
|
||||
}
|
||||
"properties": {}
|
||||
},
|
||||
"outputSchema": {
|
||||
"type": "object",
|
||||
|
||||
+131
-84
@@ -14,120 +14,167 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
// Constants for magic numbers
|
||||
namespace {
|
||||
constexpr int PROGRESS_MAX = 100;
|
||||
constexpr int MIN_ARGC = 2;
|
||||
constexpr int PROGRESS_MAX = 100;
|
||||
constexpr int MIN_ARGC = 2;
|
||||
constexpr const char* VERSION = "1.0.0";
|
||||
constexpr const char* BUILD_TIME = "2026-04-04 00:00:00";
|
||||
}
|
||||
|
||||
std::string EscapeJson(const std::string& input)
|
||||
{
|
||||
std::string escaped;
|
||||
escaped.reserve(input.size());
|
||||
for (char ch : input) {
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
escaped += "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
escaped += "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
escaped += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
escaped += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
escaped += "\\t";
|
||||
break;
|
||||
default:
|
||||
escaped += ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
void EmitProgress(int percentage, const std::string& status)
|
||||
{
|
||||
std::cout << "{\"type\": \"progress\", "
|
||||
<< "\"percentage\": " << percentage << ", "
|
||||
<< "\"status\": \"" << status << "\""
|
||||
<< "}" << std::endl;
|
||||
std::cout << "{\"type\":\"progress\",\"percentage\":" << percentage
|
||||
<< ",\"status\":\"" << EscapeJson(status) << "\"}" << std::endl;
|
||||
}
|
||||
|
||||
void EmitRunResult(const std::string& result)
|
||||
void EmitSuccessResult(const std::string& dataJson)
|
||||
{
|
||||
std::cout << "{\"type\": \"result\", "
|
||||
<< "\"status\": \"success\", "
|
||||
<< "\"data\": {"
|
||||
<< "\"result\": \"" << result << "\""
|
||||
<< "}}"
|
||||
<< "}" << std::endl;
|
||||
}
|
||||
|
||||
void EmitVersionResult(const std::string& version, const std::string& buildTime)
|
||||
{
|
||||
std::cout << "{\"type\": \"result\", "
|
||||
<< "\"status\": \"success\", "
|
||||
<< "\"data\": {"
|
||||
<< "\"version\": \"" << version << "\", "
|
||||
<< "\"build_time\": \"" << buildTime << "\""
|
||||
<< "}}"
|
||||
<< "}" << std::endl;
|
||||
std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":"
|
||||
<< dataJson << "}" << std::endl;
|
||||
}
|
||||
|
||||
void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion)
|
||||
{
|
||||
std::cout << "{\"type\": \"result\", "
|
||||
<< "\"status\": \"failed\", "
|
||||
<< "\"errCode\": \"" << errCode << "\", "
|
||||
<< "\"errMsg\": \"" << errMsg << "\", "
|
||||
<< "\"suggestion\": \"" << suggestion << "\""
|
||||
<< "}" << std::endl;
|
||||
}
|
||||
|
||||
int RunCommand(const std::vector<std::string>& args)
|
||||
{
|
||||
EmitProgress(0, "starting");
|
||||
|
||||
std::string result = "执行完成";
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
if (i > 0 || !result.empty()) {
|
||||
result += " ";
|
||||
}
|
||||
result += args[i];
|
||||
|
||||
int progress = static_cast<int>((i + 1) * PROGRESS_MAX / (args.size() + 1));
|
||||
EmitProgress(progress, "running");
|
||||
}
|
||||
|
||||
EmitProgress(PROGRESS_MAX, "completed");
|
||||
EmitRunResult(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VersionCommand()
|
||||
{
|
||||
std::string version = "1.0.0";
|
||||
|
||||
const char* buildTime = "2026-04-04 00:00:00";
|
||||
|
||||
EmitVersionResult(version, buildTime);
|
||||
|
||||
return 0;
|
||||
std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\""
|
||||
<< EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg)
|
||||
<< "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl;
|
||||
}
|
||||
|
||||
void ShowHelp()
|
||||
{
|
||||
std::cout << "Usage: ohos-example <subcommand> [args]" << std::endl;
|
||||
std::cout << "Usage: ohos-example <subcommand> [options]" << std::endl;
|
||||
std::cout << "Subcommands:" << std::endl;
|
||||
std::cout << " run [args...] Run the tool with arguments" << std::endl;
|
||||
std::cout << " version Show version information" << std::endl;
|
||||
std::cout << " help Show this help message" << std::endl;
|
||||
std::cout << " run --argLine <value> Run the tool with a single string argument" << std::endl;
|
||||
std::cout << " version Show version information" << std::endl;
|
||||
std::cout << " help Show this help message" << std::endl;
|
||||
}
|
||||
|
||||
void ShowRunHelp()
|
||||
{
|
||||
std::cout << "Usage: ohos-example run [options]" << std::endl;
|
||||
std::cout << "Options:" << std::endl;
|
||||
std::cout << " --argLine <value> Argument string to pass to the tool" << std::endl;
|
||||
std::cout << " --help, -h Show this help message" << std::endl;
|
||||
}
|
||||
|
||||
void ShowVersionHelp()
|
||||
{
|
||||
std::cout << "Usage: ohos-example version [options]" << std::endl;
|
||||
std::cout << "Options:" << std::endl;
|
||||
std::cout << " --help, -h Show this help message" << std::endl;
|
||||
}
|
||||
|
||||
int RunCommand(int argc, char* argv[])
|
||||
{
|
||||
std::string argLine;
|
||||
int i = 2;
|
||||
while (i < argc) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "--help" || arg == "-h") {
|
||||
ShowRunHelp();
|
||||
return 0;
|
||||
}
|
||||
if (arg == "--argLine") {
|
||||
if (i + 1 >= argc) {
|
||||
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'argLine'.",
|
||||
"Use: ohos-example run --argLine <value>");
|
||||
return 1;
|
||||
}
|
||||
++i;
|
||||
argLine = argv[i];
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "' for subcommand 'run'.",
|
||||
"Use: ohos-example run --argLine <value>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argLine.empty()) {
|
||||
EmitError("ERR_MISSING_PARAM", "Missing required parameter 'argLine'.",
|
||||
"Use: ohos-example run --argLine <value>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
EmitProgress(0, "starting");
|
||||
EmitProgress(50, "running");
|
||||
EmitProgress(PROGRESS_MAX, "completed");
|
||||
EmitSuccessResult("{\"result\":\"执行完成 " + EscapeJson(argLine) + "\"}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VersionCommand(int argc, char* argv[])
|
||||
{
|
||||
if (argc == 3) {
|
||||
std::string arg = argv[2];
|
||||
if (arg == "--help" || arg == "-h") {
|
||||
ShowVersionHelp();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (argc != 2) {
|
||||
EmitError("ERR_UNKNOWN_PARAM", "Subcommand 'version' does not accept extra parameters.",
|
||||
"Use: ohos-example version");
|
||||
return 1;
|
||||
}
|
||||
|
||||
EmitSuccessResult("{\"version\":\"" + std::string(VERSION) + "\",\"build_time\":\"" +
|
||||
std::string(BUILD_TIME) + "\"}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < MIN_ARGC) {
|
||||
ShowHelp();
|
||||
EmitError("ERR_MISSING_PARAM", "Missing required subcommand.",
|
||||
"Use one of: ohos-example run --argLine <value>, ohos-example version");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string subcommand = argv[1];
|
||||
|
||||
if (subcommand == "run") {
|
||||
std::vector<std::string> args;
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
return RunCommand(args);
|
||||
} else if (subcommand == "version") {
|
||||
return VersionCommand();
|
||||
} else if (subcommand == "help" || subcommand == "--help" || subcommand == "-h") {
|
||||
return RunCommand(argc, argv);
|
||||
}
|
||||
if (subcommand == "version") {
|
||||
return VersionCommand(argc, argv);
|
||||
}
|
||||
if (subcommand == "help" || subcommand == "--help" || subcommand == "-h") {
|
||||
ShowHelp();
|
||||
return 0;
|
||||
} else {
|
||||
ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
EmitError("ERR_INVALID_PARAM", "Unknown subcommand '" + subcommand + "'.",
|
||||
"Use one of: run, version, help");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,6 @@
|
||||
"type": "object",
|
||||
"description": "Tool execution result",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "Execution status, success when completed successfully"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Processed output message"
|
||||
@@ -45,6 +41,6 @@
|
||||
"description": "Actual repeat count"
|
||||
}
|
||||
},
|
||||
"required": ["status", "message", "repeat_count"]
|
||||
"required": ["message", "repeat_count"]
|
||||
}
|
||||
}
|
||||
|
||||
+122
-56
@@ -11,76 +11,136 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
|
||||
// Constants for magic numbers
|
||||
namespace {
|
||||
constexpr int MESSAGE_PREFIX_LEN = 10;
|
||||
constexpr int COUNT_PREFIX_LEN = 8;
|
||||
constexpr int MAX_COUNT = 10;
|
||||
constexpr int MIN_COUNT = 1;
|
||||
constexpr int MAX_COUNT = 10;
|
||||
constexpr int MIN_COUNT = 1;
|
||||
}
|
||||
|
||||
void EmitResult(const std::string& status, const std::string& message, int repeatCount)
|
||||
struct SimpleConfig {
|
||||
std::string message = "Hello from ohos-simple";
|
||||
int count = 1;
|
||||
bool verbose = false;
|
||||
};
|
||||
|
||||
std::string EscapeJson(const std::string& input)
|
||||
{
|
||||
std::cout << "{\"type\": \"result\", "
|
||||
<< "\"status\": \"" << status << "\", "
|
||||
<< "\"data\": {"
|
||||
<< "\"status\": \"" << status << "\", "
|
||||
<< "\"message\": \"" << message << "\", "
|
||||
<< "\"repeat_count\": " << repeatCount
|
||||
<< "}}"
|
||||
<< "}" << std::endl;
|
||||
std::string escaped;
|
||||
escaped.reserve(input.size());
|
||||
for (char ch : input) {
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
escaped += "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
escaped += "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
escaped += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
escaped += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
escaped += "\\t";
|
||||
break;
|
||||
default:
|
||||
escaped += ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
void EmitSuccessResult(const std::string& dataJson)
|
||||
{
|
||||
std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":"
|
||||
<< dataJson << "}" << std::endl;
|
||||
}
|
||||
|
||||
void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion)
|
||||
{
|
||||
std::cout << "{\"type\": \"result\", "
|
||||
<< "\"status\": \"failed\", "
|
||||
<< "\"errCode\": \"" << errCode << "\", "
|
||||
<< "\"errMsg\": \"" << errMsg << "\", "
|
||||
<< "\"suggestion\": \"" << suggestion << "\""
|
||||
<< "}" << std::endl;
|
||||
std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\""
|
||||
<< EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg)
|
||||
<< "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl;
|
||||
}
|
||||
|
||||
void ShowHelp()
|
||||
{
|
||||
std::cout << "Usage: ohos-simple [options]" << std::endl;
|
||||
std::cout << "Options:" << std::endl;
|
||||
std::cout << " --message=<msg> Set message to display (default: 'Hello from ohos-simple')" << std::endl;
|
||||
std::cout << " --count=<num> Number of repetitions (1-10, default: 1)" << std::endl;
|
||||
std::cout << " --message <msg> Set message to display (default: 'Hello from ohos-simple')" << std::endl;
|
||||
std::cout << " --count <num> Number of repetitions (1-10, default: 1)" << std::endl;
|
||||
std::cout << " --verbose Enable verbose output" << std::endl;
|
||||
std::cout << " --help, -h Show this help message" << std::endl;
|
||||
}
|
||||
|
||||
bool ParseArguments(int argc, char* argv[], std::string& message, int& count, bool& verbose)
|
||||
bool ParseInteger(const std::string& value, int& result)
|
||||
{
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg.find("--message=") == 0) {
|
||||
message = arg.substr(MESSAGE_PREFIX_LEN);
|
||||
} else if (arg.find("--count=") == 0) {
|
||||
count = std::atoi(arg.substr(COUNT_PREFIX_LEN).c_str());
|
||||
if (count < MIN_COUNT) {
|
||||
count = MIN_COUNT;
|
||||
}
|
||||
if (count > MAX_COUNT) {
|
||||
count = MAX_COUNT;
|
||||
}
|
||||
} else if (arg == "--verbose") {
|
||||
verbose = true;
|
||||
} else if (arg == "--help" || arg == "-h") {
|
||||
ShowHelp();
|
||||
return false;
|
||||
}
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
long parsed = std::strtol(value.c_str(), &end, 10);
|
||||
if (errno != 0 || end == value.c_str() || *end != '\0' || parsed < INT_MIN || parsed > INT_MAX) {
|
||||
return false;
|
||||
}
|
||||
result = static_cast<int>(parsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ExecuteTask(const std::string& message, int count, bool verbose)
|
||||
int ParseArguments(int argc, char* argv[], SimpleConfig& config)
|
||||
{
|
||||
int i = 1;
|
||||
while (i < argc) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "--message") {
|
||||
if (i + 1 >= argc) {
|
||||
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'message'.",
|
||||
"Use: ohos-simple --message <msg> [--count <num>] [--verbose]");
|
||||
return 1;
|
||||
}
|
||||
++i;
|
||||
config.message = argv[i];
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--count") {
|
||||
if (i + 1 >= argc) {
|
||||
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'count'.",
|
||||
"Use: ohos-simple --count <num> [--message <msg>] [--verbose]");
|
||||
return 1;
|
||||
}
|
||||
++i;
|
||||
if (!ParseInteger(argv[i], config.count)) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'count' must be an integer.",
|
||||
"Use an integer between 1 and 10, for example: --count 2");
|
||||
return 1;
|
||||
}
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--verbose") {
|
||||
config.verbose = true;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--help" || arg == "-h") {
|
||||
ShowHelp();
|
||||
return 2;
|
||||
}
|
||||
|
||||
EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "'.",
|
||||
"Supported parameters are: --message, --count, --verbose");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string ExecuteTask(const std::string& message, int count)
|
||||
{
|
||||
std::string result;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@@ -89,27 +149,33 @@ std::string ExecuteTask(const std::string& message, int count, bool verbose)
|
||||
}
|
||||
result += message;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::string message = "Hello from ohos-simple";
|
||||
int count = 1;
|
||||
bool verbose = false;
|
||||
|
||||
if (!ParseArguments(argc, argv, message, count, verbose)) {
|
||||
return 0;
|
||||
SimpleConfig config;
|
||||
int parseResult = ParseArguments(argc, argv, config);
|
||||
if (parseResult != 0) {
|
||||
return parseResult == 2 ? 0 : 1;
|
||||
}
|
||||
|
||||
if (message.empty()) {
|
||||
if (config.message.empty()) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'message' must not be empty.",
|
||||
"Provide a non-empty string, for example: --message hello");
|
||||
return 1;
|
||||
}
|
||||
if (config.count < MIN_COUNT || config.count > MAX_COUNT) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'count' must be between 1 and 10.",
|
||||
"Use an integer between 1 and 10, for example: --count 2");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string result = ExecuteTask(message, count, verbose);
|
||||
|
||||
EmitResult("success", result, count);
|
||||
|
||||
std::string result = ExecuteTask(config.message, config.count);
|
||||
if (config.verbose) {
|
||||
result = "[verbose] " + result;
|
||||
}
|
||||
EmitSuccessResult("{\"message\":\"" + EscapeJson(result) + "\",\"repeat_count\":" +
|
||||
std::to_string(config.count) + "}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -37,10 +37,6 @@
|
||||
"type": "object",
|
||||
"description": "Tool output result",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "Execution status"
|
||||
},
|
||||
"duration": {
|
||||
"type": "integer",
|
||||
"description": "Planned duration (seconds)"
|
||||
@@ -50,7 +46,7 @@
|
||||
"description": "Actual duration (seconds)"
|
||||
}
|
||||
},
|
||||
"required": ["status", "duration", "actual_duration"]
|
||||
"required": ["duration", "actual_duration"]
|
||||
},
|
||||
"eventSchemas": {
|
||||
"progress": {
|
||||
@@ -71,7 +67,7 @@
|
||||
"description": "Current status"
|
||||
}
|
||||
},
|
||||
"required": ["percentage", "status"]
|
||||
"required": ["type", "percentage", "status"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+139
-124
@@ -11,25 +11,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
|
||||
// Constants for magic numbers
|
||||
namespace {
|
||||
constexpr int PROGRESS_PERCENTAGE_MAX = 100;
|
||||
constexpr int DEFAULT_INTERVAL = 1;
|
||||
constexpr int MIN_DURATION = 1;
|
||||
constexpr int MIN_INTERVAL = 1;
|
||||
constexpr int DURATION_PREFIX_LEN = 11;
|
||||
constexpr int INTERVAL_PREFIX_LEN = 11;
|
||||
constexpr int HELP_ARGC = 2;
|
||||
constexpr int ARG_PARSE_START_INDEX = 1;
|
||||
constexpr int PROGRESS_MAX = 100;
|
||||
constexpr int DEFAULT_INTERVAL = 1;
|
||||
constexpr int MIN_DURATION = 1;
|
||||
constexpr int MIN_INTERVAL = 1;
|
||||
}
|
||||
|
||||
struct TimerConfig {
|
||||
@@ -39,119 +33,137 @@ struct TimerConfig {
|
||||
bool verbose = false;
|
||||
};
|
||||
|
||||
void EmitProgress(int percentage, const std::string& status)
|
||||
std::string EscapeJson(const std::string& input)
|
||||
{
|
||||
std::cout << "{\"type\": \"progress\", "
|
||||
<< "\"percentage\": " << percentage << ", "
|
||||
<< "\"status\": \"" << status << "\""
|
||||
<< "}" << std::endl;
|
||||
std::string escaped;
|
||||
escaped.reserve(input.size());
|
||||
for (char ch : input) {
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
escaped += "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
escaped += "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
escaped += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
escaped += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
escaped += "\\t";
|
||||
break;
|
||||
default:
|
||||
escaped += ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
void EmitResult(const std::string& status, int duration, int actualDuration)
|
||||
void EmitProgress(int percentage, const std::string& status)
|
||||
{
|
||||
std::cout << "{\"type\": \"result\", "
|
||||
<< "\"status\": \"" << status << "\", "
|
||||
<< "\"data\": {"
|
||||
<< "\"status\": \"" << status << "\", "
|
||||
<< "\"duration\": " << duration << ", "
|
||||
<< "\"actual_duration\": " << actualDuration
|
||||
<< "}}"
|
||||
<< "}" << std::endl;
|
||||
std::cout << "{\"type\":\"progress\",\"percentage\":" << percentage
|
||||
<< ",\"status\":\"" << EscapeJson(status) << "\"}" << std::endl;
|
||||
}
|
||||
|
||||
void EmitSuccessResult(const std::string& dataJson)
|
||||
{
|
||||
std::cout << "{\"type\":\"result\",\"status\":\"success\",\"data\":"
|
||||
<< dataJson << "}" << std::endl;
|
||||
}
|
||||
|
||||
void EmitError(const std::string& errCode, const std::string& errMsg, const std::string& suggestion)
|
||||
{
|
||||
std::cout << "{\"type\": \"result\", "
|
||||
<< "\"status\": \"failed\", "
|
||||
<< "\"errCode\": \"" << errCode << "\", "
|
||||
<< "\"errMsg\": \"" << errMsg << "\", "
|
||||
<< "\"suggestion\": \"" << suggestion << "\""
|
||||
<< "}" << std::endl;
|
||||
std::cout << "{\"type\":\"result\",\"status\":\"failed\",\"errCode\":\""
|
||||
<< EscapeJson(errCode) << "\",\"errMsg\":\"" << EscapeJson(errMsg)
|
||||
<< "\",\"suggestion\":\"" << EscapeJson(suggestion) << "\"}" << std::endl;
|
||||
}
|
||||
|
||||
void ShowHelp()
|
||||
{
|
||||
std::cout << "Usage: ohos-timer [options]" << std::endl;
|
||||
std::cout << "Options:" << std::endl;
|
||||
std::cout << " --duration=<sec> Duration in seconds (required, minimum 1)" << std::endl;
|
||||
std::cout << " --interval=<sec> Progress update interval in seconds (optional, default 1)" << std::endl;
|
||||
std::cout << " --progress Enable progress events" << std::endl;
|
||||
std::cout << " --verbose Enable verbose output" << std::endl;
|
||||
std::cout << " --help, -h Show this help message" << std::endl;
|
||||
std::cout << " --duration <sec> Duration in seconds (required, minimum 1)" << std::endl;
|
||||
std::cout << " --interval <sec> Progress update interval in seconds (default 1)" << std::endl;
|
||||
std::cout << " --showProgress Enable progress events" << std::endl;
|
||||
std::cout << " --verbose Enable verbose mode" << std::endl;
|
||||
std::cout << " --help, -h Show this help message" << std::endl;
|
||||
}
|
||||
|
||||
bool ParseArguments(int argc, char* argv[], TimerConfig& config)
|
||||
bool ParseInteger(const std::string& value, int& result)
|
||||
{
|
||||
for (int i = ARG_PARSE_START_INDEX; i < argc; ++i) {
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
long parsed = std::strtol(value.c_str(), &end, 10);
|
||||
if (errno != 0 || end == value.c_str() || *end != '\0' || parsed < INT_MIN || parsed > INT_MAX) {
|
||||
return false;
|
||||
}
|
||||
result = static_cast<int>(parsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
int ParseArguments(int argc, char* argv[], TimerConfig& config)
|
||||
{
|
||||
int i = 1;
|
||||
while (i < argc) {
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg.find("--duration=") == 0) {
|
||||
std::string value = arg.substr(DURATION_PREFIX_LEN);
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
long val = std::strtol(value.c_str(), &end, 10);
|
||||
if (errno != 0 || end == value.c_str() || *end != '\0' || val < 0 || val > INT_MAX) {
|
||||
return false;
|
||||
if (arg == "--duration") {
|
||||
if (i + 1 >= argc) {
|
||||
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'duration'.",
|
||||
"Use: ohos-timer --duration <sec> [--interval <sec>] [--showProgress] [--verbose]");
|
||||
return 1;
|
||||
}
|
||||
config.duration = static_cast<int>(val);
|
||||
} else if (arg.find("--interval=") == 0) {
|
||||
std::string value = arg.substr(INTERVAL_PREFIX_LEN);
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
long val = std::strtol(value.c_str(), &end, 10);
|
||||
if (errno != 0 || end == value.c_str() || *end != '\0' || val < 0 || val > INT_MAX) {
|
||||
return false;
|
||||
++i;
|
||||
if (!ParseInteger(argv[i], config.duration)) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'duration' must be an integer.",
|
||||
"Use a positive integer, for example: --duration 5");
|
||||
return 1;
|
||||
}
|
||||
config.interval = static_cast<int>(val);
|
||||
} else if (arg == "--progress") {
|
||||
config.showProgress = true;
|
||||
} else if (arg == "--verbose") {
|
||||
config.verbose = true;
|
||||
} else if (arg == "--help" || arg == "-h") {
|
||||
ShowHelp();
|
||||
return false;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--interval") {
|
||||
if (i + 1 >= argc) {
|
||||
EmitError("ERR_MISSING_PARAM", "Missing value for parameter 'interval'.",
|
||||
"Use: ohos-timer --interval <sec> [--duration <sec>] [--showProgress] [--verbose]");
|
||||
return 1;
|
||||
}
|
||||
++i;
|
||||
if (!ParseInteger(argv[i], config.interval)) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must be an integer.",
|
||||
"Use a positive integer, for example: --interval 1");
|
||||
return 1;
|
||||
}
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--showProgress") {
|
||||
config.showProgress = true;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--verbose") {
|
||||
config.verbose = true;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--help" || arg == "-h") {
|
||||
ShowHelp();
|
||||
return 2;
|
||||
}
|
||||
|
||||
EmitError("ERR_UNKNOWN_PARAM", "Unknown parameter '" + arg + "'.",
|
||||
"Supported parameters are: --duration, --interval, --showProgress, --verbose");
|
||||
return 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateArguments(const TimerConfig& config)
|
||||
{
|
||||
if (config.duration < MIN_DURATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.interval < MIN_INTERVAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.interval > config.duration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateProgress(int elapsed, int duration, int& lastPercentage)
|
||||
{
|
||||
if (duration == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int percentage = static_cast<int>(
|
||||
(elapsed * PROGRESS_PERCENTAGE_MAX) / duration
|
||||
);
|
||||
|
||||
if (percentage > lastPercentage && percentage > 0) {
|
||||
EmitProgress(percentage, "running");
|
||||
lastPercentage = percentage;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExecuteTimer(const TimerConfig& config)
|
||||
{
|
||||
auto startTime = std::chrono::steady_clock::now();
|
||||
int lastPercentage = -1;
|
||||
int elapsed = 0;
|
||||
|
||||
if (config.showProgress) {
|
||||
@@ -159,46 +171,49 @@ int ExecuteTimer(const TimerConfig& config)
|
||||
}
|
||||
|
||||
while (elapsed < config.duration) {
|
||||
if (config.showProgress) {
|
||||
UpdateProgress(elapsed, config.duration, lastPercentage);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(config.interval));
|
||||
|
||||
elapsed = static_cast<int>(
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::steady_clock::now() - startTime
|
||||
).count()
|
||||
);
|
||||
elapsed = static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::steady_clock::now() - startTime).count());
|
||||
if (config.showProgress && elapsed < config.duration) {
|
||||
int percentage = elapsed * PROGRESS_MAX / config.duration;
|
||||
EmitProgress(percentage, config.verbose ? "running (verbose)" : "running");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.showProgress) {
|
||||
EmitProgress(PROGRESS_PERCENTAGE_MAX, "completed");
|
||||
EmitProgress(PROGRESS_MAX, "completed");
|
||||
}
|
||||
EmitResult("success", config.duration, elapsed);
|
||||
|
||||
EmitSuccessResult("{\"duration\":" + std::to_string(config.duration) +
|
||||
",\"actual_duration\":" + std::to_string(elapsed) + "}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TimerConfig config;
|
||||
|
||||
if (argc < HELP_ARGC) {
|
||||
ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ParseArguments(argc, argv, config)) {
|
||||
return 1;
|
||||
int parseResult = ParseArguments(argc, argv, config);
|
||||
if (parseResult != 0) {
|
||||
return parseResult == 2 ? 0 : 1;
|
||||
}
|
||||
|
||||
if (config.duration == 0) {
|
||||
ShowHelp();
|
||||
EmitError("ERR_MISSING_PARAM", "Missing required parameter 'duration'.",
|
||||
"Use: ohos-timer --duration <sec> [--interval <sec>] [--showProgress] [--verbose]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ValidateArguments(config)) {
|
||||
if (config.duration < MIN_DURATION) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'duration' must be greater than or equal to 1.",
|
||||
"Use a positive integer, for example: --duration 5");
|
||||
return 1;
|
||||
}
|
||||
if (config.interval < MIN_INTERVAL) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must be greater than or equal to 1.",
|
||||
"Use a positive integer, for example: --interval 1");
|
||||
return 1;
|
||||
}
|
||||
if (config.interval > config.duration) {
|
||||
EmitError("ERR_INVALID_PARAM", "Parameter 'interval' must not be greater than 'duration'.",
|
||||
"Use values such as: --duration 5 --interval 1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user