From: Mark Wooding Date: Mon, 1 Jan 2007 12:52:33 +0000 (+0000) Subject: admin: Service ownership infrastructure and commands. X-Git-Tag: 1.0.0pre8~94^3~3 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/commitdiff_plain/be6a1b7ab43375b85c1d2cd2dc1703b4eaead98f?ds=sidebyside;hp=e6a3a5537973f060bbfbe9b5aecfd9c0312a1a7f admin: Service ownership infrastructure and commands. * Implement a table of named and versioned services. * Allow clients to claim and release services, and find out about claimed services. * Notify clients when services are claimed and released. * Notify a service provider when another client provides a later version. --- diff --git a/server/admin.c b/server/admin.c index 6ff88159..eb480398 100644 --- a/server/admin.c +++ b/server/admin.c @@ -65,6 +65,7 @@ static admin *admins; static admin *a_dead; static sel_file sock; static const char *sockname; +static sym_table a_svcs; static unsigned flags = 0; static admin *a_stdin = 0; static sig s_term, s_int, s_hup; @@ -731,6 +732,68 @@ static int a_bgadd(admin *a, admin_bgop *bg, const char *tag, return (0); } +/*----- Services infrastructure -------------------------------------------*/ + +/* --- @a_svcfind@ --- * + * + * Arguments: @admin *a@ = the requesting client + * @const char *name@ = service name wanted + * + * Returns: The service requested, or null. + * + * Use: Finds a service; reports an error if the service couldn't be + * found. + */ + +static admin_service *a_svcfind(admin *a, const char *name) +{ + admin_service *svc; + + if ((svc = sym_find(&a_svcs, name, -1, 0, 0)) == 0) { + a_fail(a, "unknown-service", "%s", name, A_END); + return (0); + } + return (svc); +} + +/* --- @a_svcunlink@ --- * + * + * Arguments: @admin_service *svc@ = pointer to service structure + * + * Returns: --- + * + * Use: Unlinks the service from its provider, without issuing a + * message or freeing the structure. The version string is + * freed, however. + */ + +static void a_svcunlink(admin_service *svc) +{ + if (svc->next) + svc->next->prev = svc->prev; + if (svc->prev) + svc->prev->next = svc->next; + else + svc->prov->svcs = svc->next; + xfree(svc->version); +} + +/* --- @a_svcrelease@ --- * + * + * Arguments: @admin_service *svc@ = pointer to service structure + * + * Returns: --- + * + * Use: Releases a service and frees its structure. + */ + +static void a_svcrelease(admin_service *svc) +{ + a_notify("SVCRELEASE", "%s", SYM_NAME(svc), A_END); + a_svcunlink(svc); + sym_remove(&a_svcs, svc); +} + /*----- Name resolution operations ----------------------------------------*/ /* --- @a_resolved@ --- * @@ -843,7 +906,7 @@ static void a_resolve(admin *a, admin_resop *r, const char *tag, if (*p) { struct servent *s = getservbyname(av[i + 1], "udp"); if (!s) { - a_fail(a, "unknown-port", "%s", av[i + 1], A_END); + a_fail(a, "unknown-service", "%s", av[i + 1], A_END); goto fail; } pt = ntohs(s->s_port); @@ -1135,6 +1198,86 @@ static void acmd_ping(admin *a, unsigned ac, char *av[]) static void acmd_eping(admin *a, unsigned ac, char *av[]) { a_ping(a, ac, av, "eping", MISC_EPING); } +/*----- Service commands --------------------------------------------------*/ + +static void acmd_svcclaim(admin *a, unsigned ac, char *av[]) +{ + admin_service *svc; + unsigned f; + + svc = sym_find(&a_svcs, av[0], -1, sizeof(*svc), &f); + if (f) { + if (versioncmp(av[1], svc->version) <= 0) { + a_fail(a, + "service-exists", + "%s", SYM_NAME(svc), + "%s", svc->version, + A_END); + return; + } + a_write(svc->prov, "SVCCLAIM", 0, "%s", av[0], "%s", av[1], A_END); + a_svcunlink(svc); + } + svc->prov = a; + svc->version = xstrdup(av[1]); + svc->next = a->svcs; + svc->prev = 0; + if (a->svcs) a->svcs->prev = svc; + a->svcs = svc; + a_notify("SVCCLAIM", "%s", SYM_NAME(svc), "%s", svc->version, A_END); + a_ok(a); +} + +static void acmd_svcrelease(admin *a, unsigned ac, char *av[]) +{ + admin_service *svc; + + if ((svc = a_svcfind(a, av[0])) == 0) + return; + if (svc->prov != a) { + a_fail(a, "not-service-provider", "%s", SYM_NAME(svc), A_END); + return; + } + a_svcrelease(svc); + a_ok(a); +} + +static void acmd_svcensure(admin *a, unsigned ac, char *av[]) +{ + admin_service *svc; + + if ((svc = a_svcfind(a, av[0])) == 0) + return; + if (av[1] && versioncmp(svc->version, av[1]) < 0) { + a_fail(a, "service-too-old", + "%s", SYM_NAME(svc), + "%s", svc->version, + A_END); + return; + } + a_ok(a); +} + +static void acmd_svcquery(admin *a, unsigned ac, char *av[]) +{ + admin_service *svc; + + if ((svc = a_svcfind(a, av[0])) != 0) { + a_info(a, "name=%s", SYM_NAME(svc), "version=%s", svc->version, A_END); + a_ok(a); + } +} + +static void acmd_svclist(admin *a, unsigned ac, char *av[]) +{ + admin_service *svc; + sym_iter i; + + for (sym_mkiter(&i, &a_svcs); (svc = sym_next(&i)) != 0; ) + a_info(a, "%s", SYM_NAME(svc), "%s", svc->version, A_END); + a_ok(a); +} + /*----- Administration commands -------------------------------------------*/ /* --- Miscellaneous commands --- */ @@ -1475,6 +1618,11 @@ static const acmd acmdtab[] = { { "reload", 0, 0, 0, acmd_reload }, { "servinfo", 0, 0, 0, acmd_servinfo }, { "setifname", "PEER NEW-NAME", 2, 2, acmd_setifname }, + { "svcclaim", "SERVICE VERSION", 2, 2, acmd_svcclaim }, + { "svcensure", "SERVICE [VERSION]", 1, 2, acmd_svcensure }, + { "svclist", 0, 0, 0, acmd_svclist }, + { "svcquery", "SERVICE", 1, 1, acmd_svcquery }, + { "svcrelease", "SERVICE", 1, 1, acmd_svcrelease }, { "stats", "PEER", 1, 1, acmd_stats }, #ifndef NTRACE { "trace", "[OPTIONS]", 0, 1, acmd_trace }, @@ -1513,6 +1661,7 @@ static void a_destroypending(void) { admin *a, *aa; admin_bgop *bg, *bbg; + admin_service *svc, *ssvc; /* --- Destroy connections marked as pending --- */ @@ -1534,6 +1683,13 @@ static void a_destroypending(void) xfree(bg); } + /* --- Release services I hold --- */ + + for (svc = a->svcs; svc; svc = ssvc) { + ssvc = svc->next; + a_svcrelease(svc); + } + /* --- Close file descriptors and selectory --- */ selbuf_destroy(&a->b); @@ -1659,6 +1815,7 @@ void a_create(int fd_in, int fd_out, unsigned f) T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); ) a->bg = 0; a->ref = 0; + a->svcs = 0; a->f = f; if (fd_in == STDIN_FILENO) a_stdin = a; fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); @@ -1743,6 +1900,10 @@ void a_init(const char *name) struct sigaction sa; size_t sz; + /* --- Create services table --- */ + + sym_create(&a_svcs); + /* --- Set up the socket address --- */ sz = strlen(name) + 1; diff --git a/server/tripe.h b/server/tripe.h index e917ddb0..1cb2d407 100644 --- a/server/tripe.h +++ b/server/tripe.h @@ -391,7 +391,14 @@ typedef struct admin_pingop { admin_bgop bg; /* Background operation header */ ping ping; /* Ping pending response */ struct timeval pingtime; /* Time last ping was sent */ -} admin_pingop; +} admin_pingop; + +typedef struct admin_service { + sym_base _b; /* Hash table base structure */ + char *version; /* The provided version */ + struct admin *prov; /* Which client provides me */ + struct admin_service *next, *prev; /* Client's list of services */ +} admin_service; typedef struct admin { struct admin *next, *prev; /* Links to next and previous */ @@ -403,6 +410,7 @@ typedef struct admin { oqueue out; /* Output buffer list */ oqueue delay; /* Delayed output buffer list */ admin_bgop *bg; /* Backgrounded operations */ + admin_service *svcs; /* Which services I provide */ selbuf b; /* Line buffer for commands */ sel_file w; /* Selector for write buffering */ } admin;