diff --git a/doc/management.txt b/doc/management.txt index 96660e7f1d..89841bdc08 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1557,6 +1557,35 @@ clear table [ data. ] | [ key ] $ echo "show table http_proxy" | socat stdio /tmp/sock1 >>> # table: http_proxy, type: ip, size:204800, used:1 +commit acl @ + Commit all changes made to version of ACL , and deletes all past + versions. is the # or the returned by "show acl". The + version number must be between "curr_ver"+1 and "next_ver" as reported in + "show acl". The contents to be committed to the ACL can be consulted with + "show acl @ " if desired. The specified version number has normally + been created with the "prepare acl" command. The replacement is atomic. It + consists in atomically updating the current version to the specified version, + which will instantly cause all entries in other versions to become invisible, + and all entries in the new version to become visible. It is also possible to + use this command to perform an atomic removal of all visible entries of an + ACL by calling "prepare acl" first then committing without adding any + entries. This command cannot be used if the reference is a file also + used as a map. In this case, the "commit map" command must be used instead. + +commit map @ + Commit all changes made to version of map , and deletes all past + versions. is the # or the returned by "show map". The + version number must be between "curr_ver"+1 and "next_ver" as reported in + "show map". The contents to be committed to the map can be consulted with + "show map @ " if desired. The specified version number has normally + been created with the "prepare map" command. The replacement is atomic. It + consists in atomically updating the current version to the specified version, + which will instantly cause all entries in other versions to become invisible, + and all entries in the new version to become visible. It is also possible to + use this command to perform an atomic removal of all visible entries of an + map by calling "prepare map" first then committing without adding any + entries. + commit ssl cert Commit a temporary SSL certificate update transaction. diff --git a/src/map.c b/src/map.c index 8ac261a116..d2781c2ae3 100644 --- a/src/map.c +++ b/src/map.c @@ -1040,17 +1040,83 @@ static int cli_parse_clear_map(char **args, char *payload, struct appctx *appctx return 1; } +/* note: sets appctx->ctx.cli.i0 and appctx->ctx.cli.i1 to the oldest and + * latest generations to clear, respectively, and will call the clear_map + * handler. + */ +static int cli_parse_commit_map(char **args, char *payload, struct appctx *appctx, void *private) +{ + if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) { + const char *gen = NULL; + uint genid; + uint ret; + + /* Set ACL or MAP flags. */ + if (args[1][0] == 'm') + appctx->ctx.map.display_flags = PAT_REF_MAP; + else + appctx->ctx.map.display_flags = PAT_REF_ACL; + + if (*args[2] != '@') + return cli_err(appctx, "Missing version number.\n"); + + /* The generation number is mandatory for a commit. The range + * of generations that get trashed by a commit starts from the + * opposite of the current one and ends at the previous one. + */ + gen = args[2] + 1; + genid = str2uic(gen); + appctx->ctx.cli.i1 = genid - 1; + appctx->ctx.cli.i0 = appctx->ctx.cli.i1 - ((~0U) >> 1); + + /* no parameter */ + if (!*args[3]) { + if (appctx->ctx.map.display_flags == PAT_REF_MAP) + return cli_err(appctx, "Missing map identifier.\n"); + else + return cli_err(appctx, "Missing ACL identifier.\n"); + } + + /* lookup into the refs and check the map flag */ + appctx->ctx.map.ref = pat_ref_lookup_ref(args[3]); + if (!appctx->ctx.map.ref || + !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) { + if (appctx->ctx.map.display_flags == PAT_REF_MAP) + return cli_err(appctx, "Unknown map identifier. Please use # or .\n"); + else + return cli_err(appctx, "Unknown ACL identifier. Please use # or .\n"); + } + + HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock); + if (genid - (appctx->ctx.map.ref->curr_gen + 1) < + appctx->ctx.map.ref->next_gen - appctx->ctx.map.ref->curr_gen) + ret = pat_ref_commit(appctx->ctx.map.ref, genid); + else + ret = 1; + HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock); + + if (ret != 0) + return cli_err(appctx, "Version number out of range.\n"); + + /* delegate the clearing to the I/O handler which can yield */ + return 0; + } + return 1; +} + /* register cli keywords */ static struct cli_kw_list cli_kws = {{ },{ { { "add", "acl", NULL }, "add acl : add acl entry", cli_parse_add_map, NULL }, { { "clear", "acl", NULL }, "clear acl [@ver] : clear the content of this acl", cli_parse_clear_map, cli_io_handler_clear_map, NULL }, + { { "commit","acl", NULL }, "commit acl @ : commit the ACL at this version", cli_parse_commit_map, cli_io_handler_clear_map, NULL }, { { "del", "acl", NULL }, "del acl : delete acl entry", cli_parse_del_map, NULL }, { { "get", "acl", NULL }, "get acl : report the patterns matching a sample for an ACL", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook }, { { "prepare","acl",NULL }, "prepare acl : prepare a new version for atomic ACL replacement", cli_parse_prepare_map, NULL }, { { "show", "acl", NULL }, "show acl [@ver] [id] : report available acls or dump an acl's contents", cli_parse_show_map, NULL }, { { "add", "map", NULL }, "add map : add map entry", cli_parse_add_map, NULL }, { { "clear", "map", NULL }, "clear map [@ver] : clear the content of this map", cli_parse_clear_map, cli_io_handler_clear_map, NULL }, + { { "commit","map", NULL }, "commit map @ : commit the map at this version", cli_parse_commit_map, cli_io_handler_clear_map, NULL }, { { "del", "map", NULL }, "del map : delete map entry", cli_parse_del_map, NULL }, { { "get", "map", NULL }, "get map : report the keys and values matching a sample for a map", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook }, { { "prepare","map",NULL }, "prepare map : prepare a new version for atomic map replacement", cli_parse_prepare_map, NULL },