1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <unistd.h>
17#include <stdlib.h>
18#include <stdio.h>
19#include <sys/wait.h>
20
21#include "hilog/log.h"
22#include "appspawn_utils.h"
23#include "securec.h"
24#include "parameter.h"
25
26#include "hnp_api.h"
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32#define HNPAPI_LOG(fmt, ...) \
33    HILOG_INFO(LOG_CORE, "[%{public}s:%{public}d]" fmt, (__FILE_NAME__), (__LINE__), ##__VA_ARGS__)
34#define MAX_ARGV_NUM 256
35#define MAX_ENV_NUM (128 + 2)
36#define IS_OPTION_SET(x, option) ((x) & (1 << (option)))
37#define BUFFER_SIZE 1024
38#define CMD_API_TEXT_LEN 50
39#define PARAM_BUFFER_SIZE 10
40
41/* 数字索引 */
42enum {
43    HNP_INDEX_0 = 0,
44    HNP_INDEX_1,
45    HNP_INDEX_2,
46    HNP_INDEX_3,
47    HNP_INDEX_4,
48    HNP_INDEX_5,
49    HNP_INDEX_6,
50    HNP_INDEX_7
51};
52
53static int HnpCmdApiReturnGet(int readFd, int *ret)
54{
55    char buffer[BUFFER_SIZE] = {0};
56    ssize_t bytesRead;
57    int bufferEnd = 0; // 跟踪缓冲区中有效数据的末尾
58    const char *prefix = "native manager process exit. ret=";
59
60    // 循环读取子进程的输出,直到结束
61    while ((bytesRead = read(readFd, buffer + bufferEnd, BUFFER_SIZE - bufferEnd - 1)) > 0) {
62        // 更新有效数据的末尾
63        bufferEnd += bytesRead;
64
65        // 如果缓冲区中的数据超过或等于50个字节,移动数据以保留最后50个字节
66        if (bufferEnd >= CMD_API_TEXT_LEN) {
67            if (memmove_s(buffer, BUFFER_SIZE, buffer + bufferEnd - CMD_API_TEXT_LEN, CMD_API_TEXT_LEN) != EOK) {
68                HNPAPI_LOG("\r\n [HNP API] mem move unsuccess!\r\n");
69                return HNP_API_ERRNO_MEMMOVE_FAILED;
70            }
71            bufferEnd = CMD_API_TEXT_LEN;
72        }
73
74        buffer[bufferEnd] = '\0';
75    }
76
77    if (bytesRead == -1) {
78        HNPAPI_LOG("\r\n [HNP API] read stream unsuccess!\r\n");
79        return HNP_API_ERRNO_PIPE_READ_FAILED;
80    }
81
82    char *retStr = strstr(buffer, prefix);
83
84    if (retStr != NULL) {
85        // 获取后续的数字字符串
86        retStr += strlen(prefix);
87        *ret = atoi(retStr);
88
89        return 0;
90    }
91
92    HNPAPI_LOG("\r\n [HNP API] get return unsuccess!, buffer is:%{public}s\r\n", buffer);
93    return HNP_API_ERRNO_RETURN_VALUE_GET_FAILED;
94}
95
96static int StartHnpProcess(char *const argv[], char *const apcEnv[])
97{
98    int fd[2];
99    pid_t pid;
100    int exitVal = -1;
101    int ret;
102    int status;
103
104    if (pipe(fd) < 0) {
105        HNPAPI_LOG("\r\n [HNP API] pipe fd unsuccess!\r\n");
106        return HNP_API_ERRNO_PIPE_CREATED_FAILED;
107    }
108
109    pid = vfork();
110    if (pid < 0) {
111        HNPAPI_LOG("\r\n [HNP API] fork unsuccess!\r\n");
112        return HNP_API_ERRNO_FORK_FAILED;
113    } else if (pid == 0) {
114        close(fd[0]);
115        // 将子进程的stdout重定向到管道的写端
116        dup2(fd[1], STDOUT_FILENO);
117        close(fd[1]);
118
119        ret = execve("/system/bin/hnp", argv, apcEnv);
120        if (ret < 0) {
121            HNPAPI_LOG("\r\n [HNP API] execve unsuccess!\r\n");
122            _exit(-1);
123        }
124        _exit(0);
125    }
126
127    HNPAPI_LOG("\r\n [HNP API] this is fork father! chid=%{public}d\r\n", pid);
128    close(fd[1]);
129    if (waitpid(pid, &status, 0) == -1) {
130        close(fd[0]);
131        HNPAPI_LOG("\r\n [HNP API] wait pid unsuccess!\r\n");
132        return HNP_API_WAIT_PID_FAILED;
133    }
134    ret = HnpCmdApiReturnGet(fd[0], &exitVal);
135    close(fd[0]);
136    if (ret != 0) {
137        HNPAPI_LOG("\r\n [HNP API] get api return value unsuccess!\r\n");
138        return ret;
139    }
140    HNPAPI_LOG("\r\n [HNP API] Child process exited with exitval=%{public}d\r\n", exitVal);
141
142    return exitVal;
143}
144
145static bool IsHnpInstallEnable()
146{
147    char buffer[PARAM_BUFFER_SIZE] = {0};
148    int ret = GetParameter("const.startup.hnp.install.enable", "false", buffer, PARAM_BUFFER_SIZE);
149    if (ret <= 0) {
150        HNPAPI_LOG("\r\n [HNP API] get hnp install enable param unsuccess! ret =%{public}d\r\n", ret);
151        return false;
152    }
153
154    if (strcmp(buffer, "true") == 0) {
155        return true;
156    }
157
158    return false;
159}
160
161int NativeInstallHnp(const char *userId, const char *hnpRootPath,  const HapInfo *hapInfo, int installOptions)
162{
163    char *argv[MAX_ARGV_NUM] = {0};
164    char *apcEnv[MAX_ENV_NUM] = {0};
165    int index = 0;
166
167    if (!IsDeveloperModeOpen()) {
168        HNPAPI_LOG("\r\n [HNP API] native package install not in developer mode");
169        return HNP_API_NOT_IN_DEVELOPER_MODE;
170    }
171
172    if ((userId == NULL) || (hnpRootPath == NULL) || (hapInfo == NULL)) {
173        return HNP_API_ERRNO_PARAM_INVALID;
174    }
175
176    if (!IsHnpInstallEnable()) {
177        return HNP_API_ERRNO_HNP_INSTALL_DISABLED;
178    }
179
180    HNPAPI_LOG("\r\n [HNP API] native package install! userId=%{public}s, hap path=%{public}s, sys abi=%{public}s, "
181        "hnp root path=%{public}s, package name=%{public}s install options=%{public}d\r\n", userId, hapInfo->hapPath,
182        hapInfo->abi, hnpRootPath, hapInfo->packageName, installOptions);
183
184    argv[index++] = "hnp";
185    argv[index++] = "install";
186    argv[index++] = "-u";
187    argv[index++] = (char *)userId;
188    argv[index++] = "-i";
189    argv[index++] = (char *)hnpRootPath;
190    argv[index++] = "-p";
191    argv[index++] = (char *)hapInfo->packageName;
192    argv[index++] = "-s";
193    argv[index++] = (char *)hapInfo->hapPath;
194    argv[index++] = "-a";
195    argv[index++] = (char *)hapInfo->abi;
196
197    if (installOptions >= 0 && IS_OPTION_SET((unsigned int)installOptions, OPTION_INDEX_FORCE)) {
198        argv[index++] = "-f";
199    }
200
201    return StartHnpProcess(argv, apcEnv);
202}
203
204int NativeUnInstallHnp(const char *userId, const char *packageName)
205{
206    char *argv[MAX_ARGV_NUM] = {0};
207    char *apcEnv[MAX_ENV_NUM] = {0};
208    int index = 0;
209
210    if (!IsDeveloperModeOpen()) {
211        HNPAPI_LOG("\r\n [HNP API] native package uninstall not in developer mode");
212        return HNP_API_NOT_IN_DEVELOPER_MODE;
213    }
214
215    if ((userId == NULL) || (packageName == NULL)) {
216        return HNP_API_ERRNO_PARAM_INVALID;
217    }
218
219    HNPAPI_LOG("\r\n [HNP API] native package uninstall! userId=%{public}s, package name=%{public}s\r\n", userId,
220        packageName);
221
222    argv[index++] = "hnp";
223    argv[index++] = "uninstall";
224    argv[index++] = "-u";
225    argv[index++] = (char *)userId;
226    argv[index++] = "-p";
227    argv[index++] = (char *)packageName;
228
229    return StartHnpProcess(argv, apcEnv);
230}
231
232#ifdef __cplusplus
233}
234#endif