X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafb-socket.c;fp=src%2Fafb-socket.c;h=c6402d42ac3d3bb45cbe36193f1985564668151a;hb=10aa15afc5bc7321d0049823dd173dda11ff3724;hp=0000000000000000000000000000000000000000;hpb=df67c029363f6d42a3e91cd62774c74b3ef66670;p=src%2Fapp-framework-binder.git diff --git a/src/afb-socket.c b/src/afb-socket.c new file mode 100644 index 00000000..c6402d42 --- /dev/null +++ b/src/afb-socket.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2015-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. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "afb-fdev.h" +#include "afb-socket.h" +#include "afb-systemd.h" +#include "fdev.h" +#include "verbose.h" + +#define BACKLOG 5 + +/******************************************************************************/ + +static int open_unix(const char *spec, int server) +{ + int fd, rc, abstract; + struct sockaddr_un addr; + size_t length; + + abstract = spec[0] == '@'; + + /* check the length */ + length = strlen(spec); + if (length >= 108) { + errno = ENAMETOOLONG; + return -1; + } + + /* remove the file on need */ + if (server && !abstract) + unlink(spec); + + /* create a socket */ + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return fd; + + /* prepare address */ + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, spec); + if (abstract) + addr.sun_path[0] = 0; /* implement abstract sockets */ + + if (server) { + rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); + } else { + rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr)); + } + if (rc < 0) { + close(fd); + return rc; + } + return fd; +} + +static int open_inet(const char *spec, int server) +{ + int rc, fd; + const char *service, *host, *api; + struct addrinfo hint, *rai, *iai; + + /* scan the uri */ + api = strrchr(spec, '/'); + service = strrchr(spec, ':'); + if (api == NULL || service == NULL || api < service) { + errno = EINVAL; + return -1; + } + host = strndupa(spec, service++ - spec); + service = strndupa(service, api - service); + + /* get addr */ + memset(&hint, 0, sizeof hint); + hint.ai_family = AF_INET; + hint.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(host, service, &hint, &rai); + if (rc != 0) { + errno = EINVAL; + return -1; + } + + /* get the socket */ + iai = rai; + while (iai != NULL) { + fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); + if (fd >= 0) { + if (server) { + rc = bind(fd, iai->ai_addr, iai->ai_addrlen); + } else { + rc = connect(fd, iai->ai_addr, iai->ai_addrlen); + } + if (rc == 0) { + freeaddrinfo(rai); + return fd; + } + close(fd); + } + iai = iai->ai_next; + } + freeaddrinfo(rai); + return -1; +} + +static int open_systemd(const char *spec) +{ +#if defined(NO_SYSTEMD_ACTIVATION) + errno = EAFNOSUPPORT; + fd = -1; +#else + return afb_systemd_fds_for(spec); +#endif +} + +/******************************************************************************/ + +enum type { + Type_Inet, + Type_Systemd, + Type_Unix +}; + +struct entry +{ + const char *prefix; + unsigned type: 2; + unsigned noreuseaddr: 1; + unsigned nolisten: 1; +}; + +static struct entry entries[] = { /* default at first place */ + { + .prefix = "tcp:", + .type = Type_Inet + }, + { + .prefix = "sd:", + .type = Type_Systemd, + .noreuseaddr = 1, + .nolisten = 1 + }, + { + .prefix = "unix:", + .type = Type_Unix + } +}; + +/******************************************************************************/ + +/* get the entry of the uri by searching to its prefix */ +static struct entry *get_entry(const char *uri, int *offset) +{ + int l, search = 1, i = (int)(sizeof entries / sizeof * entries); + + while (search) { + if (!i) { + l = 0; + search = 0; + } else { + i--; + l = (int)strlen(entries[i].prefix); + search = strncmp(uri, entries[i].prefix, l); + } + } + + *offset = l; + return &entries[i]; +} + +static int open_any(const char *uri, int server) +{ + int fd, rc, offset; + struct entry *e; + + /* search for the entry */ + e = get_entry(uri, &offset); + + /* get the names */ + + uri += offset; + + /* open the socket */ + switch (e->type) { + case Type_Unix: + fd = open_unix(uri, server); + break; + case Type_Inet: + fd = open_inet(uri, server); + break; + case Type_Systemd: + fd = open_systemd(uri); + break; + default: + errno = EAFNOSUPPORT; + fd = -1; + break; + } + if (fd < 0) + return -1; + + /* set it up */ + fcntl(fd, F_SETFD, FD_CLOEXEC); + fcntl(fd, F_SETFL, O_NONBLOCK); + if (server) { + if (!e->noreuseaddr) { + rc = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc); + } + if (!e->nolisten) + listen(fd, BACKLOG); + } + return fd; +} + +struct fdev *afb_socket_open(const char *uri, int server) +{ + int fd; + struct fdev *fdev; + + fd = open_any(uri, server); + if (fd < 0) + goto error; + + fdev = afb_fdev_create(fd); + if (!fdev) + goto error2; + + return fdev; + +error2: + close(fd); +error: + ERROR("can't make %s socket for %s", server ? "server" : "client", uri); + return NULL; +} +