Add mail-based account recovery
[blerg.git] / common / stringring.c
1 /* Blerg is (C) 2011 The Dominion of Awesome, and is distributed under a
2  * BSD-style license.  Please see the COPYING file for details.
3  */
4
5 #include <sys/file.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/mman.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <time.h>
16 #include <stringring.h>
17
18 #define STRINGRING_DATA_CHECK() \
19         if (sr == NULL)         \
20                 return 0;       \
21         if (data == NULL)       \
22                 return 0;       \
23         if (data[0] == 0)       \
24                 return 0;
25
26
27 struct stringring * stringring_open(const char *filename) {
28         struct stringring *sr;
29         struct stat st;
30         int initialize = 0;
31
32         sr = malloc(sizeof(struct stringring));
33         if (sr == NULL) {
34                 perror("stringring_handle malloc");
35                 return NULL;
36         }
37
38         sr->fd = open(filename, O_RDWR | O_CREAT, 0600);
39         flock(sr->fd, LOCK_EX);
40         if (sr->fd == -1) {
41                 perror("stringring open");
42                 goto stringring_open__open_failed;
43         }
44
45         fstat(sr->fd, &st);
46         if (st.st_size < sizeof(struct stringring_block)) {
47                 ftruncate(sr->fd, sizeof(struct stringring_block));
48                 initialize = 1;
49         }
50         sr->sb = mmap(NULL, sizeof(struct stringring_block), PROT_READ | PROT_WRITE, MAP_SHARED, sr->fd, 0);
51         if (sr->sb == MAP_FAILED) {
52                 perror("stringring mmap");
53                 goto stringring_open__map_failed;
54         }
55
56         if (initialize) {
57                 stringring_clear(sr);
58         }
59         flock(sr->fd, LOCK_UN);
60
61         return sr;
62
63 stringring_open__map_failed:
64         flock(sr->fd, LOCK_UN);
65         close(sr->fd);
66 stringring_open__open_failed:
67         free(sr);
68         return NULL;
69 }
70
71 void stringring_close(struct stringring *sr) {
72         if (sr == NULL)
73                 return;
74
75         if (sr->sb != NULL)
76                 munmap(sr->sb, sizeof(struct stringring_block));
77
78         if (sr->fd > -1)
79                 close(sr->fd);
80
81         free(sr);
82 }
83
84 int stringring_add(struct stringring *sr, const char *data) {
85         STRINGRING_DATA_CHECK();
86
87         flock(sr->fd, LOCK_EX);
88         if (sr->sb->counter == 0) {
89                 sr->sb->counter = STRINGRING_N_ENTRIES - 1;
90         } else {
91                 sr->sb->counter--;
92         }
93
94         int data_len = strlen(data);
95         sr->sb->entries[sr->sb->counter].timestamp = time(NULL);
96         strncpy((char *)sr->sb->entries[sr->sb->counter].data, data, STRINGRING_DATA_SIZE);
97         if (data_len < STRINGRING_DATA_SIZE) {
98                 /* zero out the rest of the data */
99                 memset(sr->sb->entries[sr->sb->counter].data + data_len, 0, STRINGRING_DATA_SIZE - data_len);
100         }
101         flock(sr->fd, LOCK_UN);
102
103         return 1;
104 }
105
106 int stringring_find_unlocked(struct stringring *sr, const char *data, uint64_t cutoff) {
107         int n, i;
108
109         i = sr->sb->counter;
110         for (n = 0; n < STRINGRING_N_ENTRIES; n++) {
111                 if (sr->sb->entries[i].timestamp > 0 &&
112                     sr->sb->entries[i].timestamp > cutoff &&
113                     strncmp((char *)sr->sb->entries[i].data, data, STRINGRING_DATA_SIZE) == 0) {
114                         return i;
115                 }
116                 i = (i + 1) % STRINGRING_N_ENTRIES;
117         }
118
119         return -1;
120 }
121
122 int stringring_find(struct stringring *sr, const char *data, unsigned int max_age) {
123         int ret;
124         uint64_t cutoff = (max_age > 0 ? time(NULL) - max_age : 0);
125
126         flock(sr->fd, LOCK_SH);
127         ret = stringring_find_unlocked(sr, data, cutoff);
128         flock(sr->fd, LOCK_UN);
129
130         return ret;
131 }
132
133 int stringring_remove_index_unlocked(struct stringring *sr, int idx) {
134         if (sr == NULL)
135                 return 0;
136         if (idx < 0 || idx >= STRINGRING_N_ENTRIES)
137                 return 0;
138
139         sr->sb->entries[idx].timestamp = 0;
140         memset(sr->sb->entries[idx].data, 0, STRINGRING_DATA_SIZE);
141
142         return 1;
143 }
144
145 int stringring_remove_index(struct stringring *sr, int idx) {
146         int ret;
147
148         flock(sr->fd, LOCK_EX);
149         ret = stringring_remove_index_unlocked(sr, idx);
150         flock(sr->fd, LOCK_UN);
151
152         return ret;
153 }
154
155 int stringring_touch(struct stringring *sr, const char *data) {
156         STRINGRING_DATA_CHECK();
157         int ret, idx;
158
159         flock(sr->fd, LOCK_EX);
160         idx = stringring_find_unlocked(sr, data, 0);
161         ret = idx >= 0;
162         if (ret) {
163                 sr->sb->entries[idx].timestamp = time(NULL);
164         }
165         flock(sr->fd, LOCK_UN);
166
167         return ret;
168 }
169
170 int stringring_remove(struct stringring *sr, const char *data) {
171         STRINGRING_DATA_CHECK();
172         int ret;
173
174         flock(sr->fd, LOCK_EX);
175         ret = stringring_remove_index_unlocked(sr,
176                         stringring_find_unlocked(sr, data, 0));
177         flock(sr->fd, LOCK_UN);
178
179         return ret;
180 }
181
182 int stringring_clear(struct stringring *sr) {
183         if (sr == NULL)
184                 return 0;
185
186         flock(sr->fd, LOCK_EX);
187         memset(sr->sb, 0, sizeof(struct stringring_block));
188         flock(sr->fd, LOCK_UN);
189
190         return 1;
191 }