corosync-objctl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. /*
  2. * Copyright (c) 2008 Allied Telesis Labs NZ
  3. *
  4. * All rights reserved.
  5. *
  6. * Author: Angus Salkeld <angus.salkeld@alliedtelesis.co.nz>
  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 <stdio.h>
  37. #include <stdlib.h>
  38. #include <errno.h>
  39. #include <signal.h>
  40. #include <unistd.h>
  41. #include <string.h>
  42. #include <ctype.h>
  43. #include <sys/types.h>
  44. #include <sys/un.h>
  45. #include <corosync/corotypes.h>
  46. #include <corosync/confdb.h>
  47. #define SEPERATOR '.'
  48. #define SEPERATOR_STR "."
  49. #define OBJ_NAME_SIZE 512
  50. typedef enum {
  51. ACTION_READ,
  52. ACTION_WRITE,
  53. ACTION_CREATE,
  54. ACTION_DELETE,
  55. ACTION_PRINT_ALL,
  56. ACTION_PRINT_DEFAULT,
  57. ACTION_TRACK,
  58. } action_types_t;
  59. typedef enum {
  60. FIND_OBJECT_ONLY,
  61. FIND_OBJECT_OR_KEY,
  62. FIND_KEY_ONLY
  63. } find_object_of_type_t;
  64. static void tail_key_changed(confdb_handle_t handle,
  65. confdb_change_type_t change_type,
  66. hdb_handle_t parent_object_handle,
  67. hdb_handle_t object_handle,
  68. const void *object_name,
  69. size_t object_name_len,
  70. const void *key_name,
  71. size_t key_name_len,
  72. const void *key_value,
  73. size_t key_value_len);
  74. static void tail_object_created(confdb_handle_t handle,
  75. hdb_handle_t parent_object_handle,
  76. hdb_handle_t object_handle,
  77. const void *name_pt,
  78. size_t name_len);
  79. static void tail_object_deleted(confdb_handle_t handle,
  80. hdb_handle_t parent_object_handle,
  81. const void *name_pt,
  82. size_t name_len);
  83. static confdb_callbacks_t callbacks = {
  84. .confdb_key_change_notify_fn = tail_key_changed,
  85. .confdb_object_create_change_notify_fn = tail_object_created,
  86. .confdb_object_delete_change_notify_fn = tail_object_deleted,
  87. };
  88. static int action;
  89. /* Recursively dump the object tree */
  90. static void print_config_tree(confdb_handle_t handle, hdb_handle_t parent_object_handle, char * parent_name)
  91. {
  92. hdb_handle_t object_handle;
  93. char object_name[OBJ_NAME_SIZE];
  94. size_t object_name_len;
  95. char key_name[OBJ_NAME_SIZE];
  96. size_t key_name_len;
  97. char key_value[OBJ_NAME_SIZE];
  98. size_t key_value_len;
  99. cs_error_t res;
  100. int children_printed;
  101. /* Show the keys */
  102. res = confdb_key_iter_start(handle, parent_object_handle);
  103. if (res != CS_OK) {
  104. fprintf(stderr, "error resetting key iterator for object %llu: %d\n", parent_object_handle, res);
  105. exit(EXIT_FAILURE);
  106. }
  107. children_printed = 0;
  108. while ( (res = confdb_key_iter(handle,
  109. parent_object_handle,
  110. key_name,
  111. &key_name_len,
  112. key_value,
  113. &key_value_len)) == CS_OK) {
  114. key_name[key_name_len] = '\0';
  115. key_value[key_value_len] = '\0';
  116. if (parent_name != NULL)
  117. printf("%s%c%s=%s\n", parent_name, SEPERATOR,key_name, key_value);
  118. else
  119. printf("%s=%s\n", key_name, key_value);
  120. children_printed++;
  121. }
  122. /* Show sub-objects */
  123. res = confdb_object_iter_start(handle, parent_object_handle);
  124. if (res != CS_OK) {
  125. fprintf(stderr, "error resetting object iterator for object %llu: %d\n", parent_object_handle, res);
  126. exit(EXIT_FAILURE);
  127. }
  128. while ( (res = confdb_object_iter(handle,
  129. parent_object_handle,
  130. &object_handle,
  131. object_name,
  132. &object_name_len)) == CS_OK) {
  133. object_name[object_name_len] = '\0';
  134. if (parent_name != NULL) {
  135. snprintf(key_value, OBJ_NAME_SIZE, "%s%c%s", parent_name, SEPERATOR, object_name);
  136. } else {
  137. if ((action == ACTION_PRINT_DEFAULT) && strcmp(object_name, "internal_configuration") == 0) continue;
  138. snprintf(key_value, OBJ_NAME_SIZE, "%s", object_name);
  139. }
  140. print_config_tree(handle, object_handle, key_value);
  141. children_printed++;
  142. }
  143. if (children_printed == 0 && parent_name != NULL) {
  144. printf("%s\n", parent_name);
  145. }
  146. }
  147. static int print_all(void)
  148. {
  149. confdb_handle_t handle;
  150. int result;
  151. result = confdb_initialize (&handle, &callbacks);
  152. if (result != CS_OK) {
  153. fprintf (stderr, "Could not initialize objdb library. Error %d\n", result);
  154. return 1;
  155. }
  156. print_config_tree(handle, OBJECT_PARENT_HANDLE, NULL);
  157. result = confdb_finalize (handle);
  158. return 0;
  159. }
  160. static int print_help(void)
  161. {
  162. printf("\n");
  163. printf ("usage: corosync-objctl object%ckey ... Print an object\n", SEPERATOR);
  164. printf (" corosync-objctl -c object%cchild_obj ... Create Object\n", SEPERATOR);
  165. printf (" corosync-objctl -d object%cchild_obj ... Delete object\n", SEPERATOR);
  166. printf (" corosync-objctl -w object%cchild_obj.key=value ... Create a key\n", SEPERATOR);
  167. printf (" corosync-objctl -t object%cchild_obj ... Track changes\n", SEPERATOR);
  168. printf (" corosync-objctl -a Print all objects\n");
  169. printf("\n");
  170. return 0;
  171. }
  172. static cs_error_t validate_name(char * obj_name_pt)
  173. {
  174. if ((strchr (obj_name_pt, SEPERATOR) == NULL) &&
  175. (strchr (obj_name_pt, '=') == NULL))
  176. return CS_OK;
  177. else
  178. return CS_ERR_INVALID_PARAM;
  179. }
  180. static void get_parent_name(const char * name_pt, char * parent_name)
  181. {
  182. char * tmp;
  183. strcpy(parent_name, name_pt);
  184. /* first remove the value (it could be a file path */
  185. tmp = strchr(parent_name, '=');
  186. if (tmp != NULL) *tmp = '\0';
  187. /* then truncate the child name */
  188. tmp = strrchr(parent_name, SEPERATOR);
  189. if (tmp != NULL) *tmp = '\0';
  190. }
  191. static void get_key(const char * name_pt, char * key_name, char * key_value)
  192. {
  193. char * tmp;
  194. char str_copy[OBJ_NAME_SIZE];
  195. strcpy(str_copy, name_pt);
  196. /* first remove the value (it could have a SEPERATOR in it */
  197. tmp = strchr(str_copy, '=');
  198. if (tmp != NULL && strlen(tmp) > 0) {
  199. strcpy(key_value, tmp+1);
  200. *tmp = '\0';
  201. } else {
  202. key_value[0] = '\0';
  203. }
  204. /* then remove the name */
  205. tmp = strrchr(str_copy, SEPERATOR);
  206. if (tmp == NULL) tmp = str_copy;
  207. strcpy(key_name, tmp+1);
  208. }
  209. static cs_error_t find_object (confdb_handle_t handle,
  210. char * name_pt,
  211. find_object_of_type_t type,
  212. hdb_handle_t * out_handle)
  213. {
  214. char * obj_name_pt;
  215. char * save_pt;
  216. hdb_handle_t obj_handle;
  217. confdb_handle_t parent_object_handle = OBJECT_PARENT_HANDLE;
  218. char tmp_name[OBJ_NAME_SIZE];
  219. cs_error_t res = CS_OK;
  220. strncpy (tmp_name, name_pt, OBJ_NAME_SIZE);
  221. obj_name_pt = strtok_r(tmp_name, SEPERATOR_STR, &save_pt);
  222. while (obj_name_pt != NULL) {
  223. res = confdb_object_find_start(handle, parent_object_handle);
  224. if (res != CS_OK) {
  225. fprintf (stderr, "Could not start object_find %d\n", res);
  226. exit (EXIT_FAILURE);
  227. }
  228. res = confdb_object_find(handle, parent_object_handle,
  229. obj_name_pt, strlen (obj_name_pt), &obj_handle);
  230. if (res != CS_OK) {
  231. return res;
  232. }
  233. parent_object_handle = obj_handle;
  234. obj_name_pt = strtok_r (NULL, SEPERATOR_STR, &save_pt);
  235. }
  236. *out_handle = parent_object_handle;
  237. return res;
  238. }
  239. static void read_object(confdb_handle_t handle, char * name_pt)
  240. {
  241. char parent_name[OBJ_NAME_SIZE];
  242. hdb_handle_t obj_handle;
  243. cs_error_t res;
  244. get_parent_name(name_pt, parent_name);
  245. res = find_object (handle, name_pt, FIND_OBJECT_OR_KEY, &obj_handle);
  246. if (res == CS_OK) {
  247. print_config_tree(handle, obj_handle, parent_name);
  248. }
  249. }
  250. static void write_key(confdb_handle_t handle, char * path_pt)
  251. {
  252. hdb_handle_t obj_handle;
  253. char parent_name[OBJ_NAME_SIZE];
  254. char key_name[OBJ_NAME_SIZE];
  255. char key_value[OBJ_NAME_SIZE];
  256. char old_key_value[OBJ_NAME_SIZE];
  257. size_t old_key_value_len;
  258. cs_error_t res;
  259. /* find the parent object */
  260. get_parent_name(path_pt, parent_name);
  261. get_key(path_pt, key_name, key_value);
  262. if (validate_name(key_name) != CS_OK) {
  263. fprintf(stderr, "Incorrect key name, can not have \"=\" or \"%c\"\n", SEPERATOR);
  264. exit(EXIT_FAILURE);
  265. }
  266. res = find_object (handle, parent_name, FIND_OBJECT_ONLY, &obj_handle);
  267. if (res != CS_OK) {
  268. fprintf(stderr, "Can't find parent object of \"%s\"\n", path_pt);
  269. exit(EXIT_FAILURE);
  270. }
  271. /* get the current key */
  272. res = confdb_key_get (handle,
  273. obj_handle,
  274. key_name,
  275. strlen(key_name),
  276. old_key_value,
  277. &old_key_value_len);
  278. if (res == CS_OK) {
  279. /* replace the current value */
  280. res = confdb_key_replace (handle,
  281. obj_handle,
  282. key_name,
  283. strlen(key_name),
  284. old_key_value,
  285. old_key_value_len,
  286. key_value,
  287. strlen(key_value));
  288. if (res != CS_OK)
  289. fprintf(stderr, "Failed to replace the key %s=%s. Error %d\n", key_name, key_value, res);
  290. } else {
  291. /* not there, create a new key */
  292. res = confdb_key_create (handle,
  293. obj_handle,
  294. key_name,
  295. strlen(key_name),
  296. key_value,
  297. strlen(key_value));
  298. if (res != CS_OK)
  299. fprintf(stderr, "Failed to create the key %s=%s. Error %d\n", key_name, key_value, res);
  300. }
  301. }
  302. static void create_object(confdb_handle_t handle, char * name_pt)
  303. {
  304. char * obj_name_pt;
  305. char * save_pt;
  306. hdb_handle_t obj_handle;
  307. hdb_handle_t parent_object_handle = OBJECT_PARENT_HANDLE;
  308. char tmp_name[OBJ_NAME_SIZE];
  309. cs_error_t res;
  310. strncpy (tmp_name, name_pt, OBJ_NAME_SIZE);
  311. obj_name_pt = strtok_r(tmp_name, SEPERATOR_STR, &save_pt);
  312. while (obj_name_pt != NULL) {
  313. res = confdb_object_find_start(handle, parent_object_handle);
  314. if (res != CS_OK) {
  315. fprintf (stderr, "Could not start object_find %d\n", res);
  316. exit (EXIT_FAILURE);
  317. }
  318. res = confdb_object_find(handle, parent_object_handle,
  319. obj_name_pt, strlen (obj_name_pt), &obj_handle);
  320. if (res != CS_OK) {
  321. if (validate_name(obj_name_pt) != CS_OK) {
  322. fprintf(stderr, "Incorrect object name \"%s\", \"=\" not allowed.\n",
  323. obj_name_pt);
  324. exit(EXIT_FAILURE);
  325. }
  326. res = confdb_object_create (handle,
  327. parent_object_handle,
  328. obj_name_pt,
  329. strlen (obj_name_pt),
  330. &obj_handle);
  331. if (res != CS_OK)
  332. fprintf(stderr, "Failed to create object \"%s\". Error %d.\n",
  333. obj_name_pt, res);
  334. }
  335. parent_object_handle = obj_handle;
  336. obj_name_pt = strtok_r (NULL, SEPERATOR_STR, &save_pt);
  337. }
  338. }
  339. /* Print "?" in place of any non-printable byte of OBJ. */
  340. static void print_name (FILE *fp, const void *obj, size_t obj_len)
  341. {
  342. const char *p = obj;
  343. size_t i;
  344. for (i = 0; i < obj_len; i++) {
  345. int c = *p++;
  346. if (!isprint (c)) {
  347. c = '?';
  348. }
  349. fputc (c, fp);
  350. }
  351. }
  352. static void tail_key_changed(confdb_handle_t handle,
  353. confdb_change_type_t change_type,
  354. hdb_handle_t parent_object_handle,
  355. hdb_handle_t object_handle,
  356. const void *object_name_pt,
  357. size_t object_name_len,
  358. const void *key_name_pt,
  359. size_t key_name_len,
  360. const void *key_value_pt,
  361. size_t key_value_len)
  362. {
  363. /* printf("key_changed> %.*s.%.*s=%.*s\n", */
  364. fputs("key_changed> ", stdout);
  365. print_name (stdout, object_name_pt, object_name_len);
  366. fputs(".", stdout);
  367. print_name (stdout, key_name_pt, key_name_len);
  368. fputs("=", stdout);
  369. print_name (stdout, key_value_pt, key_value_len);
  370. fputs("\n", stdout);
  371. }
  372. static void tail_object_created(confdb_handle_t handle,
  373. hdb_handle_t parent_object_handle,
  374. hdb_handle_t object_handle,
  375. const void *name_pt,
  376. size_t name_len)
  377. {
  378. fputs("object_created>", stdout);
  379. print_name(stdout, name_pt, name_len);
  380. fputs("\n", stdout);
  381. }
  382. static void tail_object_deleted(confdb_handle_t handle,
  383. hdb_handle_t parent_object_handle,
  384. const void *name_pt,
  385. size_t name_len)
  386. {
  387. fputs("object_deleted>", stdout);
  388. print_name(stdout, name_pt, name_len);
  389. fputs("\n", stdout);
  390. }
  391. static void listen_for_object_changes(confdb_handle_t handle)
  392. {
  393. int result;
  394. fd_set read_fds;
  395. int select_fd;
  396. int quit = CS_FALSE;
  397. FD_ZERO (&read_fds);
  398. if (confdb_fd_get (handle, &select_fd) != CS_OK) {
  399. printf ("can't get the confdb selector object.\n");
  400. return;
  401. }
  402. printf ("Type \"q\" to finish\n");
  403. do {
  404. FD_SET (select_fd, &read_fds);
  405. FD_SET (STDIN_FILENO, &read_fds);
  406. result = select (select_fd + 1, &read_fds, 0, 0, 0);
  407. if (result == -1) {
  408. perror ("select\n");
  409. }
  410. if (FD_ISSET (STDIN_FILENO, &read_fds)) {
  411. char inbuf[3];
  412. if (fgets(inbuf, sizeof(inbuf), stdin) == NULL)
  413. quit = CS_TRUE;
  414. else if (strncmp(inbuf, "q", 1) == 0)
  415. quit = CS_TRUE;
  416. }
  417. if (FD_ISSET (select_fd, &read_fds)) {
  418. if (confdb_dispatch (handle, CONFDB_DISPATCH_ALL) != CS_OK)
  419. exit(1);
  420. }
  421. } while (result && quit == CS_FALSE);
  422. (void)confdb_stop_track_changes(handle);
  423. }
  424. static void track_object(confdb_handle_t handle, char * name_pt)
  425. {
  426. cs_error_t res;
  427. hdb_handle_t obj_handle;
  428. res = find_object (handle, name_pt, FIND_OBJECT_ONLY, &obj_handle);
  429. if (res != CS_OK) {
  430. fprintf (stderr, "Could not find object \"%s\". Error %d\n",
  431. name_pt, res);
  432. return;
  433. }
  434. res = confdb_track_changes (handle, obj_handle, CONFDB_TRACK_DEPTH_RECURSIVE);
  435. if (res != CS_OK) {
  436. fprintf (stderr, "Could not enable tracking on object \"%s\". Error %d\n",
  437. name_pt, res);
  438. return;
  439. }
  440. }
  441. static void stop_tracking(confdb_handle_t handle)
  442. {
  443. cs_error_t res;
  444. res = confdb_stop_track_changes (handle);
  445. if (res != CS_OK) {
  446. fprintf (stderr, "Could not stop tracking. Error %d\n", res);
  447. return;
  448. }
  449. }
  450. static void delete_object(confdb_handle_t handle, char * name_pt)
  451. {
  452. cs_error_t res;
  453. hdb_handle_t obj_handle;
  454. res = find_object (handle, name_pt, FIND_OBJECT_ONLY, &obj_handle);
  455. if (res == CS_OK) {
  456. res = confdb_object_destroy (handle, obj_handle);
  457. if (res != CS_OK)
  458. fprintf(stderr, "Failed to find object \"%s\" to delete. Error %d\n", name_pt, res);
  459. } else {
  460. char parent_name[OBJ_NAME_SIZE];
  461. char key_name[OBJ_NAME_SIZE];
  462. char key_value[OBJ_NAME_SIZE];
  463. /* find the parent object */
  464. get_parent_name(name_pt, parent_name);
  465. get_key(name_pt, key_name, key_value);
  466. res = find_object (handle, parent_name, FIND_OBJECT_ONLY, &obj_handle);
  467. if (res != CS_OK) {
  468. fprintf(stderr, "Failed to find the key's parent object \"%s\". Error %d\n", parent_name, res);
  469. exit (EXIT_FAILURE);
  470. }
  471. res = confdb_key_delete (handle,
  472. obj_handle,
  473. key_name,
  474. strlen(key_name),
  475. key_value,
  476. strlen(key_value));
  477. if (res != CS_OK)
  478. fprintf(stderr, "Failed to delete key \"%s=%s\" from object \"%s\". Error %d\n",
  479. key_name, key_value, parent_name, res);
  480. }
  481. }
  482. int main (int argc, char *argv[]) {
  483. confdb_handle_t handle;
  484. cs_error_t result;
  485. int c;
  486. action = ACTION_READ;
  487. for (;;){
  488. c = getopt (argc,argv,"hawcdtp:");
  489. if (c==-1) {
  490. break;
  491. }
  492. switch (c) {
  493. case 'h':
  494. return print_help();
  495. break;
  496. case 'a':
  497. action = ACTION_PRINT_ALL;
  498. break;
  499. case 'p':
  500. printf("%s:%d NOT Implemented yet.\n", __FUNCTION__, __LINE__);
  501. return -1;
  502. //return read_in_config_file();
  503. break;
  504. case 'c':
  505. action = ACTION_CREATE;
  506. break;
  507. case 'd':
  508. action = ACTION_DELETE;
  509. break;
  510. case 'w':
  511. action = ACTION_WRITE;
  512. break;
  513. case 't':
  514. action = ACTION_TRACK;
  515. break;
  516. default :
  517. action = ACTION_READ;
  518. break;
  519. }
  520. }
  521. if (argc == 1) {
  522. action = ACTION_PRINT_DEFAULT;
  523. return print_all();
  524. } else if (action == ACTION_PRINT_ALL) {
  525. return print_all();
  526. } else if (optind >= argc) {
  527. fprintf(stderr, "Expected an object path after options\n");
  528. exit(EXIT_FAILURE);
  529. }
  530. result = confdb_initialize (&handle, &callbacks);
  531. if (result != CS_OK) {
  532. fprintf (stderr, "Failed to initialize the objdb API. Error %d\n", result);
  533. exit (EXIT_FAILURE);
  534. }
  535. while (optind < argc) {
  536. switch (action) {
  537. case ACTION_READ:
  538. read_object(handle, argv[optind++]);
  539. break;
  540. case ACTION_WRITE:
  541. write_key(handle, argv[optind++]);
  542. break;
  543. case ACTION_CREATE:
  544. create_object(handle, argv[optind++]);
  545. break;
  546. case ACTION_DELETE:
  547. delete_object(handle, argv[optind++]);
  548. break;
  549. case ACTION_TRACK:
  550. track_object(handle, argv[optind++]);
  551. break;
  552. }
  553. }
  554. if (action == ACTION_TRACK) {
  555. listen_for_object_changes(handle);
  556. stop_tracking(handle);
  557. }
  558. result = confdb_finalize (handle);
  559. if (result != CS_OK) {
  560. fprintf (stderr, "Error finalizing objdb API. Error %d\n", result);
  561. exit(EXIT_FAILURE);
  562. }
  563. return 0;
  564. }