From ca820c65c2b03a24e8936218171c6c1d138fd1f7 Mon Sep 17 00:00:00 2001 From: José Bollo Date: Thu, 22 Feb 2018 13:22:48 +0100 Subject: fdev: Introduce fdev for file event handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an effort to keep cutting dependency to systemd. Change-Id: I9a0c032a1095e297c7f3ac5b67827fda3658b8d9 Signed-off-by: José Bollo --- src/CMakeLists.txt | 6 +- src/afb-api-ws.c | 97 +++++++++++++++++-------------- src/afb-fdev.c | 26 +++++++++ src/afb-fdev.h | 22 +++++++ src/afb-proto-ws.c | 27 ++++----- src/afb-proto-ws.h | 6 +- src/afb-stub-ws.c | 23 ++++---- src/afb-stub-ws.h | 5 +- src/afb-supervision.c | 11 +++- src/afb-systemd.c | 4 +- src/afb-systemd.h | 4 +- src/afb-websock.c | 14 ++++- src/afb-ws-client.c | 25 +++++--- src/afb-ws-json1.c | 11 ++-- src/afb-ws-json1.h | 3 +- src/afb-ws.c | 33 ++++------- src/afb-ws.h | 4 +- src/afb-wsj1.c | 10 ++-- src/afb-wsj1.h | 8 +-- src/afs-supervisor.c | 10 +++- src/fdev-epoll.c | 97 +++++++++++++++++++++++++++++++ src/fdev-epoll.h | 28 +++++++++ src/fdev-systemd.c | 79 +++++++++++++++++++++++++ src/fdev-systemd.h | 23 ++++++++ src/fdev.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/fdev.h | 48 +++++++++++++++ 26 files changed, 649 insertions(+), 133 deletions(-) create mode 100644 src/afb-fdev.c create mode 100644 src/afb-fdev.h create mode 100644 src/fdev-epoll.c create mode 100644 src/fdev-epoll.h create mode 100644 src/fdev-systemd.c create mode 100644 src/fdev-systemd.h create mode 100644 src/fdev.c create mode 100644 src/fdev.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ef87853..f72d312e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,6 +42,7 @@ ADD_LIBRARY(afb-lib STATIC afb-debug.c afb-evt.c afb-export.c + afb-fdev.c afb-hook.c afb-hreq.c afb-hsrv.c @@ -61,6 +62,9 @@ ADD_LIBRARY(afb-lib STATIC afb-ws.c afb-wsj1.c afb-xreq.c + fdev.c + fdev-epoll.c + fdev-systemd.c jobs.c locale-root.c process-name.c @@ -96,7 +100,7 @@ INSTALL(TARGETS afs-supervisor ########################################### # build and install libafbwsc ########################################### -ADD_LIBRARY(afbwsc SHARED afb-ws.c afb-ws-client.c afb-wsj1.c websock.c afb-proto-ws.c jobs-fake.c) +ADD_LIBRARY(afbwsc SHARED afb-ws.c afb-ws-client.c afb-wsj1.c websock.c afb-proto-ws.c jobs-fake.c fdev.c fdev-systemd.c) SET_TARGET_PROPERTIES(afbwsc PROPERTIES VERSION ${LIBAFBWSC_VERSION} SOVERSION ${LIBAFBWSC_SOVERSION}) diff --git a/src/afb-api-ws.c b/src/afb-api-ws.c index 7fbe5be1..489b43b1 100644 --- a/src/afb-api-ws.c +++ b/src/afb-api-ws.c @@ -30,19 +30,19 @@ #include #include -#include +#include "afb-fdev.h" +#include "afb-systemd.h" #include "afb-api.h" #include "afb-apiset.h" -#include "afb-systemd.h" #include "afb-stub-ws.h" #include "verbose.h" +#include "fdev.h" struct api_ws { char *path; /* path of the object for the API */ char *api; /* api name of the interface */ - int fd; /* file descriptor */ - sd_event_source *listensrc; /**< systemd source for server socket */ + struct fdev *fdev; /* fdev handler */ struct afb_apiset *apiset; }; @@ -77,7 +77,6 @@ static struct api_ws *api_ws_make(const char *path) goto error2; } - api->fd = -1; return api; error2: @@ -176,7 +175,7 @@ static int api_ws_socket(const char *path, int server) /* check for systemd socket */ if (0 == strncmp(path, "sd:", 3)) - fd = systemd_fds_for(path + 3); + fd = afb_systemd_fds_for(path + 3); else { /* check for unix socket */ if (0 == strncmp(path, "unix:", 5)) @@ -200,6 +199,24 @@ static int api_ws_socket(const char *path, int server) return fd; } +static struct fdev *api_ws_socket_fdev(const char *path, int server) +{ + int fd; + struct fdev *fdev; + + fd = api_ws_socket(path, server); + if (fd < 0) + fdev = 0; + else { + fdev = afb_fdev_create(fd); + if (!fdev) + close(fd); + } + if (!fdev) + ERROR("can't make %s socket for %s", server ? "server" : "client", path); + return fdev; +} + /**********************************************************************************/ int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset, int strong) @@ -213,27 +230,23 @@ int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset, int stron goto error; /* connect to the service */ - apiws->fd = api_ws_socket(apiws->path, 0); - if (apiws->fd < 0) { - ERROR("can't connect to ws service %s", apiws->path); + apiws->fdev = api_ws_socket_fdev(apiws->path, 0); + if (!apiws->fdev) goto error2; - } - stubws = afb_stub_ws_create_client(apiws->fd, apiws->api, apiset); + stubws = afb_stub_ws_create_client(apiws->fdev, apiws->api, apiset); if (!stubws) { ERROR("can't setup client ws service to %s", apiws->path); goto error3; } if (afb_stub_ws_client_add(stubws, apiset) < 0) { ERROR("can't add the client to the apiset for service %s", apiws->path); - goto error4; + goto error3; } free(apiws); return 0; -error4: - afb_stub_ws_unref(stubws); error3: - close(apiws->fd); + afb_stub_ws_unref(stubws); error2: free(apiws); error: @@ -250,9 +263,9 @@ int afb_api_ws_add_client_weak(const char *path, struct afb_apiset *apiset) return afb_api_ws_add_client(path, apiset, 0); } -static int api_ws_server_accept_client(struct api_ws *apiws, int fd) +static int api_ws_server_accept_client(struct api_ws *apiws, struct fdev *fdev) { - return -!afb_stub_ws_create_server(fd, apiws->api, apiws->apiset); + return -!afb_stub_ws_create_server(fdev, apiws->api, apiws->apiset); } static void api_ws_server_accept(struct api_ws *apiws) @@ -260,20 +273,28 @@ static void api_ws_server_accept(struct api_ws *apiws) int rc, fd; struct sockaddr addr; socklen_t lenaddr; + struct fdev *fdev; lenaddr = (socklen_t)sizeof addr; - fd = accept(apiws->fd, &addr, &lenaddr); - if (fd >= 0) { - rc = api_ws_server_accept_client(apiws, fd); - if (rc >= 0) - return; - close(fd); + fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr); + if (fd < 0) { + ERROR("can't accept connection to %s: %m", apiws->path); + } else { + fdev = afb_fdev_create(fd); + if (!fdev) { + ERROR("can't hold accepted connection to %s: %m", apiws->path); + close(fd); + } else { + rc = api_ws_server_accept_client(apiws, fdev); + if (rc < 0) + ERROR("can't serve accepted connection to %s: %m", apiws->path); + } } } static int api_ws_server_connect(struct api_ws *apiws); -static int api_ws_server_listen_callback(sd_event_source *src, int fd, uint32_t revents, void *closure) +static void api_ws_server_listen_callback(void *closure, uint32_t revents, struct fdev *fdev) { struct api_ws *apiws = closure; @@ -281,42 +302,28 @@ static int api_ws_server_listen_callback(sd_event_source *src, int fd, uint32_t api_ws_server_accept(apiws); if ((revents & EPOLLHUP) != 0) api_ws_server_connect(apiws); - return 0; } static void api_ws_server_disconnect(struct api_ws *apiws) { - if (apiws->listensrc != NULL) { - sd_event_source_unref(apiws->listensrc); - apiws->listensrc = NULL; - } - if (apiws->fd >= 0) { - close(apiws->fd); - apiws->fd = -1; - } + fdev_unref(apiws->fdev); + apiws->fdev = 0; } static int api_ws_server_connect(struct api_ws *apiws) { - int rc; - /* ensure disconnected */ api_ws_server_disconnect(apiws); /* request the service object name */ - apiws->fd = api_ws_socket(apiws->path, 1); - if (apiws->fd < 0) + apiws->fdev = api_ws_socket_fdev(apiws->path, 1); + if (!apiws->fdev) ERROR("can't create socket %s", apiws->path); else { /* listen for service */ - rc = sd_event_add_io(afb_systemd_get_event_loop(), - &apiws->listensrc, apiws->fd, EPOLLIN, - api_ws_server_listen_callback, apiws); - if (rc >= 0) - return 0; - close(apiws->fd); - errno = -rc; - ERROR("can't add ws object %s", apiws->path); + fdev_set_events(apiws->fdev, EPOLLIN); + fdev_set_callback(apiws->fdev, api_ws_server_listen_callback, apiws); + return 0; } return -1; } diff --git a/src/afb-fdev.c b/src/afb-fdev.c new file mode 100644 index 00000000..943c2542 --- /dev/null +++ b/src/afb-fdev.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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 "afb-systemd.h" +#include "fdev.h" +#include "fdev-systemd.h" + +struct fdev *afb_fdev_create(int fd) +{ + return fdev_systemd_create(afb_systemd_get_event_loop(), fd); +} + diff --git a/src/afb-fdev.h b/src/afb-fdev.h new file mode 100644 index 00000000..0f29a9f3 --- /dev/null +++ b/src/afb-fdev.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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. + */ + +#pragma once + +struct fdev; + +extern struct fdev *afb_fdev_create(int fd); diff --git a/src/afb-proto-ws.c b/src/afb-proto-ws.c index ce7d75d3..56669e59 100644 --- a/src/afb-proto-ws.c +++ b/src/afb-proto-ws.c @@ -38,6 +38,7 @@ #include "afb-msg-json.h" #include "afb-proto-ws.h" #include "jobs.h" +#include "fdev.h" struct afb_proto_ws; @@ -161,7 +162,7 @@ struct afb_proto_ws int refcount; /* file descriptor */ - int fd; + struct fdev *fdev; /* resource control */ pthread_mutex_t mutex; @@ -1157,9 +1158,9 @@ static void on_hangup(void *closure) free(cd); } - if (protows->fd >= 0) { - close(protows->fd); - protows->fd = -1; + if (protows->fdev) { + fdev_unref(protows->fdev); + protows->fdev = 0; if (protows->on_hangup) protows->on_hangup(protows->closure); } @@ -1187,7 +1188,7 @@ static const struct afb_ws_itf server_ws_itf = /*****************************************************/ -static struct afb_proto_ws *afb_proto_ws_create(struct sd_event *eloop, int fd, const struct afb_proto_ws_server_itf *itfs, const struct afb_proto_ws_client_itf *itfc, void *closure, const struct afb_ws_itf *itf) +static struct afb_proto_ws *afb_proto_ws_create(struct fdev *fdev, const struct afb_proto_ws_server_itf *itfs, const struct afb_proto_ws_client_itf *itfc, void *closure, const struct afb_ws_itf *itf) { struct afb_proto_ws *protows; @@ -1195,11 +1196,11 @@ static struct afb_proto_ws *afb_proto_ws_create(struct sd_event *eloop, int fd, if (protows == NULL) errno = ENOMEM; else { - fcntl(fd, F_SETFD, FD_CLOEXEC); - fcntl(fd, F_SETFL, O_NONBLOCK); - protows->ws = afb_ws_create(eloop, fd, itf, protows); + fcntl(fdev_fd(fdev), F_SETFD, FD_CLOEXEC); + fcntl(fdev_fd(fdev), F_SETFL, O_NONBLOCK); + protows->ws = afb_ws_create(fdev, itf, protows); if (protows->ws != NULL) { - protows->fd = fd; + protows->fdev = fdev; protows->refcount = 1; protows->subcalls = NULL; protows->closure = closure; @@ -1213,14 +1214,14 @@ static struct afb_proto_ws *afb_proto_ws_create(struct sd_event *eloop, int fd, return NULL; } -struct afb_proto_ws *afb_proto_ws_create_client(struct sd_event *eloop, int fd, const struct afb_proto_ws_client_itf *itf, void *closure) +struct afb_proto_ws *afb_proto_ws_create_client(struct fdev *fdev, const struct afb_proto_ws_client_itf *itf, void *closure) { - return afb_proto_ws_create(eloop, fd, NULL, itf, closure, &proto_ws_client_ws_itf); + return afb_proto_ws_create(fdev, NULL, itf, closure, &proto_ws_client_ws_itf); } -struct afb_proto_ws *afb_proto_ws_create_server(struct sd_event *eloop, int fd, const struct afb_proto_ws_server_itf *itf, void *closure) +struct afb_proto_ws *afb_proto_ws_create_server(struct fdev *fdev, const struct afb_proto_ws_server_itf *itf, void *closure) { - return afb_proto_ws_create(eloop, fd, itf, NULL, closure, &server_ws_itf); + return afb_proto_ws_create(fdev, itf, NULL, closure, &server_ws_itf); } void afb_proto_ws_unref(struct afb_proto_ws *protows) diff --git a/src/afb-proto-ws.h b/src/afb-proto-ws.h index 103e37ab..cef7eadb 100644 --- a/src/afb-proto-ws.h +++ b/src/afb-proto-ws.h @@ -18,7 +18,7 @@ #pragma once -struct sd_event; +struct fdev; struct afb_proto_ws; struct afb_proto_ws_call; struct afb_proto_ws_subcall; @@ -47,8 +47,8 @@ struct afb_proto_ws_server_itf void (*on_describe)(void *closure, struct afb_proto_ws_describe *describe); }; -extern struct afb_proto_ws *afb_proto_ws_create_client(struct sd_event *eloop, int fd, const struct afb_proto_ws_client_itf *itf, void *closure); -extern struct afb_proto_ws *afb_proto_ws_create_server(struct sd_event *eloop, int fd, const struct afb_proto_ws_server_itf *itf, void *closure); +extern struct afb_proto_ws *afb_proto_ws_create_client(struct fdev *fdev, const struct afb_proto_ws_client_itf *itf, void *closure); +extern struct afb_proto_ws *afb_proto_ws_create_server(struct fdev *fdev, const struct afb_proto_ws_server_itf *itf, void *closure); extern void afb_proto_ws_unref(struct afb_proto_ws *protows); extern void afb_proto_ws_addref(struct afb_proto_ws *protows); diff --git a/src/afb-stub-ws.c b/src/afb-stub-ws.c index 0049c50c..37607ee0 100644 --- a/src/afb-stub-ws.c +++ b/src/afb-stub-ws.c @@ -36,8 +36,6 @@ #include -#include "afb-systemd.h" - #include "afb-session.h" #include "afb-cred.h" #include "afb-api.h" @@ -48,6 +46,7 @@ #include "afb-evt.h" #include "afb-xreq.h" #include "verbose.h" +#include "fdev.h" #include "jobs.h" struct afb_stub_ws; @@ -661,7 +660,7 @@ static void on_hangup(void *closure) /*****************************************************/ -static struct afb_stub_ws *afb_stub_ws_create(int fd, const char *apiname, struct afb_apiset *apiset, int client) +static struct afb_stub_ws *afb_stub_ws_create(struct fdev *fdev, const char *apiname, struct afb_apiset *apiset, int client) { struct afb_stub_ws *stubws; @@ -670,10 +669,11 @@ static struct afb_stub_ws *afb_stub_ws_create(int fd, const char *apiname, struc errno = ENOMEM; else { if (client) - stubws->proto = afb_proto_ws_create_client(afb_systemd_get_event_loop(), fd, &client_itf, stubws); + stubws->proto = afb_proto_ws_create_client(fdev, &client_itf, stubws); else - stubws->proto = afb_proto_ws_create_server(afb_systemd_get_event_loop(), fd, &server_itf, stubws); - if (stubws->proto != NULL) { + stubws->proto = afb_proto_ws_create_server(fdev, &server_itf, stubws); + + if (stubws->proto) { strcpy(stubws->apiname, apiname); stubws->apiset = afb_apiset_addref(apiset); stubws->refcount = 1; @@ -682,21 +682,22 @@ static struct afb_stub_ws *afb_stub_ws_create(int fd, const char *apiname, struc } free(stubws); } + fdev_unref(fdev); return NULL; } -struct afb_stub_ws *afb_stub_ws_create_client(int fd, const char *apiname, struct afb_apiset *apiset) +struct afb_stub_ws *afb_stub_ws_create_client(struct fdev *fdev, const char *apiname, struct afb_apiset *apiset) { - return afb_stub_ws_create(fd, apiname, apiset, 1); + return afb_stub_ws_create(fdev, apiname, apiset, 1); } -struct afb_stub_ws *afb_stub_ws_create_server(int fd, const char *apiname, struct afb_apiset *apiset) +struct afb_stub_ws *afb_stub_ws_create_server(struct fdev *fdev, const char *apiname, struct afb_apiset *apiset) { struct afb_stub_ws *stubws; - stubws = afb_stub_ws_create(fd, apiname, apiset, 0); + stubws = afb_stub_ws_create(fdev, apiname, apiset, 0); if (stubws) { - stubws->cred = afb_cred_create_for_socket(fd); + stubws->cred = afb_cred_create_for_socket(fdev_fd(fdev)); stubws->listener = afb_evt_listener_create(&server_evt_itf, stubws); if (stubws->listener != NULL) return stubws; diff --git a/src/afb-stub-ws.h b/src/afb-stub-ws.h index 4e07f982..3bb56feb 100644 --- a/src/afb-stub-ws.h +++ b/src/afb-stub-ws.h @@ -18,13 +18,14 @@ #pragma once +struct fdev; struct afb_stub_ws; struct afb_apiset; struct afb_api; -extern struct afb_stub_ws *afb_stub_ws_create_client(int fd, const char *apiname, struct afb_apiset *apiset); +extern struct afb_stub_ws *afb_stub_ws_create_client(struct fdev *fdev, const char *apiname, struct afb_apiset *apiset); -extern struct afb_stub_ws *afb_stub_ws_create_server(int fd, const char *apiname, struct afb_apiset *apiset); +extern struct afb_stub_ws *afb_stub_ws_create_server(struct fdev *fdev, const char *apiname, struct afb_apiset *apiset); extern void afb_stub_ws_unref(struct afb_stub_ws *stubws); diff --git a/src/afb-supervision.c b/src/afb-supervision.c index f41ca18b..0f8a16bb 100644 --- a/src/afb-supervision.c +++ b/src/afb-supervision.c @@ -43,6 +43,7 @@ #include "afs-supervision.h" #include "afb-stub-ws.h" #include "afb-debug.h" +#include "afb-fdev.h" #include "verbose.h" #include "wrap-json.h" #include "jobs.h" @@ -138,6 +139,7 @@ static void try_connect_supervisor() int fd; ssize_t srd; struct afs_supervision_initiator initiator; + struct fdev *fdev; /* get the mutex */ pthread_mutex_lock(&mutex); @@ -195,10 +197,15 @@ static void try_connect_supervisor() } /* make the supervisor link */ - supervisor = afb_stub_ws_create_server(fd, supervision_apiname, supervision_apiset); + fdev = afb_fdev_create(fd); + if (!fdev) { + ERROR("Creation of fdev failed: %m"); + goto end2; + } + supervisor = afb_stub_ws_create_server(fdev, supervision_apiname, supervision_apiset); if (!supervisor) { ERROR("Creation of supervisor failed: %m"); - goto end2; + goto end; } afb_stub_ws_on_hangup(supervisor, on_supervisor_hangup); diff --git a/src/afb-systemd.c b/src/afb-systemd.c index 575d9f1b..fe47967f 100644 --- a/src/afb-systemd.c +++ b/src/afb-systemd.c @@ -80,14 +80,14 @@ static char **fds_names() return names; } -int systemd_fds_init() +int afb_systemd_fds_init() { errno = 0; fds_names(); return -!!errno; } -int systemd_fds_for(const char *name) +int afb_systemd_fds_for(const char *name) { int idx; char **names; diff --git a/src/afb-systemd.h b/src/afb-systemd.h index ae8d61b0..bf0c6a9c 100644 --- a/src/afb-systemd.h +++ b/src/afb-systemd.h @@ -24,7 +24,7 @@ extern struct sd_event *afb_systemd_get_event_loop(); extern struct sd_bus *afb_systemd_get_user_bus(); extern struct sd_bus *afb_systemd_get_system_bus(); -extern int systemd_fds_init(); -extern int systemd_fds_for(const char *name); +extern int afb_systemd_fds_init(); +extern int afb_systemd_fds_for(const char *name); diff --git a/src/afb-websock.c b/src/afb-websock.c index 799b2bd0..e062cf77 100644 --- a/src/afb-websock.c +++ b/src/afb-websock.c @@ -30,6 +30,7 @@ #include "afb-hreq.h" #include "afb-websock.h" #include "afb-ws-json1.h" +#include "afb-fdev.h" /**************** WebSocket connection upgrade ****************************/ @@ -94,7 +95,7 @@ static int headerhas(const char *header, const char *needle) struct protodef { const char *name; - void *(*create)(int fd, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure); + void *(*create)(struct fdev *fdev, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure); }; static const struct protodef *search_proto(const struct protodef *protodefs, const char *protocols) @@ -142,11 +143,18 @@ static void upgrade_to_websocket( { struct memo_websocket *memo = cls; void *ws; + struct fdev *fdev; - ws = memo->proto->create(sock, memo->apiset, &memo->hreq->xreq.context, close_websocket, urh); - if (ws == NULL) { + fdev = afb_fdev_create(sock); + if (!fdev) { /* TODO */ close_websocket(urh); + } else { + ws = memo->proto->create(fdev, memo->apiset, &memo->hreq->xreq.context, close_websocket, urh); + if (ws == NULL) { + /* TODO */ + close_websocket(urh); + } } afb_hreq_unref(memo->hreq); free(memo); diff --git a/src/afb-ws-client.c b/src/afb-ws-client.c index e1d3277e..7e1a04f1 100644 --- a/src/afb-ws-client.c +++ b/src/afb-ws-client.c @@ -29,6 +29,7 @@ #include #include "afb-wsj1.h" +#include "fdev-systemd.h" /**************** WebSocket handshake ****************************/ @@ -320,6 +321,7 @@ struct afb_wsj1 *afb_ws_client_connect_wsj1(struct sd_event *eloop, const char * const char *path; struct addrinfo hint, *rai, *iai; struct afb_wsj1 *result; + struct fdev *fdev; /* scan the uri */ rc = parse_uri(uri, &host, &service, &path); @@ -354,10 +356,13 @@ struct afb_wsj1 *afb_ws_client_connect_wsj1(struct sd_event *eloop, const char * if (rc == 0) { rc = negociate(fd, proto_json1, path, xhost); if (rc == 0) { - result = afb_wsj1_create(eloop, fd, itf, closure); - if (result != NULL) { - fcntl(fd, F_SETFL, O_NONBLOCK); - break; + fdev = fdev_systemd_create(eloop, fd); + if (fdev) { + result = afb_wsj1_create(fdev, itf, closure); + if (result != NULL) { + fcntl(fd, F_SETFL, O_NONBLOCK); + break; + } } } } @@ -505,16 +510,18 @@ struct afb_proto_ws *afb_ws_client_connect_api(struct sd_event *eloop, const cha { int fd; struct afb_proto_ws *pws; + struct fdev *fdev; fd = get_socket(uri); if (fd >= 0) { - pws = afb_proto_ws_create_client(eloop, fd, itf, closure); - if (pws) - return pws; + fdev = fdev_systemd_create(eloop, fd); + if (fdev) { + pws = afb_proto_ws_create_client(fdev, itf, closure); + if (pws) + return pws; + } close(fd); } return NULL; } - - diff --git a/src/afb-ws-json1.c b/src/afb-ws-json1.c index 0fa13dfd..68565aa2 100644 --- a/src/afb-ws-json1.c +++ b/src/afb-ws-json1.c @@ -36,6 +36,7 @@ #include "afb-context.h" #include "afb-evt.h" #include "verbose.h" +#include "fdev.h" /* predeclaration of structures */ struct afb_ws_json1; @@ -99,11 +100,11 @@ static const struct afb_evt_itf evt_itf = { **************************************************************** ***************************************************************/ -struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure) +struct afb_ws_json1 *afb_ws_json1_create(struct fdev *fdev, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure) { struct afb_ws_json1 *result; - assert(fd >= 0); + assert(fdev); assert(context != NULL); result = malloc(sizeof * result); @@ -118,7 +119,7 @@ struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_apiset *apiset, stru if (result->session == NULL) goto error2; - result->wsj1 = afb_wsj1_create(afb_systemd_get_event_loop(), fd, &wsj1_itf, result); + result->wsj1 = afb_wsj1_create(fdev, &wsj1_itf, result); if (result->wsj1 == NULL) goto error3; @@ -126,7 +127,7 @@ struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_apiset *apiset, stru if (result->listener == NULL) goto error4; - result->cred = afb_cred_create_for_socket(fd); + result->cred = afb_cred_create_for_socket(fdev_fd(fdev)); result->apiset = afb_apiset_addref(apiset); return result; @@ -137,7 +138,7 @@ error3: error2: free(result); error: - close(fd); + fdev_unref(fdev); return NULL; } diff --git a/src/afb-ws-json1.h b/src/afb-ws-json1.h index 96db1bc6..024dd8c0 100644 --- a/src/afb-ws-json1.h +++ b/src/afb-ws-json1.h @@ -20,8 +20,9 @@ struct afb_ws_json1; struct afb_context; struct afb_apiset; +struct fdev; -extern struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *closure); +extern struct afb_ws_json1 *afb_ws_json1_create(struct fdev *fdev, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *closure); extern struct afb_ws_json1 *afb_ws_json1_addref(struct afb_ws_json1 *ws); extern void afb_ws_json1_unref(struct afb_ws_json1 *ws); diff --git a/src/afb-ws.c b/src/afb-ws.c index c48a5e24..ef73addd 100644 --- a/src/afb-ws.c +++ b/src/afb-ws.c @@ -26,12 +26,9 @@ #include #include -#include - #include "websock.h" #include "afb-ws.h" - -#include "afb-common.h" +#include "fdev.h" /* * declaration of the websock interface for afb-ws @@ -89,7 +86,7 @@ struct afb_ws const struct afb_ws_itf *itf; /* the callback interface */ void *closure; /* closure when calling the callbacks */ struct websock *ws; /* the websock handler */ - sd_event_source *evsrc; /* the event source for the socket */ + struct fdev *fdev; /* the fdev for the socket */ struct buf buffer; /* the last read fragment */ }; @@ -123,8 +120,7 @@ static void aws_disconnect(struct afb_ws *ws, int call_on_hangup) struct websock *wsi = ws->ws; if (wsi != NULL) { ws->ws = NULL; - sd_event_source_unref(ws->evsrc); - ws->evsrc = NULL; + fdev_unref(ws->fdev); websock_destroy(wsi); free(ws->buffer.buffer); ws->state = waiting; @@ -133,13 +129,12 @@ static void aws_disconnect(struct afb_ws *ws, int call_on_hangup) } } -static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, void *ws) +static void fdevcb(void *ws, uint32_t revents, struct fdev *fdev) { if ((revents & EPOLLIN) != 0) aws_on_readable(ws); if ((revents & EPOLLHUP) != 0) afb_ws_hangup(ws); - return 0; } /* @@ -151,12 +146,11 @@ static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, voi * * Returns the handle for the afb_ws created or NULL on error. */ -struct afb_ws *afb_ws_create(struct sd_event *eloop, int fd, const struct afb_ws_itf *itf, void *closure) +struct afb_ws *afb_ws_create(struct fdev *fdev, const struct afb_ws_itf *itf, void *closure) { - int rc; struct afb_ws *result; - assert(fd >= 0); + assert(fdev); /* allocation */ result = malloc(sizeof * result); @@ -164,7 +158,8 @@ struct afb_ws *afb_ws_create(struct sd_event *eloop, int fd, const struct afb_ws goto error; /* init */ - result->fd = fd; + result->fdev = fdev; + result->fd = fdev_fd(fdev); result->state = waiting; result->itf = itf; result->closure = closure; @@ -176,19 +171,15 @@ struct afb_ws *afb_ws_create(struct sd_event *eloop, int fd, const struct afb_ws if (result->ws == NULL) goto error2; - /* creates the evsrc */ - rc = sd_event_add_io(eloop, &result->evsrc, result->fd, EPOLLIN, io_event_callback, result); - if (rc < 0) { - errno = -rc; - goto error3; - } + /* finalize */ + fdev_set_events(fdev, EPOLLIN); + fdev_set_callback(fdev, fdevcb, result); return result; -error3: - websock_destroy(result->ws); error2: free(result); error: + fdev_unref(fdev); return NULL; } diff --git a/src/afb-ws.h b/src/afb-ws.h index 4b26f0b9..0af236ef 100644 --- a/src/afb-ws.h +++ b/src/afb-ws.h @@ -18,7 +18,7 @@ #pragma once struct afb_ws; -struct sd_event; +struct fdev; struct iovec; struct afb_ws_itf @@ -30,7 +30,7 @@ struct afb_ws_itf void (*on_hangup) (void *); /* optional, it is safe too call afb_ws_destroy within the callback */ }; -extern struct afb_ws *afb_ws_create(struct sd_event *eloop, int fd, const struct afb_ws_itf *itf, void *closure); +extern struct afb_ws *afb_ws_create(struct fdev *fdev, const struct afb_ws_itf *itf, void *closure); extern void afb_ws_destroy(struct afb_ws *ws); extern void afb_ws_hangup(struct afb_ws *ws); extern int afb_ws_is_connected(struct afb_ws *ws); diff --git a/src/afb-wsj1.c b/src/afb-wsj1.c index be961f51..3876c322 100644 --- a/src/afb-wsj1.c +++ b/src/afb-wsj1.c @@ -29,6 +29,7 @@ #include "afb-ws.h" #include "afb-wsj1.h" +#include "fdev.h" #define CALL 2 #define RETOK 3 @@ -81,12 +82,11 @@ struct afb_wsj1 pthread_mutex_t mutex; }; -struct afb_wsj1 *afb_wsj1_create(struct sd_event *eloop, int fd, struct afb_wsj1_itf *itf, void *closure) +struct afb_wsj1 *afb_wsj1_create(struct fdev *fdev, struct afb_wsj1_itf *itf, void *closure) { struct afb_wsj1 *result; - assert(eloop); - assert(fd >= 0); + assert(fdev); assert(itf); assert(itf->on_call); @@ -103,7 +103,7 @@ struct afb_wsj1 *afb_wsj1_create(struct sd_event *eloop, int fd, struct afb_wsj1 if (result->tokener == NULL) goto error2; - result->ws = afb_ws_create(eloop, fd, &wsj1_itf, result); + result->ws = afb_ws_create(fdev, &wsj1_itf, result); if (result->ws == NULL) goto error3; @@ -114,7 +114,7 @@ error3: error2: free(result); error: - close(fd); + fdev_unref(fdev); return NULL; } diff --git a/src/afb-wsj1.h b/src/afb-wsj1.h index 7e318f6f..86c7cfdb 100644 --- a/src/afb-wsj1.h +++ b/src/afb-wsj1.h @@ -21,7 +21,7 @@ struct afb_wsj1; struct afb_wsj1_msg; struct json_object; -struct sd_event; +struct fdev; /* * Interface for callback functions. @@ -48,13 +48,11 @@ struct afb_wsj1_itf { }; /* - * Creates the afb_wsj1 socket connected to the file descriptor 'fd' + * Creates the afb_wsj1 socket connected to the file descriptor 'fdev' * and having the callback interface defined by 'itf' for the 'closure'. - * When the creation is a success, the systemd event loop 'eloop' is - * used for handling event for 'fd'. * Returns the created wsj1 websocket or NULL in case of error. */ -extern struct afb_wsj1 *afb_wsj1_create(struct sd_event *eloop, int fd, struct afb_wsj1_itf *itf, void *closure); +extern struct afb_wsj1 *afb_wsj1_create(struct fdev *fdev, struct afb_wsj1_itf *itf, void *closure); /* * Increases by one the count of reference to 'wsj1' diff --git a/src/afs-supervisor.c b/src/afs-supervisor.c index be0cdbf5..1e92b9f3 100644 --- a/src/afs-supervisor.c +++ b/src/afs-supervisor.c @@ -45,6 +45,7 @@ #include "afb-api-so-v2.h" #include "afb-api-ws.h" #include "afb-apiset.h" +#include "afb-fdev.h" #include "jobs.h" #include "verbose.h" #include "wrap-json.h" @@ -193,13 +194,20 @@ static void on_supervised_hangup(struct afb_stub_ws *stub) static int make_supervised(int fd, struct afb_cred *cred) { struct supervised *s; + struct fdev *fdev; s = malloc(sizeof *s); if (!s) return -1; + fdev = afb_fdev_create(fd); + if (!fdev) { + free(s); + return -1; + } + s->cred = cred; - s->stub = afb_stub_ws_create_client(fd, supervision_apiname, empty_apiset); + s->stub = afb_stub_ws_create_client(fdev, supervision_apiname, empty_apiset); if (!s->stub) { free(s); return -1; diff --git a/src/fdev-epoll.c b/src/fdev-epoll.c new file mode 100644 index 00000000..80c037d0 --- /dev/null +++ b/src/fdev-epoll.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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 +#include +#include + +#define FDEV_PROVIDER +#include "fdev.h" +#include "fdev-epoll.h" + +#define epollfd(fdev_epoll) ((int)(intptr_t)fdev_epoll) + +static void disable(void *closure, const struct fdev *fdev) +{ + struct fdev_epoll *fdev_epoll = closure; + epoll_ctl(epollfd(fdev_epoll), EPOLL_CTL_DEL, fdev_fd(fdev), 0); +} + +static void enable(void *closure, const struct fdev *fdev) +{ + struct fdev_epoll *fdev_epoll = closure; + struct epoll_event event; + int rc, fd; + + fd = fdev_fd(fdev); + event.events = fdev_events(fdev); + event.data.ptr = (void*)fdev; + rc = epoll_ctl(epollfd(fdev_epoll), EPOLL_CTL_MOD, fd, &event); + if (rc < 0 && errno == ENOENT) + epoll_ctl(epollfd(fdev_epoll), EPOLL_CTL_ADD, fd, &event); +} + +static struct fdev_itf itf = +{ + .unref = 0, + .disable = disable, + .enable = enable +}; + +struct fdev_epoll *fdev_epoll_create() +{ + int fd = epoll_create1(EPOLL_CLOEXEC); + if (!fd) { + fd = dup(fd); + close(0); + } + return fd < 0 ? 0 : (struct fdev_epoll*)(intptr_t)fd; +} + +void fdev_epoll_destroy(struct fdev_epoll *fdev_epoll) +{ + close(epollfd(fdev_epoll)); +} + +int fdev_epoll_fd(struct fdev_epoll *fdev_epoll) +{ + return epollfd(fdev_epoll); +} + +struct fdev *fdev_epoll_add(struct fdev_epoll *fdev_epoll, int fd) +{ + struct fdev *fdev; + + fdev = fdev_create(fd); + if (fdev) + fdev_set_itf(fdev, &itf, fdev_epoll); + return fdev; +} + +void fdev_epoll_wait_and_dispatch(struct fdev_epoll *fdev_epoll, int timeout_ms) +{ + struct fdev *fdev; + struct epoll_event events[8]; + int rc, i; + + rc = epoll_wait(epollfd(fdev_epoll), events, sizeof events / sizeof *events, timeout_ms < 0 ? -1 : timeout_ms); + for (i = 0 ; i < rc ; i++) { + fdev = events[i].data.ptr; + fdev_dispatch(fdev, events[i].events); + } +} + diff --git a/src/fdev-epoll.h b/src/fdev-epoll.h new file mode 100644 index 00000000..53d02e72 --- /dev/null +++ b/src/fdev-epoll.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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. + */ + +#pragma once + +struct fdev; +struct fdev_epoll; + +extern struct fdev_epoll *fdev_epoll_create(); +extern void fdev_epoll_destroy(struct fdev_epoll *fdev_epoll); +extern int fdev_epoll_fd(struct fdev_epoll *fdev_epoll); +extern struct fdev *fdev_epoll_add(struct fdev_epoll *fdev_epoll, int fd); +extern void fdev_epoll_wait_and_dispatch(struct fdev_epoll *fdev_epoll, int timeout_ms); + diff --git a/src/fdev-systemd.c b/src/fdev-systemd.c new file mode 100644 index 00000000..0f4a03cd --- /dev/null +++ b/src/fdev-systemd.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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 + +#include + +#define FDEV_PROVIDER +#include "fdev.h" +#include "fdev-systemd.h" + +static int handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) +{ + struct fdev *fdev = userdata; + fdev_dispatch(fdev, revents); + return 0; +} + +static void unref(void *closure) +{ + sd_event_source *source = closure; + sd_event_source_unref(source); +} + +static void disable(void *closure, const struct fdev *fdev) +{ + sd_event_source *source = closure; + sd_event_source_set_enabled(source, SD_EVENT_OFF); +} + +static void enable(void *closure, const struct fdev *fdev) +{ + sd_event_source *source = closure; + sd_event_source_set_io_events(source, fdev_events(fdev)); + sd_event_source_set_enabled(source, SD_EVENT_ON); +} + +static struct fdev_itf itf = +{ + .unref = unref, + .disable = disable, + .enable = enable +}; + +struct fdev *fdev_systemd_create(struct sd_event *eloop, int fd) +{ + int rc; + sd_event_source *source; + struct fdev *fdev; + + fdev = fdev_create(fd); + if (fdev) { + rc = sd_event_add_io(eloop, &source, fd, 0, handler, fdev); + if (rc < 0) { + fdev_unref(fdev); + fdev = 0; + errno = -rc; + } else { + sd_event_source_set_enabled(source, SD_EVENT_OFF); + fdev_set_itf(fdev, &itf, source); + } + } + return fdev; +} + diff --git a/src/fdev-systemd.h b/src/fdev-systemd.h new file mode 100644 index 00000000..99ea5f1f --- /dev/null +++ b/src/fdev-systemd.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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. + */ + +#pragma once + +struct fdev; +struct sd_event; + +extern struct fdev *fdev_systemd_create(struct sd_event *eloop, int fd); diff --git a/src/fdev.c b/src/fdev.c new file mode 100644 index 00000000..5c31d31b --- /dev/null +++ b/src/fdev.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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 +#include +#include +#include + +#define FDEV_PROVIDER +#include "fdev.h" + +struct fdev +{ + int fd; + uint32_t events; + int repeat; + unsigned refcount; + struct fdev_itf *itf; + void *closure_itf; + void (*callback)(void*,uint32_t,struct fdev*); + void *closure_callback; +}; + +struct fdev *fdev_create(int fd) +{ + struct fdev *fdev; + + fdev = calloc(1, sizeof *fdev); + if (!fdev) + errno = ENOMEM; + else { + fdev->fd = fd; + fdev->refcount = 3; /* set autoclose by default */ + fdev->repeat = -1; + } + return fdev; +} + +void fdev_set_itf(struct fdev *fdev, struct fdev_itf *itf, void *closure_itf) +{ + fdev->itf = itf; + fdev->closure_itf = closure_itf; +} + +void fdev_dispatch(struct fdev *fdev, uint32_t events) +{ + if (fdev->repeat > 0 && !--fdev->repeat && fdev->itf) + fdev->itf->disable(fdev->closure_itf, fdev); + if (fdev->callback) + fdev->callback(fdev->closure_callback, events, fdev); +} + +struct fdev *fdev_addref(struct fdev *fdev) +{ + if (fdev) + __atomic_add_fetch(&fdev->refcount, 2, __ATOMIC_RELAXED); + return fdev; +} + +void fdev_unref(struct fdev *fdev) +{ + if (fdev && __atomic_sub_fetch(&fdev->refcount, 2, __ATOMIC_RELAXED) <= 1) { + if (fdev->itf) { + fdev->itf->disable(fdev->closure_itf, fdev); + fdev->itf->unref(fdev->closure_itf); + } + if (fdev->refcount) + close(fdev->fd); + free(fdev); + } +} + +int fdev_fd(const struct fdev *fdev) +{ + return fdev->fd; +} + +uint32_t fdev_events(const struct fdev *fdev) +{ + return fdev->events; +} + +int fdev_repeat(const struct fdev *fdev) +{ + return fdev->repeat; +} + +int fdev_autoclose(const struct fdev *fdev) +{ + return 1 & fdev->refcount; +} + +static inline int is_active(struct fdev *fdev) +{ + return fdev->repeat && fdev->callback; +} + +static inline void update_activity(struct fdev *fdev, int old_active) +{ + if (is_active(fdev)) { + if (!old_active) + fdev->itf->enable(fdev->closure_itf, fdev); + } else { + if (old_active) + fdev->itf->disable(fdev->closure_itf, fdev); + } +} + +void fdev_set_callback(struct fdev *fdev, void (*callback)(void*,uint32_t,struct fdev*), void *closure) +{ + int oa; + + oa = is_active(fdev); + fdev->callback = callback; + fdev->closure_callback = closure; + update_activity(fdev, oa); +} + +void fdev_set_events(struct fdev *fdev, uint32_t events) +{ + if (events != fdev->events) { + fdev->events = events; + if (is_active(fdev)) + fdev->itf->enable(fdev->closure_itf, fdev); + } +} + +void fdev_set_repeat(struct fdev *fdev, int count) +{ + int oa; + + oa = is_active(fdev); + fdev->repeat = count; + update_activity(fdev, oa); +} + +void fdev_set_autoclose(struct fdev *fdev, int autoclose) +{ + if (autoclose) + fdev->refcount |= 1; + else + fdev->refcount &= -2; +} + diff --git a/src/fdev.h b/src/fdev.h new file mode 100644 index 00000000..9fbcb759 --- /dev/null +++ b/src/fdev.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * 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. + */ + +#pragma once + +#include + +struct fdev; + +#if defined(FDEV_PROVIDER) +struct fdev_itf +{ + void (*unref)(void *closure); + void (*disable)(void *closure, const struct fdev *fdev); + void (*enable)(void *closure, const struct fdev *fdev); +}; + +extern struct fdev *fdev_create(int fd); +extern void fdev_set_itf(struct fdev *fdev, struct fdev_itf *itf, void *closure_itf); +extern void fdev_dispatch(struct fdev *fdev, uint32_t events); +#endif + +extern struct fdev *fdev_addref(struct fdev *fdev); +extern void fdev_unref(struct fdev *fdev); + +extern int fdev_fd(const struct fdev *fdev); +extern uint32_t fdev_events(const struct fdev *fdev); +extern int fdev_repeat(const struct fdev *fdev); +extern int fdev_autoclose(const struct fdev *fdev); + +extern void fdev_set_callback(struct fdev *fdev, void (*callback)(void*,uint32_t,struct fdev*), void *closure); +extern void fdev_set_events(struct fdev *fdev, uint32_t events); +extern void fdev_set_repeat(struct fdev *fdev, int count); +extern void fdev_set_autoclose(struct fdev *fdev, int autoclose); -- cgit 1.2.3-korg