From: Lennart Poettering Date: Tue, 19 Jan 2010 01:56:37 +0000 (+0100) Subject: parse socket files properly X-Git-Tag: v1~846 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=42f4e3c4413ad35e3815f25211fee95d775488a7 parse socket files properly --- diff --git a/Makefile b/Makefile index 45123a8b3..ae82e8ce8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CFLAGS=-Wall -Wextra -O0 -g -pipe -D_GNU_SOURCE -fdiagnostics-show-option -Wno-unused-parameter LIBS=-lrt -systemd: main.o name.o util.o set.o hashmap.o strv.o job.o manager.o conf-parser.o load-fragment.o +systemd: main.o name.o util.o set.o hashmap.o strv.o job.o manager.o conf-parser.o load-fragment.o socket-util.c $(CC) $(CFLAGS) -o $@ $^ $(LIBS) clean: diff --git a/conf-parser.c b/conf-parser.c index d6703dcb6..3a7da7960 100644 --- a/conf-parser.c +++ b/conf-parser.c @@ -76,7 +76,7 @@ static char *strip(char *s) { } /* Parse a variable assignment line */ -static int parse_line(const char *filename, unsigned line, char **section, const ConfigItem *t, char *l, void *userdata) { +static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) { char *e, *c, *b; b = l+strspn(l, WHITESPACE); @@ -109,7 +109,7 @@ static int parse_line(const char *filename, unsigned line, char **section, const } } - r = config_parse(fn, t, userdata); + r = config_parse(fn, sections, t, userdata); free(path); return r; } @@ -129,6 +129,21 @@ static int parse_line(const char *filename, unsigned line, char **section, const if (!(n = strndup(b+1, k-2))) return -ENOMEM; + if (sections) { + const char * const * i; + bool good = false; + STRV_FOREACH(i, sections) + if (streq(*i, n)) { + good = true; + break; + } + + if (!good) { + free(n); + return -EBADMSG; + } + } + free(*section); *section = n; @@ -147,7 +162,7 @@ static int parse_line(const char *filename, unsigned line, char **section, const } /* Go through the file and parse each line */ -int config_parse(const char *filename, const ConfigItem *t, void *userdata) { +int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata) { unsigned line = 0; char *section = NULL; FILE *f; @@ -174,7 +189,7 @@ int config_parse(const char *filename, const ConfigItem *t, void *userdata) { goto finish; } - if ((r = parse_line(filename, ++line, §ion, t, l, userdata)) < 0) + if ((r = parse_line(filename, ++line, §ion, sections, t, l, userdata)) < 0) goto finish; } diff --git a/conf-parser.h b/conf-parser.h index 8eb0635f8..25b77eb49 100644 --- a/conf-parser.h +++ b/conf-parser.h @@ -21,7 +21,7 @@ typedef struct ConfigItem { /* The configuration file parsing routine. Expects a table of * config_items in *t that is terminated by an item where lvalue is * NULL */ -int config_parse(const char *filename, const ConfigItem *t, void *userdata); +int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata); /* Generic parsers */ int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); diff --git a/default.milestone b/default.milestone new file mode 100644 index 000000000..48f117139 --- /dev/null +++ b/default.milestone @@ -0,0 +1,3 @@ +[Meta] +Wants=postfix.socket +Description=Default Milestone diff --git a/job.c b/job.c index 87bcd289d..2cf8be533 100644 --- a/job.c +++ b/job.c @@ -64,25 +64,25 @@ void job_free(Job *j) { void job_dump(Job *j, FILE*f) { static const char* const job_type_table[_JOB_TYPE_MAX] = { - [JOB_START] = "START", - [JOB_STOP] = "STOP", - [JOB_VERIFY_STARTED] = "VERIFY_STARTED", - [JOB_RELOAD] = "RELOAD", - [JOB_RESTART] = "RESTART", - [JOB_TRY_RESTART] = "TRY_RESTART", - [JOB_RESTART_FINISH] = "RESTART_FINISH" + [JOB_START] = "start", + [JOB_STOP] = "stop", + [JOB_VERIFY_STARTED] = "verify-started", + [JOB_RELOAD] = "reload", + [JOB_RESTART] = "restart", + [JOB_TRY_RESTART] = "try-restart", + [JOB_RESTART_FINISH] = "restart-finish" }; static const char* const job_state_table[_JOB_STATE_MAX] = { - [JOB_WAITING] = "WAITING", - [JOB_RUNNING] = "RUNNING", - [JOB_DONE] = "DONE" + [JOB_WAITING] = "waiting", + [JOB_RUNNING] = "running", + [JOB_DONE] = "done" }; assert(j); assert(f); - fprintf(f, "Job %u: %s → %s in state %s\n", + fprintf(f, "Job %u (%s) →%s in state %s\n", j->id, name_id(j->name), job_type_table[j->type], diff --git a/load-fragment.c b/load-fragment.c index 4510cc916..d6b5e2f39 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -9,7 +9,7 @@ #include "conf-parser.h" #include "load-fragment.h" -int config_parse_deps( +static int config_parse_deps( const char *filename, unsigned line, const char *section, @@ -54,7 +54,7 @@ int config_parse_deps( return 0; } -int config_parse_names( +static int config_parse_names( const char *filename, unsigned line, const char *section, @@ -119,31 +119,94 @@ int config_parse_names( return 0; } +static int config_parse_listen( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + return address_parse(data, rvalue); +} + +static int config_parse_type( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + int *type = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "stream")) + *type = SOCK_STREAM; + else if (streq(rvalue, "dgram")) + *type = SOCK_DGRAM; + else + return -EINVAL; + + return 0; +} + int name_load_fragment(Name *n) { + const char *const section_table[_NAME_TYPE_MAX] = { + [NAME_SERVICE] = "Service", + [NAME_TIMER] = "Timer", + [NAME_SOCKET] = "Socket", + [NAME_MILESTONE] = "Milestone", + [NAME_DEVICE] = "Device", + [NAME_MOUNT] = "Mount", + [NAME_AUTOMOUNT] = "Automount", + [NAME_SNAPSHOT] = "Snapshot" + }; + 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" }, + { "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" }, { NULL, NULL, NULL, NULL } }; + const + char *t; int r; void *state; + const char *sections[3]; assert(n); assert(n->meta.state == NAME_STUB); + sections[0] = "Meta"; + sections[1] = section_table[n->meta.type]; + sections[2] = NULL; + SET_FOREACH(t, n->meta.names, state) - if ((r = config_parse(t, items, n)) < 0) + if ((r = config_parse(t, sections, items, n)) < 0) goto fail; r = 0; diff --git a/name.c b/name.c index 80ce5d5b3..cbd847a91 100644 --- a/name.c +++ b/name.c @@ -158,7 +158,7 @@ void name_free(Name *name) { Socket *s = SOCKET(name); for (i = 0; i < s->n_fds; i++) - nointr_close(s->fds[i]); + close_nointr(s->fds[i]); break; } @@ -369,19 +369,53 @@ const char* name_id(Name *n) { void name_dump(Name *n, FILE *f) { static const char* const state_table[_NAME_STATE_MAX] = { - [NAME_STUB] = "STUB", - [NAME_LOADED] = "LOADED", - [NAME_FAILED] = "FAILED" + [NAME_STUB] = "stub", + [NAME_LOADED] = "loaded", + [NAME_FAILED] = "failed" + }; + + static const char* const socket_state_table[_SOCKET_STATE_MAX] = { + [SOCKET_DEAD] = "dead", + [SOCKET_BEFORE] = "before", + [SOCKET_START_PRE] = "start-pre", + [SOCKET_START] = "start", + [SOCKET_START_POST] = "start-post", + [SOCKET_LISTENING] = "listening", + [SOCKET_RUNNING] = "running", + [SOCKET_STOP_PRE] = "stop-pre", + [SOCKET_STOP] = "stop", + [SOCKET_STOP_POST] = "stop-post", + [SOCKET_MAINTAINANCE] = "maintainance" }; assert(n); fprintf(stderr, - "Name %s (%s), state %s\n", + "Name %s (\"%s\") in state %s\n", name_id(n), n->meta.description ? n->meta.description : name_id(n), state_table[n->meta.state]); + switch (n->meta.type) { + case NAME_SOCKET: { + int r; + char *s = NULL; + const char *t; + + if ((r = address_print(&n->socket.address, &s)) < 0) + t = strerror(-r); + else + t = s; + + fprintf(stderr, "\t%s in state %s\n", t, socket_state_table[n->socket.state]); + free(s); + break; + } + + default: + ; + } + if (n->meta.job) { fprintf(f, "\t▶ "); job_dump(n->meta.job, f); diff --git a/name.h b/name.h index 116523453..0ed105659 100644 --- a/name.h +++ b/name.h @@ -22,6 +22,7 @@ typedef struct Snapshot Snapshot; #include "set.h" #include "util.h" #include "list.h" +#include "socket-util.h" typedef enum NameType { NAME_SERVICE = 0, @@ -148,13 +149,16 @@ typedef enum SocketState { SOCKET_STOP_PRE, SOCKET_STOP, SOCKET_STOP_POST, - SOCKET_MAINTAINANCE + SOCKET_MAINTAINANCE, + _SOCKET_STATE_MAX } SocketState; struct Socket { Meta meta; SocketState state; + + Address address; int *fds; unsigned n_fds; diff --git a/postfix.service b/postfix.service new file mode 100644 index 000000000..77fdc766a --- /dev/null +++ b/postfix.service @@ -0,0 +1,7 @@ +[Meta] +Names=mail-transfer-agent.service +Description=Postfix Mail Server +Requires=syslog.socket + +[Service] +Exec=/usr/bin/postfix diff --git a/postfix.socket b/postfix.socket new file mode 100644 index 000000000..da5b09312 --- /dev/null +++ b/postfix.socket @@ -0,0 +1,6 @@ +[Meta] +Description=Postfix Listening Socket + +[Socket] +Listen=25 +Type=stream diff --git a/socket-util.c b/socket-util.c new file mode 100644 index 000000000..407c9617b --- /dev/null +++ b/socket-util.c @@ -0,0 +1,290 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "macro.h" +#include "util.h" +#include "socket-util.h" + +int address_parse(Address *a, const char *s) { + int r; + char *e, *n; + unsigned u; + + assert(a); + assert(s); + + memset(a, 0, sizeof(*a)); + + if (*s == '[') { + /* IPv6 in [x:.....:z]:p notation */ + + if (!(e = strchr(s+1, ']'))) + return -EINVAL; + + if (!(n = strndup(s+1, e-s-1))) + return -ENOMEM; + + errno = 0; + if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) { + free(n); + return errno != 0 ? -errno : -EINVAL; + } + + free(n); + + e++; + if (*e != ':') + return -EINVAL; + + e++; + if ((r = safe_atou(e, &u)) < 0) + return r; + + if (u <= 0 || u > 0xFFFF) + return -EINVAL; + + 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 */ + + size_t l; + + l = strlen(s); + if (l >= sizeof(a->sockaddr.un.sun_path)) + return -EINVAL; + + a->sockaddr.un.sun_family = AF_UNIX; + memcpy(a->sockaddr.un.sun_path, s, l); + a->size = sizeof(sa_family_t) + l + 1; + + } else if (*s == '=') { + /* Abstract AF_UNIX socket */ + size_t l; + + l = strlen(s+1); + if (l >= sizeof(a->sockaddr.un.sun_path) - 1) + return -EINVAL; + + a->sockaddr.un.sun_family = AF_UNIX; + memcpy(a->sockaddr.un.sun_path+1, s+1, l); + a->size = sizeof(struct sockaddr_un); + + } else { + + if ((e = strchr(s, ':'))) { + + /* 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) { + free(n); + return errno != 0 ? -errno : -EINVAL; + } + + free(n); + + e++; + if ((r = safe_atou(e, &u)) < 0) + return r; + + if (u <= 0 || u > 0xFFFF) + return -EINVAL; + + a->sockaddr.in4.sin_family = AF_INET; + a->sockaddr.in4.sin_port = htons((uint16_t) u); + a->size = sizeof(struct sockaddr_in); + } else { + + /* Just a port */ + if ((r = safe_atou(s, &u)) < 0) + return r; + + if (u <= 0 || u > 0xFFFF) + 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); + a->size = sizeof(struct sockaddr_in6); + } + } + + return 0; +} + +int address_verify(const Address *a) { + assert(a); + + switch (address_family(a)) { + case AF_INET: + if (a->size != sizeof(struct sockaddr_in)) + return -EINVAL; + + if (a->sockaddr.in4.sin_port == 0) + return -EINVAL; + + return 0; + + case AF_INET6: + if (a->size != sizeof(struct sockaddr_in6)) + return -EINVAL; + + if (a->sockaddr.in6.sin6_port == 0) + return -EINVAL; + + return 0; + + case AF_UNIX: + if (a->size < sizeof(sa_family_t)) + return -EINVAL; + + if (a->size > sizeof(sa_family_t)) { + + if (a->sockaddr.un.sun_path[0] == 0) { + /* abstract */ + if (a->size != sizeof(struct sockaddr_un)) + return -EINVAL; + } else { + char *e; + + /* path */ + if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)))) + return -EINVAL; + + if (a->size != sizeof(sa_family_t) + (e - a->sockaddr.un.sun_path) + 1) + return -EINVAL; + } + } + + return 0; + + default: + return -EAFNOSUPPORT; + } +} + +int address_print(const Address *a, char **p) { + int r; + assert(a); + assert(p); + + if ((r = address_verify(a)) < 0) + return r; + + switch (address_family(a)) { + case AF_INET: { + char *ret; + + if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1))) + return -ENOMEM; + + if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) { + free(ret); + return -errno; + } + + sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port)); + *p = ret; + return 0; + } + + case AF_INET6: { + char *ret; + + if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1))) + return -ENOMEM; + + ret[0] = '['; + if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) { + free(ret); + return -errno; + } + + sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port)); + *p = ret; + return 0; + } + + case AF_UNIX: { + char *ret; + + if (a->size <= sizeof(sa_family_t)) { + + if (!(ret = strdup(""))) + return -ENOMEM; + + } else if (a->sockaddr.un.sun_path[0] == 0) { + /* abstract */ + + /* FIXME: We assume we can print the + * socket path here and that it hasn't + * more than one NUL byte. That is + * actually an invalid assumption */ + + if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1))) + return -ENOMEM; + + ret[0] = '='; + memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1); + ret[sizeof(a->sockaddr.un.sun_path)] = 0; + + } else { + + if (!(ret = strdup(a->sockaddr.un.sun_path))) + return -ENOMEM; + } + + *p = ret; + return 0; + } + + default: + return -EINVAL; + } +} + +int address_listen(const Address *a, int backlog) { + int r, fd; + assert(a); + + if ((r = address_verify(a)) < 0) + return r; + + if ((fd = socket(address_family(a), a->type, 0)) < 0) + return -errno; + + if (bind(fd, &a->sockaddr.sa, a->size) < 0) { + close_nointr(fd); + return -errno; + } + + if (a->type == SOCK_STREAM) + if (listen(fd, backlog) < 0) { + close_nointr(fd); + 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; +} diff --git a/socket-util.h b/socket-util.h new file mode 100644 index 000000000..711f7740a --- /dev/null +++ b/socket-util.h @@ -0,0 +1,40 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foosocketutilhfoo +#define foosocketutilhfoo + +#include +#include +#include + +#include "macro.h" +#include "util.h" + +typedef struct Address { + union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + struct sockaddr_un un; + struct sockaddr_storage storage; + } sockaddr; + + /* We store the size here explicitly due to the weird + * sockaddr_un semantics for abstract sockets */ + socklen_t size; + + /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ + int type; + + /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ + bool bind_ipv6_only; +} Address; + +#define 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); + +#endif diff --git a/syslog.service b/syslog.service new file mode 100644 index 000000000..e26280937 --- /dev/null +++ b/syslog.service @@ -0,0 +1,5 @@ +[Meta] +Description=System Logging Daemon + +[Service] +Exec=/usr/bin/rsyslogd diff --git a/syslog.socket b/syslog.socket new file mode 100644 index 000000000..3d5fa818a --- /dev/null +++ b/syslog.socket @@ -0,0 +1,2 @@ +[Socket] +Listen=/dev/log diff --git a/util.c b/util.c index 4bec220f7..f04558339 100644 --- a/util.c +++ b/util.c @@ -81,7 +81,7 @@ bool startswith(const char *s, const char *prefix) { return memcmp(s, prefix, pl) == 0; } -int nointr_close(int fd) { +int close_nointr(int fd) { assert(fd >= 0); for (;;) { diff --git a/util.h b/util.h index 9dfe631b8..f5aaf704d 100644 --- a/util.h +++ b/util.h @@ -52,7 +52,7 @@ static inline bool is_path_absolute(const char *p) { bool endswith(const char *s, const char *postfix); bool startswith(const char *s, const char *prefix); -int nointr_close(int fd); +int close_nointr(int fd); int parse_boolean(const char *v);