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