From b0c759e51825d337096f0841005c0c48e45f37d6 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 15 Jun 2015 14:06:55 +0200 Subject: [PATCH] Add missing file api.c Signed-off-by: Joachim Nilsson --- api.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 api.c diff --git a/api.c b/api.c new file mode 100644 index 00000000..23e361e5 --- /dev/null +++ b/api.c @@ -0,0 +1,254 @@ +/* External client API, using UNIX domain socket. + * + * Copyright (c) 2015 Joachim Nilsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "helpers.h" +#include "plugin.h" +#include "sig.h" +#include "finit.h" +#include "service.h" + +#include "libuev/uev.h" + +uev_t api_watcher; + + +static svc_t *convert_jobid(char *jobid) +{ + int job, id = 1; + char *token; + + if (!jobid) + return NULL; + + token = strtok(jobid, ":"); + if (!token) { + job = atoi(jobid); + } else { + job = atoi(token); + token = strtok(NULL, ""); + if (token) + id = atoi(token); + } + + _d("Got job %d id %d ...", job, id); + return svc_find_by_jobid(job, id); +} + +static int do_start(char *jobid) +{ + return service_start(convert_jobid(jobid)); +} + +static int do_stop(char *jobid) +{ + return service_stop(convert_jobid(jobid)); +} + +static int do_reload(char *jobid) +{ + return service_reload(convert_jobid(jobid)); +} + +static int do_restart(char *jobid) +{ + return service_restart(convert_jobid(jobid)); +} + +static int do_query_inetd(char *buf, size_t len) +{ + char *ptr = strchr(buf, ':'); + svc_t *svc; + + if (ptr) + ptr = 0; + + svc = convert_jobid(buf); + if (!svc || !svc_is_inetd(svc)) { + _e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", buf); + return 1; + } + + return inetd_filter_str(&svc->inetd, buf, len); +} + +static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events)) +{ + int sd = accept(w->fd, NULL, NULL); + struct init_request rq; + + while (1) { + int result = 0; + ssize_t len = read(sd, &rq, sizeof(rq)); + + if (len <= 0) { + if (-1 == len) { + if (EINTR == errno) + continue; + + if (EAGAIN == errno) + break; + + _e("Failed reading initctl request, error %d: %s", errno, strerror(errno)); + } + + _d("Nothing to do, bailing out."); + + break; + } + + if (rq.magic != INIT_MAGIC || len != sizeof(rq)) { + _e("Invalid initctl request."); + break; + } + + switch (rq.cmd) { + case INIT_CMD_RUNLVL: + switch (rq.runlevel) { + case '0': + _d("Halting system (SIGUSR2)"); + do_shutdown(SIGUSR2); + break; + + case 's': + case 'S': + _d("Cannot enter bootstrap after boot ..."); + rq.runlevel = '1'; + /* Fall through to regular processing */ + + case '1'...'5': + case '7'...'9': + _d("Setting new runlevel %c", rq.runlevel); + service_runlevel(rq.runlevel - '0'); + break; + + case '6': + _d("Rebooting system (SIGUSR1)"); + do_shutdown(SIGUSR1); + break; + + default: + _d("Unsupported runlevel: %d", rq.runlevel); + break; + } + break; + + case INIT_CMD_DEBUG: + debug = !debug; + break; + + case INIT_CMD_RELOAD: + reload_finit_d(); + break; + + case INIT_CMD_START_SVC: + result = do_start(rq.data); + break; + + case INIT_CMD_STOP_SVC: + result = do_stop(rq.data); + break; + + case INIT_CMD_RELOAD_SVC: + result = do_reload(rq.data); + break; + + case INIT_CMD_RESTART_SVC: + result = do_restart(rq.data); + break; + + case INIT_CMD_QUERY_INETD: + result = do_query_inetd(rq.data, sizeof(rq.data)); + break; + + case INIT_CMD_ACK: + _d("Client failed reading ACK."); + goto leave; + + default: + _d("Unsupported cmd: %d", rq.cmd); + break; + } + + if (result) + rq.cmd = INIT_CMD_NACK; + else + rq.cmd = INIT_CMD_ACK; + len = write(sd, &rq, sizeof(rq)); + if (len != sizeof(rq)) + _d("Failed sending ACK/NACK back to client."); +#if 0 + else + sleep(1); /* Give client time to read FIFO */ + _e("Ready for next station."); +#endif + } + +leave: + close(sd); +} + +int api_init(uev_ctx_t *ctx) +{ + int sd; + struct sockaddr_un sun = { + .sun_family = AF_UNIX, + .sun_path = INIT_SOCKET, + }; + + sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (-1 == sd) { + _pe("Failed starting external API socket"); + return 1; + } + + remove(INIT_SOCKET); + if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun))) + goto error; + + if (-1 == listen(sd, 10)) + goto error; + + return uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ); + +error: + _pe("Failed intializing API socket"); + close(sd); + return 1; +} + + +/** + * Local Variables: + * version-control: t + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */