1 /*
2 Copyright Red Hat, Inc. 2010
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 2, or (at your option) any
7 later version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; see the file COPYING. If not, write to the
16 Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
17 MA 02139, USA.
18 */
19 /*
20 * Author: Lon Hohberger <lhh at redhat.com>
21 */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 #include <sys/un.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
37 #include <sys/ioctl.h>
38 #include <arpa/inet.h>
39 #include <net/if.h>
40 #include <netinet/in.h>
41 #include <netdb.h>
42 #include <sys/time.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <pthread.h>
46 #include <nss.h>
47 #include <libgen.h>
48
49 /* Local includes */
50 #include "debug.h"
51 #include "fdops.h"
52 #include "serial.h"
53 #include "list.h"
54 #include "simpleconfig.h"
55 #include "static_map.h"
56 #include "server_plugin.h"
57 #include "history.h"
58 #include "xvm.h"
59
60 #define NAME "serial"
61 #define SERIAL_VERSION "0.5"
62
63 #define SERIAL_PLUG_MAGIC 0x1227a000
64
65 #define VALIDATE(info) \
66 do {\
67 if (!info || info->magic != SERIAL_PLUG_MAGIC)\
68 return -EINVAL;\
69 } while(0)
70
71
72 typedef struct _serial_info {
73 uint64_t magic;
74 const fence_callbacks_t *cb;
75 void *priv;
76 char *uri;
77 char *path;
78 history_info_t *history;
79 map_object_t *maps;
80 int mode;
81 int wake_fd;
82 } serial_info;
83
84
85 struct serial_hostlist_arg {
86 map_object_t *map;
87 const char *src;
88 int fd;
89 };
90
91
92 /*
93 * See if we fenced this node recently (successfully)
94 * If so, ignore the request for a few seconds.
95 *
96 * We purge our history when the entries time out.
97 */
98 static int
99 check_history(void *a, void *b) {
100 serial_req_t *old = a, *current = b;
101
102 if (old->request == current->request &&
103 old->seqno == current->seqno &&
104 !strcasecmp((const char *)old->domain,
105 (const char *)current->domain)) {
106 return 1;
107 }
108 return 0;
109 }
110
111
112 static int
113 serial_hostlist(const char *vm_name, const char *vm_uuid,
114 int state, void *priv)
115 {
116 struct serial_hostlist_arg *arg = (struct serial_hostlist_arg *)priv;
|
(1) Event var_decl: |
Declaring variable "hinfo" without initializer. |
| Also see events: |
[uninit_use_in_call] |
117 host_state_t hinfo;
118 struct timeval tv;
119 int ret;
120
|
(2) Event cond_false: |
Condition "arg->map->check(arg->map->info, arg->src, vm_uuid, vm_name) == 0", taking false branch. |
121 if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
122 /* if we don't have access to fence this VM,
123 * we should not see it in a hostlist either */
124 return 0;
|
(3) Event if_end: |
End of if statement. |
125 }
126
127 strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
128 strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
129 hinfo.state = state;
130
131 tv.tv_sec = 1;
132 tv.tv_usec = 0;
133
|
(4) Event uninit_use_in_call: |
Using uninitialized value "hinfo". Field "hinfo.pad" is uninitialized when calling "_write_retry". [details] |
| Also see events: |
[var_decl] |
134 ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
135 if (ret == sizeof(hinfo))
136 return 0;
137 return 1;
138 }
139
140
141 static int
142 serial_hostlist_begin(int fd)
143 {
144 struct timeval tv;
145 serial_resp_t resp;
146
147 resp.magic = SERIAL_MAGIC;
148 resp.response = RESP_HOSTLIST;
149
150 tv.tv_sec = 1;
151 tv.tv_usec = 0;
152 return _write_retry(fd, &resp, sizeof(resp), &tv);
153 }
154
155
156 static int
157 serial_hostlist_end(int fd)
158 {
159 host_state_t hinfo;
160 struct timeval tv;
161 int ret;
162
163 //printf("Sending terminator packet\n");
164
165 memset(&hinfo, 0, sizeof(hinfo));
166
167 tv.tv_sec = 1;
168 tv.tv_usec = 0;
169 ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
170 if (ret == sizeof(hinfo))
171 return 0;
172 return 1;
173 }
174
175
176 static int
177 do_fence_request(int fd, const char *src, serial_req_t *req, serial_info *info)
178 {
179 char response = RESP_FAIL;
180 struct serial_hostlist_arg arg;
181 serial_resp_t resp;
182
183 arg.fd = fd;
184
185 switch(req->request) {
186 case FENCE_NULL:
187 response = info->cb->null((char *)req->domain, info->priv);
188 break;
189 case FENCE_ON:
190 if (map_check(info->maps, src,
191 (const char *)req->domain) == 0) {
192 response = RESP_PERM;
193 break;
194 }
195 response = info->cb->on((char *)req->domain, src,
196 req->seqno, info->priv);
197 break;
198 case FENCE_OFF:
199 if (map_check(info->maps, src,
200 (const char *)req->domain) == 0) {
201 response = RESP_PERM;
202 break;
203 }
204 response = info->cb->off((char *)req->domain, src,
205 req->seqno, info->priv);
206 break;
207 case FENCE_REBOOT:
208 if (map_check(info->maps, src,
209 (const char *)req->domain) == 0) {
210 response = RESP_PERM;
211 break;
212 }
213 response = info->cb->reboot((char *)req->domain, src,
214 req->seqno, info->priv);
215 break;
216 case FENCE_STATUS:
217 if (map_check(info->maps, src,
218 (const char *)req->domain) == 0) {
219 response = RESP_PERM;
220 break;
221 }
222 response = info->cb->status((char *)req->domain, info->priv);
223 break;
224 case FENCE_DEVSTATUS:
225 response = info->cb->devstatus(info->priv);
226 break;
227 case FENCE_HOSTLIST:
228 arg.map = info->maps;
229 arg.src = src;
230 arg.fd = fd;
231
232 serial_hostlist_begin(arg.fd);
233 response = info->cb->hostlist(serial_hostlist, &arg,
234 info->priv);
235 serial_hostlist_end(arg.fd);
236 break;
237 }
238
239 resp.magic = SERIAL_MAGIC;
240 resp.response = response;
241 swab_serial_resp_t(&resp);
242
243 dbg_printf(3, "Sending response to caller...\n");
244 if (_write_retry(fd, &resp, sizeof(resp), NULL) < 0)
245 perror("write");
246
247 /* XVM shotguns multicast packets, so we want to avoid
248 * acting on the same request multiple times if the first
249 * attempt was successful.
250 */
251 history_record(info->history, req);
252
253 return 1;
254 }
255
256
257 static int
258 serial_dispatch(listener_context_t c, struct timeval *timeout)
259 {
260 char src_domain[MAX_DOMAINNAME_LENGTH];
261 serial_info *info;
262 serial_req_t data;
263 fd_set rfds;
264 struct timeval tv;
265 int max;
266 int n, x, ret;
267
268 info = (serial_info *)c;
269 VALIDATE(info);
270
271 FD_ZERO(&rfds);
272 domain_sock_fdset(&rfds, &max);
273 FD_SET(info->wake_fd, &rfds);
274 if (info->wake_fd > max)
275 max = info->wake_fd;
276
277 n = select(max+1, &rfds, NULL, NULL, timeout);
278 if (n < 0) {
279 if (errno == EINTR || errno == EAGAIN)
280 n = 0;
281 else
282 dbg_printf(2, "select: %s\n", strerror(errno));
283 return n;
284 }
285
286 /*
287 * See if the goal was just to be woken up in order to refill our
288 * file descriptor set. For example, if multiple domains were
289 * created simultaneously, we would have to refill our fd_set
290 */
291 if (FD_ISSET(info->wake_fd, &rfds)) {
292 tv.tv_sec = 0;
293 tv.tv_usec = 10000;
294 _read_retry(info->wake_fd, &c, 1, &tv);
295 return 0;
296 }
297
298 /*
299 * If no requests, we're done
300 */
301 if (n == 0)
302 return 0;
303
304 /* find & read request */
305 for (x = 0; x <= max; x++) {
306 if (FD_ISSET(x, &rfds)) {
307 tv.tv_sec = 1;
308 tv.tv_usec = 0;
309
310 ret = _read_retry(x, &data, sizeof(data), &tv);
311
312 if (ret != sizeof(data)) {
313 if (--n > 0)
314 continue;
315 else
316 return 0;
317 } else {
318 swab_serial_req_t(&data);
319 break;
320 }
321 }
322 }
323
324 src_domain[0] = 0;
325 domain_sock_name(x, src_domain, sizeof(src_domain));
326
327 dbg_printf(2, "Sock %d Request %d seqno %d src %s target %s\n", x,
328 data.request, data.seqno, src_domain, data.domain);
329
330 if (history_check(info->history, &data) == 1) {
331 dbg_printf(3, "We just did this request; dropping packet\n");
332 return 0;
333 }
334
335 do_fence_request(x, src_domain[0] == 0 ? NULL : src_domain,
336 &data, info);
337
338 return 0;
339 }
340
341
342 static int
343 serial_config(config_object_t *config, serial_info *args)
344 {
345 char value[1024];
346 int errors = 0;
347
348 if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
349 dset(atoi(value));
350
351 if (sc_get(config, "listeners/serial/@uri",
352 value, sizeof(value)-1) == 0) {
353 dbg_printf(1, "Got %s for uri\n", value);
354 args->uri = strdup(value);
355 }
356
357 if (sc_get(config, "listeners/serial/@path",
358 value, sizeof(value)-1) == 0) {
359 dbg_printf(1, "Got %s for path\n", value);
360 args->path = strdup(value);
361 }
362
363 if (sc_get(config, "listeners/serial/@mode",
364 value, sizeof(value)-1) == 0) {
365 if (!strcasecmp(value, "vmchannel")) {
366 args->mode = 1;
367 } else if (!strcasecmp(value, "serial")) {
368 args->mode = 0;
369 } else {
370 args->mode = atoi(value);
371 if (args->mode < 0)
372 args->mode = 0;
373 }
374
375 dbg_printf(1, "Got %s for mode\n",
376 args->mode?"VMChannel":"serial");
377
378 }
379
380 return errors;
381 }
382
383
384 static int
385 serial_init(listener_context_t *c, const fence_callbacks_t *cb,
386 config_object_t *config, map_object_t *map, void *priv)
387 {
388 serial_info *info;
389 int ret;
390
391 info = malloc(sizeof(*info));
392 if (!info)
393 return -1;
394 memset(info, 0, sizeof(*info));
395
396 info->priv = priv;
397 info->cb = cb;
398
399 ret = serial_config(config, info);
400 if (ret < 0) {
401 perror("serial_config");
402 return -1;
403 } else if (ret > 0) {
404 printf("%d errors found during configuration\n",ret);
405 return -1;
406 }
407
408 info->maps = map;
409
410 info->magic = SERIAL_PLUG_MAGIC;
411 info->history = history_init(check_history, 10, sizeof(fence_req_t));
412 *c = (listener_context_t)info;
413 start_event_listener(info->uri, info->path, info->mode, &info->wake_fd);
414 sleep(1);
415
416 return 0;
417 }
418
419
420 static int
421 serial_shutdown(listener_context_t c)
422 {
423 serial_info *info = (serial_info *)c;
424
425 dbg_printf(3, "Shutting down serial\n");
426
427 VALIDATE(info);
428 info->magic = 0;
429 stop_event_listener();
430 domain_sock_cleanup();
431 history_wipe(info->history);
432 free(info->history);
433 free(info->uri);
434 free(info->path);
435 free(info);
436
437 return 0;
438 }
439
440
441 static listener_plugin_t serial_plugin = {
442 .name = NAME,
443 .version = SERIAL_VERSION,
444 .init = serial_init,
445 .dispatch = serial_dispatch,
446 .cleanup = serial_shutdown,
447 };
448
449 double
450 LISTENER_VER_SYM(void)
451 {
452 return PLUGIN_VERSION_LISTENER;
453 }
454
455 const listener_plugin_t *
456 LISTENER_INFO_SYM(void)
457 {
458 return &serial_plugin;
459 }
460