This source file includes following definitions.
- log_ais_message
- ais_string_to_boolean
- pcmk_setscheduler
- spawn_child
- stop_child
- destroy_ais_node
- update_member
- delete_member
- member_uname
- append_member
- swap_sender
- get_ais_data
- send_plugin_msg
- send_client_ipc
- send_client_msg
- ais_concat
- config_find_init
- config_find_next
- config_find_done
- get_config_opt
- ais_get_boolean
- ais_get_int
- pcmk_user_lookup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 #include <crm_internal.h>
20 #include <crm/cluster/internal.h>
21 #include <sys/types.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <signal.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/resource.h>
31
32 #include <pwd.h>
33 #include <glib.h>
34 #include <bzlib.h>
35 #include <grp.h>
36
37 #include "./utils.h"
38 #include "./plugin.h"
39
40 struct pcmk_env_s pcmk_env;
41
42 void
43 log_ais_message(int level, const AIS_Message * msg)
44 {
45 char *data = get_ais_data(msg);
46
47 qb_log_from_external_source(__func__, __FILE__,
48 "Msg[%d] (dest=%s:%s, from=%s:%s.%d, remote=%s, size=%d): %.90s",
49 level, __LINE__, 0,
50 msg->id, ais_dest(&(msg->host)), msg_type2text(msg->host.type),
51 ais_dest(&(msg->sender)), msg_type2text(msg->sender.type),
52 msg->sender.pid,
53 msg->sender.uname == local_uname ? "false" : "true",
54 ais_data_len(msg), data);
55
56
57
58
59
60
61
62 ais_free(data);
63 }
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 static int
79 ais_string_to_boolean(const char *s)
80 {
81 int rc = 0;
82
83 if (s == NULL) {
84 return rc;
85 }
86
87 if (strcasecmp(s, "true") == 0
88 || strcasecmp(s, "on") == 0
89 || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
90 rc = 1;
91 }
92 return rc;
93 }
94
95 static char *opts_default[] = { NULL, NULL };
96 static char *opts_vgrind[] = { NULL, NULL, NULL, NULL, NULL };
97
98 static void
99 pcmk_setscheduler(crm_child_t * child)
100 {
101 #if defined(HAVE_SCHED_SETSCHEDULER)
102 int policy = sched_getscheduler(0);
103
104 if (policy == -1) {
105 ais_perror("Could not get scheduling policy for %s", child->name);
106
107 } else {
108 int priority = -10;
109
110 if (policy != SCHED_OTHER) {
111 struct sched_param sp;
112
113 policy = SCHED_OTHER;
114 # if defined(SCHED_RESET_ON_FORK)
115 policy |= SCHED_RESET_ON_FORK;
116 # endif
117 memset(&sp, 0, sizeof(sp));
118 sp.sched_priority = 0;
119
120 if (sched_setscheduler(0, policy, &sp) == -1) {
121 ais_perror("Could not reset scheduling policy to SCHED_OTHER for %s", child->name);
122 return;
123 }
124 }
125
126 if (setpriority(PRIO_PROCESS, 0, priority) == -1) {
127 ais_perror("Could not reset process priority to %d for %s", priority, child->name);
128 }
129 }
130 #else
131 ais_info("The platform is missing process priority setting features. Leaving at default.");
132 #endif
133 }
134
135 gboolean
136 spawn_child(crm_child_t * child)
137 {
138 int lpc = 0;
139 uid_t uid = 0;
140 gid_t gid = 0;
141 struct rlimit oflimits;
142 gboolean use_valgrind = FALSE;
143 gboolean use_callgrind = FALSE;
144 const char *devnull = "/dev/null";
145 const char *env_valgrind = getenv("PCMK_valgrind_enabled");
146 const char *env_callgrind = getenv("PCMK_callgrind_enabled");
147
148 if (child->command == NULL) {
149 ais_info("Nothing to do for child \"%s\"", child->name);
150 return TRUE;
151 }
152
153 if (ais_string_to_boolean(env_callgrind)) {
154 use_callgrind = TRUE;
155 use_valgrind = TRUE;
156
157 } else if (env_callgrind != NULL && strstr(env_callgrind, child->name)) {
158 use_callgrind = TRUE;
159 use_valgrind = TRUE;
160
161 } else if (ais_string_to_boolean(env_valgrind)) {
162 use_valgrind = TRUE;
163
164 } else if (env_valgrind != NULL && strstr(env_valgrind, child->name)) {
165 use_valgrind = TRUE;
166 }
167
168 if (use_valgrind && strlen(VALGRIND_BIN) == 0) {
169 ais_warn("Cannot enable valgrind for %s:"
170 " The location of the valgrind binary is unknown", child->name);
171 use_valgrind = FALSE;
172 }
173
174 if (child->uid) {
175 if (pcmk_user_lookup(child->uid, &uid, &gid) < 0) {
176 ais_err("Invalid uid (%s) specified for %s", child->uid, child->name);
177 return FALSE;
178 }
179 ais_info("Using uid=%u and group=%u for process %s", uid, gid, child->name);
180 }
181
182 child->pid = fork();
183 AIS_ASSERT(child->pid != -1);
184
185 if (child->pid > 0) {
186
187 ais_info("Forked child %d for process %s%s", child->pid, child->name,
188 use_valgrind ? " (valgrind enabled: " VALGRIND_BIN ")" : "");
189
190 } else {
191 pcmk_setscheduler(child);
192
193
194 opts_vgrind[0] = ais_strdup(VALGRIND_BIN);
195 if (use_callgrind) {
196 opts_vgrind[1] = ais_strdup("--tool=callgrind");
197 opts_vgrind[2] = ais_strdup("--callgrind-out-file=" CRM_STATE_DIR "/callgrind.out.%p");
198 opts_vgrind[3] = ais_strdup(child->command);
199 opts_vgrind[4] = NULL;
200 } else {
201 opts_vgrind[1] = ais_strdup(child->command);
202 opts_vgrind[2] = NULL;
203 opts_vgrind[3] = NULL;
204 opts_vgrind[4] = NULL;
205 }
206 opts_default[0] = ais_strdup(child->command);
207
208 if (uid && initgroups(child->uid, gid) < 0) {
209 ais_perror("Cannot initialize groups for %s", child->uid);
210 }
211
212 if (uid && setuid(uid) < 0) {
213 ais_perror("Could not set user to %d (%s)", uid, child->uid);
214 }
215
216
217 getrlimit(RLIMIT_NOFILE, &oflimits);
218 for (; lpc < oflimits.rlim_cur; lpc++) {
219 close(lpc);
220 }
221
222 (void)open(devnull, O_RDONLY);
223 (void)open(devnull, O_WRONLY);
224 (void)open(devnull, O_WRONLY);
225
226
227 setenv("HA_COMPRESSION", "bz2", 1);
228 setenv("HA_cluster_type", "openais", 1);
229 setenv("HA_debug", pcmk_env.debug, 1);
230 setenv("HA_logfacility", pcmk_env.syslog, 1);
231 setenv("HA_LOGFACILITY", pcmk_env.syslog, 1);
232 setenv("HA_use_logd", pcmk_env.use_logd, 1);
233 setenv("HA_quorum_type", pcmk_env.quorum, 1);
234
235
236 if (pcmk_env.logfile) {
237 setenv("HA_logfile", pcmk_env.logfile, 1);
238 }
239
240 if (use_valgrind) {
241 (void)execvp(VALGRIND_BIN, opts_vgrind);
242 } else {
243 (void)execvp(child->command, opts_default);
244 }
245 ais_perror("FATAL: Cannot exec %s", child->command);
246 exit(100);
247 }
248 return TRUE;
249 }
250
251 gboolean
252 stop_child(crm_child_t * child, int signal)
253 {
254 if (signal == 0) {
255 signal = SIGTERM;
256 }
257
258 if (child->command == NULL) {
259 ais_info("Nothing to do for child \"%s\"", child->name);
260 return TRUE;
261 }
262
263 ais_debug("Stopping CRM child \"%s\"", child->name);
264
265 if (child->pid <= 0) {
266 ais_trace("Client %s not running", child->name);
267 return TRUE;
268 }
269
270 errno = 0;
271 if (kill(child->pid, signal) == 0) {
272 ais_notice("Sent -%d to %s: [%d]", signal, child->name, child->pid);
273
274 } else {
275 ais_perror("Sent -%d to %s: [%d]", signal, child->name, child->pid);
276 }
277
278 return TRUE;
279 }
280
281 void
282 destroy_ais_node(gpointer data)
283 {
284 crm_node_t *node = data;
285
286 ais_info("Destroying entry for node %u", node->id);
287
288 ais_free(node->addr);
289 ais_free(node->uname);
290 ais_free(node->state);
291 ais_free(node);
292 }
293
294 int
295 update_member(unsigned int id, uint64_t born, uint64_t seq, int32_t votes,
296 uint32_t procs, const char *uname, const char *state, const char *version)
297 {
298 int changed = 0;
299 crm_node_t *node = NULL;
300
301 node = g_hash_table_lookup(membership_list, GUINT_TO_POINTER(id));
302
303 if (node == NULL) {
304 ais_malloc0(node, sizeof(crm_node_t));
305 ais_info("Creating entry for node %u born on " U64T "", id, seq);
306 node->id = id;
307 node->addr = NULL;
308 node->state = ais_strdup("unknown");
309
310 g_hash_table_insert(membership_list, GUINT_TO_POINTER(id), node);
311 node = g_hash_table_lookup(membership_list, GUINT_TO_POINTER(id));
312 }
313 AIS_ASSERT(node != NULL);
314
315 if (seq != 0) {
316 node->last_seen = seq;
317 }
318
319 if (born != 0 && node->born != born) {
320 changed = TRUE;
321 node->born = born;
322 ais_info("%p Node %u (%s) born on: " U64T, node, id, uname, born);
323 }
324
325 if (version != NULL) {
326 ais_free(node->version);
327 node->version = ais_strdup(version);
328 }
329
330 if (uname != NULL) {
331 if (node->uname == NULL || ais_str_eq(node->uname, uname) == FALSE) {
332 ais_info("%p Node %u now known as %s (was: %s)", node, id, uname, node->uname);
333 ais_free(node->uname);
334 node->uname = ais_strdup(uname);
335 changed = TRUE;
336 }
337 }
338
339 if (procs != 0 && procs != node->processes) {
340 ais_info("Node %s now has process list: %.32x (%u)", node->uname, procs, procs);
341 node->processes = procs;
342 changed = TRUE;
343 }
344
345 if (votes >= 0 && votes != node->votes) {
346 ais_info("Node %s now has %d quorum votes (was %d)", node->uname, votes, node->votes);
347 node->votes = votes;
348 changed = TRUE;
349 }
350
351 if (state != NULL) {
352 if (node->state == NULL || ais_str_eq(node->state, state) == FALSE) {
353 ais_free(node->state);
354 node->state = ais_strdup(state);
355 ais_info("Node %u/%s is now: %s", id, node->uname ? node->uname : "unknown", state);
356 changed = TRUE;
357 }
358 }
359
360 return changed;
361 }
362
363 void
364 delete_member(uint32_t id, const char *uname)
365 {
366 if (uname == NULL) {
367 g_hash_table_remove(membership_list, GUINT_TO_POINTER(id));
368 return;
369 }
370 ais_err("Deleting by uname is not yet supported");
371 }
372
373 const char *
374 member_uname(uint32_t id)
375 {
376 crm_node_t *node = g_hash_table_lookup(membership_list, GUINT_TO_POINTER(id));
377
378 if (node == NULL) {
379 return ".unknown.";
380 }
381 if (node->uname == NULL) {
382 return ".pending.";
383 }
384 return node->uname;
385 }
386
387 char *
388 append_member(char *data, crm_node_t * node)
389 {
390 int size = 1;
391 int offset = 0;
392 static int fixed_len = 4 + 8 + 7 + 6 + 6 + 7 + 11;
393
394 if (data) {
395 size = strlen(data);
396 }
397 offset = size;
398
399 size += fixed_len;
400 size += 32;
401 size += 100;
402 size += strlen(node->state);
403 if (node->uname) {
404 size += (7 + strlen(node->uname));
405 }
406 if (node->addr) {
407 size += (6 + strlen(node->addr));
408 }
409 if (node->version) {
410 size += (9 + strlen(node->version));
411 }
412 data = realloc_safe(data, size);
413
414 offset += snprintf(data + offset, size - offset, "<node id=\"%u\" ", node->id);
415 if (node->uname) {
416 offset += snprintf(data + offset, size - offset, "uname=\"%s\" ", node->uname);
417 }
418 offset += snprintf(data + offset, size - offset, "state=\"%s\" ", node->state);
419 offset += snprintf(data + offset, size - offset, "born=\"" U64T "\" ", node->born);
420 offset += snprintf(data + offset, size - offset, "seen=\"" U64T "\" ", node->last_seen);
421 offset += snprintf(data + offset, size - offset, "votes=\"%d\" ", node->votes);
422 offset += snprintf(data + offset, size - offset, "processes=\"%u\" ", node->processes);
423 if (node->addr) {
424 offset += snprintf(data + offset, size - offset, "addr=\"%s\" ", node->addr);
425 }
426 if (node->version) {
427 offset += snprintf(data + offset, size - offset, "version=\"%s\" ", node->version);
428 }
429 offset += snprintf(data + offset, size - offset, "/>");
430
431 return data;
432 }
433
434 void
435 swap_sender(AIS_Message * msg)
436 {
437 int tmp = 0;
438 char tmp_s[256];
439
440 tmp = msg->host.type;
441 msg->host.type = msg->sender.type;
442 msg->sender.type = tmp;
443
444 tmp = msg->host.type;
445 msg->host.size = msg->sender.type;
446 msg->sender.type = tmp;
447
448 memcpy(tmp_s, msg->host.uname, 256);
449 memcpy(msg->host.uname, msg->sender.uname, 256);
450 memcpy(msg->sender.uname, tmp_s, 256);
451 }
452
453 char *
454 get_ais_data(const AIS_Message * msg)
455 {
456 int rc = BZ_OK;
457 char *uncompressed = NULL;
458 unsigned int new_size = msg->size + 1;
459
460 if (msg->is_compressed == FALSE) {
461 uncompressed = strdup(msg->data);
462
463 } else {
464 ais_malloc0(uncompressed, new_size);
465
466 rc = BZ2_bzBuffToBuffDecompress(uncompressed, &new_size, (char *)msg->data,
467 msg->compressed_size, 1, 0);
468 if (rc != BZ_OK) {
469 ais_info("rc=%d, new=%u expected=%u", rc, new_size, msg->size);
470 }
471 AIS_ASSERT(rc == BZ_OK);
472 AIS_ASSERT(new_size == msg->size);
473 }
474
475 return uncompressed;
476 }
477
478 int
479 send_plugin_msg(enum crm_ais_msg_types type, const char *host, const char *data)
480 {
481 int rc = 0;
482 int data_len = 0;
483 AIS_Message *ais_msg = NULL;
484 int total_size = sizeof(AIS_Message);
485
486 AIS_ASSERT(local_nodeid != 0);
487
488 if (data != NULL) {
489 data_len = 1 + strlen(data);
490 total_size += data_len;
491 }
492 ais_malloc0(ais_msg, total_size);
493
494 ais_msg->header.size = total_size;
495 ais_msg->header.error = CS_OK;
496 ais_msg->header.id = 0;
497
498 ais_msg->size = data_len;
499 ais_msg->sender.type = crm_msg_ais;
500 if (data != NULL) {
501 memcpy(ais_msg->data, data, data_len);
502 }
503
504 ais_msg->host.type = type;
505 ais_msg->host.id = 0;
506 if (host) {
507 ais_msg->host.size = strlen(host);
508 memset(ais_msg->host.uname, 0, MAX_NAME);
509 memcpy(ais_msg->host.uname, host, ais_msg->host.size);
510
511
512 } else {
513 ais_msg->host.type = type;
514 ais_msg->host.size = 0;
515 memset(ais_msg->host.uname, 0, MAX_NAME);
516 }
517
518 rc = send_plugin_msg_raw(ais_msg);
519 ais_free(ais_msg);
520
521 return rc;
522 }
523
524 extern struct corosync_api_v1 *pcmk_api;
525
526 int
527 send_client_ipc(void *conn, const AIS_Message * ais_msg)
528 {
529 int rc = -1;
530
531 if (conn == NULL) {
532 rc = -2;
533
534 } else if (!libais_connection_active(conn)) {
535 ais_warn("Connection no longer active");
536 rc = -3;
537
538
539
540
541 } else {
542 #if SUPPORT_COROSYNC
543 rc = pcmk_api->ipc_dispatch_send(conn, ais_msg, ais_msg->header.size);
544 #endif
545 }
546 return rc;
547 }
548
549 int
550 send_client_msg(void *conn, enum crm_ais_msg_class class, enum crm_ais_msg_types type,
551 const char *data)
552 {
553 int rc = 0;
554 int data_len = 0;
555 int total_size = sizeof(AIS_Message);
556 AIS_Message *ais_msg = NULL;
557 static int msg_id = 0;
558
559 AIS_ASSERT(local_nodeid != 0);
560
561 msg_id++;
562 AIS_ASSERT(msg_id != 0 );
563
564 if (data != NULL) {
565 data_len = 1 + strlen(data);
566 }
567 total_size += data_len;
568
569 ais_malloc0(ais_msg, total_size);
570
571 ais_msg->id = msg_id;
572 ais_msg->header.id = class;
573 ais_msg->header.size = total_size;
574 ais_msg->header.error = CS_OK;
575
576 ais_msg->size = data_len;
577 if (data != NULL) {
578 memcpy(ais_msg->data, data, data_len);
579 }
580
581 ais_msg->host.size = 0;
582 ais_msg->host.type = type;
583 memset(ais_msg->host.uname, 0, MAX_NAME);
584 ais_msg->host.id = 0;
585
586 ais_msg->sender.type = crm_msg_ais;
587 ais_msg->sender.size = local_uname_len;
588 memset(ais_msg->sender.uname, 0, MAX_NAME);
589 memcpy(ais_msg->sender.uname, local_uname, ais_msg->sender.size);
590 ais_msg->sender.id = local_nodeid;
591
592 rc = send_client_ipc(conn, ais_msg);
593
594 if (rc != 0) {
595 ais_warn("Sending message to %s failed: %d", msg_type2text(type), rc);
596 log_ais_message(LOG_DEBUG, ais_msg);
597 }
598
599 ais_free(ais_msg);
600 return rc;
601 }
602
603 char *
604 ais_concat(const char *prefix, const char *suffix, char join)
605 {
606 int len = 0;
607 char *new_str = NULL;
608
609 AIS_ASSERT(prefix != NULL);
610 AIS_ASSERT(suffix != NULL);
611 len = strlen(prefix) + strlen(suffix) + 2;
612
613 ais_malloc0(new_str, (len));
614 sprintf(new_str, "%s%c%s", prefix, join, suffix);
615 new_str[len - 1] = 0;
616 return new_str;
617 }
618
619 hdb_handle_t
620 config_find_init(struct corosync_api_v1 * config, char *name)
621 {
622 hdb_handle_t local_handle = 0;
623
624 #if SUPPORT_COROSYNC
625 config->object_find_create(OBJECT_PARENT_HANDLE, name, strlen(name), &local_handle);
626 ais_info("Local handle: %lld for %s", (long long)local_handle, name);
627 #endif
628
629 return local_handle;
630 }
631
632 hdb_handle_t
633 config_find_next(struct corosync_api_v1 * config, char *name, hdb_handle_t top_handle)
634 {
635 int rc = 0;
636 hdb_handle_t local_handle = 0;
637
638 #if SUPPORT_COROSYNC
639 rc = config->object_find_next(top_handle, &local_handle);
640 #endif
641
642 if (rc < 0) {
643 ais_info("No additional configuration supplied for: %s", name);
644 local_handle = 0;
645 } else {
646 ais_info("Processing additional %s options...", name);
647 }
648 return local_handle;
649 }
650
651 void
652 config_find_done(struct corosync_api_v1 *config, hdb_handle_t local_handle)
653 {
654 #if SUPPORT_COROSYNC
655 config->object_find_destroy(local_handle);
656 #endif
657 }
658
659 int
660 get_config_opt(struct corosync_api_v1 *config,
661 hdb_handle_t object_service_handle, char *key, char **value, const char *fallback)
662 {
663 char *env_key = NULL;
664
665 *value = NULL;
666
667 if (object_service_handle > 0) {
668 config->object_key_get(object_service_handle, key, strlen(key), (void **)value, NULL);
669 }
670
671 if (*value) {
672 ais_info("Found '%s' for option: %s", *value, key);
673 return 0;
674 }
675
676 env_key = ais_concat("HA", key, '_');
677 *value = getenv(env_key);
678 ais_free(env_key);
679
680 if (*value) {
681 ais_info("Found '%s' in ENV for option: %s", *value, key);
682 return 0;
683 }
684
685 if (fallback) {
686 ais_info("Defaulting to '%s' for option: %s", fallback, key);
687 *value = ais_strdup(fallback);
688
689 } else {
690 ais_info("No default for option: %s", key);
691 }
692
693 return -1;
694 }
695
696 int
697 ais_get_boolean(const char *value)
698 {
699 if (value == NULL) {
700 return 0;
701
702 } else if (strcasecmp(value, "true") == 0
703 || strcasecmp(value, "on") == 0
704 || strcasecmp(value, "yes") == 0
705 || strcasecmp(value, "y") == 0 || strcasecmp(value, "1") == 0) {
706 return 1;
707 }
708 return 0;
709 }
710
711 long long
712 ais_get_int(const char *text, char **end_text)
713 {
714 long long result = -1;
715 char *local_end_text = NULL;
716
717 errno = 0;
718
719 if (text != NULL) {
720 #ifdef ANSI_ONLY
721 if (end_text != NULL) {
722 result = strtol(text, end_text, 10);
723 } else {
724 result = strtol(text, &local_end_text, 10);
725 }
726 #else
727 if (end_text != NULL) {
728 result = strtoll(text, end_text, 10);
729 } else {
730 result = strtoll(text, &local_end_text, 10);
731 }
732 #endif
733
734 if (errno == EINVAL) {
735 ais_err("Conversion of %s failed", text);
736 result = -1;
737
738 } else if (errno == ERANGE) {
739 ais_err("Conversion of %s was clipped: %lld", text, result);
740
741 } else if (errno != 0) {
742 ais_perror("Conversion of %s failed:", text);
743 }
744
745 if (local_end_text != NULL && local_end_text[0] != '\0') {
746 ais_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
747 }
748 }
749 return result;
750 }
751
752 #define PW_BUFFER_LEN 500
753
754 int
755 pcmk_user_lookup(const char *name, uid_t * uid, gid_t * gid)
756 {
757 int rc = -1;
758 char *buffer = NULL;
759 struct passwd pwd;
760 struct passwd *pwentry = NULL;
761
762 ais_malloc0(buffer, PW_BUFFER_LEN);
763 getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
764 if (pwentry) {
765 rc = 0;
766 if (uid) {
767 *uid = pwentry->pw_uid;
768 }
769 if (gid) {
770 *gid = pwentry->pw_gid;
771 }
772 ais_debug("Cluster user %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
773
774 } else {
775 ais_err("Cluster user %s does not exist", name);
776 }
777
778 ais_free(buffer);
779 return rc;
780 }