* Copyright (c) 2026 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 <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "appspawn_fd_manager.h"
#include "appspawn_manager.h"
#include "appspawn_utils.h"
void SpawningFdsDestroy(ListNode *node)
{
AppSpawnFds *fdSets = ListEntry(node, AppSpawnFds, node);
for (size_t i = 0; i < fdSets->count; i++) {
APPSPAWN_ONLY_EXPER(fdSets->fds[i] >= 0, close(fdSets->fds[i]);
fdSets->fds[i] = -1);
}
free(fdSets);
}
* @brief Compare function for finding spawning fd by pid and type.
* @return 0 if matched, 1 otherwise (convention for OH_ListFind)
*/
static int SpawningFdPidComparePro(ListNode *node, void *data)
{
APPSPAWN_ONLY_EXPER(node == NULL || data == NULL, return 1);
AppSpawnFds *fdsNode = ListEntry(node, AppSpawnFds, node);
SpawningFdKey *key = data;
return (fdsNode->pid == key->pid && fdsNode->type == key->type) ? 0 : 1;
}
* @brief Get current timestamp in microseconds (CLOCK_MONOTONIC).
* @return Timestamp in microseconds, or 0 if clock_gettime fails
*/
static uint64_t GetCurrentTimestamp(void)
{
struct timespec ts;
APPSPAWN_ONLY_EXPER(clock_gettime(CLOCK_MONOTONIC, &ts) != 0, return 0);
return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)ts.tv_nsec / 1000ULL;
}
AppSpawnFds *RegisterSpawningFds(AppSpawnMgr *mgr, const SpawningFdRegInfo *regInfo)
{
APPSPAWN_CHECK(mgr != NULL, return NULL, "Invalid mgr");
APPSPAWN_CHECK(regInfo != NULL, return NULL, "Invalid regInfo");
APPSPAWN_CHECK(regInfo->type >= TYPE_FOR_DEC && regInfo->type < TYPE_INVALID, return NULL,
"Invalid type %{public}d", regInfo->type);
APPSPAWN_CHECK(regInfo->count > 0 && regInfo->fds != NULL,
return NULL, "Invalid fds");
APPSPAWN_CHECK(regInfo->count <= MAX_SPAWNING_FDS_PER_NODE,
return NULL, "Invalid count %{public}u exceed max %{public}u",
regInfo->count, MAX_SPAWNING_FDS_PER_NODE);
size_t size = sizeof(AppSpawnFds) + regInfo->count * sizeof(int);
AppSpawnFds *spawnFds = (AppSpawnFds *)calloc(1, size);
APPSPAWN_CHECK(spawnFds != NULL, return NULL, "Failed to alloc spawning fds");
OH_ListInit(&spawnFds->node);
spawnFds->type = regInfo->type;
spawnFds->count = regInfo->count;
spawnFds->pid = regInfo->pid;
spawnFds->timestamp = GetCurrentTimestamp();
for (uint32_t i = 0; i < regInfo->count; i++) {
spawnFds->fds[i] = regInfo->fds[i];
}
OH_ListAddTail(&mgr->spawningFdsQueue, &spawnFds->node);
APPSPAWN_LOGV("RegisterSpawningFds type=%{public}d pid=%{public}d count=%{public}u",
regInfo->type, regInfo->pid, regInfo->count);
return spawnFds;
}
AppSpawnFds *FindSpawningFdsByPid(const AppSpawnMgr *mgr, pid_t pid, SpawningFdType type)
{
APPSPAWN_CHECK_ONLY_EXPER(mgr != NULL, return NULL);
SpawningFdKey key = { pid, type };
ListNode *node = OH_ListFind(&mgr->spawningFdsQueue, &key, SpawningFdPidComparePro);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return NULL);
return ListEntry(node, AppSpawnFds, node);
}
int UnregisterSpawningFdsByPid(AppSpawnMgr *mgr, pid_t pid, SpawningFdType type)
{
APPSPAWN_CHECK(mgr != NULL, return APPSPAWN_ARG_INVALID, "Invalid mgr");
SpawningFdKey key = { pid, type };
ListNode *node = OH_ListFind(&mgr->spawningFdsQueue, &key, SpawningFdPidComparePro);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return APPSPAWN_ARG_INVALID);
AppSpawnFds *spawnFds = ListEntry(node, AppSpawnFds, node);
for (uint32_t i = 0; i < spawnFds->count; i++) {
if (spawnFds->fds[i] >= 0) {
close(spawnFds->fds[i]);
spawnFds->fds[i] = -1;
}
}
OH_ListRemove(&spawnFds->node);
APPSPAWN_LOGV("UnregisterSpawningFdsByPid type=%{public}d pid=%{public}d", type, pid);
free(spawnFds);
return 0;
}
int RemoveSpawningFdsByPid(AppSpawnMgr *mgr, pid_t pid, SpawningFdType type)
{
APPSPAWN_CHECK(mgr != NULL, return APPSPAWN_ARG_INVALID, "Invalid mgr");
SpawningFdKey key = { pid, type };
ListNode *node = OH_ListFind(&mgr->spawningFdsQueue, &key, SpawningFdPidComparePro);
APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return APPSPAWN_ARG_INVALID);
AppSpawnFds *spawnFds = ListEntry(node, AppSpawnFds, node);
OH_ListRemove(&spawnFds->node);
APPSPAWN_LOGV("RemoveSpawningFdsByPid type=%{public}d pid=%{public}d (fd not closed)", type, pid);
free(spawnFds);
return 0;
}
void DeleteSpawningFds(AppSpawnFds **fds)
{
APPSPAWN_CHECK_ONLY_EXPER(fds != NULL && *fds != NULL, return);
OH_ListRemove(&(*fds)->node);
APPSPAWN_LOGV("DeleteSpawningFds type=%{public}d pid=%{public}d (fd not closed)", (*fds)->type, (*fds)->pid);
free(*fds);
*fds = NULL;
}
* @brief Close all valid FDs in a spawning fd node (does not free the node).
*/
static void CloseSpawnFds(AppSpawnFds *spawnFds)
{
for (uint32_t i = 0; i < spawnFds->count; i++) {
APPSPAWN_ONLY_EXPER(spawnFds->fds[i] >= 0, close(spawnFds->fds[i]);
spawnFds->fds[i] = -1);
}
}
uint32_t CleanupSpawningFdsByPid(AppSpawnMgr *mgr, pid_t pid)
{
APPSPAWN_CHECK_ONLY_EXPER(mgr != NULL, return 0);
uint32_t cleanedCount = 0;
ListNode *node = mgr->spawningFdsQueue.next;
while (node != &mgr->spawningFdsQueue) {
ListNode *next = node->next;
AppSpawnFds *spawnFds = ListEntry(node, AppSpawnFds, node);
if (spawnFds->pid == pid) {
CloseSpawnFds(spawnFds);
OH_ListRemove(&spawnFds->node);
OH_ListInit(&spawnFds->node);
APPSPAWN_LOGI("CleanupSpawningFdsByPid type=%{public}d pid=%{public}d", spawnFds->type, spawnFds->pid);
free(spawnFds);
cleanedCount++;
}
node = next;
}
return cleanedCount;
}
void DumpSpawningFds(const AppSpawnMgr *mgr)
{
APPSPAWN_CHECK_ONLY_EXPER(mgr != NULL, return);
APPSPAWN_DUMP("Spawning Fds Queue:");
ListNode *node = mgr->spawningFdsQueue.next;
while (node != &mgr->spawningFdsQueue) {
AppSpawnFds *spawnFds = ListEntry(node, AppSpawnFds, node);
APPSPAWN_DUMP(" type=%{public}d pid=%{public}d timestamp=%{public}" PRIu64,
spawnFds->type, spawnFds->pid, spawnFds->timestamp);
for (uint32_t i = 0; i < spawnFds->count; i++) {
APPSPAWN_DUMP(" fd[%{public}u]=%{public}d", i, spawnFds->fds[i]);
}
node = node->next;
}
}
void GetSpawningFdsStats(const AppSpawnMgr *mgr, uint32_t *total, uint32_t *childParentCount,
uint32_t *parentChildCount)
{
APPSPAWN_CHECK_ONLY_EXPER(mgr != NULL && total != NULL && childParentCount != NULL && parentChildCount != NULL,
return);
*total = 0;
*childParentCount = 0;
*parentChildCount = 0;
ListNode *node = mgr->spawningFdsQueue.next;
while (node != &mgr->spawningFdsQueue) {
AppSpawnFds *spawnFds = ListEntry(node, AppSpawnFds, node);
(*total)++;
if (spawnFds->type == TYPE_CHILD_PARENT) {
(*childParentCount)++;
} else if (spawnFds->type == TYPE_PARENT_CHILD) {
(*parentChildCount)++;
}
node = node->next;
}
}