corosync-notifyd.c 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. /*
  2. * Copyright (c) 2011 Red Hat
  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. #include <config.h>
  35. #include <sys/select.h>
  36. #include <sys/socket.h>
  37. #include <sys/un.h>
  38. #include <sys/types.h>
  39. #include <netdb.h>
  40. #include <arpa/inet.h>
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <errno.h>
  44. #include <unistd.h>
  45. #include <string.h>
  46. #include <ctype.h>
  47. #include <poll.h>
  48. #include <signal.h>
  49. #include <qb/qbdefs.h>
  50. #include <qb/qbloop.h>
  51. #include <qb/qblog.h>
  52. #include <qb/qbdefs.h>
  53. #include <qb/qbloop.h>
  54. #include <corosync/corotypes.h>
  55. #include <corosync/cfg.h>
  56. #include <corosync/quorum.h>
  57. #include <corosync/cmap.h>
  58. /*
  59. * generic declarations
  60. */
  61. enum {
  62. CS_NTF_LOG,
  63. CS_NTF_STDOUT,
  64. CS_NTF_SNMP,
  65. CS_NTF_DBUS,
  66. CS_NTF_FG,
  67. CS_NTF_MAX,
  68. };
  69. static int conf[CS_NTF_MAX];
  70. static int32_t _cs_is_quorate = 0;
  71. typedef void (*node_membership_fn_t)(char *nodename, uint32_t nodeid, char *state, char* ip);
  72. typedef void (*node_quorum_fn_t)(char *nodename, uint32_t nodeid, const char *state);
  73. typedef void (*application_connection_fn_t)(char *nodename, uint32_t nodeid, char *app_name, const char *state);
  74. struct notify_callbacks {
  75. node_membership_fn_t node_membership_fn;
  76. node_quorum_fn_t node_quorum_fn;
  77. application_connection_fn_t application_connection_fn;
  78. };
  79. #define MAX_NOTIFIERS 5
  80. static int num_notifiers = 0;
  81. static struct notify_callbacks notifiers[MAX_NOTIFIERS];
  82. static uint32_t local_nodeid = 0;
  83. static char local_nodename[CS_MAX_NAME_LENGTH];
  84. static qb_loop_t *main_loop;
  85. static quorum_handle_t quorum_handle;
  86. static void _cs_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip);
  87. static void _cs_node_quorum_event(const char *state);
  88. static void _cs_application_connection_event(char *app_name, const char *state);
  89. #ifdef HAVE_DBUS
  90. #include <dbus/dbus.h>
  91. /*
  92. * dbus
  93. */
  94. #define DBUS_CS_NAME "org.corosync"
  95. #define DBUS_CS_IFACE "org.corosync"
  96. #define DBUS_CS_PATH "/org/corosync"
  97. static DBusConnection *db = NULL;
  98. static char _err[512];
  99. static int err_set = 0;
  100. static void _cs_dbus_init(void);
  101. #endif /* HAVE_DBUS */
  102. #ifdef ENABLE_SNMP
  103. #include <net-snmp/net-snmp-config.h>
  104. #include <net-snmp/snmpv3_api.h>
  105. #include <net-snmp/agent/agent_trap.h>
  106. #include <net-snmp/library/mib.h>
  107. #include <net-snmp/library/snmp_api.h>
  108. #include <net-snmp/library/snmp_client.h>
  109. #include <net-snmp/library/snmp_debug.h>
  110. enum snmp_node_status {
  111. SNMP_NODE_STATUS_UNKNOWN = 0,
  112. SNMP_NODE_STATUS_JOINED = 1,
  113. SNMP_NODE_STATUS_LEFT = 2
  114. };
  115. #define SNMP_OID_COROSYNC "1.3.6.1.4.1.35488"
  116. #define SNMP_OID_OBJECT_ROOT SNMP_OID_COROSYNC ".1"
  117. #define SNMP_OID_OBJECT_NODE_NAME SNMP_OID_OBJECT_ROOT ".1"
  118. #define SNMP_OID_OBJECT_NODE_ID SNMP_OID_OBJECT_ROOT ".2"
  119. #define SNMP_OID_OBJECT_NODE_STATUS SNMP_OID_OBJECT_ROOT ".3"
  120. #define SNMP_OID_OBJECT_NODE_ADDR SNMP_OID_OBJECT_ROOT ".4"
  121. #define SNMP_OID_OBJECT_RINGSEQ SNMP_OID_OBJECT_ROOT ".20"
  122. #define SNMP_OID_OBJECT_QUORUM SNMP_OID_OBJECT_ROOT ".21"
  123. #define SNMP_OID_OBJECT_APP_NAME SNMP_OID_OBJECT_ROOT ".40"
  124. #define SNMP_OID_OBJECT_APP_STATUS SNMP_OID_OBJECT_ROOT ".41"
  125. #define SNMP_OID_TRAPS_ROOT SNMP_OID_COROSYNC ".0"
  126. #define SNMP_OID_TRAPS_NODE SNMP_OID_TRAPS_ROOT ".1"
  127. #define SNMP_OID_TRAPS_QUORUM SNMP_OID_TRAPS_ROOT ".2"
  128. #define SNMP_OID_TRAPS_APP SNMP_OID_TRAPS_ROOT ".3"
  129. #define CS_TIMESTAMP_STR_LEN 20
  130. static const char *local_host = "localhost";
  131. #endif /* ENABLE_SNMP */
  132. static char snmp_manager_buf[CS_MAX_NAME_LENGTH];
  133. static char *snmp_manager = NULL;
  134. #define CMAP_MAX_RETRIES 10
  135. /*
  136. * cmap
  137. */
  138. static cmap_handle_t cmap_handle;
  139. static int32_t _cs_ip_to_hostname(char* ip, char* name_out)
  140. {
  141. struct sockaddr_in sa;
  142. int rc;
  143. if (strchr(ip, ':') == NULL) {
  144. sa.sin_family = AF_INET;
  145. } else {
  146. sa.sin_family = AF_INET6;
  147. }
  148. rc = inet_pton(sa.sin_family, ip, &sa.sin_addr);
  149. if (rc == 0) {
  150. return -EINVAL;
  151. }
  152. rc = getnameinfo((struct sockaddr*)&sa, sizeof(sa),
  153. name_out, CS_MAX_NAME_LENGTH, NULL, 0, 0);
  154. if (rc != 0) {
  155. qb_log(LOG_ERR, 0, "error looking up %s : %s", ip, gai_strerror(rc));
  156. return -EINVAL;
  157. }
  158. return 0;
  159. }
  160. static void _cs_cmap_members_key_changed (
  161. cmap_handle_t cmap_handle_c,
  162. cmap_track_handle_t cmap_track_handle,
  163. int32_t event,
  164. const char *key_name,
  165. struct cmap_notify_value new_value,
  166. struct cmap_notify_value old_value,
  167. void *user_data)
  168. {
  169. char nodename[CS_MAX_NAME_LENGTH];
  170. char* open_bracket = NULL;
  171. char* close_bracket = NULL;
  172. int res;
  173. uint32_t nodeid;
  174. char *ip_str;
  175. char tmp_key[CMAP_KEYNAME_MAXLEN];
  176. cs_error_t err;
  177. int no_retries;
  178. if (event != CMAP_TRACK_MODIFY) {
  179. return ;
  180. }
  181. res = sscanf(key_name, "runtime.totem.pg.mrp.srp.members.%u.%s", &nodeid, tmp_key);
  182. if (res != 2)
  183. return ;
  184. if (strcmp(tmp_key, "status") != 0) {
  185. return ;
  186. }
  187. snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "runtime.totem.pg.mrp.srp.members.%u.ip", nodeid);
  188. no_retries = 0;
  189. while ((err = cmap_get_string(cmap_handle, tmp_key, &ip_str)) == CS_ERR_TRY_AGAIN &&
  190. no_retries++ < CMAP_MAX_RETRIES) {
  191. sleep(1);
  192. }
  193. if (err != CS_OK) {
  194. return ;
  195. }
  196. /*
  197. * We want the ip out of: "r(0) ip(192.168.100.92)"
  198. */
  199. open_bracket = strrchr(ip_str, '(');
  200. open_bracket++;
  201. close_bracket = strrchr(open_bracket, ')');
  202. *close_bracket = '\0';
  203. _cs_ip_to_hostname(open_bracket, nodename);
  204. _cs_node_membership_event(nodename, nodeid, (char *)new_value.data, open_bracket);
  205. free(ip_str);
  206. }
  207. static void _cs_cmap_connections_key_changed (
  208. cmap_handle_t cmap_handle_c,
  209. cmap_track_handle_t cmap_track_handle,
  210. int32_t event,
  211. const char *key_name,
  212. struct cmap_notify_value new_value,
  213. struct cmap_notify_value old_value,
  214. void *user_data)
  215. {
  216. char obj_name[CS_MAX_NAME_LENGTH];
  217. char conn_str[CMAP_KEYNAME_MAXLEN];
  218. char tmp_key[CMAP_KEYNAME_MAXLEN];
  219. int res;
  220. res = sscanf(key_name, "runtime.connections.%[^.].%s", conn_str, tmp_key);
  221. if (res != 2) {
  222. return ;
  223. }
  224. if (strcmp(tmp_key, "service_id") != 0) {
  225. return ;
  226. }
  227. snprintf(obj_name, CS_MAX_NAME_LENGTH, "%s", conn_str);
  228. if (event == CMAP_TRACK_ADD) {
  229. _cs_application_connection_event(obj_name, "connected");
  230. }
  231. if (event == CMAP_TRACK_DELETE) {
  232. _cs_application_connection_event(obj_name, "disconnected");
  233. }
  234. }
  235. static int
  236. _cs_cmap_dispatch(int fd, int revents, void *data)
  237. {
  238. cmap_dispatch(cmap_handle, CS_DISPATCH_ONE);
  239. return 0;
  240. }
  241. static void _cs_quorum_notification(quorum_handle_t handle,
  242. uint32_t quorate, uint64_t ring_seq,
  243. uint32_t view_list_entries, uint32_t *view_list)
  244. {
  245. if (_cs_is_quorate == quorate) {
  246. return;
  247. }
  248. _cs_is_quorate = quorate;
  249. if (quorate) {
  250. _cs_node_quorum_event("quorate");
  251. } else {
  252. _cs_node_quorum_event("not quorate");
  253. }
  254. }
  255. static int
  256. _cs_quorum_dispatch(int fd, int revents, void *data)
  257. {
  258. quorum_dispatch(quorum_handle, CS_DISPATCH_ONE);
  259. return 0;
  260. }
  261. static void
  262. _cs_quorum_init(void)
  263. {
  264. cs_error_t rc;
  265. int fd;
  266. quorum_callbacks_t quorum_callbacks = {
  267. .quorum_notify_fn = _cs_quorum_notification,
  268. };
  269. rc = quorum_initialize (&quorum_handle, &quorum_callbacks);
  270. if (rc != CS_OK) {
  271. qb_log(LOG_ERR, "Could not connect to corosync(quorum)");
  272. return;
  273. }
  274. quorum_fd_get(quorum_handle, &fd);
  275. qb_loop_poll_add(main_loop, QB_LOOP_MED, fd, POLLIN|POLLNVAL, NULL,
  276. _cs_quorum_dispatch);
  277. quorum_trackstart(quorum_handle, CS_TRACK_CHANGES);
  278. }
  279. static void
  280. _cs_quorum_finalize(void)
  281. {
  282. quorum_finalize (quorum_handle);
  283. }
  284. #ifdef HAVE_DBUS
  285. /*
  286. * dbus notifications
  287. */
  288. static void
  289. _cs_dbus_auto_flush(void)
  290. {
  291. dbus_connection_ref(db);
  292. dbus_connection_read_write(db, 500);
  293. dbus_connection_unref(db);
  294. }
  295. static void
  296. _cs_dbus_release(void)
  297. {
  298. DBusError err;
  299. if (!db)
  300. return;
  301. dbus_error_init(&err);
  302. dbus_bus_release_name(db, DBUS_CS_NAME, &err);
  303. dbus_error_free(&err);
  304. dbus_connection_unref(db);
  305. db = NULL;
  306. }
  307. static void
  308. _cs_dbus_node_quorum_event(char *nodename, uint32_t nodeid, const char *state)
  309. {
  310. DBusMessage *msg = NULL;
  311. if (err_set) {
  312. qb_log(LOG_ERR, "%s", _err);
  313. err_set = 0;
  314. }
  315. if (!db) {
  316. goto out_free;
  317. }
  318. if (dbus_connection_get_is_connected(db) != TRUE) {
  319. err_set = 1;
  320. snprintf(_err, sizeof(_err), "DBus connection lost");
  321. _cs_dbus_release();
  322. goto out_unlock;
  323. }
  324. _cs_dbus_auto_flush();
  325. if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
  326. DBUS_CS_IFACE,
  327. "QuorumStateChange"))) {
  328. qb_log(LOG_ERR, "error creating dbus signal");
  329. goto out_unlock;
  330. }
  331. if (!dbus_message_append_args(msg,
  332. DBUS_TYPE_STRING, &nodename,
  333. DBUS_TYPE_UINT32, &nodeid,
  334. DBUS_TYPE_STRING, &state,
  335. DBUS_TYPE_INVALID)) {
  336. qb_log(LOG_ERR, "error adding args to quorum signal");
  337. goto out_unlock;
  338. }
  339. dbus_connection_send(db, msg, NULL);
  340. out_unlock:
  341. if (msg) {
  342. dbus_message_unref(msg);
  343. }
  344. out_free:
  345. return;
  346. }
  347. static void
  348. _cs_dbus_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
  349. {
  350. DBusMessage *msg = NULL;
  351. if (err_set) {
  352. qb_log(LOG_ERR, "%s", _err);
  353. err_set = 0;
  354. }
  355. if (!db) {
  356. goto out_free;
  357. }
  358. if (dbus_connection_get_is_connected(db) != TRUE) {
  359. err_set = 1;
  360. snprintf(_err, sizeof(_err), "DBus connection lost");
  361. _cs_dbus_release();
  362. goto out_unlock;
  363. }
  364. _cs_dbus_auto_flush();
  365. if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
  366. DBUS_CS_IFACE,
  367. "NodeStateChange"))) {
  368. qb_log(LOG_ERR, "error creating NodeStateChange signal");
  369. goto out_unlock;
  370. }
  371. if (!dbus_message_append_args(msg,
  372. DBUS_TYPE_STRING, &nodename,
  373. DBUS_TYPE_UINT32, &nodeid,
  374. DBUS_TYPE_STRING, &ip,
  375. DBUS_TYPE_STRING, &state,
  376. DBUS_TYPE_INVALID)) {
  377. qb_log(LOG_ERR, "error adding args to NodeStateChange signal");
  378. goto out_unlock;
  379. }
  380. dbus_connection_send(db, msg, NULL);
  381. out_unlock:
  382. if (msg) {
  383. dbus_message_unref(msg);
  384. }
  385. out_free:
  386. return;
  387. }
  388. static void
  389. _cs_dbus_application_connection_event(char *nodename, uint32_t nodeid, char *app_name, const char *state)
  390. {
  391. DBusMessage *msg = NULL;
  392. if (err_set) {
  393. qb_log(LOG_ERR, "%s", _err);
  394. err_set = 0;
  395. }
  396. if (!db) {
  397. goto out_free;
  398. }
  399. if (dbus_connection_get_is_connected(db) != TRUE) {
  400. err_set = 1;
  401. snprintf(_err, sizeof(_err), "DBus connection lost");
  402. _cs_dbus_release();
  403. goto out_unlock;
  404. }
  405. _cs_dbus_auto_flush();
  406. if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
  407. DBUS_CS_IFACE,
  408. "ConnectionStateChange"))) {
  409. qb_log(LOG_ERR, "error creating ConnectionStateChange signal");
  410. goto out_unlock;
  411. }
  412. if (!dbus_message_append_args(msg,
  413. DBUS_TYPE_STRING, &nodename,
  414. DBUS_TYPE_UINT32, &nodeid,
  415. DBUS_TYPE_STRING, &app_name,
  416. DBUS_TYPE_STRING, &state,
  417. DBUS_TYPE_INVALID)) {
  418. qb_log(LOG_ERR, "error adding args to ConnectionStateChange signal");
  419. goto out_unlock;
  420. }
  421. dbus_connection_send(db, msg, NULL);
  422. out_unlock:
  423. if (msg) {
  424. dbus_message_unref(msg);
  425. }
  426. out_free:
  427. return;
  428. }
  429. static void
  430. _cs_dbus_init(void)
  431. {
  432. DBusConnection *dbc = NULL;
  433. DBusError err;
  434. dbus_error_init(&err);
  435. dbc = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
  436. if (!dbc) {
  437. snprintf(_err, sizeof(_err),
  438. "dbus_bus_get: %s", err.message);
  439. err_set = 1;
  440. dbus_error_free(&err);
  441. return;
  442. }
  443. dbus_connection_set_exit_on_disconnect(dbc, FALSE);
  444. db = dbc;
  445. notifiers[num_notifiers].node_membership_fn =
  446. _cs_dbus_node_membership_event;
  447. notifiers[num_notifiers].node_quorum_fn =
  448. _cs_dbus_node_quorum_event;
  449. notifiers[num_notifiers].application_connection_fn =
  450. _cs_dbus_application_connection_event;
  451. num_notifiers++;
  452. }
  453. #endif /* HAVE_DBUS */
  454. #ifdef ENABLE_SNMP
  455. static netsnmp_session *snmp_init (const char *target)
  456. {
  457. static netsnmp_session *session = NULL;
  458. #ifndef NETSNMPV54
  459. char default_port[128];
  460. snprintf (default_port, sizeof (default_port), "%s:162", target);
  461. #endif
  462. if (session) {
  463. return (session);
  464. }
  465. if (target == NULL) {
  466. return NULL;
  467. }
  468. session = malloc (sizeof (netsnmp_session));
  469. snmp_sess_init (session);
  470. session->version = SNMP_VERSION_2c;
  471. session->callback = NULL;
  472. session->callback_magic = NULL;
  473. session = snmp_add(session,
  474. #ifdef NETSNMPV54
  475. netsnmp_transport_open_client ("snmptrap", target),
  476. #else
  477. netsnmp_tdomain_transport (default_port, 0, "udp"),
  478. #endif
  479. NULL, NULL);
  480. if (session == NULL) {
  481. qb_log(LOG_ERR, 0, "Could not create snmp transport");
  482. }
  483. return (session);
  484. }
  485. static inline void add_field (
  486. netsnmp_pdu *trap_pdu,
  487. u_char asn_type,
  488. const char *prefix,
  489. void *value,
  490. size_t value_size)
  491. {
  492. oid _oid[MAX_OID_LEN];
  493. size_t _oid_len = MAX_OID_LEN;
  494. if (snmp_parse_oid(prefix, _oid, &_oid_len)) {
  495. snmp_pdu_add_variable (trap_pdu, _oid, _oid_len, asn_type, (u_char *) value, value_size);
  496. }
  497. }
  498. static void
  499. _cs_snmp_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
  500. {
  501. int ret;
  502. char csysuptime[CS_TIMESTAMP_STR_LEN];
  503. static oid snmptrap_oid[] = { 1,3,6,1,6,3,1,1,4,1,0 };
  504. static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 };
  505. time_t now = time (NULL);
  506. netsnmp_pdu *trap_pdu;
  507. netsnmp_session *session = snmp_init (snmp_manager);
  508. if (session == NULL) {
  509. qb_log(LOG_NOTICE, "Failed to init SNMP session.");
  510. return ;
  511. }
  512. trap_pdu = snmp_pdu_create (SNMP_MSG_TRAP2);
  513. if (!trap_pdu) {
  514. qb_log(LOG_NOTICE, "Failed to create SNMP notification.");
  515. return ;
  516. }
  517. /* send uptime */
  518. snprintf (csysuptime, CS_TIMESTAMP_STR_LEN, "%ld", now);
  519. snmp_add_var (trap_pdu, sysuptime_oid, sizeof (sysuptime_oid) / sizeof (oid), 't', csysuptime);
  520. snmp_add_var (trap_pdu, snmptrap_oid, sizeof (snmptrap_oid) / sizeof (oid), 'o', SNMP_OID_TRAPS_NODE);
  521. /* Add extries to the trap */
  522. add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_NAME, (void*)nodename, strlen (nodename));
  523. add_field (trap_pdu, ASN_INTEGER, SNMP_OID_OBJECT_NODE_ID, (void*)&nodeid, sizeof (nodeid));
  524. add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_ADDR, (void*)ip, strlen (ip));
  525. add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_STATUS, (void*)state, strlen (state));
  526. /* Send and cleanup */
  527. ret = snmp_send (session, trap_pdu);
  528. if (ret == 0) {
  529. /* error */
  530. qb_log(LOG_ERR, "Could not send SNMP trap");
  531. snmp_free_pdu (trap_pdu);
  532. }
  533. }
  534. static void
  535. _cs_snmp_node_quorum_event(char *nodename, uint32_t nodeid,
  536. const char *state)
  537. {
  538. int ret;
  539. char csysuptime[20];
  540. static oid snmptrap_oid[] = { 1,3,6,1,6,3,1,1,4,1,0 };
  541. static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 };
  542. time_t now = time (NULL);
  543. netsnmp_pdu *trap_pdu;
  544. netsnmp_session *session = snmp_init (snmp_manager);
  545. if (session == NULL) {
  546. qb_log(LOG_NOTICE, "Failed to init SNMP session.");
  547. return ;
  548. }
  549. trap_pdu = snmp_pdu_create (SNMP_MSG_TRAP2);
  550. if (!trap_pdu) {
  551. qb_log(LOG_NOTICE, "Failed to create SNMP notification.");
  552. return ;
  553. }
  554. /* send uptime */
  555. sprintf (csysuptime, "%ld", now);
  556. snmp_add_var (trap_pdu, sysuptime_oid, sizeof (sysuptime_oid) / sizeof (oid), 't', csysuptime);
  557. snmp_add_var (trap_pdu, snmptrap_oid, sizeof (snmptrap_oid) / sizeof (oid), 'o', SNMP_OID_TRAPS_NODE);
  558. /* Add extries to the trap */
  559. add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_NODE_NAME, (void*)nodename, strlen (nodename));
  560. add_field (trap_pdu, ASN_INTEGER, SNMP_OID_OBJECT_NODE_ID, (void*)&nodeid, sizeof (nodeid));
  561. add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_OBJECT_QUORUM, (void*)state, strlen (state));
  562. /* Send and cleanup */
  563. ret = snmp_send (session, trap_pdu);
  564. if (ret == 0) {
  565. /* error */
  566. qb_log(LOG_ERR, "Could not send SNMP trap");
  567. snmp_free_pdu (trap_pdu);
  568. }
  569. }
  570. static void
  571. _cs_snmp_init(void)
  572. {
  573. if (snmp_manager == NULL) {
  574. snmp_manager = (char*)local_host;
  575. }
  576. notifiers[num_notifiers].node_membership_fn =
  577. _cs_snmp_node_membership_event;
  578. notifiers[num_notifiers].node_quorum_fn =
  579. _cs_snmp_node_quorum_event;
  580. notifiers[num_notifiers].application_connection_fn = NULL;
  581. num_notifiers++;
  582. }
  583. #endif /* ENABLE_SNMP */
  584. static void
  585. _cs_syslog_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
  586. {
  587. qb_log(LOG_NOTICE, "%s[%d] ip:%s %s", nodename, nodeid, ip, state);
  588. }
  589. static void
  590. _cs_syslog_node_quorum_event(char *nodename, uint32_t nodeid, const char *state)
  591. {
  592. if (strcmp(state, "quorate") == 0) {
  593. qb_log(LOG_NOTICE, "%s[%d] is now %s", nodename, nodeid, state);
  594. } else {
  595. qb_log(LOG_NOTICE, "%s[%d] has lost quorum", nodename, nodeid);
  596. }
  597. }
  598. static void
  599. _cs_syslog_application_connection_event(char *nodename, uint32_t nodeid, char* app_name, const char *state)
  600. {
  601. if (strcmp(state, "connected") == 0) {
  602. qb_log(LOG_NOTICE, "%s[%d] %s is now %s to corosync", nodename, nodeid, app_name, state);
  603. } else {
  604. qb_log(LOG_NOTICE, "%s[%d] %s is now %s from corosync", nodename, nodeid, app_name, state);
  605. }
  606. }
  607. static void
  608. _cs_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip)
  609. {
  610. int i;
  611. for (i = 0; i < num_notifiers; i++) {
  612. if (notifiers[i].node_membership_fn) {
  613. notifiers[i].node_membership_fn(nodename, nodeid, state, ip);
  614. }
  615. }
  616. }
  617. static void
  618. _cs_local_node_info_get(char **nodename, uint32_t *nodeid)
  619. {
  620. cs_error_t rc;
  621. corosync_cfg_handle_t cfg_handle;
  622. if (local_nodeid == 0) {
  623. rc = corosync_cfg_initialize(&cfg_handle, NULL);
  624. if (rc != CS_OK) {
  625. syslog (LOG_ERR, "Failed to initialize the cfg API. Error %d\n", rc);
  626. exit (EXIT_FAILURE);
  627. }
  628. rc = corosync_cfg_local_get (cfg_handle, &local_nodeid);
  629. corosync_cfg_finalize(cfg_handle);
  630. if (rc != CS_OK) {
  631. local_nodeid = 0;
  632. strncpy(local_nodename, "localhost", sizeof (local_nodename));
  633. local_nodename[sizeof (local_nodename) - 1] = '\0';
  634. } else {
  635. gethostname(local_nodename, CS_MAX_NAME_LENGTH);
  636. }
  637. }
  638. *nodeid = local_nodeid;
  639. *nodename = local_nodename;
  640. }
  641. static void
  642. _cs_node_quorum_event(const char *state)
  643. {
  644. int i;
  645. char *nodename;
  646. uint32_t nodeid;
  647. _cs_local_node_info_get(&nodename, &nodeid);
  648. for (i = 0; i < num_notifiers; i++) {
  649. if (notifiers[i].node_quorum_fn) {
  650. notifiers[i].node_quorum_fn(nodename, nodeid, state);
  651. }
  652. }
  653. }
  654. static void
  655. _cs_application_connection_event(char *app_name, const char *state)
  656. {
  657. int i;
  658. char *nodename;
  659. uint32_t nodeid;
  660. _cs_local_node_info_get(&nodename, &nodeid);
  661. for (i = 0; i < num_notifiers; i++) {
  662. if (notifiers[i].application_connection_fn) {
  663. notifiers[i].application_connection_fn(nodename, nodeid, app_name, state);
  664. }
  665. }
  666. }
  667. static int32_t
  668. sig_exit_handler(int32_t num, void *data)
  669. {
  670. qb_loop_stop(main_loop);
  671. return 0;
  672. }
  673. static void
  674. _cs_cmap_init(void)
  675. {
  676. cs_error_t rc;
  677. int cmap_fd = 0;
  678. cmap_track_handle_t track_handle;
  679. rc = cmap_initialize (&cmap_handle);
  680. if (rc != CS_OK) {
  681. qb_log(LOG_ERR, "Failed to initialize the cmap API. Error %d", rc);
  682. exit (EXIT_FAILURE);
  683. }
  684. cmap_fd_get(cmap_handle, &cmap_fd);
  685. qb_loop_poll_add(main_loop, QB_LOOP_MED, cmap_fd, POLLIN|POLLNVAL, NULL,
  686. _cs_cmap_dispatch);
  687. rc = cmap_track_add(cmap_handle, "runtime.connections.",
  688. CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_PREFIX,
  689. _cs_cmap_connections_key_changed,
  690. NULL,
  691. &track_handle);
  692. if (rc != CS_OK) {
  693. qb_log(LOG_ERR,
  694. "Failed to track the connections key. Error %d", rc);
  695. exit (EXIT_FAILURE);
  696. }
  697. rc = cmap_track_add(cmap_handle, "runtime.totem.pg.mrp.srp.members.",
  698. CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX,
  699. _cs_cmap_members_key_changed,
  700. NULL,
  701. &track_handle);
  702. if (rc != CS_OK) {
  703. qb_log(LOG_ERR,
  704. "Failed to track the members key. Error %d", rc);
  705. exit (EXIT_FAILURE);
  706. }
  707. }
  708. static void
  709. _cs_cmap_finalize(void)
  710. {
  711. cmap_finalize (cmap_handle);
  712. }
  713. static void
  714. _cs_check_config(void)
  715. {
  716. if (conf[CS_NTF_LOG] == QB_FALSE &&
  717. conf[CS_NTF_STDOUT] == QB_FALSE &&
  718. conf[CS_NTF_SNMP] == QB_FALSE &&
  719. conf[CS_NTF_DBUS] == QB_FALSE) {
  720. qb_log(LOG_ERR, "no event type enabled, see corosync-notifyd -h, exiting.");
  721. exit(EXIT_FAILURE);
  722. }
  723. #ifndef ENABLE_SNMP
  724. if (conf[CS_NTF_SNMP]) {
  725. qb_log(LOG_ERR, "Not compiled with SNMP support enabled, exiting.");
  726. exit(EXIT_FAILURE);
  727. }
  728. #endif
  729. #ifndef HAVE_DBUS
  730. if (conf[CS_NTF_DBUS]) {
  731. qb_log(LOG_ERR, "Not compiled with DBus support enabled, exiting.");
  732. exit(EXIT_FAILURE);
  733. }
  734. #endif
  735. if (conf[CS_NTF_STDOUT] && !conf[CS_NTF_FG]) {
  736. qb_log(LOG_ERR, "configured to print to stdout and run in the background, exiting");
  737. exit(EXIT_FAILURE);
  738. }
  739. if (conf[CS_NTF_SNMP] && conf[CS_NTF_DBUS]) {
  740. qb_log(LOG_ERR, "configured to send snmp traps and dbus signals - are you sure?.");
  741. }
  742. }
  743. static void
  744. _cs_usage(void)
  745. {
  746. fprintf(stderr, "usage:\n"\
  747. " -f : Start application in foreground.\n"\
  748. " -l : Log all events.\n"\
  749. " -o : Print events to stdout (turns on -l).\n"\
  750. " -s : Send SNMP traps on all events.\n"\
  751. " -m : SNMP Manager IP address (defaults to localhost).\n"\
  752. " -d : Send DBUS signals on all events.\n"\
  753. " -h : Print this help\n\n");
  754. }
  755. int
  756. main(int argc, char *argv[])
  757. {
  758. int ch;
  759. conf[CS_NTF_FG] = QB_FALSE;
  760. conf[CS_NTF_LOG] = QB_FALSE;
  761. conf[CS_NTF_STDOUT] = QB_FALSE;
  762. conf[CS_NTF_SNMP] = QB_FALSE;
  763. conf[CS_NTF_DBUS] = QB_FALSE;
  764. while ((ch = getopt (argc, argv, "floshdm:")) != EOF) {
  765. switch (ch) {
  766. case 'f':
  767. conf[CS_NTF_FG] = QB_TRUE;
  768. break;
  769. case 'l':
  770. conf[CS_NTF_LOG] = QB_TRUE;
  771. break;
  772. case 'm':
  773. conf[CS_NTF_SNMP] = QB_TRUE;
  774. strncpy(snmp_manager_buf, optarg, sizeof (snmp_manager_buf));
  775. snmp_manager_buf[sizeof (snmp_manager_buf) - 1] = '\0';
  776. snmp_manager = snmp_manager_buf;
  777. break;
  778. case 'o':
  779. conf[CS_NTF_LOG] = QB_TRUE;
  780. conf[CS_NTF_STDOUT] = QB_TRUE;
  781. break;
  782. case 's':
  783. conf[CS_NTF_SNMP] = QB_TRUE;
  784. break;
  785. case 'd':
  786. conf[CS_NTF_DBUS] = QB_TRUE;
  787. break;
  788. case 'h':
  789. default:
  790. _cs_usage();
  791. return EXIT_FAILURE;
  792. }
  793. }
  794. qb_log_init("notifyd", LOG_DAEMON, LOG_INFO);
  795. if (conf[CS_NTF_STDOUT]) {
  796. qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
  797. QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
  798. qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, conf[CS_NTF_STDOUT]);
  799. }
  800. _cs_check_config();
  801. if (!conf[CS_NTF_FG]) {
  802. if (daemon(0, 0) < 0)
  803. {
  804. perror("daemon() failed");
  805. return EXIT_FAILURE;
  806. }
  807. }
  808. num_notifiers = 0;
  809. if (conf[CS_NTF_LOG]) {
  810. notifiers[num_notifiers].node_membership_fn =
  811. _cs_syslog_node_membership_event;
  812. notifiers[num_notifiers].node_quorum_fn =
  813. _cs_syslog_node_quorum_event;
  814. notifiers[num_notifiers].application_connection_fn =
  815. _cs_syslog_application_connection_event;
  816. num_notifiers++;
  817. }
  818. main_loop = qb_loop_create();
  819. _cs_cmap_init();
  820. _cs_quorum_init();
  821. #ifdef HAVE_DBUS
  822. if (conf[CS_NTF_DBUS]) {
  823. _cs_dbus_init();
  824. }
  825. #endif /* HAVE_DBUS */
  826. #ifdef ENABLE_SNMP
  827. if (conf[CS_NTF_SNMP]) {
  828. _cs_snmp_init();
  829. }
  830. #endif /* ENABLE_SNMP */
  831. qb_loop_signal_add(main_loop,
  832. QB_LOOP_HIGH,
  833. SIGINT,
  834. NULL,
  835. sig_exit_handler,
  836. NULL);
  837. qb_loop_signal_add(main_loop,
  838. QB_LOOP_HIGH,
  839. SIGQUIT,
  840. NULL,
  841. sig_exit_handler,
  842. NULL);
  843. qb_loop_signal_add(main_loop,
  844. QB_LOOP_HIGH,
  845. SIGTERM,
  846. NULL,
  847. sig_exit_handler,
  848. NULL);
  849. qb_loop_run(main_loop);
  850. #ifdef HAVE_DBUS
  851. if (conf[CS_NTF_DBUS]) {
  852. _cs_dbus_release();
  853. }
  854. #endif /* HAVE_DBUS */
  855. _cs_quorum_finalize();
  856. _cs_cmap_finalize();
  857. return 0;
  858. }