Merge in changes to allow for weights on hosts.
[awesomized/libmemcached] / libtest / server.c
1 /* LibMemcached
2 * Copyright (C) 2006-2009 Brian Aker
3 * All rights reserved.
4 *
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
7 *
8 * Summary:
9 *
10 */
11
12 /*
13 Startup, and shutdown the memcached servers.
14 */
15
16 #define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10
17
18 #define PID_FILE_BASE "/tmp/%ulibmemcached_memc.pid"
19
20 #include "config.h"
21
22 #include <assert.h>
23 #include <limits.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <errno.h>
32
33 #include <libmemcached/memcached.h>
34 #include <libmemcached/util.h>
35
36 #include <libtest/server.h>
37
38 static struct timespec global_sleep_value= { .tv_sec= 0, .tv_nsec= 50000 };
39
40 static void global_sleep(void)
41 {
42 #ifdef WIN32
43 sleep(1);
44 #else
45 nanosleep(&global_sleep_value, NULL);
46 #endif
47 }
48
49 static bool wait_for_file(const char *filename)
50 {
51 uint32_t timeout= 6;
52 uint32_t waited;
53 uint32_t this_wait;
54 uint32_t retry;
55
56 for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
57 {
58 if ((! access(filename, R_OK)) || (waited >= timeout))
59 {
60 return true;
61 }
62
63 this_wait= retry * retry / 3 + 1;
64 sleep(this_wait);
65 }
66
67 return false;
68 }
69
70 static void kill_file(const char *file_buffer)
71 {
72 FILE *fp;
73
74 while ((fp= fopen(file_buffer, "r")))
75 {
76 char pid_buffer[1024];
77
78 if (fgets(pid_buffer, sizeof(pid_buffer), fp) != NULL)
79 {
80 pid_t pid= (pid_t)atoi(pid_buffer);
81 if (pid != 0)
82 {
83 if (kill(pid, SIGTERM) == -1)
84 {
85 remove(file_buffer); // If this happens we may be dealing with a dead server that left its pid file.
86 }
87 else
88 {
89 uint32_t counter= 3;
90 while ((kill(pid, 0) == 0) && --counter)
91 {
92 global_sleep();
93 }
94 }
95 }
96 }
97
98 global_sleep();
99
100 fclose(fp);
101 }
102 }
103
104 void server_startup(server_startup_st *construct)
105 {
106 if ((construct->server_list= getenv("MEMCACHED_SERVERS")))
107 {
108 printf("servers %s\n", construct->server_list);
109 construct->servers= memcached_servers_parse(construct->server_list);
110 construct->server_list= NULL;
111 construct->count= 0;
112 }
113 else
114 {
115 {
116 char server_string_buffer[8096];
117 char *end_ptr;
118 end_ptr= server_string_buffer;
119
120 for (uint32_t x= 0; x < construct->count; x++)
121 {
122 int status;
123 in_port_t port;
124
125 {
126 char *var;
127 char variable_buffer[1024];
128
129 snprintf(variable_buffer, sizeof(variable_buffer), "LIBMEMCACHED_PORT_%u", x);
130
131 if ((var= getenv(variable_buffer)))
132 {
133 port= (in_port_t)atoi(var);
134 }
135 else
136 {
137 port= (in_port_t)(x + TEST_PORT_BASE);
138 }
139 }
140
141 char buffer[PATH_MAX];
142 snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x);
143 kill_file(buffer);
144
145 if (x == 0)
146 {
147 snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u -m 128",
148 MEMCACHED_BINARY, x, port, port);
149 }
150 else
151 {
152 snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u",
153 MEMCACHED_BINARY, x, port, port);
154 }
155 if (libmemcached_util_ping("localhost", port, NULL))
156 {
157 fprintf(stderr, "Server on port %u already exists\n", port);
158 }
159 else
160 {
161 status= system(buffer);
162 fprintf(stderr, "STARTING SERVER: %s status:%d\n", buffer, status);
163 }
164 int count;
165 size_t remaining_length= sizeof(server_string_buffer) - (size_t)(end_ptr -server_string_buffer);
166 count= snprintf(end_ptr, remaining_length, "localhost:%u,", port);
167
168 if ((size_t)count >= remaining_length || count < 0)
169 {
170 fprintf(stderr, "server names grew to be larger then buffer allowed\n");
171 abort();
172 }
173 end_ptr+= count;
174 }
175 *end_ptr= 0;
176
177
178 int *pids= calloc(construct->count, sizeof(int));
179 for (uint32_t x= 0; x < construct->count; x++)
180 {
181 char buffer[PATH_MAX]; /* Nothing special for number */
182
183 snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x);
184
185 uint32_t counter= 3000; // Absurd, just to catch run away process
186 while (pids[x] <= 0 && --counter)
187 {
188 FILE *file= fopen(buffer, "r");
189 if (file)
190 {
191 char pid_buffer[1024];
192 char *found= fgets(pid_buffer, sizeof(pid_buffer), file);
193
194 if (found)
195 {
196 pids[x]= atoi(pid_buffer);
197 fclose(file);
198
199 if (pids[x] > 0)
200 break;
201 }
202 fclose(file);
203 }
204 switch (errno)
205 {
206 default:
207 fprintf(stderr, "%s\n", strerror(errno));
208 abort();
209 case ENOENT:
210 case EINTR:
211 case EACCES:
212 break;
213 }
214
215 if (! wait_for_file(buffer))
216 {
217 abort();
218 }
219 }
220
221 bool was_started= false;
222 if (pids[x] > 0)
223 {
224 counter= 30;
225 while (--counter)
226 {
227 if (kill(pids[x], 0) == 0)
228 {
229 was_started= true;
230 break;
231 }
232 global_sleep();
233 }
234 }
235
236 if (was_started == false)
237 {
238 fprintf(stderr, "Failed to open buffer %s(%d)\n", buffer, pids[x]);
239 for (uint32_t y= 0; y < construct->count; y++)
240 {
241 if (pids[y] > 0)
242 kill(pids[y], SIGTERM);
243 }
244 abort();
245 }
246 }
247 free(pids);
248
249 construct->server_list= strdup(server_string_buffer);
250 }
251 printf("servers %s\n", construct->server_list);
252 construct->servers= memcached_servers_parse(construct->server_list);
253 }
254
255 assert(construct->servers);
256
257 srandom((unsigned int)time(NULL));
258
259 for (uint32_t x= 0; x < memcached_server_list_count(construct->servers); x++)
260 {
261 printf("\t%s : %d\n", memcached_server_name(&construct->servers[x]), memcached_server_port(&construct->servers[x]));
262 assert(construct->servers[x].fd == -1);
263 assert(construct->servers[x].cursor_active == 0);
264 }
265
266 printf("\n");
267 }
268
269 void server_shutdown(server_startup_st *construct)
270 {
271 if (construct->server_list)
272 {
273 for (uint32_t x= 0; x < construct->count; x++)
274 {
275 char file_buffer[PATH_MAX]; /* Nothing special for number */
276 snprintf(file_buffer, sizeof(file_buffer), PID_FILE_BASE, x);
277 kill_file(file_buffer);
278 }
279
280 free(construct->server_list);
281 }
282 }