1 // Copyright 1997-2024 The OpenLDAP Foundation, All Rights Reserved. 2 // COPYING RESTRICTIONS APPLY, see COPYRIGHT file 3 4 // (c) Copyright 1999-2001 TimesTen Performance Software. All rights reserved. 5 6 //// Note: This file was contributed by Sam Drake of TimesTen Performance 7 //// Software for use and redistribution as an integral part of 8 //// OpenLDAP Software. -Kdz 9 10 #include <stdlib.h> 11 12 #include <TTConnectionPool.h> 13 #include <TTConnection.h> 14 #include <TTCmd.h> 15 #include <TTXla.h> 16 17 #include <signal.h> 18 19 TTConnectionPool pool; 20 TTXlaConnection conn; 21 TTConnection conn2; 22 TTCmd assignDn_ru; 23 TTCmd getNullDNs; 24 25 //---------------------------------------------------------------------- 26 // This class contains all the logic to be implemented whenever 27 // the SCOTT.MYDATA table is changed. This is the table that is 28 // created by "sample.cpp", one of the other TTClasses demos. 29 // That application should be executed before this one in order to 30 // create and populate the table. 31 //---------------------------------------------------------------------- 32 33 class LDAPEntriesHandler: public TTXlaTableHandler { 34 private: 35 // Definition of the columns in the table 36 int Id; 37 int Dn; 38 int Oc_map_id; 39 int Parent; 40 int Keyval; 41 int Dn_ru; 42 43 protected: 44 45 public: 46 LDAPEntriesHandler(TTXlaConnection& conn, const char* ownerP, const char* nameP); 47 ~LDAPEntriesHandler(); 48 49 virtual void HandleDelete(ttXlaUpdateDesc_t*); 50 virtual void HandleInsert(ttXlaUpdateDesc_t*); 51 virtual void HandleUpdate(ttXlaUpdateDesc_t*); 52 53 static void ReverseAndUpper(char* dnP, int id, bool commit=true); 54 55 }; 56 57 LDAPEntriesHandler::LDAPEntriesHandler(TTXlaConnection& conn, 58 const char* ownerP, const char* nameP) : 59 TTXlaTableHandler(conn, ownerP, nameP) 60 { 61 Id = Dn = Oc_map_id = Parent = Keyval = Dn_ru = -1; 62 63 // We are looking for several particular named columns. We need to get 64 // the ordinal position of the columns by name for later use. 65 66 Id = tbl.getColNumber("ID"); 67 if (Id < 0) { 68 cerr << "target table has no 'ID' column" << endl; 69 exit(1); 70 } 71 Dn = tbl.getColNumber("DN"); 72 if (Dn < 0) { 73 cerr << "target table has no 'DN' column" << endl; 74 exit(1); 75 } 76 Oc_map_id = tbl.getColNumber("OC_MAP_ID"); 77 if (Oc_map_id < 0) { 78 cerr << "target table has no 'OC_MAP_ID' column" << endl; 79 exit(1); 80 } 81 Parent = tbl.getColNumber("PARENT"); 82 if (Parent < 0) { 83 cerr << "target table has no 'PARENT' column" << endl; 84 exit(1); 85 } 86 Keyval = tbl.getColNumber("KEYVAL"); 87 if (Keyval < 0) { 88 cerr << "target table has no 'KEYVAL' column" << endl; 89 exit(1); 90 } 91 Dn_ru = tbl.getColNumber("DN_RU"); 92 if (Dn_ru < 0) { 93 cerr << "target table has no 'DN_RU' column" << endl; 94 exit(1); 95 } 96 97 } 98 99 LDAPEntriesHandler::~LDAPEntriesHandler() 100 { 101 102 } 103 104 void LDAPEntriesHandler::ReverseAndUpper(char* dnP, int id, bool commit) 105 { 106 TTStatus stat; 107 char dn_rn[512]; 108 int i; 109 int j; 110 111 // Reverse and upper case the given DN 112 113 for ((j=0, i = strlen(dnP)-1); i > -1; (j++, i--)) { 114 dn_rn[j] = toupper(*(dnP+i)); 115 } 116 dn_rn[j] = '\0'; 117 118 119 // Update the database 120 121 try { 122 assignDn_ru.setParam(1, (char*) &dn_rn[0]); 123 assignDn_ru.setParam(2, id); 124 assignDn_ru.Execute(stat); 125 } 126 catch (TTStatus stat) { 127 cerr << "Error updating id " << id << " ('" << dnP << "' to '" 128 << dn_rn << "'): " << stat; 129 exit(1); 130 } 131 132 // Commit the transaction 133 134 if (commit) { 135 try { 136 conn2.Commit(stat); 137 } 138 catch (TTStatus stat) { 139 cerr << "Error committing update: " << stat; 140 exit(1); 141 } 142 } 143 144 } 145 146 147 148 void LDAPEntriesHandler::HandleInsert(ttXlaUpdateDesc_t* p) 149 { 150 char* dnP; 151 int id; 152 153 row.Get(Dn, &dnP); 154 cerr << "DN '" << dnP << "': Inserted "; 155 row.Get(Id, &id); 156 157 ReverseAndUpper(dnP, id); 158 159 } 160 161 void LDAPEntriesHandler::HandleUpdate(ttXlaUpdateDesc_t* p) 162 { 163 char* newDnP; 164 char* oldDnP; 165 char oDn[512]; 166 int id; 167 168 // row is 'old'; row2 is 'new' 169 row.Get(Dn, &oldDnP); 170 strcpy(oDn, oldDnP); 171 row.Get(Id, &id); 172 row2.Get(Dn, &newDnP); 173 174 cerr << "old DN '" << oDn << "' / new DN '" << newDnP << "' : Updated "; 175 176 if (strcmp(oDn, newDnP) != 0) { 177 // The DN field changed, update it 178 cerr << "(new DN: '" << newDnP << "')"; 179 ReverseAndUpper(newDnP, id); 180 } 181 else { 182 // The DN field did NOT change, leave it alone 183 } 184 185 cerr << endl; 186 187 } 188 189 void LDAPEntriesHandler::HandleDelete(ttXlaUpdateDesc_t* p) 190 { 191 char* dnP; 192 193 row.Get(Dn, &dnP); 194 cerr << "DN '" << dnP << "': Deleted "; 195 } 196 197 198 199 200 //---------------------------------------------------------------------- 201 202 int pleaseStop = 0; 203 204 extern "C" { 205 void 206 onintr(int sig) 207 { 208 pleaseStop = 1; 209 cerr << "Stopping...\n"; 210 } 211 }; 212 213 //---------------------------------------------------------------------- 214 215 int 216 main(int argc, char* argv[]) 217 { 218 219 char* ownerP; 220 221 TTXlaTableList list(&conn); // List of tables to monitor 222 223 // Handlers, one for each table we want to monitor 224 225 LDAPEntriesHandler* sampP = NULL; 226 227 // Misc stuff 228 229 TTStatus stat; 230 231 ttXlaUpdateDesc_t ** arry; 232 233 int records; 234 235 SQLUBIGINT oldsize; 236 int j; 237 238 if (argc < 2) { 239 cerr << "syntax: " << argv[0] << " <username>" << endl; 240 exit(3); 241 } 242 243 ownerP = argv[1]; 244 245 signal(SIGINT, onintr); /* signal for CTRL-C */ 246 #ifdef _WIN32 247 signal(SIGBREAK, onintr); /* signal for CTRL-BREAK */ 248 #endif 249 250 // Before we do anything related to XLA, first we connect 251 // to the database. This is the connection we will use 252 // to perform non-XLA operations on the tables. 253 254 try { 255 cerr << "Connecting..." << endl; 256 257 conn2.Connect("DSN=ldap_tt", stat); 258 } 259 catch (TTStatus stat) { 260 cerr << "Error connecting to TimesTen: " << stat; 261 exit(1); 262 } 263 264 try { 265 assignDn_ru.Prepare(&conn2, 266 "update ldap_entries set dn_ru=? where id=?", 267 "", stat); 268 getNullDNs.Prepare(&conn2, 269 "select dn, id from ldap_entries " 270 "where dn_ru is null " 271 "for update", 272 "", stat); 273 conn2.Commit(stat); 274 } 275 catch (TTStatus stat) { 276 cerr << "Error preparing update: " << stat; 277 exit(1); 278 } 279 280 // If there are any entries with a NULL reversed/upper cased DN, 281 // fix them now. 282 283 try { 284 cerr << "Fixing NULL reversed DNs" << endl; 285 getNullDNs.Execute(stat); 286 for (int k = 0;; k++) { 287 getNullDNs.FetchNext(stat); 288 if (stat.rc == SQL_NO_DATA_FOUND) break; 289 char* dnP; 290 int id; 291 getNullDNs.getColumn(1, &dnP); 292 getNullDNs.getColumn(2, &id); 293 // cerr << "Id " << id << ", Dn '" << dnP << "'" << endl; 294 LDAPEntriesHandler::ReverseAndUpper(dnP, id, false); 295 if (k % 1000 == 0) 296 cerr << "."; 297 } 298 getNullDNs.Close(stat); 299 conn2.Commit(stat); 300 } 301 catch (TTStatus stat) { 302 cerr << "Error updating NULL rows: " << stat; 303 exit(1); 304 } 305 306 307 // Go ahead and start up the change monitoring application 308 309 cerr << "Starting change monitoring..." << endl; 310 try { 311 conn.Connect("DSN=ldap_tt", stat); 312 } 313 catch (TTStatus stat) { 314 cerr << "Error connecting to TimesTen: " << stat; 315 exit(1); 316 } 317 318 /* set and configure size of buffer */ 319 conn.setXlaBufferSize((SQLUBIGINT) 1000000, &oldsize, stat); 320 if (stat.rc) { 321 cerr << "Error setting buffer size " << stat << endl; 322 exit(1); 323 } 324 325 // Make a handler to process changes to the MYDATA table and 326 // add the handler to the list of all handlers 327 328 sampP = new LDAPEntriesHandler(conn, ownerP, "ldap_entries"); 329 if (!sampP) { 330 cerr << "Could not create LDAPEntriesHandler" << endl; 331 exit(3); 332 } 333 list.add(sampP); 334 335 // Enable transaction logging for the table we're interested in 336 337 sampP->EnableTracking(stat); 338 339 // Get updates. Dispatch them to the appropriate handler. 340 // This loop will handle updates to all the tables. 341 342 while (pleaseStop == 0) { 343 conn.fetchUpdates(&arry, 1000, &records, stat); 344 if (stat.rc) { 345 cerr << "Error fetching updates" << stat << endl; 346 exit(1); 347 } 348 349 // Interpret the updates 350 351 for(j=0;j < records;j++){ 352 ttXlaUpdateDesc_t *p; 353 354 p = arry[j]; 355 356 list.HandleChange(p, stat); 357 358 } // end for each record fetched 359 360 if (records) { 361 cerr << "Processed " << records << " records\n"; 362 } 363 364 if (records == 0) { 365 #ifdef _WIN32 366 Sleep(250); 367 #else 368 struct timeval t; 369 t.tv_sec = 0; 370 t.tv_usec = 250000; // .25 seconds 371 select(0, NULL, NULL, NULL, &t); 372 #endif 373 } 374 } // end while pleasestop == 0 375 376 377 // When we get to here, the program is exiting. 378 379 list.del(sampP); // Take the table out of the list 380 delete sampP; 381 382 conn.setXlaBufferSize(oldsize, NULL, stat); 383 384 return 0; 385 386 } 387 388