1 /* distcache, Distributed Session Caching technology
2 * Copyright (C) 2000-2003 Geoff Thorpe, and Cryptographic Appliances, Inc.
3 * Copyright (C) 2004 The Distcache.org project
4 *
5 * This library is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License as published by the Free
7 * Software Foundation; using version 2.1 of the License. The copyright holders
8 * may elect to allow the application of later versions of the License to this
9 * software, please contact the author (geoff@distcache.org) if you wish us to
10 * review any later version released by the Free Software Foundation.
11 *
12 * This library is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "swamp.h"
23
24 /*************/
25 /* Constants */
26 /*************/
27
28 /* Do we do reference count debugging on dist_pattern? */
29 /* #define REF_DEBUG */
30
31 /* Maximum number of servers we store in a list */
32 #define MAX_SERVERS 1000 /* XXX arbitary */
33
34 /* Maximum number of server distribution patterns to allow. */
35 #define MAX_DISTRIBUTE_PATTERNS 1000 /* XXX arbitary */
36
37 /******************************/
38 /* Un-opaque our opaque types */
39 /******************************/
40
41 /* This type represents the pattern and order of servers to swamp */
42 struct st_dist_pattern {
43 NAL_ADDRESS *items[MAX_SERVERS];
44 unsigned int num;
45 unsigned int idx[MAX_DISTRIBUTE_PATTERNS];
46 unsigned int period;
47 unsigned int references;
48 unsigned int start_idx;
49 };
50
51 /**************************/
52 /* dist_pattern functions */
53 /**************************/
54
55 /* A function used in the distribute_cursor code to "get" a reference to an
56 * existing pattern. */
dist_pattern_up(dist_pattern * p)57 void dist_pattern_up(dist_pattern *p)
58 {
59 (p->references)++;
60 #ifdef REF_DEBUG
61 printf("REF_DEBUG: dist_pattern_up(%08x), ref count now %u\n",
62 (unsigned int)p, p->references);
63 #endif
64 }
65
66 /* Create/initialise a distribution_pattern structure ready for use */
dist_pattern_new(void)67 dist_pattern *dist_pattern_new(void)
68 {
69 dist_pattern *p = SYS_malloc(dist_pattern, 1);
70 if(!p)
71 return NULL;
72 p->num = 0;
73 p->period = 0;
74 p->references = 1;
75 p->start_idx = 0;
76 #ifdef REF_DEBUG
77 printf("REF_DEBUG: dist_pattern_new(%08x), ref count now %u\n",
78 (unsigned int)p, p->references);
79 #endif
80 return p;
81 }
82
dist_pattern_free(dist_pattern * p)83 void dist_pattern_free(dist_pattern *p)
84 {
85 (p->references)--;
86 #ifdef REF_DEBUG
87 printf("REF_DEBUG: dist_pattern_free(%08x), ref count now %u\n",
88 (unsigned int)p, p->references);
89 #endif
90 if(p->references < 0) {
91 assert(NULL == "shouldn't happen!");
92 abort();
93 } else if(p->references == 0)
94 SYS_free(dist_pattern, p);
95 }
96
dist_pattern_get_start_idx(dist_pattern * p)97 unsigned int dist_pattern_get_start_idx(dist_pattern *p)
98 {
99 unsigned int toret = p->start_idx++;
100 if(p->start_idx >= p->period)
101 p->start_idx = 0;
102 return toret;
103 }
104
105 /* Return the number of entires in the distribution pattern structure */
dist_pattern_period(dist_pattern * dist)106 unsigned int dist_pattern_period(dist_pattern *dist)
107 {
108 return dist->period;
109 }
110
111 /* Return the number of servers in the distribution pattern structure */
dist_pattern_num(dist_pattern * dist)112 unsigned int dist_pattern_num(dist_pattern *dist)
113 {
114 return dist->num;
115 }
116
117 /* Return the address of the server corresponding to the "idx"th entry in the
118 * distribution pattern, or NULL for error */
dist_pattern_get(const dist_pattern * dist,unsigned int idx)119 const NAL_ADDRESS *dist_pattern_get(const dist_pattern *dist,
120 unsigned int idx)
121 {
122 if(idx >= dist->period)
123 return NULL;
124 return dist->items[dist->idx[idx]];
125 }
126
127 /* Parses an "<hostname>:<port>" string address and places it on the top of a
128 * server_list stack */
dist_pattern_push_address(dist_pattern * dist,const char * address)129 int dist_pattern_push_address(dist_pattern *dist, const char *address)
130 {
131 NAL_ADDRESS *sa;
132
133 if((dist->num >= MAX_SERVERS) || ((sa = NAL_ADDRESS_new()) == NULL))
134 return 0;
135 if(!NAL_ADDRESS_create(sa, address, SWAMP_BUFFER_SIZE)) {
136 NAL_ADDRESS_free(sa);
137 return 0;
138 }
139 /* Parsing went OK, add this server address to our list */
140 dist->items[dist->num++] = sa;
141 return 1;
142 }
143
dist_pattern_push(dist_pattern * dist,unsigned int val)144 static dist_pattern_error_t dist_pattern_push(dist_pattern *dist,
145 unsigned int val)
146 {
147 /* Is there room in the array? */
148 if(dist->period >= MAX_DISTRIBUTE_PATTERNS)
149 return ERR_DIST_PAT_ARRAY_FULL;
150
151 /* 'val' must between 1 and (dist->num) */
152 if(!val || (val > dist->num))
153 return ERR_DIST_PAT_VALUE_OUT_OF_RANGE;
154
155 /* Everything checks out - place the server number at the end of the
156 * array. Note that we reduce the value by one as internally, servers
157 * are numbered from 0 onwards. */
158 dist->idx[dist->period++] = val - 1;
159
160 return ERR_DIST_PAT_OKAY;
161 }
162
163 /* Parse a distribution pattern string. The format is a bunch of values
164 * representing server numbers delimited by spaces and/or commas. e.g.
165 * "1, 2, 3, 4,5 6 7 8,"
166 *
167 * Values are expected to between "1" and the number of servers. If something
168 * fails, a suitable error code is returned (see also
169 * dist_pattern_error_string()).
170 *
171 * An empty string is valid - a canonical pattern is generated from the server
172 * list (if there 5 servers, a pattern of "1,2,3,4,5" is assumed). */
dist_pattern_parse(dist_pattern * dist,const char * dist_str)173 dist_pattern_error_t dist_pattern_parse(dist_pattern *dist,
174 const char *dist_str)
175 {
176 char *res;
177 unsigned long val;
178 tokeniser_t tok;
179 dist_pattern_error_t ret = ERR_DIST_PAT_OKAY;
180
181 assert(dist);
182
183 /* If 'dist_str' is NULL, generate a canonical pattern */
184 if(!dist_str) {
185 unsigned int loop = 0;
186 while(loop < dist->num) {
187 ret = dist_pattern_push(dist, ++loop);
188 if(ret != ERR_DIST_PAT_OKAY)
189 return ret;
190 }
191 return ret;
192 }
193
194 /* Now process the (non-NULL) 'dist_str' */
195 init_tokeniser(&tok, dist_str, ", ");
196 while((res = do_tokenising(&tok)) != NULL) {
197 if(!int_substrtoul(res, &val, ", ")) {
198 ret = ERR_DIST_PAT_INVALID_SYNTAX;
199 break;
200 }
201 ret = dist_pattern_push(dist, val);
202 if(ret != ERR_DIST_PAT_OKAY)
203 break;
204 }
205 free_tokeniser(&tok);
206 return ret;
207 }
208
209 /* Return a string briefly describing the problem reported by "err". N.B.
210 * this will also return something for "ERR_DIST_PAT_OKAY" which could confuse
211 * people; e.g. "error with distribute pattern: no error".
212 *
213 * This function is guaranteed to return a valid string (even if it is empty).
214 */
dist_pattern_error_string(dist_pattern_error_t err)215 const char *dist_pattern_error_string(dist_pattern_error_t err)
216 {
217 switch(err) {
218 case ERR_DIST_PAT_OKAY:
219 return "no error";
220 case ERR_DIST_PAT_VALUE_OUT_OF_RANGE:
221 return "value out of range";
222 case ERR_DIST_PAT_INVALID_SYNTAX:
223 return "invalid syntax";
224 case ERR_DIST_PAT_ARRAY_FULL:
225 return "distribute pattern array is full";
226 case ERR_DIST_PAT_INTERNAL_PROBLEM:
227 return "internal error (a bug?)";
228 }
229 /* Return something */
230 return "";
231 }
232