1 /*
2  * Copyright (c) 2011-2012 Red Hat, Inc.
3  *
4  * All rights reserved.
5  *
6  * Author: Angus Salkeld <asalkeld@redhat.com>
7  *
8  * This software licensed under BSD license, the text of which follows:
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * - Redistributions of source code must retain the above copyright notice,
14  *   this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright notice,
16  *   this list of conditions and the following disclaimer in the documentation
17  *   and/or other materials provided with the distribution.
18  * - Neither the name of the MontaVista Software, Inc. nor the names of its
19  *   contributors may be used to endorse or promote products derived from this
20  *   software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <config.h>
36 
37 #include <sys/select.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 #include <sys/types.h>
41 #include <netdb.h>
42 #include <arpa/inet.h>
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <errno.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <poll.h>
51 #include <signal.h>
52 
53 #include <qb/qbdefs.h>
54 #include <qb/qbloop.h>
55 #include <qb/qblog.h>
56 
57 #include <corosync/corotypes.h>
58 #include <corosync/cfg.h>
59 #include <corosync/quorum.h>
60 #include <corosync/cmap.h>
61 
62 /*
63  * generic declarations
64  */
65 enum {
66 	CS_NTF_LOG,
67 	CS_NTF_STDOUT,
68 	CS_NTF_SNMP,
69 	CS_NTF_DBUS,
70 	CS_NTF_FG,
71 	CS_NTF_NODNS,
72 	CS_NTF_MAX,
73 };
74 static int conf[CS_NTF_MAX];
75 
76 static int exit_code = 0;
77 
78 static int32_t _cs_is_quorate = 0;
79 
80 typedef void (*node_membership_fn_t)(char *nodename, uint32_t nodeid, char *state, char* ip);
81 typedef void (*node_quorum_fn_t)(char *nodename, uint32_t nodeid, const char *state);
82 typedef void (*application_connection_fn_t)(char *nodename, uint32_t nodeid, char *app_name, const char *state);
83 typedef void (*rrp_faulty_fn_t)(char *nodename, uint32_t nodeid, uint32_t iface_no, const char *state);
84 
85 struct notify_callbacks {
86 	node_membership_fn_t node_membership_fn;
87 	node_quorum_fn_t node_quorum_fn;
88 	application_connection_fn_t application_connection_fn;
89 	rrp_faulty_fn_t rrp_faulty_fn;
90 };
91 
92 #define MAX_NOTIFIERS 5
93 static int num_notifiers = 0;
94 static struct notify_callbacks notifiers[MAX_NOTIFIERS];
95 /*
96  * Global variable with local nodeid
97  */
98 static uint32_t g_local_nodeid = 0;
99 static char local_nodename[CS_MAX_NAME_LENGTH];
100 static qb_loop_t *main_loop;
101 static quorum_handle_t quorum_handle;
102 
103 static void _cs_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip);
104 static void _cs_node_quorum_event(const char *state);
105 static void _cs_application_connection_event(char *app_name, const char *state);
106 static void _cs_rrp_faulty_event(uint32_t iface_no, const char *state);
107 
108 #ifdef HAVE_DBUS
109 #include <dbus/dbus.h>
110 /*
111  * dbus
112  */
113 #define DBUS_CS_NAME	"org.corosync"
114 #define DBUS_CS_IFACE	"org.corosync"
115 #define DBUS_CS_PATH	"/org/corosync"
116 
117 static DBusConnection *db = NULL;
118 static char _err[512];
119 static int err_set = 0;
120 static void _cs_dbus_init(void);
121 #endif /* HAVE_DBUS */
122 
123 #ifdef ENABLE_SNMP
124 #include <net-snmp/net-snmp-config.h>
125 #include <net-snmp/snmpv3_api.h>
126 #include <net-snmp/agent/agent_trap.h>
127 #include <net-snmp/library/mib.h>
128 #include <net-snmp/library/snmp_api.h>
129 #include <net-snmp/library/snmp_client.h>
130 #include <net-snmp/library/snmp_debug.h>
131 
132 enum snmp_node_status {
133        SNMP_NODE_STATUS_UNKNOWN = 0,
134        SNMP_NODE_STATUS_JOINED = 1,
135        SNMP_NODE_STATUS_LEFT = 2
136 };
137 
138 #define SNMP_OID_COROSYNC "1.3.6.1.4.1.35488"
139 #define SNMP_OID_OBJECT_ROOT		SNMP_OID_COROSYNC ".1"
140 #define SNMP_OID_OBJECT_NODE_NAME	SNMP_OID_OBJECT_ROOT ".1"
141 #define SNMP_OID_OBJECT_NODE_ID		SNMP_OID_OBJECT_ROOT ".2"
142 #define SNMP_OID_OBJECT_NODE_STATUS	SNMP_OID_OBJECT_ROOT ".3"
143 #define SNMP_OID_OBJECT_NODE_ADDR	SNMP_OID_OBJECT_ROOT ".4"
144 
145 #define SNMP_OID_OBJECT_RINGSEQ		SNMP_OID_OBJECT_ROOT ".20"
146 #define SNMP_OID_OBJECT_QUORUM		SNMP_OID_OBJECT_ROOT ".21"
147 
148 #define SNMP_OID_OBJECT_APP_NAME	SNMP_OID_OBJECT_ROOT ".40"
149 #define SNMP_OID_OBJECT_APP_STATUS	SNMP_OID_OBJECT_ROOT ".41"
150 
151 #define SNMP_OID_OBJECT_RRP_IFACE_NO	SNMP_OID_OBJECT_ROOT ".60"
152 #define SNMP_OID_OBJECT_RRP_STATUS	SNMP_OID_OBJECT_ROOT ".61"
153 
154 #define SNMP_OID_TRAPS_ROOT		SNMP_OID_COROSYNC ".0"
155 #define SNMP_OID_TRAPS_NODE		SNMP_OID_TRAPS_ROOT ".1"
156 #define SNMP_OID_TRAPS_QUORUM		SNMP_OID_TRAPS_ROOT ".2"
157 #define SNMP_OID_TRAPS_APP		SNMP_OID_TRAPS_ROOT ".3"
158 #define SNMP_OID_TRAPS_RRP		SNMP_OID_TRAPS_ROOT ".4"
159 
160 #define CS_TIMESTAMP_STR_LEN 20
161 static const char *local_host = "localhost";
162 #endif /* ENABLE_SNMP */
163 static char snmp_manager_buf[CS_MAX_NAME_LENGTH];
164 static char *snmp_manager = NULL;
165 static char snmp_community_buf[CS_MAX_NAME_LENGTH];
166 static char *snmp_community = NULL;
167 
168 #define CMAP_MAX_RETRIES 10
169 
170 /*
171  * cmap
172  */
173 static cmap_handle_t cmap_handle;
174 static cmap_track_handle_t cmap_track_handle_connections_key_changed;
175 static cmap_track_handle_t cmap_track_handle_members_key_changed;
176 static cmap_track_handle_t cmap_track_handle_rrp_faulty_key_changed;
177 
_cs_ip_to_hostname(char * ip,char * name_out)178 static int32_t _cs_ip_to_hostname(char* ip, char* name_out)
179 {
180 	struct sockaddr_in sa;
181 	int rc;
182 
183 	if (strchr(ip, ':') == NULL) {
184 		sa.sin_family = AF_INET;
185 	} else {
186 		sa.sin_family = AF_INET6;
187 	}
188 
189 	rc = inet_pton(sa.sin_family, ip, &sa.sin_addr);
190 	if (rc == 0) {
191 		return -EINVAL;
192 	}
193 
194 	rc = getnameinfo((struct sockaddr*)&sa, sizeof(sa),
195 			name_out, CS_MAX_NAME_LENGTH, NULL, 0, 0);
196 	if (rc != 0) {
197 		qb_log(LOG_ERR, "error looking up %s : %s", ip, gai_strerror(rc));
198 		return -EINVAL;
199 	}
200 	return 0;
201 }
202 
_cs_cmap_members_key_changed(cmap_handle_t cmap_handle_c,cmap_track_handle_t cmap_track_handle,int32_t event,const char * key_name,struct cmap_notify_value new_value,struct cmap_notify_value old_value,void * user_data)203 static void _cs_cmap_members_key_changed (
204 	cmap_handle_t cmap_handle_c,
205 	cmap_track_handle_t cmap_track_handle,
206 	int32_t event,
207 	const char *key_name,
208 	struct cmap_notify_value new_value,
209 	struct cmap_notify_value old_value,
210 	void *user_data)
211 {
212 	char nodename[CS_MAX_NAME_LENGTH];
213 	char* open_bracket = NULL;
214 	char* close_bracket = NULL;
215 	int res;
216 	uint32_t nodeid;
217 	char *ip_str;
218 	char tmp_key[CMAP_KEYNAME_MAXLEN];
219 	cs_error_t err;
220 	int no_retries;
221 
222 	if (event != CMAP_TRACK_ADD && event != CMAP_TRACK_MODIFY) {
223 		return ;
224 	}
225 
226 	if (NULL == key_name) {
227 		qb_log(LOG_ERR, "key_name: nil");
228 		return ;
229 	}
230 
231 	res = sscanf(key_name, "runtime.totem.pg.mrp.srp.members.%u.%s", &nodeid, tmp_key);
232 	if (res != 2)
233 		return ;
234 
235 	if (strcmp(tmp_key, "status") != 0) {
236 		return ;
237 	}
238 
239 	res = snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "runtime.totem.pg.mrp.srp.members.%u.ip", nodeid);
240 	if (res <= 0 || res >= CMAP_KEYNAME_MAXLEN) {
241 		qb_log(LOG_ERR, "temp_key: failed, res: %d, nodeid: %u", res, nodeid);
242 		return ;
243 	}
244 	no_retries = 0;
245 	while ((err = cmap_get_string(cmap_handle, tmp_key, &ip_str)) == CS_ERR_TRY_AGAIN &&
246 			no_retries++ < CMAP_MAX_RETRIES) {
247 		sleep(1);
248 	}
249 
250 	if (err != CS_OK) {
251 		return ;
252 	}
253 	/*
254 	 * We want the ip out of: "r(0) ip(192.168.100.92)"
255 	 */
256 	open_bracket = strrchr(ip_str, '(');
257 	if (NULL == open_bracket) {
258 		qb_log(LOG_ERR, "ip_str: %s", ip_str);
259 		free(ip_str);
260 		return ;
261 	}
262 	open_bracket++;
263 	close_bracket = strchr(open_bracket, ')');
264 	if (NULL == close_bracket) {
265 		qb_log(LOG_ERR, "open_bracket: %s", open_bracket);
266 		free(ip_str);
267 		return ;
268 	}
269 	*close_bracket = '\0';
270 	if(conf[CS_NTF_NODNS]) {
271 		strncpy(nodename, open_bracket, CS_MAX_NAME_LENGTH-1);
272 	} else {
273 		res = _cs_ip_to_hostname(open_bracket, nodename);
274 		if (res) {
275 			strncpy(nodename, open_bracket, CS_MAX_NAME_LENGTH-1);
276 		}
277 	}
278 	_cs_node_membership_event(nodename, nodeid, (char *)new_value.data, open_bracket);
279 	free(ip_str);
280 }
281 
_cs_cmap_connections_key_changed(cmap_handle_t cmap_handle_c,cmap_track_handle_t cmap_track_handle,int32_t event,const char * key_name,struct cmap_notify_value new_value,struct cmap_notify_value old_value,void * user_data)282 static void _cs_cmap_connections_key_changed (
283 	cmap_handle_t cmap_handle_c,
284 	cmap_track_handle_t cmap_track_handle,
285 	int32_t event,
286 	const char *key_name,
287 	struct cmap_notify_value new_value,
288 	struct cmap_notify_value old_value,
289 	void *user_data)
290 {
291 	char obj_name[CS_MAX_NAME_LENGTH];
292 	char conn_str[CMAP_KEYNAME_MAXLEN];
293 	char tmp_key[CMAP_KEYNAME_MAXLEN];
294 	int res;
295 
296 	res = sscanf(key_name, "runtime.connections.%[^.].%s", conn_str, tmp_key);
297 	if (res != 2) {
298 		return ;
299 	}
300 
301 	if (strcmp(tmp_key, "service_id") != 0) {
302 		return ;
303 	}
304 
305 	snprintf(obj_name, CS_MAX_NAME_LENGTH, "%s", conn_str);
306 
307 	if (event == CMAP_TRACK_ADD) {
308 		_cs_application_connection_event(obj_name, "connected");
309 	}
310 
311 	if (event == CMAP_TRACK_DELETE) {
312 		_cs_application_connection_event(obj_name, "disconnected");
313 	}
314 }
315 
_cs_cmap_rrp_faulty_key_changed(cmap_handle_t cmap_handle_c,cmap_track_handle_t cmap_track_handle,int32_t event,const char * key_name,struct cmap_notify_value new_value,struct cmap_notify_value old_value,void * user_data)316 static void _cs_cmap_rrp_faulty_key_changed (
317 	cmap_handle_t cmap_handle_c,
318 	cmap_track_handle_t cmap_track_handle,
319 	int32_t event,
320 	const char *key_name,
321 	struct cmap_notify_value new_value,
322 	struct cmap_notify_value old_value,
323 	void *user_data)
324 {
325 	uint32_t iface_no;
326 	char tmp_key[CMAP_KEYNAME_MAXLEN];
327 	int res;
328 	int no_retries;
329 	uint8_t faulty;
330 	cs_error_t err;
331 
332 	res = sscanf(key_name, "runtime.totem.pg.mrp.rrp.%u.%s", &iface_no, tmp_key);
333 	if (res != 2) {
334 		return ;
335 	}
336 
337 	if (strcmp(tmp_key, "faulty") != 0) {
338 		return ;
339 	}
340 
341 	no_retries = 0;
342 	while ((err = cmap_get_uint8(cmap_handle, key_name, &faulty)) == CS_ERR_TRY_AGAIN &&
343 			no_retries++ < CMAP_MAX_RETRIES) {
344 		sleep(1);
345 	}
346 
347 	if (err != CS_OK) {
348 		return ;
349 	}
350 
351 	if (faulty) {
352 		_cs_rrp_faulty_event(iface_no, "faulty");
353 	} else {
354 		_cs_rrp_faulty_event(iface_no, "operational");
355 	}
356 }
357 
358 static int
_cs_cmap_dispatch(int fd,int revents,void * data)359 _cs_cmap_dispatch(int fd, int revents, void *data)
360 {
361 	cs_error_t err;
362 
363 	err = cmap_dispatch(cmap_handle, CS_DISPATCH_ONE);
364 
365 	if (err != CS_OK && err != CS_ERR_TRY_AGAIN && err != CS_ERR_TIMEOUT &&
366 		err != CS_ERR_QUEUE_FULL) {
367 		qb_log(LOG_ERR, "Could not dispatch cmap events. Error %u", err);
368 		qb_loop_stop(main_loop);
369 
370 		exit_code = 1;
371 
372 		return -1;
373 	}
374 
375 	return 0;
376 }
377 
_cs_quorum_notification(quorum_handle_t handle,uint32_t quorate,uint64_t ring_seq,uint32_t view_list_entries,uint32_t * view_list)378 static void _cs_quorum_notification(quorum_handle_t handle,
379 	uint32_t quorate, uint64_t ring_seq,
380 	uint32_t view_list_entries, uint32_t *view_list)
381 {
382 	if (_cs_is_quorate == quorate) {
383 		return;
384 	}
385 	_cs_is_quorate = quorate;
386 
387 	if (quorate) {
388 		_cs_node_quorum_event("quorate");
389 	} else {
390 		_cs_node_quorum_event("not quorate");
391 	}
392 }
393 
394 static int
_cs_quorum_dispatch(int fd,int revents,void * data)395 _cs_quorum_dispatch(int fd, int revents, void *data)
396 {
397 	cs_error_t err;
398 
399 	err = quorum_dispatch(quorum_handle, CS_DISPATCH_ONE);
400 	if (err != CS_OK && err != CS_ERR_TRY_AGAIN && err != CS_ERR_TIMEOUT &&
401 		err != CS_ERR_QUEUE_FULL) {
402 		qb_log(LOG_ERR, "Could not dispatch quorum events. Error %u", err);
403 		qb_loop_stop(main_loop);
404 
405 		exit_code = 1;
406 
407 		return -1;
408 	}
409 	return 0;
410 }
411 
412 static void
_cs_quorum_init(void)413 _cs_quorum_init(void)
414 {
415 	cs_error_t rc;
416 	uint32_t quorum_type;
417 	int fd;
418 
419 	quorum_callbacks_t quorum_callbacks = {
420 		.quorum_notify_fn = _cs_quorum_notification,
421 	};
422 
423 	rc = quorum_initialize (&quorum_handle, &quorum_callbacks,
424 			        &quorum_type);
425 	if (rc != CS_OK) {
426 		qb_log(LOG_ERR, "Could not connect to corosync(quorum)");
427 		return;
428 	}
429 	quorum_fd_get(quorum_handle, &fd);
430 	qb_loop_poll_add(main_loop, QB_LOOP_MED, fd, POLLIN|POLLNVAL, NULL,
431 		_cs_quorum_dispatch);
432 	rc = quorum_trackstart(quorum_handle, CS_TRACK_CHANGES);
433 	if (rc != CS_OK) {
434 		qb_log(LOG_ERR, "Could not start tracking");
435 		return;
436 	}
437 }
438 
439 static void
_cs_quorum_finalize(void)440 _cs_quorum_finalize(void)
441 {
442 	quorum_finalize (quorum_handle);
443 }
444 
445 
446 #ifdef HAVE_DBUS
447 /*
448  * dbus notifications
449  */
450 static void
_cs_dbus_auto_flush(void)451 _cs_dbus_auto_flush(void)
452 {
453 	dbus_connection_ref(db);
454 	while (dbus_connection_get_dispatch_status(db) == DBUS_DISPATCH_DATA_REMAINS) {
455 		dbus_connection_dispatch(db);
456 	}
457 
458 	while (dbus_connection_has_messages_to_send(db)) {
459 		dbus_connection_flush(db);
460 	}
461 
462 	dbus_connection_unref(db);
463 }
464 
465 static void
_cs_dbus_release(void)466 _cs_dbus_release(void)
467 {
468 	DBusError err;
469 
470 	if (!db)
471 		return;
472 
473 	dbus_error_init(&err);
474 	dbus_bus_release_name(db, DBUS_CS_NAME, &err);
475 	dbus_error_free(&err);
476 	dbus_connection_unref(db);
477 	db = NULL;
478 }
479 
480 static void
_cs_dbus_node_quorum_event(char * nodename,uint32_t nodeid,const char * state)481 _cs_dbus_node_quorum_event(char *nodename, uint32_t nodeid, const char *state)
482 {
483 	DBusMessage *msg = NULL;
484 
485 	if (err_set) {
486 		qb_log(LOG_ERR, "%s", _err);
487 		err_set = 0;
488 	}
489 
490 	if (!db) {
491 		goto out_free;
492 	}
493 
494 	if (dbus_connection_get_is_connected(db) != TRUE) {
495 		err_set = 1;
496 		snprintf(_err, sizeof(_err), "DBus connection lost");
497 		_cs_dbus_release();
498 		goto out_unlock;
499 	}
500 
501 	_cs_dbus_auto_flush();
502 
503 	if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
504 					    DBUS_CS_IFACE,
505 					    "QuorumStateChange"))) {
506 		qb_log(LOG_ERR, "error creating dbus signal");
507 		goto out_unlock;
508 	}
509 
510 	if (!dbus_message_append_args(msg,
511 			DBUS_TYPE_STRING, &nodename,
512 			DBUS_TYPE_UINT32, &nodeid,
513 			DBUS_TYPE_STRING, &state,
514 			DBUS_TYPE_INVALID)) {
515 		qb_log(LOG_ERR, "error adding args to quorum signal");
516 		goto out_unlock;
517 	}
518 
519 	dbus_connection_send(db, msg, NULL);
520 
521 out_unlock:
522 	if (msg) {
523 		dbus_message_unref(msg);
524 	}
525 out_free:
526 	return;
527 }
528 
529 static void
_cs_dbus_node_membership_event(char * nodename,uint32_t nodeid,char * state,char * ip)530 _cs_dbus_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
531 {
532 	DBusMessage *msg = NULL;
533 
534 	if (err_set) {
535 		qb_log(LOG_ERR, "%s", _err);
536 		err_set = 0;
537 	}
538 
539 	if (!db) {
540 		goto out_free;
541 	}
542 
543 	if (dbus_connection_get_is_connected(db) != TRUE) {
544 		err_set = 1;
545 		snprintf(_err, sizeof(_err), "DBus connection lost");
546 		_cs_dbus_release();
547 		goto out_unlock;
548 	}
549 
550 	_cs_dbus_auto_flush();
551 
552 	if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
553 					    DBUS_CS_IFACE,
554 					    "NodeStateChange"))) {
555 		qb_log(LOG_ERR, "error creating NodeStateChange signal");
556 		goto out_unlock;
557 	}
558 
559 	if (!dbus_message_append_args(msg,
560 			DBUS_TYPE_STRING, &nodename,
561 			DBUS_TYPE_UINT32, &nodeid,
562 			DBUS_TYPE_STRING, &ip,
563 			DBUS_TYPE_STRING, &state,
564 			DBUS_TYPE_INVALID)) {
565 		qb_log(LOG_ERR, "error adding args to NodeStateChange signal");
566 		goto out_unlock;
567 	}
568 
569 	dbus_connection_send(db, msg, NULL);
570 
571 out_unlock:
572 	if (msg) {
573 		dbus_message_unref(msg);
574 	}
575 out_free:
576 	return;
577 }
578 
579 static void
_cs_dbus_application_connection_event(char * nodename,uint32_t nodeid,char * app_name,const char * state)580 _cs_dbus_application_connection_event(char *nodename, uint32_t nodeid, char *app_name, const char *state)
581 {
582 	DBusMessage *msg = NULL;
583 
584 	if (err_set) {
585 		qb_log(LOG_ERR, "%s", _err);
586 		err_set = 0;
587 	}
588 
589 	if (!db) {
590 		goto out_free;
591 	}
592 
593 	if (dbus_connection_get_is_connected(db) != TRUE) {
594 		err_set = 1;
595 		snprintf(_err, sizeof(_err), "DBus connection lost");
596 		_cs_dbus_release();
597 		goto out_unlock;
598 	}
599 
600 	_cs_dbus_auto_flush();
601 
602 	if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
603 				DBUS_CS_IFACE,
604 				"ConnectionStateChange"))) {
605 		qb_log(LOG_ERR, "error creating ConnectionStateChange signal");
606 		goto out_unlock;
607 	}
608 
609 	if (!dbus_message_append_args(msg,
610 			DBUS_TYPE_STRING, &nodename,
611 			DBUS_TYPE_UINT32, &nodeid,
612 			DBUS_TYPE_STRING, &app_name,
613 			DBUS_TYPE_STRING, &state,
614 			DBUS_TYPE_INVALID)) {
615 		qb_log(LOG_ERR, "error adding args to ConnectionStateChange signal");
616 		goto out_unlock;
617 	}
618 
619 	dbus_connection_send(db, msg, NULL);
620 
621 out_unlock:
622 	if (msg) {
623 		dbus_message_unref(msg);
624 	}
625 out_free:
626 	return;
627 }
628 
629 static void
_cs_dbus_rrp_faulty_event(char * nodename,uint32_t nodeid,uint32_t iface_no,const char * state)630 _cs_dbus_rrp_faulty_event(char *nodename, uint32_t nodeid, uint32_t iface_no, const char *state)
631 {
632 	DBusMessage *msg = NULL;
633 
634 	if (err_set) {
635 		qb_log(LOG_ERR, "%s", _err);
636 		err_set = 0;
637 	}
638 
639 	if (!db) {
640 		goto out_free;
641 	}
642 
643 	if (dbus_connection_get_is_connected(db) != TRUE) {
644 		err_set = 1;
645 		snprintf(_err, sizeof(_err), "DBus connection lost");
646 		_cs_dbus_release();
647 		goto out_unlock;
648 	}
649 
650 	_cs_dbus_auto_flush();
651 
652 	if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
653 					    DBUS_CS_IFACE,
654 					    "QuorumStateChange"))) {
655 		qb_log(LOG_ERR, "error creating dbus signal");
656 		goto out_unlock;
657 	}
658 
659 	if (!dbus_message_append_args(msg,
660 			DBUS_TYPE_STRING, &nodename,
661 			DBUS_TYPE_UINT32, &nodeid,
662 			DBUS_TYPE_UINT32, &iface_no,
663 			DBUS_TYPE_STRING, &state,
664 			DBUS_TYPE_INVALID)) {
665 		qb_log(LOG_ERR, "error adding args to rrp signal");
666 		goto out_unlock;
667 	}
668 
669 	dbus_connection_send(db, msg, NULL);
670 
671 out_unlock:
672 	if (msg) {
673 		dbus_message_unref(msg);
674 	}
675 out_free:
676 	return;
677 }
678 
679 static void
_cs_dbus_init(void)680 _cs_dbus_init(void)
681 {
682 	DBusConnection *dbc = NULL;
683 	DBusError err;
684 
685 	dbus_error_init(&err);
686 
687 	dbc = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
688 	if (!dbc) {
689 		snprintf(_err, sizeof(_err),
690 			 "dbus_bus_get: %s", err.message);
691 		err_set = 1;
692 		dbus_error_free(&err);
693 		return;
694 	}
695 
696 	dbus_connection_set_exit_on_disconnect(dbc, FALSE);
697 
698 	db = dbc;
699 
700 	notifiers[num_notifiers].node_membership_fn =
701 		_cs_dbus_node_membership_event;
702 	notifiers[num_notifiers].node_quorum_fn =
703 		_cs_dbus_node_quorum_event;
704 	notifiers[num_notifiers].application_connection_fn =
705 		_cs_dbus_application_connection_event;
706 	notifiers[num_notifiers].rrp_faulty_fn =
707 		_cs_dbus_rrp_faulty_event;
708 
709 	num_notifiers++;
710 }
711 
712 #endif /* HAVE_DBUS */
713 
714 #ifdef ENABLE_SNMP
snmp_init(const char * target)715 static netsnmp_session *snmp_init (const char *target)
716 {
717 	static netsnmp_session *session = NULL;
718 #ifndef NETSNMPV54
719 	char default_port[128];
720 	snprintf (default_port, sizeof (default_port), "%s:162", target);
721 #endif
722 	if (session) {
723 		return (session);
724 	}
725 
726 	if (target == NULL) {
727 		return NULL;
728 	}
729 
730 	session = malloc (sizeof (netsnmp_session));
731 	snmp_sess_init (session);
732 	session->version = SNMP_VERSION_2c;
733 	session->callback = NULL;
734 	session->callback_magic = NULL;
735 
736 	if (snmp_community) {
737 		session->community = (u_char *)snmp_community;
738 		session->community_len = strlen(snmp_community_buf);
739 	}
740 
741 	session = snmp_add(session,
742 #ifdef NETSNMPV54
743 		netsnmp_transport_open_client ("snmptrap", target),
744 #else
745 		netsnmp_tdomain_transport (default_port, 0, "udp"),
746 #endif
747 		NULL, NULL);
748 
749 	if (session == NULL) {
750 		qb_log(LOG_ERR, 0, "Could not create snmp transport");
751 	}
752 	return (session);
753 }
754 
add_field(netsnmp_pdu * trap_pdu,u_char asn_type,const char * prefix,void * value,size_t value_size)755 static inline void add_field (
756 	netsnmp_pdu *trap_pdu,
757 	u_char asn_type,
758 	const char *prefix,
759 	void *value,
760 	size_t value_size)
761 {
762 	oid _oid[MAX_OID_LEN];
763 	size_t _oid_len = MAX_OID_LEN;
764 	if (snmp_parse_oid(prefix, _oid, &_oid_len)) {
765 		snmp_pdu_add_variable (trap_pdu, _oid, _oid_len, asn_type, (u_char *) value, value_size);
766 	}
767 }
768 
769 static void
_cs_snmp_node_membership_event(char * nodename,uint32_t nodeid,char * state,char * ip)770 _cs_snmp_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
771 {
772 	int ret;
773 	char csysuptime[CS_TIMESTAMP_STR_LEN];
774 	static oid snmptrap_oid[]  = { 1,3,6,1,6,3,1,1,4,1,0 };
775 	static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 };
776 	time_t now = time (NULL);
777 
778 	netsnmp_pdu *trap_pdu;
779 	netsnmp_session *session = snmp_init (snmp_manager);
780 	if (session == NULL) {
781 		qb_log(LOG_NOTICE, "Failed to init SNMP session.");
782 		return ;
783 	}
784 
785 	trap_pdu = snmp_pdu_create (SNMP_MSG_TRAP2);
786 	if (!trap_pdu) {
787 		qb_log(LOG_NOTICE, "Failed to create SNMP notification.");
788 		return ;
789 	}
790 
791 	/* send uptime */
792 	snprintf (csysuptime, CS_TIMESTAMP_STR_LEN, "%ld", now);
793 	snmp_add_var (trap_pdu, sysuptime_oid, sizeof (sysuptime_oid) / sizeof (oid), 't', csysuptime);
794 	snmp_add_var (trap_pdu, snmptrap_oid, sizeof (snmptrap_oid) / sizeof (oid), 'o', SNMP_OID_TRAPS_NODE);
795 
796 	/* Add extries to the trap */
797 	add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_NAME, (void*)nodename, strlen (nodename));
798 	add_field (trap_pdu, ASN_INTEGER, SNMP_OID_OBJECT_NODE_ID, (void*)&nodeid, sizeof (nodeid));
799 	add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_ADDR, (void*)ip, strlen (ip));
800 	add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_STATUS, (void*)state, strlen (state));
801 
802 	/* Send and cleanup */
803 	ret = snmp_send (session, trap_pdu);
804 	if (ret == 0) {
805 		/* error */
806 		qb_log(LOG_ERR, "Could not send SNMP trap");
807 		snmp_free_pdu (trap_pdu);
808 	}
809 }
810 
811 static void
_cs_snmp_node_quorum_event(char * nodename,uint32_t nodeid,const char * state)812 _cs_snmp_node_quorum_event(char *nodename, uint32_t nodeid,
813 			   const char *state)
814 {
815 	int ret;
816 	char csysuptime[20];
817 	static oid snmptrap_oid[]  = { 1,3,6,1,6,3,1,1,4,1,0 };
818 	static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 };
819 	time_t now = time (NULL);
820 
821 	netsnmp_pdu *trap_pdu;
822 	netsnmp_session *session = snmp_init (snmp_manager);
823 	if (session == NULL) {
824 		qb_log(LOG_NOTICE, "Failed to init SNMP session.");
825 		return ;
826 	}
827 
828 	trap_pdu = snmp_pdu_create (SNMP_MSG_TRAP2);
829 	if (!trap_pdu) {
830 		qb_log(LOG_NOTICE, "Failed to create SNMP notification.");
831 		return ;
832 	}
833 
834 	/* send uptime */
835 	sprintf (csysuptime, "%ld", now);
836 	snmp_add_var (trap_pdu, sysuptime_oid, sizeof (sysuptime_oid) / sizeof (oid), 't', csysuptime);
837 	snmp_add_var (trap_pdu, snmptrap_oid, sizeof (snmptrap_oid) / sizeof (oid), 'o', SNMP_OID_TRAPS_QUORUM);
838 
839 	/* Add extries to the trap */
840 	add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_NAME, (void*)nodename, strlen (nodename));
841 	add_field (trap_pdu, ASN_INTEGER, SNMP_OID_OBJECT_NODE_ID, (void*)&nodeid, sizeof (nodeid));
842 	add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_QUORUM, (void*)state, strlen (state));
843 
844 	/* Send and cleanup */
845 	ret = snmp_send (session, trap_pdu);
846 	if (ret == 0) {
847 		/* error */
848 		qb_log(LOG_ERR, "Could not send SNMP trap");
849 		snmp_free_pdu (trap_pdu);
850 	}
851 }
852 
853 static void
_cs_snmp_rrp_faulty_event(char * nodename,uint32_t nodeid,uint32_t iface_no,const char * state)854 _cs_snmp_rrp_faulty_event(char *nodename, uint32_t nodeid,
855 		uint32_t iface_no, const char *state)
856 {
857 	int ret;
858 	char csysuptime[20];
859 	static oid snmptrap_oid[]  = { 1,3,6,1,6,3,1,1,4,1,0 };
860 	static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 };
861 	time_t now = time (NULL);
862 
863 	netsnmp_pdu *trap_pdu;
864 	netsnmp_session *session = snmp_init (snmp_manager);
865 	if (session == NULL) {
866 		qb_log(LOG_NOTICE, "Failed to init SNMP session.");
867 		return ;
868 	}
869 
870 	trap_pdu = snmp_pdu_create (SNMP_MSG_TRAP2);
871 	if (!trap_pdu) {
872 		qb_log(LOG_NOTICE, "Failed to create SNMP notification.");
873 		return ;
874 	}
875 
876 	/* send uptime */
877 	sprintf (csysuptime, "%ld", now);
878 	snmp_add_var (trap_pdu, sysuptime_oid, sizeof (sysuptime_oid) / sizeof (oid), 't', csysuptime);
879 	snmp_add_var (trap_pdu, snmptrap_oid, sizeof (snmptrap_oid) / sizeof (oid), 'o', SNMP_OID_TRAPS_RRP);
880 
881 	/* Add extries to the trap */
882 	add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_NAME, (void*)nodename, strlen (nodename));
883 	add_field (trap_pdu, ASN_INTEGER, SNMP_OID_OBJECT_NODE_ID, (void*)&nodeid, sizeof (nodeid));
884 	add_field (trap_pdu, ASN_INTEGER, SNMP_OID_OBJECT_RRP_IFACE_NO, (void*)&iface_no, sizeof (iface_no));
885 	add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_RRP_STATUS, (void*)state, strlen (state));
886 
887 	/* Send and cleanup */
888 	ret = snmp_send (session, trap_pdu);
889 	if (ret == 0) {
890 		/* error */
891 		qb_log(LOG_ERR, "Could not send SNMP trap");
892 		snmp_free_pdu (trap_pdu);
893 	}
894 }
895 
896 static void
_cs_snmp_init(void)897 _cs_snmp_init(void)
898 {
899 	if (snmp_manager == NULL) {
900 		snmp_manager = (char*)local_host;
901 	}
902 
903 	notifiers[num_notifiers].node_membership_fn =
904 		_cs_snmp_node_membership_event;
905 	notifiers[num_notifiers].node_quorum_fn =
906 		_cs_snmp_node_quorum_event;
907 	notifiers[num_notifiers].application_connection_fn = NULL;
908 	notifiers[num_notifiers].rrp_faulty_fn =
909 		_cs_snmp_rrp_faulty_event;
910 	num_notifiers++;
911 }
912 
913 #endif /* ENABLE_SNMP */
914 
915 static void
_cs_syslog_node_membership_event(char * nodename,uint32_t nodeid,char * state,char * ip)916 _cs_syslog_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
917 {
918 	qb_log(LOG_NOTICE, "%s[%d] ip:%s %s", nodename, nodeid, ip, state);
919 }
920 
921 static void
_cs_syslog_node_quorum_event(char * nodename,uint32_t nodeid,const char * state)922 _cs_syslog_node_quorum_event(char *nodename, uint32_t nodeid, const char *state)
923 {
924 	if (strcmp(state, "quorate") == 0) {
925 		qb_log(LOG_NOTICE, "%s[%d] is now %s", nodename, nodeid, state);
926 	} else {
927 		qb_log(LOG_NOTICE, "%s[%d] has lost quorum", nodename, nodeid);
928 	}
929 }
930 
931 static void
_cs_syslog_application_connection_event(char * nodename,uint32_t nodeid,char * app_name,const char * state)932 _cs_syslog_application_connection_event(char *nodename, uint32_t nodeid, char* app_name, const char *state)
933 {
934 	if (strcmp(state, "connected") == 0) {
935 		qb_log(LOG_NOTICE, "%s[%d] %s is now %s to corosync", nodename, nodeid, app_name, state);
936 	} else {
937 		qb_log(LOG_NOTICE, "%s[%d] %s is now %s from corosync", nodename, nodeid, app_name, state);
938 	}
939 }
940 
941 static void
_cs_syslog_rrp_faulty_event(char * nodename,uint32_t nodeid,uint32_t iface_no,const char * state)942 _cs_syslog_rrp_faulty_event(char *nodename, uint32_t nodeid, uint32_t iface_no, const char *state)
943 {
944 	qb_log(LOG_NOTICE, "%s[%d] interface %u is now %s", nodename, nodeid, iface_no, state);
945 }
946 
947 static void
_cs_node_membership_event(char * nodename,uint32_t nodeid,char * state,char * ip)948 _cs_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
949 {
950 	int i;
951 
952 	for (i = 0; i < num_notifiers; i++) {
953 		if (notifiers[i].node_membership_fn) {
954 			notifiers[i].node_membership_fn(nodename, nodeid, state, ip);
955 		}
956 	}
957 }
958 
959 static void
_cs_local_node_info_get(char ** nodename,uint32_t * nodeid)960 _cs_local_node_info_get(char **nodename, uint32_t *nodeid)
961 {
962 	cs_error_t rc;
963 	corosync_cfg_handle_t cfg_handle;
964 
965 	if (g_local_nodeid == 0) {
966 		rc = corosync_cfg_initialize(&cfg_handle, NULL);
967 		if (rc != CS_OK) {
968 			syslog (LOG_ERR, "Failed to initialize the cfg API. Error %d\n", rc);
969 			exit (EXIT_FAILURE);
970 		}
971 
972 		rc = corosync_cfg_local_get (cfg_handle, &g_local_nodeid);
973 		corosync_cfg_finalize(cfg_handle);
974 		if (rc != CS_OK) {
975 			g_local_nodeid = 0;
976 			strncpy(local_nodename, "localhost", sizeof (local_nodename));
977 			local_nodename[sizeof (local_nodename) - 1] = '\0';
978 		} else {
979 			gethostname(local_nodename, CS_MAX_NAME_LENGTH);
980 		}
981 	}
982 	*nodeid = g_local_nodeid;
983 	*nodename = local_nodename;
984 }
985 
986 static void
_cs_node_quorum_event(const char * state)987 _cs_node_quorum_event(const char *state)
988 {
989 	int i;
990 	char *nodename;
991 	uint32_t nodeid;
992 
993 	_cs_local_node_info_get(&nodename, &nodeid);
994 
995 	for (i = 0; i < num_notifiers; i++) {
996 		if (notifiers[i].node_quorum_fn) {
997 			notifiers[i].node_quorum_fn(nodename, nodeid, state);
998 		}
999 	}
1000 }
1001 
1002 static void
_cs_application_connection_event(char * app_name,const char * state)1003 _cs_application_connection_event(char *app_name, const char *state)
1004 {
1005 	int i;
1006 	char *nodename;
1007 	uint32_t nodeid;
1008 
1009 	_cs_local_node_info_get(&nodename, &nodeid);
1010 
1011 	for (i = 0; i < num_notifiers; i++) {
1012 		if (notifiers[i].application_connection_fn) {
1013 			notifiers[i].application_connection_fn(nodename, nodeid, app_name, state);
1014 		}
1015 	}
1016 }
1017 
1018 static void
_cs_rrp_faulty_event(uint32_t iface_no,const char * state)1019 _cs_rrp_faulty_event(uint32_t iface_no, const char *state)
1020 {
1021 	int i;
1022 	char *nodename;
1023 	uint32_t nodeid;
1024 
1025 	_cs_local_node_info_get(&nodename, &nodeid);
1026 
1027 	for (i = 0; i < num_notifiers; i++) {
1028 		if (notifiers[i].rrp_faulty_fn) {
1029 			notifiers[i].rrp_faulty_fn(nodename, nodeid, iface_no, state);
1030 		}
1031 	}
1032 }
1033 
1034 static int32_t
sig_exit_handler(int32_t num,void * data)1035 sig_exit_handler(int32_t num, void *data)
1036 {
1037 	qb_loop_stop(main_loop);
1038 	return 0;
1039 }
1040 
1041 static void
_cs_cmap_init(void)1042 _cs_cmap_init(void)
1043 {
1044 	cs_error_t rc;
1045 	int cmap_fd = 0;
1046 
1047 	rc = cmap_initialize (&cmap_handle);
1048 	if (rc != CS_OK) {
1049 		qb_log(LOG_ERR, "Failed to initialize the cmap API. Error %d", rc);
1050 		exit (EXIT_FAILURE);
1051 	}
1052 	cmap_fd_get(cmap_handle, &cmap_fd);
1053 
1054 	qb_loop_poll_add(main_loop, QB_LOOP_MED, cmap_fd, POLLIN|POLLNVAL, NULL,
1055 		_cs_cmap_dispatch);
1056 
1057 	rc = cmap_track_add(cmap_handle, "runtime.connections.",
1058 			CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_PREFIX,
1059 			_cs_cmap_connections_key_changed,
1060 			NULL,
1061 			&cmap_track_handle_connections_key_changed);
1062 	if (rc != CS_OK) {
1063 		qb_log(LOG_ERR,
1064 			"Failed to track the connections key. Error %d", rc);
1065 		exit (EXIT_FAILURE);
1066 	}
1067 
1068 	rc = cmap_track_add(cmap_handle, "runtime.totem.pg.mrp.srp.members.",
1069 			CMAP_TRACK_ADD | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX,
1070 			_cs_cmap_members_key_changed,
1071 			NULL,
1072 			&cmap_track_handle_members_key_changed);
1073 	if (rc != CS_OK) {
1074 		qb_log(LOG_ERR,
1075 			"Failed to track the members key. Error %d", rc);
1076 		exit (EXIT_FAILURE);
1077 	}
1078 	rc = cmap_track_add(cmap_handle, "runtime.totem.pg.mrp.rrp.",
1079 			CMAP_TRACK_ADD | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX,
1080 			_cs_cmap_rrp_faulty_key_changed,
1081 			NULL,
1082 			&cmap_track_handle_rrp_faulty_key_changed);
1083 	if (rc != CS_OK) {
1084 		qb_log(LOG_ERR,
1085 			"Failed to track the rrp key. Error %d", rc);
1086 		exit (EXIT_FAILURE);
1087 	}
1088 }
1089 
1090 static void
_cs_cmap_finalize(void)1091 _cs_cmap_finalize(void)
1092 {
1093 	cmap_track_delete(cmap_handle, cmap_track_handle_connections_key_changed);
1094 	cmap_track_delete(cmap_handle, cmap_track_handle_members_key_changed);
1095 	cmap_track_delete(cmap_handle, cmap_track_handle_rrp_faulty_key_changed);
1096 	cmap_finalize (cmap_handle);
1097 }
1098 
1099 static void
_cs_check_config(void)1100 _cs_check_config(void)
1101 {
1102 	if (conf[CS_NTF_LOG] == QB_FALSE &&
1103 		conf[CS_NTF_STDOUT] == QB_FALSE &&
1104 		conf[CS_NTF_SNMP] == QB_FALSE &&
1105 		conf[CS_NTF_DBUS] == QB_FALSE) {
1106 		qb_log(LOG_ERR, "no event type enabled, see corosync-notifyd -h, exiting.");
1107 		exit(EXIT_FAILURE);
1108 	}
1109 
1110 #ifndef ENABLE_SNMP
1111 	if (conf[CS_NTF_SNMP]) {
1112 		qb_log(LOG_ERR, "Not compiled with SNMP support enabled, exiting.");
1113 		exit(EXIT_FAILURE);
1114 	}
1115 #endif
1116 #ifndef HAVE_DBUS
1117 	if (conf[CS_NTF_DBUS]) {
1118 		qb_log(LOG_ERR, "Not compiled with DBus support enabled, exiting.");
1119 		exit(EXIT_FAILURE);
1120 	}
1121 #endif
1122 
1123 	if (conf[CS_NTF_STDOUT] && !conf[CS_NTF_FG]) {
1124 		qb_log(LOG_ERR, "configured to print to stdout and run in the background, exiting");
1125 		exit(EXIT_FAILURE);
1126 	}
1127 	if (conf[CS_NTF_SNMP] && conf[CS_NTF_DBUS]) {
1128 		qb_log(LOG_ERR, "configured to send snmp traps and dbus signals - are you sure?.");
1129 	}
1130 }
1131 
1132 static void
_cs_usage(void)1133 _cs_usage(void)
1134 {
1135 	fprintf(stderr,	"usage:\n"\
1136 		"        -c     : SNMP Community name.\n"\
1137 		"        -f     : Start application in foreground.\n"\
1138 		"        -l     : Log all events.\n"\
1139 		"        -o     : Print events to stdout (turns on -l).\n"\
1140 		"        -s     : Send SNMP traps on all events.\n"\
1141 		"        -m     : Set the SNMP Manager IP address (defaults to localhost).\n"\
1142 		"        -n     : No reverse DNS lookup on cmap member change events.\n"\
1143 		"        -d     : Send DBUS signals on all events.\n"\
1144 		"        -h     : Print this help.\n\n");
1145 }
1146 
1147 int
main(int argc,char * argv[])1148 main(int argc, char *argv[])
1149 {
1150 	int ch;
1151 
1152 	conf[CS_NTF_FG] = QB_FALSE;
1153 	conf[CS_NTF_LOG] = QB_FALSE;
1154 	conf[CS_NTF_STDOUT] = QB_FALSE;
1155 	conf[CS_NTF_SNMP] = QB_FALSE;
1156 	conf[CS_NTF_DBUS] = QB_FALSE;
1157 
1158 	while ((ch = getopt (argc, argv, "c:floshdnm:")) != EOF) {
1159 		switch (ch) {
1160 			case 'c':
1161 				strncpy(snmp_community_buf, optarg, sizeof (snmp_community_buf));
1162 				snmp_community_buf[sizeof (snmp_community_buf) - 1] = '\0';
1163 				snmp_community = snmp_community_buf;
1164 				break;
1165 			case 'f':
1166 				conf[CS_NTF_FG] = QB_TRUE;
1167 				break;
1168 			case 'l':
1169 				conf[CS_NTF_LOG] = QB_TRUE;
1170 				break;
1171 			case 'm':
1172 				conf[CS_NTF_SNMP] = QB_TRUE;
1173 				strncpy(snmp_manager_buf, optarg, sizeof (snmp_manager_buf));
1174 				snmp_manager_buf[sizeof (snmp_manager_buf) - 1] = '\0';
1175 				snmp_manager = snmp_manager_buf;
1176 				break;
1177 			case 'n':
1178 				conf[CS_NTF_NODNS] = QB_TRUE;
1179 				break;
1180 			case 'o':
1181 				conf[CS_NTF_LOG] = QB_TRUE;
1182 				conf[CS_NTF_STDOUT] = QB_TRUE;
1183 				break;
1184 			case 's':
1185 				conf[CS_NTF_SNMP] = QB_TRUE;
1186 				break;
1187 			case 'd':
1188 				conf[CS_NTF_DBUS] = QB_TRUE;
1189 				break;
1190 			case 'h':
1191 			default:
1192 				_cs_usage();
1193 				return EXIT_FAILURE;
1194 		}
1195 	}
1196 
1197 	qb_log_init("notifyd", LOG_DAEMON, LOG_INFO);
1198 
1199 	if (conf[CS_NTF_STDOUT]) {
1200 		qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
1201 			  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
1202 		qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, conf[CS_NTF_STDOUT]);
1203 	}
1204 	_cs_check_config();
1205 
1206 	if (!conf[CS_NTF_FG]) {
1207 		if (daemon(0, 0) < 0)
1208 		{
1209 			perror("daemon() failed");
1210 			return EXIT_FAILURE;
1211 		}
1212 	}
1213 
1214 	num_notifiers = 0;
1215 	if (conf[CS_NTF_LOG]) {
1216 		notifiers[num_notifiers].node_membership_fn =
1217 			_cs_syslog_node_membership_event;
1218 		notifiers[num_notifiers].node_quorum_fn =
1219 			_cs_syslog_node_quorum_event;
1220 		notifiers[num_notifiers].application_connection_fn =
1221 			_cs_syslog_application_connection_event;
1222 		notifiers[num_notifiers].rrp_faulty_fn =
1223 			_cs_syslog_rrp_faulty_event;
1224 		num_notifiers++;
1225 	}
1226 
1227 	main_loop = qb_loop_create();
1228 
1229 	_cs_cmap_init();
1230 	_cs_quorum_init();
1231 
1232 #ifdef HAVE_DBUS
1233 	if (conf[CS_NTF_DBUS]) {
1234 		_cs_dbus_init();
1235 	}
1236 #endif /* HAVE_DBUS */
1237 
1238 #ifdef ENABLE_SNMP
1239 	if (conf[CS_NTF_SNMP]) {
1240 		_cs_snmp_init();
1241 	}
1242 #endif /* ENABLE_SNMP */
1243 
1244 	qb_loop_signal_add(main_loop,
1245 			   QB_LOOP_HIGH,
1246 			   SIGINT,
1247 			   NULL,
1248 			   sig_exit_handler,
1249 			   NULL);
1250 	qb_loop_signal_add(main_loop,
1251 			   QB_LOOP_HIGH,
1252 			   SIGQUIT,
1253 			   NULL,
1254 			   sig_exit_handler,
1255 			   NULL);
1256 	qb_loop_signal_add(main_loop,
1257 			   QB_LOOP_HIGH,
1258 			   SIGTERM,
1259 			   NULL,
1260 			   sig_exit_handler,
1261 			   NULL);
1262 
1263 	qb_loop_run(main_loop);
1264 
1265 #ifdef HAVE_DBUS
1266 	if (conf[CS_NTF_DBUS]) {
1267 		_cs_dbus_release();
1268 	}
1269 #endif /* HAVE_DBUS */
1270 
1271 	_cs_quorum_finalize();
1272 	_cs_cmap_finalize();
1273 
1274 	return (exit_code);
1275 }
1276 
1277