diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2014-12-05 22:26:11 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2014-12-05 22:26:11 +0000 |
commit | 90b12bd71bb9fc79a4640b9112c13ef529d0196a (patch) | |
tree | 523b3f4ee2969e7a729bab2ba749c4b924ae62af /src/libs6/s6lockd.c | |
download | s6-90b12bd71bb9fc79a4640b9112c13ef529d0196a.tar.xz |
Initial commit
Diffstat (limited to 'src/libs6/s6lockd.c')
-rw-r--r-- | src/libs6/s6lockd.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/libs6/s6lockd.c b/src/libs6/s6lockd.c new file mode 100644 index 0000000..ef088f9 --- /dev/null +++ b/src/libs6/s6lockd.c @@ -0,0 +1,316 @@ +/* ISC license. */ + +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <skalibs/uint16.h> +#include <skalibs/uint32.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/error.h> +#include <skalibs/strerr2.h> +#include <skalibs/genalloc.h> +#include <skalibs/sig.h> +#include <skalibs/selfpipe.h> +#include <skalibs/tai.h> +#include <skalibs/djbunix.h> +#include <skalibs/iopause.h> +#include <skalibs/unixmessage.h> +#include <skalibs/skaclient.h> +#include <s6/s6lock.h> + +#define USAGE "s6lockd lockdir" +#define X() strerr_dief1x(101, "internal inconsistency, please submit a bug-report.") + +typedef struct s6lockio_s s6lockio_t, *s6lockio_t_ref ; +struct s6lockio_s +{ + unsigned int xindex ; + unsigned int pid ; + tain_t limit ; + int p[2] ; + uint16 id ; /* given by client */ +} ; +#define S6LOCKIO_ZERO { 0, 0, TAIN_ZERO, { -1, -1 }, 0 } +static s6lockio_t const szero = S6LOCKIO_ZERO ; + +static genalloc a = GENALLOC_ZERO ; /* array of s6lockio_t */ + +static void s6lockio_free (s6lockio_t_ref p) +{ + register int e = errno ; + fd_close(p->p[1]) ; + fd_close(p->p[0]) ; + kill(p->pid, SIGTERM) ; + *p = szero ; + errno = e ; +} + +static void cleanup (void) +{ + register unsigned int i = genalloc_len(s6lockio_t, &a) ; + for (; i ; i--) s6lockio_free(genalloc_s(s6lockio_t, &a) + i - 1) ; + genalloc_setlen(s6lockio_t, &a, 0) ; +} + +static void trig (uint16 id, char e) +{ + char pack[3] ; + unixmessage_t m = { .s = pack, .len = 3, .fds = 0, .nfds = 0 } ; + uint16_pack_big(pack, id) ; + pack[2] = e ; + if (!unixmessage_put(unixmessage_sender_x, &m)) + { + cleanup() ; + strerr_diefu1sys(111, "build answer") ; + } +} + +static void answer (char c) +{ + unixmessage_t m = { .s = &c, .len = 1, .fds = 0, .nfds = 0 } ; + if (!unixmessage_put(unixmessage_sender_1, &m)) + { + cleanup() ; + strerr_diefu1sys(111, "unixmessage_put") ; + } +} + +static void remove (unsigned int i) +{ + register unsigned int n = genalloc_len(s6lockio_t, &a) - 1 ; + s6lockio_free(genalloc_s(s6lockio_t, &a) + i) ; + genalloc_s(s6lockio_t, &a)[i] = genalloc_s(s6lockio_t, &a)[n] ; + genalloc_setlen(s6lockio_t, &a, n) ; +} + +static void handle_signals (void) +{ + for (;;) + { + switch (selfpipe_read()) + { + case -1 : cleanup() ; strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : return ; + case SIGTERM : + case SIGQUIT : + case SIGHUP : + case SIGABRT : + case SIGINT : cleanup() ; _exit(0) ; + case SIGCHLD : wait_reap() ; break ; + default : cleanup() ; X() ; + } + } +} + +static int parse_protocol (unixmessage_t const *m, void *context) +{ + uint16 id ; + if (m->len < 3 || m->nfds) + { + cleanup() ; + strerr_dief1x(100, "invalid client request") ; + } + uint16_unpack_big(m->s, &id) ; + switch (m->s[2]) + { + case '>' : /* release */ + { + register unsigned int i = genalloc_len(s6lockio_t, &a) ; + for (; i ; i--) if (genalloc_s(s6lockio_t, &a)[i-1].id == id) break ; + if (i) + { + remove(i-1) ; + answer(0) ; + } + else answer(ENOENT) ; + break ; + } + case '<' : /* lock path */ + { + s6lockio_t f = S6LOCKIO_ZERO ; + char const *cargv[3] = { S6LOCKD_HELPER_PROG, 0, 0 } ; + char const *cenvp[2] = { 0, 0 } ; + uint32 options, pathlen ; + if (m->len < 23) + { + answer(EPROTO) ; + break ; + } + uint32_unpack_big(m->s + 3, &options) ; + tain_unpack(m->s + 7, &f.limit) ; + uint32_unpack_big(m->s + 19, &pathlen) ; + if (pathlen + 23 != m->len || m->s[m->len - 1]) + { + answer(EPROTO) ; + break ; + } + f.id = id ; + m->s[21] = '.' ; + m->s[22] = '/' ; + cargv[1] = (char const *)m->s + 21 ; + if (options & S6LOCK_OPTIONS_EX) cenvp[0] = "S6LOCK_EX=1" ; + f.pid = child_spawn(cargv[0], cargv, cenvp, f.p, 2) ; + if (!f.pid) + { + answer(errno) ; + break ; + } + if (!genalloc_append(s6lockio_t, &a, &f)) + { + s6lockio_free(&f) ; + answer(errno) ; + break ; + } + answer(0) ; + break ; + } + default : + { + cleanup() ; + strerr_dief1x(100, "invalid client request") ; + } + } + (void)context ; + return 1 ; +} + +int main (int argc, char const *const *argv) +{ + tain_t deadline ; + int sfd ; + PROG = "s6lockd" ; + + if (argc < 2) strerr_dieusage(100, USAGE) ; + if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ; + if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ; + if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + + sfd = selfpipe_init() ; + if (sfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGCHLD) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGQUIT) ; + sigaddset(&set, SIGHUP) ; + sigaddset(&set, SIGABRT) ; + sigaddset(&set, SIGINT) ; + if (selfpipe_trapset(&set) < 0) + strerr_diefu1sys(111, "trap signals") ; + } + + tain_now_g() ; + tain_addsec_g(&deadline, 2) ; + + if (!skaclient_server_01x_init_g(S6LOCK_BANNER1, S6LOCK_BANNER1_LEN, S6LOCK_BANNER2, S6LOCK_BANNER2_LEN, &deadline)) + strerr_diefu1sys(111, "sync with client") ; + + for (;;) + { + register unsigned int n = genalloc_len(s6lockio_t, &a) ; + iopause_fd x[4 + n] ; + unsigned int i = 0 ; + int r ; + + tain_add_g(&deadline, &tain_infinite_relative) ; + x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ; + x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_1) ? 0 : IOPAUSE_WRITE ) ; + x[2].fd = unixmessage_sender_fd(unixmessage_sender_x) ; + x[2].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_x) ? 0 : IOPAUSE_WRITE) ; + x[3].fd = sfd ; x[3].events = IOPAUSE_READ ; + for (; i < n ; i++) + { + register s6lockio_t_ref p = genalloc_s(s6lockio_t, &a) + i ; + x[4+i].fd = p->p[0] ; + x[4+i].events = IOPAUSE_READ ; + if (p->limit.sec.x && tain_less(&p->limit, &deadline)) deadline = p->limit ; + p->xindex = 4+i ; + } + + r = iopause_g(x, 4 + n, &deadline) ; + if (r < 0) + { + cleanup() ; + strerr_diefu1sys(111, "iopause") ; + } + + /* timeout => seek and destroy */ + if (!r) + { + for (i = 0 ; i < n ; i++) + { + register s6lockio_t_ref p = genalloc_s(s6lockio_t, &a) + i ; + if (p->limit.sec.x && !tain_future(&p->limit)) break ; + } + if (i < n) + { + trig(genalloc_s(s6lockio_t, &a)[i].id, ETIMEDOUT) ; + remove(i) ; + } + continue ; + } + + /* client closed */ + if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) break ; + + /* client is reading */ + if (x[1].revents & IOPAUSE_WRITE) + if ((unixmessage_sender_flush(unixmessage_sender_1) < 0) && !error_isagain(errno)) + { + cleanup() ; + strerr_diefu1sys(111, "flush stdout") ; + } + if (x[2].revents & IOPAUSE_WRITE) + if ((unixmessage_sender_flush(unixmessage_sender_x) < 0) && !error_isagain(errno)) + { + cleanup() ; + strerr_diefu1sys(111, "flush asyncout") ; + } + + /* scan children for successes */ + for (i = 0 ; i < genalloc_len(s6lockio_t, &a) ; i++) + { + register s6lockio_t_ref p = genalloc_s(s6lockio_t, &a) + i ; + if (p->p[0] < 0) continue ; + if (x[p->xindex].revents & IOPAUSE_READ) + { + char c ; + register int r = sanitize_read(fd_read(p->p[0], &c, 1)) ; + if (!r) continue ; + if (r < 0) + { + trig(p->id, errno) ; + remove(i--) ; + } + else if (c != '!') + { + trig(p->id, EPROTO) ; + remove(i--) ; + } + else + { + trig(p->id, 0) ; + p->limit = tain_zero ; + } + } + } + + /* signals arrived */ + if (x[3].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT)) handle_signals() ; + + /* client is writing */ + if (!unixmessage_receiver_isempty(unixmessage_receiver_0) || x[0].revents & IOPAUSE_READ) + { + if (unixmessage_handle(unixmessage_receiver_0, &parse_protocol, 0) < 0) + { + if (errno == EPIPE) break ; /* normal exit */ + cleanup() ; + strerr_diefu1sys(111, "handle messages from client") ; + } + } + } + cleanup() ; + return 0 ; +} |