task.c 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998
  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. /*
  42. * directory online tasks (import, export, backup, restore)
  43. */
  44. #include "slap.h"
  45. /***********************************
  46. * Static Global Variables
  47. ***********************************/
  48. /* don't panic, this is only used when creating new tasks or removing old
  49. * ones...
  50. */
  51. static Slapi_Task *global_task_list = NULL;
  52. static PRLock *global_task_lock = NULL;
  53. static int shutting_down = 0;
  54. /***********************************
  55. * Private Defines
  56. ***********************************/
  57. #define TASK_BASE_DN "cn=tasks,cn=config"
  58. #define TASK_IMPORT_DN "cn=import,cn=tasks,cn=config"
  59. #define TASK_EXPORT_DN "cn=export,cn=tasks,cn=config"
  60. #define TASK_BACKUP_DN "cn=backup,cn=tasks,cn=config"
  61. #define TASK_RESTORE_DN "cn=restore,cn=tasks,cn=config"
  62. #define TASK_INDEX_DN "cn=index,cn=tasks,cn=config"
  63. #define TASK_UPGRADEDB_DN "cn=upgradedb,cn=tasks,cn=config"
  64. #define TASK_LOG_NAME "nsTaskLog"
  65. #define TASK_STATUS_NAME "nsTaskStatus"
  66. #define TASK_EXITCODE_NAME "nsTaskExitCode"
  67. #define TASK_PROGRESS_NAME "nsTaskCurrentItem"
  68. #define TASK_WORK_NAME "nsTaskTotalItems"
  69. #define DEFAULT_TTL "120" /* seconds */
  70. #define LOG_BUFFER 256
  71. /* if the cumul. log gets larger than this, it's truncated: */
  72. #define MAX_SCROLLBACK_BUFFER 8192
  73. #define NEXTMOD(_type, _val) do { \
  74. modlist[cur].mod_op = LDAP_MOD_REPLACE; \
  75. modlist[cur].mod_type = (_type); \
  76. modlist[cur].mod_values = (char **)slapi_ch_malloc(2*sizeof(char *)); \
  77. modlist[cur].mod_values[0] = (_val); \
  78. modlist[cur].mod_values[1] = NULL; \
  79. mod[cur] = &modlist[cur]; \
  80. cur++; \
  81. } while (0)
  82. /***********************************
  83. * Static Function Prototypes
  84. ***********************************/
  85. static Slapi_Task *new_task(const char *dn);
  86. static void destroy_task(time_t when, void *arg);
  87. static int task_modify(Slapi_PBlock *pb, Slapi_Entry *e,
  88. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
  89. static int task_deny(Slapi_PBlock *pb, Slapi_Entry *e,
  90. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
  91. static void task_generic_destructor(Slapi_Task *task);
  92. static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
  93. const char *default_val);
  94. static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn);
  95. static void modify_internal_entry(char *dn, LDAPMod **mods);
  96. /***********************************
  97. * Public Functions
  98. ***********************************/
  99. /*
  100. * slapi_new_task: create a new task, fill in DN, and setup modify callback
  101. * argument:
  102. * dn: task dn
  103. * result:
  104. * Success: Slapi_Task object
  105. * Failure: NULL
  106. */
  107. Slapi_Task *
  108. slapi_new_task(const char *dn)
  109. {
  110. return new_task(dn);
  111. }
  112. /* slapi_destroy_task: destroy a task
  113. * argument:
  114. * task: task to destroy
  115. * result:
  116. * none
  117. */
  118. void
  119. slapi_destroy_task(void *arg)
  120. {
  121. if (arg) {
  122. destroy_task(1, arg);
  123. }
  124. }
  125. /*
  126. * Sets the initial task state and updated status
  127. */
  128. void slapi_task_begin(Slapi_Task *task, int total_work)
  129. {
  130. if (task) {
  131. task->task_work = total_work;
  132. task->task_progress = 0;
  133. task->task_state = SLAPI_TASK_RUNNING;
  134. slapi_task_status_changed(task);
  135. }
  136. }
  137. /*
  138. * Increments task progress and updates status
  139. */
  140. void slapi_task_inc_progress(Slapi_Task *task)
  141. {
  142. if (task) {
  143. task->task_progress++;
  144. slapi_task_status_changed(task);
  145. }
  146. }
  147. /*
  148. * Sets completed task state and updates status
  149. */
  150. void slapi_task_finish(Slapi_Task *task, int rc)
  151. {
  152. if (task) {
  153. task->task_exitcode = rc;
  154. task->task_state = SLAPI_TASK_FINISHED;
  155. slapi_task_status_changed(task);
  156. }
  157. }
  158. /*
  159. * Cancels a task
  160. */
  161. void slapi_task_cancel(Slapi_Task *task, int rc)
  162. {
  163. if (task) {
  164. task->task_exitcode = rc;
  165. task->task_state = SLAPI_TASK_CANCELLED;
  166. slapi_task_status_changed(task);
  167. }
  168. }
  169. /*
  170. * Get the current state of a task
  171. */
  172. int slapi_task_get_state(Slapi_Task *task)
  173. {
  174. if (task) {
  175. return task->task_state;
  176. }
  177. return 0; /* return value not currently used */
  178. }
  179. /* this changes the 'nsTaskStatus' value, which is transient (anything logged
  180. * here wipes out any previous status)
  181. */
  182. void slapi_task_log_status(Slapi_Task *task, char *format, ...)
  183. {
  184. va_list ap;
  185. if (! task->task_status)
  186. task->task_status = (char *)slapi_ch_malloc(10 * LOG_BUFFER);
  187. if (! task->task_status)
  188. return; /* out of memory? */
  189. va_start(ap, format);
  190. PR_vsnprintf(task->task_status, (10 * LOG_BUFFER), format, ap);
  191. va_end(ap);
  192. slapi_task_status_changed(task);
  193. }
  194. void slapi_task_log_status_ext(Slapi_Task *task, char *format, va_list ap)
  195. {
  196. if (! task->task_status)
  197. task->task_status = (char *)slapi_ch_malloc(10 * LOG_BUFFER);
  198. if (! task->task_status)
  199. return; /* out of memory? */
  200. PR_vsnprintf(task->task_status, (10 * LOG_BUFFER), format, ap);
  201. slapi_task_status_changed(task);
  202. }
  203. /* this adds a line to the 'nsTaskLog' value, which is cumulative (anything
  204. * logged here is added to the end)
  205. */
  206. void slapi_task_log_notice(Slapi_Task *task, char *format, ...)
  207. {
  208. va_list ap;
  209. char buffer[LOG_BUFFER];
  210. size_t len;
  211. va_start(ap, format);
  212. PR_vsnprintf(buffer, LOG_BUFFER, format, ap);
  213. va_end(ap);
  214. if (task->task_log_lock) {
  215. PR_Lock(task->task_log_lock);
  216. }
  217. len = 2 + strlen(buffer) + (task->task_log ? strlen(task->task_log) : 0);
  218. if ((len > MAX_SCROLLBACK_BUFFER) && task->task_log) {
  219. size_t i;
  220. char *newbuf;
  221. /* start from middle of buffer, and find next linefeed */
  222. i = strlen(task->task_log)/2;
  223. while (task->task_log[i] && (task->task_log[i] != '\n'))
  224. i++;
  225. if (task->task_log[i])
  226. i++;
  227. len = strlen(task->task_log) - i + 2 + strlen(buffer);
  228. newbuf = (char *)slapi_ch_malloc(len);
  229. strcpy(newbuf, task->task_log + i);
  230. slapi_ch_free((void **)&task->task_log);
  231. task->task_log = newbuf;
  232. } else {
  233. if (! task->task_log) {
  234. task->task_log = (char *)slapi_ch_malloc(len);
  235. task->task_log[0] = 0;
  236. } else {
  237. task->task_log = (char *)slapi_ch_realloc(task->task_log, len);
  238. }
  239. }
  240. if (task->task_log[0])
  241. strcat(task->task_log, "\n");
  242. strcat(task->task_log, buffer);
  243. if (task->task_log_lock) {
  244. PR_Unlock(task->task_log_lock);
  245. }
  246. slapi_task_status_changed(task);
  247. }
  248. void slapi_task_log_notice_ext(Slapi_Task *task, char *format, va_list ap)
  249. {
  250. char buffer[LOG_BUFFER];
  251. size_t len;
  252. PR_vsnprintf(buffer, LOG_BUFFER, format, ap);
  253. if (task->task_log_lock) {
  254. PR_Lock(task->task_log_lock);
  255. }
  256. len = 2 + strlen(buffer) + (task->task_log ? strlen(task->task_log) : 0);
  257. if ((len > MAX_SCROLLBACK_BUFFER) && task->task_log) {
  258. size_t i;
  259. char *newbuf;
  260. /* start from middle of buffer, and find next linefeed */
  261. i = strlen(task->task_log)/2;
  262. while (task->task_log[i] && (task->task_log[i] != '\n'))
  263. i++;
  264. if (task->task_log[i])
  265. i++;
  266. len = strlen(task->task_log) - i + 2 + strlen(buffer);
  267. newbuf = (char *)slapi_ch_malloc(len);
  268. strcpy(newbuf, task->task_log + i);
  269. slapi_ch_free((void **)&task->task_log);
  270. task->task_log = newbuf;
  271. } else {
  272. if (! task->task_log) {
  273. task->task_log = (char *)slapi_ch_malloc(len);
  274. task->task_log[0] = 0;
  275. } else {
  276. task->task_log = (char *)slapi_ch_realloc(task->task_log, len);
  277. }
  278. }
  279. if (task->task_log[0])
  280. strcat(task->task_log, "\n");
  281. strcat(task->task_log, buffer);
  282. if (task->task_log_lock) {
  283. PR_Unlock(task->task_log_lock);
  284. }
  285. slapi_task_status_changed(task);
  286. }
  287. /* update attributes in the entry under "cn=tasks" to match the current
  288. * status of the task. */
  289. void slapi_task_status_changed(Slapi_Task *task)
  290. {
  291. LDAPMod modlist[20];
  292. LDAPMod *mod[20];
  293. int cur = 0, i;
  294. char s1[20], s2[20], s3[20];
  295. if (shutting_down) {
  296. /* don't care about task status updates anymore */
  297. return;
  298. }
  299. if (task->task_log_lock) {
  300. PR_Lock(task->task_log_lock);
  301. }
  302. NEXTMOD(TASK_LOG_NAME, task->task_log);
  303. if (task->task_log_lock) {
  304. PR_Unlock(task->task_log_lock);
  305. }
  306. NEXTMOD(TASK_STATUS_NAME, task->task_status);
  307. sprintf(s1, "%d", task->task_exitcode);
  308. sprintf(s2, "%d", task->task_progress);
  309. sprintf(s3, "%d", task->task_work);
  310. NEXTMOD(TASK_PROGRESS_NAME, s2);
  311. NEXTMOD(TASK_WORK_NAME, s3);
  312. /* only add the exit code when the job is done */
  313. if ((task->task_state == SLAPI_TASK_FINISHED) ||
  314. (task->task_state == SLAPI_TASK_CANCELLED)) {
  315. NEXTMOD(TASK_EXITCODE_NAME, s1);
  316. /* make sure the console can tell the task has ended */
  317. if (task->task_progress != task->task_work) {
  318. task->task_progress = task->task_work;
  319. }
  320. }
  321. mod[cur] = NULL;
  322. modify_internal_entry(task->task_dn, mod);
  323. for (i = 0; i < cur; i++)
  324. slapi_ch_free((void **)&modlist[i].mod_values);
  325. /*
  326. * Removed (task->task_state == SLAPI_TASK_CANCELLED) from
  327. * task_state checking to fix bz 515805.
  328. */
  329. if ((task->task_state == SLAPI_TASK_FINISHED) &&
  330. !(task->task_flags & SLAPI_TASK_DESTROYING)) {
  331. Slapi_PBlock *pb = slapi_pblock_new();
  332. Slapi_Entry *e;
  333. int ttl;
  334. time_t expire;
  335. e = get_internal_entry(pb, task->task_dn);
  336. if (e == NULL)
  337. return;
  338. ttl = atoi(fetch_attr(e, "ttl", DEFAULT_TTL));
  339. if (ttl > 3600)
  340. ttl = 3600; /* be reasonable. */
  341. expire = time(NULL) + ttl;
  342. task->task_flags |= SLAPI_TASK_DESTROYING;
  343. /* queue an event to destroy the state info */
  344. slapi_eq_once(destroy_task, (void *)task, expire);
  345. slapi_free_search_results_internal(pb);
  346. slapi_pblock_destroy(pb);
  347. }
  348. }
  349. /*
  350. * Stash some opaque task specific data in the task for later use.
  351. */
  352. void slapi_task_set_data(Slapi_Task *task, void *data)
  353. {
  354. if (task) {
  355. task->task_private = data;
  356. }
  357. }
  358. /*
  359. * Retrieve some opaque task specific data from the task.
  360. */
  361. void * slapi_task_get_data(Slapi_Task *task)
  362. {
  363. if (task) {
  364. return task->task_private;
  365. }
  366. return NULL; /* return value not currently used */
  367. }
  368. /*
  369. * Increment the task reference count
  370. */
  371. void slapi_task_inc_refcount(Slapi_Task *task)
  372. {
  373. if (task) {
  374. task->task_refcount++;
  375. }
  376. }
  377. /*
  378. * Decrement the task reference count
  379. */
  380. void slapi_task_dec_refcount(Slapi_Task *task)
  381. {
  382. if (task) {
  383. task->task_refcount--;
  384. }
  385. }
  386. /*
  387. * Returns the task reference count
  388. */
  389. int slapi_task_get_refcount(Slapi_Task *task)
  390. {
  391. if (task) {
  392. return task->task_refcount;
  393. }
  394. return 0; /* return value not currently used */
  395. }
  396. /* name is, for example, "import" */
  397. int slapi_task_register_handler(const char *name, dseCallbackFn func)
  398. {
  399. char *dn = NULL;
  400. Slapi_PBlock *pb = NULL;
  401. Slapi_Operation *op;
  402. LDAPMod *mods[3];
  403. LDAPMod mod[3];
  404. const char *objectclass[3];
  405. const char *cnvals[2];
  406. int ret = -1;
  407. int x;
  408. dn = slapi_create_dn_string("cn=%s,%s", name, TASK_BASE_DN);
  409. if (NULL == dn) {
  410. LDAPDebug1Arg( LDAP_DEBUG_ANY,
  411. "slapi_task_register_handler: "
  412. "failed to create task dn for %s\n", name);
  413. return ret;
  414. }
  415. pb = slapi_pblock_new();
  416. if (pb == NULL) {
  417. goto out;
  418. }
  419. /* this is painful :( */
  420. mods[0] = &mod[0];
  421. mod[0].mod_op = LDAP_MOD_ADD;
  422. mod[0].mod_type = "objectClass";
  423. mod[0].mod_values = (char **)objectclass;
  424. objectclass[0] = "top";
  425. objectclass[1] = "extensibleObject";
  426. objectclass[2] = NULL;
  427. mods[1] = &mod[1];
  428. mod[1].mod_op = LDAP_MOD_ADD;
  429. mod[1].mod_type = "cn";
  430. mod[1].mod_values = (char **)cnvals;
  431. cnvals[0] = name;
  432. cnvals[1] = NULL;
  433. mods[2] = NULL;
  434. slapi_add_internal_set_pb(pb, dn, mods, NULL,
  435. plugin_get_default_component_id(), 0);
  436. x = 1;
  437. slapi_pblock_set(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &x);
  438. /* Make sure these adds don't appear in the audit and change logs */
  439. slapi_pblock_get(pb, SLAPI_OPERATION, &op);
  440. operation_set_flag(op, OP_FLAG_ACTION_NOLOG);
  441. slapi_add_internal_pb(pb);
  442. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &x);
  443. if ((x != LDAP_SUCCESS) && (x != LDAP_ALREADY_EXISTS)) {
  444. LDAPDebug(LDAP_DEBUG_ANY,
  445. "Can't create task node '%s' (error %d)\n",
  446. name, x, 0);
  447. ret = x;
  448. goto out;
  449. }
  450. /* register add callback */
  451. slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP,
  452. dn, LDAP_SCOPE_SUBTREE, "(objectclass=*)", func, NULL);
  453. /* deny modify/delete of the root task entry */
  454. slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP,
  455. dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_deny, NULL);
  456. slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP,
  457. dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_deny, NULL);
  458. ret = 0;
  459. out:
  460. slapi_ch_free_string(&dn);
  461. if (pb) {
  462. slapi_pblock_destroy(pb);
  463. }
  464. return ret;
  465. }
  466. void slapi_task_set_destructor_fn(Slapi_Task *task, TaskCallbackFn func)
  467. {
  468. if (task) {
  469. task->destructor = func;
  470. }
  471. }
  472. void slapi_task_set_cancel_fn(Slapi_Task *task, TaskCallbackFn func)
  473. {
  474. if (task) {
  475. task->cancel = func;
  476. }
  477. }
  478. /***********************************
  479. * Static Helper Functions
  480. ***********************************/
  481. /* create a new task, fill in DN, and setup modify callback */
  482. static Slapi_Task *
  483. new_task(const char *rawdn)
  484. {
  485. Slapi_Task *task = NULL;
  486. char *dn = NULL;
  487. if (rawdn == NULL) {
  488. return NULL;
  489. }
  490. dn = slapi_create_dn_string("%s", rawdn);
  491. if (NULL == dn) {
  492. LDAPDebug1Arg(LDAP_DEBUG_ANY,
  493. "new_task failed: invalid task dn: %s\n", rawdn);
  494. return NULL;
  495. }
  496. task = (Slapi_Task *)slapi_ch_calloc(1, sizeof(Slapi_Task));
  497. PR_Lock(global_task_lock);
  498. task->next = global_task_list;
  499. global_task_list = task;
  500. PR_Unlock(global_task_lock);
  501. task->task_dn = dn;
  502. task->task_state = SLAPI_TASK_SETUP;
  503. task->task_flags = SLAPI_TASK_RUNNING_AS_TASK;
  504. task->destructor = NULL;
  505. task->cancel = NULL;
  506. task->task_private = NULL;
  507. slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
  508. LDAP_SCOPE_BASE, "(objectclass=*)", task_modify, (void *)task);
  509. slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
  510. LDAP_SCOPE_BASE, "(objectclass=*)", task_deny, NULL);
  511. /* don't add entries under this one */
  512. #if 0
  513. /* don't know why, but this doesn't work. it makes the current add
  514. * operation fail. :(
  515. */
  516. slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
  517. LDAP_SCOPE_SUBTREE, "(objectclass=*)", task_deny, NULL);
  518. #endif
  519. /* To protect task_log to be realloced if it's in use */
  520. task->task_log_lock = PR_NewLock();
  521. return task;
  522. }
  523. /* called by the event queue to destroy a task */
  524. static void
  525. destroy_task(time_t when, void *arg)
  526. {
  527. Slapi_Task *task = (Slapi_Task *)arg;
  528. Slapi_Task *t1;
  529. Slapi_PBlock *pb;
  530. if (task == NULL) return;
  531. pb = slapi_pblock_new();
  532. /* Call the custom destructor callback if one was provided,
  533. * then perform the internal task destruction. */
  534. if (task->destructor != NULL) {
  535. (*task->destructor)(task);
  536. }
  537. task_generic_destructor(task);
  538. /* if when == 0, we're already locked (called during shutdown) */
  539. if (when != 0) {
  540. PR_Lock(global_task_lock);
  541. }
  542. if (global_task_list == task) {
  543. global_task_list = task->next;
  544. } else {
  545. for (t1 = global_task_list; t1; t1 = t1->next) {
  546. if (t1->next == task) {
  547. t1->next = task->next;
  548. break;
  549. }
  550. }
  551. }
  552. if (when != 0) {
  553. PR_Unlock(global_task_lock);
  554. }
  555. slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP,
  556. task->task_dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_modify);
  557. slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP,
  558. task->task_dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_deny);
  559. slapi_delete_internal_set_pb(pb, task->task_dn, NULL, NULL,
  560. (void *)plugin_get_default_component_id(), 0);
  561. slapi_delete_internal_pb(pb);
  562. slapi_pblock_destroy(pb);
  563. slapi_ch_free((void **)&task->task_dn);
  564. slapi_ch_free((void **)&task);
  565. }
  566. /* extract a single value from the entry (as a string) -- if it's not in the
  567. * entry, the default will be returned (which can be NULL).
  568. * you do not need to free anything returned by this.
  569. */
  570. static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
  571. const char *default_val)
  572. {
  573. Slapi_Attr *attr;
  574. Slapi_Value *val = NULL;
  575. if (slapi_entry_attr_find(e, attrname, &attr) != 0)
  576. return default_val;
  577. slapi_attr_first_value(attr, &val);
  578. return slapi_value_get_string(val);
  579. }
  580. /* supply the pblock, destroy it when you're done */
  581. static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn)
  582. {
  583. Slapi_Entry **entries = NULL;
  584. int ret = 0;
  585. slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
  586. NULL, 0, NULL, NULL, (void *)plugin_get_default_component_id(), 0);
  587. slapi_search_internal_pb(pb);
  588. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
  589. if (ret != LDAP_SUCCESS) {
  590. LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't find task entry '%s'\n",
  591. dn, 0, 0);
  592. return NULL;
  593. }
  594. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  595. if ((NULL == entries) || (NULL == entries[0])) {
  596. LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't find task entry '%s'\n",
  597. dn, 0, 0);
  598. return NULL;
  599. }
  600. return entries[0];
  601. }
  602. static void modify_internal_entry(char *dn, LDAPMod **mods)
  603. {
  604. Slapi_PBlock pb;
  605. Slapi_Operation *op;
  606. int ret = 0;
  607. int tries = 0;
  608. int dont_write_file = 1;
  609. do {
  610. pblock_init(&pb);
  611. slapi_modify_internal_set_pb(&pb, dn, mods, NULL, NULL,
  612. (void *)plugin_get_default_component_id(), 0);
  613. /* all modifications to the cn=tasks subtree are transient --
  614. * we erase them all when the server starts up next time, so there's
  615. * no need to save them in the dse file.
  616. */
  617. slapi_pblock_set(&pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
  618. /* Make sure these mods are not logged in audit or changelog */
  619. slapi_pblock_get(&pb, SLAPI_OPERATION, &op);
  620. operation_set_flag(op, OP_FLAG_ACTION_NOLOG);
  621. slapi_modify_internal_pb(&pb);
  622. slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
  623. if (ret != LDAP_SUCCESS) {
  624. /* could be waiting for another thread to finish adding this
  625. * entry -- try at least 3 times before giving up.
  626. */
  627. tries++;
  628. if (tries == 3) {
  629. LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't modify task "
  630. "entry '%s'; %s (%d)\n", dn, ldap_err2string(ret), ret);
  631. pblock_done(&pb);
  632. return;
  633. }
  634. DS_Sleep(PR_SecondsToInterval(1));
  635. }
  636. pblock_done(&pb);
  637. } while (ret != LDAP_SUCCESS);
  638. }
  639. static void task_generic_destructor(Slapi_Task *task)
  640. {
  641. if (task->task_log) {
  642. slapi_ch_free((void **)&task->task_log);
  643. }
  644. if (task->task_status) {
  645. slapi_ch_free((void **)&task->task_status);
  646. }
  647. if (task->task_log_lock) {
  648. PR_DestroyLock(task->task_log_lock);
  649. task->task_log_lock = NULL;
  650. }
  651. task->task_log = task->task_status = NULL;
  652. }
  653. /********** actual task callbacks **********/
  654. static int task_deny(Slapi_PBlock *pb, Slapi_Entry *e,
  655. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
  656. {
  657. /* internal operations (conn=NULL) are allowed to do whatever they want */
  658. if (pb->pb_conn == NULL) {
  659. *returncode = LDAP_SUCCESS;
  660. return SLAPI_DSE_CALLBACK_OK;
  661. }
  662. *returncode = LDAP_UNWILLING_TO_PERFORM;
  663. return SLAPI_DSE_CALLBACK_ERROR;
  664. }
  665. static int task_modify(Slapi_PBlock *pb, Slapi_Entry *e,
  666. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
  667. {
  668. Slapi_Task *task = (Slapi_Task *)arg;
  669. LDAPMod **mods;
  670. int i;
  671. /* the connection block will be NULL for internal operations */
  672. if (pb->pb_conn == NULL) {
  673. *returncode = LDAP_SUCCESS;
  674. return SLAPI_DSE_CALLBACK_OK;
  675. }
  676. /* ignore eAfter, just scan the mods for anything unacceptable */
  677. slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
  678. for (i = 0; mods[i] != NULL; i++) {
  679. /* for some reason, "modifiersName" and "modifyTimestamp" are
  680. * stuck in by the server */
  681. if ((strcasecmp(mods[i]->mod_type, "ttl") != 0) &&
  682. (strcasecmp(mods[i]->mod_type, "nsTaskCancel") != 0) &&
  683. (strcasecmp(mods[i]->mod_type, "modifiersName") != 0) &&
  684. (strcasecmp(mods[i]->mod_type, "modifyTimestamp") != 0)) {
  685. /* you aren't allowed to change this! */
  686. *returncode = LDAP_UNWILLING_TO_PERFORM;
  687. return SLAPI_DSE_CALLBACK_ERROR;
  688. }
  689. }
  690. /* okay, we've decided to accept these changes. now look at the new
  691. * entry and absorb any new values.
  692. */
  693. if (strcasecmp(fetch_attr(eAfter, "nsTaskCancel", "false"), "true") == 0) {
  694. /* cancel this task, if not already */
  695. if (task->task_state != SLAPI_TASK_CANCELLED) {
  696. task->task_state = SLAPI_TASK_CANCELLED;
  697. if (task->cancel) {
  698. (*task->cancel)(task);
  699. LDAPDebug(LDAP_DEBUG_ANY, "Cancelling task '%s'\n",
  700. fetch_attr(eAfter, "cn", "?"), 0, 0);
  701. }
  702. }
  703. }
  704. /* we fetch ttl from the entry when it's needed */
  705. *returncode = LDAP_SUCCESS;
  706. return SLAPI_DSE_CALLBACK_OK;
  707. }
  708. static int task_import_add(Slapi_PBlock *pb, Slapi_Entry *e,
  709. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
  710. {
  711. Slapi_Attr *attr;
  712. Slapi_Value *val = NULL;
  713. Slapi_Backend *be = NULL;
  714. const char *instance_name;
  715. char **ldif_file = NULL, **include = NULL, **exclude = NULL;
  716. int idx, rv = 0;
  717. const char *do_attr_indexes, *uniqueid_kind_str;
  718. int uniqueid_kind = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
  719. Slapi_PBlock mypb;
  720. Slapi_Task *task;
  721. char *nameFrombe_name = NULL;
  722. const char *encrypt_on_import = NULL;
  723. if (fetch_attr(e, "cn", NULL) == NULL) {
  724. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  725. return SLAPI_DSE_CALLBACK_ERROR;
  726. }
  727. instance_name = fetch_attr(e, "nsInstance", NULL);
  728. encrypt_on_import = fetch_attr(e, "nsImportEncrypt", NULL);
  729. /* include/exclude suffixes */
  730. if (slapi_entry_attr_find(e, "nsIncludeSuffix", &attr) == 0) {
  731. for (idx = slapi_attr_first_value(attr, &val);
  732. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  733. rv = charray_normdn_add(&include,
  734. (char *)slapi_value_get_string(val),
  735. "nsIncludeSuffix");
  736. if (0 != rv) {
  737. *returncode = LDAP_PARAM_ERROR;
  738. return SLAPI_DSE_CALLBACK_ERROR;
  739. }
  740. }
  741. }
  742. if (slapi_entry_attr_find(e, "nsExcludeSuffix", &attr) == 0) {
  743. for (idx = slapi_attr_first_value(attr, &val);
  744. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  745. rv = charray_normdn_add(&exclude,
  746. (char *)slapi_value_get_string(val),
  747. "nsExcludeSuffix");
  748. if (0 != rv) {
  749. *returncode = LDAP_PARAM_ERROR;
  750. return SLAPI_DSE_CALLBACK_ERROR;
  751. }
  752. }
  753. }
  754. /*
  755. * if instance is given, just use it to get the backend.
  756. * otherwise, we use included/excluded suffix list to specify a backend.
  757. */
  758. if (NULL == instance_name) {
  759. char **instances, **ip;
  760. int counter;
  761. if (slapi_lookup_instance_name_by_suffixes(include, exclude,
  762. &instances) < 0) {
  763. LDAPDebug(LDAP_DEBUG_ANY,
  764. "ERROR: No backend instance is specified.\n", 0, 0, 0);
  765. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  766. return SLAPI_DSE_CALLBACK_ERROR;
  767. }
  768. if (instances) {
  769. for (ip = instances, counter = 0; ip && *ip; ip++, counter++)
  770. ;
  771. if (counter == 1){
  772. instance_name = *instances;
  773. nameFrombe_name = *instances;
  774. }
  775. else if (counter == 0) {
  776. LDAPDebug(LDAP_DEBUG_ANY,
  777. "ERROR: No backend instance is specified.\n", 0, 0, 0);
  778. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  779. return SLAPI_DSE_CALLBACK_ERROR;
  780. } else {
  781. LDAPDebug(LDAP_DEBUG_ANY,
  782. "ERROR: Multiple backend instances are specified: "
  783. "%s, %s, ...\n", instances[0], instances[1], 0);
  784. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  785. return SLAPI_DSE_CALLBACK_ERROR;
  786. }
  787. } else {
  788. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  789. return SLAPI_DSE_CALLBACK_ERROR;
  790. }
  791. }
  792. /* lookup the backend */
  793. be = slapi_be_select_by_instance_name(instance_name);
  794. if (be == NULL) {
  795. LDAPDebug(LDAP_DEBUG_ANY, "can't import to nonexistent backend %s\n",
  796. instance_name, 0, 0);
  797. slapi_ch_free_string(&nameFrombe_name);
  798. *returncode = LDAP_NO_SUCH_OBJECT;
  799. return SLAPI_DSE_CALLBACK_ERROR;
  800. }
  801. /* refuse to do an import on pre-V3 plugins. plugin api V3 is the one
  802. * for DS 5.0 where the import/export stuff changed a lot.
  803. */
  804. if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
  805. LDAPDebug(LDAP_DEBUG_ANY, "can't perform an import with pre-V3 "
  806. "backend plugin %s\n", be->be_database->plg_name, 0, 0);
  807. *returncode = LDAP_UNWILLING_TO_PERFORM;
  808. slapi_ch_free_string(&nameFrombe_name);
  809. return SLAPI_DSE_CALLBACK_ERROR;
  810. }
  811. if (be->be_database->plg_ldif2db == NULL) {
  812. LDAPDebug(LDAP_DEBUG_ANY, "ERROR: no ldif2db function defined for "
  813. "backend %s\n", be->be_database->plg_name, 0, 0);
  814. *returncode = LDAP_UNWILLING_TO_PERFORM;
  815. slapi_ch_free_string(&nameFrombe_name);
  816. return SLAPI_DSE_CALLBACK_ERROR;
  817. }
  818. /* get ldif filenames -- from here on, memory has been allocated */
  819. if (slapi_entry_attr_find(e, "nsFilename", &attr) != 0) {
  820. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  821. slapi_ch_free_string(&nameFrombe_name);
  822. return SLAPI_DSE_CALLBACK_ERROR;
  823. }
  824. for (idx = slapi_attr_first_value(attr, &val);
  825. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  826. charray_add(&ldif_file, slapi_ch_strdup(slapi_value_get_string(val)));
  827. }
  828. do_attr_indexes = fetch_attr(e, "nsImportIndexAttrs", "true");
  829. uniqueid_kind_str = fetch_attr(e, "nsUniqueIdGenerator", NULL);
  830. if (uniqueid_kind_str != NULL) {
  831. if (strcasecmp(uniqueid_kind_str, "none") == 0) {
  832. uniqueid_kind = SLAPI_UNIQUEID_GENERATE_NONE;
  833. } else if (strcasecmp(uniqueid_kind_str, "deterministic") == 0) {
  834. uniqueid_kind = SLAPI_UNIQUEID_GENERATE_NAME_BASED;
  835. } else {
  836. /* default - time based */
  837. uniqueid_kind = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
  838. }
  839. }
  840. /* allocate new task now */
  841. task = slapi_new_task(slapi_entry_get_ndn(e));
  842. if (task == NULL) {
  843. LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
  844. rv = LDAP_OPERATIONS_ERROR;
  845. goto out;
  846. }
  847. memset(&mypb, 0, sizeof(mypb));
  848. mypb.pb_backend = be;
  849. mypb.pb_plugin = be->be_database;
  850. mypb.pb_removedupvals = atoi(fetch_attr(e, "nsImportChunkSize", "0"));
  851. mypb.pb_ldif2db_noattrindexes =
  852. !(strcasecmp(do_attr_indexes, "true") == 0);
  853. mypb.pb_ldif_generate_uniqueid = uniqueid_kind;
  854. mypb.pb_ldif_namespaceid =
  855. (char *)fetch_attr(e, "nsUniqueIdGeneratorNamespace", NULL);
  856. mypb.pb_instance_name = (char *)instance_name;
  857. mypb.pb_ldif_files = ldif_file;
  858. mypb.pb_ldif_include = include;
  859. mypb.pb_ldif_exclude = exclude;
  860. mypb.pb_task = task;
  861. mypb.pb_task_flags = SLAPI_TASK_RUNNING_AS_TASK;
  862. if (NULL != encrypt_on_import && 0 == strcasecmp(encrypt_on_import, "true") ) {
  863. mypb.pb_ldif_encrypt = 1;
  864. }
  865. rv = (*mypb.pb_plugin->plg_ldif2db)(&mypb);
  866. if (rv == 0) {
  867. slapi_entry_attr_set_charptr(e, TASK_LOG_NAME, "");
  868. slapi_entry_attr_set_charptr(e, TASK_STATUS_NAME, "");
  869. slapi_entry_attr_set_int(e, TASK_PROGRESS_NAME, task->task_progress);
  870. slapi_entry_attr_set_int(e, TASK_WORK_NAME, task->task_work);
  871. }
  872. out:
  873. slapi_ch_free_string(&nameFrombe_name);
  874. charray_free(ldif_file);
  875. charray_free(include);
  876. charray_free(exclude);
  877. if (rv != 0) {
  878. *returncode = LDAP_OPERATIONS_ERROR;
  879. destroy_task(1, task);
  880. return SLAPI_DSE_CALLBACK_ERROR;
  881. }
  882. *returncode = LDAP_SUCCESS;
  883. return SLAPI_DSE_CALLBACK_OK;
  884. }
  885. static void task_export_thread(void *arg)
  886. {
  887. Slapi_PBlock *pb = (Slapi_PBlock *)arg;
  888. char **instance_names = (char **)pb->pb_instance_name;
  889. char **inp;
  890. char *ldif_file = pb->pb_ldif_file;
  891. char *this_ldif_file = NULL;
  892. Slapi_Backend *be = NULL;
  893. int rv = -1;
  894. int count;
  895. Slapi_Task *task = pb->pb_task;
  896. g_incr_active_threadcnt();
  897. for (count = 0, inp = instance_names; *inp; inp++, count++)
  898. ;
  899. slapi_task_begin(task, count);
  900. for (inp = instance_names; *inp; inp++) {
  901. int release_me = 0;
  902. /* lookup the backend */
  903. be = slapi_be_select_by_instance_name((const char *)*inp);
  904. if (be == NULL) {
  905. /* shouldn't happen */
  906. LDAPDebug(LDAP_DEBUG_ANY, "ldbm2ldif: backend '%s' is AWOL!\n",
  907. (const char *)*inp, 0, 0);
  908. continue;
  909. }
  910. pb->pb_backend = be;
  911. pb->pb_plugin = be->be_database;
  912. pb->pb_instance_name = (char *)*inp;
  913. /* ldif_file name for each? */
  914. if (pb->pb_ldif_printkey & EXPORT_APPENDMODE) {
  915. if (inp == instance_names) { /* first export */
  916. pb->pb_ldif_printkey |= EXPORT_APPENDMODE_1;
  917. } else {
  918. pb->pb_ldif_printkey &= ~EXPORT_APPENDMODE_1;
  919. }
  920. } else {
  921. if (strcmp(ldif_file, "-")) { /* not '-' */
  922. char *p;
  923. #if defined( _WIN32 )
  924. char sep = '\\';
  925. if (NULL != strchr(ldif_file, '/'))
  926. sep = '/';
  927. #else
  928. char sep = '/';
  929. #endif
  930. this_ldif_file = (char *)slapi_ch_malloc(strlen(ldif_file) +
  931. strlen(*inp) + 2);
  932. p = strrchr(ldif_file, sep);
  933. if (NULL == p) {
  934. sprintf(this_ldif_file, "%s_%s", *inp, ldif_file);
  935. } else {
  936. char *q;
  937. q = p + 1;
  938. *p = '\0';
  939. sprintf(this_ldif_file, "%s%c%s_%s",
  940. ldif_file, sep, *inp, q);
  941. *p = sep;
  942. }
  943. pb->pb_ldif_file = this_ldif_file;
  944. release_me = 1;
  945. }
  946. }
  947. slapi_task_log_notice(task, "Beginning export of '%s'", *inp);
  948. LDAPDebug(LDAP_DEBUG_ANY, "Beginning export of '%s'\n", *inp, 0, 0);
  949. rv = (*pb->pb_plugin->plg_db2ldif)(pb);
  950. if (rv != 0) {
  951. slapi_task_log_notice(task, "backend '%s' export failed (%d)",
  952. *inp, rv);
  953. LDAPDebug(LDAP_DEBUG_ANY,
  954. "ldbm2ldif: backend '%s' export failed (%d)\n",
  955. (const char *)*inp, rv, 0);
  956. }
  957. if (release_me) {
  958. slapi_ch_free((void **)&this_ldif_file);
  959. }
  960. if (rv != 0)
  961. break;
  962. slapi_task_inc_progress(task);
  963. }
  964. /* free the memory now */
  965. charray_free(instance_names);
  966. slapi_ch_free((void **)&ldif_file);
  967. charray_free(pb->pb_ldif_include);
  968. charray_free(pb->pb_ldif_exclude);
  969. slapi_pblock_destroy(pb);
  970. if (rv == 0) {
  971. slapi_task_log_notice(task, "Export finished.");
  972. LDAPDebug(LDAP_DEBUG_ANY, "Export finished.\n", 0, 0, 0);
  973. } else {
  974. slapi_task_log_notice(task, "Export failed.");
  975. LDAPDebug(LDAP_DEBUG_ANY, "Export failed.\n", 0, 0, 0);
  976. }
  977. slapi_task_finish(task, rv);
  978. g_decr_active_threadcnt();
  979. }
  980. static int task_export_add(Slapi_PBlock *pb, Slapi_Entry *e,
  981. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
  982. {
  983. Slapi_Attr *attr;
  984. Slapi_Value *val = NULL;
  985. Slapi_Backend *be = NULL;
  986. char *ldif_file = NULL;
  987. char **instance_names = NULL, **inp;
  988. char **include = NULL, **exclude = NULL;
  989. int idx, rv = SLAPI_DSE_CALLBACK_OK;
  990. int export_replica_flag = 0;
  991. int ldif_printkey_flag = 0;
  992. int dump_uniqueid_flag = 0;
  993. int instance_cnt = 0;
  994. const char *my_ldif_file;
  995. const char *use_one_file;
  996. const char *export_replica;
  997. const char *ldif_printkey;
  998. const char *dump_uniqueid;
  999. Slapi_PBlock *mypb = NULL;
  1000. Slapi_Task *task = NULL;
  1001. PRThread *thread;
  1002. const char *decrypt_on_export = NULL;
  1003. *returncode = LDAP_SUCCESS;
  1004. if (fetch_attr(e, "cn", NULL) == NULL) {
  1005. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1006. rv = SLAPI_DSE_CALLBACK_ERROR;
  1007. goto out;
  1008. }
  1009. decrypt_on_export = fetch_attr(e, "nsExportDecrypt", NULL);
  1010. /* nsInstances -- from here on, memory has been allocated */
  1011. if (slapi_entry_attr_find(e, "nsInstance", &attr) == 0) {
  1012. for (idx = slapi_attr_first_value(attr, &val);
  1013. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  1014. charray_add(&instance_names,
  1015. slapi_ch_strdup(slapi_value_get_string(val)));
  1016. instance_cnt++;
  1017. }
  1018. }
  1019. /* include/exclude suffixes */
  1020. if (slapi_entry_attr_find(e, "nsIncludeSuffix", &attr) == 0) {
  1021. for (idx = slapi_attr_first_value(attr, &val);
  1022. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  1023. rv = charray_normdn_add(&include,
  1024. (char *)slapi_value_get_string(val),
  1025. "nsIncludeSuffix");
  1026. if (0 != rv) {
  1027. *returncode = LDAP_PARAM_ERROR;
  1028. goto out;
  1029. }
  1030. }
  1031. }
  1032. if (slapi_entry_attr_find(e, "nsExcludeSuffix", &attr) == 0) {
  1033. for (idx = slapi_attr_first_value(attr, &val);
  1034. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  1035. rv = charray_normdn_add(&exclude,
  1036. (char *)slapi_value_get_string(val),
  1037. "nsExcludeSuffix");
  1038. if (0 != rv) {
  1039. *returncode = LDAP_PARAM_ERROR;
  1040. goto out;
  1041. }
  1042. }
  1043. }
  1044. if (NULL == instance_names) {
  1045. char **ip;
  1046. if (slapi_lookup_instance_name_by_suffixes(include, exclude,
  1047. &instance_names) < 0) {
  1048. LDAPDebug(LDAP_DEBUG_ANY,
  1049. "ERROR: No backend instance is specified.\n", 0, 0, 0);
  1050. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1051. rv = SLAPI_DSE_CALLBACK_ERROR;
  1052. goto out;
  1053. }
  1054. if (instance_names) {
  1055. for (ip = instance_names, instance_cnt = 0; ip && *ip;
  1056. ip++, instance_cnt++)
  1057. ;
  1058. if (instance_cnt == 0) {
  1059. LDAPDebug(LDAP_DEBUG_ANY,
  1060. "ERROR: No backend instance is specified.\n", 0, 0, 0);
  1061. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1062. rv = SLAPI_DSE_CALLBACK_ERROR;
  1063. goto out;
  1064. }
  1065. } else {
  1066. LDAPDebug(LDAP_DEBUG_ANY,
  1067. "ERROR: No backend instance is specified.\n", 0, 0, 0);
  1068. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1069. rv = SLAPI_DSE_CALLBACK_ERROR;
  1070. goto out;
  1071. }
  1072. }
  1073. /* ldif file name */
  1074. if ((my_ldif_file = fetch_attr(e, "nsFilename", NULL)) == NULL) {
  1075. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1076. rv = SLAPI_DSE_CALLBACK_ERROR;
  1077. goto out;
  1078. }
  1079. ldif_file = slapi_ch_strdup(my_ldif_file);
  1080. /* if true, multiple backends are dumped into one ldif file */
  1081. use_one_file = fetch_attr(e, "nsUseOneFile", "true");
  1082. if (strcasecmp(use_one_file, "true") == 0) {
  1083. ldif_printkey_flag |= EXPORT_APPENDMODE;
  1084. }
  1085. /* -r: export replica */
  1086. export_replica = fetch_attr(e, "nsExportReplica", "false");
  1087. if (!strcasecmp(export_replica, "true")) /* true */
  1088. export_replica_flag = 1;
  1089. /* -N: eq "false" ==> does not print out key value */
  1090. ldif_printkey = fetch_attr(e, "nsPrintKey", "true");
  1091. if (!strcasecmp(ldif_printkey, "true")) /* true */
  1092. ldif_printkey_flag |= EXPORT_PRINTKEY;
  1093. /* -C: eq "true" ==> use only id2entry file */
  1094. ldif_printkey = fetch_attr(e, "nsUseId2Entry", "false");
  1095. if (!strcasecmp(ldif_printkey, "true")) /* true */
  1096. ldif_printkey_flag |= EXPORT_ID2ENTRY_ONLY;
  1097. /* if "true" ==> 8-bit strings are not base64 encoded */
  1098. ldif_printkey = fetch_attr(e, "nsMinimalEncoding", "false");
  1099. if (!strcasecmp(ldif_printkey, "true")) /* true */
  1100. ldif_printkey_flag |= EXPORT_MINIMAL_ENCODING;
  1101. /* -U: eq "true" ==> does not fold the output */
  1102. ldif_printkey = fetch_attr(e, "nsNoWrap", "false");
  1103. if (!strcasecmp(ldif_printkey, "true")) /* true */
  1104. ldif_printkey_flag |= EXPORT_NOWRAP;
  1105. /* -1: eq "true" ==> does not print version line */
  1106. ldif_printkey = fetch_attr(e, "nsNoVersionLine", "false");
  1107. if (!strcasecmp(ldif_printkey, "true")) /* true */
  1108. ldif_printkey_flag |= EXPORT_NOVERSION;
  1109. /* -u: eq "false" ==> does not dump unique id */
  1110. dump_uniqueid = fetch_attr(e, "nsDumpUniqId", "true");
  1111. if (!strcasecmp(dump_uniqueid, "true")) /* true */
  1112. dump_uniqueid_flag = 1;
  1113. /* check that all the backends are ok */
  1114. for (inp = instance_names; *inp; inp++) {
  1115. /* lookup the backend */
  1116. be = slapi_be_select_by_instance_name((const char *)*inp);
  1117. if (be == NULL) {
  1118. LDAPDebug(LDAP_DEBUG_ANY,
  1119. "can't export to nonexistent backend %s\n", *inp, 0, 0);
  1120. *returncode = LDAP_NO_SUCH_OBJECT;
  1121. rv = SLAPI_DSE_CALLBACK_ERROR;
  1122. goto out;
  1123. }
  1124. /* refuse to do an export on pre-V3 plugins. plugin api V3 is the one
  1125. * for DS 5.0 where the import/export stuff changed a lot.
  1126. */
  1127. if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
  1128. LDAPDebug(LDAP_DEBUG_ANY, "can't perform an export with pre-V3 "
  1129. "backend plugin %s\n", be->be_database->plg_name, 0, 0);
  1130. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1131. rv = SLAPI_DSE_CALLBACK_ERROR;
  1132. goto out;
  1133. }
  1134. if (be->be_database->plg_db2ldif == NULL) {
  1135. LDAPDebug(LDAP_DEBUG_ANY, "ERROR: no db2ldif function defined for "
  1136. "backend %s\n", be->be_database->plg_name, 0, 0);
  1137. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1138. rv = SLAPI_DSE_CALLBACK_ERROR;
  1139. goto out;
  1140. }
  1141. }
  1142. /* allocate new task now */
  1143. task = slapi_new_task(slapi_entry_get_ndn(e));
  1144. if (task == NULL) {
  1145. LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
  1146. *returncode = LDAP_OPERATIONS_ERROR;
  1147. rv = SLAPI_DSE_CALLBACK_ERROR;
  1148. goto out;
  1149. }
  1150. mypb = slapi_pblock_new();
  1151. if (mypb == NULL) {
  1152. *returncode = LDAP_OPERATIONS_ERROR;
  1153. rv = SLAPI_DSE_CALLBACK_ERROR;
  1154. goto out;
  1155. }
  1156. mypb->pb_ldif_include = include;
  1157. mypb->pb_ldif_exclude = exclude;
  1158. mypb->pb_ldif_printkey = ldif_printkey_flag;
  1159. mypb->pb_ldif_dump_replica = export_replica_flag;
  1160. mypb->pb_ldif_dump_uniqueid = dump_uniqueid_flag;
  1161. mypb->pb_ldif_file = ldif_file;
  1162. /* horrible hack */
  1163. mypb->pb_instance_name = (char *)instance_names;
  1164. mypb->pb_task = task;
  1165. mypb->pb_task_flags = SLAPI_TASK_RUNNING_AS_TASK;
  1166. if (NULL != decrypt_on_export && 0 == strcasecmp(decrypt_on_export, "true") ) {
  1167. mypb->pb_ldif_encrypt = 1;
  1168. }
  1169. /* start the export as a separate thread */
  1170. thread = PR_CreateThread(PR_USER_THREAD, task_export_thread,
  1171. (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  1172. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
  1173. if (thread == NULL) {
  1174. LDAPDebug(LDAP_DEBUG_ANY,
  1175. "unable to create ldbm2ldif thread!\n", 0, 0, 0);
  1176. *returncode = LDAP_OPERATIONS_ERROR;
  1177. rv = SLAPI_DSE_CALLBACK_ERROR;
  1178. slapi_pblock_destroy(mypb);
  1179. goto out;
  1180. }
  1181. /* thread successful -- don't free the pb, let the thread do that. */
  1182. return SLAPI_DSE_CALLBACK_OK;
  1183. out:
  1184. charray_free(instance_names);
  1185. charray_free(include);
  1186. charray_free(exclude);
  1187. if (ldif_file != NULL) {
  1188. slapi_ch_free((void **)&ldif_file);
  1189. }
  1190. if (task) {
  1191. destroy_task(1, task);
  1192. }
  1193. return rv;
  1194. }
  1195. static void task_backup_thread(void *arg)
  1196. {
  1197. Slapi_PBlock *pb = (Slapi_PBlock *)arg;
  1198. Slapi_Task *task = pb->pb_task;
  1199. int rv;
  1200. g_incr_active_threadcnt();
  1201. slapi_task_begin(task, 1);
  1202. slapi_task_log_notice(task, "Beginning backup of '%s'",
  1203. pb->pb_plugin->plg_name);
  1204. LDAPDebug(LDAP_DEBUG_ANY, "Beginning backup of '%s'\n",
  1205. pb->pb_plugin->plg_name, 0, 0);
  1206. rv = (*pb->pb_plugin->plg_db2archive)(pb);
  1207. if (rv != 0) {
  1208. slapi_task_log_notice(task, "Backup failed (error %d)", rv);
  1209. slapi_task_log_status(task, "Backup failed (error %d)", rv);
  1210. LDAPDebug(LDAP_DEBUG_ANY, "Backup failed (error %d)\n", rv, 0, 0);
  1211. } else {
  1212. slapi_task_log_notice(task, "Backup finished.");
  1213. slapi_task_log_status(task, "Backup finished.");
  1214. LDAPDebug(LDAP_DEBUG_ANY, "Backup finished.\n", 0, 0, 0);
  1215. }
  1216. slapi_task_finish(task, rv);
  1217. slapi_ch_free((void **)&pb->pb_seq_val);
  1218. slapi_pblock_destroy(pb);
  1219. g_decr_active_threadcnt();
  1220. }
  1221. static int task_backup_add(Slapi_PBlock *pb, Slapi_Entry *e,
  1222. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
  1223. {
  1224. Slapi_Backend *be = NULL;
  1225. PRThread *thread = NULL;
  1226. const char *archive_dir = NULL;
  1227. const char *my_database_type = NULL;
  1228. const char *database_type = "ldbm database";
  1229. char *cookie = NULL;
  1230. int rv = SLAPI_DSE_CALLBACK_OK;
  1231. Slapi_PBlock *mypb = NULL;
  1232. Slapi_Task *task = NULL;
  1233. *returncode = LDAP_SUCCESS;
  1234. if (fetch_attr(e, "cn", NULL) == NULL) {
  1235. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1236. rv = SLAPI_DSE_CALLBACK_ERROR;
  1237. goto out;
  1238. }
  1239. /* archive dir name */
  1240. if ((archive_dir = fetch_attr(e, "nsArchiveDir", NULL)) == NULL) {
  1241. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1242. rv = SLAPI_DSE_CALLBACK_ERROR;
  1243. goto out;
  1244. }
  1245. /* database type */
  1246. my_database_type = fetch_attr(e, "nsDatabaseType", NULL);
  1247. if (NULL != my_database_type)
  1248. database_type = my_database_type;
  1249. /* get backend that has db2archive and the database type matches. */
  1250. cookie = NULL;
  1251. be = slapi_get_first_backend(&cookie);
  1252. while (be) {
  1253. if (NULL != be->be_database->plg_db2archive &&
  1254. !strcasecmp(database_type, be->be_database->plg_name))
  1255. break;
  1256. be = (backend *)slapi_get_next_backend (cookie);
  1257. }
  1258. slapi_ch_free((void **)&cookie);
  1259. if (NULL == be || NULL == be->be_database->plg_db2archive) {
  1260. LDAPDebug(LDAP_DEBUG_ANY,
  1261. "ERROR: no db2archive function defined.\n", 0, 0, 0);
  1262. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1263. rv = SLAPI_DSE_CALLBACK_ERROR;
  1264. goto out;
  1265. }
  1266. if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
  1267. LDAPDebug(LDAP_DEBUG_ANY, "can't perform an backup with pre-V3 "
  1268. "backend plugin %s\n", be->be_database->plg_name, 0, 0);
  1269. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1270. rv = SLAPI_DSE_CALLBACK_ERROR;
  1271. goto out;
  1272. }
  1273. /* allocate new task now */
  1274. task = slapi_new_task(slapi_entry_get_ndn(e));
  1275. if (task == NULL) {
  1276. LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
  1277. *returncode = LDAP_OPERATIONS_ERROR;
  1278. rv = SLAPI_DSE_CALLBACK_ERROR;
  1279. goto out;
  1280. }
  1281. mypb = slapi_pblock_new();
  1282. if (mypb == NULL) {
  1283. *returncode = LDAP_OPERATIONS_ERROR;
  1284. rv = SLAPI_DSE_CALLBACK_ERROR;
  1285. goto out;
  1286. }
  1287. mypb->pb_seq_val = slapi_ch_strdup(archive_dir);
  1288. mypb->pb_plugin = be->be_database;
  1289. mypb->pb_task = task;
  1290. mypb->pb_task_flags = SLAPI_TASK_RUNNING_AS_TASK;
  1291. /* start the backup as a separate thread */
  1292. thread = PR_CreateThread(PR_USER_THREAD, task_backup_thread,
  1293. (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  1294. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
  1295. if (thread == NULL) {
  1296. LDAPDebug(LDAP_DEBUG_ANY,
  1297. "unable to create backup thread!\n", 0, 0, 0);
  1298. *returncode = LDAP_OPERATIONS_ERROR;
  1299. rv = SLAPI_DSE_CALLBACK_ERROR;
  1300. slapi_ch_free((void **)&mypb->pb_seq_val);
  1301. slapi_pblock_destroy(mypb);
  1302. goto out;
  1303. }
  1304. /* thread successful -- don't free the pb, let the thread do that. */
  1305. return SLAPI_DSE_CALLBACK_OK;
  1306. out:
  1307. if (task) {
  1308. destroy_task(1, task);
  1309. }
  1310. return rv;
  1311. }
  1312. static void task_restore_thread(void *arg)
  1313. {
  1314. Slapi_PBlock *pb = (Slapi_PBlock *)arg;
  1315. Slapi_Task *task = pb->pb_task;
  1316. int rv;
  1317. g_incr_active_threadcnt();
  1318. slapi_task_begin(task, 1);
  1319. slapi_task_log_notice(task, "Beginning restore to '%s'",
  1320. pb->pb_plugin->plg_name);
  1321. LDAPDebug(LDAP_DEBUG_ANY, "Beginning restore to '%s'\n",
  1322. pb->pb_plugin->plg_name, 0, 0);
  1323. rv = (*pb->pb_plugin->plg_archive2db)(pb);
  1324. if (rv != 0) {
  1325. slapi_task_log_notice(task, "Restore failed (error %d)", rv);
  1326. slapi_task_log_status(task, "Restore failed (error %d)", rv);
  1327. LDAPDebug(LDAP_DEBUG_ANY, "Restore failed (error %d)\n", rv, 0, 0);
  1328. } else {
  1329. slapi_task_log_notice(task, "Restore finished.");
  1330. slapi_task_log_status(task, "Restore finished.");
  1331. LDAPDebug(LDAP_DEBUG_ANY, "Restore finished.\n", 0, 0, 0);
  1332. }
  1333. slapi_task_finish(task, rv);
  1334. slapi_ch_free((void **)&pb->pb_seq_val);
  1335. slapi_ch_free_string(&pb->pb_instance_name);
  1336. slapi_pblock_destroy(pb);
  1337. g_decr_active_threadcnt();
  1338. }
  1339. static int task_restore_add(Slapi_PBlock *pb, Slapi_Entry *e,
  1340. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
  1341. {
  1342. Slapi_Backend *be = NULL;
  1343. const char *instance_name = NULL;
  1344. const char *archive_dir = NULL;
  1345. const char *my_database_type = NULL;
  1346. const char *database_type = "ldbm database";
  1347. char *cookie = NULL;
  1348. int rv = SLAPI_DSE_CALLBACK_OK;
  1349. Slapi_PBlock *mypb = NULL;
  1350. Slapi_Task *task = NULL;
  1351. PRThread *thread = NULL;
  1352. *returncode = LDAP_SUCCESS;
  1353. if (fetch_attr(e, "cn", NULL) == NULL) {
  1354. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1355. rv = SLAPI_DSE_CALLBACK_ERROR;
  1356. goto out;
  1357. }
  1358. /* archive dir name */
  1359. if ((archive_dir = fetch_attr(e, "nsArchiveDir", NULL)) == NULL) {
  1360. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1361. rv = SLAPI_DSE_CALLBACK_ERROR;
  1362. goto out;
  1363. }
  1364. /* database type */
  1365. my_database_type = fetch_attr(e, "nsDatabaseType", NULL);
  1366. if (NULL != my_database_type)
  1367. database_type = my_database_type;
  1368. instance_name = fetch_attr(e, "nsInstance", NULL);
  1369. /* get backend that has archive2db and the database type matches. */
  1370. cookie = NULL;
  1371. be = slapi_get_first_backend (&cookie);
  1372. while (be) {
  1373. if (NULL != be->be_database->plg_archive2db &&
  1374. !strcasecmp(database_type, be->be_database->plg_name))
  1375. break;
  1376. be = (backend *)slapi_get_next_backend (cookie);
  1377. }
  1378. slapi_ch_free((void **)&cookie);
  1379. if (NULL == be || NULL == be->be_database->plg_archive2db) {
  1380. LDAPDebug(LDAP_DEBUG_ANY,
  1381. "ERROR: no archive2db function defined.\n", 0, 0, 0);
  1382. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1383. rv = SLAPI_DSE_CALLBACK_ERROR;
  1384. goto out;
  1385. }
  1386. /* refuse to do an export on pre-V3 plugins. plugin api V3 is the one
  1387. * for DS 5.0 where the import/export stuff changed a lot.
  1388. */
  1389. if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
  1390. LDAPDebug(LDAP_DEBUG_ANY, "can't perform an restore with pre-V3 "
  1391. "backend plugin %s\n", be->be_database->plg_name, 0, 0);
  1392. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1393. rv = SLAPI_DSE_CALLBACK_ERROR;
  1394. goto out;
  1395. }
  1396. /* allocate new task now */
  1397. task = slapi_new_task(slapi_entry_get_ndn(e));
  1398. if (task == NULL) {
  1399. LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
  1400. *returncode = LDAP_OPERATIONS_ERROR;
  1401. rv = SLAPI_DSE_CALLBACK_ERROR;
  1402. goto out;
  1403. }
  1404. mypb = slapi_pblock_new();
  1405. if (mypb == NULL) {
  1406. *returncode = LDAP_OPERATIONS_ERROR;
  1407. rv = SLAPI_DSE_CALLBACK_ERROR;
  1408. goto out;
  1409. }
  1410. mypb->pb_seq_val = slapi_ch_strdup(archive_dir);
  1411. mypb->pb_plugin = be->be_database;
  1412. if (NULL != instance_name)
  1413. mypb->pb_instance_name = slapi_ch_strdup(instance_name);
  1414. mypb->pb_task = task;
  1415. mypb->pb_task_flags = SLAPI_TASK_RUNNING_AS_TASK;
  1416. /* start the restore as a separate thread */
  1417. thread = PR_CreateThread(PR_USER_THREAD, task_restore_thread,
  1418. (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  1419. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
  1420. if (thread == NULL) {
  1421. LDAPDebug(LDAP_DEBUG_ANY,
  1422. "unable to create restore thread!\n", 0, 0, 0);
  1423. *returncode = LDAP_OPERATIONS_ERROR;
  1424. rv = SLAPI_DSE_CALLBACK_ERROR;
  1425. slapi_ch_free((void **)&mypb->pb_seq_val);
  1426. slapi_ch_free_string(&pb->pb_instance_name);
  1427. slapi_pblock_destroy(mypb);
  1428. goto out;
  1429. }
  1430. /* thread successful -- don't free the pb, let the thread do that. */
  1431. return SLAPI_DSE_CALLBACK_OK;
  1432. out:
  1433. if (task) {
  1434. destroy_task(1, task);
  1435. }
  1436. return rv;
  1437. }
  1438. static void task_index_thread(void *arg)
  1439. {
  1440. Slapi_PBlock *pb = (Slapi_PBlock *)arg;
  1441. Slapi_Task *task = pb->pb_task;
  1442. int rv;
  1443. g_incr_active_threadcnt();
  1444. slapi_task_begin(task, 1);
  1445. rv = (*pb->pb_plugin->plg_db2index)(pb);
  1446. if (rv != 0) {
  1447. slapi_task_log_notice(task, "Index failed (error %d)", rv);
  1448. slapi_task_log_status(task, "Index failed (error %d)", rv);
  1449. LDAPDebug(LDAP_DEBUG_ANY, "Index failed (error %d)\n", rv, 0, 0);
  1450. }
  1451. slapi_task_finish(task, rv);
  1452. charray_free(pb->pb_db2index_attrs);
  1453. slapi_ch_free((void **)&pb->pb_instance_name);
  1454. slapi_pblock_destroy(pb);
  1455. g_decr_active_threadcnt();
  1456. }
  1457. static int task_index_add(Slapi_PBlock *pb, Slapi_Entry *e,
  1458. Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
  1459. {
  1460. const char *instance_name;
  1461. int rv = SLAPI_DSE_CALLBACK_OK;
  1462. Slapi_Backend *be = NULL;
  1463. Slapi_Task *task = NULL;
  1464. Slapi_Attr *attr;
  1465. Slapi_Value *val = NULL;
  1466. char **indexlist = NULL;
  1467. int idx;
  1468. Slapi_PBlock *mypb = NULL;
  1469. PRThread *thread = NULL;
  1470. *returncode = LDAP_SUCCESS;
  1471. if (fetch_attr(e, "cn", NULL) == NULL) {
  1472. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1473. rv = SLAPI_DSE_CALLBACK_ERROR;
  1474. goto out;
  1475. }
  1476. if ((instance_name = fetch_attr(e, "nsInstance", NULL)) == NULL) {
  1477. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1478. rv = SLAPI_DSE_CALLBACK_ERROR;
  1479. goto out;
  1480. }
  1481. /* lookup the backend */
  1482. be = slapi_be_select_by_instance_name(instance_name);
  1483. if (be == NULL) {
  1484. LDAPDebug(LDAP_DEBUG_ANY, "can't import to nonexistent backend %s\n",
  1485. instance_name, 0, 0);
  1486. *returncode = LDAP_NO_SUCH_OBJECT;
  1487. return SLAPI_DSE_CALLBACK_ERROR;
  1488. }
  1489. if (be->be_database->plg_db2index == NULL) {
  1490. LDAPDebug(LDAP_DEBUG_ANY, "ERROR: no db2index function defined for "
  1491. "backend %s\n", be->be_database->plg_name, 0, 0);
  1492. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1493. return SLAPI_DSE_CALLBACK_ERROR;
  1494. }
  1495. /* normal indexes */
  1496. if (slapi_entry_attr_find(e, "nsIndexAttribute", &attr) == 0) {
  1497. for (idx = slapi_attr_first_value(attr, &val);
  1498. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  1499. const char *indexname = slapi_value_get_string(val);
  1500. char *index = slapi_ch_smprintf("t%s", indexname);
  1501. if (index != NULL) {
  1502. charray_add(&indexlist, index);
  1503. }
  1504. }
  1505. }
  1506. /* vlv indexes */
  1507. if (slapi_entry_attr_find(e, "nsIndexVlvAttribute", &attr) == 0) {
  1508. for (idx = slapi_attr_first_value(attr, &val);
  1509. idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
  1510. const char *indexname = slapi_value_get_string(val);
  1511. char *index = slapi_ch_smprintf("T%s", indexname);
  1512. if (index != NULL) {
  1513. charray_add(&indexlist, index);
  1514. }
  1515. }
  1516. }
  1517. if (NULL == indexlist) {
  1518. LDAPDebug(LDAP_DEBUG_ANY, "no index is specified!\n", 0, 0, 0);
  1519. *returncode = LDAP_OPERATIONS_ERROR;
  1520. rv = SLAPI_DSE_CALLBACK_OK;
  1521. goto out;
  1522. }
  1523. /* allocate new task now */
  1524. task = slapi_new_task(slapi_entry_get_ndn(e));
  1525. if (task == NULL) {
  1526. LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
  1527. *returncode = LDAP_OPERATIONS_ERROR;
  1528. rv = SLAPI_DSE_CALLBACK_ERROR;
  1529. goto out;
  1530. }
  1531. mypb = slapi_pblock_new();
  1532. if (mypb == NULL) {
  1533. *returncode = LDAP_OPERATIONS_ERROR;
  1534. rv = SLAPI_DSE_CALLBACK_ERROR;
  1535. goto out;
  1536. }
  1537. mypb->pb_backend = be;
  1538. mypb->pb_plugin = be->be_database;
  1539. mypb->pb_instance_name = slapi_ch_strdup(instance_name);
  1540. mypb->pb_db2index_attrs = indexlist;
  1541. mypb->pb_task = task;
  1542. mypb->pb_task_flags = SLAPI_TASK_RUNNING_AS_TASK;
  1543. /* start the db2index as a separate thread */
  1544. thread = PR_CreateThread(PR_USER_THREAD, task_index_thread,
  1545. (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  1546. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
  1547. if (thread == NULL) {
  1548. LDAPDebug(LDAP_DEBUG_ANY,
  1549. "unable to create index thread!\n", 0, 0, 0);
  1550. rv = SLAPI_DSE_CALLBACK_ERROR;
  1551. slapi_ch_free((void **)&mypb->pb_instance_name);
  1552. slapi_pblock_destroy(mypb);
  1553. goto out;
  1554. }
  1555. /* thread successful -- don't free the pb, let the thread do that. */
  1556. return SLAPI_DSE_CALLBACK_OK;
  1557. out:
  1558. if (task) {
  1559. destroy_task(1, task);
  1560. }
  1561. if (indexlist) {
  1562. charray_free(indexlist);
  1563. }
  1564. return rv;
  1565. }
  1566. static int
  1567. task_upgradedb_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter,
  1568. int *returncode, char *returntext, void *arg)
  1569. {
  1570. int rv = SLAPI_DSE_CALLBACK_OK;
  1571. Slapi_Backend *be = NULL;
  1572. Slapi_Task *task = NULL;
  1573. Slapi_PBlock mypb;
  1574. const char *archive_dir = NULL;
  1575. const char *force = NULL;
  1576. const char *database_type = "ldbm database";
  1577. const char *my_database_type = NULL;
  1578. char *cookie = NULL;
  1579. *returncode = LDAP_SUCCESS;
  1580. if (fetch_attr(e, "cn", NULL) == NULL) {
  1581. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1582. rv = SLAPI_DSE_CALLBACK_ERROR;
  1583. goto out;
  1584. }
  1585. /* archive dir name */
  1586. if ((archive_dir = fetch_attr(e, "nsArchiveDir", NULL)) == NULL) {
  1587. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1588. rv = SLAPI_DSE_CALLBACK_ERROR;
  1589. goto out;
  1590. }
  1591. /* database type */
  1592. my_database_type = fetch_attr(e, "nsDatabaseType", NULL);
  1593. if (NULL != my_database_type)
  1594. database_type = my_database_type;
  1595. /* force to reindex? */
  1596. force = fetch_attr(e, "nsForceToReindex", NULL);
  1597. /* get backend that has db2archive and the database type matches. */
  1598. cookie = NULL;
  1599. be = slapi_get_first_backend(&cookie);
  1600. while (be) {
  1601. if (NULL != be->be_database->plg_upgradedb)
  1602. break;
  1603. be = (backend *)slapi_get_next_backend (cookie);
  1604. }
  1605. slapi_ch_free((void **)&cookie);
  1606. if (NULL == be) {
  1607. LDAPDebug(LDAP_DEBUG_ANY,
  1608. "ERROR: no upgradedb is defined.\n", 0, 0, 0);
  1609. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1610. rv = SLAPI_DSE_CALLBACK_ERROR;
  1611. goto out;
  1612. }
  1613. if (NULL == be->be_database->plg_upgradedb ||
  1614. strcasecmp(database_type, be->be_database->plg_name)) {
  1615. LDAPDebug(LDAP_DEBUG_ANY,
  1616. "ERROR: no upgradedb is defined in %s.\n",
  1617. be->be_database->plg_name, 0, 0);
  1618. *returncode = LDAP_UNWILLING_TO_PERFORM;
  1619. rv = SLAPI_DSE_CALLBACK_ERROR;
  1620. goto out;
  1621. }
  1622. /* allocate new task now */
  1623. task = slapi_new_task(slapi_entry_get_ndn(e));
  1624. if (task == NULL) {
  1625. LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
  1626. *returncode = LDAP_OPERATIONS_ERROR;
  1627. rv = SLAPI_DSE_CALLBACK_ERROR;
  1628. goto out;
  1629. }
  1630. /* NGK - This could use some cleanup to use the SLAPI task API, such as slapi_task_begin() */
  1631. task->task_work = 1;
  1632. task->task_progress = 0;
  1633. memset(&mypb, 0, sizeof(mypb));
  1634. mypb.pb_backend = be;
  1635. mypb.pb_plugin = be->be_database;
  1636. if (force && 0 == strcasecmp(force, "true"))
  1637. mypb.pb_seq_type = SLAPI_UPGRADEDB_FORCE; /* force; reindex all regardless the dbversion */
  1638. mypb.pb_seq_val = slapi_ch_strdup(archive_dir);
  1639. mypb.pb_task = task;
  1640. mypb.pb_task_flags = SLAPI_TASK_RUNNING_AS_TASK;
  1641. rv = (mypb.pb_plugin->plg_upgradedb)(&mypb);
  1642. if (rv == 0) {
  1643. slapi_entry_attr_set_charptr(e, TASK_LOG_NAME, "");
  1644. slapi_entry_attr_set_charptr(e, TASK_STATUS_NAME, "");
  1645. slapi_entry_attr_set_int(e, TASK_PROGRESS_NAME, task->task_progress);
  1646. slapi_entry_attr_set_int(e, TASK_WORK_NAME, task->task_work);
  1647. }
  1648. out:
  1649. slapi_ch_free((void **)&mypb.pb_seq_val);
  1650. if (rv != 0) {
  1651. if (task)
  1652. destroy_task(1, task);
  1653. *returncode = LDAP_OPERATIONS_ERROR;
  1654. return SLAPI_DSE_CALLBACK_ERROR;
  1655. }
  1656. *returncode = LDAP_SUCCESS;
  1657. return SLAPI_DSE_CALLBACK_OK;
  1658. }
  1659. /* cleanup old tasks that may still be in the DSE from a previous session
  1660. * (this can happen if the server crashes [no matter how unlikely we like
  1661. * to think that is].)
  1662. */
  1663. void task_cleanup(void)
  1664. {
  1665. Slapi_PBlock *pb = slapi_pblock_new();
  1666. Slapi_Entry **entries = NULL;
  1667. int ret = 0, i, x;
  1668. Slapi_DN *rootDN;
  1669. slapi_search_internal_set_pb(pb, TASK_BASE_DN, LDAP_SCOPE_SUBTREE,
  1670. "(objectclass=*)", NULL, 0, NULL, NULL,
  1671. (void *)plugin_get_default_component_id(), 0);
  1672. slapi_search_internal_pb(pb);
  1673. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
  1674. if (ret != LDAP_SUCCESS) {
  1675. LDAPDebug(LDAP_DEBUG_ANY, "WARNING: entire cn=tasks tree seems to "
  1676. "be AWOL!\n", 0, 0, 0);
  1677. slapi_pblock_destroy(pb);
  1678. return;
  1679. }
  1680. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  1681. if (NULL == entries) {
  1682. LDAPDebug(LDAP_DEBUG_ANY, "WARNING: entire cn=tasks tree seems to "
  1683. "be AWOL!\n", 0, 0, 0);
  1684. slapi_pblock_destroy(pb);
  1685. return;
  1686. }
  1687. rootDN = slapi_sdn_new_dn_byval(TASK_BASE_DN);
  1688. /* rotate through entries, skipping the base dn */
  1689. for (i = 0; entries[i] != NULL; i++) {
  1690. const Slapi_DN *sdn = slapi_entry_get_sdn_const(entries[i]);
  1691. Slapi_PBlock *mypb;
  1692. Slapi_Operation *op;
  1693. if (slapi_sdn_compare(sdn, rootDN) == 0)
  1694. continue;
  1695. mypb = slapi_pblock_new();
  1696. if (mypb == NULL) {
  1697. continue;
  1698. }
  1699. slapi_delete_internal_set_pb(mypb, slapi_sdn_get_dn(sdn), NULL, NULL,
  1700. plugin_get_default_component_id(), 0);
  1701. /* Make sure these deletes don't appear in the audit and change logs */
  1702. slapi_pblock_get(mypb, SLAPI_OPERATION, &op);
  1703. operation_set_flag(op, OP_FLAG_ACTION_NOLOG);
  1704. x = 1;
  1705. slapi_pblock_set(mypb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &x);
  1706. slapi_delete_internal_pb(mypb);
  1707. slapi_pblock_destroy(mypb);
  1708. }
  1709. slapi_sdn_free(&rootDN);
  1710. slapi_free_search_results_internal(pb);
  1711. slapi_pblock_destroy(pb);
  1712. }
  1713. void task_init(void)
  1714. {
  1715. global_task_lock = PR_NewLock();
  1716. if (global_task_lock == NULL) {
  1717. LDAPDebug(LDAP_DEBUG_ANY, "unable to create global tasks lock! "
  1718. "(that's bad)\n", 0, 0, 0);
  1719. return;
  1720. }
  1721. slapi_task_register_handler("import", task_import_add);
  1722. slapi_task_register_handler("export", task_export_add);
  1723. slapi_task_register_handler("backup", task_backup_add);
  1724. slapi_task_register_handler("restore", task_restore_add);
  1725. slapi_task_register_handler("index", task_index_add);
  1726. slapi_task_register_handler("upgradedb", task_upgradedb_add);
  1727. }
  1728. /* called when the server is shutting down -- abort all existing tasks */
  1729. void task_shutdown(void)
  1730. {
  1731. Slapi_Task *task;
  1732. int found_any = 0;
  1733. /* first, cancel all tasks */
  1734. PR_Lock(global_task_lock);
  1735. shutting_down = 1;
  1736. for (task = global_task_list; task; task = task->next) {
  1737. if ((task->task_state != SLAPI_TASK_CANCELLED) &&
  1738. (task->task_state != SLAPI_TASK_FINISHED)) {
  1739. task->task_state = SLAPI_TASK_CANCELLED;
  1740. if (task->cancel) {
  1741. LDAPDebug(LDAP_DEBUG_ANY, "Cancelling task '%s'\n",
  1742. task->task_dn, 0, 0);
  1743. (*task->cancel)(task);
  1744. found_any = 1;
  1745. }
  1746. }
  1747. }
  1748. if (found_any) {
  1749. /* give any tasks 1 second to say their last rites */
  1750. DS_Sleep(PR_SecondsToInterval( 1 ));
  1751. }
  1752. while (global_task_list) {
  1753. destroy_task(0, global_task_list);
  1754. }
  1755. PR_Unlock(global_task_lock);
  1756. }