X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafb-socket.c;h=73efaaca575b4af9e09a4c54c23ea15c83c749ce;hb=65353dce81a629e042800bb7b86fcd869a76727e;hp=c6402d42ac3d3bb45cbe36193f1985564668151a;hpb=10aa15afc5bc7321d0049823dd173dda11ff3724;p=src%2Fapp-framework-binder.git diff --git a/src/afb-socket.c b/src/afb-socket.c index c6402d42..73efaaca 100644 --- a/src/afb-socket.c +++ b/src/afb-socket.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2018 "IoT.bzh" + * Copyright (C) 2015-2020 "IoT.bzh" * Author José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,7 +31,8 @@ #include "afb-fdev.h" #include "afb-socket.h" -#include "afb-systemd.h" + +#include "systemd.h" #include "fdev.h" #include "verbose.h" @@ -39,6 +40,74 @@ /******************************************************************************/ +/** + * known types + */ +enum type { + /** type internet */ + Type_Inet, + + /** type systemd */ + Type_Systemd, + + /** type Unix */ + Type_Unix +}; + +/** + * Structure for known entries + */ +struct entry +{ + /** the known prefix */ + const char *prefix; + + /** the type of the entry */ + unsigned type: 2; + + /** should not set SO_REUSEADDR for servers */ + unsigned noreuseaddr: 1; + + /** should not call listen for servers */ + unsigned nolisten: 1; +}; + +/** + * The known entries with the default one at the first place + */ +static struct entry entries[] = { + { + .prefix = "tcp:", + .type = Type_Inet + }, + { + .prefix = "sd:", + .type = Type_Systemd, + .noreuseaddr = 1, + .nolisten = 1 + }, + { + .prefix = "unix:", + .type = Type_Unix + } +}; + +/** + * It is possible to set explicit api name instead of using the + * default one. + */ +static const char as_api[] = "?as-api="; + +/******************************************************************************/ + +/** + * open a unix domain socket for client or server + * + * @param spec the specification of the path (prefix with @ for abstract) + * @param server 0 for client, server otherwise + * + * @return the file descriptor number of the socket or -1 in case of error + */ static int open_unix(const char *spec, int server) { int fd, rc, abstract; @@ -82,26 +151,39 @@ static int open_unix(const char *spec, int server) return fd; } -static int open_inet(const char *spec, int server) +/** + * open a tcp socket for client or server + * + * @param spec the specification of the host:port/... + * @param server 0 for client, server otherwise + * + * @return the file descriptor number of the socket or -1 in case of error + */ +static int open_tcp(const char *spec, int server, int reuseaddr) { int rc, fd; - const char *service, *host, *api; + const char *service, *host, *tail; struct addrinfo hint, *rai, *iai; /* scan the uri */ - api = strrchr(spec, '/'); - service = strrchr(spec, ':'); - if (api == NULL || service == NULL || api < service) { + tail = strchrnul(spec, '/'); + service = strchr(spec, ':'); + if (tail == NULL || service == NULL || tail < service) { errno = EINVAL; return -1; } host = strndupa(spec, service++ - spec); - service = strndupa(service, api - service); + service = strndupa(service, tail - service); /* get addr */ memset(&hint, 0, sizeof hint); hint.ai_family = AF_INET; hint.ai_socktype = SOCK_STREAM; + if (server) { + hint.ai_flags = AI_PASSIVE; + if (host[0] == 0 || (host[0] == '*' && host[1] == 0)) + host = NULL; + } rc = getaddrinfo(host, service, &hint, &rai); if (rc != 0) { errno = EINVAL; @@ -114,6 +196,10 @@ static int open_inet(const char *spec, int server) fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); if (fd >= 0) { if (server) { + if (reuseaddr) { + rc = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc); + } rc = bind(fd, iai->ai_addr, iai->ai_addrlen); } else { rc = connect(fd, iai->ai_addr, iai->ai_addrlen); @@ -130,82 +216,91 @@ static int open_inet(const char *spec, int server) return -1; } +/** + * open a systemd socket for server + * + * @param spec the specification of the systemd name + * + * @return the file descriptor number of the socket or -1 in case of error + */ static int open_systemd(const char *spec) { #if defined(NO_SYSTEMD_ACTIVATION) errno = EAFNOSUPPORT; - fd = -1; + return -1; #else - return afb_systemd_fds_for(spec); + return systemd_fds_for(spec); #endif } /******************************************************************************/ -enum type { - Type_Inet, - Type_Systemd, - Type_Unix -}; - -struct entry +/** + * Get the entry of the uri by searching to its prefix + * + * @param uri the searched uri + * @param offset where to store the prefix length + * @param scheme the default scheme to use if none is set in uri (can be NULL) + * + * @return the found entry or the default one + */ +static struct entry *get_entry(const char *uri, int *offset, const char *scheme) { - 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 + int len, i, deflen; + + /* search as prefix of URI */ + i = (int)(sizeof entries / sizeof * entries); + while (i) { + i--; + len = (int)strlen(entries[i].prefix); + if (!strncmp(uri, entries[i].prefix, len)) + goto end; /* found */ } -}; - -/******************************************************************************/ -/* 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); + /* not a prefix of uri */ + len = 0; - while (search) { - if (!i) { - l = 0; - search = 0; - } else { + /* search default scheme if given and valid */ + if (scheme && *scheme) { + deflen = (int)strlen(scheme); + deflen += (scheme[deflen - 1] != ':'); /* add virtual trailing colon */ + i = (int)(sizeof entries / sizeof * entries); + while (i) { i--; - l = (int)strlen(entries[i].prefix); - search = strncmp(uri, entries[i].prefix, l); + if (deflen == (int)strlen(entries[i].prefix) + && !strncmp(scheme, entries[i].prefix, deflen - 1)) + goto end; /* found */ } } - *offset = l; +end: + *offset = len; return &entries[i]; } -static int open_any(const char *uri, int server) +/** + * open socket for client or server + * + * @param uri the specification of the socket + * @param server 0 for client, server otherwise + * @param scheme the default scheme to use if none is set in uri (can be NULL) + * + * @return the file descriptor number of the socket or -1 in case of error + */ +static int open_uri(const char *uri, int server, const char *scheme) { - int fd, rc, offset; + int fd, offset; struct entry *e; + const char *api; /* search for the entry */ - e = get_entry(uri, &offset); + e = get_entry(uri, &offset, scheme); /* get the names */ - uri += offset; + api = strstr(uri, as_api); + if (api) + uri = strndupa(uri, api - uri); /* open the socket */ switch (e->type) { @@ -213,10 +308,15 @@ static int open_any(const char *uri, int server) fd = open_unix(uri, server); break; case Type_Inet: - fd = open_inet(uri, server); + fd = open_tcp(uri, server, !e->noreuseaddr); break; case Type_Systemd: - fd = open_systemd(uri); + if (server) + fd = open_systemd(uri); + else { + errno = EINVAL; + fd = -1; + } break; default: errno = EAFNOSUPPORT; @@ -230,35 +330,83 @@ static int open_any(const char *uri, int server) 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) +/** + * open socket for client or server + * + * @param uri the specification of the socket + * @param server 0 for client, server otherwise + * @param scheme the default scheme to use if none is set in uri (can be NULL) + * + * @return the file descriptor number of the socket or -1 in case of error + */ +int afb_socket_open_scheme(const char *uri, int server, const char *scheme) { - int fd; - struct fdev *fdev; - - fd = open_any(uri, server); + int fd = open_uri(uri, server, scheme); if (fd < 0) - goto error; + ERROR("can't open %s socket for %s", server ? "server" : "client", uri); + return fd; +} - fdev = afb_fdev_create(fd); - if (!fdev) - goto error2; +/** + * open socket for client or server + * + * @param uri the specification of the socket + * @param server 0 for client, server otherwise + * @param scheme the default scheme to use if none is set in uri (can be NULL) + * + * @return the fdev of the socket or NULL in case of error + */ +struct fdev *afb_socket_open_fdev_scheme(const char *uri, int server, const char *scheme) +{ + struct fdev *fdev; + int fd; + fd = afb_socket_open_scheme(uri, server, scheme); + if (fd < 0) + fdev = NULL; + else { + fdev = afb_fdev_create(fd); + if (!fdev) { + close(fd); + ERROR("can't make %s socket for %s", server ? "server" : "client", uri); + } + } return fdev; - -error2: - close(fd); -error: - ERROR("can't make %s socket for %s", server ? "server" : "client", uri); - return NULL; } +/** + * Get the api name of the uri + * + * @param uri the specification of the socket + * + * @return the api name or NULL if none can be deduced + */ +const char *afb_socket_api(const char *uri) +{ + int offset; + const char *api; + struct entry *entry; + + entry = get_entry(uri, &offset, NULL); + uri += offset; + uri += (entry->type == Type_Unix && *uri == '@'); + api = strstr(uri, as_api); + if (api) + api += sizeof as_api - 1; + else { + api = strrchr(uri, '/'); + if (api) + api++; + else + api = uri; + if (strchr(api, ':')) + api = NULL; + } + return api; +}