blob: 69b533825b5e98c0b9b948b7424468b73b74bb24 [file] [log] [blame]
/*
* Copyright (c) 2024-2025 Project CHIP Authors
* All rights reserved.
*
* 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 "UloopHandler.h"
#include <lib/core/CHIPConfig.h>
#include <lib/support/CodeUtils.h>
#include <platform/CHIPDeviceLayer.h>
#include <chrono>
extern "C" {
#include <libubox/uloop.h>
}
#ifndef CHIP_ULOOP_DEBUGGING
#define CHIP_ULOOP_DEBUGGING 0
#endif
namespace chip {
namespace ubus {
namespace {
System::LayerSocketsLoop & SystemLayer()
{
return static_cast<System::LayerSocketsLoop &>(DeviceLayer::SystemLayer());
}
} // namespace
///// UloopHandler
UloopHandler UloopHandler::gInstance;
int UloopHandler::gRegistered = 0;
CHIP_ERROR UloopHandler::Register()
{
VerifyOrReturnError(gRegistered++ == 0, CHIP_NO_ERROR);
if (uloop_fd_set_cb != nullptr)
{
gRegistered = 0;
return CHIP_ERROR_INCORRECT_STATE;
}
ChipLogDetail(DeviceLayer, "Registering uloop handler");
uloop_fd_set_cb = &UloopFdSet;
uloop_handle_sigchld = false;
{
UloopSignalGuard guard;
uloop_init();
}
SystemLayer().AddLoopHandler(gInstance);
return CHIP_NO_ERROR;
}
void UloopHandler::Unregister()
{
auto count = gRegistered--;
VerifyOrDie(count > 0);
VerifyOrReturn(count == 1);
ChipLogDetail(DeviceLayer, "Unregistering uloop handler");
SystemLayer().RemoveLoopHandler(gInstance);
uloop_done();
uloop_fd_set_cb = nullptr;
}
System::Clock::Timestamp UloopHandler::PrepareEvents(System::Clock::Timestamp now)
{
std::chrono::duration<int, std::milli> timeout(uloop_get_next_timeout());
VerifyOrReturnValue(timeout.count() >= 0, System::Clock::Timestamp::max());
#if CHIP_ULOOP_DEBUGGING
ChipLogDetail(DeviceLayer, "Next uloop timeout: %d", timeout.count());
#endif
return now + std::chrono::duration_cast<System::Clock::Timestamp>(timeout);
}
void UloopHandler::HandleEvents()
{
uloop_run_timeout(0);
}
void UloopHandler::UloopFdSet(uloop_fd * fd, unsigned int events)
{
#if CHIP_ULOOP_DEBUGGING
ChipLogDetail(DeviceLayer, "Uloop fd_set(%d, 0x%x)", fd->fd, events);
#endif
VerifyOrDieWithMsg(!(events & ~(ULOOP_READ | ULOOP_WRITE | ULOOP_BLOCKING)), DeviceLayer,
"Unsupported uloop fd_set events: 0x%x", events);
auto & system = SystemLayer();
System::SocketWatchToken watch;
CHIP_ERROR err = system.StartWatchingSocket(fd->fd, &watch); // or find existing watch
if (events != 0)
{
int changed = fd->flags ^ events;
if (changed & ULOOP_READ)
{
if (events & ULOOP_READ)
{
system.RequestCallbackOnPendingRead(watch);
}
else
{
system.ClearCallbackOnPendingRead(watch);
}
}
if (changed & ULOOP_WRITE)
{
if (events & ULOOP_WRITE)
{
system.RequestCallbackOnPendingWrite(watch);
}
else
{
system.ClearCallbackOnPendingWrite(watch);
}
}
}
else
{
VerifyOrReturn(err == CHIP_NO_ERROR); // shouldn't happen, but ignore
SuccessOrExit(err = system.StopWatchingSocket(&watch));
}
return;
exit:
ChipLogError(DeviceLayer, "Failed to handle uloop fd_set(%d, %d): %" CHIP_ERROR_FORMAT, fd->fd, events, err.Format());
}
///// UloopSignalGuard
UloopSignalGuard::UloopSignalGuard()
{
Guard(SIGINT, mSigInt);
Guard(SIGTERM, mSigTerm);
}
UloopSignalGuard::~UloopSignalGuard()
{
Restore(SIGINT, mSigInt);
Restore(SIGTERM, mSigTerm);
}
void UloopSignalGuard::Guard(int sig, struct sigaction & action)
{
sigaction(sig, nullptr, &action);
VerifyOrReturn(action.sa_handler == SIG_DFL);
// Uloop will only modify signal handlers if they are currently set to SIG_DFL.
// In that case, install a handler (which won't equal SIG_DFL) while the guard
// is active. In the unlikely case that the guard handler receives a signal,
// the handler is reset to SIG_DFL via SA_RESETHAND and re-raised.
struct sigaction guard = {};
sigemptyset(&guard.sa_mask);
guard.sa_flags = SA_RESETHAND;
guard.sa_handler = [](int received) { raise(received); };
sigaction(sig, &guard, nullptr);
}
void UloopSignalGuard::Restore(int sig, struct sigaction & action)
{
// Restore unconditionally (belt and suspenders ...)
sigaction(sig, &action, nullptr);
}
} // namespace ubus
} // namespace chip