retrocl_po.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. #include "retrocl.h"
  42. static int
  43. entry2reple( Slapi_Entry *e, Slapi_Entry *oe );
  44. static int
  45. mods2reple( Slapi_Entry *e, LDAPMod **ldm );
  46. static int
  47. modrdn2reple( Slapi_Entry *e, const char *newrdn, int deloldrdn,
  48. LDAPMod **ldm, const char *newsup );
  49. /******************************/
  50. const char *attr_changenumber = "changenumber";
  51. const char *attr_targetdn = "targetdn";
  52. const char *attr_changetype = "changetype";
  53. const char *attr_newrdn = "newrdn";
  54. const char *attr_deleteoldrdn = "deleteoldrdn";
  55. const char *attr_changes = "changes";
  56. const char *attr_newsuperior = "newsuperior";
  57. const char *attr_changetime = "changetime";
  58. const char *attr_objectclass = "objectclass";
  59. /*
  60. * Function: make_changes_string
  61. *
  62. * Returns:
  63. *
  64. * Arguments:
  65. *
  66. * Description:
  67. * loop through the LDAPMod struct and construct the changes attribute/
  68. *
  69. */
  70. static lenstr *make_changes_string(LDAPMod **ldm, const char **includeattrs)
  71. {
  72. lenstr *l;
  73. int i, j, len;
  74. int skip;
  75. l = lenstr_new();
  76. for ( i = 0; ldm[ i ] != NULL; i++ ) {
  77. /* If a list of explicit attributes was given, only add those */
  78. if ( NULL != includeattrs ) {
  79. skip = 1;
  80. for ( j = 0; includeattrs[ j ] != NULL; j++ ) {
  81. if ( strcasecmp( includeattrs[ j ], ldm[ i ]->mod_type ) == 0 ) {
  82. skip = 0;
  83. break;
  84. }
  85. }
  86. if ( skip ) {
  87. continue;
  88. }
  89. }
  90. switch ( ldm[ i ]->mod_op & ~LDAP_MOD_BVALUES ) {
  91. case LDAP_MOD_ADD:
  92. addlenstr( l, "add: " );
  93. addlenstr( l, ldm[ i ]->mod_type );
  94. addlenstr( l, "\n" );
  95. break;
  96. case LDAP_MOD_DELETE:
  97. addlenstr( l, "delete: " );
  98. addlenstr( l, ldm[ i ]->mod_type );
  99. addlenstr( l, "\n" );
  100. break;
  101. case LDAP_MOD_REPLACE:
  102. addlenstr( l, "replace: " );
  103. addlenstr( l, ldm[ i ]->mod_type );
  104. addlenstr( l, "\n" );
  105. break;
  106. }
  107. for ( j = 0; ldm[ i ]->mod_bvalues != NULL &&
  108. ldm[ i ]->mod_bvalues[ j ] != NULL; j++ ) {
  109. char *buf = NULL;
  110. char *bufp = NULL;
  111. len = strlen( ldm[ i ]->mod_type );
  112. len = LDIF_SIZE_NEEDED( len,
  113. ldm[ i ]->mod_bvalues[ j ]->bv_len ) + 1;
  114. buf = slapi_ch_malloc( len );
  115. bufp = buf;
  116. slapi_ldif_put_type_and_value_with_options( &bufp, ldm[ i ]->mod_type,
  117. ldm[ i ]->mod_bvalues[ j ]->bv_val,
  118. ldm[ i ]->mod_bvalues[ j ]->bv_len, 0 );
  119. *bufp = '\0';
  120. addlenstr( l, buf );
  121. slapi_ch_free_string( &buf );
  122. }
  123. addlenstr( l, "-\n" );
  124. }
  125. return l;
  126. }
  127. /*
  128. * Function: write_replog_db
  129. * Arguments: be - backend to which this change is being applied
  130. * optype - type of LDAP operation being logged
  131. * dn - distinguished name of entry being changed
  132. * log_m - pointer to the actual change operation on a modify
  133. * flag - only used by modrdn operations - value of deleteoldrdn
  134. * curtime - the current time
  135. * Returns: nothing
  136. * Description: Given a change, construct an entry which is to be added to the
  137. * changelog database.
  138. */
  139. static void
  140. write_replog_db(
  141. int optype,
  142. char *dn,
  143. LDAPMod **log_m,
  144. int flag,
  145. time_t curtime,
  146. Slapi_Entry *log_e,
  147. const char *newrdn,
  148. LDAPMod **modrdn_mods,
  149. const char *newsuperior
  150. )
  151. {
  152. char *edn;
  153. struct berval *vals[ 2 ];
  154. struct berval val;
  155. Slapi_Entry *e;
  156. char chnobuf[ 20 ];
  157. int err;
  158. Slapi_PBlock *pb = NULL;
  159. changeNumber changenum;
  160. PR_Lock(retrocl_internal_lock);
  161. changenum = retrocl_assign_changenumber();
  162. PR_ASSERT( changenum > 0UL );
  163. slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
  164. "write_replog_db: write change record %lu for dn: \"%s\"\n",
  165. changenum, ( dn == NULL ) ? "NULL" : dn );
  166. /* Construct the dn of this change record */
  167. edn = slapi_ch_smprintf( "%s=%lu,%s", attr_changenumber, changenum, RETROCL_CHANGELOG_DN);
  168. /*
  169. * Create the entry struct, and fill in fields common to all types
  170. * of change records.
  171. */
  172. vals[ 0 ] = &val;
  173. vals[ 1 ] = NULL;
  174. e = slapi_entry_alloc();
  175. slapi_entry_set_dn( e, slapi_ch_strdup( edn ));
  176. /* Set the objectclass attribute */
  177. val.bv_val = "top";
  178. val.bv_len = 3;
  179. slapi_entry_add_values( e, "objectclass", vals );
  180. val.bv_val = "changelogentry";
  181. val.bv_len = 14;
  182. slapi_entry_add_values( e, "objectclass", vals );
  183. /* Set the changeNumber attribute */
  184. sprintf( chnobuf, "%lu", changenum );
  185. val.bv_val = chnobuf;
  186. val.bv_len = strlen( chnobuf );
  187. slapi_entry_add_values( e, attr_changenumber, vals );
  188. /* Set the targetentrydn attribute */
  189. val.bv_val = dn;
  190. val.bv_len = strlen( dn );
  191. slapi_entry_add_values( e, attr_targetdn, vals );
  192. /* Set the changeTime attribute */
  193. val.bv_val = format_genTime (curtime);
  194. val.bv_len = strlen( val.bv_val );
  195. slapi_entry_add_values( e, attr_changetime, vals );
  196. slapi_ch_free( (void **)&val.bv_val );
  197. /*
  198. * Finish constructing the entry. How to do it depends on the type
  199. * of modification being logged.
  200. */
  201. err = 0;
  202. switch ( optype ) {
  203. case OP_ADD:
  204. if ( entry2reple( e, log_e ) != 0 ) {
  205. err = 1;
  206. }
  207. break;
  208. case OP_MODIFY:
  209. if ( mods2reple( e, log_m ) != 0 ) {
  210. err = 1;
  211. }
  212. break;
  213. case OP_MODRDN:
  214. if ( modrdn2reple( e, newrdn, flag, modrdn_mods, newsuperior ) != 0 ) {
  215. err = 1;
  216. }
  217. break;
  218. case OP_DELETE:
  219. /* Set the changetype attribute */
  220. val.bv_val = "delete";
  221. val.bv_len = 6;
  222. slapi_entry_add_values( e, attr_changetype, vals );
  223. break;
  224. default:
  225. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "replog: Unknown LDAP operation type "
  226. "%d.\n", optype );
  227. err = 1;
  228. }
  229. /* Call the repl backend to add this entry */
  230. if ( 0 == err ) {
  231. int rc;
  232. pb = slapi_pblock_new ();
  233. slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */,
  234. g_plg_identity[PLUGIN_RETROCL],
  235. 0 /* actions */ );
  236. slapi_add_internal_pb (pb);
  237. slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
  238. slapi_pblock_destroy(pb);
  239. if ( 0 != rc ) {
  240. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  241. "replog: an error occured while adding change "
  242. "number %lu, dn = %s: %s. \n",
  243. changenum, edn, ldap_err2string( rc ));
  244. retrocl_release_changenumber();
  245. } else {
  246. /* Tell the change numbering system this one's committed to disk */
  247. retrocl_commit_changenumber( );
  248. }
  249. } else {
  250. slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
  251. "An error occurred while constructing "
  252. "change record number %ld.\n", changenum );
  253. retrocl_release_changenumber();
  254. }
  255. PR_Unlock(retrocl_internal_lock);
  256. if ( NULL != edn ) {
  257. slapi_ch_free((void **) &edn);
  258. }
  259. }
  260. /*
  261. * Function: entry2reple
  262. * Arguments: e - a partially-constructed Slapi_Entry structure
  263. * oe - the original entry (an entry being added by a client).
  264. * Returns: 0 on success.
  265. * Description: Given an Slapi_Entry struct, construct a changelog entry which will be
  266. * added to the replication database. It is assumed that e points
  267. * to an entry obtained from slapi_entry_alloc().
  268. */
  269. static int
  270. entry2reple( Slapi_Entry *e, Slapi_Entry *oe )
  271. {
  272. char *p, *estr;
  273. struct berval *vals[ 2 ];
  274. struct berval val;
  275. int len;
  276. vals[ 0 ] = &val;
  277. vals[ 1 ] = NULL;
  278. /* Set the changetype attribute */
  279. val.bv_val = "add";
  280. val.bv_len = 3;
  281. slapi_entry_add_values( e, attr_changetype, vals );
  282. estr = slapi_entry2str( oe, &len );
  283. p = estr;
  284. /* Skip over the dn: line */
  285. while (( p = strchr( p, '\n' )) != NULL ) {
  286. p++;
  287. if ( !ldap_utf8isspace( p )) {
  288. break;
  289. }
  290. }
  291. val.bv_val = p;
  292. val.bv_len = len - ( p - estr ); /* length + terminating \0 */
  293. slapi_entry_add_values( e, attr_changes, vals );
  294. slapi_ch_free_string( &estr );
  295. return 0;
  296. }
  297. /*
  298. * Function: mods2reple
  299. * Arguments: e - a partially-constructed Slapi_Entry structure
  300. * ldm - an array of pointers to LDAPMod structures describing the
  301. * change applied.
  302. * Returns: 0 on success.
  303. * Description: Given a pointer to an LDAPMod struct and a dn, construct
  304. * a new entry which will be added to the replication database.
  305. * It is assumed that e points to an entry obtained from
  306. * slapi_entry_alloc().
  307. */
  308. static int
  309. mods2reple( Slapi_Entry *e, LDAPMod **ldm )
  310. {
  311. struct berval val;
  312. struct berval *vals[ 2 ];
  313. lenstr *l;
  314. vals[ 0 ] = &val;
  315. vals[ 1 ] = NULL;
  316. /* Set the changetype attribute */
  317. val.bv_val = "modify";
  318. val.bv_len = 6;
  319. slapi_entry_add_values( e, "changetype", vals );
  320. if (NULL != ldm) {
  321. l = make_changes_string( ldm, NULL );
  322. if ( NULL != l ) {
  323. val.bv_val = l->ls_buf;
  324. val.bv_len = l->ls_len + 1; /* string + terminating \0 */
  325. slapi_entry_add_values( e, attr_changes, vals );
  326. lenstr_free( &l );
  327. }
  328. }
  329. return 0;
  330. }
  331. /*
  332. * Function: modrdn2reple
  333. * Arguments: e - a partially-constructed Slapi_Entry structure
  334. * newrdn - the new relative distinguished name for the entry
  335. * deloldrdn - the "deleteoldrdn" flag provided by the client
  336. * ldm - any modifications applied as a side-effect of the modrdn
  337. * Returns: 0 on success
  338. * Description: Given a dn, a new rdn, and a deleteoldrdn flag, construct
  339. * a new entry which will be added to the replication database reflecting a
  340. * completed modrdn operation. The entry has the same form as above.
  341. * It is assumed that e points to an entry obtained from slapi_entry_alloc().
  342. */
  343. static int
  344. modrdn2reple(
  345. Slapi_Entry *e,
  346. const char *newrdn,
  347. int deloldrdn,
  348. LDAPMod **ldm,
  349. const char *newsuperior
  350. )
  351. {
  352. struct berval val;
  353. struct berval *vals[ 2 ];
  354. lenstr *l;
  355. static const char *lastmodattrs[] = {"modifiersname", "modifytimestamp",
  356. "creatorsname", "createtimestamp",
  357. NULL };
  358. vals[ 0 ] = &val;
  359. vals[ 1 ] = NULL;
  360. val.bv_val = "modrdn";
  361. val.bv_len = 6;
  362. slapi_entry_add_values( e, attr_changetype, vals );
  363. if (newrdn) {
  364. val.bv_val = (char *)newrdn; /* cast away const */
  365. val.bv_len = strlen( newrdn );
  366. slapi_entry_add_values( e, attr_newrdn, vals );
  367. }
  368. if ( deloldrdn == 0 ) {
  369. val.bv_val = "FALSE";
  370. val.bv_len = 5;
  371. } else {
  372. val.bv_val = "TRUE";
  373. val.bv_len = 4;
  374. }
  375. slapi_entry_add_values( e, attr_deleteoldrdn, vals );
  376. if (newsuperior) {
  377. val.bv_val = (char *)newsuperior; /* cast away const */
  378. val.bv_len = strlen(newsuperior);
  379. slapi_entry_add_values(e, attr_newsuperior,vals);
  380. }
  381. if (NULL != ldm) {
  382. l = make_changes_string( ldm, lastmodattrs );
  383. if ( NULL != l ) {
  384. val.bv_val = l->ls_buf;
  385. val.bv_len = l->ls_len + 1; /* string + terminating \0 */
  386. slapi_entry_add_values( e, attr_changes, vals );
  387. lenstr_free( &l );
  388. }
  389. }
  390. return 0;
  391. }
  392. /*
  393. * Function: retrocl_postob
  394. *
  395. * Returns: 0 on success
  396. *
  397. * Arguments: pblock, optype (add, del, modify etc)
  398. *
  399. * Description: called from retrocl.c op-specific plugins.
  400. *
  401. * Please be aware that operation threads may be scheduled out between their
  402. * being performed inside of the LDBM database and the changelog plugin
  403. * running. For example, suppose MA and MB are two modify operations on the
  404. * same entry. MA may be performed on the LDBM database, and then block
  405. * before the changelog runs. MB then runs against the LDBM database and then
  406. * is written to the changelog. MA starts running. In the changelog, MB will
  407. * appear to have been performed before MA, but in the LDBM database the
  408. * opposite will have occured.
  409. *
  410. *
  411. */
  412. int retrocl_postob (Slapi_PBlock *pb,int optype)
  413. {
  414. char *dn;
  415. LDAPMod **log_m = NULL;
  416. int flag = 0;
  417. Slapi_Entry *te = NULL;
  418. Slapi_Operation *op = NULL;
  419. LDAPMod **modrdn_mods = NULL;
  420. char *newrdn = NULL;
  421. char *newsuperior = NULL;
  422. Slapi_Backend *be = NULL;
  423. time_t curtime;
  424. int rc;
  425. /*
  426. * Check to see if the change was made to the replication backend db.
  427. * If so, don't try to log it to the db (otherwise, we'd get in a loop).
  428. */
  429. (void)slapi_pblock_get( pb, SLAPI_BACKEND, &be );
  430. if (slapi_be_logchanges(be) == 0) {
  431. LDAPDebug0Args(LDAP_DEBUG_TRACE,"not applying change if not logging\n");
  432. return 0;
  433. }
  434. if (retrocl_be_changelog == NULL || be == retrocl_be_changelog) {
  435. LDAPDebug0Args(LDAP_DEBUG_TRACE,"not applying change if no/cl be\n");
  436. return 0;
  437. }
  438. slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rc);
  439. if (rc != LDAP_SUCCESS) {
  440. LDAPDebug1Arg(LDAP_DEBUG_TRACE,"not applying change if op failed %d\n",rc);
  441. return 0;
  442. }
  443. if (slapi_op_abandoned(pb)) {
  444. LDAPDebug0Args(LDAP_DEBUG_PLUGIN,"not applying change if op abandoned\n");
  445. return 0;
  446. }
  447. curtime = current_time();
  448. (void)slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET_DN, &dn );
  449. /* change number could be retrieved from Operation extension stored in
  450. * the pblock, or else it needs to be made dynamically. */
  451. /* get the operation extension and retrieve the change number */
  452. slapi_pblock_get( pb, SLAPI_OPERATION, &op );
  453. if (op == NULL) {
  454. LDAPDebug0Args(LDAP_DEBUG_TRACE,"not applying change if no op\n");
  455. return 0;
  456. }
  457. if (operation_is_flag_set(op, OP_FLAG_TOMBSTONE_ENTRY)){
  458. LDAPDebug0Args(LDAP_DEBUG_TRACE,"not applying change for nsTombstone entries\n");
  459. return 0;
  460. }
  461. switch ( optype ) {
  462. case OP_MODIFY:
  463. (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &log_m );
  464. break;
  465. case OP_ADD:
  466. /*
  467. * For adds, we want the unnormalized dn, so we can preserve
  468. * spacing, case, when replicating it.
  469. */
  470. (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &te );
  471. if ( NULL != te ) {
  472. dn = slapi_entry_get_dn( te );
  473. }
  474. break;
  475. case OP_DELETE:
  476. break;
  477. case OP_MODRDN:
  478. (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
  479. (void)slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &flag );
  480. (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &modrdn_mods );
  481. (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperior);
  482. break;
  483. }
  484. /* check if we should log change to retro changelog, and
  485. * if so, do it here */
  486. write_replog_db( optype, dn, log_m, flag, curtime, te,
  487. newrdn, modrdn_mods, newsuperior );
  488. return 0;
  489. }