Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
3 : : * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
4 : : *
5 : : * This file is part of LVM2.
6 : : *
7 : : * This copyrighted material is made available to anyone wishing to use,
8 : : * modify, copy, or redistribute it subject to the terms and conditions
9 : : * of the GNU Lesser General Public License v.2.1.
10 : : *
11 : : * You should have received a copy of the GNU Lesser General Public License
12 : : * along with this program; if not, write to the Free Software Foundation,
13 : : * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 : : */
15 : :
16 : : /*
17 : : * Locking functions for LVM.
18 : : * The main purpose of this part of the library is to serialise LVM
19 : : * management operations across a cluster.
20 : : */
21 : :
22 : : #include "lib.h"
23 : : #include "clvm.h"
24 : : #include "lvm-string.h"
25 : : #include "locking.h"
26 : : #include "locking_types.h"
27 : : #include "toolcontext.h"
28 : :
29 : : #include <assert.h>
30 : : #include <stddef.h>
31 : : #include <sys/socket.h>
32 : : #include <sys/un.h>
33 : : #include <unistd.h>
34 : :
35 : : #ifndef CLUSTER_LOCKING_INTERNAL
36 : : int lock_resource(struct cmd_context *cmd, const char *resource, uint32_t flags);
37 : : int query_resource(const char *resource, int *mode);
38 : : void locking_end(void);
39 : : int locking_init(int type, struct config_tree *cf, uint32_t *flags);
40 : : #endif
41 : :
42 : : typedef struct lvm_response {
43 : : char node[255];
44 : : char *response;
45 : : int status;
46 : : int len;
47 : : } lvm_response_t;
48 : :
49 : : /*
50 : : * This gets stuck at the start of memory we allocate so we
51 : : * can sanity-check it at deallocation time
52 : : */
53 : : #define LVM_SIGNATURE 0x434C564D
54 : :
55 : : /*
56 : : * NOTE: the LVMD uses the socket FD as the client ID, this means
57 : : * that any client that calls fork() will inherit the context of
58 : : * it's parent.
59 : : */
60 : : static int _clvmd_sock = -1;
61 : :
62 : : /* FIXME Install SIGPIPE handler? */
63 : :
64 : : /* Open connection to the Cluster Manager daemon */
65 : 0 : static int _open_local_sock(void)
66 : : {
67 : : int local_socket;
68 : : struct sockaddr_un sockaddr;
69 : :
70 : : /* Open local socket */
71 [ # # ]: 0 : if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
72 : 0 : log_error("Local socket creation failed: %s", strerror(errno));
73 : 0 : return -1;
74 : : }
75 : :
76 : 0 : memset(&sockaddr, 0, sizeof(sockaddr));
77 : 0 : memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
78 : :
79 : 0 : sockaddr.sun_family = AF_UNIX;
80 : :
81 [ # # ]: 0 : if (connect(local_socket,(struct sockaddr *) &sockaddr,
82 : : sizeof(sockaddr))) {
83 : 0 : int saved_errno = errno;
84 : :
85 : 0 : log_error("connect() failed on local socket: %s",
86 : : strerror(errno));
87 [ # # ]: 0 : if (close(local_socket))
88 : 0 : stack;
89 : :
90 : 0 : errno = saved_errno;
91 : 0 : return -1;
92 : : }
93 : :
94 : 0 : return local_socket;
95 : : }
96 : :
97 : : /* Send a request and return the status */
98 : 0 : static int _send_request(char *inbuf, int inlen, char **retbuf)
99 : : {
100 : : char outbuf[PIPE_BUF] __attribute((aligned(8)));
101 : 0 : struct clvm_header *outheader = (struct clvm_header *) outbuf;
102 : : int len;
103 : : int off;
104 : : int buflen;
105 : : int err;
106 : :
107 : : /* Send it to CLVMD */
108 : : rewrite:
109 [ # # ]: 0 : if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
110 [ # # ][ # # ]: 0 : if (err == -1 && errno == EINTR)
111 : 0 : goto rewrite;
112 : 0 : log_error("Error writing data to clvmd: %s", strerror(errno));
113 : 0 : return 0;
114 : : }
115 : :
116 : : /* Get the response */
117 : : reread:
118 [ # # ]: 0 : if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
119 [ # # ]: 0 : if (errno == EINTR)
120 : 0 : goto reread;
121 : 0 : log_error("Error reading data from clvmd: %s", strerror(errno));
122 : 0 : return 0;
123 : : }
124 : :
125 [ # # ]: 0 : if (len == 0) {
126 : 0 : log_error("EOF reading CLVMD");
127 : 0 : errno = ENOTCONN;
128 : 0 : return 0;
129 : : }
130 : :
131 : : /* Allocate buffer */
132 : 0 : buflen = len + outheader->arglen;
133 : 0 : *retbuf = dm_malloc(buflen);
134 [ # # ]: 0 : if (!*retbuf) {
135 : 0 : errno = ENOMEM;
136 : 0 : return 0;
137 : : }
138 : :
139 : : /* Copy the header */
140 : 0 : memcpy(*retbuf, outbuf, len);
141 : 0 : outheader = (struct clvm_header *) *retbuf;
142 : :
143 : : /* Read the returned values */
144 : 0 : off = 1; /* we've already read the first byte */
145 [ # # ][ # # ]: 0 : while (off <= outheader->arglen && len > 0) {
146 : 0 : len = read(_clvmd_sock, outheader->args + off,
147 : 0 : buflen - off - offsetof(struct clvm_header, args));
148 [ # # ]: 0 : if (len > 0)
149 : 0 : off += len;
150 : : }
151 : :
152 : : /* Was it an error ? */
153 [ # # ]: 0 : if (outheader->status != 0) {
154 : 0 : errno = outheader->status;
155 : :
156 : : /* Only return an error here if there are no node-specific
157 : : errors present in the message that might have more detail */
158 [ # # ]: 0 : if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
159 : 0 : log_error("cluster request failed: %s", strerror(errno));
160 : 0 : return 0;
161 : : }
162 : :
163 : : }
164 : :
165 : 0 : return 1;
166 : : }
167 : :
168 : : /* Build the structure header and parse-out wildcard node names */
169 : : /* FIXME: Cleanup implicit casts of clvmd_cmd (int, char, uint8_t, etc). */
170 : 0 : static void _build_header(struct clvm_header *head, int clvmd_cmd, const char *node,
171 : : int len)
172 : : {
173 : 0 : head->cmd = clvmd_cmd;
174 : 0 : head->status = 0;
175 : 0 : head->flags = 0;
176 : 0 : head->clientid = 0;
177 : 0 : head->arglen = len;
178 : :
179 [ # # ]: 0 : if (node) {
180 : : /*
181 : : * Allow a couple of special node names:
182 : : * "*" for all nodes,
183 : : * "." for the local node only
184 : : */
185 [ # # ]: 0 : if (strcmp(node, "*") == 0) {
186 : 0 : head->node[0] = '\0';
187 [ # # ]: 0 : } else if (strcmp(node, ".") == 0) {
188 : 0 : head->node[0] = '\0';
189 : 0 : head->flags = CLVMD_FLAG_LOCAL;
190 : : } else
191 : 0 : strcpy(head->node, node);
192 : : } else
193 : 0 : head->node[0] = '\0';
194 : 0 : }
195 : :
196 : : /*
197 : : * Send a message to a(or all) node(s) in the cluster and wait for replies
198 : : */
199 : 0 : static int _cluster_request(char clvmd_cmd, const char *node, void *data, int len,
200 : : lvm_response_t ** response, int *num)
201 : : {
202 : 0 : char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1] __attribute((aligned(8)));
203 : : char *inptr;
204 : 0 : char *retbuf = NULL;
205 : : int status;
206 : : int i;
207 : 0 : int num_responses = 0;
208 : 0 : struct clvm_header *head = (struct clvm_header *) outbuf;
209 : : lvm_response_t *rarray;
210 : :
211 : 0 : *num = 0;
212 : :
213 [ # # ]: 0 : if (_clvmd_sock == -1)
214 : 0 : _clvmd_sock = _open_local_sock();
215 : :
216 [ # # ]: 0 : if (_clvmd_sock == -1)
217 : 0 : return 0;
218 : :
219 : 0 : _build_header(head, clvmd_cmd, node, len);
220 : 0 : memcpy(head->node + strlen(head->node) + 1, data, len);
221 : :
222 : 0 : status = _send_request(outbuf, sizeof(struct clvm_header) +
223 : : strlen(head->node) + len, &retbuf);
224 [ # # ]: 0 : if (!status)
225 : 0 : goto out;
226 : :
227 : : /* Count the number of responses we got */
228 : 0 : head = (struct clvm_header *) retbuf;
229 : 0 : inptr = head->args;
230 [ # # ]: 0 : while (inptr[0]) {
231 : 0 : num_responses++;
232 : 0 : inptr += strlen(inptr) + 1;
233 : 0 : inptr += sizeof(int);
234 : 0 : inptr += strlen(inptr) + 1;
235 : : }
236 : :
237 : : /*
238 : : * Allocate response array.
239 : : * With an extra pair of INTs on the front to sanity
240 : : * check the pointer when we are given it back to free
241 : : */
242 : 0 : *response = dm_malloc(sizeof(lvm_response_t) * num_responses);
243 [ # # ]: 0 : if (!*response) {
244 : 0 : errno = ENOMEM;
245 : 0 : status = 0;
246 : 0 : goto out;
247 : : }
248 : :
249 : 0 : rarray = *response;
250 : :
251 : : /* Unpack the response into an lvm_response_t array */
252 : 0 : inptr = head->args;
253 : 0 : i = 0;
254 [ # # ]: 0 : while (inptr[0]) {
255 : 0 : strcpy(rarray[i].node, inptr);
256 : 0 : inptr += strlen(inptr) + 1;
257 : :
258 : 0 : memcpy(&rarray[i].status, inptr, sizeof(int));
259 : 0 : inptr += sizeof(int);
260 : :
261 : 0 : rarray[i].response = dm_malloc(strlen(inptr) + 1);
262 [ # # ]: 0 : if (rarray[i].response == NULL) {
263 : : /* Free up everything else and return error */
264 : : int j;
265 [ # # ]: 0 : for (j = 0; j < i; j++)
266 : 0 : dm_free(rarray[i].response);
267 : 0 : free(*response);
268 : 0 : errno = ENOMEM;
269 : 0 : status = -1;
270 : 0 : goto out;
271 : : }
272 : :
273 : 0 : strcpy(rarray[i].response, inptr);
274 : 0 : rarray[i].len = strlen(inptr);
275 : 0 : inptr += strlen(inptr) + 1;
276 : 0 : i++;
277 : : }
278 : 0 : *num = num_responses;
279 : 0 : *response = rarray;
280 : :
281 : : out:
282 [ # # ]: 0 : if (retbuf)
283 : 0 : dm_free(retbuf);
284 : :
285 : 0 : return status;
286 : : }
287 : :
288 : : /* Free reply array */
289 : 0 : static int _cluster_free_request(lvm_response_t * response, int num)
290 : : {
291 : : int i;
292 : :
293 [ # # ]: 0 : for (i = 0; i < num; i++) {
294 : 0 : dm_free(response[i].response);
295 : : }
296 : :
297 : 0 : dm_free(response);
298 : :
299 : 0 : return 1;
300 : : }
301 : :
302 : 0 : static int _lock_for_cluster(struct cmd_context *cmd, unsigned char clvmd_cmd,
303 : : uint32_t flags, const char *name)
304 : : {
305 : : int status;
306 : : int i;
307 : : char *args;
308 : 0 : const char *node = "";
309 : : int len;
310 : : int dmeventd_mode;
311 : 0 : int saved_errno = errno;
312 : 0 : lvm_response_t *response = NULL;
313 : : int num_responses;
314 : :
315 [ # # ]: 0 : assert(name);
316 : :
317 : 0 : len = strlen(name) + 3;
318 : 0 : args = alloca(len);
319 : 0 : strcpy(args + 2, name);
320 : :
321 : : /* Maskoff lock flags */
322 : 0 : args[0] = flags & (LCK_SCOPE_MASK | LCK_TYPE_MASK | LCK_NONBLOCK | LCK_HOLD);
323 : 0 : args[1] = flags & (LCK_LOCAL | LCK_CLUSTER_VG);
324 : :
325 [ # # ]: 0 : if (mirror_in_sync())
326 : 0 : args[1] |= LCK_MIRROR_NOSYNC_MODE;
327 : :
328 : : /*
329 : : * Must handle tri-state return from dmeventd_monitor_mode.
330 : : * But DMEVENTD_MONITOR_IGNORE is not propagated across the cluster.
331 : : */
332 : 0 : dmeventd_mode = dmeventd_monitor_mode();
333 [ # # # # ]: 0 : if (dmeventd_mode != DMEVENTD_MONITOR_IGNORE && dmeventd_mode)
334 : 0 : args[1] |= LCK_DMEVENTD_MONITOR_MODE;
335 : :
336 [ # # ]: 0 : if (cmd->partial_activation)
337 : 0 : args[1] |= LCK_PARTIAL_MODE;
338 : :
339 : : /*
340 : : * VG locks are just that: locks, and have no side effects
341 : : * so we only need to do them on the local node because all
342 : : * locks are cluster-wide.
343 : : * Also, if the lock is exclusive it makes no sense to try to
344 : : * acquire it on all nodes, so just do that on the local node too.
345 : : * One exception, is that P_ locks /do/ get distributed across
346 : : * the cluster because they might have side-effects.
347 : : */
348 [ # # ][ # # ]: 0 : if (strncmp(name, "P_", 2) &&
[ # # ][ # # ]
[ # # ]
349 : : (clvmd_cmd == CLVMD_CMD_LOCK_VG ||
350 : 0 : (flags & LCK_TYPE_MASK) == LCK_EXCL ||
351 : 0 : (flags & LCK_LOCAL) ||
352 : 0 : !(flags & LCK_CLUSTER_VG)))
353 : 0 : node = ".";
354 : :
355 : 0 : status = _cluster_request(clvmd_cmd, node, args, len,
356 : : &response, &num_responses);
357 : :
358 : : /* If any nodes were down then display them and return an error */
359 [ # # ]: 0 : for (i = 0; i < num_responses; i++) {
360 [ # # ]: 0 : if (response[i].status == EHOSTDOWN) {
361 : 0 : log_error("clvmd not running on node %s",
362 : : response[i].node);
363 : 0 : status = 0;
364 : 0 : errno = response[i].status;
365 [ # # ]: 0 : } else if (response[i].status) {
366 [ # # ]: 0 : log_error("Error locking on node %s: %s",
367 : : response[i].node,
368 : : response[i].response[0] ?
369 : : response[i].response :
370 : : strerror(response[i].status));
371 : 0 : status = 0;
372 : 0 : errno = response[i].status;
373 : : }
374 : : }
375 : :
376 : 0 : saved_errno = errno;
377 : 0 : _cluster_free_request(response, num_responses);
378 : 0 : errno = saved_errno;
379 : :
380 : 0 : return status;
381 : : }
382 : :
383 : : /* API entry point for LVM */
384 : : #ifdef CLUSTER_LOCKING_INTERNAL
385 : 0 : static int _lock_resource(struct cmd_context *cmd, const char *resource,
386 : : uint32_t flags)
387 : : #else
388 : : int lock_resource(struct cmd_context *cmd, const char *resource, uint32_t flags)
389 : : #endif
390 : : {
391 : : char lockname[PATH_MAX];
392 : 0 : int clvmd_cmd = 0;
393 : : const char *lock_scope;
394 : 0 : const char *lock_type = "";
395 : :
396 [ # # ]: 0 : assert(strlen(resource) < sizeof(lockname));
397 [ # # ]: 0 : assert(resource);
398 : :
399 [ # # # ]: 0 : switch (flags & LCK_SCOPE_MASK) {
400 : : case LCK_VG:
401 [ # # ]: 0 : if (flags == LCK_VG_BACKUP) {
402 : 0 : log_very_verbose("Requesting backup of VG metadata for %s",
403 : : resource);
404 : 0 : return _lock_for_cluster(cmd, CLVMD_CMD_VG_BACKUP,
405 : : LCK_CLUSTER_VG, resource);
406 : : }
407 : :
408 : : /* If the VG name is empty then lock the unused PVs */
409 [ # # ][ # # ]: 0 : if (*resource == '#' || (flags & LCK_CACHE))
410 : 0 : dm_snprintf(lockname, sizeof(lockname), "P_%s",
411 : : resource);
412 : : else
413 : 0 : dm_snprintf(lockname, sizeof(lockname), "V_%s",
414 : : resource);
415 : :
416 : 0 : lock_scope = "VG";
417 : 0 : clvmd_cmd = CLVMD_CMD_LOCK_VG;
418 : 0 : break;
419 : :
420 : : case LCK_LV:
421 : 0 : clvmd_cmd = CLVMD_CMD_LOCK_LV;
422 : 0 : strcpy(lockname, resource);
423 : 0 : lock_scope = "LV";
424 : 0 : flags &= ~LCK_HOLD; /* Mask off HOLD flag */
425 : 0 : break;
426 : :
427 : : default:
428 : 0 : log_error("Unrecognised lock scope: %d",
429 : : flags & LCK_SCOPE_MASK);
430 : 0 : return 0;
431 : : }
432 : :
433 [ # # # # # : 0 : switch(flags & LCK_TYPE_MASK) {
# # ]
434 : : case LCK_UNLOCK:
435 : 0 : lock_type = "UN";
436 : 0 : break;
437 : : case LCK_NULL:
438 : 0 : lock_type = "NL";
439 : 0 : break;
440 : : case LCK_READ:
441 : 0 : lock_type = "CR";
442 : 0 : break;
443 : : case LCK_PREAD:
444 : 0 : lock_type = "PR";
445 : 0 : break;
446 : : case LCK_WRITE:
447 : 0 : lock_type = "PW";
448 : 0 : break;
449 : : case LCK_EXCL:
450 : 0 : lock_type = "EX";
451 : 0 : break;
452 : : default:
453 : 0 : log_error("Unrecognised lock type: %u",
454 : : flags & LCK_TYPE_MASK);
455 : 0 : return 0;
456 : : }
457 : :
458 [ # # ][ # # ]: 0 : log_very_verbose("Locking %s %s %s (%s%s%s%s%s%s) (0x%x)", lock_scope, lockname,
[ # # ][ # # ]
[ # # ]
459 : : lock_type, lock_scope,
460 : : flags & LCK_NONBLOCK ? "|NONBLOCK" : "",
461 : : flags & LCK_HOLD ? "|HOLD" : "",
462 : : flags & LCK_LOCAL ? "|LOCAL" : "",
463 : : flags & LCK_CLUSTER_VG ? "|CLUSTER" : "",
464 : : flags & LCK_CACHE ? "|CACHE" : "",
465 : : flags);
466 : :
467 : : /* Send a message to the cluster manager */
468 : 0 : return _lock_for_cluster(cmd, clvmd_cmd, flags, lockname);
469 : : }
470 : :
471 : 0 : static int decode_lock_type(const char *response)
472 : : {
473 [ # # ]: 0 : if (!response)
474 : 0 : return LCK_NULL;
475 [ # # ]: 0 : else if (strcmp(response, "EX"))
476 : 0 : return LCK_EXCL;
477 [ # # ]: 0 : else if (strcmp(response, "CR"))
478 : 0 : return LCK_READ;
479 [ # # ]: 0 : else if (strcmp(response, "PR"))
480 : 0 : return LCK_PREAD;
481 : :
482 : 0 : stack;
483 : 0 : return 0;
484 : : }
485 : :
486 : : #ifdef CLUSTER_LOCKING_INTERNAL
487 : 0 : static int _query_resource(const char *resource, int *mode)
488 : : #else
489 : : int query_resource(const char *resource, int *mode)
490 : : #endif
491 : : {
492 : : int i, status, len, num_responses, saved_errno;
493 : 0 : const char *node = "";
494 : : char *args;
495 : 0 : lvm_response_t *response = NULL;
496 : :
497 : 0 : saved_errno = errno;
498 : 0 : len = strlen(resource) + 3;
499 : 0 : args = alloca(len);
500 : 0 : strcpy(args + 2, resource);
501 : :
502 : 0 : args[0] = 0;
503 : 0 : args[1] = LCK_CLUSTER_VG;
504 : :
505 : 0 : status = _cluster_request(CLVMD_CMD_LOCK_QUERY, node, args, len,
506 : : &response, &num_responses);
507 : 0 : *mode = LCK_NULL;
508 [ # # ]: 0 : for (i = 0; i < num_responses; i++) {
509 [ # # ]: 0 : if (response[i].status == EHOSTDOWN)
510 : 0 : continue;
511 : :
512 [ # # ]: 0 : if (!response[i].response[0])
513 : 0 : continue;
514 : :
515 : : /*
516 : : * All nodes should use CR, or exactly one node
517 : : * should held EX. (PR is obsolete)
518 : : * If two nodes node reports different locks,
519 : : * something is broken - just return more important mode.
520 : : */
521 [ # # ]: 0 : if (decode_lock_type(response[i].response) > *mode)
522 : 0 : *mode = decode_lock_type(response[i].response);
523 : :
524 : 0 : log_debug("Lock held for %s, node %s : %s", resource,
525 : : response[i].node, response[i].response);
526 : : }
527 : :
528 : 0 : _cluster_free_request(response, num_responses);
529 : 0 : errno = saved_errno;
530 : :
531 : 0 : return status;
532 : : }
533 : :
534 : : #ifdef CLUSTER_LOCKING_INTERNAL
535 : 0 : static void _locking_end(void)
536 : : #else
537 : : void locking_end(void)
538 : : #endif
539 : : {
540 [ # # ][ # # ]: 0 : if (_clvmd_sock != -1 && close(_clvmd_sock))
541 : 0 : stack;
542 : :
543 : 0 : _clvmd_sock = -1;
544 : 0 : }
545 : :
546 : : #ifdef CLUSTER_LOCKING_INTERNAL
547 : 0 : static void _reset_locking(void)
548 : : #else
549 : : void reset_locking(void)
550 : : #endif
551 : : {
552 [ # # ]: 0 : if (close(_clvmd_sock))
553 : 0 : stack;
554 : :
555 : 0 : _clvmd_sock = _open_local_sock();
556 [ # # ]: 0 : if (_clvmd_sock == -1)
557 : 0 : stack;
558 : 0 : }
559 : :
560 : : #ifdef CLUSTER_LOCKING_INTERNAL
561 : 0 : int init_cluster_locking(struct locking_type *locking, struct cmd_context *cmd)
562 : : {
563 : 0 : locking->lock_resource = _lock_resource;
564 : 0 : locking->query_resource = _query_resource;
565 : 0 : locking->fin_locking = _locking_end;
566 : 0 : locking->reset_locking = _reset_locking;
567 : 0 : locking->flags = LCK_PRE_MEMLOCK | LCK_CLUSTERED;
568 : :
569 : 0 : _clvmd_sock = _open_local_sock();
570 [ # # ]: 0 : if (_clvmd_sock == -1)
571 : 0 : return 0;
572 : :
573 : 0 : return 1;
574 : : }
575 : : #else
576 : : int locking_init(int type, struct config_tree *cf, uint32_t *flags)
577 : : {
578 : : _clvmd_sock = _open_local_sock();
579 : : if (_clvmd_sock == -1)
580 : : return 0;
581 : :
582 : : /* Ask LVM to lock memory before calling us */
583 : : *flags |= LCK_PRE_MEMLOCK;
584 : : *flags |= LCK_CLUSTERED;
585 : :
586 : : return 1;
587 : : }
588 : : #endif
|