void *userdata) {
int r;
+ SocketPort *p;
+ Socket *s;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((r = address_parse(data, rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue);
- return r;
+ s = (Socket*) data;
+
+ if (!(p = new0(SocketPort, 1)))
+ return -ENOMEM;
+
+ if (streq(lvalue, "ListenFIFO")) {
+ p->type = SOCKET_FIFO;
+
+ if (!(p->path = strdup(rvalue))) {
+ free(p);
+ return -ENOMEM;
+ }
+ } else {
+ p->type = SOCKET_SOCKET;
+
+ if ((r = socket_address_parse(&p->address, rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue);
+ free(p);
+ return r;
+ }
+
+ if (streq(lvalue, "ListenStream"))
+ p->address.type = SOCK_STREAM;
+ else if (streq(lvalue, "ListenDatagram"))
+ p->address.type = SOCK_DGRAM;
+ else {
+ assert(streq(lvalue, "ListenSequentialPacket"));
+ p->address.type = SOCK_SEQPACKET;
+ }
+
+ if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
+ free(p);
+ return -EPROTONOSUPPORT;
+ }
}
+ p->fd = -1;
+ LIST_PREPEND(SocketPort, s->ports, p);
+
return 0;
}
-static int config_parse_type(
+static int config_parse_bind(
const char *filename,
unsigned line,
const char *section,
void *data,
void *userdata) {
- int *type = data;
+ int r;
+ Socket *s;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (streq(rvalue, "stream"))
- *type = SOCK_STREAM;
- else if (streq(rvalue, "dgram"))
- *type = SOCK_DGRAM;
- else {
- log_error("[%s:%u] Failed to parse socket type value: %s", filename, line, rvalue);
- return -EINVAL;
+ s = (Socket*) data;
+
+ if ((r = parse_boolean(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse bind IPv6 only value: %s", filename, line, rvalue);
+ return r;
}
+ s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
+
return 0;
}
};
const ConfigItem items[] = {
- { "Names", config_parse_names, &n->meta.names, "Meta" },
- { "Description", config_parse_string, &n->meta.description, "Meta" },
- { "Requires", config_parse_deps, n->meta.dependencies+NAME_REQUIRES, "Meta" },
- { "SoftRequires", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUIRES, "Meta" },
- { "Wants", config_parse_deps, n->meta.dependencies+NAME_WANTS, "Meta" },
- { "Requisite", config_parse_deps, n->meta.dependencies+NAME_REQUISITE, "Meta" },
- { "SoftRequisite", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUISITE, "Meta" },
- { "Conflicts", config_parse_deps, n->meta.dependencies+NAME_CONFLICTS, "Meta" },
- { "Before", config_parse_deps, n->meta.dependencies+NAME_BEFORE, "Meta" },
- { "After", config_parse_deps, n->meta.dependencies+NAME_AFTER, "Meta" },
- { "Listen", config_parse_listen, &n->socket.address, "Socket" },
- { "Type", config_parse_type, &n->socket.address.type, "Socket" },
+ { "Names", config_parse_names, &n->meta.names, "Meta" },
+ { "Description", config_parse_string, &n->meta.description, "Meta" },
+ { "Requires", config_parse_deps, n->meta.dependencies+NAME_REQUIRES, "Meta" },
+ { "SoftRequires", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUIRES, "Meta" },
+ { "Wants", config_parse_deps, n->meta.dependencies+NAME_WANTS, "Meta" },
+ { "Requisite", config_parse_deps, n->meta.dependencies+NAME_REQUISITE, "Meta" },
+ { "SoftRequisite", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUISITE, "Meta" },
+ { "Conflicts", config_parse_deps, n->meta.dependencies+NAME_CONFLICTS, "Meta" },
+ { "Before", config_parse_deps, n->meta.dependencies+NAME_BEFORE, "Meta" },
+ { "After", config_parse_deps, n->meta.dependencies+NAME_AFTER, "Meta" },
+ { "ListenStream", config_parse_listen, &n->socket, "Socket" },
+ { "ListenDatagram", config_parse_listen, &n->socket, "Socket" },
+ { "ListenSequentialPacket", config_parse_listen, &n->socket, "Socket" },
+ { "ListenFIFO", config_parse_listen, &n->socket, "Socket" },
+ { "BindIPv6Only", config_parse_bind, &n->socket, "Socket" },
+ { "Backlog", config_parse_unsigned, &n->socket.backlog, "Socket" },
{ NULL, NULL, NULL, NULL }
};
/* Finally, recursively add in all dependencies. */
if (type == JOB_START || type == JOB_RELOAD_OR_START) {
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state)
- if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state)
- if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state)
- if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
goto fail;
} else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state)
- if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) != -EBADR)
+ if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
goto fail;
}
[NAME_SOFT_REQUISITE] = "SoftRequisite",
[NAME_REQUIRED_BY] = "RequiredBy",
[NAME_SOFT_REQUIRED_BY] = "SoftRequiredBy",
+ [NAME_WANTED_BY] = "WantedBy",
[NAME_CONFLICTS] = "Conflicts",
[NAME_BEFORE] = "Before",
[NAME_AFTER] = "After",
#include <stdlib.h>
#include <arpa/inet.h>
#include <stdio.h>
+#include <net/if.h>
#include "macro.h"
#include "util.h"
#include "socket-util.h"
-int address_parse(Address *a, const char *s) {
+int socket_address_parse(SocketAddress *a, const char *s) {
int r;
char *e, *n;
unsigned u;
assert(s);
memset(a, 0, sizeof(*a));
+ a->type = SOCK_STREAM;
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
a->size = sizeof(struct sockaddr_in6);
- a->bind_ipv6_only = true;
} else if (*s == '/') {
/* AF_UNIX socket */
} else {
if ((e = strchr(s, ':'))) {
+ int r;
+
+ if ((r = safe_atou(e+1, &u)) < 0)
+ return r;
+
+ if (u <= 0 || u > 0xFFFF)
+ return -EINVAL;
- /* IPv4 in w.x.y.z:p notation */
if (!(n = strndup(s, e-s)))
return -ENOMEM;
- errno = 0;
- if (inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr) <= 0) {
+ /* IPv4 in w.x.y.z:p notation? */
+ if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
free(n);
- return errno != 0 ? -errno : -EINVAL;
+ return -errno;
}
- free(n);
+ if (r > 0) {
+ /* Gotcha, it's a traditional IPv4 address */
+ free(n);
- e++;
- if ((r = safe_atou(e, &u)) < 0)
- return r;
+ a->sockaddr.in4.sin_family = AF_INET;
+ a->sockaddr.in4.sin_port = htons((uint16_t) u);
+ a->size = sizeof(struct sockaddr_in);
+ } else {
+ unsigned idx;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
+ /* Uh, our last resort, an interface name */
+ idx = if_nametoindex(n);
+ free(n);
+
+ if (n == 0)
+ return -EINVAL;
- a->sockaddr.in4.sin_family = AF_INET;
- a->sockaddr.in4.sin_port = htons((uint16_t) u);
- a->size = sizeof(struct sockaddr_in);
+ a->sockaddr.in6.sin6_family = AF_INET6;
+ a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ a->sockaddr.in6.sin6_scope_id = idx;
+ memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN);
+ a->size = sizeof(struct sockaddr_in6);
+ }
} else {
/* Just a port */
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
- memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN);
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+ memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN);
a->size = sizeof(struct sockaddr_in6);
}
}
return 0;
}
-int address_verify(const Address *a) {
+int socket_address_verify(const SocketAddress *a) {
assert(a);
- switch (address_family(a)) {
+ switch (socket_address_family(a)) {
case AF_INET:
if (a->size != sizeof(struct sockaddr_in))
return -EINVAL;
}
}
-int address_print(const Address *a, char **p) {
+int socket_address_print(const SocketAddress *a, char **p) {
int r;
assert(a);
assert(p);
- if ((r = address_verify(a)) < 0)
+ if ((r = socket_address_verify(a)) < 0)
return r;
- switch (address_family(a)) {
+ switch (socket_address_family(a)) {
case AF_INET: {
char *ret;
}
}
-int address_listen(const Address *a, int backlog) {
+int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only) {
int r, fd;
assert(a);
- if ((r = address_verify(a)) < 0)
+ if ((r = socket_address_verify(a)) < 0)
return r;
- if ((fd = socket(address_family(a), a->type, 0)) < 0)
+ if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
return -errno;
+ if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
+ int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) {
+ close_nointr(fd);
+ return -errno;
+ }
+ }
+
if (bind(fd, &a->sockaddr.sa, a->size) < 0) {
close_nointr(fd);
return -errno;
return -errno;
}
- if (address_family(a) == AF_INET6) {
- int flag = a->bind_ipv6_only;
-
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) {
- close_nointr(fd);
- return -errno;
- }
- }
-
return 0;
}
#include "macro.h"
#include "util.h"
-typedef struct Address {
+typedef struct SocketAddress {
union {
struct sockaddr sa;
struct sockaddr_in in4;
/* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
int type;
+} SocketAddress;
- /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
- bool bind_ipv6_only;
-} Address;
+typedef enum SocketAddressBindIPv6Only {
+ SOCKET_ADDRESS_DEFAULT,
+ SOCKET_ADDRESS_BOTH,
+ SOCKET_ADDRESS_IPV6_ONLY
+} SocketAddressBindIPv6Only;
-#define address_family(a) ((a)->sockaddr.sa.sa_family)
+#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
-int address_parse(Address *a, const char *s);
-int address_print(const Address *a, char **p);
-int address_verify(const Address *a);
-int address_listen(const Address *a, int backlog);
+int socket_address_parse(SocketAddress *a, const char *s);
+int socket_address_print(const SocketAddress *a, char **p);
+int socket_address_verify(const SocketAddress *a);
+int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only);
#endif
Socket *s = SOCKET(n);
exec_context_defaults(&s->exec_context);
+ s->backlog = SOMAXCONN;
return name_load_fragment_and_dropin(n);
}
+static const char* listen_lookup(int type) {
+
+ if (type == SOCK_STREAM)
+ return "ListenStream";
+ else if (type == SOCK_DGRAM)
+ return "ListenDatagram";
+ else if (type == SOCK_SEQPACKET)
+ return "ListenSequentialPacket";
+
+ assert_not_reached("Unkown socket type");
+ return NULL;
+}
+
static void socket_dump(Name *n, FILE *f, const char *prefix) {
static const char* const state_table[_SOCKET_STATE_MAX] = {
SocketExecCommand c;
Socket *s = SOCKET(n);
- const char *t;
- int r;
- char *k;
+ SocketPort *p;
assert(s);
- if ((r = address_print(&n->socket.address, &k)) < 0)
- t = strerror(-r);
- else
- t = k;
-
fprintf(f,
"%sSocket State: %s\n"
- "%sAddress: %s\n",
+ "%sBindIPv6Only: %s\n"
+ "%sBacklog: %u\n",
prefix, state_table[s->state],
- prefix, t);
+ prefix, yes_no(s->bind_ipv6_only),
+ prefix, s->backlog);
+
+ LIST_FOREACH(p, s->ports) {
- free(k);
+ if (p->type == SOCKET_SOCKET) {
+ const char *t;
+ int r;
+ char *k;
+
+ if ((r = socket_address_print(&p->address, &k)) < 0)
+ t = strerror(-r);
+ else
+ t = k;
+
+ fprintf(f, "%s%s: %s\n", prefix, listen_lookup(p->address.type), k);
+ free(k);
+ } else
+ fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
+ }
exec_context_dump(&s->exec_context, f, prefix);
}
}
+static int socket_start(Name *n) {
+ return 0;
+}
+
+static int socket_stop(Name *n) {
+ return 0;
+}
+
static NameActiveState socket_active_state(Name *n) {
static const NameActiveState table[_SOCKET_STATE_MAX] = {
}
static void socket_free_hook(Name *n) {
- unsigned i;
SocketExecCommand c;
Socket *s = SOCKET(n);
+ SocketPort *p;
assert(s);
- for (i = 0; i < s->n_fds; i++)
- close_nointr(s->fds[i]);
+ while ((p = s->ports)) {
+ LIST_REMOVE(SocketPort, s->ports, p);
+
+ if (p->fd >= 0)
+ close_nointr(p->fd);
+ free(p->path);
+ free(p);
+ }
exec_context_free(&s->exec_context);
.load = socket_load,
.dump = socket_dump,
- .start = NULL,
- .stop = NULL,
+ .start = socket_start,
+ .stop = socket_stop,
.reload = NULL,
.active_state = socket_active_state,
typedef struct Socket Socket;
#include "name.h"
+#include "socket-util.h"
typedef enum SocketState {
SOCKET_DEAD,
_SOCKET_EXEC_MAX
} SocketExecCommand;
+typedef enum SocketType {
+ SOCKET_SOCKET,
+ SOCKET_FIFO
+} SocketType;
+
+typedef struct SocketPort SocketPort;
+
+struct SocketPort {
+ SocketType type;
+
+ SocketAddress address;
+ char *path;
+
+ int fd;
+
+ LIST_FIELDS(SocketPort);
+};
+
struct Socket {
Meta meta;
SocketState state;
- Address address;
- int *fds;
- unsigned n_fds;
+ LIST_HEAD(SocketPort, ports);
+
+ /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
+ bool bind_ipv6_only;
+ unsigned backlog;
ExecCommand* exec_command[_SOCKET_EXEC_MAX];
ExecContext exec_context;
Description=Postfix SMTP Socket
[Socket]
-Listen=25
-Type=stream
+ListenStream=25
+ListenFIFO=/dev/test
Description=Syslog Socket
[Socket]
-Listen=/dev/log
-Type=dgram
+ListenDatagram=/dev/log
+ListenStream=eth0:4711