/* * Copyright (c) 2025 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup libsystem * @{ */ /** @file System control service interface */ #include #include #include #include #include #include #include #include #include "../private/system.h" static errno_t system_callback_create(system_t *); static void system_cb_conn(ipc_call_t *, void *); /** Open system control service. * * @param svcname Service name * @param cb Pointer to callback structure * @param arg Argument passed to callback functions * @param rsystem Place to store pointer to system control service object. * * @return EOK on success or an error code */ errno_t system_open(const char *svcname, system_cb_t *cb, void *arg, system_t **rsystem) { service_id_t system_svc; async_sess_t *sess; system_t *system; errno_t rc; system = calloc(1, sizeof(system_t)); if (system == NULL) return ENOMEM; fibril_mutex_initialize(&system->lock); fibril_condvar_initialize(&system->cv); rc = loc_service_get_id(svcname, &system_svc, IPC_FLAG_BLOCKING); if (rc != EOK) goto error; sess = loc_service_connect(system_svc, INTERFACE_SYSTEM, IPC_FLAG_BLOCKING); if (sess == NULL) goto error; system->sess = sess; system->cb = cb; system->cb_arg = arg; rc = system_callback_create(system); if (rc != EOK) { async_hangup(sess); goto error; } *rsystem = system; return EOK; error: free(system); return rc; } /** Close system control service. * * @param system System control service */ void system_close(system_t *system) { fibril_mutex_lock(&system->lock); async_hangup(system->sess); system->sess = NULL; /* Wait for callback handler to terminate */ while (!system->cb_done) fibril_condvar_wait(&system->cv, &system->lock); fibril_mutex_unlock(&system->lock); free(system); } /** Create callback connection from system control service. * * @param system System control service * @return EOK on success or an error code */ static errno_t system_callback_create(system_t *system) { async_exch_t *exch = async_exchange_begin(system->sess); aid_t req = async_send_0(exch, SYSTEM_CALLBACK_CREATE, NULL); port_id_t port; errno_t rc = async_create_callback_port(exch, INTERFACE_SYSTEM_CB, 0, 0, system_cb_conn, system, &port); async_exchange_end(exch); if (rc != EOK) return rc; errno_t retval; async_wait_for(req, &retval); return retval; } /** Shut down and power the system off. * * This function is asynchronous. It returns immediately with success * if the system started shutting down. Once shutdown is completed, * the @c shutdown_complete callback is executed. If the shutdown fails, * the @c shutdown_fail callback is executed. * * @param system System control service * @return EOK on succes or an error code */ errno_t system_poweroff(system_t *system) { async_exch_t *exch = async_exchange_begin(system->sess); errno_t rc = async_req_0_0(exch, SYSTEM_POWEROFF); async_exchange_end(exch); return rc; } /** Shut down and restart the system. * * This function is asynchronous. It returns immediately with success * if the system started shutting down. Once shutdown is completed, * the @c shutdown_complete callback is executed. If the shutdown fails, * the @c shutdown_fail callback is executed. * * @param system System control service * @return EOK on succes or an error code */ errno_t system_restart(system_t *system) { async_exch_t *exch = async_exchange_begin(system->sess); errno_t rc = async_req_0_0(exch, SYSTEM_RESTART); async_exchange_end(exch); return rc; } /** System shutdown completed. * * @param system System control service * @param icall Call data */ static void system_shutdown_complete(system_t *system, ipc_call_t *icall) { if (system->cb != NULL && system->cb->shutdown_complete != NULL) system->cb->shutdown_complete(system->cb_arg); async_answer_0(icall, EOK); } /** System shutdown failed. * * @param system System control service * @param icall Call data */ static void system_shutdown_failed(system_t *system, ipc_call_t *icall) { if (system->cb != NULL && system->cb->shutdown_complete != NULL) system->cb->shutdown_failed(system->cb_arg); async_answer_0(icall, EOK); } /** Callback connection handler. * * @param icall Connect call data * @param arg Argument, system_t * */ static void system_cb_conn(ipc_call_t *icall, void *arg) { system_t *system = (system_t *) arg; while (true) { ipc_call_t call; async_get_call(&call); if (!ipc_get_imethod(&call)) { /* Hangup */ async_answer_0(&call, EOK); goto out; } switch (ipc_get_imethod(&call)) { case SYSTEM_SHUTDOWN_COMPLETE: system_shutdown_complete(system, &call); break; case SYSTEM_SHUTDOWN_FAILED: system_shutdown_failed(system, &call); break; default: async_answer_0(&call, ENOTSUP); break; } } out: fibril_mutex_lock(&system->lock); system->cb_done = true; fibril_mutex_unlock(&system->lock); fibril_condvar_broadcast(&system->cv); } /** @} */