1 /*	$NetBSD: refresh_clvmd.c,v 1.1.1.2 2009/12/02 00:27:06 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU General Public License v.2.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 /*
19  * Tell all clvmds in a cluster to refresh their toolcontext
20  */
21 
22 #define _GNU_SOURCE
23 #define _FILE_OFFSET_BITS 64
24 
25 #include <configure.h>
26 #include <stddef.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <libdevmapper.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <limits.h>
35 
36 #include "clvm.h"
37 #include "refresh_clvmd.h"
38 
39 typedef struct lvm_response {
40 	char node[255];
41 	char *response;
42 	int status;
43 	int len;
44 } lvm_response_t;
45 
46 /*
47  * This gets stuck at the start of memory we allocate so we
48  * can sanity-check it at deallocation time
49  */
50 #define LVM_SIGNATURE 0x434C564D
51 
52 static int _clvmd_sock = -1;
53 
54 /* Open connection to the clvm daemon */
55 static int _open_local_sock(void)
56 {
57 	int local_socket;
58 	struct sockaddr_un sockaddr;
59 
60 	/* Open local socket */
61 	if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
62 		fprintf(stderr, "Local socket creation failed: %s", strerror(errno));
63 		return -1;
64 	}
65 
66 	memset(&sockaddr, 0, sizeof(sockaddr));
67 	memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
68 
69 	sockaddr.sun_family = AF_UNIX;
70 
71 	if (connect(local_socket,(struct sockaddr *) &sockaddr,
72 		    sizeof(sockaddr))) {
73 		int saved_errno = errno;
74 
75 		fprintf(stderr, "connect() failed on local socket: %s\n",
76 			  strerror(errno));
77 		if (close(local_socket))
78 			return -1;
79 
80 		errno = saved_errno;
81 		return -1;
82 	}
83 
84 	return local_socket;
85 }
86 
87 /* Send a request and return the status */
88 static int _send_request(const char *inbuf, int inlen, char **retbuf)
89 {
90 	char outbuf[PIPE_BUF];
91 	struct clvm_header *outheader = (struct clvm_header *) outbuf;
92 	int len;
93 	int off;
94 	int buflen;
95 	int err;
96 
97 	/* Send it to CLVMD */
98  rewrite:
99 	if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
100 	        if (err == -1 && errno == EINTR)
101 		        goto rewrite;
102 		fprintf(stderr, "Error writing data to clvmd: %s", strerror(errno));
103 		return 0;
104 	}
105 
106 	/* Get the response */
107  reread:
108 	if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
109 	        if (errno == EINTR)
110 		        goto reread;
111 		fprintf(stderr, "Error reading data from clvmd: %s", strerror(errno));
112 		return 0;
113 	}
114 
115 	if (len == 0) {
116 		fprintf(stderr, "EOF reading CLVMD");
117 		errno = ENOTCONN;
118 		return 0;
119 	}
120 
121 	/* Allocate buffer */
122 	buflen = len + outheader->arglen;
123 	*retbuf = dm_malloc(buflen);
124 	if (!*retbuf) {
125 		errno = ENOMEM;
126 		return 0;
127 	}
128 
129 	/* Copy the header */
130 	memcpy(*retbuf, outbuf, len);
131 	outheader = (struct clvm_header *) *retbuf;
132 
133 	/* Read the returned values */
134 	off = 1;		/* we've already read the first byte */
135 	while (off <= outheader->arglen && len > 0) {
136 		len = read(_clvmd_sock, outheader->args + off,
137 			   buflen - off - offsetof(struct clvm_header, args));
138 		if (len > 0)
139 			off += len;
140 	}
141 
142 	/* Was it an error ? */
143 	if (outheader->status != 0) {
144 		errno = outheader->status;
145 
146 		/* Only return an error here if there are no node-specific
147 		   errors present in the message that might have more detail */
148 		if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
149 			fprintf(stderr, "cluster request failed: %s\n", strerror(errno));
150 			return 0;
151 		}
152 
153 	}
154 
155 	return 1;
156 }
157 
158 /* Build the structure header and parse-out wildcard node names */
159 static void _build_header(struct clvm_header *head, int cmd, const char *node,
160 			  int len)
161 {
162 	head->cmd = cmd;
163 	head->status = 0;
164 	head->flags = 0;
165 	head->clientid = 0;
166 	head->arglen = len;
167 
168 	if (node) {
169 		/*
170 		 * Allow a couple of special node names:
171 		 * "*" for all nodes,
172 		 * "." for the local node only
173 		 */
174 		if (strcmp(node, "*") == 0) {
175 			head->node[0] = '\0';
176 		} else if (strcmp(node, ".") == 0) {
177 			head->node[0] = '\0';
178 			head->flags = CLVMD_FLAG_LOCAL;
179 		} else
180 			strcpy(head->node, node);
181 	} else
182 		head->node[0] = '\0';
183 }
184 
185 /*
186  * Send a message to a(or all) node(s) in the cluster and wait for replies
187  */
188 static int _cluster_request(char cmd, const char *node, void *data, int len,
189 			   lvm_response_t ** response, int *num)
190 {
191 	char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
192 	char *inptr;
193 	char *retbuf = NULL;
194 	int status;
195 	int i;
196 	int num_responses = 0;
197 	struct clvm_header *head = (struct clvm_header *) outbuf;
198 	lvm_response_t *rarray;
199 
200 	*num = 0;
201 
202 	if (_clvmd_sock == -1)
203 		_clvmd_sock = _open_local_sock();
204 
205 	if (_clvmd_sock == -1)
206 		return 0;
207 
208 	_build_header(head, cmd, node, len);
209 	memcpy(head->node + strlen(head->node) + 1, data, len);
210 
211 	status = _send_request(outbuf, sizeof(struct clvm_header) +
212 			      strlen(head->node) + len, &retbuf);
213 	if (!status)
214 		goto out;
215 
216 	/* Count the number of responses we got */
217 	head = (struct clvm_header *) retbuf;
218 	inptr = head->args;
219 	while (inptr[0]) {
220 		num_responses++;
221 		inptr += strlen(inptr) + 1;
222 		inptr += sizeof(int);
223 		inptr += strlen(inptr) + 1;
224 	}
225 
226 	/*
227 	 * Allocate response array.
228 	 * With an extra pair of INTs on the front to sanity
229 	 * check the pointer when we are given it back to free
230 	 */
231 	*response = dm_malloc(sizeof(lvm_response_t) * num_responses +
232 			    sizeof(int) * 2);
233 	if (!*response) {
234 		errno = ENOMEM;
235 		status = 0;
236 		goto out;
237 	}
238 
239 	rarray = *response;
240 
241 	/* Unpack the response into an lvm_response_t array */
242 	inptr = head->args;
243 	i = 0;
244 	while (inptr[0]) {
245 		strcpy(rarray[i].node, inptr);
246 		inptr += strlen(inptr) + 1;
247 
248 		memcpy(&rarray[i].status, inptr, sizeof(int));
249 		inptr += sizeof(int);
250 
251 		rarray[i].response = dm_malloc(strlen(inptr) + 1);
252 		if (rarray[i].response == NULL) {
253 			/* Free up everything else and return error */
254 			int j;
255 			for (j = 0; j < i; j++)
256 				dm_free(rarray[j].response);
257 			free(*response);
258 			errno = ENOMEM;
259 			status = -1;
260 			goto out;
261 		}
262 
263 		strcpy(rarray[i].response, inptr);
264 		rarray[i].len = strlen(inptr);
265 		inptr += strlen(inptr) + 1;
266 		i++;
267 	}
268 	*num = num_responses;
269 	*response = rarray;
270 
271       out:
272 	if (retbuf)
273 		dm_free(retbuf);
274 
275 	return status;
276 }
277 
278 /* Free reply array */
279 static int _cluster_free_request(lvm_response_t * response, int num)
280 {
281 	int i;
282 
283 	for (i = 0; i < num; i++) {
284 		dm_free(response[i].response);
285 	}
286 
287 	dm_free(response);
288 
289 	return 1;
290 }
291 
292 int refresh_clvmd()
293 {
294 	int num_responses;
295 	char args[1]; // No args really.
296 	lvm_response_t *response;
297 	int saved_errno;
298 	int status;
299 	int i;
300 
301 	status = _cluster_request(CLVMD_CMD_REFRESH, "*", args, 0, &response, &num_responses);
302 
303 	/* If any nodes were down then display them and return an error */
304 	for (i = 0; i < num_responses; i++) {
305 		if (response[i].status == EHOSTDOWN) {
306 			fprintf(stderr, "clvmd not running on node %s",
307 				  response[i].node);
308 			status = 0;
309 			errno = response[i].status;
310 		} else if (response[i].status) {
311 			fprintf(stderr, "Error resetting node %s: %s",
312 				  response[i].node,
313 				  response[i].response[0] ?
314 				  	response[i].response :
315 				  	strerror(response[i].status));
316 			status = 0;
317 			errno = response[i].status;
318 		}
319 	}
320 
321 	saved_errno = errno;
322 	_cluster_free_request(response, num_responses);
323 	errno = saved_errno;
324 
325 	return status;
326 }
327 
328 int debug_clvmd(int level, int clusterwide)
329 {
330 	int num_responses;
331 	char args[1];
332 	const char *nodes;
333 	lvm_response_t *response;
334 	int saved_errno;
335 	int status;
336 	int i;
337 
338 	args[0] = level;
339 	if (clusterwide)
340 		nodes = "*";
341 	else
342 		nodes = ".";
343 
344 	status = _cluster_request(CLVMD_CMD_SET_DEBUG, nodes, args, 1, &response, &num_responses);
345 
346 	/* If any nodes were down then display them and return an error */
347 	for (i = 0; i < num_responses; i++) {
348 		if (response[i].status == EHOSTDOWN) {
349 			fprintf(stderr, "clvmd not running on node %s",
350 				  response[i].node);
351 			status = 0;
352 			errno = response[i].status;
353 		} else if (response[i].status) {
354 			fprintf(stderr, "Error setting debug on node %s: %s",
355 				  response[i].node,
356 				  response[i].response[0] ?
357 				  	response[i].response :
358 				  	strerror(response[i].status));
359 			status = 0;
360 			errno = response[i].status;
361 		}
362 	}
363 
364 	saved_errno = errno;
365 	_cluster_free_request(response, num_responses);
366 	errno = saved_errno;
367 
368 	return status;
369 }
370