dbscan.c 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2005 Red Hat, Inc.
  3. * All rights reserved.
  4. *
  5. * License: GPL (version 3 or any later version).
  6. * See LICENSE for details.
  7. * END COPYRIGHT BLOCK **/
  8. #ifdef HAVE_CONFIG_H
  9. # include <config.h>
  10. #endif
  11. /*
  12. * small program to scan a Directory Server db file and dump the contents
  13. *
  14. * TODO: indirect indexes
  15. */
  16. #include <stdio.h>
  17. #include <stdarg.h>
  18. #include <stdlib.h>
  19. #include <sys/types.h>
  20. #include <time.h>
  21. #include <string.h>
  22. #include <ctype.h>
  23. #include <errno.h>
  24. #include "db.h"
  25. #include "nspr.h"
  26. #include <netinet/in.h>
  27. #include <inttypes.h>
  28. #if ( defined( hpux ) )
  29. #ifdef _XOPEN_SOURCE_EXTENDED
  30. #include <arpa/inet.h> /* for ntohl, et al. */
  31. #endif
  32. #endif
  33. /* file type */
  34. #define ENTRYTYPE 0x1
  35. #define INDEXTYPE 0x2
  36. #define VLVINDEXTYPE 0x4
  37. #define CHANGELOGTYPE 0x8
  38. #define ENTRYRDNINDEXTYPE 0x10
  39. /* display mode */
  40. #define RAWDATA 0x1
  41. #define SHOWCOUNT 0x2
  42. #define SHOWDATA 0x4
  43. #define SHOWSUMMARY 0x8
  44. /* stolen from slapi-plugin.h */
  45. #define SLAPI_OPERATION_BIND 0x00000001UL
  46. #define SLAPI_OPERATION_UNBIND 0x00000002UL
  47. #define SLAPI_OPERATION_SEARCH 0x00000004UL
  48. #define SLAPI_OPERATION_MODIFY 0x00000008UL
  49. #define SLAPI_OPERATION_ADD 0x00000010UL
  50. #define SLAPI_OPERATION_DELETE 0x00000020UL
  51. #define SLAPI_OPERATION_MODDN 0x00000040UL
  52. #define SLAPI_OPERATION_MODRDN SLAPI_OPERATION_MODDN
  53. #define SLAPI_OPERATION_COMPARE 0x00000080UL
  54. #define SLAPI_OPERATION_ABANDON 0x00000100UL
  55. #define SLAPI_OPERATION_EXTENDED 0x00000200UL
  56. #define SLAPI_OPERATION_ANY 0xFFFFFFFFUL
  57. #define SLAPI_OPERATION_NONE 0x00000000UL
  58. /* changelog ruv info. These correspond with some special csn
  59. * timestamps from cl5_api.c */
  60. #define ENTRY_COUNT_KEY "0000006f" /* 111 csn timestamp */
  61. #define PURGE_RUV_KEY "000000de" /* 222 csn timestamp */
  62. #define MAX_RUV_KEY "0000014d" /* 333 csn timestamp */
  63. #define ONEMEG (1024*1024)
  64. #ifndef DB_BUFFER_SMALL
  65. #define DB_BUFFER_SMALL ENOMEM
  66. #endif
  67. #if defined(linux)
  68. #include <getopt.h>
  69. #endif
  70. typedef PRUint32 ID;
  71. typedef struct {
  72. PRUint32 max;
  73. PRUint32 used;
  74. PRUint32 id[1];
  75. } IDL;
  76. #define RDN_BULK_FETCH_BUFFER_SIZE (8*1024)
  77. typedef struct _rdn_elem {
  78. char rdn_elem_id[sizeof(ID)];
  79. char rdn_elem_nrdn_len[2]; /* ushort; length including '\0' */
  80. char rdn_elem_rdn_len[2]; /* ushort; length including '\0' */
  81. char rdn_elem_nrdn_rdn[1]; /* "normalized rdn" '\0' "rdn" '\0' */
  82. } rdn_elem;
  83. #define RDN_ADDR(elem) \
  84. ((elem)->rdn_elem_nrdn_rdn + \
  85. sizeushort_stored_to_internal((elem)->rdn_elem_nrdn_len))
  86. static void display_entryrdn_parent(DB *db, ID id, const char *nrdn, int indent);
  87. static void display_entryrdn_self(DB *db, ID id, const char *nrdn, int indent);
  88. static void display_entryrdn_children(DB *db, ID id, const char *nrdn, int indent);
  89. static void display_entryrdn_item(DB *db, DBC *cursor, DBT *key);
  90. PRUint32 file_type = 0;
  91. PRUint32 min_display = 0;
  92. PRUint32 display_mode = 0;
  93. int truncatesiz = 0;
  94. long pres_cnt = 0;
  95. long eq_cnt = 0;
  96. long app_cnt = 0;
  97. long sub_cnt = 0;
  98. long match_cnt = 0;
  99. long ind_cnt = 0;
  100. long allids_cnt = 0;
  101. long other_cnt = 0;
  102. /** db_printf - functioning same as printf but a place for manipluating output.
  103. */
  104. void db_printf(char *fmt, ...)
  105. {
  106. va_list ap;
  107. va_start(ap, fmt);
  108. vfprintf(stdout, fmt, ap);
  109. va_end(ap);
  110. }
  111. void db_printfln(char *fmt, ...)
  112. {
  113. va_list ap;
  114. va_start(ap, fmt);
  115. vfprintf(stdout, fmt, ap);
  116. va_end(ap);
  117. fprintf(stdout, "\n");
  118. }
  119. int MAX_BUFFER = 4096;
  120. int MIN_BUFFER = 20;
  121. static IDL *idl_make(DBT *data)
  122. {
  123. IDL *idl = NULL, *xidl;
  124. if (data->size < 2*sizeof(PRUint32)) {
  125. idl = (IDL *)malloc(sizeof(IDL) + 64*sizeof(ID));
  126. if (! idl)
  127. return NULL;
  128. idl->max = 64;
  129. idl->used = 1;
  130. idl->id[0] = *(ID *)(data->data);
  131. return idl;
  132. }
  133. xidl = (IDL *)(data->data);
  134. idl = (IDL *)malloc(data->size);
  135. if (! idl)
  136. return NULL;
  137. memcpy(idl, xidl, data->size);
  138. return idl;
  139. }
  140. static void idl_free(IDL *idl)
  141. {
  142. idl->max = 0;
  143. idl->used = 0;
  144. free(idl);
  145. }
  146. static IDL *idl_append(IDL *idl, ID id)
  147. {
  148. if (idl->used >= idl->max) {
  149. /* must grow */
  150. idl->max *= 2;
  151. idl = realloc(idl, sizeof(IDL) + idl->max * sizeof(ID));
  152. if (! idl)
  153. return NULL;
  154. }
  155. idl->id[idl->used++] = id;
  156. return idl;
  157. }
  158. /* format a string for easy printing */
  159. #define FMT_LF_OK 1
  160. #define FMT_SP_OK 2
  161. static char *format_raw(unsigned char *s, int len, int flags,
  162. unsigned char *buf, int buflen)
  163. {
  164. static char hex[] = "0123456789ABCDEF";
  165. unsigned char *p, *o, *bufend = buf + buflen - 1;
  166. int i;
  167. if (NULL == buf || buflen <= 0)
  168. return NULL;
  169. for (p = s, o = buf, i = 0; i < len && o < bufend; p++, i++) {
  170. if ((*p == '%') || (*p <= ' ') || (*p >= 126)) {
  171. /* index keys are stored with their trailing NUL */
  172. if ((*p == 0) && (i == len-1))
  173. continue;
  174. if ((flags & FMT_LF_OK) && (*p == '\n')) {
  175. *o++ = '\n';
  176. *o++ = '\t';
  177. } else if ((flags & FMT_SP_OK) && (*p == ' ')) {
  178. *o++ = ' ';
  179. } else {
  180. *o++ = '%';
  181. *o++ = hex[*p / 16];
  182. *o++ = hex[*p % 16];
  183. }
  184. } else {
  185. *o++ = *p;
  186. }
  187. if (truncatesiz > 0 && o > bufend - 5) {
  188. /* truncate it */
  189. strcpy((char *)o, " ...");
  190. i = len;
  191. o += 4;
  192. }
  193. }
  194. *o = 0;
  195. return (char *)buf;
  196. }
  197. static char *format(unsigned char *s, int len, unsigned char *buf, int buflen)
  198. {
  199. return format_raw(s, len, 0, buf, buflen);
  200. }
  201. static char *format_entry(unsigned char *s, int len,
  202. unsigned char *buf, int buflen)
  203. {
  204. return format_raw(s, len, FMT_LF_OK | FMT_SP_OK, buf, buflen);
  205. }
  206. static char *idl_format(IDL *idl, int isfirsttime, int *done)
  207. {
  208. static char *buf = NULL;
  209. static ID i = 0;
  210. if (buf == NULL) {
  211. buf = (char *)malloc(MAX_BUFFER);
  212. if (buf == NULL)
  213. return "?";
  214. }
  215. buf[0] = 0;
  216. if (0 != isfirsttime) {
  217. i = 0;
  218. }
  219. for (; i < idl->used; i++) {
  220. sprintf((char *)buf + strlen(buf), "%d ", idl->id[i]);
  221. if (strlen(buf) > (size_t)MAX_BUFFER-MIN_BUFFER) {
  222. i++;
  223. done = 0;
  224. return (char *)buf;
  225. }
  226. }
  227. *done = 1;
  228. return (char *)buf;
  229. }
  230. /*** Copied from cl5_api.c: _cl5ReadString ***/
  231. void _cl5ReadString (char **str, char **buff)
  232. {
  233. if (str)
  234. {
  235. int len = strlen (*buff);
  236. if (len)
  237. {
  238. *str = strdup(*buff);
  239. (*buff) += len + 1;
  240. }
  241. else /* just null char - skip it */
  242. {
  243. *str = NULL;
  244. (*buff) ++;
  245. }
  246. }
  247. else /* just skip this string */
  248. {
  249. (*buff) += strlen (*buff) + 1;
  250. }
  251. }
  252. /** print_attr - print attribute name followed by one value.
  253. assume the value stored as null terminated string.
  254. */
  255. void print_attr(char *attrname, char **buff)
  256. {
  257. char *val = NULL;
  258. _cl5ReadString(&val, buff);
  259. if(attrname == NULL && val == NULL) {
  260. return;
  261. }
  262. db_printf("\t");
  263. if(attrname) {
  264. db_printf("%s: ", attrname);
  265. } else {
  266. db_printf("unknown attribute: ");
  267. }
  268. if(val) {
  269. db_printf("%s\n", val);
  270. free(val);
  271. } else {
  272. db_printf("\n");
  273. }
  274. }
  275. /*** Copied from cl5_api.c: _cl5ReadMods ***/
  276. /* mods format:
  277. -----------
  278. <4 byte mods count><mod1><mod2>...
  279. mod format:
  280. -----------
  281. <1 byte modop><null terminated attr name><4 byte count>
  282. {<4 byte size><value1><4 byte size><value2>... ||
  283. <null terminated str1> <null terminated str2>...}
  284. */
  285. void _cl5ReadMod(char **buff);
  286. void _cl5ReadMods(char **buff)
  287. {
  288. char *pos = *buff;
  289. ID i;
  290. PRUint32 mod_count;
  291. /* need to copy first, to skirt around alignment problems on certain
  292. architectures */
  293. memcpy((char *)&mod_count, *buff, sizeof(mod_count));
  294. mod_count = ntohl(mod_count);
  295. pos += sizeof (mod_count);
  296. for (i = 0; i < mod_count; i++)
  297. {
  298. _cl5ReadMod (&pos);
  299. }
  300. *buff = pos;
  301. }
  302. /** print_ber_attr - print one line of attribute, the value was stored
  303. in ber format, length followed by string.
  304. */
  305. void print_ber_attr(char* attrname, char** buff)
  306. {
  307. char *val = NULL;
  308. PRUint32 bv_len;
  309. memcpy((char *)&bv_len, *buff, sizeof(bv_len));
  310. bv_len = ntohl(bv_len);
  311. *buff += sizeof (uint32);
  312. if (bv_len > 0) {
  313. db_printf("\t\t");
  314. if(attrname != NULL) {
  315. db_printf("%s: ", attrname);
  316. }
  317. val = malloc(bv_len + 1);
  318. memcpy (val, *buff, bv_len);
  319. val[bv_len] = 0;
  320. *buff += bv_len;
  321. db_printf("%s\n", val);
  322. free(val);
  323. }
  324. }
  325. /*
  326. * Conversion routines between host byte order and
  327. * big-endian (which is how we store data in the db).
  328. */
  329. static ID id_stored_to_internal(char* b)
  330. {
  331. ID i;
  332. i = (ID)b[3] & 0x000000ff;
  333. i |= (((ID)b[2]) << 8) & 0x0000ff00;
  334. i |= (((ID)b[1]) << 16) & 0x00ff0000;
  335. i |= ((ID)b[0]) << 24;
  336. return i;
  337. }
  338. static void id_internal_to_stored(ID i,char *b)
  339. {
  340. if ( sizeof(ID) > 4 ) {
  341. (void)memset (b+4, 0, sizeof(ID)-4);
  342. }
  343. b[0] = (char)(i >> 24);
  344. b[1] = (char)(i >> 16);
  345. b[2] = (char)(i >> 8);
  346. b[3] = (char)i;
  347. }
  348. static size_t sizeushort_stored_to_internal(char* b)
  349. {
  350. size_t i;
  351. i = (PRUint16)b[1] & 0x000000ff;
  352. i |= (((PRUint16)b[0]) << 8) & 0x0000ff00;
  353. return i;
  354. }
  355. void _cl5ReadMod(char **buff)
  356. {
  357. char *pos = *buff;
  358. PRUint32 i;
  359. PRUint32 val_count;
  360. char *type = NULL;
  361. pos ++;
  362. _cl5ReadString (&type, &pos);
  363. /* need to do the copy first, to skirt around alignment problems on
  364. certain architectures */
  365. memcpy((char *)&val_count, pos, sizeof(val_count));
  366. val_count = ntohl(val_count);
  367. pos += sizeof (PRUint32);
  368. for (i = 0; i < val_count; i++)
  369. {
  370. print_ber_attr(type, &pos);
  371. }
  372. (*buff) = pos;
  373. free(type);
  374. }
  375. /* data format: <value count> <value size> <value> <value size> <value> ..... */
  376. void print_ruv(unsigned char *buff)
  377. {
  378. char *pos = (char *)buff;
  379. PRUint32 i;
  380. PRUint32 val_count;
  381. /* need to do the copy first, to skirt around alignment problems on
  382. certain architectures */
  383. memcpy((char *)&val_count, pos, sizeof(val_count));
  384. val_count = ntohl(val_count);
  385. pos += sizeof (PRUint32);
  386. for (i = 0; i < val_count; i++)
  387. {
  388. print_ber_attr(NULL, &pos);
  389. }
  390. }
  391. /*
  392. *** Copied from cl5_api:cl5DBData2Entry ***
  393. Data in db format:
  394. ------------------
  395. <1 byte version><1 byte change_type><sizeof PRUint32 time><null terminated dbid>
  396. <null terminated csn><null terminated uniqueid><null terminated targetdn>
  397. [<null terminated newrdn><1 byte deleteoldrdn>][<4 byte mod count><mod1><mod2>....]
  398. Note: the length of time is set PRUint32 instead of time_t. Regardless of the
  399. width of long (32-bit or 64-bit), it's stored using 4bytes by the server [153306].
  400. mod format:
  401. -----------
  402. <0 byte modop><null terminated attr name><4 byte value count>
  403. <4 byte value size><value1><4 byte value size><value2>
  404. */
  405. void print_changelog(unsigned char *data, int len)
  406. {
  407. uint8_t version;
  408. unsigned long operation_type;
  409. char *pos = (char *)data;
  410. PRUint32 thetime32;
  411. time_t thetime;
  412. PRUint32 replgen;
  413. /* read byte of version */
  414. version = *((uint8_t *)pos);
  415. if (version != 5)
  416. {
  417. db_printf("Invalid changelog db version %i\nWorks for version 5 only.\n", version);
  418. exit(1);
  419. }
  420. pos += sizeof(version);
  421. /* read change type */
  422. operation_type = (unsigned long)(*(uint8_t *)pos);
  423. pos ++;
  424. /* need to do the copy first, to skirt around alignment problems on
  425. certain architectures */
  426. memcpy((char *)&thetime32, pos, sizeof(thetime32));
  427. replgen = ntohl(thetime32);
  428. pos += sizeof(PRUint32);
  429. thetime = (time_t)replgen;
  430. db_printf("\treplgen: %ld %s", replgen, ctime((time_t *)&thetime));
  431. /* read csn */
  432. print_attr("csn", &pos);
  433. /* read UniqueID */
  434. print_attr("uniqueid", &pos);
  435. /* figure out what else we need to read depending on the operation type */
  436. switch (operation_type)
  437. {
  438. case SLAPI_OPERATION_ADD:
  439. print_attr("parentuniqueid", &pos);
  440. print_attr("dn", &pos);
  441. /* convert mods to entry */
  442. db_printf("\toperation: add\n");
  443. _cl5ReadMods(&pos);
  444. break;
  445. case SLAPI_OPERATION_MODIFY:
  446. print_attr("dn", &pos);
  447. db_printf("\toperation: modify\n");
  448. _cl5ReadMods(&pos);
  449. break;
  450. case SLAPI_OPERATION_MODRDN:
  451. {
  452. print_attr("dn", &pos);
  453. db_printf("\toperation: modrdn\n");
  454. print_attr("newrdn", &pos);
  455. db_printf("\tdeleteoldrdn: %d\n", (int )(*pos++));
  456. print_attr("newsuperior", &pos);
  457. _cl5ReadMods(&pos);
  458. break;
  459. }
  460. case SLAPI_OPERATION_DELETE:
  461. print_attr("dn", &pos);
  462. db_printf("\toperation: delete\n");
  463. break;
  464. default:
  465. db_printf("Failed to format entry\n");
  466. break;
  467. }
  468. }
  469. static void display_index_item(DBC *cursor, DBT *key, DBT *data,
  470. unsigned char *buf, int buflen)
  471. {
  472. IDL *idl = NULL;
  473. int ret = 0;
  474. idl = idl_make(data);
  475. if (idl == NULL) {
  476. printf("\t(illegal idl)\n");
  477. return;
  478. }
  479. if (file_type & VLVINDEXTYPE) { /* vlv index file */
  480. if (1 > min_display) { /* recno is always 1 */
  481. if (display_mode & SHOWCOUNT) { /* key size=1 */
  482. printf("%-40s 1\n",
  483. format(key->data, key->size, buf, buflen));
  484. } else {
  485. printf("%-40s\n", format(key->data, key->size, buf, buflen));
  486. }
  487. if (display_mode & SHOWDATA) {
  488. cursor->c_get(cursor, key, data, DB_GET_RECNO);
  489. printf("\t%5d\n", *(db_recno_t *)(data->data));
  490. }
  491. }
  492. goto index_done;
  493. }
  494. /* ordinary index file */
  495. /* fetch all other id's too */
  496. while (ret == 0) {
  497. ret = cursor->c_get(cursor, key, data, DB_NEXT_DUP);
  498. if (ret == 0)
  499. idl = idl_append(idl, *(PRUint32 *)(data->data));
  500. }
  501. if (ret == DB_NOTFOUND)
  502. ret = 0;
  503. if (ret != 0) {
  504. printf("Failure while looping dupes: %s\n", db_strerror(ret));
  505. exit(1);
  506. }
  507. if (idl == NULL) {
  508. printf("\t(illegal idl)\n");
  509. return;
  510. }
  511. if (idl->max == 0) {
  512. /* allids; should not exist in the new idl world */
  513. if ( allids_cnt == 0 && (display_mode & SHOWSUMMARY)) {
  514. printf("The following index keys reached allids:\n");
  515. }
  516. printf("%-40s(allids)\n", format(key->data, key->size, buf, buflen));
  517. allids_cnt++;
  518. } else {
  519. if (idl->used < min_display) {
  520. goto index_done; /* less than minimum display count */
  521. } else if (display_mode & SHOWCOUNT) { /* key size */
  522. printf("%-40s%d\n",
  523. format(key->data, key->size, buf, buflen), idl->used);
  524. } else if (!(display_mode & SHOWSUMMARY) || (display_mode & SHOWDATA)) {
  525. /* show keys only if show summary is not set or
  526. * even if it's set, but with show data */
  527. printf("%-40s\n", format(key->data, key->size, buf, buflen));
  528. }
  529. if (display_mode & SHOWDATA) {
  530. char *formatted_idl = NULL;
  531. int done = 0;
  532. int isfirsttime = 1;
  533. while (0 == done) {
  534. formatted_idl = idl_format(idl, isfirsttime, &done);
  535. if (NULL == formatted_idl) {
  536. done = 1; /* no more idl */
  537. } else {
  538. if (1 == isfirsttime) {
  539. printf("\t%s", formatted_idl);
  540. isfirsttime = 0;
  541. } else {
  542. printf("%s", formatted_idl);
  543. }
  544. }
  545. }
  546. printf("\n");
  547. }
  548. }
  549. index_done:
  550. if ( display_mode & SHOWSUMMARY ) {
  551. char firstchar;
  552. firstchar = ((char*)key->data)[0];
  553. switch ( firstchar ) {
  554. case '+':
  555. pres_cnt += idl->used;
  556. break;
  557. case '=':
  558. eq_cnt += idl->used;
  559. break;
  560. case '~':
  561. app_cnt += idl->used;
  562. break;
  563. case '*':
  564. sub_cnt += idl->used;
  565. break;
  566. case ':':
  567. match_cnt += idl->used;
  568. break;
  569. case '\\':
  570. ind_cnt += idl->used;
  571. break;
  572. default:
  573. other_cnt += idl->used;
  574. break;
  575. }
  576. }
  577. idl_free(idl);
  578. return;
  579. }
  580. static void display_item(DBC *cursor, DBT *key, DBT *data)
  581. {
  582. static unsigned char *buf = NULL;
  583. static int buflen = 0;
  584. int tmpbuflen;
  585. if (truncatesiz > 0) {
  586. tmpbuflen = truncatesiz;
  587. } else if (file_type & INDEXTYPE) {
  588. /* +256: extra buffer for '\t' and '%##' */
  589. tmpbuflen = key->size + 256;
  590. } else {
  591. /* +1024: extra buffer for '\t' and '%##' */
  592. tmpbuflen = (key->size > data->size ? key->size : data->size) + 1024;
  593. }
  594. if (buflen < tmpbuflen) {
  595. buflen = tmpbuflen;
  596. buf = (unsigned char *)realloc(buf, buflen);
  597. if (NULL == buf) {
  598. printf("\t(malloc failed -- %d bytes)\n", buflen);
  599. return;
  600. }
  601. }
  602. if (display_mode & RAWDATA) {
  603. printf("%s\n", format(key->data, key->size, buf, buflen));
  604. printf("\t%s\n", format(data->data, data->size, buf, buflen));
  605. } else {
  606. if (file_type & INDEXTYPE) {
  607. display_index_item(cursor, key, data, buf, buflen);
  608. } else if (file_type & CHANGELOGTYPE) {
  609. /* changelog db file */
  610. printf("\ndbid: %s\n", format(key->data, key->size, buf, buflen));
  611. if (strncasecmp((char *)key->data, ENTRY_COUNT_KEY, 8) == 0) {
  612. printf("\tentry count: %d\n", *(int*)data->data);
  613. } else if (strncasecmp((char *)key->data, PURGE_RUV_KEY, 8) == 0) {
  614. printf("\tpurge ruv:\n");
  615. print_ruv(data->data);
  616. } else if (strncasecmp((char *)key->data, MAX_RUV_KEY, 8) == 0) {
  617. printf("\tmax ruv:\n");
  618. print_ruv(data->data);
  619. } else {
  620. print_changelog(data->data, data->size);
  621. }
  622. return;
  623. } else if (file_type & ENTRYTYPE) {
  624. /* id2entry file */
  625. ID entry_id = id_stored_to_internal(key->data);
  626. printf("id %u\n", entry_id);
  627. printf("\t%s\n", format_entry(data->data, data->size, buf, buflen));
  628. } else {
  629. /* user didn't tell us what kind of file, dump it raw */
  630. printf("%s\n", format(key->data, key->size, buf, buflen));
  631. printf("\t%s\n", format(data->data, data->size, buf, buflen));
  632. }
  633. }
  634. return;
  635. }
  636. void
  637. _entryrdn_dump_rdn_elem(char *key, rdn_elem *elem, int indent)
  638. {
  639. char *indentp = (char *)malloc(indent + 1);
  640. char *p, *endp = indentp + indent;
  641. for (p = indentp; p < endp; p++) *p = ' ';
  642. *p = '\0';
  643. printf("%s\n", key);
  644. printf("%sID: %u; RDN: \"%s\"; NRDN: \"%s\"\n",
  645. indentp, id_stored_to_internal(elem->rdn_elem_id),
  646. RDN_ADDR(elem), elem->rdn_elem_nrdn_rdn);
  647. free(indentp);
  648. }
  649. static void
  650. display_entryrdn_self(DB *db, ID id, const char *nrdn, int indent)
  651. {
  652. DBC *cursor = NULL;
  653. DBT key, data;
  654. char *keybuf = NULL;
  655. int rc = 0;
  656. rdn_elem *elem;
  657. char buffer[1024];
  658. rc = db->cursor(db, NULL, &cursor, 0);
  659. if (rc) {
  660. printf("Can't create db cursor: %s\n", db_strerror(rc));
  661. exit(1);
  662. }
  663. snprintf(buffer, sizeof(buffer), "%u", id);
  664. keybuf = strdup(buffer);
  665. key.data = keybuf;
  666. key.size = key.ulen = strlen(keybuf) + 1;
  667. key.flags = DB_DBT_USERMEM;
  668. memset(&data, 0, sizeof(data));
  669. /* Position cursor at the matching key */
  670. rc = cursor->c_get(cursor, &key, &data, DB_SET);
  671. if (rc) {
  672. fprintf(stderr, "Failed to position cursor at the key: %s: %s "
  673. "(%d)\n", (char *)key.data, db_strerror(rc), rc);
  674. goto bail;
  675. }
  676. elem = (rdn_elem *)data.data;
  677. _entryrdn_dump_rdn_elem(keybuf, elem, indent);
  678. display_entryrdn_parent(db, id_stored_to_internal(elem->rdn_elem_id),
  679. elem->rdn_elem_nrdn_rdn, indent);
  680. display_entryrdn_children(db, id_stored_to_internal(elem->rdn_elem_id),
  681. elem->rdn_elem_nrdn_rdn, indent);
  682. bail:
  683. free(keybuf);
  684. cursor->c_close(cursor);
  685. return;
  686. }
  687. static void
  688. display_entryrdn_parent(DB *db, ID id, const char *nrdn, int indent)
  689. {
  690. DBC *cursor = NULL;
  691. DBT key, data;
  692. char *keybuf = NULL;
  693. int rc = 0;
  694. rdn_elem *elem;
  695. char buffer[1024];
  696. rc = db->cursor(db, NULL, &cursor, 0);
  697. if (rc) {
  698. printf("Can't create db cursor: %s\n", db_strerror(rc));
  699. exit(1);
  700. }
  701. snprintf(buffer, sizeof(buffer), "P%d", id);
  702. keybuf = strdup(buffer);
  703. key.data = keybuf;
  704. key.size = key.ulen = strlen(keybuf) + 1;
  705. key.flags = DB_DBT_USERMEM;
  706. memset(&data, 0, sizeof(data));
  707. /* Position cursor at the matching key */
  708. rc = cursor->c_get(cursor, &key, &data, DB_SET);
  709. if (rc) {
  710. fprintf(stderr, "Failed to position cursor at the key: %s: %s "
  711. "(%d)\n", (char *)key.data, db_strerror(rc), rc);
  712. goto bail;
  713. }
  714. elem = (rdn_elem *)data.data;
  715. _entryrdn_dump_rdn_elem(keybuf, elem, indent);
  716. bail:
  717. free(keybuf);
  718. cursor->c_close(cursor);
  719. return;
  720. }
  721. static void
  722. display_entryrdn_children(DB *db, ID id, const char *nrdn, int indent)
  723. {
  724. DBC *cursor = NULL;
  725. DBT key, data;
  726. char *keybuf = NULL;
  727. int rc = 0;
  728. rdn_elem *elem = NULL;;
  729. char buffer[1024];
  730. rc = db->cursor(db, NULL, &cursor, 0);
  731. if (rc) {
  732. printf("Can't create db cursor: %s\n", db_strerror(rc));
  733. exit(1);
  734. }
  735. indent += 2;
  736. snprintf(buffer, sizeof(buffer), "C%d", id);
  737. keybuf = strdup(buffer);
  738. key.data = keybuf;
  739. key.size = key.ulen = strlen(keybuf) + 1;
  740. key.flags = DB_DBT_USERMEM;
  741. memset(&data, 0, sizeof(data));
  742. data.ulen = sizeof(buffer);
  743. data.size = sizeof(buffer);
  744. data.data = buffer;
  745. data.flags = DB_DBT_USERMEM;
  746. /* Position cursor at the matching key */
  747. rc = cursor->c_get(cursor, &key, &data, DB_SET);
  748. if (rc) {
  749. if (DB_BUFFER_SMALL == rc) {
  750. fprintf(stderr, "Entryrdn index is corrupt; "
  751. "data item for key %s is too large for our "
  752. "buffer (need=%d actual=%d)\n",
  753. (char *)key.data, data.size, data.ulen);
  754. } else if (rc != DB_NOTFOUND) {
  755. fprintf(stderr, "Failed to position cursor at the key: %s: %s "
  756. "(%d)\n", (char *)key.data, db_strerror(rc), rc);
  757. }
  758. goto bail;
  759. }
  760. /* Iterate over the duplicates */
  761. for (;;) {
  762. elem = (rdn_elem *)data.data;
  763. _entryrdn_dump_rdn_elem(keybuf, elem, indent);
  764. display_entryrdn_self(db, id_stored_to_internal(elem->rdn_elem_id),
  765. elem->rdn_elem_nrdn_rdn, indent);
  766. rc = cursor->c_get(cursor, &key, &data, DB_NEXT_DUP);
  767. if (rc) {
  768. break;
  769. }
  770. }
  771. if (rc) {
  772. if (DB_BUFFER_SMALL == rc) {
  773. fprintf(stderr, "Entryrdn index is corrupt; "
  774. "data item for key %s is too large for our "
  775. "buffer (need=%d actual=%d)\n",
  776. (char *)key.data, data.size, data.ulen);
  777. } else if (rc != DB_NOTFOUND) {
  778. fprintf(stderr, "Failed to position cursor at the key: %s: %s "
  779. "(%d)\n", (char *)key.data, db_strerror(rc), rc);
  780. }
  781. }
  782. bail:
  783. free(keybuf);
  784. cursor->c_close(cursor);
  785. return;
  786. }
  787. static void
  788. display_entryrdn_item(DB *db, DBC *cursor, DBT *key)
  789. {
  790. rdn_elem *elem = NULL;
  791. int indent = 2;
  792. DBT data;
  793. int rc;
  794. PRUint32 flags = 0;
  795. char buffer[RDN_BULK_FETCH_BUFFER_SIZE];
  796. DBT dataret;
  797. int find_key_flag = 0;
  798. /* Setting the bulk fetch buffer */
  799. memset(&data, 0, sizeof(data));
  800. data.ulen = sizeof(buffer);
  801. data.size = sizeof(buffer);
  802. data.data = buffer;
  803. data.flags = DB_DBT_USERMEM;
  804. if (key->data) { /* key is given */
  805. /* Position cursor at the matching key */
  806. flags = DB_SET|DB_MULTIPLE;
  807. find_key_flag = 1;
  808. } else { /* key is not given; scan all */
  809. flags = DB_FIRST|DB_MULTIPLE;
  810. }
  811. do {
  812. /* Position cursor at the matching key */
  813. rc = cursor->c_get(cursor, key, &data, flags);
  814. if (rc) {
  815. if (DB_BUFFER_SMALL == rc) {
  816. fprintf(stderr, "Entryrdn index is corrupt; "
  817. "data item for key %s is too large for our "
  818. "buffer (need=%d actual=%d)\n",
  819. (char *)key->data, data.size, data.ulen);
  820. } else {
  821. if (rc != DB_NOTFOUND) {
  822. fprintf(stderr, "Failed to position cursor "
  823. "at the key: %s: %s (%d)\n",
  824. (char *)key->data, db_strerror(rc), rc);
  825. }
  826. }
  827. goto bail;
  828. }
  829. /* Iterate over the duplicates */
  830. for (;;) {
  831. void *ptr;
  832. DB_MULTIPLE_INIT(ptr, &data);
  833. for (;;) {
  834. memset(&dataret, 0, sizeof(dataret));
  835. DB_MULTIPLE_NEXT(ptr, &data, dataret.data, dataret.size);
  836. if (dataret.data == NULL)
  837. break;
  838. if (ptr == NULL)
  839. break;
  840. elem = (rdn_elem *)dataret.data;
  841. _entryrdn_dump_rdn_elem((char *)key->data, elem, indent);
  842. display_entryrdn_children(db, id_stored_to_internal(elem->rdn_elem_id),
  843. elem->rdn_elem_nrdn_rdn, indent);
  844. }
  845. rc = cursor->c_get(cursor, key, &data, DB_NEXT_DUP|DB_MULTIPLE);
  846. if (rc) {
  847. break;
  848. }
  849. }
  850. /* When it comes here, rc is not 0. */
  851. if (DB_BUFFER_SMALL == rc) {
  852. fprintf(stderr, "Entryrdn index is corrupt; "
  853. "data item for key %s is too large for our "
  854. "buffer (need=%d actual=%d)\n",
  855. (char *)key->data, data.size, data.ulen);
  856. goto bail;
  857. } else if (rc == DB_NOTFOUND) {
  858. if (find_key_flag) { /* key is given */
  859. goto bail; /* done */
  860. } else { /* otherwise, continue scanning. */
  861. rc = 0;
  862. }
  863. } else {
  864. fprintf(stderr, "Failed to position cursor at the key: %s: %s "
  865. "(%d)\n", (char *)key->data, db_strerror(rc), rc);
  866. goto bail;
  867. }
  868. flags = DB_NEXT|DB_MULTIPLE;
  869. } while (0 == rc);
  870. bail:
  871. return;
  872. }
  873. static int
  874. is_changelog(char *filename)
  875. {
  876. char *ptr = NULL;
  877. int dashes = 0;
  878. int underscore = 0;
  879. if (NULL == (ptr = strrchr(filename, '/'))) {
  880. if (NULL == (ptr = strrchr(filename, '\\'))) {
  881. ptr = filename;
  882. } else {
  883. ptr++;
  884. }
  885. } else {
  886. ptr++;
  887. }
  888. for (; ptr && *ptr; ptr++) {
  889. if ('.' == *ptr) {
  890. if (0 == strncmp(ptr, ".db", 3)) {
  891. if (3 == dashes && 1 == underscore) {
  892. return 1;
  893. } else {
  894. return 0;
  895. }
  896. }
  897. } else if ('-' == *ptr) {
  898. if (underscore > 0) {
  899. return 0;
  900. }
  901. dashes++;
  902. } else if ('_' == *ptr) {
  903. if (dashes < 3) {
  904. return 0;
  905. }
  906. underscore++;
  907. } else if (!isxdigit(*ptr)) {
  908. return 0;
  909. }
  910. }
  911. return 0;
  912. }
  913. static void usage(char *argv0)
  914. {
  915. char *copy = strdup(argv0);
  916. char *p0 = NULL, *p1 = NULL;
  917. if (NULL != copy) {
  918. /* the full path is not needed in the usages */
  919. p0 = strrchr(argv0, '/');
  920. if (NULL != p0) {
  921. *p0 = '\0';
  922. p0++;
  923. } else {
  924. p0 = argv0;
  925. }
  926. p1 = strrchr(p0, '-'); /* get rid of -bin from the usage */
  927. if (NULL != p1) {
  928. *p1 = '\0';
  929. }
  930. }
  931. if (NULL == p0) {
  932. p0 = argv0;
  933. }
  934. printf("\n%s - scan a db file and dump the contents\n", p0);
  935. printf(" common options:\n");
  936. printf(" -f <filename> specify db file\n");
  937. printf(" -R dump as raw data\n");
  938. printf(" -t <size> entry truncate size (bytes)\n");
  939. printf(" entry file options:\n");
  940. printf(" -K <entry_id> lookup only a specific entry id\n");
  941. printf(" index file options:\n");
  942. printf(" -k <key> lookup only a specific key\n");
  943. printf(" -l <size> max length of dumped id list\n");
  944. printf(" (default %d; 40 bytes <= size <= 1048576 bytes)\n",
  945. MAX_BUFFER);
  946. printf(" -G <n> only display index entries with more than <n> ids\n");
  947. printf(" -n display ID list lengths\n");
  948. printf(" -r display the conents of ID list\n");
  949. printf(" -s Summary of index counts\n");
  950. printf(" sample usages:\n");
  951. printf(" # dump the entry file\n");
  952. printf(" %s -f id2entry.db\n", p0);
  953. printf(" # display index keys in cn.db4\n");
  954. printf(" %s -f cn.db4\n", p0);
  955. printf(" # display index keys and the count of entries having the key in mail.db4\n");
  956. printf(" %s -r -f mail.db4\n", p0);
  957. printf(" # display index keys and the IDs having more than 20 IDs in sn.db4\n");
  958. printf(" %s -r -G 20 -f sn.db4\n", p0);
  959. printf(" # display summary of objectclass.db4\n");
  960. printf(" %s -f objectclass.db4\n", p0);
  961. printf("\n");
  962. exit(1);
  963. }
  964. int main(int argc, char **argv)
  965. {
  966. DB_ENV *env = NULL;
  967. DB *db = NULL;
  968. DBC *cursor = NULL;
  969. char *filename = NULL;
  970. DBT key = {0}, data = {0};
  971. int ret;
  972. char *find_key = NULL;
  973. PRUint32 entry_id = 0xffffffff;
  974. int c;
  975. key.flags = DB_DBT_REALLOC;
  976. data.flags = DB_DBT_REALLOC;
  977. while ((c = getopt(argc, argv, "f:Rl:nG:srk:K:hvt:")) != EOF) {
  978. switch (c) {
  979. case 'f':
  980. filename = optarg;
  981. break;
  982. case 'R':
  983. display_mode |= RAWDATA;
  984. break;
  985. case 'l':
  986. {
  987. PRUint32 tmpmaxbufsz = atoi(optarg);
  988. if (tmpmaxbufsz > ONEMEG) {
  989. tmpmaxbufsz = ONEMEG;
  990. printf("WARNING: max length of dumped id list too long, "
  991. "reduced to %d\n", tmpmaxbufsz);
  992. } else if (tmpmaxbufsz < MIN_BUFFER * 2) {
  993. tmpmaxbufsz = MIN_BUFFER * 2;
  994. printf("WARNING: max length of dumped id list too short, "
  995. "increased to %d\n", tmpmaxbufsz);
  996. }
  997. MAX_BUFFER = tmpmaxbufsz;
  998. break;
  999. }
  1000. case 'n':
  1001. display_mode |= SHOWCOUNT;
  1002. break;
  1003. case 'G':
  1004. min_display = atoi(optarg)+1;
  1005. break;
  1006. case 'r':
  1007. display_mode |= SHOWDATA;
  1008. break;
  1009. case 's':
  1010. display_mode |= SHOWSUMMARY;
  1011. break;
  1012. case 'k':
  1013. find_key = optarg;
  1014. break;
  1015. case 'K':
  1016. id_internal_to_stored((ID)atoi(optarg), (char *)&entry_id);
  1017. break;
  1018. case 't':
  1019. truncatesiz = atoi(optarg);
  1020. break;
  1021. case 'h':
  1022. default:
  1023. usage(argv[0]);
  1024. }
  1025. }
  1026. if(filename == NULL) {
  1027. usage(argv[0]);
  1028. }
  1029. if (NULL != strstr(filename, "id2entry.db")) {
  1030. file_type |= ENTRYTYPE;
  1031. } else if (is_changelog(filename)) {
  1032. file_type |= CHANGELOGTYPE;
  1033. } else {
  1034. file_type |= INDEXTYPE;
  1035. if (NULL != strstr(filename, "vlv#")) {
  1036. file_type |= VLVINDEXTYPE;
  1037. } else if (NULL != strstr(filename, "entryrdn.db")) {
  1038. file_type |= ENTRYRDNINDEXTYPE;
  1039. }
  1040. }
  1041. ret = db_env_create(&env, 0);
  1042. if (ret != 0) {
  1043. printf("Can't create dbenv: %s\n", db_strerror(ret));
  1044. exit(1);
  1045. }
  1046. ret = env->open(env, NULL, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0);
  1047. if (ret != 0) {
  1048. printf("Can't open dbenv: %s\n", db_strerror(ret));
  1049. exit(1);
  1050. }
  1051. ret = db_create(&db, env, 0);
  1052. if (ret != 0) {
  1053. printf("Can't create db handle: %d\n", ret);
  1054. exit(1);
  1055. }
  1056. ret = db->open(db, NULL, filename, NULL, DB_UNKNOWN, DB_RDONLY, 0);
  1057. if (ret != 0) {
  1058. printf("Can't open db file '%s': %s\n", filename, db_strerror(ret));
  1059. exit(1);
  1060. }
  1061. /* cursor through the db */
  1062. ret = db->cursor(db, NULL, &cursor, 0);
  1063. if (ret != 0) {
  1064. printf("Can't create db cursor: %s\n", db_strerror(ret));
  1065. exit(1);
  1066. }
  1067. ret = cursor->c_get(cursor, &key, &data, DB_FIRST);
  1068. if (ret == DB_NOTFOUND) {
  1069. printf("Empty database!\n");
  1070. exit(0);
  1071. }
  1072. if (ret != 0) {
  1073. printf("Can't get first cursor: %s\n", db_strerror(ret));
  1074. exit(1);
  1075. }
  1076. if (find_key) {
  1077. key.size = strlen(find_key)+1;
  1078. key.data = find_key;
  1079. ret = db->get(db, NULL, &key, &data, 0);
  1080. if (ret != 0) {
  1081. /* could be a key that doesn't have the trailing null? */
  1082. key.size--;
  1083. ret = db->get(db, NULL, &key, &data, 0);
  1084. if (ret != 0) {
  1085. printf("Can't find key '%s'\n", find_key);
  1086. exit(1);
  1087. }
  1088. }
  1089. if (file_type & ENTRYRDNINDEXTYPE) {
  1090. display_entryrdn_item(db, cursor, &key);
  1091. } else {
  1092. ret = cursor->c_get(cursor, &key, &data, DB_SET);
  1093. if (ret != 0) {
  1094. printf("Can't set cursor to returned item: %s\n",
  1095. db_strerror(ret));
  1096. exit(1);
  1097. }
  1098. do {
  1099. display_item(cursor, &key, &data);
  1100. ret = cursor->c_get(cursor, &key, &data, DB_NEXT_DUP);
  1101. } while (0 == ret);
  1102. key.size = 0;
  1103. key.data = NULL;
  1104. }
  1105. } else if (entry_id != 0xffffffff) {
  1106. key.size = sizeof(entry_id);
  1107. key.data = &entry_id;
  1108. ret = db->get(db, NULL, &key, &data, 0);
  1109. if (ret != 0) {
  1110. printf("Can't set cursor to returned item: %s\n",
  1111. db_strerror(ret));
  1112. exit(1);
  1113. }
  1114. display_item(cursor, &key, &data);
  1115. key.size = 0;
  1116. key.data = NULL;
  1117. } else {
  1118. if (file_type & ENTRYRDNINDEXTYPE) {
  1119. key.data = NULL;
  1120. display_entryrdn_item(db, cursor, &key);
  1121. } else {
  1122. while (ret == 0) {
  1123. /* display */
  1124. display_item(cursor, &key, &data);
  1125. ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
  1126. if ((ret != 0) && (ret != DB_NOTFOUND)) {
  1127. printf("Bizarre error: %s\n", db_strerror(ret));
  1128. exit(1);
  1129. }
  1130. }
  1131. }
  1132. }
  1133. ret = cursor->c_close(cursor);
  1134. if (ret != 0) {
  1135. printf("Can't close the cursor (?!): %s\n", db_strerror(ret));
  1136. exit(1);
  1137. }
  1138. ret = db->close(db, 0);
  1139. if (ret != 0) {
  1140. printf("Unable to close db file: %s\n", db_strerror(ret));
  1141. exit(1);
  1142. }
  1143. if ( display_mode & SHOWSUMMARY) {
  1144. if ( allids_cnt > 0 ) {
  1145. printf("Index keys that reached ALLIDs threshold: %ld\n", allids_cnt);
  1146. }
  1147. if ( pres_cnt > 0 ) {
  1148. printf("Presence index keys: %ld\n", pres_cnt);
  1149. }
  1150. if ( eq_cnt > 0 ) {
  1151. printf("Equality index keys: %ld\n", eq_cnt);
  1152. }
  1153. if ( app_cnt > 0 ) {
  1154. printf("Approximate index keys: %ld\n", app_cnt);
  1155. }
  1156. if ( sub_cnt > 0 ) {
  1157. printf("Substring index keys: %ld\n", sub_cnt);
  1158. }
  1159. if ( match_cnt > 0 ) {
  1160. printf("Match index keys: %ld\n", match_cnt);
  1161. }
  1162. if ( ind_cnt > 0 ) {
  1163. printf("Indirect index keys: %ld\n", ind_cnt);
  1164. }
  1165. if ( other_cnt > 0 ) {
  1166. printf("This file contains %ld number of unknown type ( possible corruption)\n",other_cnt);
  1167. }
  1168. }
  1169. ret = env->close(env, 0);
  1170. if (ret != 0) {
  1171. printf("Unable to shutdown libdb: %s\n", db_strerror(ret));
  1172. exit(1);
  1173. }
  1174. return 0;
  1175. }