corosync-objctl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  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. uint8_t *name_pt,
  78. int name_len);
  79. static void tail_object_deleted(confdb_handle_t handle,
  80. hdb_handle_t parent_object_handle,
  81. uint8_t *name_pt,
  82. int 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. int object_name_len;
  95. char key_name[OBJ_NAME_SIZE];
  96. int key_name_len;
  97. char key_value[OBJ_NAME_SIZE];
  98. int 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;
  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. int 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. uint8_t *name_pt,
  376. int name_len)
  377. {
  378. name_pt[name_len] = '\0';
  379. printf("object_created> %s\n", name_pt);
  380. }
  381. static void tail_object_deleted(confdb_handle_t handle,
  382. hdb_handle_t parent_object_handle,
  383. uint8_t *name_pt,
  384. int name_len)
  385. {
  386. name_pt[name_len] = '\0';
  387. printf("object_deleted> %s\n", name_pt);
  388. }
  389. static void listen_for_object_changes(confdb_handle_t handle)
  390. {
  391. int result;
  392. fd_set read_fds;
  393. int select_fd;
  394. int quit = CS_FALSE;
  395. FD_ZERO (&read_fds);
  396. if (confdb_fd_get (handle, &select_fd) != CS_OK) {
  397. printf ("can't get the confdb selector object.\n");
  398. return;
  399. }
  400. printf ("Type \"q\" to finish\n");
  401. do {
  402. FD_SET (select_fd, &read_fds);
  403. FD_SET (STDIN_FILENO, &read_fds);
  404. result = select (select_fd + 1, &read_fds, 0, 0, 0);
  405. if (result == -1) {
  406. perror ("select\n");
  407. }
  408. if (FD_ISSET (STDIN_FILENO, &read_fds)) {
  409. char inbuf[3];
  410. if (fgets(inbuf, sizeof(inbuf), stdin) == NULL)
  411. quit = CS_TRUE;
  412. else if (strncmp(inbuf, "q", 1) == 0)
  413. quit = CS_TRUE;
  414. }
  415. if (FD_ISSET (select_fd, &read_fds)) {
  416. if (confdb_dispatch (handle, CONFDB_DISPATCH_ALL) != CS_OK)
  417. exit(1);
  418. }
  419. } while (result && quit == CS_FALSE);
  420. (void)confdb_stop_track_changes(handle);
  421. }
  422. static void track_object(confdb_handle_t handle, char * name_pt)
  423. {
  424. cs_error_t res;
  425. hdb_handle_t obj_handle;
  426. res = find_object (handle, name_pt, FIND_OBJECT_ONLY, &obj_handle);
  427. if (res != CS_OK) {
  428. fprintf (stderr, "Could not find object \"%s\". Error %d\n",
  429. name_pt, res);
  430. return;
  431. }
  432. res = confdb_track_changes (handle, obj_handle, CONFDB_TRACK_DEPTH_RECURSIVE);
  433. if (res != CS_OK) {
  434. fprintf (stderr, "Could not enable tracking on object \"%s\". Error %d\n",
  435. name_pt, res);
  436. return;
  437. }
  438. }
  439. static void stop_tracking(confdb_handle_t handle)
  440. {
  441. cs_error_t res;
  442. res = confdb_stop_track_changes (handle);
  443. if (res != CS_OK) {
  444. fprintf (stderr, "Could not stop tracking. Error %d\n", res);
  445. return;
  446. }
  447. }
  448. static void delete_object(confdb_handle_t handle, char * name_pt)
  449. {
  450. cs_error_t res;
  451. hdb_handle_t obj_handle;
  452. res = find_object (handle, name_pt, FIND_OBJECT_ONLY, &obj_handle);
  453. if (res == CS_OK) {
  454. res = confdb_object_destroy (handle, obj_handle);
  455. if (res != CS_OK)
  456. fprintf(stderr, "Failed to find object \"%s\" to delete. Error %d\n", name_pt, res);
  457. } else {
  458. char parent_name[OBJ_NAME_SIZE];
  459. char key_name[OBJ_NAME_SIZE];
  460. char key_value[OBJ_NAME_SIZE];
  461. /* find the parent object */
  462. get_parent_name(name_pt, parent_name);
  463. get_key(name_pt, key_name, key_value);
  464. res = find_object (handle, parent_name, FIND_OBJECT_ONLY, &obj_handle);
  465. if (res != CS_OK) {
  466. fprintf(stderr, "Failed to find the key's parent object \"%s\". Error %d\n", parent_name, res);
  467. exit (EXIT_FAILURE);
  468. }
  469. res = confdb_key_delete (handle,
  470. obj_handle,
  471. key_name,
  472. strlen(key_name),
  473. key_value,
  474. strlen(key_value));
  475. if (res != CS_OK)
  476. fprintf(stderr, "Failed to delete key \"%s=%s\" from object \"%s\". Error %d\n",
  477. key_name, key_value, parent_name, res);
  478. }
  479. }
  480. int main (int argc, char *argv[]) {
  481. confdb_handle_t handle;
  482. cs_error_t result;
  483. int c;
  484. action = ACTION_READ;
  485. for (;;){
  486. c = getopt (argc,argv,"hawcdtp:");
  487. if (c==-1) {
  488. break;
  489. }
  490. switch (c) {
  491. case 'h':
  492. return print_help();
  493. break;
  494. case 'a':
  495. action = ACTION_PRINT_ALL;
  496. break;
  497. case 'p':
  498. printf("%s:%d NOT Implemented yet.\n", __FUNCTION__, __LINE__);
  499. return -1;
  500. //return read_in_config_file();
  501. break;
  502. case 'c':
  503. action = ACTION_CREATE;
  504. break;
  505. case 'd':
  506. action = ACTION_DELETE;
  507. break;
  508. case 'w':
  509. action = ACTION_WRITE;
  510. break;
  511. case 't':
  512. action = ACTION_TRACK;
  513. break;
  514. default :
  515. action = ACTION_READ;
  516. break;
  517. }
  518. }
  519. if (argc == 1) {
  520. action = ACTION_PRINT_DEFAULT;
  521. return print_all();
  522. } else if (action == ACTION_PRINT_ALL) {
  523. return print_all();
  524. } else if (optind >= argc) {
  525. fprintf(stderr, "Expected an object path after options\n");
  526. exit(EXIT_FAILURE);
  527. }
  528. result = confdb_initialize (&handle, &callbacks);
  529. if (result != CS_OK) {
  530. fprintf (stderr, "Failed to initialize the objdb API. Error %d\n", result);
  531. exit (EXIT_FAILURE);
  532. }
  533. while (optind < argc) {
  534. switch (action) {
  535. case ACTION_READ:
  536. read_object(handle, argv[optind++]);
  537. break;
  538. case ACTION_WRITE:
  539. write_key(handle, argv[optind++]);
  540. break;
  541. case ACTION_CREATE:
  542. create_object(handle, argv[optind++]);
  543. break;
  544. case ACTION_DELETE:
  545. delete_object(handle, argv[optind++]);
  546. break;
  547. case ACTION_TRACK:
  548. track_object(handle, argv[optind++]);
  549. break;
  550. }
  551. }
  552. if (action == ACTION_TRACK) {
  553. listen_for_object_changes(handle);
  554. stop_tracking(handle);
  555. }
  556. result = confdb_finalize (handle);
  557. if (result != CS_OK) {
  558. fprintf (stderr, "Error finalizing objdb API. Error %d\n", result);
  559. exit(EXIT_FAILURE);
  560. }
  561. return 0;
  562. }