X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=socket.c;h=ed14db55f99c898e2851b0e569957be6c806a3a1;hb=83c60c9f0c7581b607dc5c0f84582978894e3d4a;hp=9f8718447d28d21fc3bc8bf7a921105bc681a217;hpb=5cb5a6ffc33667c93e9bc3572534dcaa684046e3;p=elogind.git diff --git a/socket.c b/socket.c index 9f8718447..ed14db55f 100644 --- a/socket.c +++ b/socket.c @@ -1,16 +1,48 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ +#include +#include +#include +#include +#include + #include "name.h" #include "socket.h" +#include "log.h" + +static const NameActiveState state_table[_SOCKET_STATE_MAX] = { + [SOCKET_DEAD] = NAME_INACTIVE, + [SOCKET_START_PRE] = NAME_ACTIVATING, + [SOCKET_START_POST] = NAME_ACTIVATING, + [SOCKET_LISTENING] = NAME_ACTIVE, + [SOCKET_RUNNING] = NAME_ACTIVE, + [SOCKET_STOP_PRE] = NAME_DEACTIVATING, + [SOCKET_STOP_POST] = NAME_DEACTIVATING, + [SOCKET_MAINTAINANCE] = NAME_INACTIVE, +}; static int socket_load(Name *n) { 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] = { @@ -33,24 +65,35 @@ static void socket_dump(Name *n, FILE *f, const char *prefix) { 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); - free(k); + LIST_FOREACH(p, s->ports) { + + 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); @@ -62,31 +105,147 @@ static void socket_dump(Name *n, FILE *f, const char *prefix) { } } -static NameActiveState socket_active_state(Name *n) { +static void socket_set_state(Socket *s, SocketState state) { + SocketState old_state; + assert(s); - static const NameActiveState table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = NAME_INACTIVE, - [SOCKET_START_PRE] = NAME_ACTIVATING, - [SOCKET_START_POST] = NAME_ACTIVATING, - [SOCKET_LISTENING] = NAME_ACTIVE, - [SOCKET_RUNNING] = NAME_ACTIVE, - [SOCKET_STOP_PRE] = NAME_DEACTIVATING, - [SOCKET_STOP_POST] = NAME_DEACTIVATING, - [SOCKET_MAINTAINANCE] = NAME_INACTIVE, - }; + old_state = s->state; + s->state = state; + + name_notify(NAME(s), state_table[old_state], state_table[s->state]); +} + +static void close_fds(Socket *s) { + SocketPort *p; + + assert(s); + + LIST_FOREACH(p, s->ports) { + if (p->fd < 0) + continue; - return table[SOCKET(n)->state]; + close_nointr(p->fd); + p->fd = -1; + } +} + +static int socket_start(Name *n) { + Socket *s = SOCKET(n); + SocketPort *p; + int r; + + assert(s); + + if (s->state == SOCKET_START_PRE || + s->state == SOCKET_START_POST) + return 0; + + if (s->state == SOCKET_LISTENING || + s->state == SOCKET_RUNNING) + return -EALREADY; + + if (s->state == SOCKET_STOP_PRE || + s->state == SOCKET_STOP_POST) + return -EAGAIN; + + assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE); + + LIST_FOREACH(p, s->ports) { + + assert(p->fd < 0); + + if (p->type == SOCKET_SOCKET) { + + if ((r = socket_address_listen(&p->address, s->backlog, s->bind_ipv6_only, &p->fd)) < 0) + goto rollback; + + } else { + struct stat st; + assert(p->type == SOCKET_FIFO); + + if (mkfifo(p->path, 0666 & ~s->exec_context.umask) < 0 && errno != EEXIST) { + r = -errno; + goto rollback; + } + + if ((p->fd = open(p->path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) { + r = -errno; + goto rollback; + } + + if (fstat(p->fd, &st) < 0) { + r = -errno; + goto rollback; + } + + /* FIXME verify user, access mode */ + + if (!S_ISFIFO(st.st_mode)) { + r = -EEXIST; + goto rollback; + } + } + } + + socket_set_state(s, SOCKET_LISTENING); + + return 0; + +rollback: + close_fds(s); + + socket_set_state(s, SOCKET_MAINTAINANCE); + + return r; +} + +static int socket_stop(Name *n) { + Socket *s = SOCKET(n); + + assert(s); + + if (s->state == SOCKET_START_PRE || + s->state == SOCKET_START_POST) + return -EAGAIN; + + if (s->state == SOCKET_DEAD || + s->state == SOCKET_MAINTAINANCE) + return -EALREADY; + + if (s->state == SOCKET_STOP_PRE || + s->state == SOCKET_STOP_POST) + return 0; + + assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING); + + close_fds(s); + + socket_set_state(s, SOCKET_DEAD); + + return 0; +} + +static NameActiveState socket_active_state(Name *n) { + assert(n); + + return state_table[SOCKET(n)->state]; } 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); @@ -103,8 +262,8 @@ const NameVTable socket_vtable = { .load = socket_load, .dump = socket_dump, - .start = NULL, - .stop = NULL, + .start = socket_start, + .stop = socket_stop, .reload = NULL, .active_state = socket_active_state,