~ubuntu-branches/ubuntu/trusty/postfix/trusty-updates

« back to all changes in this revision

Viewing changes to src/util/dict_cache.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones, LaMont Jones, localization folks
  • Date: 2014-02-11 07:44:30 UTC
  • mfrom: (58.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20140211074430-kwkoxdz0fbajn0fj
Tags: 2.11.0-1
[LaMont Jones]

* New upstream release: 2.11.0

[localization folks]

* l10n: Updated German translations.  Closes: #734893 (Helge Kreutzmann)

Show diffs side-by-side

added added

removed removed

Lines of Context:
159
159
/*      until a cache cleanup run is completed. Some entries may
160
160
/*      never be removed when the process max_idle time is less
161
161
/*      than the time needed to make a full pass over the cache.
 
162
/*
 
163
/*      The delete-behind strategy assumes that all updates are
 
164
/*      made by a single process. Otherwise, delete-behind may
 
165
/*      remove an entry that was updated after it was scheduled for
 
166
/*      deletion.
162
167
/* LICENSE
163
168
/* .ad
164
169
/* .fi
677
682
     */
678
683
    return (cp->name);
679
684
}
 
685
 
 
686
 /*
 
687
  * Test driver with support for interleaved access. First, enter a number of
 
688
  * requests to look up, update or delete a sequence of cache entries, then
 
689
  * interleave those sequences with the "run" command.
 
690
  */
 
691
#ifdef TEST
 
692
#include <msg_vstream.h>
 
693
#include <vstring_vstream.h>
 
694
#include <argv.h>
 
695
#include <stringops.h>
 
696
 
 
697
#define DELIMS  " "
 
698
#define USAGE   "\n\tTo manage settings:" \
 
699
                "\n\tverbose <level> (verbosity level)" \
 
700
                "\n\telapsed <level> (0=don't show elapsed time)" \
 
701
                "\n\tlmdb_map_size <limit> (initial LMDB size limit)" \
 
702
                "\n\tcache <type>:<name> (switch to named database)" \
 
703
                "\n\tstatus (show map size, cache, pending requests)" \
 
704
                "\n\n\tTo manage pending requests:" \
 
705
                "\n\treset (discard pending requests)" \
 
706
                "\n\trun (execute pending requests in interleaved order)" \
 
707
                "\n\n\tTo add a pending request:" \
 
708
                "\n\tquery <key-suffix> <count> (negative to reverse order)" \
 
709
                "\n\tupdate <key-suffix> <count> (negative to reverse order)" \
 
710
                "\n\tdelete <key-suffix> <count> (negative to reverse order)" \
 
711
                "\n\tpurge <key-suffix>" \
 
712
                "\n\tcount <key-suffix>"
 
713
 
 
714
 /*
 
715
  * For realism, open the cache with the same flags as postscreen(8) and
 
716
  * verify(8).
 
717
  */
 
718
#define DICT_CACHE_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE | \
 
719
        DICT_FLAG_OPEN_LOCK)
 
720
 
 
721
 /*
 
722
  * Storage for one request to access a sequence of cache entries.
 
723
  */
 
724
typedef struct DICT_CACHE_SREQ {
 
725
    int     flags;                      /* per-request: reverse, purge */
 
726
    char   *cmd;                        /* command for status report */
 
727
    void    (*action) (struct DICT_CACHE_SREQ *, DICT_CACHE *, VSTRING *);
 
728
    char   *suffix;                     /* key suffix */
 
729
    int     done;                       /* progress indicator */
 
730
    int     todo;                       /* number of entries to process */
 
731
    int     first_next;                 /* first/next */
 
732
} DICT_CACHE_SREQ;
 
733
 
 
734
#define DICT_CACHE_SREQ_FLAG_PURGE      (1<<1)  /* purge instead of count */
 
735
#define DICT_CACHE_SREQ_FLAG_REVERSE    (1<<2)  /* reverse instead of forward */
 
736
 
 
737
#define DICT_CACHE_SREQ_LIMIT           10
 
738
 
 
739
 /*
 
740
  * All test requests combined.
 
741
  */
 
742
typedef struct DICT_CACHE_TEST {
 
743
    int     flags;                      /* exclusion flags */
 
744
    int     size;                       /* allocated slots */
 
745
    int     used;                       /* used slots */
 
746
    DICT_CACHE_SREQ job_list[1];        /* actually, a bunch */
 
747
} DICT_CACHE_TEST;
 
748
 
 
749
#define DICT_CACHE_TEST_FLAG_ITER       (1<<0)  /* count or purge */
 
750
 
 
751
#define STR(x)  vstring_str(x)
 
752
 
 
753
int     show_elapsed = 1;               /* show elapsed time */
 
754
 
 
755
#ifdef HAS_LMDB
 
756
extern size_t dict_lmdb_map_size;       /* LMDB-specific */
 
757
 
 
758
#endif
 
759
 
 
760
/* usage - command-line usage message */
 
761
 
 
762
static NORETURN usage(const char *progname)
 
763
{
 
764
    msg_fatal("usage: %s (no argument)", progname);
 
765
}
 
766
 
 
767
/* make_tagged_key - make tagged search key */
 
768
 
 
769
static void make_tagged_key(VSTRING *bp, DICT_CACHE_SREQ *cp)
 
770
{
 
771
    if (cp->done < 0)
 
772
        msg_panic("make_tagged_key: bad done count: %d", cp->done);
 
773
    if (cp->todo < 1)
 
774
        msg_panic("make_tagged_key: bad todo count: %d", cp->todo);
 
775
    vstring_sprintf(bp, "%d-%s",
 
776
                    (cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
 
777
                    cp->todo - cp->done - 1 : cp->done, cp->suffix);
 
778
}
 
779
 
 
780
/* create_requests - create request list */
 
781
 
 
782
static DICT_CACHE_TEST *create_requests(int count)
 
783
{
 
784
    DICT_CACHE_TEST *tp;
 
785
    DICT_CACHE_SREQ *cp;
 
786
 
 
787
    tp = (DICT_CACHE_TEST *) mymalloc(sizeof(DICT_CACHE_TEST) +
 
788
                                      (count - 1) *sizeof(DICT_CACHE_SREQ));
 
789
    tp->flags = 0;
 
790
    tp->size = count;
 
791
    tp->used = 0;
 
792
    for (cp = tp->job_list; cp < tp->job_list + count; cp++) {
 
793
        cp->flags = 0;
 
794
        cp->cmd = 0;
 
795
        cp->action = 0;
 
796
        cp->suffix = 0;
 
797
        cp->todo = 0;
 
798
        cp->first_next = DICT_SEQ_FUN_FIRST;
 
799
    }
 
800
    return (tp);
 
801
}
 
802
 
 
803
/* reset_requests - reset request list */
 
804
 
 
805
static void reset_requests(DICT_CACHE_TEST *tp)
 
806
{
 
807
    DICT_CACHE_SREQ *cp;
 
808
 
 
809
    tp->flags = 0;
 
810
    tp->used = 0;
 
811
    for (cp = tp->job_list; cp < tp->job_list + tp->size; cp++) {
 
812
        cp->flags = 0;
 
813
        if (cp->cmd) {
 
814
            myfree(cp->cmd);
 
815
            cp->cmd = 0;
 
816
        }
 
817
        cp->action = 0;
 
818
        if (cp->suffix) {
 
819
            myfree(cp->suffix);
 
820
            cp->suffix = 0;
 
821
        }
 
822
        cp->todo = 0;
 
823
        cp->first_next = DICT_SEQ_FUN_FIRST;
 
824
    }
 
825
}
 
826
 
 
827
/* free_requests - destroy request list */
 
828
 
 
829
static void free_requests(DICT_CACHE_TEST *tp)
 
830
{
 
831
    reset_requests(tp);
 
832
    myfree((char *) tp);
 
833
}
 
834
 
 
835
/* run_requests - execute pending requests in interleaved order */
 
836
 
 
837
static void run_requests(DICT_CACHE_TEST *tp, DICT_CACHE *dp, VSTRING *bp)
 
838
{
 
839
    DICT_CACHE_SREQ *cp;
 
840
    int     todo;
 
841
    struct timeval start;
 
842
    struct timeval finish;
 
843
    struct timeval elapsed;
 
844
 
 
845
    if (dp == 0) {
 
846
        msg_warn("no cache");
 
847
        return;
 
848
    }
 
849
    GETTIMEOFDAY(&start);
 
850
    do {
 
851
        todo = 0;
 
852
        for (cp = tp->job_list; cp < tp->job_list + tp->used; cp++) {
 
853
            if (cp->done < cp->todo) {
 
854
                todo = 1;
 
855
                cp->action(cp, dp, bp);
 
856
            }
 
857
        }
 
858
    } while (todo);
 
859
    GETTIMEOFDAY(&finish);
 
860
    timersub(&finish, &start, &elapsed);
 
861
    if (show_elapsed)
 
862
        vstream_printf("Elapsed: %g\n",
 
863
                       elapsed.tv_sec + elapsed.tv_usec / 1000000.0);
 
864
 
 
865
    reset_requests(tp);
 
866
}
 
867
 
 
868
/* show_status - show settings and pending requests */
 
869
 
 
870
static void show_status(DICT_CACHE_TEST *tp, DICT_CACHE *dp)
 
871
{
 
872
    DICT_CACHE_SREQ *cp;
 
873
 
 
874
#ifdef HAS_LMDB
 
875
    vstream_printf("lmdb_map_size\t%ld\n", (long) dict_lmdb_map_size);
 
876
#endif
 
877
    vstream_printf("cache\t%s\n", dp ? dp->name : "(none)");
 
878
 
 
879
    if (tp->used == 0)
 
880
        vstream_printf("No pending requests\n");
 
881
    else
 
882
        vstream_printf("%s\t%s\t%s\t%s\t%s\t%s\n",
 
883
                     "cmd", "dir", "suffix", "count", "done", "first/next");
 
884
 
 
885
    for (cp = tp->job_list; cp < tp->job_list + tp->used; cp++)
 
886
        if (cp->todo > 0)
 
887
            vstream_printf("%s\t%s\t%s\t%d\t%d\t%d\n",
 
888
                           cp->cmd,
 
889
                           (cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
 
890
                           "reverse" : "forward",
 
891
                           cp->suffix ? cp->suffix : "(null)", cp->todo,
 
892
                           cp->done, cp->first_next);
 
893
}
 
894
 
 
895
/* query_action - lookup cache entry */
 
896
 
 
897
static void query_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
 
898
{
 
899
    const char *lookup;
 
900
 
 
901
    make_tagged_key(bp, cp);
 
902
    if ((lookup = dict_cache_lookup(dp, STR(bp))) == 0) {
 
903
        if (dp->error)
 
904
            msg_warn("query_action: query failed: %s: %m", STR(bp));
 
905
        else
 
906
            msg_warn("query_action: query failed: %s", STR(bp));
 
907
    } else if (strcmp(STR(bp), lookup) != 0) {
 
908
        msg_warn("lookup result \"%s\" differs from key \"%s\"",
 
909
                 lookup, STR(bp));
 
910
    }
 
911
    cp->done += 1;
 
912
}
 
913
 
 
914
/* update_action - update cache entry */
 
915
 
 
916
static void update_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
 
917
{
 
918
    make_tagged_key(bp, cp);
 
919
    if (dict_cache_update(dp, STR(bp), STR(bp)) != 0) {
 
920
        if (dp->error)
 
921
            msg_warn("update_action: update failed: %s: %m", STR(bp));
 
922
        else
 
923
            msg_warn("update_action: update failed: %s", STR(bp));
 
924
    }
 
925
    cp->done += 1;
 
926
}
 
927
 
 
928
/* delete_action - delete cache entry */
 
929
 
 
930
static void delete_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
 
931
{
 
932
    make_tagged_key(bp, cp);
 
933
    if (dict_cache_delete(dp, STR(bp)) != 0) {
 
934
        if (dp->error)
 
935
            msg_warn("delete_action: delete failed: %s: %m", STR(bp));
 
936
        else
 
937
            msg_warn("delete_action: delete failed: %s", STR(bp));
 
938
    }
 
939
    cp->done += 1;
 
940
}
 
941
 
 
942
/* iter_action - iterate over cache and act on entries with given suffix */
 
943
 
 
944
static void iter_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
 
945
{
 
946
    const char *cache_key;
 
947
    const char *cache_val;
 
948
    const char *what;
 
949
    const char *suffix;
 
950
 
 
951
    if (dict_cache_sequence(dp, cp->first_next, &cache_key, &cache_val) == 0) {
 
952
        if (strcmp(cache_key, cache_val) != 0)
 
953
            msg_warn("value \"%s\" differs from key \"%s\"",
 
954
                     cache_val, cache_key);
 
955
        suffix = cache_key + strspn(cache_key, "0123456789");
 
956
        if (suffix[0] == '-' && strcmp(suffix + 1, cp->suffix) == 0) {
 
957
            cp->done += 1;
 
958
            cp->todo = cp->done + 1;            /* XXX */
 
959
            if ((cp->flags & DICT_CACHE_SREQ_FLAG_PURGE)
 
960
                && dict_cache_delete(dp, cache_key) != 0) {
 
961
                if (dp->error)
 
962
                    msg_warn("purge_action: delete failed: %s: %m", STR(bp));
 
963
                else
 
964
                    msg_warn("purge_action: delete failed: %s", STR(bp));
 
965
            }
 
966
        }
 
967
        cp->first_next = DICT_SEQ_FUN_NEXT;
 
968
    } else {
 
969
        what = (cp->flags & DICT_CACHE_SREQ_FLAG_PURGE) ? "purge" : "count";
 
970
        if (dp->error)
 
971
            msg_warn("%s error after %d: %m", what, cp->done);
 
972
        else
 
973
            vstream_printf("suffix=%s %s=%d\n", cp->suffix, what, cp->done);
 
974
        cp->todo = 0;
 
975
    }
 
976
}
 
977
 
 
978
 /*
 
979
  * Table-driven support.
 
980
  */
 
981
typedef struct DICT_CACHE_SREQ_INFO {
 
982
    const char *name;
 
983
    int     argc;
 
984
    void    (*action) (DICT_CACHE_SREQ *, DICT_CACHE *, VSTRING *);
 
985
    int     test_flags;
 
986
    int     req_flags;
 
987
} DICT_CACHE_SREQ_INFO;
 
988
 
 
989
static DICT_CACHE_SREQ_INFO req_info[] = {
 
990
    {"query", 3, query_action},
 
991
    {"update", 3, update_action},
 
992
    {"delete", 3, delete_action},
 
993
    {"count", 2, iter_action, DICT_CACHE_TEST_FLAG_ITER},
 
994
    {"purge", 2, iter_action, DICT_CACHE_TEST_FLAG_ITER, DICT_CACHE_SREQ_FLAG_PURGE},
 
995
    0,
 
996
};
 
997
 
 
998
/* add_request - add a request to the list */
 
999
 
 
1000
static void add_request(DICT_CACHE_TEST *tp, ARGV *argv)
 
1001
{
 
1002
    DICT_CACHE_SREQ_INFO *rp;
 
1003
    DICT_CACHE_SREQ *cp;
 
1004
    int     req_flags;
 
1005
    int     count;
 
1006
    char   *cmd = argv->argv[0];
 
1007
    char   *suffix = (argv->argc > 1 ? argv->argv[1] : 0);
 
1008
    char   *todo = (argv->argc > 2 ? argv->argv[2] : "1");      /* XXX */
 
1009
 
 
1010
    if (tp->used >= tp->size) {
 
1011
        msg_warn("%s: request list is full", cmd);
 
1012
        return;
 
1013
    }
 
1014
    for (rp = req_info; /* See below */ ; rp++) {
 
1015
        if (rp->name == 0) {
 
1016
            vstream_printf("usage: %s\n", USAGE);
 
1017
            return;
 
1018
        }
 
1019
        if (strcmp(rp->name, argv->argv[0]) == 0
 
1020
            && rp->argc == argv->argc)
 
1021
            break;
 
1022
    }
 
1023
    req_flags = rp->req_flags;
 
1024
    if (todo[0] == '-') {
 
1025
        req_flags |= DICT_CACHE_SREQ_FLAG_REVERSE;
 
1026
        todo += 1;
 
1027
    }
 
1028
    if (!alldig(todo) || (count = atoi(todo)) == 0) {
 
1029
        msg_warn("%s: bad count: %s", cmd, todo);
 
1030
        return;
 
1031
    }
 
1032
    if (tp->flags & rp->test_flags) {
 
1033
        msg_warn("%s: command conflicts with other command", cmd);
 
1034
        return;
 
1035
    }
 
1036
    tp->flags |= rp->test_flags;
 
1037
    cp = tp->job_list + tp->used;
 
1038
    cp->cmd = mystrdup(cmd);
 
1039
    cp->action = rp->action;
 
1040
    if (suffix)
 
1041
        cp->suffix = mystrdup(suffix);
 
1042
    cp->done = 0;
 
1043
    cp->flags = req_flags;
 
1044
    cp->todo = count;
 
1045
    tp->used += 1;
 
1046
}
 
1047
 
 
1048
/* main - main program */
 
1049
 
 
1050
int     main(int argc, char **argv)
 
1051
{
 
1052
    DICT_CACHE_TEST *test_job;
 
1053
    VSTRING *inbuf = vstring_alloc(100);
 
1054
    char   *bufp;
 
1055
    ARGV   *args;
 
1056
    DICT_CACHE *cache = 0;
 
1057
    int     stdin_is_tty;
 
1058
 
 
1059
    msg_vstream_init(argv[0], VSTREAM_ERR);
 
1060
    if (argc != 1)
 
1061
        usage(argv[0]);
 
1062
 
 
1063
 
 
1064
    test_job = create_requests(DICT_CACHE_SREQ_LIMIT);
 
1065
 
 
1066
    stdin_is_tty = isatty(0);
 
1067
 
 
1068
    for (;;) {
 
1069
        if (stdin_is_tty) {
 
1070
            vstream_printf("> ");
 
1071
            vstream_fflush(VSTREAM_OUT);
 
1072
        }
 
1073
        if (vstring_fgets_nonl(inbuf, VSTREAM_IN) == 0)
 
1074
            break;
 
1075
        bufp = vstring_str(inbuf);
 
1076
        if (!stdin_is_tty) {
 
1077
            vstream_printf("> %s\n", bufp);
 
1078
            vstream_fflush(VSTREAM_OUT);
 
1079
        }
 
1080
        if (*bufp == '#')
 
1081
            continue;
 
1082
        args = argv_split(bufp, DELIMS);
 
1083
        if (argc == 0) {
 
1084
            vstream_printf("usage: %s\n", USAGE);
 
1085
            vstream_fflush(VSTREAM_OUT);
 
1086
            continue;
 
1087
        }
 
1088
        if (strcmp(args->argv[0], "verbose") == 0 && args->argc == 2) {
 
1089
            msg_verbose = atoi(args->argv[1]);
 
1090
        } else if (strcmp(args->argv[0], "elapsed") == 0 && args->argc == 2) {
 
1091
            show_elapsed = atoi(args->argv[1]);
 
1092
#ifdef HAS_LMDB
 
1093
        } else if (strcmp(args->argv[0], "lmdb_map_size") == 0 && args->argc == 2) {
 
1094
            dict_lmdb_map_size = atol(args->argv[1]);
 
1095
#endif
 
1096
        } else if (strcmp(args->argv[0], "cache") == 0 && args->argc == 2) {
 
1097
            if (cache)
 
1098
                dict_cache_close(cache);
 
1099
            cache = dict_cache_open(args->argv[1], O_CREAT | O_RDWR,
 
1100
                                    DICT_CACHE_OPEN_FLAGS);
 
1101
        } else if (strcmp(args->argv[0], "reset") == 0 && args->argc == 1) {
 
1102
            reset_requests(test_job);
 
1103
        } else if (strcmp(args->argv[0], "run") == 0 && args->argc == 1) {
 
1104
            run_requests(test_job, cache, inbuf);
 
1105
        } else if (strcmp(args->argv[0], "status") == 0 && args->argc == 1) {
 
1106
            show_status(test_job, cache);
 
1107
        } else {
 
1108
            add_request(test_job, args);
 
1109
        }
 
1110
        vstream_fflush(VSTREAM_OUT);
 
1111
        argv_free(args);
 
1112
    }
 
1113
 
 
1114
    vstring_free(inbuf);
 
1115
    free_requests(test_job);
 
1116
    if (cache)
 
1117
        dict_cache_close(cache);
 
1118
    return (0);
 
1119
}
 
1120
 
 
1121
#endif