cl5_api.c 202 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123
  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. /* cl5_api.c - implementation of 5.0 style changelog API */
  42. #include <unistd.h>
  43. #include <errno.h>
  44. #include <sys/stat.h>
  45. #if defined( OS_solaris ) || defined( hpux )
  46. #include <sys/types.h>
  47. #include <sys/statvfs.h>
  48. #endif
  49. #if defined( linux )
  50. #include <sys/vfs.h>
  51. #endif
  52. #include "cl5_api.h"
  53. #include "plhash.h"
  54. #include "plstr.h"
  55. #include "db.h"
  56. #include "cl5_clcache.h" /* To use the Changelog Cache */
  57. #include "repl5.h" /* for agmt_get_consumer_rid() */
  58. #define GUARDIAN_FILE "guardian" /* name of the guardian file */
  59. #define VERSION_FILE "DBVERSION" /* name of the version file */
  60. #define MAX_TRIALS 50 /* number of retries on db operations */
  61. #define V_5 5 /* changelog entry version */
  62. #define CHUNK_SIZE 64*1024
  63. #define DBID_SIZE 64
  64. #define FILE_SEP "_" /* separates parts of the db file name */
  65. #define T_CSNSTR "csn"
  66. #define T_UNIQUEIDSTR "nsuniqueid"
  67. #define T_PARENTIDSTR "parentuniqueid"
  68. #define T_NEWSUPERIORDNSTR "newsuperiordn"
  69. #define T_NEWSUPERIORIDSTR "newsuperioruniqueid"
  70. #define T_REPLGEN "replgen"
  71. #define ENTRY_COUNT_TIME 111 /* this time is used to construct csn
  72. used to store/retrieve entry count */
  73. #define PURGE_RUV_TIME 222 /* this time is used to construct csn
  74. used to store purge RUV vector */
  75. #define MAX_RUV_TIME 333 /* this time is used to construct csn
  76. used to store upper boundary RUV vector */
  77. #define DB_EXTENSION_DB3 "db3"
  78. #define DB_EXTENSION "db4"
  79. #define HASH_BACKETS_COUNT 16 /* number of buckets in a hash table */
  80. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4100
  81. #define DEFAULT_DB_OP_FLAGS DB_AUTO_COMMIT
  82. #define DB_OPEN(oflags, db, txnid, file, database, type, flags, mode, rval) \
  83. { \
  84. if (((oflags) & DB_INIT_TXN) && ((oflags) & DB_INIT_LOG)) \
  85. { \
  86. (rval) = (db)->open((db), (txnid), (file), (database), (type), (flags)|DB_AUTO_COMMIT, (mode)); \
  87. } \
  88. else \
  89. { \
  90. (rval) = (db)->open((db), (txnid), (file), (database), (type), (flags), (mode)); \
  91. } \
  92. }
  93. #else /* older then db 41 */
  94. #define DEFAULT_DB_OP_FLAGS 0
  95. #define DB_OPEN(oflags, db, txnid, file, database, type, flags, mode, rval) \
  96. (rval) = (db)->open((db), (file), (database), (type), (flags), (mode))
  97. #endif
  98. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4000
  99. #define DB_ENV_SET_REGION_INIT(env) (env)->set_flags((env), DB_REGION_INIT, 1)
  100. #define TXN_BEGIN(env, parent_txn, tid, flags) \
  101. (env)->txn_begin((env), (parent_txn), (tid), (flags))
  102. #define TXN_COMMIT(txn, flags) (txn)->commit((txn), (flags))
  103. #define TXN_ABORT(txn) (txn)->abort(txn)
  104. #define TXN_CHECKPOINT(env, kbyte, min, flags) \
  105. (env)->txn_checkpoint((env), (kbyte), (min), (flags))
  106. #define MEMP_STAT(env, gsp, fsp, flags, malloc) \
  107. (env)->memp_stat((env), (gsp), (fsp), (flags))
  108. #define MEMP_TRICKLE(env, pct, nwrotep) \
  109. (env)->memp_trickle((env), (pct), (nwrotep))
  110. #define LOG_ARCHIVE(env, listp, flags, malloc) \
  111. (env)->log_archive((env), (listp), (flags))
  112. #define LOG_FLUSH(env, lsn) (env)->log_flush((env), (lsn))
  113. #define LOCK_DETECT(env, flags, atype, aborted) \
  114. (env)->lock_detect((env), (flags), (atype), (aborted))
  115. #if DB_VERSION_MINOR >= 4 /* i.e. 4.4 or later */
  116. #define DB_ENV_SET_TAS_SPINS(env, tas_spins) \
  117. (env)->mutex_set_tas_spins((env), (tas_spins))
  118. #else /* < 4.4 */
  119. #define DB_ENV_SET_TAS_SPINS(env, tas_spins) \
  120. (env)->set_tas_spins((env), (tas_spins))
  121. #endif /* 4.4 or later */
  122. #else /* older than db 4.0 */
  123. #define DB_ENV_SET_REGION_INIT(env) db_env_set_region_init(1)
  124. #define DB_ENV_SET_TAS_SPINS(env, tas_spins) \
  125. db_env_set_tas_spins((tas_spins))
  126. #define TXN_BEGIN(env, parent_txn, tid, flags) \
  127. txn_begin((env), (parent_txn), (tid), (flags))
  128. #define TXN_COMMIT(txn, flags) txn_commit((txn), (flags))
  129. #define TXN_ABORT(txn) txn_abort((txn))
  130. #define TXN_CHECKPOINT(env, kbyte, min, flags) \
  131. txn_checkpoint((env), (kbyte), (min), (flags))
  132. #define MEMP_TRICKLE(env, pct, nwrotep) memp_trickle((env), (pct), (nwrotep))
  133. #define LOG_FLUSH(env, lsn) log_flush((env), (lsn))
  134. #define LOCK_DETECT(env, flags, atype, aborted) \
  135. lock_detect((env), (flags), (atype), (aborted))
  136. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
  137. #define MEMP_STAT(env, gsp, fsp, flags, malloc) memp_stat((env), (gsp), (fsp))
  138. #define LOG_ARCHIVE(env, listp, flags, malloc) \
  139. log_archive((env), (listp), (flags))
  140. #else /* older than db 3.3 */
  141. #define MEMP_STAT(env, gsp, fsp, flags, malloc) \
  142. memp_stat((env), (gsp), (fsp), (malloc))
  143. #define LOG_ARCHIVE(env, listp, flags, malloc) \
  144. log_archive((env), (listp), (flags), (malloc))
  145. #endif
  146. #endif
  147. /*
  148. * The defult thread stacksize for nspr21 is 64k. For OSF, we require
  149. * a larger stacksize as actual storage allocation is higher i.e
  150. * pointers are allocated 8 bytes but lower 4 bytes are used.
  151. * The value 0 means use the default stacksize.
  152. */
  153. #if defined (OSF1) || defined (__LP64__) || defined (_LP64) /* 64-bit architectures need bigger stacks */
  154. #if defined(__hpux) && defined(__ia64)
  155. #define DEFAULT_THREAD_STACKSIZE 524288L
  156. #else
  157. #define DEFAULT_THREAD_STACKSIZE 131072L
  158. #endif
  159. #else
  160. #define DEFAULT_THREAD_STACKSIZE 0
  161. #endif
  162. #ifdef _WIN32
  163. #define FILE_CREATE_MODE S_IREAD | S_IWRITE
  164. #define DIR_CREATE_MODE 0755
  165. #else /* _WIN32 */
  166. #define FILE_CREATE_MODE S_IRUSR | S_IWUSR
  167. #define DIR_CREATE_MODE 0755
  168. #endif
  169. #define NO_DISK_SPACE 1024
  170. #define MIN_DISK_SPACE 10485760 /* 10 MB */
  171. /***** Data Definitions *****/
  172. /* possible changelog open modes */
  173. typedef enum
  174. {
  175. CL5_OPEN_NONE, /* nothing specified */
  176. CL5_OPEN_NORMAL, /* open for normal read/write use */
  177. CL5_OPEN_RESTORE_RECOVER, /* restore from archive and recover */
  178. CL5_OPEN_RESTORE, /* restore, but no recovery */
  179. CL5_OPEN_LDIF2CL, /* open as part of ldif2cl: no locking,
  180. recovery, checkpointing */
  181. CL5_OPEN_CLEAN_RECOVER /* remove env after recover open (upgrade) */
  182. } CL5OpenMode;
  183. #define DB_FILE_DELETED 0x1
  184. #define DB_FILE_INIT 0x2
  185. /* this structure represents one changelog file, Each changelog file contains
  186. changes applied to a single backend. Files are named by the database id */
  187. typedef struct cl5dbfile
  188. {
  189. char *name; /* file name (with the extension) */
  190. char *replGen; /* replica generation of the data */
  191. char *replName; /* replica name */
  192. DB *db; /* db handle to the changelog file*/
  193. int entryCount; /* number of entries in the file */
  194. int flags; /* currently used to mark the file as deleted
  195. * or as initialized */
  196. RUV *purgeRUV; /* ruv to which the file has been purged */
  197. RUV *maxRUV; /* ruv that marks the upper boundary of the data */
  198. char *semaName; /* semaphore name */
  199. PRSem *sema; /* semaphore for max concurrent cl writes */
  200. }CL5DBFile;
  201. /* structure that allows to iterate through entries to be sent to a consumer
  202. that originated on a particular supplier. */
  203. struct cl5replayiterator
  204. {
  205. Object *fileObj;
  206. CLC_Buffer *clcache; /* changelog cache */
  207. ReplicaId consumerRID; /* consumer's RID */
  208. const RUV *consumerRuv; /* consumer's update vector */
  209. Object *supplierRuvObj;/* supplier's update vector object */
  210. };
  211. typedef struct cl5iterator
  212. {
  213. DBC *cursor; /* current position in the db file */
  214. Object *file; /* handle to release db file object */
  215. }CL5Iterator;
  216. /* changelog trimming configuration */
  217. typedef struct cl5trim
  218. {
  219. time_t maxAge; /* maximum entry age in seconds */
  220. int maxEntries; /* maximum number of entries across all changelog files */
  221. PRLock* lock; /* controls access to trimming configuration */
  222. } CL5Trim;
  223. /* this structure defines 5.0 changelog internals */
  224. typedef struct cl5desc
  225. {
  226. char *dbDir; /* absolute path to changelog directory */
  227. DB_ENV *dbEnv; /* db environment shared by all db files */
  228. int dbEnvOpenFlags;/* openflag used for env->open */
  229. Objset *dbFiles; /* ref counted set of changelog files (CL5DBFile) */
  230. PRLock *fileLock; /* ensures that changelog file is not added twice */
  231. CL5OpenMode dbOpenMode; /* how we open db */
  232. CL5DBConfig dbConfig; /* database configuration params */
  233. CL5Trim dbTrim; /* trimming parameters */
  234. CL5State dbState; /* changelog current state */
  235. PRRWLock *stLock; /* lock that controls access to the changelog state */
  236. PRBool dbRmOnClose;/* indicates whether changelog should be removed when
  237. it is closed */
  238. PRBool fatalError; /* bad stuff happened like out of disk space; don't
  239. write guardian file on close - UnUsed so far */
  240. int threadCount;/* threads that globally access changelog like
  241. deadlock detection, etc. */
  242. PRLock *clLock; /* Lock associated to clVar, used to notify threads on close */
  243. PRCondVar *clCvar; /* Condition Variable used to notify threads on close */
  244. } CL5Desc;
  245. typedef void (*VFP)(void *);
  246. int g_get_shutdown(); /* declared in proto-slap.h */
  247. /***** Global Variables *****/
  248. static CL5Desc s_cl5Desc;
  249. /***** Forward Declarations *****/
  250. /* changelog initialization and cleanup */
  251. static int _cl5Open (const char *dir, const CL5DBConfig *config, CL5OpenMode openMode);
  252. static int _cl5AppInit (PRBool *didRecovery);
  253. static int _cl5DBOpen ();
  254. static void _cl5SetDefaultDBConfig ();
  255. static void _cl5SetDBConfig (const CL5DBConfig *config);
  256. static void _cl5InitDBEnv(DB_ENV *dbEnv);
  257. static int _cl5CheckDBVersion ();
  258. static int _cl5ReadDBVersion (const char *dir, char *clVersion, int buflen);
  259. static int _cl5WriteDBVersion ();
  260. static int _cl5CheckGuardian ();
  261. static int _cl5ReadGuardian (char *buff);
  262. static int _cl5WriteGuardian ();
  263. static int _cl5RemoveGuardian ();
  264. static void _cl5Close ();
  265. static int _cl5Delete (const char *dir, PRBool rmDir);
  266. static void _cl5DBClose ();
  267. /* thread management */
  268. static int _cl5DispatchDBThreads ();
  269. static int _cl5AddThread ();
  270. static void _cl5RemoveThread ();
  271. static int _cl5DeadlockMain (void *param);
  272. static int _cl5CheckpointMain (void *param);
  273. static int _cl5TrickleMain (void *param);
  274. /* functions that work with individual changelog files */
  275. static int _cl5NewDBFile (const char *replName, const char *replGen, CL5DBFile** dbFile);
  276. static int _cl5DBOpenFile (Object *replica, Object **obj, PRBool checkDups);
  277. static int _cl5DBOpenFileByReplicaName (const char *replName, const char *replGen,
  278. Object **obj, PRBool checkDups);
  279. static void _cl5DBCloseFile (void **data);
  280. static void _cl5DBDeleteFile (Object *obj);
  281. static void _cl5DBFileInitialized (Object *obj);
  282. static int _cl5GetDBFile (Object *replica, Object **obj);
  283. static int _cl5GetDBFileByReplicaName (const char *replName, const char *replGen,
  284. Object **obj);
  285. static int _cl5AddDBFile (CL5DBFile *file, Object **obj);
  286. static int _cl5CompareDBFile (Object *el1, const void *el2);
  287. static int _cl5CopyDBFiles (const char *srcDir, const char *distDir, Object **replicas);
  288. static char* _cl5Replica2FileName (Object *replica);
  289. static char* _cl5MakeFileName (const char *replName, const char *replGen);
  290. static PRBool _cl5FileName2Replica (const char *fileName, Object **replica);
  291. static int _cl5ExportFile (PRFileDesc *prFile, Object *obj);
  292. static PRBool _cl5ReplicaInList (Object *replica, Object **replicas);
  293. /* data storage and retrieval */
  294. static int _cl5Entry2DBData (const CL5Entry *entry, char **data, PRUint32 *len);
  295. static int _cl5WriteOperation(const char *replName, const char *replGen,
  296. const slapi_operation_parameters *op, PRBool local);
  297. static int _cl5GetFirstEntry (Object *obj, CL5Entry *entry, void **iterator, DB_TXN *txnid);
  298. static int _cl5GetNextEntry (CL5Entry *entry, void *iterator);
  299. static int _cl5CurrentDeleteEntry (void *iterator);
  300. static PRBool _cl5IsValidIterator (const CL5Iterator *iterator);
  301. static int _cl5GetOperation (Object *replica, slapi_operation_parameters *op);
  302. static const char* _cl5OperationType2Str (int type);
  303. static int _cl5Str2OperationType (const char *str);
  304. static void _cl5WriteString (const char *str, char **buff);
  305. static void _cl5ReadString (char **str, char **buff);
  306. static void _cl5WriteMods (LDAPMod **mods, char **buff);
  307. static void _cl5WriteMod (LDAPMod *mod, char **buff);
  308. static int _cl5ReadMods (LDAPMod ***mods, char **buff);
  309. static int _cl5ReadMod (Slapi_Mod *mod, char **buff);
  310. static int _cl5GetModsSize (LDAPMod **mods);
  311. static int _cl5GetModSize (LDAPMod *mod);
  312. static void _cl5ReadBerval (struct berval *bv, char** buff);
  313. static void _cl5WriteBerval (struct berval *bv, char** buff);
  314. static int _cl5ReadBervals (struct berval ***bv, char** buff, unsigned int size);
  315. static int _cl5WriteBervals (struct berval **bv, char** buff, unsigned int *size);
  316. /* replay iteration */
  317. #ifdef FOR_DEBUGGING
  318. static PRBool _cl5ValidReplayIterator (const CL5ReplayIterator *iterator);
  319. #endif
  320. static int _cl5PositionCursorForReplay (ReplicaId consumerRID, const RUV *consumerRuv,
  321. Object *replica, Object *fileObject, CL5ReplayIterator **iterator);
  322. static int _cl5CheckMissingCSN (const CSN *minCsn, const RUV *supplierRUV, CL5DBFile *file);
  323. /* changelog trimming */
  324. static int _cl5TrimInit ();
  325. static void _cl5TrimCleanup ();
  326. static int _cl5TrimMain (void *param);
  327. static void _cl5DoTrimming ();
  328. static void _cl5TrimFile (Object *obj, long *numToTrim);
  329. static PRBool _cl5CanTrim (time_t time, long *numToTrim);
  330. static int _cl5ReadRUV (const char *replGen, Object *obj, PRBool purge);
  331. static int _cl5WriteRUV (CL5DBFile *file, PRBool purge);
  332. static int _cl5ConstructRUV (const char *replGen, Object *obj, PRBool purge);
  333. static int _cl5UpdateRUV (Object *obj, CSN *csn, PRBool newReplica, PRBool purge);
  334. static int _cl5GetRUV2Purge2 (Object *fileObj, RUV **ruv);
  335. /* db error processing */
  336. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4300
  337. static void _cl5DBLogPrint(const DB_ENV *dbenv, const char* prefix, const char *buffer);
  338. #else /* assume 42 */
  339. static void _cl5DBLogPrint(const char* prefix, char *buffer);
  340. #endif
  341. /* bakup/recovery, import/export */
  342. static PRBool _cl5IsLogFile (const char *name);
  343. static int _cl5Recover (int open_flags, DB_ENV *dbEnv);
  344. static int _cl5LDIF2Operation (char *ldifEntry, slapi_operation_parameters *op,
  345. char **replGen);
  346. static int _cl5Operation2LDIF (const slapi_operation_parameters *op, const char *replGen,
  347. char **ldifEntry, PRInt32 *lenLDIF);
  348. /* entry count */
  349. static int _cl5GetEntryCount (CL5DBFile *file);
  350. static int _cl5WriteEntryCount (CL5DBFile *file);
  351. /* misc */
  352. static char* _cl5GetHelperEntryKey (int type, char *csnStr);
  353. static Object* _cl5GetReplica (const slapi_operation_parameters *op, const char* replGen);
  354. static int _cl5FileEndsWith(const char *filename, const char *ext);
  355. /* Callback function for libdb to spit error info into our log */
  356. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4300
  357. static void dblayer_log_print(const DB_ENV *dbenv, const char* prefix,
  358. const char *buffer)
  359. #else
  360. static void dblayer_log_print(const char* prefix, char *buffer)
  361. #endif
  362. {
  363. /* We ignore the prefix since we know who we are anyway */
  364. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "libdb: %s\n", buffer);
  365. }
  366. static PRLock *cl5_diskfull_lock = NULL;
  367. static int cl5_diskfull_flag = 0;
  368. static void cl5_set_diskfull();
  369. static void cl5_set_no_diskfull();
  370. /***** Module APIs *****/
  371. /* Name: cl5Init
  372. Description: initializes changelog module; must be called by a single thread
  373. before any other changelog function.
  374. Parameters: none
  375. Return: CL5_SUCCESS if function is successful;
  376. CL5_SYSTEM_ERROR error if NSPR call fails.
  377. */
  378. int cl5Init ()
  379. {
  380. s_cl5Desc.stLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "state_lock");
  381. if (s_cl5Desc.stLock == NULL)
  382. {
  383. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  384. "cl5Init: failed to create state lock; NSPR error - %d\n",
  385. PR_GetError ());
  386. return CL5_SYSTEM_ERROR;
  387. }
  388. if ((s_cl5Desc.clLock = PR_NewLock()) == NULL)
  389. {
  390. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  391. "cl5Init: failed to create on close lock; NSPR error - %d\n",
  392. PR_GetError ());
  393. return CL5_SYSTEM_ERROR;
  394. }
  395. if ((s_cl5Desc.clCvar = PR_NewCondVar(s_cl5Desc.clLock)) == NULL)
  396. {
  397. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  398. "cl5Init: failed to create on close cvar; NSPR error - %d\n",
  399. PR_GetError ());
  400. return CL5_SYSTEM_ERROR;
  401. }
  402. if (( clcache_init (&s_cl5Desc.dbEnv) != 0 )) {
  403. return CL5_SYSTEM_ERROR;
  404. }
  405. s_cl5Desc.dbState = CL5_STATE_CLOSED;
  406. s_cl5Desc.fatalError = PR_FALSE;
  407. s_cl5Desc.dbRmOnClose = PR_FALSE;
  408. s_cl5Desc.threadCount = 0;
  409. if (NULL == cl5_diskfull_lock)
  410. {
  411. cl5_diskfull_lock = PR_NewLock ();
  412. }
  413. return CL5_SUCCESS;
  414. }
  415. /* Name: cl5Cleanup
  416. Description: performs cleanup of the changelog module; must be called by a single
  417. thread; it closes changelog if it is still open.
  418. Parameters: none
  419. Return: none
  420. */
  421. void cl5Cleanup ()
  422. {
  423. /* close db if it is still open */
  424. if (s_cl5Desc.dbState == CL5_STATE_OPEN)
  425. {
  426. cl5Close ();
  427. }
  428. if (s_cl5Desc.stLock)
  429. PR_DestroyRWLock (s_cl5Desc.stLock);
  430. s_cl5Desc.stLock = NULL;
  431. if (cl5_diskfull_lock)
  432. {
  433. PR_DestroyLock (cl5_diskfull_lock);
  434. cl5_diskfull_lock = NULL;
  435. }
  436. memset (&s_cl5Desc, 0, sizeof (s_cl5Desc));
  437. }
  438. /* Name: cl5Open
  439. Description: opens changelog; must be called after changelog is
  440. initialized using cl5Init. It is thread safe and the second
  441. call is ignored.
  442. Parameters: dir - changelog dir
  443. config - db configuration parameters; currently not used
  444. Return: CL5_SUCCESS if successfull;
  445. CL5_BAD_DATA if invalid directory is passed;
  446. CL5_BAD_STATE if changelog is not initialized;
  447. CL5_BAD_DBVERSION if dbversion file is missing or has unexpected data
  448. CL5_SYSTEM_ERROR if NSPR error occured (during db directory creation);
  449. CL5_MEMORY_ERROR if memory allocation fails;
  450. CL5_DB_ERROR if db initialization fails.
  451. */
  452. int cl5Open (const char *dir, const CL5DBConfig *config)
  453. {
  454. int rc;
  455. if (dir == NULL)
  456. {
  457. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5Open: null directory\n");
  458. return CL5_BAD_DATA;
  459. }
  460. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  461. {
  462. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  463. "cl5Open: changelog is not initialized\n");
  464. return CL5_BAD_STATE;
  465. }
  466. /* prevent state from changing */
  467. PR_RWLock_Wlock (s_cl5Desc.stLock);
  468. /* already open - ignore */
  469. if (s_cl5Desc.dbState == CL5_STATE_OPEN)
  470. {
  471. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  472. "cl5Open: changelog already opened; request ignored\n");
  473. rc = CL5_SUCCESS;
  474. goto done;
  475. }
  476. else if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
  477. {
  478. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  479. "cl5Open: invalid state - %d\n", s_cl5Desc.dbState);
  480. rc = CL5_BAD_STATE;
  481. goto done;
  482. }
  483. rc = _cl5Open (dir, config, CL5_OPEN_NORMAL);
  484. if (rc != CL5_SUCCESS)
  485. {
  486. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  487. "cl5Open: failed to open changelog\n");
  488. goto done;
  489. }
  490. /* dispatch global threads like deadlock detection, trimming, etc */
  491. rc = _cl5DispatchDBThreads ();
  492. if (rc != CL5_SUCCESS)
  493. {
  494. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  495. "cl5Open: failed to start database monitoring threads\n");
  496. _cl5Close ();
  497. }
  498. else
  499. {
  500. s_cl5Desc.dbState = CL5_STATE_OPEN;
  501. clcache_set_config((CL5DBConfig *)config);
  502. }
  503. done:;
  504. PR_RWLock_Unlock (s_cl5Desc.stLock);
  505. return rc;
  506. }
  507. /* Name: cl5Close
  508. Description: closes changelog; waits until all threads are done using changelog;
  509. call is ignored if changelog is already closed.
  510. Parameters: none
  511. Return: CL5_SUCCESS if successful;
  512. CL5_BAD_STATE if db is not in the open or closed state;
  513. CL5_SYSTEM_ERROR if NSPR call fails;
  514. CL5_DB_ERROR if db shutdown fails
  515. */
  516. int cl5Close ()
  517. {
  518. int rc = CL5_SUCCESS;
  519. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  520. {
  521. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  522. "cl5Close: changelog is not initialized\n");
  523. return CL5_BAD_STATE;
  524. }
  525. PR_RWLock_Wlock (s_cl5Desc.stLock);
  526. /* already closed - ignore */
  527. if (s_cl5Desc.dbState == CL5_STATE_CLOSED)
  528. {
  529. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  530. "cl5Close: changelog closed; request ignored\n");
  531. PR_RWLock_Unlock (s_cl5Desc.stLock);
  532. return CL5_SUCCESS;
  533. }
  534. else if (s_cl5Desc.dbState != CL5_STATE_OPEN)
  535. {
  536. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  537. "cl5Close: invalid state - %d\n", s_cl5Desc.dbState);
  538. PR_RWLock_Unlock (s_cl5Desc.stLock);
  539. return CL5_BAD_STATE;
  540. }
  541. /* signal changelog closing to all threads */
  542. s_cl5Desc.dbState = CL5_STATE_CLOSING;
  543. PR_Lock(s_cl5Desc.clLock);
  544. PR_NotifyCondVar(s_cl5Desc.clCvar);
  545. PR_Unlock(s_cl5Desc.clLock);
  546. _cl5Close ();
  547. s_cl5Desc.dbState = CL5_STATE_CLOSED;
  548. PR_RWLock_Unlock (s_cl5Desc.stLock);
  549. return rc;
  550. }
  551. /* Name: cl5Delete
  552. Description: removes changelog; changelog must be in the closed state.
  553. Parameters: dir - changelog directory
  554. Return: CL5_SUCCESS if successful;
  555. CL5_BAD_STATE if the changelog is not in closed state;
  556. CL5_BAD_DATA if invalid directory supplied
  557. CL5_SYSTEM_ERROR if NSPR call fails
  558. */
  559. int cl5Delete (const char *dir)
  560. {
  561. int rc;
  562. if (dir == NULL)
  563. {
  564. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl, "cl5Delete: null directory\n");
  565. return CL5_BAD_DATA;
  566. }
  567. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  568. {
  569. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  570. "cl5Delete: changelog is not initialized\n");
  571. return CL5_BAD_STATE;
  572. }
  573. PR_RWLock_Wlock (s_cl5Desc.stLock);
  574. if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
  575. {
  576. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  577. "cl5Delete: invalid state - %d\n", s_cl5Desc.dbState);
  578. PR_RWLock_Unlock (s_cl5Desc.stLock);
  579. return CL5_BAD_STATE;
  580. }
  581. rc = _cl5Delete (dir, PR_TRUE /* remove changelog dir */);
  582. if (rc != CL5_SUCCESS)
  583. {
  584. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  585. "cl5Delete: failed to remove changelog\n");
  586. }
  587. PR_RWLock_Unlock (s_cl5Desc.stLock);
  588. return rc;
  589. }
  590. /* Name: cl5OpenDB
  591. Description: opens changelog file for specified file
  592. Parameters: replica - replica whose file we wish to open
  593. Return: CL5_SUCCESS if successful;
  594. CL5_BAD_STATE if the changelog is not initialized;
  595. CL5_BAD_DATA - if NULL id is supplied
  596. */
  597. int cl5OpenDB (Object *replica)
  598. {
  599. int rc;
  600. if (replica == NULL)
  601. {
  602. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5OpenDB: null replica\n");
  603. return CL5_BAD_DATA;
  604. }
  605. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  606. {
  607. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  608. "cl5OpenDB: changelog is not initialized\n");
  609. return CL5_BAD_STATE;
  610. }
  611. /* make sure that changelog stays open while operation is in progress */
  612. rc = _cl5AddThread ();
  613. if (rc != CL5_SUCCESS)
  614. return rc;
  615. rc = _cl5DBOpenFile (replica, NULL /* file object */, PR_TRUE /* check for duplicates */);
  616. _cl5RemoveThread ();
  617. return rc;
  618. }
  619. /* Name: cl5CloseDB
  620. Description: closes changelog file for the specified replica
  621. Parameters: replica - replica whose file we wish to close
  622. Return: CL5_SUCCESS if successful;
  623. CL5_BAD_STATE if the changelog is not initialized;
  624. CL5_BAD_DATA - if NULL id is supplied
  625. CL5_NOTFOUND - nothing is known about specified database
  626. */
  627. int cl5CloseDB (Object *replica)
  628. {
  629. int rc;
  630. Object *obj;
  631. if (replica == NULL)
  632. {
  633. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5CloseDB: null replica\n");
  634. return CL5_BAD_DATA;
  635. }
  636. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  637. {
  638. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  639. "cl5CloseDB: changelog is not initialized\n");
  640. return CL5_BAD_STATE;
  641. }
  642. /* make sure that changelog is open while operation is in progress */
  643. rc = _cl5AddThread ();
  644. if (rc != CL5_SUCCESS)
  645. return rc;
  646. rc = _cl5GetDBFile (replica, &obj);
  647. if (rc == CL5_SUCCESS)
  648. {
  649. rc = objset_remove_obj(s_cl5Desc.dbFiles, obj);
  650. object_release (obj);
  651. }
  652. else
  653. {
  654. Replica *r;
  655. r = (Replica*)object_get_data (replica);
  656. PR_ASSERT (r);
  657. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  658. "cl5CloseDB: failed to close file for replica at (%s)\n",
  659. slapi_sdn_get_dn (replica_get_root (r)));
  660. }
  661. _cl5RemoveThread ();
  662. return rc;
  663. }
  664. /* Name: cl5DeleteDB
  665. Description: asynchronously removes changelog file for the specified replica.
  666. The file is physically removed when it is no longer in use.
  667. This function is called when a backend is removed or reloaded.
  668. Parameters: replica - replica whose file we wish to delete
  669. Return: CL5_SUCCESS if successful;
  670. CL5_BAD_STATE if the changelog is not initialized;
  671. CL5_BAD_DATA - if NULL id is supplied
  672. CL5_NOTFOUND - nothing is known about specified database
  673. */
  674. int cl5DeleteDB (Object *replica)
  675. {
  676. Object *obj;
  677. int rc;
  678. if (replica == NULL)
  679. {
  680. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  681. "cl5DeleteDB: invalid database id\n");
  682. return CL5_BAD_DATA;
  683. }
  684. /* changelog is not initialized */
  685. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  686. {
  687. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDB: "
  688. "changelog is not initialized\n");
  689. return CL5_BAD_STATE;
  690. }
  691. /* make sure that changelog stays open while operation is in progress */
  692. rc = _cl5AddThread ();
  693. if (rc != CL5_SUCCESS)
  694. return rc;
  695. rc = _cl5GetDBFile (replica, &obj);
  696. if (rc == CL5_SUCCESS)
  697. {
  698. _cl5DBDeleteFile (obj);
  699. }
  700. else
  701. {
  702. Replica *r = (Replica*)object_get_data (replica);
  703. PR_ASSERT (r);
  704. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDB: "
  705. "file for replica at (%s) not found\n",
  706. slapi_sdn_get_dn (replica_get_root (r)));
  707. }
  708. _cl5RemoveThread ();
  709. return rc;
  710. }
  711. /* Name: cl5DeleteDBSync
  712. Description: The same as cl5DeleteDB except the function does not return
  713. until the file is removed.
  714. */
  715. int cl5DeleteDBSync (Object *replica)
  716. {
  717. Object *obj;
  718. int rc;
  719. CL5DBFile *file;
  720. char fName [MAXPATHLEN + 1];
  721. if (replica == NULL)
  722. {
  723. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  724. "cl5DeleteDBSync: invalid database id\n");
  725. return CL5_BAD_DATA;
  726. }
  727. /* changelog is not initialized */
  728. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  729. {
  730. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDBSync: "
  731. "changelog is not initialized\n");
  732. return CL5_BAD_STATE;
  733. }
  734. /* make sure that changelog stays open while operation is in progress */
  735. rc = _cl5AddThread ();
  736. if (rc != CL5_SUCCESS)
  737. return rc;
  738. rc = _cl5GetDBFile (replica, &obj);
  739. if (rc == CL5_SUCCESS)
  740. {
  741. file = (CL5DBFile*)object_get_data (obj);
  742. PR_ASSERT (file);
  743. PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, file->name);
  744. _cl5DBDeleteFile (obj);
  745. /* wait until the file is gone */
  746. while (PR_Access (fName, PR_ACCESS_EXISTS) == PR_SUCCESS)
  747. {
  748. DS_Sleep (PR_MillisecondsToInterval(100));
  749. }
  750. }
  751. else
  752. {
  753. Replica *r = (Replica*)object_get_data (replica);
  754. PR_ASSERT (r);
  755. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDBSync: "
  756. "file for replica at (%s) not found\n",
  757. slapi_sdn_get_dn (replica_get_root (r)));
  758. }
  759. _cl5RemoveThread ();
  760. return rc;
  761. }
  762. /* Name: cl5GetUpperBoundRUV
  763. Description: retrieves vector for that represnts the upper bound of the changes for a replica.
  764. Parameters: r - replica for which the purge vector is requested
  765. ruv - contains a copy of the purge ruv if function is successful;
  766. unchanged otherwise. It is responsobility pf the caller to free
  767. the ruv when it is no longer is in use
  768. Return: CL5_SUCCESS if function is successfull
  769. CL5_BAD_STATE if the changelog is not initialized;
  770. CL5_BAD_DATA - if NULL id is supplied
  771. CL5_NOTFOUND, if changelog file for replica is not found
  772. */
  773. int cl5GetUpperBoundRUV (Replica *r, RUV **ruv)
  774. {
  775. int rc;
  776. Object *r_obj, *file_obj;
  777. CL5DBFile *file;
  778. if (r == NULL || ruv == NULL)
  779. {
  780. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  781. "cl5GetUpperBoundRUV: invalid parameters\n");
  782. return CL5_BAD_DATA;
  783. }
  784. /* changelog is not initialized */
  785. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  786. {
  787. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5GetUpperBoundRUV: "
  788. "changelog is not initialized\n");
  789. return CL5_BAD_STATE;
  790. }
  791. /* make sure that changelog stays open while operation is in progress */
  792. rc = _cl5AddThread ();
  793. if (rc != CL5_SUCCESS)
  794. return rc;
  795. /* create a temporary replica object because of the interface we have */
  796. r_obj = object_new (r, NULL);
  797. rc = _cl5GetDBFile (r_obj, &file_obj);
  798. if (rc == CL5_SUCCESS)
  799. {
  800. file = (CL5DBFile*)object_get_data (file_obj);
  801. PR_ASSERT (file && file->maxRUV);
  802. *ruv = ruv_dup (file->maxRUV);
  803. object_release (file_obj);
  804. }
  805. else
  806. {
  807. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5GetUpperBoundRUV: "
  808. "could not find DB object for replica\n");
  809. }
  810. object_release (r_obj);
  811. _cl5RemoveThread ();
  812. return rc;
  813. }
  814. /* Name: cl5Backup
  815. Description: makes a backup of the changelog including *.db2,
  816. log files, and dbversion. Can be called with the changelog in either open or
  817. closed state.
  818. Parameters: bkDir - directory to which the data is backed up;
  819. created if it does not exist
  820. replicas - optional list of replicas whose changes should be backed up;
  821. if the list is NULL, entire changelog is backed up.
  822. Return: CL5_SUCCESS if function is successful;
  823. CL5_BAD_DATA if invalid directory is passed;
  824. CL5_BAD_STATE if changelog has not been initialized;
  825. CL5_DB_ERROR if db call fails;
  826. CL5_SYSTEM_ERROR if NSPR call or file copy failes.
  827. */
  828. int cl5Backup (const char *bkDir, Object **replicas)
  829. {
  830. int rc;
  831. char **list = NULL;
  832. char **logFile;
  833. char srcFile [MAXPATHLEN + 1];
  834. char destFile[MAXPATHLEN + 1];
  835. DB_TXN *txn = NULL;
  836. if (bkDir == NULL)
  837. {
  838. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5Backup: null backup directory\n");
  839. return CL5_BAD_DATA;
  840. }
  841. /* changelog must be initialized */
  842. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  843. {
  844. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  845. "cl5Backup: changelog is not initialized\n");
  846. return CL5_BAD_STATE;
  847. }
  848. /* make sure that changelog stays open while operation is in progress */
  849. rc = _cl5AddThread ();
  850. if (rc != CL5_SUCCESS)
  851. return rc;
  852. /* create backup directory if necessary */
  853. rc = cl5CreateDirIfNeeded (bkDir);
  854. if (rc != CL5_SUCCESS)
  855. {
  856. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  857. "cl5Backup: failed to create backup directory\n");
  858. goto done;
  859. }
  860. /* start transaction to tempararily prevent transaction log
  861. from being trimmed
  862. */
  863. rc = TXN_BEGIN(s_cl5Desc.dbEnv, NULL /*pid*/, &txn, 0);
  864. if (rc != 0)
  865. {
  866. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  867. "cl5Backup: failed to begin transaction; db error - %d %s\n",
  868. rc, db_strerror(rc));
  869. rc = CL5_DB_ERROR;
  870. goto done;
  871. }
  872. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  873. "cl5Backup: starting changelog backup from %s to %s ...\n", s_cl5Desc.dbDir, bkDir);
  874. /* The following files are backed up: *.<dbext>, log files, dbversion file */
  875. /* copy db file */
  876. /* ONREPL currently, list of replicas is ignored because db code can't handle
  877. discrepancy between transaction log and present files; should be fixed before 5.0 ships */
  878. rc = _cl5CopyDBFiles (s_cl5Desc.dbDir, bkDir, replicas);
  879. if (rc != CL5_SUCCESS)
  880. {
  881. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  882. "cl5Backup : failed to copy database files from %s to %s\n", s_cl5Desc.dbDir, bkDir);
  883. goto done;
  884. }
  885. /* copy db log files */
  886. rc = LOG_ARCHIVE(s_cl5Desc.dbEnv, &list, DB_ARCH_LOG, (void *)slapi_ch_malloc);
  887. if (rc != 0)
  888. {
  889. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  890. "cl5Backup: failed to get list of log files; db error - %d %s\n",
  891. rc, db_strerror(rc));
  892. rc = CL5_SYSTEM_ERROR;
  893. goto done;
  894. }
  895. if (list)
  896. {
  897. logFile = list;
  898. while (*logFile)
  899. {
  900. PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, *logFile);
  901. PR_snprintf(destFile, MAXPATHLEN, "%s/%s", bkDir, *logFile);
  902. rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
  903. if (rc != 0)
  904. {
  905. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  906. "cl5Backup: failed to copy %s\n", *logFile);
  907. rc = CL5_SYSTEM_ERROR;
  908. goto done;
  909. }
  910. logFile ++;
  911. }
  912. slapi_ch_free((void **)&list);
  913. }
  914. /* now, copy the version file */
  915. PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, VERSION_FILE);
  916. PR_snprintf(destFile, MAXPATHLEN, "%s/%s", bkDir, VERSION_FILE);
  917. rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
  918. if (rc != 0)
  919. {
  920. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  921. "cl5Backup: failed to copy %s\n", VERSION_FILE);
  922. rc = CL5_SYSTEM_ERROR;
  923. goto done;
  924. }
  925. rc = CL5_SUCCESS;
  926. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  927. "cl5Backup: changelog backup is finished \n");
  928. done:;
  929. if (txn && TXN_ABORT (txn) != 0)
  930. {
  931. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  932. "cl5Backup: failed to abort transaction; db error - %d %s\n",
  933. rc, db_strerror(rc));
  934. rc = CL5_DB_ERROR;
  935. }
  936. _cl5RemoveThread ();
  937. return rc;
  938. }
  939. /* Name: cl5Restore
  940. Description: restores changelog from the backed up copy. Changelog must be ibnitalized and closed.
  941. Parameters: clDir - changelog dir
  942. bkDir - directory that contains the backup
  943. replicas - optional list of replicas whose changes should be recovered;
  944. if the list is NULL, entire changelog is recovered.
  945. Return: CL5_SUCCESS if function is successfull;
  946. CL5_BAD_DATA if invalid parameter is passed;
  947. CL5_BAD_STATE if changelog is open or not initialized;
  948. CL5_DB_ERROR if db call fails;
  949. CL5_SYSTEM_ERROR if NSPR call of file copy fails
  950. */
  951. int cl5Restore (const char *clDir, const char *bkDir, Object **replicas)
  952. {
  953. int rc;
  954. char srcFile[MAXPATHLEN + 1];
  955. char destFile[MAXPATHLEN + 1];
  956. PRDir *prDir;
  957. PRDirEntry *prDirEntry;
  958. int seenLog = 0; /* Tells us if we restored any logfiles */
  959. if (clDir == NULL || bkDir == NULL)
  960. {
  961. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5Restore: null parameter\n");
  962. return CL5_BAD_DATA;
  963. }
  964. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  965. {
  966. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  967. "cl5Restore: changelog is not initialized\n");
  968. return CL5_BAD_STATE;
  969. }
  970. /* prevent state change while recovery is in progress */
  971. PR_RWLock_Wlock (s_cl5Desc.stLock);
  972. if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
  973. {
  974. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  975. "cl5Restore: changelog must be closed\n");
  976. PR_RWLock_Unlock (s_cl5Desc.stLock);
  977. return CL5_BAD_STATE;
  978. }
  979. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  980. "cl5Restore: starting changelog recovery from %s to %s ...\n", bkDir, clDir);
  981. /* delete current changelog content */
  982. rc = _cl5Delete (clDir, PR_FALSE);
  983. if (rc != CL5_SUCCESS)
  984. {
  985. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  986. "cl5Restore: failed to remove changelog\n");
  987. goto done;
  988. }
  989. /* We copy the files over from the staging area */
  990. prDir = PR_OpenDir(bkDir);
  991. if (prDir == NULL)
  992. {
  993. rc = CL5_SYSTEM_ERROR;
  994. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  995. "cl5Restore: unable to access backup directory %s; NSPR error - %d\n",
  996. bkDir, PR_GetError ());
  997. goto done;
  998. }
  999. while (NULL != (prDirEntry = PR_ReadDir(prDir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
  1000. {
  1001. if (NULL == prDirEntry->name) /* NSPR doesn't behave like the docs say it should */
  1002. {
  1003. break;
  1004. }
  1005. /* Log files have names of the form "log.xxxxx". We detect these by looking for
  1006. the prefix "log." and the lack of the ".<dbext>" suffix */
  1007. seenLog |= _cl5IsLogFile(prDirEntry->name);
  1008. /* ONREPL currently, list of replicas is ignored because db code can't handle discrepancy
  1009. between transaction log and present files; this should change before 5.0 ships */
  1010. PR_snprintf(destFile, MAXPATHLEN, "%s/%s", clDir, prDirEntry->name);
  1011. PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", bkDir, prDirEntry->name);
  1012. rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
  1013. if (rc != 0)
  1014. {
  1015. rc = CL5_SYSTEM_ERROR;
  1016. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1017. "cl5Restore: failed to copy %s\n", prDirEntry->name);
  1018. PR_CloseDir(prDir);
  1019. goto done;
  1020. }
  1021. }
  1022. PR_CloseDir(prDir);
  1023. /* now open and close changelog to create all necessary files */
  1024. if (seenLog)
  1025. rc = _cl5Open (clDir, NULL, CL5_OPEN_RESTORE_RECOVER);
  1026. else
  1027. rc = _cl5Open (clDir, NULL, CL5_OPEN_RESTORE);
  1028. if (rc == CL5_SUCCESS)
  1029. {
  1030. _cl5Close ();
  1031. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  1032. "cl5Restore: changelog recovery is finished \n");
  1033. }
  1034. else
  1035. {
  1036. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1037. "cl5Restore: failed open changelog after recovery\n");
  1038. }
  1039. done:;
  1040. PR_RWLock_Unlock (s_cl5Desc.stLock);
  1041. return rc;
  1042. }
  1043. /* Name: cl5ExportLDIF
  1044. Description: dumps changelog to an LDIF file; changelog can be open or closed.
  1045. Parameters: clDir - changelog dir
  1046. ldifFile - full path to ldif file to write
  1047. replicas - optional list of replicas whose changes should be exported;
  1048. if the list is NULL, entire changelog is exported.
  1049. Return: CL5_SUCCESS if function is successfull;
  1050. CL5_BAD_DATA if invalid parameter is passed;
  1051. CL5_BAD_STATE if changelog is not initialized;
  1052. CL5_DB_ERROR if db api fails;
  1053. CL5_SYSTEM_ERROR if NSPR call fails;
  1054. CL5_MEMORY_ERROR if memory allocation fials.
  1055. */
  1056. int cl5ExportLDIF (const char *ldifFile, Object **replicas)
  1057. {
  1058. int i;
  1059. int rc;
  1060. PRFileDesc *prFile = NULL;
  1061. Object *obj;
  1062. if (ldifFile == NULL)
  1063. {
  1064. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1065. "cl5ExportLDIF: null ldif file name\n");
  1066. return CL5_BAD_DATA;
  1067. }
  1068. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1069. {
  1070. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1071. "cl5ExportLDIF: changelog is not initialized\n");
  1072. return CL5_BAD_STATE;
  1073. }
  1074. /* make sure that changelog is open while operation is in progress */
  1075. rc = _cl5AddThread ();
  1076. if (rc != CL5_SUCCESS)
  1077. return rc;
  1078. prFile = PR_Open (ldifFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600);
  1079. if (prFile == NULL)
  1080. {
  1081. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1082. "cl5ExportLDIF: failed to open (%s) file; NSPR error - %d\n",
  1083. ldifFile, PR_GetError ());
  1084. rc = CL5_SYSTEM_ERROR;
  1085. goto done;
  1086. }
  1087. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  1088. "cl5ExportLDIF: starting changelog export to (%s) ...\n", ldifFile);
  1089. if (replicas) /* export only selected files */
  1090. {
  1091. for (i = 0; replicas[i]; i++)
  1092. {
  1093. rc = _cl5GetDBFile (replicas[i], &obj);
  1094. if (rc == CL5_SUCCESS)
  1095. {
  1096. rc = _cl5ExportFile (prFile, obj);
  1097. object_release (obj);
  1098. }
  1099. else
  1100. {
  1101. Replica *r = (Replica*)object_get_data (replicas[i]);
  1102. PR_ASSERT (r);
  1103. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5ExportLDIF: "
  1104. "failed to locate changelog file for replica at (%s)\n",
  1105. slapi_sdn_get_dn (replica_get_root (r)));
  1106. }
  1107. }
  1108. }
  1109. else /* export all files */
  1110. {
  1111. for (obj = objset_first_obj(s_cl5Desc.dbFiles); obj;
  1112. obj = objset_next_obj(s_cl5Desc.dbFiles, obj))
  1113. {
  1114. rc = _cl5ExportFile (prFile, obj);
  1115. object_release (obj);
  1116. }
  1117. }
  1118. rc = CL5_SUCCESS;
  1119. done:;
  1120. _cl5RemoveThread ();
  1121. if (rc == CL5_SUCCESS)
  1122. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  1123. "cl5ExportLDIF: changelog export is finished.\n");
  1124. if (prFile)
  1125. PR_Close (prFile);
  1126. return rc;
  1127. }
  1128. /* Name: cl5ImportLDIF
  1129. Description: imports ldif file into changelog; changelog must be in the closed state
  1130. Parameters: clDir - changelog dir
  1131. ldifFile - absolute path to the ldif file to import
  1132. replicas - optional list of replicas whose data should be imported;
  1133. if the list is NULL, all data in the file is imported.
  1134. Return: CL5_SUCCESS if function is successfull;
  1135. CL5_BAD_DATA if invalid parameter is passed;
  1136. CL5_BAD_STATE if changelog is open or not inititalized;
  1137. CL5_DB_ERROR if db api fails;
  1138. CL5_SYSTEM_ERROR if NSPR call fails;
  1139. CL5_MEMORY_ERROR if memory allocation fials.
  1140. */
  1141. int cl5ImportLDIF (const char *clDir, const char *ldifFile, Object **replicas)
  1142. {
  1143. #if defined(USE_OPENLDAP)
  1144. LDIFFP *file = NULL;
  1145. int buflen;
  1146. #else
  1147. FILE *file = NULL;
  1148. #endif
  1149. int rc;
  1150. char *buff = NULL;
  1151. int lineno = 0;
  1152. slapi_operation_parameters op;
  1153. Object *replica = NULL;
  1154. char *replGen = NULL;
  1155. /* validate params */
  1156. if (ldifFile == NULL)
  1157. {
  1158. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1159. "cl5ImportLDIF: null ldif file name\n");
  1160. return CL5_BAD_DATA;
  1161. }
  1162. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1163. {
  1164. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1165. "cl5ImportLDIF: changelog is not initialized\n");
  1166. return CL5_BAD_STATE;
  1167. }
  1168. /* make sure that nobody change changelog state while import is in progress */
  1169. PR_RWLock_Wlock (s_cl5Desc.stLock);
  1170. /* make sure changelog is closed */
  1171. if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
  1172. {
  1173. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1174. "cl5ImportLDIF: invalid state - %d \n", s_cl5Desc.dbState);
  1175. PR_RWLock_Unlock (s_cl5Desc.stLock);
  1176. return CL5_BAD_STATE;
  1177. }
  1178. /* open LDIF file */
  1179. #if defined(USE_OPENLDAP)
  1180. file = ldif_open (ldifFile, "r");
  1181. #else
  1182. file = fopen (ldifFile, "r"); /* XXXggood Does fopen reliably work if > 255 files open? */
  1183. #endif
  1184. if (file == NULL)
  1185. {
  1186. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1187. "cl5ImportLDIF: failed to open (%s) ldif file; system error - %d\n",
  1188. ldifFile, errno);
  1189. rc = CL5_SYSTEM_ERROR;
  1190. goto done;
  1191. }
  1192. /* remove changelog */
  1193. rc = _cl5Delete (clDir, PR_FALSE);
  1194. if (rc != CL5_SUCCESS)
  1195. {
  1196. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1197. "cl5ImportLDIF: failed to remove changelog\n");
  1198. goto done;
  1199. }
  1200. /* open changelog */
  1201. rc = _cl5Open (clDir, NULL, CL5_OPEN_LDIF2CL);
  1202. if (rc != CL5_SUCCESS)
  1203. {
  1204. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1205. "cl5ImportLDIF: failed to open changelog\n");
  1206. goto done;
  1207. }
  1208. /* read entries and write them to changelog */
  1209. #if defined(USE_OPENLDAP)
  1210. while (ldif_read_record( file, &lineno, &buff, &buflen ))
  1211. #else
  1212. while ((buff = ldif_get_entry( file, &lineno )) != NULL)
  1213. #endif
  1214. {
  1215. rc = _cl5LDIF2Operation (buff, &op, &replGen);
  1216. slapi_ch_free_string(&buff);
  1217. if (rc != CL5_SUCCESS)
  1218. {
  1219. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1220. "cl5ImportLDIF: failed to convert LDIF fragment to LDAP operation; "
  1221. "end of fragment line number - %d\n", lineno);
  1222. goto done;
  1223. }
  1224. /* if we perform selective import, check if the operation should be wriiten to changelog */
  1225. replica = _cl5GetReplica (&op, replGen);
  1226. if (replica == NULL)
  1227. {
  1228. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1229. "cl5ImportLDIF: failed to locate replica for target dn (%s) and "
  1230. "replica generation %s\n", op.target_address.dn, replGen);
  1231. slapi_ch_free_string(&replGen);
  1232. operation_parameters_done (&op);
  1233. goto done;
  1234. }
  1235. if (!replicas || _cl5ReplicaInList (replica, replicas))
  1236. {
  1237. /* write operation creates the file if it does not exist */
  1238. rc = _cl5WriteOperation (replica_get_name ((Replica*)object_get_data(replica)),
  1239. replGen, &op, 1);
  1240. if (rc != CL5_SUCCESS)
  1241. {
  1242. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1243. "cl5ImportLDIF: failed to write operation to the changelog\n");
  1244. object_release (replica);
  1245. slapi_ch_free_string(&replGen);
  1246. operation_parameters_done (&op);
  1247. goto done;
  1248. }
  1249. }
  1250. object_release (replica);
  1251. slapi_ch_free_string(&replGen);
  1252. operation_parameters_done (&op);
  1253. }
  1254. done:;
  1255. if (file) {
  1256. #if defined(USE_OPENLDAP)
  1257. ldif_close(file);
  1258. #else
  1259. fclose(file);
  1260. #endif
  1261. }
  1262. _cl5Close ();
  1263. PR_RWLock_Unlock (s_cl5Desc.stLock);
  1264. return rc;
  1265. }
  1266. /* Name: cl5GetState
  1267. Description: returns database state
  1268. Parameters: none
  1269. Return: changelog state
  1270. */
  1271. int cl5GetState ()
  1272. {
  1273. return s_cl5Desc.dbState;
  1274. }
  1275. /* Name: cl5ConfigTrimming
  1276. Description: sets changelog trimming parameters; changelog must be open.
  1277. Parameters: maxEntries - maximum number of entries in the chnagelog (in all files);
  1278. maxAge - maximum entry age;
  1279. Return: CL5_SUCCESS if successful;
  1280. CL5_BAD_STATE if changelog is not open
  1281. */
  1282. int cl5ConfigTrimming (int maxEntries, const char *maxAge)
  1283. {
  1284. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1285. {
  1286. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1287. "cl5ConfigTrimming: changelog is not initialized\n");
  1288. return CL5_BAD_STATE;
  1289. }
  1290. /* make sure changelog is not closed while trimming configuration
  1291. is updated.*/
  1292. if (CL5_SUCCESS != _cl5AddThread ()) {
  1293. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1294. "cl5ConfigTrimming: could not start changelog trimming thread\n");
  1295. return CL5_BAD_STATE;
  1296. }
  1297. PR_Lock (s_cl5Desc.dbTrim.lock);
  1298. if (maxAge)
  1299. {
  1300. /* don't ignore this argument */
  1301. if (strcmp (maxAge, CL5_STR_IGNORE) != 0)
  1302. {
  1303. s_cl5Desc.dbTrim.maxAge = age_str2time (maxAge);
  1304. }
  1305. }
  1306. else
  1307. {
  1308. /* unlimited */
  1309. s_cl5Desc.dbTrim.maxAge = 0;
  1310. }
  1311. if (maxEntries != CL5_NUM_IGNORE)
  1312. {
  1313. s_cl5Desc.dbTrim.maxEntries = maxEntries;
  1314. }
  1315. PR_Unlock (s_cl5Desc.dbTrim.lock);
  1316. _cl5RemoveThread ();
  1317. return CL5_SUCCESS;
  1318. }
  1319. /* Name: cl5GetOperation
  1320. Description: retireves operation specified by its csn and databaseid
  1321. Parameters: op - must contain csn and databaseid; the rest of data is
  1322. filled if function is successfull
  1323. Return: CL5_SUCCESS if function is successfull;
  1324. CL5_BAD_DATA if invalid op is passed;
  1325. CL5_BAD_STATE if db has not been initialized;
  1326. CL5_NOTFOUND if entry was not found;
  1327. CL5_DB_ERROR if any other db error occured;
  1328. CL5_BADFORMAT if db data format does not match entry format.
  1329. */
  1330. int cl5GetOperation (Object *replica, slapi_operation_parameters *op)
  1331. {
  1332. int rc;
  1333. char *agmt_name;
  1334. agmt_name = get_thread_private_agmtname();
  1335. if (replica == NULL)
  1336. {
  1337. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5GetOperation: NULL replica\n");
  1338. return CL5_BAD_DATA;
  1339. }
  1340. if (op == NULL)
  1341. {
  1342. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5GetOperation: NULL operation\n");
  1343. return CL5_BAD_DATA;
  1344. }
  1345. if (op->csn == NULL)
  1346. {
  1347. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "%s: cl5GetOperation: operation contains no CSN\n", agmt_name);
  1348. return CL5_BAD_DATA;
  1349. }
  1350. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1351. {
  1352. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1353. "%s: cl5GetOperation: changelog is not initialized\n", agmt_name);
  1354. return CL5_BAD_STATE;
  1355. }
  1356. /* make sure that changelog is open while operation is in progress */
  1357. rc = _cl5AddThread ();
  1358. if (rc != CL5_SUCCESS)
  1359. return rc;
  1360. rc = _cl5GetOperation (replica, op);
  1361. _cl5RemoveThread ();
  1362. return rc;
  1363. }
  1364. /* Name: cl5GetFirstOperation
  1365. Description: retrieves first operation for a particular database
  1366. replica - replica for which the operation should be retrieved.
  1367. Parameters: op - buffer to store the operation;
  1368. iterator - to be passed to the call to cl5GetNextOperation
  1369. Return: CL5_SUCCESS, if successful
  1370. CL5_BADDATA, if operation is NULL
  1371. CL5_BAD_STATE, if changelog is not open
  1372. CL5_DB_ERROR, if db call fails
  1373. */
  1374. int cl5GetFirstOperation (Object *replica, slapi_operation_parameters *op, void **iterator)
  1375. {
  1376. int rc;
  1377. CL5Entry entry;
  1378. Object *obj;
  1379. char *agmt_name;
  1380. if (replica == NULL || op == NULL || iterator == NULL)
  1381. {
  1382. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1383. "cl5GetFirstOperation: invalid argument\n");
  1384. return CL5_BAD_DATA;
  1385. }
  1386. *iterator = NULL;
  1387. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1388. {
  1389. agmt_name = get_thread_private_agmtname();
  1390. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1391. "%s: cl5GetFirstOperation: changelog is not initialized\n", agmt_name);
  1392. return CL5_BAD_STATE;
  1393. }
  1394. /* make sure that changelog stays open while operation is in progress */
  1395. rc = _cl5AddThread ();
  1396. if (rc != CL5_SUCCESS)
  1397. return rc;
  1398. rc = _cl5GetDBFile (replica, &obj);
  1399. if (rc != CL5_SUCCESS)
  1400. {
  1401. _cl5RemoveThread ();
  1402. return rc;
  1403. }
  1404. entry.op = op;
  1405. /* Callers of this function should cl5_operation_parameters_done(op) */
  1406. rc = _cl5GetFirstEntry (obj, &entry, iterator, NULL);
  1407. object_release (obj);
  1408. _cl5RemoveThread ();
  1409. return rc;
  1410. }
  1411. /* Name: cl5GetNextOperation
  1412. Description: retrieves the next op from the changelog as defined by the iterator;
  1413. changelog must be open.
  1414. Parameters: op - returned operation, if function is successful
  1415. iterator - in: identifies op to retrieve; out: identifies next op
  1416. Return: CL5_SUCCESS, if successful
  1417. CL5_BADDATA, if op is NULL
  1418. CL5_BAD_STATE, if changelog is not open
  1419. CL5_NOTFOUND, empty changelog
  1420. CL5_DB_ERROR, if db call fails
  1421. */
  1422. int cl5GetNextOperation (slapi_operation_parameters *op, void *iterator)
  1423. {
  1424. CL5Entry entry;
  1425. if (op == NULL || iterator == NULL || !_cl5IsValidIterator (iterator))
  1426. {
  1427. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1428. "cl5GetNextOperation: invalid argument\n");
  1429. return CL5_BAD_DATA;
  1430. }
  1431. if (s_cl5Desc.dbState != CL5_STATE_OPEN)
  1432. {
  1433. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1434. "cl5GetNextOperation: changelog is not open\n");
  1435. return CL5_BAD_STATE;
  1436. }
  1437. /* we don't need to increment thread count since cl5GetFirstOperation
  1438. locked the file through which we are iterating */
  1439. entry.op = op;
  1440. /* Callers of this function should cl5_operation_parameters_done(op) */
  1441. return _cl5GetNextEntry (&entry, iterator);
  1442. }
  1443. /* Name: cl5DestroyIterator
  1444. Description: destroys iterator once iteration through changelog is done
  1445. Parameters: iterator - iterator to destroy
  1446. Return: none
  1447. */
  1448. void cl5DestroyIterator (void *iterator)
  1449. {
  1450. CL5Iterator *it = (CL5Iterator*)iterator;
  1451. if (it == NULL)
  1452. return;
  1453. /* close cursor */
  1454. if (it->cursor)
  1455. it->cursor->c_close (it->cursor);
  1456. if (it->file)
  1457. object_release (it->file);
  1458. slapi_ch_free ((void**)&it);
  1459. }
  1460. /* Name: cl5WriteOperation
  1461. Description: writes operation to changelog
  1462. Parameters: replName - name of the replica to which operation applies
  1463. replGen - replica generation for the operation
  1464. !!!Note that we pass name and generation rather than
  1465. replica object since generation can change while operation
  1466. is in progress (if the data is reloaded). !!!
  1467. op - operation to write
  1468. local - this is a non-replicated operation
  1469. Return: CL5_SUCCESS if function is successfull;
  1470. CL5_BAD_DATA if invalid op is passed;
  1471. CL5_BAD_STATE if db has not been initialized;
  1472. CL5_MEMORY_ERROR if memory allocation failed;
  1473. CL5_DB_ERROR if any other db error occured;
  1474. */
  1475. int cl5WriteOperation(const char *replName, const char *replGen,
  1476. const slapi_operation_parameters *op, PRBool local)
  1477. {
  1478. int rc;
  1479. if (op == NULL)
  1480. {
  1481. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1482. "cl5WriteOperation: NULL operation passed\n");
  1483. return CL5_BAD_DATA;
  1484. }
  1485. if (!IsValidOperation (op))
  1486. {
  1487. return CL5_BAD_DATA;
  1488. }
  1489. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1490. {
  1491. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1492. "cl5WriteOperation: changelog is not initialized\n");
  1493. return CL5_BAD_STATE;
  1494. }
  1495. /* make sure that changelog is open while operation is in progress */
  1496. rc = _cl5AddThread ();
  1497. if (rc != CL5_SUCCESS)
  1498. return rc;
  1499. rc = _cl5WriteOperation(replName, replGen, op, local);
  1500. /* update the upper bound ruv vector */
  1501. if (rc == CL5_SUCCESS)
  1502. {
  1503. Object *file_obj = NULL;
  1504. if ( _cl5GetDBFileByReplicaName (replName, replGen, &file_obj) == CL5_SUCCESS) {
  1505. rc = _cl5UpdateRUV (file_obj, op->csn, PR_FALSE, PR_FALSE);
  1506. object_release (file_obj);
  1507. }
  1508. }
  1509. _cl5RemoveThread ();
  1510. return rc;
  1511. }
  1512. /* Name: cl5CreateReplayIterator
  1513. Description: creates an iterator that allows to retireve changes that should
  1514. to be sent to the consumer identified by ruv. The iteration is peformed by
  1515. repeated calls to cl5GetNextOperationToReplay.
  1516. Parameters: replica - replica whose data we wish to iterate;
  1517. ruv - consumer ruv;
  1518. iterator - iterator to be passed to cl5GetNextOperationToReplay call
  1519. Return: CL5_SUCCESS, if function is successfull;
  1520. CL5_MISSING_DATA, if data that should be in the changelog is missing
  1521. CL5_PURGED_DATA, if some data that consumer needs has been purged.
  1522. Note that the iterator can be non null if the supplier contains
  1523. some data that needs to be sent to the consumer
  1524. CL5_NOTFOUND if the consumer is up to data with respect to the supplier
  1525. CL5_BAD_DATA if invalid parameter is passed;
  1526. CL5_BAD_STATE if db has not been open;
  1527. CL5_DB_ERROR if any other db error occured;
  1528. CL5_MEMORY_ERROR if memory allocation fails.
  1529. Algorithm: Build a list of csns from consumer's and supplier's ruv. For each element
  1530. of the consumer's ruv put max csn into the csn list. For each element
  1531. of the supplier's ruv not in the consumer's ruv put min csn from the
  1532. supplier's ruv into the list. The list contains, for each known replica,
  1533. the starting point for changes to be sent to the consumer.
  1534. Sort the list in accending order.
  1535. Build a hash which contains, for each known replica, whether the
  1536. supplier can bring the consumer up to data with respect to that replica.
  1537. The hash is used to decide whether a change can be sent to the consumer
  1538. Find the replica with the smallest csn in the list for which
  1539. we can bring the consumer up to date.
  1540. Position the db cursor on the change entry that corresponds to this csn.
  1541. Hash entries are created for each replica traversed so far. sendChanges
  1542. flag is set to FALSE for all repolicas except the last traversed.
  1543. */
  1544. int cl5CreateReplayIteratorEx (Private_Repl_Protocol *prp, const RUV *consumerRuv,
  1545. CL5ReplayIterator **iterator, ReplicaId consumerRID )
  1546. {
  1547. int rc;
  1548. Object *replica;
  1549. Object *obj = NULL;
  1550. replica = prp->replica_object;
  1551. if (replica == NULL || consumerRuv == NULL || iterator == NULL)
  1552. {
  1553. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1554. "cl5CreateReplayIteratorEx: invalid parameter\n");
  1555. return CL5_BAD_DATA;
  1556. }
  1557. *iterator = NULL;
  1558. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1559. {
  1560. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1561. "cl5CreateReplayIteratorEx: changelog is not initialized\n");
  1562. return CL5_BAD_STATE;
  1563. }
  1564. /* make sure that changelog is open while operation is in progress */
  1565. rc = _cl5AddThread ();
  1566. if (rc != CL5_SUCCESS ) return rc;
  1567. rc = _cl5GetDBFile (replica, &obj);
  1568. if (rc == CL5_SUCCESS && obj)
  1569. {
  1570. /* iterate through the ruv in csn order to find first master for which
  1571. we can replay changes */
  1572. rc = _cl5PositionCursorForReplay (consumerRID, consumerRuv, replica, obj, iterator);
  1573. }
  1574. else
  1575. {
  1576. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1577. "cl5CreateReplayIteratorEx: could not find DB object for replica\n");
  1578. }
  1579. if (rc != CL5_SUCCESS)
  1580. {
  1581. if (obj) object_release (obj);
  1582. /* release the thread */
  1583. _cl5RemoveThread ();
  1584. }
  1585. return rc;
  1586. }
  1587. /* cl5CreateReplayIterator is now a wrapper for cl5CreateReplayIteratorEx */
  1588. int cl5CreateReplayIterator (Private_Repl_Protocol *prp, const RUV *consumerRuv,
  1589. CL5ReplayIterator **iterator)
  1590. {
  1591. /* DBDB : I thought it should be possible to refactor this like so, but it seems to not work.
  1592. Possibly the ordering of the calls is significant.
  1593. ReplicaId consumerRID = agmt_get_consumer_rid ( prp->agmt, prp->conn );
  1594. return cl5CreateReplayIteratorEx(prp,consumerRuv,iterator,consumerRID); */
  1595. int rc;
  1596. Object *replica;
  1597. Object *obj = NULL;
  1598. replica = prp->replica_object;
  1599. if (replica == NULL || consumerRuv == NULL || iterator == NULL)
  1600. {
  1601. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1602. "cl5CreateReplayIterator: invalid parameter\n");
  1603. return CL5_BAD_DATA;
  1604. }
  1605. *iterator = NULL;
  1606. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1607. {
  1608. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1609. "cl5CreateReplayIterator: changelog is not initialized\n");
  1610. return CL5_BAD_STATE;
  1611. }
  1612. /* make sure that changelog is open while operation is in progress */
  1613. rc = _cl5AddThread ();
  1614. if (rc != CL5_SUCCESS ) return rc;
  1615. rc = _cl5GetDBFile (replica, &obj);
  1616. if (rc == CL5_SUCCESS && obj)
  1617. {
  1618. /* iterate through the ruv in csn order to find first master for which
  1619. we can replay changes */
  1620. ReplicaId consumerRID = agmt_get_consumer_rid ( prp->agmt, prp->conn );
  1621. rc = _cl5PositionCursorForReplay (consumerRID, consumerRuv, replica, obj, iterator);
  1622. }
  1623. else
  1624. {
  1625. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1626. "cl5CreateReplayIterator: could not find DB object for replica\n");
  1627. }
  1628. if (rc != CL5_SUCCESS)
  1629. {
  1630. if (obj) object_release (obj);
  1631. /* release the thread */
  1632. _cl5RemoveThread ();
  1633. }
  1634. return rc;
  1635. }
  1636. /* Name: cl5GetNextOperationToReplay
  1637. Description: retrieves next operation to be sent to a particular consumer and
  1638. that was created on a particular master. Consumer and master info
  1639. is encoded in the iterator parameter that must be created by call
  1640. to cl5CreateReplayIterator.
  1641. Parameters: iterator - iterator that identifies next entry to retrieve;
  1642. op - operation retrieved if function is successful
  1643. Return: CL5_SUCCESS if function is successfull;
  1644. CL5_BAD_DATA if invalid parameter is passed;
  1645. CL5_NOTFOUND if end of iteration list is reached
  1646. CL5_DB_ERROR if any other db error occured;
  1647. CL5_BADFORMAT if data in db is of unrecognized format;
  1648. CL5_MEMORY_ERROR if memory allocation fails.
  1649. Algorithm: Iterate through changelog entries until a change is found that
  1650. originated at the replica for which we are sending changes
  1651. (based on the information in the iteration hash) and
  1652. whose csn is larger than the csn already seen by the consumer
  1653. If change originated at the replica not in the hash,
  1654. determine whether we should send changes originated at the replica
  1655. and add replica entry into the hash. We can send the changes for
  1656. the replica if the current csn is smaller or equal to the csn
  1657. in the consumer's ruv (if present) or if it is equal to the min
  1658. csn in the supplier's ruv.
  1659. */
  1660. int
  1661. cl5GetNextOperationToReplay (CL5ReplayIterator *iterator, CL5Entry *entry)
  1662. {
  1663. CSN *csn;
  1664. char *key, *data;
  1665. size_t keylen, datalen;
  1666. char *agmt_name;
  1667. int rc = 0;
  1668. agmt_name = get_thread_private_agmtname();
  1669. if (entry == NULL)
  1670. {
  1671. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1672. "%s: cl5GetNextOperationToReplay: invalid parameter passed\n", agmt_name);
  1673. return CL5_BAD_DATA;
  1674. }
  1675. rc = clcache_get_next_change (iterator->clcache, (void **)&key, &keylen, (void **)&data, &datalen, &csn);
  1676. if (rc == DB_NOTFOUND)
  1677. {
  1678. /*
  1679. * Abort means we've figured out that we've passed the replica Min CSN,
  1680. * so we should stop looping through the changelog
  1681. */
  1682. return CL5_NOTFOUND;
  1683. }
  1684. if (rc != 0)
  1685. {
  1686. slapi_log_error(SLAPI_LOG_FATAL, NULL, "%s: cl5GetNextOperationToReplay: "
  1687. "failed to read next entry; DB error %d\n", agmt_name, rc);
  1688. return CL5_DB_ERROR;
  1689. }
  1690. /* there is an entry we should return */
  1691. /* Callers of this function should cl5_operation_parameters_done(op) */
  1692. if ( 0 != cl5DBData2Entry ( data, datalen, entry ) )
  1693. {
  1694. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1695. "%s: cl5GetNextOperationToReplay: failed to format entry rc=%d\n", agmt_name, rc);
  1696. return rc;
  1697. }
  1698. return CL5_SUCCESS;
  1699. }
  1700. /* Name: cl5DestroyReplayIterator
  1701. Description: destorys iterator
  1702. Parameters: iterator - iterator to destory
  1703. Return: none
  1704. */
  1705. void cl5DestroyReplayIterator (CL5ReplayIterator **iterator)
  1706. {
  1707. if (iterator == NULL)
  1708. {
  1709. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1710. "cl5DestroyReplayIterator: invalid iterartor passed\n");
  1711. return;
  1712. }
  1713. clcache_return_buffer ( &(*iterator)->clcache );
  1714. if ((*iterator)->fileObj) {
  1715. object_release ((*iterator)->fileObj);
  1716. (*iterator)->fileObj = NULL;
  1717. }
  1718. /* release supplier's ruv */
  1719. if ((*iterator)->supplierRuvObj) {
  1720. object_release ((*iterator)->supplierRuvObj);
  1721. (*iterator)->supplierRuvObj = NULL;
  1722. }
  1723. slapi_ch_free ((void **)iterator);
  1724. /* this thread no longer holds a db reference, release it */
  1725. _cl5RemoveThread();
  1726. }
  1727. /* Name: cl5DeleteOnClose
  1728. Description: marks changelog for deletion when it is closed
  1729. Parameters: flag; if flag = 1 then delete else don't
  1730. Return: none
  1731. */
  1732. void cl5DeleteOnClose (PRBool rm)
  1733. {
  1734. s_cl5Desc.dbRmOnClose = rm;
  1735. }
  1736. /* Name: cl5GetDir
  1737. Description: returns changelog directory
  1738. Parameters: none
  1739. Return: copy of the directory; caller needs to free the string
  1740. */
  1741. char *cl5GetDir ()
  1742. {
  1743. if (s_cl5Desc.dbDir == NULL)
  1744. {
  1745. return NULL;
  1746. }
  1747. else
  1748. {
  1749. return slapi_ch_strdup (s_cl5Desc.dbDir);
  1750. }
  1751. }
  1752. /* Name: cl5Exist
  1753. Description: checks if a changelog exists in the specified directory;
  1754. We consider changelog to exist if it contains the dbversion file.
  1755. Parameters: clDir - directory to check
  1756. Return: 1 - if changelog exists; 0 - otherwise
  1757. */
  1758. PRBool cl5Exist (const char *clDir)
  1759. {
  1760. char fName [MAXPATHLEN + 1];
  1761. int rc;
  1762. PR_snprintf (fName, MAXPATHLEN, "%s/%s", clDir, VERSION_FILE);
  1763. rc = PR_Access (fName, PR_ACCESS_EXISTS);
  1764. return (rc == PR_SUCCESS);
  1765. }
  1766. /* Name: cl5GetOperationCount
  1767. Description: returns number of entries in the changelog. The changelog must be
  1768. open for the value to be meaningful.
  1769. Parameters: replica - optional parameter that specifies the replica whose operations
  1770. we wish to count; if NULL all changelog entries are counted
  1771. Return: number of entries in the changelog
  1772. */
  1773. int cl5GetOperationCount (Object *replica)
  1774. {
  1775. Object *obj;
  1776. CL5DBFile *file;
  1777. int count = 0;
  1778. int rc;
  1779. if (s_cl5Desc.dbState == CL5_STATE_NONE)
  1780. {
  1781. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1782. "cl5GetOperationCount: changelog is not initialized\n");
  1783. return -1;
  1784. }
  1785. /* make sure that changelog is open while operation is in progress */
  1786. rc = _cl5AddThread ();
  1787. if (rc != CL5_SUCCESS)
  1788. return -1;
  1789. if (replica == NULL) /* compute total entry count */
  1790. {
  1791. obj = objset_first_obj (s_cl5Desc.dbFiles);
  1792. while (obj)
  1793. {
  1794. file = (CL5DBFile*)object_get_data (obj);
  1795. PR_ASSERT (file);
  1796. count += file->entryCount;
  1797. obj = objset_next_obj (s_cl5Desc.dbFiles, obj);
  1798. }
  1799. }
  1800. else /* return count for particular db */
  1801. {
  1802. /* select correct db file */
  1803. rc = _cl5GetDBFile (replica, &obj);
  1804. if (rc == CL5_SUCCESS)
  1805. {
  1806. file = (CL5DBFile*)object_get_data (obj);
  1807. PR_ASSERT (file);
  1808. count = file->entryCount;
  1809. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1810. "cl5GetOperationCount: found DB object %p\n", obj);
  1811. object_release (obj);
  1812. }
  1813. else
  1814. {
  1815. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1816. "cl5GetOperationCount: could not get DB object for replica\n");
  1817. count = 0;
  1818. }
  1819. }
  1820. _cl5RemoveThread ();
  1821. return count;
  1822. }
  1823. /***** Helper Functions *****/
  1824. /* this call happens under state lock */
  1825. static int _cl5Open (const char *dir, const CL5DBConfig *config, CL5OpenMode openMode)
  1826. {
  1827. int rc;
  1828. PRBool didRecovery;
  1829. PR_ASSERT (dir);
  1830. /* setup db configuration parameters */
  1831. if (config)
  1832. {
  1833. _cl5SetDBConfig (config);
  1834. }
  1835. else
  1836. {
  1837. _cl5SetDefaultDBConfig ();
  1838. }
  1839. /* init the clcache */
  1840. if (( clcache_init (&s_cl5Desc.dbEnv) != 0 )) {
  1841. rc = CL5_SYSTEM_ERROR;
  1842. goto done;
  1843. }
  1844. /* initialize trimming */
  1845. rc = _cl5TrimInit ();
  1846. if (rc != CL5_SUCCESS)
  1847. {
  1848. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1849. "_cl5Open: failed to initialize trimming\n");
  1850. goto done;
  1851. }
  1852. /* create the changelog directory if it does not exist */
  1853. rc = cl5CreateDirIfNeeded (dir);
  1854. if (rc != CL5_SUCCESS)
  1855. {
  1856. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1857. "_cl5Open: failed to create changelog directory (%s)\n", dir);
  1858. goto done;
  1859. }
  1860. s_cl5Desc.dbDir = slapi_ch_strdup (dir);
  1861. /* check database version */
  1862. rc = _cl5CheckDBVersion ();
  1863. if (rc != CL5_SUCCESS)
  1864. {
  1865. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5Open: invalid db version\n");
  1866. goto done;
  1867. }
  1868. s_cl5Desc.dbOpenMode = openMode;
  1869. /* initialize db environment */
  1870. rc = _cl5AppInit (&didRecovery);
  1871. if (rc != CL5_SUCCESS)
  1872. {
  1873. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1874. "_cl5Open: failed to initialize db environment\n");
  1875. goto done;
  1876. }
  1877. /* open database files */
  1878. rc = _cl5DBOpen (!didRecovery);
  1879. if (rc != CL5_SUCCESS)
  1880. {
  1881. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1882. "_cl5Open: failed to open changelog database\n");
  1883. goto done;
  1884. }
  1885. done:;
  1886. if (rc != CL5_SUCCESS)
  1887. {
  1888. _cl5Close ();
  1889. }
  1890. return rc;
  1891. }
  1892. int cl5CreateDirIfNeeded (const char *dirName)
  1893. {
  1894. int rc;
  1895. char buff [MAXPATHLEN + 1];
  1896. char *t;
  1897. PR_ASSERT (dirName);
  1898. rc = PR_Access(dirName, PR_ACCESS_EXISTS);
  1899. if (rc == PR_SUCCESS)
  1900. {
  1901. return CL5_SUCCESS;
  1902. }
  1903. /* directory does not exist - try to create */
  1904. PL_strncpyz (buff, dirName, sizeof(buff)-1);
  1905. t = strchr (buff, '/');
  1906. /* skip first slash */
  1907. if (t)
  1908. {
  1909. t = strchr (t+1, '/');
  1910. }
  1911. while (t)
  1912. {
  1913. *t = '\0';
  1914. if (PR_Access (buff, PR_ACCESS_EXISTS) != PR_SUCCESS)
  1915. {
  1916. rc = PR_MkDir (buff, DIR_CREATE_MODE);
  1917. if (rc != PR_SUCCESS)
  1918. {
  1919. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1920. "cl5CreateDirIfNeeded: failed to create dir (%s); NSPR error - %d\n",
  1921. dirName, PR_GetError ());
  1922. return CL5_SYSTEM_ERROR;
  1923. }
  1924. }
  1925. *t++ = FILE_PATHSEP;
  1926. t = strchr (t, '/');
  1927. }
  1928. /* last piece */
  1929. rc = PR_MkDir (buff, DIR_CREATE_MODE);
  1930. if (rc != PR_SUCCESS)
  1931. {
  1932. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1933. "cl5CreateDirIfNeeded: failed to create dir; NSPR error - %d\n",
  1934. PR_GetError ());
  1935. return CL5_SYSTEM_ERROR;
  1936. }
  1937. return CL5_SUCCESS;
  1938. }
  1939. static int _cl5RemoveEnv ()
  1940. {
  1941. DB_ENV *dbEnv = NULL;
  1942. int rc = 0;
  1943. if ((rc = db_env_create(&dbEnv, 0)) != 0)
  1944. dbEnv = NULL;
  1945. if (dbEnv == NULL)
  1946. {
  1947. char *errstr = db_strerror(rc);
  1948. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1949. "_cl5RemoveEnv: failed to allocate db environment; "
  1950. "db error - %d %s\n", rc, errstr ? errstr : "unknown");
  1951. return CL5_MEMORY_ERROR;
  1952. }
  1953. rc = dbEnv->remove(dbEnv, s_cl5Desc.dbDir, DB_FORCE);
  1954. if (0 != rc)
  1955. {
  1956. char *errstr = db_strerror(rc);
  1957. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1958. "_cl5AppInit: failed to remove db environment; "
  1959. "db error - %d %s\n", rc, errstr ? errstr : "unknown");
  1960. return CL5_DB_ERROR;
  1961. }
  1962. return CL5_SUCCESS;
  1963. }
  1964. #ifdef NEED_CL5_REMOVE_LOGS
  1965. static int _cl5RemoveLogs ()
  1966. {
  1967. int rc = CL5_DB_ERROR;
  1968. char filename1[MAXPATHLEN];
  1969. PRDir *dirhandle = NULL;
  1970. dirhandle = PR_OpenDir(s_cl5Desc.dbDir);
  1971. if (NULL != dirhandle) {
  1972. PRDirEntry *direntry = NULL;
  1973. int pre = 0;
  1974. PRFileInfo info;
  1975. while (NULL != (direntry =
  1976. PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
  1977. {
  1978. if (NULL == direntry->name) {
  1979. /* NSPR doesn't behave like the docs say it should */
  1980. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  1981. "_cl5RemoveLogs: PR_ReadDir failed (%d): %s\n",
  1982. PR_GetError(),slapd_pr_strerror(PR_GetError()));
  1983. break;
  1984. }
  1985. PR_snprintf(filename1, MAXPATHLEN,
  1986. "%s/%s", s_cl5Desc.dbDir, direntry->name);
  1987. pre = PR_GetFileInfo(filename1, &info);
  1988. if (pre == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) {
  1989. continue;
  1990. }
  1991. if (0 == strncmp(direntry->name, "log.", 4))
  1992. {
  1993. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  1994. "Deleting log file: (%s)\n", filename1);
  1995. unlink(filename1);
  1996. }
  1997. rc = CL5_SUCCESS;
  1998. }
  1999. PR_CloseDir(dirhandle);
  2000. }
  2001. else if (PR_FILE_NOT_FOUND_ERROR != PR_GetError())
  2002. {
  2003. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2004. "_cl5RemoveLogs:: PR_OpenDir(%s) failed (%d): %s\n",
  2005. s_cl5Desc.dbDir, PR_GetError(),slapd_pr_strerror(PR_GetError()));
  2006. }
  2007. return rc;
  2008. }
  2009. #endif /* NEED_CL5_REMOVE_LOGS */
  2010. static int _cl5AppInit (PRBool *didRecovery)
  2011. {
  2012. int rc;
  2013. unsigned int flags = DB_CREATE | DB_INIT_MPOOL | DB_THREAD;
  2014. DB_ENV *dbEnv;
  2015. if ((rc = db_env_create(&dbEnv, 0)) != 0)
  2016. dbEnv = NULL;
  2017. if (dbEnv == NULL)
  2018. {
  2019. char *errstr = db_strerror(rc);
  2020. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2021. "_cl5AppInit: failed to allocate db environment; db error - %d (%s)\n",
  2022. rc, errstr ? errstr : "unknown");
  2023. return CL5_MEMORY_ERROR;
  2024. }
  2025. _cl5InitDBEnv (dbEnv);
  2026. if (didRecovery)
  2027. *didRecovery = PR_FALSE;
  2028. /* decide how two open based on the mode in which db is open */
  2029. switch (s_cl5Desc.dbOpenMode)
  2030. {
  2031. case CL5_OPEN_NORMAL:
  2032. flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG;
  2033. /* check if need to initiate recovery */
  2034. rc = _cl5CheckGuardian ();
  2035. if (rc != CL5_SUCCESS)
  2036. {
  2037. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2038. "_cl5AppInit: recovering changelog after disorderly shutdown\n");
  2039. flags |= DB_RECOVER;
  2040. }
  2041. break;
  2042. case CL5_OPEN_RESTORE:
  2043. flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG;
  2044. break;
  2045. case CL5_OPEN_CLEAN_RECOVER:
  2046. flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG | DB_RECOVER;
  2047. break;
  2048. case CL5_OPEN_RESTORE_RECOVER:
  2049. flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG | DB_RECOVER_FATAL;
  2050. break;
  2051. case CL5_OPEN_LDIF2CL:
  2052. /* ONREPL - don't think we need any extra flags here */
  2053. break;
  2054. default:
  2055. /* fixme? CL5_OPEN_NONE */
  2056. break;
  2057. }
  2058. if (!s_cl5Desc.dbConfig.durableTrans)
  2059. {
  2060. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3200
  2061. dbEnv->set_flags(dbEnv, DB_TXN_NOSYNC, 1);
  2062. #else
  2063. flags |= DB_TXN_NOSYNC;
  2064. #endif
  2065. }
  2066. dbEnv->set_errcall(dbEnv, dblayer_log_print);
  2067. /* do recovery if necessary */
  2068. if ((flags & DB_RECOVER) || (flags & DB_RECOVER_FATAL))
  2069. {
  2070. if (CL5_OPEN_CLEAN_RECOVER == s_cl5Desc.dbOpenMode)
  2071. {
  2072. _cl5RemoveEnv();
  2073. }
  2074. rc = _cl5Recover (flags, dbEnv);
  2075. if (rc != CL5_SUCCESS)
  2076. {
  2077. char *errstr = db_strerror(rc);
  2078. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2079. "_cl5AppInit: failed to recover changelog; db error - %d %s\n",
  2080. rc, errstr ? errstr : "unknown");
  2081. slapi_ch_free ((void **)&dbEnv);
  2082. return rc;
  2083. }
  2084. if (didRecovery)
  2085. *didRecovery = PR_TRUE;
  2086. flags &= ~(DB_RECOVER | DB_RECOVER_FATAL);
  2087. /* Need to reset the env */
  2088. /* Does this leak the dbEnv? */
  2089. if ((rc = db_env_create(&dbEnv, 0)) != 0)
  2090. dbEnv = NULL;
  2091. if (dbEnv == NULL)
  2092. {
  2093. char *errstr = db_strerror(rc);
  2094. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2095. "_cl5AppInit: failed to allocate db environment after recovery; "
  2096. "db error - %d %s\n", rc, errstr ? errstr : "unknown");
  2097. return CL5_MEMORY_ERROR;
  2098. }
  2099. _cl5InitDBEnv (dbEnv);
  2100. }
  2101. rc = dbEnv->open(dbEnv, s_cl5Desc.dbDir, flags,
  2102. s_cl5Desc.dbConfig.fileMode);
  2103. if (rc == 0)
  2104. {
  2105. s_cl5Desc.dbEnv = dbEnv;
  2106. s_cl5Desc.dbEnvOpenFlags = flags;
  2107. return CL5_SUCCESS;
  2108. }
  2109. else
  2110. {
  2111. char *errstr = db_strerror(rc);
  2112. char flagstr[20];
  2113. flagstr[0] = 0;
  2114. /* EINVAL return means bad flags - let's see what the flags are */
  2115. if (rc == EINVAL)
  2116. {
  2117. sprintf(flagstr, "%u", flags);
  2118. }
  2119. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2120. "_cl5AppInit: db environment open failed; db error - %d %s %s\n",
  2121. rc, errstr ? errstr : "unknown", flagstr);
  2122. slapi_ch_free ((void **)&dbEnv);
  2123. return CL5_DB_ERROR;
  2124. }
  2125. }
  2126. static int _cl5DBOpen ()
  2127. {
  2128. PRBool dbFile;
  2129. PRDir *dir;
  2130. PRDirEntry *entry = NULL;
  2131. int rc;
  2132. Object *replica;
  2133. int count = 0;
  2134. /* create lock that guarantees that each file is only added once to the list */
  2135. s_cl5Desc.fileLock = PR_NewLock ();
  2136. /* loop over all db files and open them; file name format is cl5_<dbid>.<dbext> */
  2137. dir = PR_OpenDir(s_cl5Desc.dbDir);
  2138. if (dir == NULL)
  2139. {
  2140. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2141. "_cl5DBOpen: failed to open changelog dir; NSPR error - %d\n",
  2142. PR_GetError ());
  2143. return CL5_SYSTEM_ERROR;
  2144. }
  2145. /* initialize set of db file objects */
  2146. s_cl5Desc.dbFiles = objset_new(NULL);
  2147. while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
  2148. {
  2149. if (NULL == entry->name)
  2150. {
  2151. break;
  2152. }
  2153. dbFile = _cl5FileName2Replica (entry->name, &replica);
  2154. if (dbFile) /* this is db file, not a log or dbversion; those are just skipped */
  2155. {
  2156. /* we only open files for existing replicas */
  2157. if (replica)
  2158. {
  2159. rc = _cl5DBOpenFile (replica, NULL /* file object */,
  2160. PR_FALSE /* check for duplicates */);
  2161. if (rc != CL5_SUCCESS)
  2162. {
  2163. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBOpen: "
  2164. "Error opening file %s\n",
  2165. entry->name);
  2166. return rc;
  2167. }
  2168. object_release (replica);
  2169. count++;
  2170. }
  2171. else /* there is no matching replica for the file - remove */
  2172. {
  2173. char fullpathname[MAXPATHLEN];
  2174. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBOpen: "
  2175. "file %s has no matching replica; removing\n", entry->name);
  2176. rc = s_cl5Desc.dbEnv->dbremove(s_cl5Desc.dbEnv,
  2177. 0, fullpathname, 0, 0);
  2178. if (rc != 0)
  2179. {
  2180. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2181. "_cl5DBOpen: failed to remove (%s) file; "
  2182. "libdb error - %d (%s)\n",
  2183. fullpathname, rc, db_strerror(rc));
  2184. }
  2185. }
  2186. }
  2187. }
  2188. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBOpen: "
  2189. "opened %d existing databases in %s\n", count, s_cl5Desc.dbDir);
  2190. PR_CloseDir(dir);
  2191. return CL5_SUCCESS;
  2192. }
  2193. /* this function assumes that the entry was validated
  2194. using IsValidOperation
  2195. Data in db format:
  2196. ------------------
  2197. <1 byte version><1 byte change_type><sizeof time_t time><null terminated csn>
  2198. <null terminated uniqueid><null terminated targetdn>
  2199. [<null terminated newrdn><1 byte deleteoldrdn>][<4 byte mod count><mod1><mod2>....]
  2200. mod format:
  2201. -----------
  2202. <1 byte modop><null terminated attr name><4 byte value count>
  2203. <4 byte value size><value1><4 byte value size><value2>
  2204. */
  2205. static int _cl5Entry2DBData (const CL5Entry *entry, char **data, PRUint32 *len)
  2206. {
  2207. int size = 1 /* version */ + 1 /* operation type */ + sizeof (time_t);
  2208. char *pos;
  2209. PRUint32 t;
  2210. slapi_operation_parameters *op;
  2211. LDAPMod **add_mods = NULL;
  2212. char *rawDN = NULL;
  2213. char s[CSN_STRSIZE];
  2214. PR_ASSERT (entry && entry->op && data && len);
  2215. op = entry->op;
  2216. PR_ASSERT (op->target_address.uniqueid);
  2217. /* compute size of the buffer needed to hold the data */
  2218. size += CSN_STRSIZE;
  2219. size += strlen (op->target_address.uniqueid) + 1;
  2220. switch (op->operation_type)
  2221. {
  2222. case SLAPI_OPERATION_ADD: if (op->p.p_add.parentuniqueid)
  2223. size += strlen (op->p.p_add.parentuniqueid) + 1;
  2224. else
  2225. size ++; /* we just store NULL char */
  2226. slapi_entry2mods (op->p.p_add.target_entry, &rawDN/* dn */, &add_mods);
  2227. size += strlen (rawDN) + 1;
  2228. size += _cl5GetModsSize (add_mods);
  2229. break;
  2230. case SLAPI_OPERATION_MODIFY: size += strlen (op->target_address.dn) + 1;
  2231. size += _cl5GetModsSize (op->p.p_modify.modify_mods);
  2232. break;
  2233. case SLAPI_OPERATION_MODRDN: size += strlen (op->target_address.dn) + 1;
  2234. /* 1 for deleteoldrdn */
  2235. size += strlen (op->p.p_modrdn.modrdn_newrdn) + 2;
  2236. if (op->p.p_modrdn.modrdn_newsuperior_address.dn)
  2237. size += strlen (op->p.p_modrdn.modrdn_newsuperior_address.dn) + 1;
  2238. else
  2239. size ++; /* for NULL char */
  2240. if (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid)
  2241. size += strlen (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid) + 1;
  2242. else
  2243. size ++; /* for NULL char */
  2244. size += _cl5GetModsSize (op->p.p_modrdn.modrdn_mods);
  2245. break;
  2246. case SLAPI_OPERATION_DELETE: size += strlen (op->target_address.dn) + 1;
  2247. break;
  2248. }
  2249. /* allocate data buffer */
  2250. (*data) = slapi_ch_malloc (size);
  2251. if ((*data) == NULL)
  2252. {
  2253. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2254. "_cl5Entry2DBData: failed to allocate data buffer\n");
  2255. return CL5_MEMORY_ERROR;
  2256. }
  2257. /* fill in the data buffer */
  2258. pos = *data;
  2259. /* write a byte of version */
  2260. (*pos) = V_5;
  2261. pos ++;
  2262. /* write change type */
  2263. (*pos) = (unsigned char)op->operation_type;
  2264. pos ++;
  2265. /* write time */
  2266. t = PR_htonl((PRUint32)entry->time);
  2267. memcpy (pos, &t, sizeof (t));
  2268. pos += sizeof (t);
  2269. /* write csn */
  2270. _cl5WriteString (csn_as_string(op->csn,PR_FALSE,s), &pos);
  2271. /* write UniqueID */
  2272. _cl5WriteString (op->target_address.uniqueid, &pos);
  2273. /* figure out what else we need to write depending on the operation type */
  2274. switch (op->operation_type)
  2275. {
  2276. case SLAPI_OPERATION_ADD: _cl5WriteString (op->p.p_add.parentuniqueid, &pos);
  2277. _cl5WriteString (rawDN, &pos);
  2278. _cl5WriteMods (add_mods, &pos);
  2279. slapi_ch_free ((void**)&rawDN);
  2280. ldap_mods_free (add_mods, 1);
  2281. break;
  2282. case SLAPI_OPERATION_MODIFY: _cl5WriteString (op->target_address.dn, &pos);
  2283. _cl5WriteMods (op->p.p_modify.modify_mods, &pos);
  2284. break;
  2285. case SLAPI_OPERATION_MODRDN: _cl5WriteString (op->target_address.dn, &pos);
  2286. _cl5WriteString (op->p.p_modrdn.modrdn_newrdn, &pos);
  2287. *pos = (PRUint8)op->p.p_modrdn.modrdn_deloldrdn;
  2288. pos ++;
  2289. _cl5WriteString (op->p.p_modrdn.modrdn_newsuperior_address.dn, &pos);
  2290. _cl5WriteString (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid, &pos);
  2291. _cl5WriteMods (op->p.p_modrdn.modrdn_mods, &pos);
  2292. break;
  2293. case SLAPI_OPERATION_DELETE: _cl5WriteString (op->target_address.dn, &pos);
  2294. break;
  2295. }
  2296. (*len) = size;
  2297. return CL5_SUCCESS;
  2298. }
  2299. /*
  2300. Data in db format:
  2301. ------------------
  2302. <1 byte version><1 byte change_type><sizeof time_t time><null terminated dbid>
  2303. <null terminated csn><null terminated uniqueid><null terminated targetdn>
  2304. [<null terminated newrdn><1 byte deleteoldrdn>][<4 byte mod count><mod1><mod2>....]
  2305. mod format:
  2306. -----------
  2307. <1 byte modop><null terminated attr name><4 byte value count>
  2308. <4 byte value size><value1><4 byte value size><value2>
  2309. */
  2310. int
  2311. cl5DBData2Entry (const char *data, PRUint32 len, CL5Entry *entry)
  2312. {
  2313. int rc;
  2314. PRUint8 version;
  2315. char *pos = (char *)data;
  2316. char *strCSN;
  2317. PRUint32 thetime;
  2318. slapi_operation_parameters *op;
  2319. LDAPMod **add_mods;
  2320. char *rawDN = NULL;
  2321. char s[CSN_STRSIZE];
  2322. PR_ASSERT (data && entry && entry->op);
  2323. /* ONREPL - check that we do not go beyond the end of the buffer */
  2324. /* read byte of version */
  2325. version = (PRUint8)(*pos);
  2326. if (version != V_5)
  2327. {
  2328. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2329. "cl5DBData2Entry: invalid data version\n");
  2330. return CL5_BAD_FORMAT;
  2331. }
  2332. op = entry->op;
  2333. pos += sizeof(version);
  2334. /* read change type */
  2335. op->operation_type = (PRUint8)(*pos);
  2336. pos ++;
  2337. /* need to do the copy first, to skirt around alignment problems on
  2338. certain architectures */
  2339. memcpy((char *)&thetime,pos,sizeof(thetime));
  2340. entry->time = (time_t)PR_ntohl(thetime);
  2341. pos += sizeof (thetime);
  2342. /* read csn */
  2343. _cl5ReadString (&strCSN, &pos);
  2344. if (op->csn == NULL || strcmp (strCSN, csn_as_string(op->csn,PR_FALSE,s)) != 0)
  2345. {
  2346. op->csn = csn_new_by_string (strCSN);
  2347. }
  2348. slapi_ch_free ((void**)&strCSN);
  2349. /* read UniqueID */
  2350. _cl5ReadString (&op->target_address.uniqueid, &pos);
  2351. /* figure out what else we need to read depending on the operation type */
  2352. switch (op->operation_type)
  2353. {
  2354. case SLAPI_OPERATION_ADD: _cl5ReadString (&op->p.p_add.parentuniqueid, &pos);
  2355. /* richm: need to free parentuniqueid */
  2356. _cl5ReadString (&rawDN, &pos);
  2357. op->target_address.dn = rawDN;
  2358. /* convert mods to entry */
  2359. rc = _cl5ReadMods (&add_mods, &pos);
  2360. slapi_mods2entry (&(op->p.p_add.target_entry), rawDN, add_mods);
  2361. ldap_mods_free (add_mods, 1);
  2362. break;
  2363. case SLAPI_OPERATION_MODIFY: _cl5ReadString (&op->target_address.dn, &pos);
  2364. rc = _cl5ReadMods (&op->p.p_modify.modify_mods, &pos);
  2365. break;
  2366. case SLAPI_OPERATION_MODRDN: _cl5ReadString (&op->target_address.dn, &pos);
  2367. _cl5ReadString (&op->p.p_modrdn.modrdn_newrdn, &pos);
  2368. op->p.p_modrdn.modrdn_deloldrdn = *pos;
  2369. pos ++;
  2370. _cl5ReadString (&op->p.p_modrdn.modrdn_newsuperior_address.dn, &pos);
  2371. _cl5ReadString (&op->p.p_modrdn.modrdn_newsuperior_address.uniqueid, &pos);
  2372. rc = _cl5ReadMods (&op->p.p_modrdn.modrdn_mods, &pos);
  2373. break;
  2374. case SLAPI_OPERATION_DELETE: _cl5ReadString (&op->target_address.dn, &pos);
  2375. rc = CL5_SUCCESS;
  2376. break;
  2377. default: rc = CL5_BAD_FORMAT;
  2378. slapi_log_error(SLAPI_LOG_FATAL,
  2379. repl_plugin_name_cl,
  2380. "cl5DBData2Entry: failed to format entry\n");
  2381. break;
  2382. }
  2383. return rc;
  2384. }
  2385. /* thread management functions */
  2386. static int _cl5DispatchDBThreads ()
  2387. {
  2388. if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void *)_cl5DeadlockMain,
  2389. NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  2390. PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE))
  2391. {
  2392. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2393. "_cl5DispatchDBThreads: failed to create deadlock thread; "
  2394. "NSPR error - %d\n", PR_GetError ());
  2395. return CL5_SYSTEM_ERROR;
  2396. }
  2397. if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void *)_cl5CheckpointMain,
  2398. NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  2399. PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE))
  2400. {
  2401. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2402. "_cl5DispatchDBThreads: failed to create checkpoint thread; "
  2403. "NSPR error - %d\n", PR_GetError ());
  2404. return CL5_SYSTEM_ERROR;
  2405. }
  2406. if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void *)_cl5TrickleMain,
  2407. NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  2408. PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE) )
  2409. {
  2410. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2411. "_cl5DispatchDBThreads: failed to create trickle thread; "
  2412. "NSPR error - %d\n", PR_GetError ());
  2413. return CL5_SYSTEM_ERROR;
  2414. }
  2415. if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void*)_cl5TrimMain,
  2416. NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  2417. PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE) )
  2418. {
  2419. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2420. "_cl5DispatchDBThreads: failed to create trimming thread; "
  2421. "NSPR error - %d\n", PR_GetError ());
  2422. return CL5_SYSTEM_ERROR;
  2423. }
  2424. return CL5_SUCCESS;
  2425. }
  2426. static int _cl5AddThread ()
  2427. {
  2428. /* lock the state lock so that nobody can change the state
  2429. while backup is in progress
  2430. */
  2431. PR_RWLock_Rlock (s_cl5Desc.stLock);
  2432. /* open changelog if it is not already open */
  2433. if (s_cl5Desc.dbState != CL5_STATE_OPEN)
  2434. {
  2435. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2436. "_cl5AddThread: invalid changelog state - %d\n", s_cl5Desc.dbState);
  2437. PR_RWLock_Unlock (s_cl5Desc.stLock);
  2438. return CL5_BAD_STATE;
  2439. }
  2440. PR_RWLock_Unlock (s_cl5Desc.stLock);
  2441. /* increment global thread count to make sure that changelog does not close while
  2442. backup is in progress */
  2443. PR_AtomicIncrement (&s_cl5Desc.threadCount);
  2444. return CL5_SUCCESS;
  2445. }
  2446. static void _cl5RemoveThread ()
  2447. {
  2448. PR_ASSERT (s_cl5Desc.threadCount > 0);
  2449. PR_AtomicDecrement (&s_cl5Desc.threadCount);
  2450. }
  2451. /* data conversion functions */
  2452. static void _cl5WriteString (const char *str, char **buff)
  2453. {
  2454. if (str)
  2455. {
  2456. strcpy (*buff, str);
  2457. (*buff) += strlen (str) + 1;
  2458. }
  2459. else /* just write NULL char */
  2460. {
  2461. (**buff) = '\0';
  2462. (*buff) ++;
  2463. }
  2464. }
  2465. static void _cl5ReadString (char **str, char **buff)
  2466. {
  2467. if (str)
  2468. {
  2469. int len = strlen (*buff);
  2470. if (len)
  2471. {
  2472. *str = slapi_ch_strdup (*buff);
  2473. (*buff) += len + 1;
  2474. }
  2475. else /* just null char - skip it */
  2476. {
  2477. *str = NULL;
  2478. (*buff) ++;
  2479. }
  2480. }
  2481. else /* just skip this string */
  2482. {
  2483. (*buff) += strlen (*buff) + 1;
  2484. }
  2485. }
  2486. /* mods format:
  2487. -----------
  2488. <4 byte mods count><mod1><mod2>...
  2489. mod format:
  2490. -----------
  2491. <1 byte modop><null terminated attr name><4 byte count>
  2492. <4 byte size><value1><4 byte size><value2>...
  2493. */
  2494. static void _cl5WriteMods (LDAPMod **mods, char **buff)
  2495. {
  2496. PRInt32 i;
  2497. char *mod_start;
  2498. PRInt32 count;
  2499. if (mods == NULL)
  2500. return;
  2501. /* skip mods count */
  2502. mod_start = (*buff) + sizeof (count);
  2503. /* write mods*/
  2504. for (i=0; mods[i]; i++)
  2505. {
  2506. _cl5WriteMod (mods[i], &mod_start);
  2507. }
  2508. count = PR_htonl(i);
  2509. memcpy (*buff, &count, sizeof (count));
  2510. (*buff) = mod_start;
  2511. }
  2512. static void _cl5WriteMod (LDAPMod *mod, char **buff)
  2513. {
  2514. char *pos;
  2515. PRInt32 count;
  2516. struct berval *bv;
  2517. Slapi_Mod smod;
  2518. slapi_mod_init_byref(&smod, mod);
  2519. pos = *buff;
  2520. /* write mod op */
  2521. *pos = (PRUint8)slapi_mod_get_operation (&smod);
  2522. pos ++;
  2523. /* write attribute name */
  2524. _cl5WriteString (slapi_mod_get_type (&smod), &pos);
  2525. /* write value count */
  2526. count = PR_htonl(slapi_mod_get_num_values(&smod));
  2527. memcpy (pos, &count, sizeof (count));
  2528. pos += sizeof (PRInt32);
  2529. bv = slapi_mod_get_first_value (&smod);
  2530. while (bv)
  2531. {
  2532. _cl5WriteBerval (bv, &pos);
  2533. bv = slapi_mod_get_next_value (&smod);
  2534. }
  2535. (*buff) = pos;
  2536. slapi_mod_done (&smod);
  2537. }
  2538. /* mods format:
  2539. -----------
  2540. <4 byte mods count><mod1><mod2>...
  2541. mod format:
  2542. -----------
  2543. <1 byte modop><null terminated attr name><4 byte count>
  2544. {<4 byte size><value1><4 byte size><value2>... ||
  2545. <null terminated str1> <null terminated str2>...}
  2546. */
  2547. static int _cl5ReadMods (LDAPMod ***mods, char **buff)
  2548. {
  2549. char *pos = *buff;
  2550. int i;
  2551. int rc;
  2552. PRInt32 mod_count;
  2553. Slapi_Mods smods;
  2554. Slapi_Mod smod;
  2555. /* need to copy first, to skirt around alignment problems on certain
  2556. architectures */
  2557. memcpy((char *)&mod_count,*buff,sizeof(mod_count));
  2558. mod_count = PR_ntohl(mod_count);
  2559. pos += sizeof (mod_count);
  2560. slapi_mods_init (&smods , mod_count);
  2561. for (i = 0; i < mod_count; i++)
  2562. {
  2563. rc = _cl5ReadMod (&smod, &pos);
  2564. if (rc != CL5_SUCCESS)
  2565. {
  2566. slapi_mods_done(&smods);
  2567. return rc;
  2568. }
  2569. slapi_mods_add_smod(&smods, &smod);
  2570. }
  2571. *buff = pos;
  2572. *mods = slapi_mods_get_ldapmods_passout (&smods);
  2573. slapi_mods_done(&smods);
  2574. return CL5_SUCCESS;
  2575. }
  2576. static int _cl5ReadMod (Slapi_Mod *smod, char **buff)
  2577. {
  2578. char *pos = *buff;
  2579. int i;
  2580. PRInt32 val_count;
  2581. char *type;
  2582. int op;
  2583. struct berval bv;
  2584. op = (*pos) & 0x000000FF;
  2585. pos ++;
  2586. _cl5ReadString (&type, &pos);
  2587. /* need to do the copy first, to skirt around alignment problems on
  2588. certain architectures */
  2589. memcpy((char *)&val_count,pos,sizeof(val_count));
  2590. val_count = PR_ntohl(val_count);
  2591. pos += sizeof (PRInt32);
  2592. slapi_mod_init(smod, val_count);
  2593. slapi_mod_set_operation (smod, op|LDAP_MOD_BVALUES);
  2594. slapi_mod_set_type (smod, type);
  2595. slapi_ch_free ((void**)&type);
  2596. for (i = 0; i < val_count; i++)
  2597. {
  2598. _cl5ReadBerval (&bv, &pos);
  2599. slapi_mod_add_value (smod, &bv);
  2600. slapi_ch_free((void **) &bv.bv_val);
  2601. }
  2602. (*buff) = pos;
  2603. return CL5_SUCCESS;
  2604. }
  2605. static int _cl5GetModsSize (LDAPMod **mods)
  2606. {
  2607. int size;
  2608. int i;
  2609. if (mods == NULL)
  2610. return 0;
  2611. size = sizeof (PRInt32);
  2612. for (i=0; mods[i]; i++)
  2613. {
  2614. size += _cl5GetModSize (mods[i]);
  2615. }
  2616. return size;
  2617. }
  2618. static int _cl5GetModSize (LDAPMod *mod)
  2619. {
  2620. int size;
  2621. int i;
  2622. size = 1 + strlen (mod->mod_type) + 1 + sizeof (mod->mod_op);
  2623. i = 0;
  2624. if (mod->mod_op & LDAP_MOD_BVALUES) /* values are in binary form */
  2625. {
  2626. while (mod->mod_bvalues != NULL && mod->mod_bvalues[i] != NULL)
  2627. {
  2628. size += mod->mod_bvalues[i]->bv_len + sizeof (mod->mod_bvalues[i]->bv_len);
  2629. i++;
  2630. }
  2631. }
  2632. else /* string data */
  2633. {
  2634. PR_ASSERT(0); /* ggood string values should never be used in the server */
  2635. }
  2636. return size;
  2637. }
  2638. static void _cl5ReadBerval (struct berval *bv, char** buff)
  2639. {
  2640. PRUint32 length = 0;
  2641. PRUint32 net_length = 0;
  2642. PR_ASSERT (bv && buff);
  2643. /***PINAKI need to do the copy first, to skirt around alignment problems on
  2644. certain architectures */
  2645. /* DBDB : struct berval.bv_len is defined as unsigned long
  2646. * But code here expects it to be 32-bits in size.
  2647. * On 64-bit machines, this is not the case.
  2648. * I changed the code to consistently use 32-bit (4-byte)
  2649. * values on the encoded side. This means that it's
  2650. * possible to generate a huge berval that will not
  2651. * be encoded properly. However, this seems unlikely
  2652. * to happen in reality, and I felt that retaining the
  2653. * old on-disk format for the changely in the 64-bit
  2654. * version of the server was important.
  2655. */
  2656. memcpy((char *)&net_length, *buff, sizeof(net_length));
  2657. length = PR_ntohl(net_length);
  2658. *buff += sizeof(net_length);
  2659. bv->bv_len = length;
  2660. if (bv->bv_len > 0) {
  2661. bv->bv_val = slapi_ch_malloc (bv->bv_len);
  2662. memcpy (bv->bv_val, *buff, bv->bv_len);
  2663. *buff += bv->bv_len;
  2664. }
  2665. else {
  2666. bv->bv_val = NULL;
  2667. }
  2668. }
  2669. static void _cl5WriteBerval (struct berval *bv, char** buff)
  2670. {
  2671. PRUint32 length = 0;
  2672. PRUint32 net_length = 0;
  2673. length = (PRUint32) bv->bv_len;
  2674. net_length = PR_htonl(length);
  2675. memcpy(*buff, &net_length, sizeof (net_length));
  2676. *buff += sizeof (net_length);
  2677. memcpy (*buff, bv->bv_val, length);
  2678. *buff += length;
  2679. }
  2680. /* data format: <value count> <value size> <value> <value size> <value> ..... */
  2681. static int _cl5ReadBervals (struct berval ***bv, char** buff, unsigned int size)
  2682. {
  2683. PRInt32 count;
  2684. int i;
  2685. char *pos;
  2686. PR_ASSERT (bv && buff);
  2687. /* ONREPL - need to check that we don't go beyond the end of the buffer */
  2688. pos = *buff;
  2689. memcpy((char *)&count, pos, sizeof(count));
  2690. count = PR_htonl (count);
  2691. pos += sizeof(count);
  2692. /* allocate bervals */
  2693. *bv = (struct berval **)slapi_ch_malloc ((count + 1) * sizeof (struct berval*));
  2694. if (*bv == NULL)
  2695. {
  2696. return CL5_MEMORY_ERROR;
  2697. }
  2698. for (i = 0; i < count; i++)
  2699. {
  2700. (*bv)[i] = (struct berval *)slapi_ch_malloc (sizeof (struct berval));
  2701. if ((*bv)[i] == NULL)
  2702. {
  2703. ber_bvecfree(*bv);
  2704. return CL5_MEMORY_ERROR;
  2705. }
  2706. _cl5ReadBerval ((*bv)[i], &pos);
  2707. }
  2708. (*bv)[count] = NULL;
  2709. *buff = pos;
  2710. return CL5_SUCCESS;
  2711. }
  2712. /* data format: <value count> <value size> <value> <value size> <value> ..... */
  2713. static int _cl5WriteBervals (struct berval **bv, char** buff, unsigned int *size)
  2714. {
  2715. PRInt32 count, net_count;
  2716. char *pos;
  2717. int i;
  2718. PR_ASSERT (bv && buff && size);
  2719. /* compute number of values and size of the buffer to hold them */
  2720. *size = sizeof (count);
  2721. for (count = 0; bv[count]; count ++)
  2722. {
  2723. *size += sizeof (bv[count]->bv_len) + bv[count]->bv_len;
  2724. }
  2725. /* allocate buffer */
  2726. *buff = (char*) slapi_ch_malloc (*size);
  2727. if (*buff == NULL)
  2728. {
  2729. *size = 0;
  2730. return CL5_MEMORY_ERROR;
  2731. }
  2732. /* fill the buffer */
  2733. pos = *buff;
  2734. net_count = PR_htonl(count);
  2735. memcpy (pos, &net_count, sizeof (net_count));
  2736. pos += sizeof (net_count);
  2737. for (i = 0; i < count; i ++)
  2738. {
  2739. _cl5WriteBerval (bv[i], &pos);
  2740. }
  2741. return CL5_SUCCESS;
  2742. }
  2743. static int _cl5DeadlockMain (void *param)
  2744. {
  2745. PRIntervalTime interval;
  2746. int rc;
  2747. PR_AtomicIncrement (&s_cl5Desc.threadCount);
  2748. interval = PR_MillisecondsToInterval(100);
  2749. while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
  2750. {
  2751. int aborted;
  2752. if ((rc = LOCK_DETECT(s_cl5Desc.dbEnv, 0, DB_LOCK_YOUNGEST, &aborted)) != 0)
  2753. {
  2754. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2755. "_cl5DeadlockMain: lock_detect failed (%d transaction%s aborted); db error - %d %s\n",
  2756. aborted, (aborted == 1)? "":"s", rc, db_strerror(rc));
  2757. }
  2758. else if (aborted)
  2759. {
  2760. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2761. "_cl5DeadlockMain: lock_detect succeeded, but %d transaction%s ha%s been aborted\n",
  2762. aborted, (aborted == 1)? "":"s", (aborted == 1)? "s":"ve");
  2763. }
  2764. DS_Sleep(interval);
  2765. }
  2766. PR_AtomicDecrement (&s_cl5Desc.threadCount);
  2767. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DeadlockMain: exiting\n");
  2768. return 0;
  2769. }
  2770. static int _cl5CheckpointMain (void *param)
  2771. {
  2772. time_t lastCheckpointCompletion = 0;
  2773. PRIntervalTime interval;
  2774. int rc = -1;
  2775. PR_AtomicIncrement (&s_cl5Desc.threadCount);
  2776. interval = PR_MillisecondsToInterval(1000);
  2777. lastCheckpointCompletion = current_time();
  2778. while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
  2779. {
  2780. /* Check to see if the checkpoint interval has elapsed */
  2781. if (current_time() - lastCheckpointCompletion > s_cl5Desc.dbConfig.checkpointInterval)
  2782. {
  2783. rc = TXN_CHECKPOINT(s_cl5Desc.dbEnv, 0, 0, 0);
  2784. if (rc == 0)
  2785. {
  2786. lastCheckpointCompletion = current_time();
  2787. }
  2788. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  2789. else if (rc != DB_INCOMPLETE) /* real error happened */
  2790. {
  2791. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2792. "_cl5CheckpointMain: checkpoint failed, db error - %d %s\n",
  2793. rc, db_strerror(rc));
  2794. }
  2795. #endif
  2796. /* According to dboreham, we are doing checkpoint twice
  2797. to reduce the number of transaction log files which need
  2798. to be retained at any time. */
  2799. rc = TXN_CHECKPOINT(s_cl5Desc.dbEnv, 0, 0, 0);
  2800. if (rc == 0)
  2801. {
  2802. lastCheckpointCompletion = current_time();
  2803. }
  2804. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  2805. else if (rc != DB_INCOMPLETE) /* real error happened */
  2806. {
  2807. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2808. "_cl5CheckpointMain: checkpoint failed, db error - %d %s\n",
  2809. rc, db_strerror(rc));
  2810. }
  2811. #endif
  2812. /* check if we should truncate logs */
  2813. if (s_cl5Desc.dbConfig.circularLogging)
  2814. {
  2815. char **list = NULL;
  2816. /* find out which log files don't contain active txns */
  2817. /* DB_ARCH_REMOVE: Remove log files that are no longer needed;
  2818. * no filenames are returned. */
  2819. int rc = LOG_ARCHIVE(s_cl5Desc.dbEnv, &list,
  2820. DB_ARCH_REMOVE, (void *)slapi_ch_malloc);
  2821. if (rc)
  2822. {
  2823. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2824. "_cl5CheckpointMain: log archive failed, "
  2825. "db error - %d %s\n", rc, db_strerror(rc));
  2826. }
  2827. slapi_ch_free((void **)&list); /* just in case */
  2828. }
  2829. else
  2830. {
  2831. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2832. "_cl5CheckpointMain: %s is off; "
  2833. "transaction logs won't be removed.\n",
  2834. CONFIG_CHANGELOG_DB_CIRCULAR_LOGGING);
  2835. }
  2836. }
  2837. /* sleep for a while */
  2838. /* why aren't we sleeping exactly the right amount of time ? */
  2839. /* answer---because the interval might be changed after the server starts up */
  2840. DS_Sleep(interval);
  2841. }
  2842. /* Check point and archive before shutting down */
  2843. rc = TXN_CHECKPOINT(s_cl5Desc.dbEnv, 0, 0, 0);
  2844. PR_AtomicDecrement (&s_cl5Desc.threadCount);
  2845. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckpointMain: exiting\n");
  2846. return 0;
  2847. }
  2848. static int _cl5TrickleMain (void *param)
  2849. {
  2850. PRIntervalTime interval;
  2851. int pages_written;
  2852. int rc;
  2853. PR_AtomicIncrement (&s_cl5Desc.threadCount);
  2854. interval = PR_MillisecondsToInterval(1000);
  2855. while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
  2856. {
  2857. if ((rc = MEMP_TRICKLE(s_cl5Desc.dbEnv,
  2858. s_cl5Desc.dbConfig.tricklePercentage, &pages_written)) != 0)
  2859. {
  2860. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2861. "_cl5TrickleMain: memp_trickle failed; db error - %d %s\n",
  2862. rc, db_strerror(rc));
  2863. }
  2864. DS_Sleep(interval);
  2865. }
  2866. PR_AtomicDecrement (&s_cl5Desc.threadCount);
  2867. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5TrickleMain: exiting\n");
  2868. return 0;
  2869. }
  2870. /* upgrade from db33 to db41
  2871. * 1. Run recovery on the database environment using the DB_ENV->open method
  2872. * 2. Remove any Berkeley DB environment using the DB_ENV->remove method
  2873. * 3. Remove any Berkeley DB transaction log files
  2874. * 4. extention .db3 -> .db4
  2875. */
  2876. static int _cl5Upgrade3_4(char *fromVersion, char *toVersion)
  2877. {
  2878. PRDir *dir = NULL;
  2879. PRDirEntry *entry = NULL;
  2880. DB *thisdb = NULL;
  2881. CL5OpenMode backup;
  2882. int rc = 0;
  2883. backup = s_cl5Desc.dbOpenMode;
  2884. s_cl5Desc.dbOpenMode = CL5_OPEN_CLEAN_RECOVER;
  2885. /* CL5_OPEN_CLEAN_RECOVER does 1 and 2 */
  2886. rc = _cl5AppInit (NULL);
  2887. if (rc != CL5_SUCCESS)
  2888. {
  2889. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2890. "_cl5Upgrade3_4: failed to open the db env\n");
  2891. return rc;
  2892. }
  2893. s_cl5Desc.dbOpenMode = backup;
  2894. dir = PR_OpenDir(s_cl5Desc.dbDir);
  2895. if (dir == NULL)
  2896. {
  2897. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2898. "_cl5Upgrade3_4: failed to open changelog dir %s; NSPR error - %d\n",
  2899. s_cl5Desc.dbDir, PR_GetError ());
  2900. goto out;
  2901. }
  2902. while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
  2903. {
  2904. if (NULL == entry->name)
  2905. {
  2906. break;
  2907. }
  2908. if (_cl5FileEndsWith(entry->name, DB_EXTENSION_DB3))
  2909. {
  2910. char oName [MAXPATHLEN + 1];
  2911. char nName [MAXPATHLEN + 1];
  2912. char *p = NULL;
  2913. char c;
  2914. int baselen = 0;
  2915. PR_snprintf(oName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, entry->name);
  2916. p = strstr(oName, DB_EXTENSION_DB3);
  2917. if (NULL == p)
  2918. {
  2919. continue;
  2920. }
  2921. /* db->rename closes DB; need to create every time */
  2922. rc = db_create(&thisdb, s_cl5Desc.dbEnv, 0);
  2923. if (0 != rc) {
  2924. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2925. "_cl5Upgrade3_4: failed to get db handle\n");
  2926. goto out;
  2927. }
  2928. baselen = p - oName;
  2929. c = *p;
  2930. *p = '\0';
  2931. PR_snprintf(nName, MAXPATHLEN+1, "%s", oName);
  2932. PR_snprintf(nName + baselen, MAXPATHLEN+1-baselen, "%s", DB_EXTENSION);
  2933. *p = c;
  2934. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2935. "_cl5Upgrade3_4: renaming %s to %s\n", oName, nName);
  2936. rc = thisdb->rename(thisdb, (const char *)oName, NULL /* subdb */,
  2937. (const char *)nName, 0);
  2938. if (rc != PR_SUCCESS)
  2939. {
  2940. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  2941. "_cl5Upgrade3_4: failed to rename file (%s -> %s); "
  2942. "db error - %d %s\n", oName, nName, rc, db_strerror(rc));
  2943. break;
  2944. }
  2945. }
  2946. }
  2947. /* update the version file */
  2948. _cl5WriteDBVersion ();
  2949. /* update the guardian file */
  2950. _cl5WriteGuardian ();
  2951. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2952. "Upgrading from %s to %s is successfully done (%s)\n",
  2953. fromVersion, toVersion, s_cl5Desc.dbDir);
  2954. out:
  2955. if (NULL != dir)
  2956. {
  2957. PR_CloseDir(dir);
  2958. }
  2959. if (s_cl5Desc.dbEnv)
  2960. {
  2961. DB_ENV *dbEnv = s_cl5Desc.dbEnv;
  2962. dbEnv->close(dbEnv, 0);
  2963. s_cl5Desc.dbEnv = NULL;
  2964. }
  2965. return rc;
  2966. }
  2967. /* upgrade from db41 -> db42 -> db43 -> db44 -> db45
  2968. * 1. Run recovery on the database environment using the DB_ENV->open method
  2969. * 2. Remove any Berkeley DB environment using the DB_ENV->remove method
  2970. * 3. Remove any Berkeley DB transaction log files
  2971. */
  2972. static int _cl5Upgrade4_4(char *fromVersion, char *toVersion)
  2973. {
  2974. CL5OpenMode backup;
  2975. int rc = 0;
  2976. backup = s_cl5Desc.dbOpenMode;
  2977. s_cl5Desc.dbOpenMode = CL5_OPEN_CLEAN_RECOVER;
  2978. /* CL5_OPEN_CLEAN_RECOVER does 1 and 2 */
  2979. rc = _cl5AppInit (NULL);
  2980. if (rc != CL5_SUCCESS)
  2981. {
  2982. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2983. "_cl5Upgrade4_4: failed to open the db env\n");
  2984. return rc;
  2985. }
  2986. s_cl5Desc.dbOpenMode = backup;
  2987. /* update the version file */
  2988. _cl5WriteDBVersion ();
  2989. /* update the guardian file */
  2990. _cl5WriteGuardian ();
  2991. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  2992. "Upgrading from %s to %s is successfully done (%s)\n",
  2993. fromVersion, toVersion, s_cl5Desc.dbDir);
  2994. if (s_cl5Desc.dbEnv)
  2995. {
  2996. DB_ENV *dbEnv = s_cl5Desc.dbEnv;
  2997. dbEnv->close(dbEnv, 0);
  2998. s_cl5Desc.dbEnv = NULL;
  2999. }
  3000. return rc;
  3001. }
  3002. static int _cl5CheckDBVersion ()
  3003. {
  3004. char clVersion [VERSION_SIZE + 1];
  3005. char dbVersion [VERSION_SIZE + 1];
  3006. int rc;
  3007. if (!cl5Exist (s_cl5Desc.dbDir))
  3008. {
  3009. /* this is new changelog - write DB version and guardian file */
  3010. rc = _cl5WriteDBVersion ();
  3011. if (rc == CL5_SUCCESS) {
  3012. rc = _cl5WriteGuardian();
  3013. }
  3014. }
  3015. else
  3016. {
  3017. char *versionp = NULL;
  3018. char *versionendp = NULL;
  3019. char *dotp = NULL;
  3020. int dbmajor = 0;
  3021. int dbminor = 0;
  3022. PR_snprintf (clVersion, VERSION_SIZE, "%s/%d.%d/%s",
  3023. BDB_IMPL, DB_VERSION_MAJOR, DB_VERSION_MINOR, BDB_REPLPLUGIN);
  3024. rc = _cl5ReadDBVersion (s_cl5Desc.dbDir, dbVersion, sizeof(dbVersion));
  3025. if (rc != CL5_SUCCESS)
  3026. {
  3027. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3028. "_cl5CheckDBVersion: invalid dbversion\n");
  3029. rc = CL5_BAD_DBVERSION;
  3030. goto bailout;
  3031. }
  3032. versionendp = dbVersion + strlen(dbVersion);
  3033. /* get the version number */
  3034. /* old DBVERSION string: CL5_TYPE/REPL_PLUGIN_NAME/#.# */
  3035. if (PL_strncmp(dbVersion, CL5_TYPE, strlen(CL5_TYPE)) == 0)
  3036. {
  3037. versionp = strrchr(dbVersion, '/');
  3038. }
  3039. /* new DBVERSION string: bdb/#.#/libreplication-plugin */
  3040. else if (PL_strncmp(dbVersion, BDB_IMPL, strlen(BDB_IMPL)) == 0)
  3041. {
  3042. versionp = strchr(dbVersion, '/');
  3043. }
  3044. if (NULL == versionp || versionp == versionendp)
  3045. {
  3046. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3047. "_cl5CheckDBVersion: invalid dbversion: %s\n", dbVersion);
  3048. rc = CL5_BAD_DBVERSION;
  3049. goto bailout;
  3050. }
  3051. dotp = strchr(++versionp, '.');
  3052. if (NULL != dotp)
  3053. {
  3054. *dotp = '\0';
  3055. dbmajor = strtol(versionp, (char **)NULL, 10);
  3056. dbminor = strtol(dotp+1, (char **)NULL, 10);
  3057. *dotp = '.';
  3058. }
  3059. else
  3060. {
  3061. dbmajor = strtol(versionp, (char **)NULL, 10);
  3062. }
  3063. if (dbmajor < DB_VERSION_MAJOR)
  3064. {
  3065. /* upgrade */
  3066. rc = _cl5Upgrade3_4(dbVersion, clVersion);
  3067. if (rc != CL5_SUCCESS)
  3068. {
  3069. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3070. "_cl5CheckDBVersion: upgrade %s -> %s failed\n",
  3071. dbVersion, clVersion);
  3072. rc = CL5_BAD_DBVERSION;
  3073. }
  3074. }
  3075. else if (dbminor < DB_VERSION_MINOR)
  3076. {
  3077. /* minor upgrade */
  3078. rc = _cl5Upgrade4_4(dbVersion, clVersion);
  3079. if (rc != CL5_SUCCESS)
  3080. {
  3081. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3082. "_cl5CheckDBVersion: upgrade %s -> %s failed\n",
  3083. dbVersion, clVersion);
  3084. rc = CL5_BAD_DBVERSION;
  3085. }
  3086. }
  3087. }
  3088. bailout:
  3089. return rc;
  3090. }
  3091. static int _cl5ReadDBVersion (const char *dir, char *clVersion, int buflen)
  3092. {
  3093. int rc;
  3094. PRFileDesc *file;
  3095. char fName [MAXPATHLEN + 1];
  3096. char buff [BUFSIZ];
  3097. PRInt32 size;
  3098. char *tok;
  3099. char * iter = NULL;
  3100. if (clVersion)
  3101. {
  3102. clVersion [0] = '\0';
  3103. }
  3104. PR_snprintf (fName, MAXPATHLEN, "%s/%s", dir, VERSION_FILE);
  3105. file = PR_Open (fName, PR_RDONLY, 777);
  3106. if (file == NULL)
  3107. {
  3108. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3109. "_cl5ReadDBVersion: failed to open DBVERSION; NSPR error - %d\n",
  3110. PR_GetError ());
  3111. return CL5_SYSTEM_ERROR;
  3112. }
  3113. size = slapi_read_buffer (file, buff, BUFSIZ);
  3114. if (size < 0)
  3115. {
  3116. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3117. "_cl5ReadDBVersion: failed to read DBVERSION; NSPR error - %d\n",
  3118. PR_GetError ());
  3119. PR_Close (file);
  3120. return CL5_SYSTEM_ERROR;
  3121. }
  3122. /* parse the data */
  3123. buff[size]= '\0';
  3124. tok = ldap_utf8strtok_r (buff, "\n", &iter);
  3125. if (tok)
  3126. {
  3127. if (clVersion)
  3128. {
  3129. PL_strncpyz(clVersion, tok, buflen);
  3130. }
  3131. }
  3132. rc = PR_Close (file);
  3133. if (rc != PR_SUCCESS)
  3134. {
  3135. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3136. "_cl5ReadDBVersion: failed to close DBVERSION; NSPR error - %d\n",
  3137. PR_GetError ());
  3138. return CL5_SYSTEM_ERROR;
  3139. }
  3140. return CL5_SUCCESS;
  3141. }
  3142. static int _cl5WriteDBVersion ()
  3143. {
  3144. int rc;
  3145. PRFileDesc *file;
  3146. char fName [MAXPATHLEN + 1];
  3147. char clVersion [VERSION_SIZE + 1];
  3148. PRInt32 len, size;
  3149. PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, VERSION_FILE);
  3150. file = PR_Open (fName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
  3151. s_cl5Desc.dbConfig.fileMode);
  3152. if (file == NULL)
  3153. {
  3154. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3155. "_cl5WriteDBVersion: failed to open DBVERSION; NSPR error - %d\n",
  3156. PR_GetError ());
  3157. return CL5_SYSTEM_ERROR;
  3158. }
  3159. /* write changelog version */
  3160. PR_snprintf (clVersion, VERSION_SIZE, "%s/%d.%d/%s\n",
  3161. BDB_IMPL, DB_VERSION_MAJOR, DB_VERSION_MINOR, BDB_REPLPLUGIN);
  3162. len = strlen(clVersion);
  3163. size = slapi_write_buffer (file, clVersion, len);
  3164. if (size != len)
  3165. {
  3166. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3167. "_cl5WriteDBVersion: failed to write DBVERSION; NSPR error - %d\n",
  3168. PR_GetError ());
  3169. PR_Close (file);
  3170. return CL5_SYSTEM_ERROR;
  3171. }
  3172. rc = PR_Close (file);
  3173. if (rc != PR_SUCCESS)
  3174. {
  3175. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3176. "_cl5WriteDBVersion: failed to close DBVERSION; NSPR error - %d\n",
  3177. PR_GetError ());
  3178. return CL5_SYSTEM_ERROR;
  3179. }
  3180. return CL5_SUCCESS;
  3181. }
  3182. /* for now guardian file is just like dbversion file */
  3183. static int _cl5CheckGuardian ()
  3184. {
  3185. char plVersion [VERSION_SIZE + 1];
  3186. char dbVersion [VERSION_SIZE + 1];
  3187. int rc;
  3188. /* new changelog - no guardian file */
  3189. if (!cl5Exist(s_cl5Desc.dbDir))
  3190. {
  3191. return CL5_SUCCESS;
  3192. }
  3193. else
  3194. {
  3195. PR_snprintf (plVersion, VERSION_SIZE, "%s/%d.%d/%s\n",
  3196. BDB_IMPL, DB_VERSION_MAJOR, DB_VERSION_MINOR, BDB_REPLPLUGIN);
  3197. dbVersion[0] = '\0';
  3198. rc = _cl5ReadGuardian (dbVersion);
  3199. if (rc != CL5_SUCCESS || strcasecmp (plVersion, dbVersion) != 0)
  3200. {
  3201. PR_snprintf (plVersion, VERSION_SIZE, "%s/%s/%s",
  3202. CL5_TYPE, REPL_PLUGIN_NAME, CHANGELOG_DB_VERSION);
  3203. if (strcasecmp (plVersion, dbVersion) != 0)
  3204. {
  3205. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3206. "_cl5CheckGuardian: found old style of guardian file: %s\n",
  3207. dbVersion);
  3208. }
  3209. else
  3210. {
  3211. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3212. "_cl5CheckGuardian: missing or invalid guardian file\n");
  3213. return (CL5_BAD_FORMAT);
  3214. }
  3215. }
  3216. /* remove guardian file */
  3217. rc = _cl5RemoveGuardian ();
  3218. if (rc != CL5_SUCCESS)
  3219. {
  3220. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3221. "_cl5CheckGuardian: failed to remove guardian file\n");
  3222. }
  3223. }
  3224. return rc;
  3225. }
  3226. static int _cl5WriteGuardian ()
  3227. {
  3228. int rc;
  3229. PRFileDesc *file;
  3230. char fName [MAXPATHLEN + 1];
  3231. char version [VERSION_SIZE];
  3232. PRInt32 len, size;
  3233. PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, GUARDIAN_FILE);
  3234. file = PR_Open (fName, PR_WRONLY | PR_CREATE_FILE, s_cl5Desc.dbConfig.fileMode);
  3235. if (file == NULL)
  3236. {
  3237. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3238. "_cl5WriteGuardian: failed to open guardian file; NSPR error - %d\n",
  3239. PR_GetError());
  3240. return CL5_SYSTEM_ERROR;
  3241. }
  3242. PR_snprintf (version, VERSION_SIZE, "%s/%d.%d/%s\n",
  3243. BDB_IMPL, DB_VERSION_MAJOR, DB_VERSION_MINOR, BDB_REPLPLUGIN);
  3244. len = strlen (version);
  3245. size = slapi_write_buffer (file, version, len);
  3246. if (size != len)
  3247. {
  3248. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3249. "_cl5WriteGuardian: failed to write guardian file; NSPR error - %d\n",
  3250. PR_GetError());
  3251. PR_Close (file);
  3252. return CL5_SYSTEM_ERROR;
  3253. }
  3254. rc = PR_Close (file);
  3255. if (rc != PR_SUCCESS)
  3256. {
  3257. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3258. "_cl5WriteGuardian: failed to close guardian file; NSPR error - %d\n",
  3259. PR_GetError());
  3260. return CL5_SYSTEM_ERROR;
  3261. }
  3262. return CL5_SUCCESS;
  3263. }
  3264. static int _cl5ReadGuardian (char *buff)
  3265. {
  3266. int rc;
  3267. PRFileDesc *file;
  3268. char fName [MAXPATHLEN + 1];
  3269. PRInt32 size;
  3270. PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, GUARDIAN_FILE);
  3271. file = PR_Open (fName, PR_RDONLY, 0);
  3272. if (file == NULL)
  3273. {
  3274. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3275. "_cl5ReadGuardian: failed to open guardian file; NSPR error - %d\n",
  3276. PR_GetError());
  3277. return CL5_SYSTEM_ERROR;
  3278. }
  3279. size = slapi_read_buffer (file, buff, VERSION_SIZE);
  3280. if (size <= 0)
  3281. {
  3282. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3283. "_cl5ReadGuardian: failed to read guardian file; NSPR error - %d\n",
  3284. PR_GetError());
  3285. PR_Close (file);
  3286. return CL5_SYSTEM_ERROR;
  3287. }
  3288. buff [size-1] = '\0';
  3289. rc = PR_Close (file);
  3290. if (rc != PR_SUCCESS)
  3291. {
  3292. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3293. "_cl5ReadGuardian: failed to close guardian file; NSPR error - %d\n",
  3294. PR_GetError());
  3295. return CL5_SYSTEM_ERROR;
  3296. }
  3297. return CL5_SUCCESS;
  3298. }
  3299. static int _cl5RemoveGuardian ()
  3300. {
  3301. char fName [MAXPATHLEN + 1];
  3302. int rc;
  3303. PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, GUARDIAN_FILE);
  3304. rc = PR_Delete (fName);
  3305. if (rc != PR_SUCCESS)
  3306. {
  3307. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3308. "_cl5RemoveGuardian: failed to remove guardian file; NSPR error - %d\n",
  3309. PR_GetError());
  3310. return CL5_SYSTEM_ERROR;
  3311. }
  3312. return CL5_SUCCESS;
  3313. }
  3314. /* must be called under the state lock */
  3315. static void _cl5Close ()
  3316. {
  3317. PRIntervalTime interval;
  3318. if (s_cl5Desc.dbState != CL5_STATE_CLOSED) /* Don't try to close twice */
  3319. {
  3320. /* cl5Close() set the state flag to CL5_STATE_CLOSING, which should
  3321. trigger all of the db housekeeping threads to exit, and which will
  3322. eventually cause no new update threads to start - so we wait here
  3323. for those other threads to finish before we proceed */
  3324. interval = PR_MillisecondsToInterval(100);
  3325. while (s_cl5Desc.threadCount > 0)
  3326. {
  3327. slapi_log_error( SLAPI_LOG_REPL, repl_plugin_name_cl,
  3328. "_cl5Close: waiting for threads to exit: %d thread(s) still active\n",
  3329. s_cl5Desc.threadCount);
  3330. DS_Sleep(interval);
  3331. }
  3332. /* There should now be no threads accessing any of the changelog databases -
  3333. it is safe to remove those databases */
  3334. _cl5DBClose ();
  3335. /* cleanup trimming */
  3336. _cl5TrimCleanup ();
  3337. /* There should be no more open databases after _cl5DBClose, so it is now
  3338. safe to close the dbEnv */
  3339. if (s_cl5Desc.dbEnv)
  3340. {
  3341. DB_ENV *dbEnv = s_cl5Desc.dbEnv;
  3342. int rc = dbEnv->close(dbEnv, 0);
  3343. s_cl5Desc.dbEnv = NULL;
  3344. if (rc) {
  3345. slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3346. "_cl5Close: error closing DB environment: %d (%s)\n",
  3347. rc, db_strerror(rc));
  3348. }
  3349. }
  3350. /* record successful close by writing guardian file;
  3351. we do it in all case accept incomplete open due to an error */
  3352. if (s_cl5Desc.dbState == CL5_STATE_CLOSING || s_cl5Desc.dbOpenMode != CL5_OPEN_NORMAL)
  3353. {
  3354. _cl5WriteGuardian ();
  3355. }
  3356. /* remove changelog if requested */
  3357. if (s_cl5Desc.dbRmOnClose)
  3358. {
  3359. if (_cl5Delete (s_cl5Desc.dbDir, 1) != CL5_SUCCESS)
  3360. {
  3361. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3362. "cl5Close: failed to remove changelog\n");
  3363. }
  3364. s_cl5Desc.dbRmOnClose = PR_FALSE;
  3365. }
  3366. slapi_ch_free ((void **)&s_cl5Desc.dbDir);
  3367. memset (&s_cl5Desc.dbConfig, 0, sizeof (s_cl5Desc.dbConfig));
  3368. s_cl5Desc.fatalError = PR_FALSE;
  3369. s_cl5Desc.threadCount = 0;
  3370. s_cl5Desc.dbOpenMode = CL5_OPEN_NONE;
  3371. }
  3372. }
  3373. static void _cl5DBClose ()
  3374. {
  3375. if (NULL != s_cl5Desc.dbFiles)
  3376. {
  3377. Object *obj;
  3378. for (obj = objset_first_obj(s_cl5Desc.dbFiles); obj;
  3379. obj = objset_next_obj(s_cl5Desc.dbFiles, obj)) {
  3380. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3381. "_cl5DBClose: deleting DB object %p\n", obj);
  3382. }
  3383. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3384. "_cl5DBClose: closing databases in %s\n", s_cl5Desc.dbDir);
  3385. objset_delete (&s_cl5Desc.dbFiles);
  3386. }
  3387. if (NULL != s_cl5Desc.fileLock)
  3388. {
  3389. PR_DestroyLock (s_cl5Desc.fileLock);
  3390. s_cl5Desc.fileLock = NULL;
  3391. }
  3392. }
  3393. /* see if the given file is a changelog db file */
  3394. static int
  3395. _cl5IsDbFile(const char *fname)
  3396. {
  3397. char *ptr = NULL;
  3398. if (!fname || !*fname) {
  3399. return 0;
  3400. }
  3401. if (!strcmp(fname, GUARDIAN_FILE)) {
  3402. return 1;
  3403. }
  3404. if (!strcmp(fname, VERSION_FILE)) {
  3405. return 1;
  3406. }
  3407. if (_cl5FileEndsWith(fname, DB_EXTENSION)) {
  3408. return 1;
  3409. }
  3410. if (_cl5IsLogFile(fname)) {
  3411. return 1;
  3412. }
  3413. ptr = strstr(fname, "__db.");
  3414. if (ptr == fname) { /* begins with __db. */
  3415. return 1;
  3416. }
  3417. return 0; /* not a filename we recognize as being associated with the db */
  3418. }
  3419. /* state lock must be locked */
  3420. static int _cl5Delete (const char *clDir, int rmDir)
  3421. {
  3422. PRDir *dir;
  3423. char filename[MAXPATHLEN + 1];
  3424. PRDirEntry *entry = NULL;
  3425. int rc;
  3426. int dirisempty = 1;
  3427. /* remove all files in the directory and the directory */
  3428. dir = PR_OpenDir(clDir);
  3429. if (dir == NULL)
  3430. {
  3431. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3432. "_cl5Delete: failed to open changelog dir; NSPR error - %d\n",
  3433. PR_GetError ());
  3434. return CL5_SYSTEM_ERROR;
  3435. }
  3436. while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
  3437. {
  3438. if (NULL == entry->name)
  3439. {
  3440. break;
  3441. }
  3442. if (!_cl5IsDbFile(entry->name)) {
  3443. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3444. "_cl5Delete: Skipping file [%s/%s] because it is not a changelogdb file.\n",
  3445. clDir, entry->name);
  3446. dirisempty = 0; /* skipped at least one file - dir not empty */
  3447. continue;
  3448. }
  3449. PR_snprintf(filename, MAXPATHLEN, "%s/%s", clDir, entry->name);
  3450. /* _cl5Delete deletes the whole changelog directory with all the files
  3451. * underneath. Thus, we can just remove them physically. */
  3452. rc = PR_Delete(filename);
  3453. if (rc != PR_SUCCESS)
  3454. {
  3455. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3456. "_cl5Delete: failed to remove (%s) file; NSPR error - %d\n",
  3457. filename, PR_GetError ());
  3458. }
  3459. }
  3460. rc = PR_CloseDir(dir);
  3461. if (rc != PR_SUCCESS)
  3462. {
  3463. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3464. "_cl5Delete: failed to close changelog dir (%s); NSPR error - %d\n",
  3465. clDir, PR_GetError ());
  3466. return CL5_SYSTEM_ERROR;
  3467. }
  3468. if (rmDir && dirisempty)
  3469. {
  3470. rc = PR_RmDir (clDir);
  3471. if (rc != 0)
  3472. {
  3473. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3474. "_cl5Delete: failed to remove changelog dir (%s); errno = %d\n",
  3475. clDir, errno);
  3476. return CL5_SYSTEM_ERROR;
  3477. }
  3478. } else if (rmDir && !dirisempty) {
  3479. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3480. "_cl5Delete: changelog dir (%s) is not empty - cannot remove\n",
  3481. clDir);
  3482. }
  3483. /* invalidate the clcache */
  3484. clcache_destroy();
  3485. return CL5_SUCCESS;
  3486. }
  3487. static void _cl5SetDefaultDBConfig ()
  3488. {
  3489. s_cl5Desc.dbConfig.cacheSize = CL5_DEFAULT_CONFIG_DB_DBCACHESIZE;
  3490. s_cl5Desc.dbConfig.durableTrans = CL5_DEFAULT_CONFIG_DB_DURABLE_TRANSACTIONS;
  3491. s_cl5Desc.dbConfig.checkpointInterval = CL5_DEFAULT_CONFIG_DB_CHECKPOINT_INTERVAL;
  3492. s_cl5Desc.dbConfig.circularLogging = CL5_DEFAULT_CONFIG_DB_CIRCULAR_LOGGING;
  3493. s_cl5Desc.dbConfig.pageSize = CL5_DEFAULT_CONFIG_DB_PAGE_SIZE;
  3494. s_cl5Desc.dbConfig.logfileSize = CL5_DEFAULT_CONFIG_DB_LOGFILE_SIZE;
  3495. s_cl5Desc.dbConfig.maxTxnSize = CL5_DEFAULT_CONFIG_DB_TXN_MAX;
  3496. s_cl5Desc.dbConfig.verbose = CL5_DEFAULT_CONFIG_DB_VERBOSE;
  3497. s_cl5Desc.dbConfig.debug = CL5_DEFAULT_CONFIG_DB_DEBUG;
  3498. s_cl5Desc.dbConfig.tricklePercentage = CL5_DEFAULT_CONFIG_DB_TRICKLE_PERCENTAGE;
  3499. s_cl5Desc.dbConfig.spinCount = CL5_DEFAULT_CONFIG_DB_SPINCOUNT;
  3500. s_cl5Desc.dbConfig.nb_lock_config = CL5_DEFAULT_CONFIG_NB_LOCK;
  3501. s_cl5Desc.dbConfig.fileMode = FILE_CREATE_MODE;
  3502. }
  3503. static void _cl5SetDBConfig (const CL5DBConfig *config)
  3504. {
  3505. /* through CL5DBConfig, we have access to all the LDAP configurable Changelog DB parameters */
  3506. s_cl5Desc.dbConfig.cacheSize = config->cacheSize;
  3507. s_cl5Desc.dbConfig.durableTrans = config->durableTrans;
  3508. s_cl5Desc.dbConfig.checkpointInterval = config->checkpointInterval;
  3509. s_cl5Desc.dbConfig.circularLogging = config->circularLogging;
  3510. s_cl5Desc.dbConfig.pageSize = config->pageSize;
  3511. s_cl5Desc.dbConfig.logfileSize = config->logfileSize;
  3512. s_cl5Desc.dbConfig.maxTxnSize = config->maxTxnSize;
  3513. s_cl5Desc.dbConfig.verbose = config->verbose;
  3514. s_cl5Desc.dbConfig.debug = config->debug;
  3515. s_cl5Desc.dbConfig.tricklePercentage = config->tricklePercentage;
  3516. s_cl5Desc.dbConfig.spinCount = config->spinCount;
  3517. s_cl5Desc.dbConfig.nb_lock_config = config->nb_lock_config;
  3518. s_cl5Desc.dbConfig.maxConcurrentWrites = config->maxConcurrentWrites;
  3519. if (config->spinCount != 0)
  3520. {
  3521. DB_ENV_SET_TAS_SPINS(s_cl5Desc.dbEnv, config->spinCount);
  3522. }
  3523. /* Some other configuration parameters are hardcoded... */
  3524. s_cl5Desc.dbConfig.fileMode = FILE_CREATE_MODE;
  3525. }
  3526. /*
  3527. * a wrapper for slapi_ch_free; it's declared to set slapi_ch_free in BDB
  3528. * dbEnv->set_alloc(dbEnv, (void *)slapi_ch_malloc, (void *)slapi_ch_realloc, _cl5_api_free);
  3529. *
  3530. */
  3531. void _cl5_api_free(void *ptr)
  3532. {
  3533. slapi_ch_free(&ptr);
  3534. }
  3535. #define ONEG 1073741824 /* one giga bytes */
  3536. static void _cl5InitDBEnv(DB_ENV *dbEnv)
  3537. {
  3538. dbEnv->set_errpfx(dbEnv, "ns-slapd");
  3539. dbEnv->set_lg_max(dbEnv, s_cl5Desc.dbConfig.logfileSize);
  3540. dbEnv->set_tx_max(dbEnv, s_cl5Desc.dbConfig.maxTxnSize);
  3541. dbEnv->set_cachesize(dbEnv, s_cl5Desc.dbConfig.cacheSize/ONEG,
  3542. s_cl5Desc.dbConfig.cacheSize%ONEG,
  3543. 0);
  3544. /* Set default number of locks */
  3545. dbEnv->set_lk_max_locks(dbEnv, s_cl5Desc.dbConfig.nb_lock_config);
  3546. if (s_cl5Desc.dbConfig.verbose)
  3547. {
  3548. int on = 1;
  3549. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4300
  3550. /* DB_VERB_CHKPOINT removed in 43 */
  3551. #else
  3552. dbEnv->set_verbose(dbEnv, DB_VERB_CHKPOINT, on);
  3553. #endif
  3554. dbEnv->set_verbose(dbEnv, DB_VERB_DEADLOCK, on);
  3555. dbEnv->set_verbose(dbEnv, DB_VERB_RECOVERY, on);
  3556. dbEnv->set_verbose(dbEnv, DB_VERB_WAITSFOR, on);
  3557. }
  3558. if (s_cl5Desc.dbConfig.debug)
  3559. {
  3560. dbEnv->set_errcall(dbEnv, _cl5DBLogPrint);
  3561. }
  3562. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
  3563. dbEnv->set_alloc(dbEnv, (void *)slapi_ch_malloc, (void *)slapi_ch_realloc, _cl5_api_free);
  3564. #endif
  3565. }
  3566. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4300
  3567. static void _cl5DBLogPrint(const DB_ENV *dbenv, const char* prefix,
  3568. const char *buffer)
  3569. #else
  3570. static void _cl5DBLogPrint(const char* prefix, char *buffer)
  3571. #endif
  3572. {
  3573. /* We ignore the prefix since we know who we are anyway */
  3574. slapi_log_error (SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5: %s\n", buffer);
  3575. }
  3576. static PRBool _cl5IsLogFile (const char *path)
  3577. {
  3578. int rc;
  3579. /* Is the filename at least 4 characters long ? */
  3580. if (strlen(path) < 4)
  3581. {
  3582. return PR_FALSE; /* Not a log file then */
  3583. }
  3584. /* Are the first 4 characters "log." ? */
  3585. rc = strncmp(path,"log.",4);
  3586. if (0 == rc)
  3587. {
  3588. /* Now, are the last 4 characters _not_ .db# ? */
  3589. const char *piece = path + (strlen(path) - 4);
  3590. rc = strcmp(piece, DB_EXTENSION);
  3591. if (0 != rc)
  3592. {
  3593. /* Is */
  3594. return PR_TRUE;
  3595. }
  3596. }
  3597. return PR_FALSE; /* Is not */
  3598. }
  3599. static int _cl5Recover (int open_flags, DB_ENV *dbEnv)
  3600. {
  3601. /* If we're doing recovery, we MUST open the env single-threaded ! */
  3602. int recover_flags = open_flags & ~DB_THREAD;
  3603. int rc;
  3604. rc = dbEnv->open(dbEnv, s_cl5Desc.dbDir, recover_flags, s_cl5Desc.dbConfig.fileMode);
  3605. if (rc != 0)
  3606. {
  3607. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3608. "_cl5Recover: appinit failed; db error - %d %s\n",
  3609. rc, db_strerror(rc));
  3610. return CL5_DB_ERROR;
  3611. }
  3612. /* Now close it so we can re-open it again... */
  3613. dbEnv->close(dbEnv, 0);
  3614. return CL5_SUCCESS;
  3615. }
  3616. /* Trimming helper functions */
  3617. static int _cl5TrimInit ()
  3618. {
  3619. /* just create the lock while we are singlethreaded */
  3620. s_cl5Desc.dbTrim.lock = PR_NewLock();
  3621. if (s_cl5Desc.dbTrim.lock == NULL)
  3622. {
  3623. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3624. "_cl5InitTrimming: failed to create lock; NSPR error - %d\n",
  3625. PR_GetError ());
  3626. return CL5_SYSTEM_ERROR;
  3627. }
  3628. else
  3629. {
  3630. return CL5_SUCCESS;
  3631. }
  3632. }
  3633. static void _cl5TrimCleanup ()
  3634. {
  3635. if (s_cl5Desc.dbTrim.lock)
  3636. PR_DestroyLock (s_cl5Desc.dbTrim.lock);
  3637. memset (&s_cl5Desc.dbTrim, 0, sizeof (s_cl5Desc.dbTrim));
  3638. }
  3639. static int _cl5TrimMain (void *param)
  3640. {
  3641. PRIntervalTime interval;
  3642. time_t timePrev = current_time ();
  3643. time_t timeNow;
  3644. PR_AtomicIncrement (&s_cl5Desc.threadCount);
  3645. interval = PR_SecondsToInterval(CHANGELOGDB_TRIM_INTERVAL);
  3646. while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
  3647. {
  3648. timeNow = current_time ();
  3649. if (timeNow - timePrev >= CHANGELOGDB_TRIM_INTERVAL)
  3650. {
  3651. /* time to trim */
  3652. timePrev = timeNow;
  3653. _cl5DoTrimming ();
  3654. }
  3655. if (NULL == s_cl5Desc.clLock)
  3656. {
  3657. /* most likely, emergency */
  3658. break;
  3659. }
  3660. PR_Lock(s_cl5Desc.clLock);
  3661. PR_WaitCondVar(s_cl5Desc.clCvar, interval);
  3662. PR_Unlock(s_cl5Desc.clLock);
  3663. }
  3664. PR_AtomicDecrement (&s_cl5Desc.threadCount);
  3665. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5TrimMain: exiting\n");
  3666. return 0;
  3667. }
  3668. /* We remove an entry if it has been replayed to all consumers and
  3669. and the number of entries in the changelog is larger than maxEntries
  3670. or age of the entry is larger than maxAge.
  3671. Also we can't purge entries which correspond to max csns in the
  3672. supplier's ruv. Here is a example where we can get into trouble:
  3673. The server is setup with time based trimming and no consumer's
  3674. At some point all the entries are trimmed from the changelog.
  3675. At a later point a consumer is added and initialized online
  3676. Then a change is made on the supplier.
  3677. To update the consumer, the supplier would attempt to locate
  3678. the last change sent to the consumer in the changelog and will
  3679. fail because the change was removed.
  3680. */
  3681. static void _cl5DoTrimming ()
  3682. {
  3683. Object *obj;
  3684. long numToTrim;
  3685. PR_Lock (s_cl5Desc.dbTrim.lock);
  3686. /* ONREPL We trim file by file which means that some files will be
  3687. trimmed more often than other. We might have to fix that by, for
  3688. example, randomizing starting point */
  3689. obj = objset_first_obj (s_cl5Desc.dbFiles);
  3690. while (obj && _cl5CanTrim ((time_t)0, &numToTrim))
  3691. {
  3692. _cl5TrimFile (obj, &numToTrim);
  3693. obj = objset_next_obj (s_cl5Desc.dbFiles, obj);
  3694. }
  3695. if (obj)
  3696. object_release (obj);
  3697. PR_Unlock (s_cl5Desc.dbTrim.lock);
  3698. return;
  3699. }
  3700. /* Note that each file contains changes for a single replicated area.
  3701. trimming algorithm:
  3702. */
  3703. #define CL5_TRIM_MAX_PER_TRANSACTION 10
  3704. static void _cl5TrimFile (Object *obj, long *numToTrim)
  3705. {
  3706. DB_TXN *txnid;
  3707. RUV *ruv = NULL;
  3708. CL5Entry entry;
  3709. slapi_operation_parameters op = {0};
  3710. void *it;
  3711. int finished = 0, totalTrimmed = 0, count;
  3712. PRBool abort;
  3713. char strCSN[CSN_STRSIZE];
  3714. int rc;
  3715. PR_ASSERT (obj);
  3716. /* construct the ruv up to which we can purge */
  3717. rc = _cl5GetRUV2Purge2 (obj, &ruv);
  3718. if (rc != CL5_SUCCESS || ruv == NULL)
  3719. {
  3720. return;
  3721. }
  3722. entry.op = &op;
  3723. while ( !finished && !g_get_shutdown() )
  3724. {
  3725. it = NULL;
  3726. count = 0;
  3727. txnid = NULL;
  3728. abort = PR_FALSE;
  3729. /* DB txn lock accessed pages until the end of the transaction. */
  3730. rc = TXN_BEGIN(s_cl5Desc.dbEnv, NULL, &txnid, 0);
  3731. if (rc != 0)
  3732. {
  3733. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3734. "_cl5TrimFile: failed to begin transaction; db error - %d %s\n",
  3735. rc, db_strerror(rc));
  3736. finished = PR_TRUE;
  3737. break;
  3738. }
  3739. finished = _cl5GetFirstEntry (obj, &entry, &it, txnid);
  3740. while ( !finished )
  3741. {
  3742. /*
  3743. * This change can be trimmed if it exceeds purge
  3744. * parameters and has been seen by all consumers.
  3745. */
  3746. if ( (*numToTrim > 0 || _cl5CanTrim (entry.time, numToTrim)) &&
  3747. ruv_covers_csn_strict (ruv, op.csn) )
  3748. {
  3749. rc = _cl5CurrentDeleteEntry (it);
  3750. if ( rc == CL5_SUCCESS )
  3751. {
  3752. /* update purge vector */
  3753. rc = _cl5UpdateRUV (obj, op.csn, PR_FALSE, PR_TRUE);
  3754. }
  3755. if ( rc == CL5_SUCCESS)
  3756. {
  3757. if (*numToTrim > 0) (*numToTrim)--;
  3758. count++;
  3759. }
  3760. else
  3761. {
  3762. /* The above two functions have logged the error */
  3763. abort = PR_TRUE;
  3764. }
  3765. }
  3766. else
  3767. {
  3768. /* The changelog DB is time ordered. If we can not trim
  3769. * a CSN, we will not be allowed to trim the rest of the
  3770. * CSNs generally. However, the maxcsn of each replica ID
  3771. * is always kept in the changelog as an anchor for
  3772. * replaying future changes. We have to skip those anchor
  3773. * CSNs, otherwise a non-active replica ID could block
  3774. * the trim forever.
  3775. */
  3776. CSN *maxcsn = NULL;
  3777. ReplicaId rid;
  3778. rid = csn_get_replicaid (op.csn);
  3779. ruv_get_largest_csn_for_replica (ruv, rid, &maxcsn);
  3780. if ( csn_compare (op.csn, maxcsn) != 0 )
  3781. {
  3782. /* op.csn is not anchor CSN */
  3783. finished = 1;
  3784. }
  3785. else
  3786. {
  3787. slapi_log_error (SLAPI_LOG_REPL, NULL,
  3788. "Changelog purge skipped anchor csn %s\n",
  3789. csn_as_string (maxcsn, PR_FALSE, strCSN));
  3790. /* extra read to skip the current record */
  3791. cl5_operation_parameters_done (&op);
  3792. finished =_cl5GetNextEntry (&entry, it);
  3793. }
  3794. if (maxcsn) csn_free (&maxcsn);
  3795. }
  3796. cl5_operation_parameters_done (&op);
  3797. if (finished || abort || count >= CL5_TRIM_MAX_PER_TRANSACTION)
  3798. {
  3799. /* If we reach CL5_TRIM_MAX_PER_TRANSACTION,
  3800. * we close the cursor,
  3801. * commit the transaction and restart a new transaction
  3802. */
  3803. break;
  3804. }
  3805. finished = _cl5GetNextEntry (&entry, it);
  3806. }
  3807. /* MAB: We need to close the cursor BEFORE the txn commits/aborts.
  3808. * If we don't respect this order, we'll screw up the database,
  3809. * placing it in DB_RUNRECOVERY mode
  3810. */
  3811. cl5DestroyIterator (it);
  3812. if (abort)
  3813. {
  3814. finished = 1;
  3815. rc = TXN_ABORT (txnid);
  3816. if (rc != 0)
  3817. {
  3818. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3819. "_cl5TrimFile: failed to abort transaction; db error - %d %s\n",
  3820. rc, db_strerror(rc));
  3821. }
  3822. }
  3823. else
  3824. {
  3825. rc = TXN_COMMIT (txnid, 0);
  3826. if (rc != 0)
  3827. {
  3828. finished = 1;
  3829. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3830. "_cl5TrimFile: failed to commit transaction; db error - %d %s\n",
  3831. rc, db_strerror(rc));
  3832. }
  3833. else
  3834. {
  3835. totalTrimmed += count;
  3836. }
  3837. }
  3838. } /* While (!finished) */
  3839. if (ruv)
  3840. ruv_destroy (&ruv);
  3841. if (totalTrimmed)
  3842. {
  3843. slapi_log_error (SLAPI_LOG_REPL, NULL, "Trimmed %d changes from the changelog\n", totalTrimmed);
  3844. }
  3845. }
  3846. static PRBool _cl5CanTrim (time_t time, long *numToTrim)
  3847. {
  3848. *numToTrim = 0;
  3849. if (s_cl5Desc.dbTrim.maxAge == 0 && s_cl5Desc.dbTrim.maxEntries == 0)
  3850. return PR_FALSE;
  3851. if (s_cl5Desc.dbTrim.maxAge == 0)
  3852. {
  3853. *numToTrim = cl5GetOperationCount (NULL) - s_cl5Desc.dbTrim.maxEntries;
  3854. return ( *numToTrim > 0 );
  3855. }
  3856. if (s_cl5Desc.dbTrim.maxEntries > 0 &&
  3857. (*numToTrim = cl5GetOperationCount (NULL) - s_cl5Desc.dbTrim.maxEntries) > 0)
  3858. return PR_TRUE;
  3859. if (time)
  3860. return (current_time () - time > s_cl5Desc.dbTrim.maxAge);
  3861. else
  3862. return PR_TRUE;
  3863. }
  3864. static int _cl5ReadRUV (const char *replGen, Object *obj, PRBool purge)
  3865. {
  3866. int rc;
  3867. char csnStr [CSN_STRSIZE];
  3868. DBT key={0}, data={0};
  3869. struct berval **vals = NULL;
  3870. CL5DBFile *file;
  3871. char *pos;
  3872. char *agmt_name;
  3873. PR_ASSERT (replGen && obj);
  3874. file = (CL5DBFile*)object_get_data (obj);
  3875. PR_ASSERT (file);
  3876. agmt_name = get_thread_private_agmtname();
  3877. if (purge) /* read purge vector entry */
  3878. key.data = _cl5GetHelperEntryKey (PURGE_RUV_TIME, csnStr);
  3879. else /* read upper bound vector */
  3880. key.data = _cl5GetHelperEntryKey (MAX_RUV_TIME, csnStr);
  3881. key.size = CSN_STRSIZE;
  3882. data.flags = DB_DBT_MALLOC;
  3883. rc = file->db->get(file->db, NULL/*txn*/, &key, &data, 0);
  3884. switch (rc)
  3885. {
  3886. case 0: pos = data.data;
  3887. rc = _cl5ReadBervals (&vals, &pos, data.size);
  3888. slapi_ch_free (&(data.data));
  3889. if (rc != CL5_SUCCESS)
  3890. goto done;
  3891. if (purge)
  3892. rc = ruv_init_from_bervals(vals, &file->purgeRUV);
  3893. else
  3894. rc = ruv_init_from_bervals(vals, &file->maxRUV);
  3895. if (rc != RUV_SUCCESS)
  3896. {
  3897. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  3898. "%s: _cl5ReadRUV: failed to initialize %s ruv; "
  3899. "RUV error %d\n", agmt_name, purge? "purge" : "upper bound", rc);
  3900. rc = CL5_RUV_ERROR;
  3901. goto done;
  3902. }
  3903. /* delete the entry; it is re-added when file
  3904. is successfully closed */
  3905. file->db->del (file->db, NULL, &key, DEFAULT_DB_OP_FLAGS);
  3906. rc = CL5_SUCCESS;
  3907. goto done;
  3908. case DB_NOTFOUND: /* RUV is lost - need to construct */
  3909. rc = _cl5ConstructRUV (replGen, obj, purge);
  3910. goto done;
  3911. default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3912. "%s: _cl5ReadRUV: failed to get purge RUV; "
  3913. "db error - %d %s\n", agmt_name, rc, db_strerror(rc));
  3914. rc = CL5_DB_ERROR;
  3915. goto done;
  3916. }
  3917. done:
  3918. ber_bvecfree(vals);
  3919. return rc;
  3920. }
  3921. static int _cl5WriteRUV (CL5DBFile *file, PRBool purge)
  3922. {
  3923. int rc;
  3924. DBT key={0}, data={0};
  3925. char csnStr [CSN_STRSIZE];
  3926. struct berval **vals;
  3927. DB_TXN *txnid = NULL;
  3928. char *buff;
  3929. if ((purge && file->purgeRUV == NULL) || (!purge && file->maxRUV == NULL))
  3930. return CL5_SUCCESS;
  3931. if (purge)
  3932. {
  3933. key.data = _cl5GetHelperEntryKey (PURGE_RUV_TIME, csnStr);
  3934. rc = ruv_to_bervals(file->purgeRUV, &vals);
  3935. }
  3936. else
  3937. {
  3938. key.data = _cl5GetHelperEntryKey (MAX_RUV_TIME, csnStr);
  3939. rc = ruv_to_bervals(file->maxRUV, &vals);
  3940. }
  3941. key.size = CSN_STRSIZE;
  3942. rc = _cl5WriteBervals (vals, &buff, &data.size);
  3943. data.data = buff;
  3944. ber_bvecfree(vals);
  3945. if (rc != CL5_SUCCESS)
  3946. {
  3947. return rc;
  3948. }
  3949. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  3950. rc = txn_begin(s_cl5Desc.dbEnv, NULL, &txnid, 0);
  3951. if (rc != 0)
  3952. {
  3953. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3954. "_cl5WriteRUV: failed to begin transaction; db error - %d %s\n",
  3955. rc, db_strerror(rc));
  3956. return CL5_DB_ERROR;
  3957. }
  3958. #endif
  3959. rc = file->db->put(file->db, txnid, &key, &data, DEFAULT_DB_OP_FLAGS);
  3960. slapi_ch_free (&(data.data));
  3961. if ( rc == 0 )
  3962. {
  3963. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  3964. rc = txn_commit (txnid, 0);
  3965. if (rc != 0)
  3966. {
  3967. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3968. "_cl5WriteRUV: failed to commit transaction; db error - %d %s\n",
  3969. rc, db_strerror(rc));
  3970. return CL5_DB_ERROR;
  3971. }
  3972. #endif
  3973. return CL5_SUCCESS;
  3974. }
  3975. else
  3976. {
  3977. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3978. "_cl5WriteRUV: failed to write %s RUV for file %s; db error - %d (%s)\n",
  3979. purge? "purge" : "upper bound", file->name, rc, db_strerror(rc));
  3980. if (CL5_OS_ERR_IS_DISKFULL(rc))
  3981. {
  3982. cl5_set_diskfull();
  3983. return CL5_DB_ERROR;
  3984. }
  3985. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  3986. rc = txn_abort (txnid);
  3987. if (rc != 0)
  3988. {
  3989. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  3990. "_cl5WriteRUV: failed to abort transaction; db error - %d %s\n",
  3991. rc, db_strerror(rc));
  3992. }
  3993. #endif
  3994. return CL5_DB_ERROR;
  3995. }
  3996. }
  3997. /* This is a very slow process since we have to read every changelog entry.
  3998. Hopefully, this function is not called too often */
  3999. static int _cl5ConstructRUV (const char *replGen, Object *obj, PRBool purge)
  4000. {
  4001. int rc;
  4002. CL5Entry entry;
  4003. void *iterator = NULL;
  4004. slapi_operation_parameters op = {0};
  4005. CL5DBFile *file;
  4006. PR_ASSERT (replGen && obj);
  4007. file = (CL5DBFile*)object_get_data (obj);
  4008. PR_ASSERT (file);
  4009. /* construct the RUV */
  4010. if (purge)
  4011. rc = ruv_init_new (replGen, 0, NULL, &file->purgeRUV);
  4012. else
  4013. rc = ruv_init_new (replGen, 0, NULL, &file->maxRUV);
  4014. if (rc != RUV_SUCCESS)
  4015. {
  4016. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5ConstructRUV: "
  4017. "failed to initialize %s RUV for file %s; ruv error - %d\n",
  4018. purge? "purge" : "upper bound", file->name, rc);
  4019. return CL5_RUV_ERROR;
  4020. }
  4021. entry.op = &op;
  4022. rc = _cl5GetFirstEntry (obj, &entry, &iterator, NULL);
  4023. while (rc == CL5_SUCCESS)
  4024. {
  4025. if (purge)
  4026. rc = ruv_set_csns_keep_smallest(file->purgeRUV, op.csn);
  4027. else
  4028. rc = ruv_set_csns (file->maxRUV, op.csn, NULL);
  4029. cl5_operation_parameters_done (&op);
  4030. if (rc != RUV_SUCCESS)
  4031. {
  4032. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5ConstructRUV: "
  4033. "failed to updated %s RUV for file %s; ruv error - %d\n",
  4034. purge ? "purge" : "upper bound", file->name, rc);
  4035. rc = CL5_RUV_ERROR;
  4036. continue;
  4037. }
  4038. rc = _cl5GetNextEntry (&entry, iterator);
  4039. }
  4040. cl5_operation_parameters_done (&op);
  4041. if (iterator)
  4042. cl5DestroyIterator (iterator);
  4043. if (rc == CL5_NOTFOUND)
  4044. {
  4045. rc = CL5_SUCCESS;
  4046. }
  4047. else
  4048. {
  4049. if (purge)
  4050. ruv_destroy (&file->purgeRUV);
  4051. else
  4052. ruv_destroy (&file->maxRUV);
  4053. }
  4054. return rc;
  4055. }
  4056. static int _cl5UpdateRUV (Object *obj, CSN *csn, PRBool newReplica, PRBool purge)
  4057. {
  4058. ReplicaId rid;
  4059. int rc = RUV_SUCCESS; /* initialize rc to avoid erroneous logs */
  4060. CL5DBFile *file;
  4061. PR_ASSERT (obj && csn);
  4062. file = (CL5DBFile*)object_get_data (obj);
  4063. /* if purge is TRUE, file->purgeRUV must be set;
  4064. if purge is FALSE, maxRUV must be set */
  4065. PR_ASSERT (file && ((purge && file->purgeRUV) || (!purge && file->maxRUV)));
  4066. /* update vector only if this replica is not yet part of RUV */
  4067. if (purge && newReplica)
  4068. {
  4069. rid = csn_get_replicaid(csn);
  4070. if (ruv_contains_replica (file->purgeRUV, rid))
  4071. return CL5_SUCCESS;
  4072. else
  4073. {
  4074. /* if the replica is not part of the purgeRUV yet, add it */
  4075. ruv_add_replica (file->purgeRUV, rid, multimaster_get_local_purl());
  4076. }
  4077. }
  4078. else
  4079. {
  4080. if (purge)
  4081. rc = ruv_set_csns(file->purgeRUV, csn, NULL);
  4082. else
  4083. rc = ruv_set_csns(file->maxRUV, csn, NULL);
  4084. }
  4085. if (rc != RUV_SUCCESS)
  4086. {
  4087. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5UpdatePurgeRUV: "
  4088. "failed to update %s RUV for file %s; ruv error - %d\n",
  4089. purge ? "purge" : "upper bound", file->name, rc);
  4090. return CL5_RUV_ERROR;
  4091. }
  4092. return CL5_SUCCESS;
  4093. }
  4094. static int _cl5EnumConsumerRUV (const ruv_enum_data *element, void *arg)
  4095. {
  4096. int rc;
  4097. RUV *ruv;
  4098. CSN *csn = NULL;
  4099. PR_ASSERT (element && element->csn && arg);
  4100. ruv = (RUV*)arg;
  4101. rc = ruv_get_largest_csn_for_replica(ruv, csn_get_replicaid (element->csn), &csn);
  4102. if (rc != RUV_SUCCESS || csn == NULL || csn_compare (element->csn, csn) < 0)
  4103. {
  4104. ruv_set_max_csn(ruv, element->csn, NULL);
  4105. }
  4106. if (csn)
  4107. csn_free (&csn);
  4108. return 0;
  4109. }
  4110. static int _cl5GetRUV2Purge2 (Object *fileObj, RUV **ruv)
  4111. {
  4112. int rc = CL5_SUCCESS;
  4113. CL5DBFile *dbFile;
  4114. Object *rObj = NULL;
  4115. Replica *r = NULL;
  4116. Object *agmtObj = NULL;
  4117. Repl_Agmt *agmt;
  4118. Object *consRUVObj, *supRUVObj;
  4119. RUV *consRUV, *supRUV;
  4120. CSN *csn;
  4121. PR_ASSERT (fileObj && ruv);
  4122. if (!ruv) {
  4123. rc = CL5_UNKNOWN_ERROR;
  4124. goto done;
  4125. }
  4126. dbFile = (CL5DBFile*)object_get_data (fileObj);
  4127. PR_ASSERT (dbFile);
  4128. rObj = replica_get_by_name (dbFile->replName);
  4129. PR_ASSERT (rObj);
  4130. if (!rObj) {
  4131. rc = CL5_NOTFOUND;
  4132. goto done;
  4133. }
  4134. r = (Replica*)object_get_data (rObj);
  4135. PR_ASSERT (r);
  4136. /* We start with this replica's RUV. See note in _cl5DoTrimming */
  4137. supRUVObj = replica_get_ruv (r);
  4138. PR_ASSERT (supRUVObj);
  4139. supRUV = (RUV*)object_get_data (supRUVObj);
  4140. PR_ASSERT (supRUV);
  4141. *ruv = ruv_dup (supRUV);
  4142. object_release (supRUVObj);
  4143. agmtObj = agmtlist_get_first_agreement_for_replica (r);
  4144. while (agmtObj)
  4145. {
  4146. agmt = (Repl_Agmt*)object_get_data (agmtObj);
  4147. PR_ASSERT (agmt);
  4148. consRUVObj = agmt_get_consumer_ruv (agmt);
  4149. if (consRUVObj)
  4150. {
  4151. consRUV = (RUV*)object_get_data (consRUVObj);
  4152. rc = ruv_enumerate_elements (consRUV, _cl5EnumConsumerRUV, *ruv);
  4153. if (rc != RUV_SUCCESS)
  4154. {
  4155. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5GetRUV2Purge2: "
  4156. "failed to construct ruv; ruv error - %d\n", rc);
  4157. rc = CL5_RUV_ERROR;
  4158. object_release (consRUVObj);
  4159. object_release (agmtObj);
  4160. break;
  4161. }
  4162. object_release (consRUVObj);
  4163. }
  4164. agmtObj = agmtlist_get_next_agreement_for_replica (r, agmtObj);
  4165. }
  4166. /* check if there is any data in the constructed ruv - otherwise get rid of it */
  4167. if (ruv_get_max_csn(*ruv, &csn) != RUV_SUCCESS || csn == NULL)
  4168. {
  4169. ruv_destroy (ruv);
  4170. }
  4171. else
  4172. {
  4173. csn_free (&csn);
  4174. }
  4175. done:
  4176. if (rObj)
  4177. object_release (rObj);
  4178. if (rc != CL5_SUCCESS && ruv)
  4179. ruv_destroy (ruv);
  4180. return rc;
  4181. }
  4182. static int _cl5GetEntryCount (CL5DBFile *file)
  4183. {
  4184. int rc;
  4185. char csnStr [CSN_STRSIZE];
  4186. DBT key={0}, data={0};
  4187. DB_BTREE_STAT *stats = NULL;
  4188. PR_ASSERT (file);
  4189. /* read entry count. if the entry is there - the file was successfully closed
  4190. last time it was used */
  4191. key.data = _cl5GetHelperEntryKey (ENTRY_COUNT_TIME, csnStr);
  4192. key.size = CSN_STRSIZE;
  4193. data.flags = DB_DBT_MALLOC;
  4194. rc = file->db->get(file->db, NULL/*txn*/, &key, &data, 0);
  4195. switch (rc)
  4196. {
  4197. case 0: file->entryCount = *(int*)data.data;
  4198. slapi_ch_free (&(data.data));
  4199. /* delete the entry. the entry is re-added when file
  4200. is successfully closed */
  4201. file->db->del (file->db, NULL, &key, DEFAULT_DB_OP_FLAGS);
  4202. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4203. "_cl5GetEntryCount: %d changes for replica %s\n",
  4204. file->entryCount, file->replName);
  4205. return CL5_SUCCESS;
  4206. case DB_NOTFOUND: file->entryCount = 0;
  4207. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4300
  4208. rc = file->db->stat(file->db, NULL, (void*)&stats, 0);
  4209. #elif 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
  4210. rc = file->db->stat(file->db, (void*)&stats, 0);
  4211. #else
  4212. rc = file->db->stat(file->db, (void*)&stats, (void *)slapi_ch_malloc, 0);
  4213. #endif
  4214. if (rc != 0)
  4215. {
  4216. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4217. "_cl5GetEntryCount: failed to get changelog statistics; "
  4218. "db error - %d %s\n", rc, db_strerror(rc));
  4219. return CL5_DB_ERROR;
  4220. }
  4221. #ifdef DB30
  4222. file->entryCount = stats->bt_nrecs;
  4223. #else /* DB31 */
  4224. file->entryCount = stats->bt_ndata;
  4225. #endif
  4226. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4227. "_cl5GetEntryCount: %d changes for replica %s\n",
  4228. file->entryCount, file->replName);
  4229. slapi_ch_free ((void **)&stats);
  4230. return CL5_SUCCESS;
  4231. default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4232. "_cl5GetEntryCount: failed to get count entry; "
  4233. "db error - %d %s\n", rc, db_strerror(rc));
  4234. return CL5_DB_ERROR;
  4235. }
  4236. }
  4237. static int _cl5WriteEntryCount (CL5DBFile *file)
  4238. {
  4239. int rc;
  4240. DBT key={0}, data={0};
  4241. char csnStr [CSN_STRSIZE];
  4242. DB_TXN *txnid = NULL;
  4243. key.data = _cl5GetHelperEntryKey (ENTRY_COUNT_TIME, csnStr);
  4244. key.size = CSN_STRSIZE;
  4245. data.data = (void*)&file->entryCount;
  4246. data.size = sizeof (file->entryCount);
  4247. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  4248. rc = txn_begin(s_cl5Desc.dbEnv, NULL, &txnid, 0);
  4249. if (rc != 0)
  4250. {
  4251. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4252. "_cl5WriteEntryCount: failed to begin transaction; db error - %d %s\n",
  4253. rc, db_strerror(rc));
  4254. return CL5_DB_ERROR;
  4255. }
  4256. #endif
  4257. rc = file->db->put(file->db, txnid, &key, &data, DEFAULT_DB_OP_FLAGS);
  4258. if (rc == 0)
  4259. {
  4260. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  4261. rc = txn_commit (txnid, 0);
  4262. if (rc != 0)
  4263. {
  4264. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4265. "_cl5WriteEntryCount: failed to commit transaction; db error - %d %s\n",
  4266. rc, db_strerror(rc));
  4267. return CL5_DB_ERROR;
  4268. }
  4269. #endif
  4270. return CL5_SUCCESS;
  4271. }
  4272. else
  4273. {
  4274. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4275. "_cl5WriteEntryCount: "
  4276. "failed to write count entry for file %s; db error - %d %s\n",
  4277. file->name, rc, db_strerror(rc));
  4278. if (CL5_OS_ERR_IS_DISKFULL(rc))
  4279. {
  4280. cl5_set_diskfull();
  4281. return CL5_DB_ERROR;
  4282. }
  4283. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  4284. rc = txn_abort (txnid);
  4285. if (rc != 0)
  4286. {
  4287. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4288. "_cl5WriteEntryCount: failed to abort transaction; db error - %d %s\n",
  4289. rc, db_strerror(rc));
  4290. }
  4291. #endif
  4292. return CL5_DB_ERROR;
  4293. }
  4294. }
  4295. static const char* _cl5OperationType2Str (int type)
  4296. {
  4297. switch (type)
  4298. {
  4299. case SLAPI_OPERATION_ADD: return T_ADDCTSTR;
  4300. case SLAPI_OPERATION_MODIFY: return T_MODIFYCTSTR;
  4301. case SLAPI_OPERATION_MODRDN: return T_MODRDNCTSTR;
  4302. case SLAPI_OPERATION_DELETE: return T_DELETECTSTR;
  4303. default: return NULL;
  4304. }
  4305. }
  4306. static int _cl5Str2OperationType (const char *str)
  4307. {
  4308. if (strcasecmp (str, T_ADDCTSTR) == 0)
  4309. return SLAPI_OPERATION_ADD;
  4310. if (strcasecmp (str, T_MODIFYCTSTR) == 0)
  4311. return SLAPI_OPERATION_MODIFY;
  4312. if (strcasecmp (str, T_MODRDNCTSTR) == 0)
  4313. return SLAPI_OPERATION_MODRDN;
  4314. if (strcasecmp (str, T_DELETECTSTR) == 0)
  4315. return SLAPI_OPERATION_DELETE;
  4316. return -1;
  4317. }
  4318. static int _cl5Operation2LDIF (const slapi_operation_parameters *op, const char *replGen,
  4319. char **ldifEntry, PRInt32 *lenLDIF)
  4320. {
  4321. int len = 2;
  4322. lenstr *l = NULL;
  4323. const char *strType;
  4324. char *strDeleteOldRDN;
  4325. char *buff, *start;
  4326. LDAPMod **add_mods;
  4327. char *rawDN = NULL;
  4328. char strCSN[CSN_STRSIZE];
  4329. PR_ASSERT (op && replGen && ldifEntry && IsValidOperation (op));
  4330. strType = _cl5OperationType2Str (op->operation_type);
  4331. csn_as_string(op->csn,PR_FALSE,strCSN);
  4332. /* find length of the buffer */
  4333. len += LDIF_SIZE_NEEDED(strlen (T_CHANGETYPESTR), strlen (strType));
  4334. len += LDIF_SIZE_NEEDED(strlen (T_REPLGEN), strlen (replGen));
  4335. len += LDIF_SIZE_NEEDED(strlen (T_CSNSTR), strlen (strCSN));
  4336. len += LDIF_SIZE_NEEDED(strlen (T_UNIQUEIDSTR), strlen (op->target_address.uniqueid));
  4337. switch (op->operation_type)
  4338. {
  4339. case SLAPI_OPERATION_ADD: if (op->p.p_add.parentuniqueid)
  4340. len += LDIF_SIZE_NEEDED(strlen (T_PARENTIDSTR),
  4341. strlen (op->p.p_add.parentuniqueid));
  4342. slapi_entry2mods (op->p.p_add.target_entry, &rawDN, &add_mods);
  4343. len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (rawDN));
  4344. l = make_changes_string(add_mods, NULL);
  4345. len += LDIF_SIZE_NEEDED(strlen (T_CHANGESTR), l->ls_len);
  4346. ldap_mods_free (add_mods, 1);
  4347. break;
  4348. case SLAPI_OPERATION_MODIFY: len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (op->target_address.dn));
  4349. l = make_changes_string(op->p.p_modify.modify_mods, NULL);
  4350. len += LDIF_SIZE_NEEDED(strlen (T_CHANGESTR), l->ls_len);
  4351. break;
  4352. case SLAPI_OPERATION_MODRDN: len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (op->target_address.dn));
  4353. len += LDIF_SIZE_NEEDED(strlen (T_NEWRDNSTR),
  4354. strlen (op->p.p_modrdn.modrdn_newrdn));
  4355. strDeleteOldRDN = (op->p.p_modrdn.modrdn_deloldrdn ? "true" : "false");
  4356. len += LDIF_SIZE_NEEDED(strlen (T_DRDNFLAGSTR),
  4357. strlen (strDeleteOldRDN));
  4358. if (op->p.p_modrdn.modrdn_newsuperior_address.dn)
  4359. len += LDIF_SIZE_NEEDED(strlen (T_NEWSUPERIORDNSTR),
  4360. strlen (op->p.p_modrdn.modrdn_newsuperior_address.dn));
  4361. if (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid)
  4362. len += LDIF_SIZE_NEEDED(strlen (T_NEWSUPERIORIDSTR),
  4363. strlen (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid));
  4364. l = make_changes_string(op->p.p_modrdn.modrdn_mods, NULL);
  4365. len += LDIF_SIZE_NEEDED(strlen (T_CHANGESTR), l->ls_len);
  4366. break;
  4367. case SLAPI_OPERATION_DELETE: len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (op->target_address.dn));
  4368. break;
  4369. default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4370. "_cl5Operation2LDIF: invalid operation type - %lu\n", op->operation_type);
  4371. return CL5_BAD_FORMAT;
  4372. }
  4373. /* allocate buffer */
  4374. buff = slapi_ch_malloc (len);
  4375. start = buff;
  4376. if (buff == NULL)
  4377. {
  4378. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4379. "_cl5Operation2LDIF: memory allocation failed\n");
  4380. return CL5_MEMORY_ERROR;
  4381. }
  4382. /* fill buffer */
  4383. slapi_ldif_put_type_and_value_with_options(&buff, T_CHANGETYPESTR, (char*)strType, strlen (strType), 0);
  4384. slapi_ldif_put_type_and_value_with_options(&buff, T_REPLGEN, (char*)replGen, strlen (replGen), 0);
  4385. slapi_ldif_put_type_and_value_with_options(&buff, T_CSNSTR, (char*)strCSN, strlen (strCSN), 0);
  4386. slapi_ldif_put_type_and_value_with_options(&buff, T_UNIQUEIDSTR, op->target_address.uniqueid,
  4387. strlen (op->target_address.uniqueid), 0);
  4388. switch (op->operation_type)
  4389. {
  4390. case SLAPI_OPERATION_ADD: if (op->p.p_add.parentuniqueid)
  4391. slapi_ldif_put_type_and_value_with_options(&buff, T_PARENTIDSTR,
  4392. op->p.p_add.parentuniqueid, strlen (op->p.p_add.parentuniqueid), 0);
  4393. slapi_ldif_put_type_and_value_with_options(&buff, T_DNSTR, rawDN, strlen (rawDN), 0);
  4394. slapi_ldif_put_type_and_value_with_options(&buff, T_CHANGESTR, l->ls_buf, l->ls_len, 0);
  4395. slapi_ch_free ((void**)&rawDN);
  4396. break;
  4397. case SLAPI_OPERATION_MODIFY: slapi_ldif_put_type_and_value_with_options(&buff, T_DNSTR, op->target_address.dn,
  4398. strlen (op->target_address.dn), 0);
  4399. slapi_ldif_put_type_and_value_with_options(&buff, T_CHANGESTR, l->ls_buf, l->ls_len, 0);
  4400. break;
  4401. case SLAPI_OPERATION_MODRDN: slapi_ldif_put_type_and_value_with_options(&buff, T_DNSTR, op->target_address.dn,
  4402. strlen (op->target_address.dn), 0);
  4403. slapi_ldif_put_type_and_value_with_options(&buff, T_NEWRDNSTR, op->p.p_modrdn.modrdn_newrdn,
  4404. strlen (op->p.p_modrdn.modrdn_newrdn), 0);
  4405. slapi_ldif_put_type_and_value_with_options(&buff, T_DRDNFLAGSTR, strDeleteOldRDN,
  4406. strlen (strDeleteOldRDN), 0);
  4407. if (op->p.p_modrdn.modrdn_newsuperior_address.dn)
  4408. slapi_ldif_put_type_and_value_with_options(&buff, T_NEWSUPERIORDNSTR,
  4409. op->p.p_modrdn.modrdn_newsuperior_address.dn,
  4410. strlen (op->p.p_modrdn.modrdn_newsuperior_address.dn), 0);
  4411. if (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid)
  4412. slapi_ldif_put_type_and_value_with_options(&buff, T_NEWSUPERIORIDSTR,
  4413. op->p.p_modrdn.modrdn_newsuperior_address.uniqueid,
  4414. strlen (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid), 0);
  4415. slapi_ldif_put_type_and_value_with_options(&buff, T_CHANGESTR, l->ls_buf, l->ls_len, 0);
  4416. break;
  4417. case SLAPI_OPERATION_DELETE: slapi_ldif_put_type_and_value_with_options(&buff, T_DNSTR, op->target_address.dn,
  4418. strlen (op->target_address.dn), 0);
  4419. break;
  4420. }
  4421. *buff = '\n';
  4422. buff ++;
  4423. *buff = '\0';
  4424. *ldifEntry = start;
  4425. *lenLDIF = buff - start;
  4426. if (l)
  4427. lenstr_free(&l);
  4428. return CL5_SUCCESS;
  4429. }
  4430. static int
  4431. _cl5LDIF2Operation (char *ldifEntry, slapi_operation_parameters *op, char **replGen)
  4432. {
  4433. int rc;
  4434. char *next, *line;
  4435. struct berval type, value;
  4436. struct berval bv_null = {0, NULL};
  4437. int freeval = 0;
  4438. Slapi_Mods *mods;
  4439. char *rawDN = NULL;
  4440. PR_ASSERT (op && ldifEntry && replGen);
  4441. memset (op, 0, sizeof (*op));
  4442. next = ldifEntry;
  4443. while ((line = ldif_getline(&next)) != NULL)
  4444. {
  4445. if ( *line == '\n' || *line == '\0' )
  4446. {
  4447. break;
  4448. }
  4449. /* this call modifies ldifEntry */
  4450. type = bv_null;
  4451. value = bv_null;
  4452. rc = slapi_ldif_parse_line(line, &type, &value, &freeval);
  4453. if (rc != 0)
  4454. {
  4455. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4456. "_cl5LDIF2Operation: warning - failed to parse ldif line\n");
  4457. continue;
  4458. }
  4459. if (strncasecmp (type.bv_val, T_CHANGETYPESTR, type.bv_len) == 0)
  4460. {
  4461. op->operation_type = _cl5Str2OperationType (value.bv_val);
  4462. }
  4463. else if (strncasecmp (type.bv_val, T_REPLGEN, type.bv_len) == 0)
  4464. {
  4465. *replGen = slapi_ch_strdup (value.bv_val);
  4466. }
  4467. else if (strncasecmp (type.bv_val, T_CSNSTR, type.bv_len) == 0)
  4468. {
  4469. op->csn = csn_new_by_string(value.bv_val);
  4470. }
  4471. else if (strncasecmp (type.bv_val, T_UNIQUEIDSTR, type.bv_len) == 0)
  4472. {
  4473. op->target_address.uniqueid = slapi_ch_strdup (value.bv_val);
  4474. }
  4475. else if (strncasecmp (type.bv_val, T_DNSTR, type.bv_len) == 0)
  4476. {
  4477. PR_ASSERT (op->operation_type);
  4478. if (op->operation_type == SLAPI_OPERATION_ADD)
  4479. {
  4480. rawDN = slapi_ch_strdup (value.bv_val);
  4481. op->target_address.dn = slapi_ch_strdup(rawDN);
  4482. }
  4483. else
  4484. op->target_address.dn = slapi_ch_strdup (value.bv_val);
  4485. }
  4486. else if (strncasecmp (type.bv_val, T_PARENTIDSTR, type.bv_len) == 0)
  4487. {
  4488. op->p.p_add.parentuniqueid = slapi_ch_strdup (value.bv_val);
  4489. }
  4490. else if (strncasecmp (type.bv_val, T_NEWRDNSTR, type.bv_len) == 0)
  4491. {
  4492. op->p.p_modrdn.modrdn_newrdn = slapi_ch_strdup (value.bv_val);
  4493. }
  4494. else if (strncasecmp (type.bv_val, T_DRDNFLAGSTR, type.bv_len) == 0)
  4495. {
  4496. op->p.p_modrdn.modrdn_deloldrdn = (strncasecmp (value.bv_val, "true", value.bv_len) ? PR_FALSE : PR_TRUE);
  4497. }
  4498. else if (strncasecmp (type.bv_val, T_NEWSUPERIORDNSTR, type.bv_len) == 0)
  4499. {
  4500. op->p.p_modrdn.modrdn_newsuperior_address.dn = slapi_ch_strdup (value.bv_val);
  4501. }
  4502. else if (strncasecmp (type.bv_val, T_NEWSUPERIORIDSTR, type.bv_len) == 0)
  4503. {
  4504. op->p.p_modrdn.modrdn_newsuperior_address.uniqueid = slapi_ch_strdup (value.bv_val);
  4505. }
  4506. else if (strncasecmp (type.bv_val, T_CHANGESTR, type.bv_len) == 0)
  4507. {
  4508. PR_ASSERT (op->operation_type);
  4509. switch (op->operation_type)
  4510. {
  4511. case SLAPI_OPERATION_ADD: /*
  4512. * When it comes here, case T_DNSTR is already
  4513. * passed and rawDN is supposed to set.
  4514. * But it's a good idea to make sure it is
  4515. * not NULL.
  4516. */
  4517. if (NULL == rawDN) {
  4518. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4519. "_cl5LDIF2Operation: corrupted format "
  4520. "for operation type - %lu\n",
  4521. op->operation_type);
  4522. return CL5_BAD_FORMAT;
  4523. }
  4524. mods = parse_changes_string(value.bv_val);
  4525. PR_ASSERT (mods);
  4526. slapi_mods2entry (&(op->p.p_add.target_entry), rawDN,
  4527. slapi_mods_get_ldapmods_byref(mods));
  4528. slapi_ch_free ((void**)&rawDN);
  4529. slapi_mods_free (&mods);
  4530. break;
  4531. case SLAPI_OPERATION_MODIFY: mods = parse_changes_string(value.bv_val);
  4532. PR_ASSERT (mods);
  4533. op->p.p_modify.modify_mods = slapi_mods_get_ldapmods_passout (mods);
  4534. slapi_mods_free (&mods);
  4535. break;
  4536. case SLAPI_OPERATION_MODRDN: mods = parse_changes_string(value.bv_val);
  4537. PR_ASSERT (mods);
  4538. op->p.p_modrdn.modrdn_mods = slapi_mods_get_ldapmods_passout (mods);
  4539. slapi_mods_free (&mods);
  4540. break;
  4541. default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4542. "_cl5LDIF2Operation: invalid operation type - %lu\n",
  4543. op->operation_type);
  4544. if (freeval) {
  4545. slapi_ch_free_string(&value.bv_val);
  4546. }
  4547. return CL5_BAD_FORMAT;
  4548. }
  4549. }
  4550. if (freeval) {
  4551. slapi_ch_free_string(&value.bv_val);
  4552. }
  4553. }
  4554. if (IsValidOperation (op))
  4555. return CL5_SUCCESS;
  4556. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4557. "_cl5LDIF2Operation: invalid data format\n");
  4558. return CL5_BAD_FORMAT;
  4559. }
  4560. static int _cl5WriteOperation(const char *replName, const char *replGen,
  4561. const slapi_operation_parameters *op, PRBool local)
  4562. {
  4563. int rc;
  4564. int cnt;
  4565. DBT key={0};
  4566. DBT * data=NULL;
  4567. char csnStr [CSN_STRSIZE];
  4568. PRIntervalTime interval;
  4569. CL5Entry entry;
  4570. CL5DBFile *file = NULL;
  4571. Object *file_obj = NULL;
  4572. DB_TXN *txnid = NULL;
  4573. rc = _cl5GetDBFileByReplicaName (replName, replGen, &file_obj);
  4574. if (rc == CL5_NOTFOUND)
  4575. {
  4576. rc = _cl5DBOpenFileByReplicaName (replName, replGen, &file_obj,
  4577. PR_TRUE /* check for duplicates */);
  4578. if (rc != CL5_SUCCESS)
  4579. {
  4580. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4581. "_cl5WriteOperation: failed to find or open DB object for replica %s\n", replName);
  4582. return rc;
  4583. }
  4584. }
  4585. else if (rc != CL5_SUCCESS)
  4586. {
  4587. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4588. "_cl5WriteOperation: failed to get db file for target dn (%s)",
  4589. op->target_address.dn);
  4590. return CL5_OBJSET_ERROR;
  4591. }
  4592. /* assign entry time - used for trimming */
  4593. entry.time = current_time ();
  4594. entry.op = (slapi_operation_parameters *)op;
  4595. /* construct the key */
  4596. key.data = csn_as_string(op->csn, PR_FALSE, csnStr);
  4597. key.size = CSN_STRSIZE;
  4598. /* construct the data */
  4599. data = (DBT *) slapi_ch_calloc(1, sizeof(DBT));
  4600. rc = _cl5Entry2DBData (&entry, (char**)&data->data, &data->size);
  4601. if (rc != CL5_SUCCESS)
  4602. {
  4603. char s[CSN_STRSIZE];
  4604. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4605. "_cl5WriteOperation: failed to convert entry with csn (%s) "
  4606. "to db format\n", csn_as_string(op->csn,PR_FALSE,s));
  4607. goto done;
  4608. }
  4609. file = (CL5DBFile*)object_get_data (file_obj);
  4610. PR_ASSERT (file);
  4611. /* if this is part of ldif2cl - just write the entry without transaction */
  4612. if (s_cl5Desc.dbOpenMode == CL5_OPEN_LDIF2CL)
  4613. {
  4614. rc = file->db->put(file->db, NULL, &key, data, 0);
  4615. if (rc != 0)
  4616. {
  4617. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4618. "_cl5WriteOperation: failed to write entry; db error - %d %s\n",
  4619. rc, db_strerror(rc));
  4620. if (CL5_OS_ERR_IS_DISKFULL(rc))
  4621. {
  4622. cl5_set_diskfull();
  4623. }
  4624. rc = CL5_DB_ERROR;
  4625. }
  4626. goto done;
  4627. }
  4628. /* write the entry */
  4629. rc = EAGAIN;
  4630. cnt = 0;
  4631. while ((rc == EAGAIN || rc == DB_LOCK_DEADLOCK) && cnt < MAX_TRIALS)
  4632. {
  4633. if (cnt != 0)
  4634. {
  4635. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  4636. /* abort previous transaction */
  4637. rc = txn_abort (txnid);
  4638. if (rc != 0)
  4639. {
  4640. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4641. "_cl5WriteOperation: failed to abort transaction; db error - %d %s\n",
  4642. rc, db_strerror(rc));
  4643. rc = CL5_DB_ERROR;
  4644. goto done;
  4645. }
  4646. #endif
  4647. /* back off */
  4648. interval = PR_MillisecondsToInterval(slapi_rand() % 100);
  4649. DS_Sleep(interval);
  4650. }
  4651. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  4652. /* begin transaction */
  4653. rc = txn_begin(s_cl5Desc.dbEnv, NULL /*pid*/, &txnid, 0);
  4654. if (rc != 0)
  4655. {
  4656. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4657. "_cl5WriteOperation: failed to start transaction; db error - %d %s\n",
  4658. rc, db_strerror(rc));
  4659. rc = CL5_DB_ERROR;
  4660. goto done;
  4661. }
  4662. #endif
  4663. if ( file->sema )
  4664. {
  4665. PR_WaitSemaphore(file->sema);
  4666. }
  4667. rc = file->db->put(file->db, txnid, &key, data, DEFAULT_DB_OP_FLAGS);
  4668. if ( file->sema )
  4669. {
  4670. PR_PostSemaphore(file->sema);
  4671. }
  4672. if (CL5_OS_ERR_IS_DISKFULL(rc))
  4673. {
  4674. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4675. "_cl5WriteOperation: changelog (%s) DISK FULL; db error - %d %s\n",
  4676. s_cl5Desc.dbDir, rc, db_strerror(rc));
  4677. cl5_set_diskfull();
  4678. rc = CL5_DB_ERROR;
  4679. goto done;
  4680. }
  4681. if (cnt != 0)
  4682. {
  4683. if (rc == 0)
  4684. {
  4685. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "_cl5WriteOperation: retry (%d) the transaction (csn=%s) succeeded\n", cnt, (char*)key.data);
  4686. }
  4687. else if ((cnt + 1) >= MAX_TRIALS)
  4688. {
  4689. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "_cl5WriteOperation: retry (%d) the transaction (csn=%s) failed (rc=%d (%s))\n", cnt, (char*)key.data, rc, db_strerror(rc));
  4690. }
  4691. }
  4692. cnt ++;
  4693. }
  4694. if (rc == 0) /* we successfully added entry */
  4695. {
  4696. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  4697. rc = txn_commit (txnid, 0);
  4698. #endif
  4699. }
  4700. else
  4701. {
  4702. char s[CSN_STRSIZE];
  4703. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4704. "_cl5WriteOperation: failed to write entry with csn (%s); "
  4705. "db error - %d %s\n", csn_as_string(op->csn,PR_FALSE,s),
  4706. rc, db_strerror(rc));
  4707. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
  4708. rc = txn_abort (txnid);
  4709. if (rc != 0)
  4710. {
  4711. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4712. "_cl5WriteOperation: failed to abort transaction; db error - %d %s\n",
  4713. rc, db_strerror(rc));
  4714. }
  4715. #endif
  4716. rc = CL5_DB_ERROR;
  4717. goto done;
  4718. }
  4719. /* update entry count - we assume that all entries are new */
  4720. PR_AtomicIncrement (&file->entryCount);
  4721. /* update purge vector if we have not seen any changes from this replica before */
  4722. _cl5UpdateRUV (file_obj, op->csn, PR_TRUE, PR_TRUE);
  4723. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  4724. "cl5WriteOperation: successfully written entry with csn (%s)\n", csnStr);
  4725. rc = CL5_SUCCESS;
  4726. done:
  4727. if (data->data)
  4728. slapi_ch_free (&(data->data));
  4729. slapi_ch_free((void**) &data);
  4730. if (file_obj)
  4731. object_release (file_obj);
  4732. return rc;
  4733. }
  4734. static int _cl5GetFirstEntry (Object *obj, CL5Entry *entry, void **iterator, DB_TXN *txnid)
  4735. {
  4736. int rc;
  4737. DBC *cursor = NULL;
  4738. DBT key={0}, data={0};
  4739. CL5Iterator *it;
  4740. CL5DBFile *file;
  4741. PR_ASSERT (obj && entry && iterator);
  4742. file = (CL5DBFile*)object_get_data (obj);
  4743. PR_ASSERT (file);
  4744. /* create cursor */
  4745. rc = file->db->cursor(file->db, txnid, &cursor, 0);
  4746. if (rc != 0)
  4747. {
  4748. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4749. "_cl5GetFirstEntry: failed to create cursor; db error - %d %s\n", rc, db_strerror(rc));
  4750. rc = CL5_DB_ERROR;
  4751. goto done;
  4752. }
  4753. key.flags = DB_DBT_MALLOC;
  4754. data.flags = DB_DBT_MALLOC;
  4755. while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0)
  4756. {
  4757. /* skip service entries */
  4758. if (cl5HelperEntry ((char*)key.data, NULL))
  4759. {
  4760. slapi_ch_free (&(key.data));
  4761. slapi_ch_free (&(data.data));
  4762. continue;
  4763. }
  4764. /* format entry */
  4765. slapi_ch_free (&(key.data));
  4766. rc = cl5DBData2Entry (data.data, data.size, entry);
  4767. slapi_ch_free (&(data.data));
  4768. if (rc != 0)
  4769. {
  4770. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4771. "_cl5GetFirstOperation: failed to format entry: %d\n", rc);
  4772. goto done;
  4773. }
  4774. it = (CL5Iterator*)slapi_ch_malloc (sizeof (CL5Iterator));
  4775. it->cursor = cursor;
  4776. object_acquire (obj);
  4777. it->file = obj;
  4778. *(CL5Iterator**)iterator = it;
  4779. return CL5_SUCCESS;
  4780. }
  4781. /*
  4782. * Bug 430172 - memory leaks after db "get" deadlocks, e.g. in CL5 trim
  4783. * Even when db->c_get() does not return success, memory may have been
  4784. * allocated in the DBT. This seems to happen when DB_DBT_MALLOC was set,
  4785. * the data being retrieved is larger than the page size, and we got
  4786. * DB_LOCK_DEADLOCK. libdb allocates the memory and then finds itself
  4787. * deadlocked trying to go through the overflow page list. It returns
  4788. * DB_LOCK_DEADLOCK which we've assumed meant that no memory was allocated
  4789. * for the DBT.
  4790. *
  4791. * The following slapi_ch_free frees the memory only when the value is
  4792. * non NULL, which is true if the situation described above occurs.
  4793. */
  4794. slapi_ch_free ((void **)&key.data);
  4795. slapi_ch_free ((void **)&data.data);
  4796. /* walked of the end of the file */
  4797. if (rc == DB_NOTFOUND)
  4798. {
  4799. rc = CL5_NOTFOUND;
  4800. goto done;
  4801. }
  4802. /* db error occured while iterating */
  4803. /* On this path, the condition "rc != 0" cannot be false */
  4804. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4805. "_cl5GetFirstEntry: failed to get entry; db error - %d %s\n",
  4806. rc, db_strerror(rc));
  4807. rc = CL5_DB_ERROR;
  4808. done:;
  4809. /* error occured */
  4810. /* We didn't success in assigning this cursor to the iterator,
  4811. * so we need to free the cursor here */
  4812. if (cursor)
  4813. cursor->c_close(cursor);
  4814. return rc;
  4815. }
  4816. static int _cl5GetNextEntry (CL5Entry *entry, void *iterator)
  4817. {
  4818. int rc;
  4819. CL5Iterator *it;
  4820. DBT key={0}, data={0};
  4821. PR_ASSERT (entry && iterator);
  4822. it = (CL5Iterator*) iterator;
  4823. key.flags = DB_DBT_MALLOC;
  4824. data.flags = DB_DBT_MALLOC;
  4825. while ((rc = it->cursor->c_get(it->cursor, &key, &data, DB_NEXT)) == 0)
  4826. {
  4827. if (cl5HelperEntry ((char*)key.data, NULL))
  4828. {
  4829. slapi_ch_free (&(key.data));
  4830. slapi_ch_free (&(data.data));
  4831. continue;
  4832. }
  4833. slapi_ch_free (&(key.data));
  4834. /* format entry */
  4835. rc = cl5DBData2Entry (data.data, data.size, entry);
  4836. slapi_ch_free (&(data.data));
  4837. if (rc != 0)
  4838. {
  4839. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4840. "_cl5GetNextEntry: failed to format entry: %d\n", rc);
  4841. }
  4842. return rc;
  4843. }
  4844. /*
  4845. * Bug 430172 - memory leaks after db "get" deadlocks, e.g. in CL5 trim
  4846. * Even when db->c_get() does not return success, memory may have been
  4847. * allocated in the DBT. This seems to happen when DB_DBT_MALLOC was set,
  4848. * the data being retrieved is larger than the page size, and we got
  4849. * DB_LOCK_DEADLOCK. libdb allocates the memory and then finds itself
  4850. * deadlocked trying to go through the overflow page list. It returns
  4851. * DB_LOCK_DEADLOCK which we've assumed meant that no memory was allocated
  4852. * for the DBT.
  4853. *
  4854. * The following slapi_ch_free frees the memory only when the value is
  4855. * non NULL, which is true if the situation described above occurs.
  4856. */
  4857. slapi_ch_free ((void **)&key.data);
  4858. slapi_ch_free ((void **)&data.data);
  4859. /* walked of the end of the file or entry is out of range */
  4860. if (rc == 0 || rc == DB_NOTFOUND)
  4861. {
  4862. return CL5_NOTFOUND;
  4863. }
  4864. /* cursor operation failed */
  4865. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4866. "_cl5GetNextEntry: failed to get entry; db error - %d %s\n",
  4867. rc, db_strerror(rc));
  4868. return CL5_DB_ERROR;
  4869. }
  4870. static int _cl5CurrentDeleteEntry (void *iterator)
  4871. {
  4872. int rc;
  4873. CL5Iterator *it;
  4874. CL5DBFile *file;
  4875. PR_ASSERT (iterator);
  4876. it = (CL5Iterator*)iterator;
  4877. rc = it->cursor->c_del (it->cursor, 0);
  4878. if (rc == 0) {
  4879. /* decrement entry count */
  4880. file = (CL5DBFile*)object_get_data (it->file);
  4881. PR_AtomicDecrement (&file->entryCount);
  4882. return CL5_SUCCESS;
  4883. } else {
  4884. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  4885. "_cl5CurrentDeleteEntry failed, err=%d %s\n",
  4886. rc, db_strerror(rc));
  4887. /* We don't free(close) the cursor here, as the caller will free it by a call to cl5DestroyIterator */
  4888. /* Freeing it here is a potential bug, as the cursor can't be referenced later once freed */
  4889. return CL5_DB_ERROR;
  4890. }
  4891. }
  4892. static PRBool _cl5IsValidIterator (const CL5Iterator *iterator)
  4893. {
  4894. return (iterator && iterator->cursor && iterator->file);
  4895. }
  4896. static int _cl5GetOperation (Object *replica, slapi_operation_parameters *op)
  4897. {
  4898. int rc;
  4899. DBT key={0}, data={0};
  4900. CL5DBFile *file;
  4901. CL5Entry entry;
  4902. Object *obj = NULL;
  4903. char csnStr[CSN_STRSIZE];
  4904. rc = _cl5GetDBFile (replica, &obj);
  4905. if (rc != CL5_SUCCESS || !obj)
  4906. {
  4907. goto done;
  4908. }
  4909. file = (CL5DBFile*)object_get_data (obj);
  4910. PR_ASSERT (file);
  4911. /* construct the key */
  4912. key.data = csn_as_string(op->csn, PR_FALSE, csnStr);
  4913. key.size = CSN_STRSIZE;
  4914. data.flags = DB_DBT_MALLOC;
  4915. rc = file->db->get(file->db, NULL/*txn*/, &key, &data, 0);
  4916. switch (rc)
  4917. {
  4918. case 0: entry.op = op;
  4919. /* Callers of this function should cl5_operation_parameters_done(op) */
  4920. rc = cl5DBData2Entry (data.data, data.size, &entry);
  4921. if (rc == CL5_SUCCESS)
  4922. {
  4923. slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
  4924. "_cl5GetOperation: successfully retrieved operation with csn (%s)\n",
  4925. csnStr);
  4926. }
  4927. else
  4928. {
  4929. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4930. "_cl5GetOperation: failed to convert db data to operation;"
  4931. " csn - %s\n", csnStr);
  4932. }
  4933. goto done;
  4934. case DB_NOTFOUND: slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4935. "_cl5GetOperation: operation for csn (%s) is not found in db that should contain dn (%s)\n",
  4936. csnStr, op->target_address.dn);
  4937. rc = CL5_NOTFOUND;
  4938. goto done;
  4939. default: slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4940. "_cl5GetOperation: failed to get entry for csn (%s); "
  4941. "db error - %d %s\n", csnStr, rc, db_strerror(rc));
  4942. rc = CL5_DB_ERROR;
  4943. goto done;
  4944. }
  4945. done:
  4946. if (obj)
  4947. object_release (obj);
  4948. slapi_ch_free (&(data.data));
  4949. return rc;
  4950. }
  4951. PRBool cl5HelperEntry (const char *csnstr, CSN *csnp)
  4952. {
  4953. CSN *csn;
  4954. time_t csnTime;
  4955. PRBool retval = PR_FALSE;
  4956. if (csnp)
  4957. {
  4958. csn = csnp;
  4959. }
  4960. else
  4961. {
  4962. csn= csn_new_by_string(csnstr);
  4963. }
  4964. if (csn == NULL)
  4965. {
  4966. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  4967. "cl5HelperEntry: failed to get csn time; csn error\n");
  4968. return PR_FALSE;
  4969. }
  4970. csnTime= csn_get_time(csn);
  4971. if (csnTime == ENTRY_COUNT_TIME || csnTime == PURGE_RUV_TIME)
  4972. {
  4973. retval = PR_TRUE;
  4974. }
  4975. if (NULL == csnp)
  4976. csn_free(&csn);
  4977. return retval;
  4978. }
  4979. #ifdef FOR_DEBUGGING
  4980. /* Replay iteration helper functions */
  4981. static PRBool _cl5ValidReplayIterator (const CL5ReplayIterator *iterator)
  4982. {
  4983. if (iterator == NULL ||
  4984. iterator->consumerRuv == NULL || iterator->supplierRuvObj == NULL ||
  4985. iterator->fileObj == NULL)
  4986. return PR_FALSE;
  4987. return PR_TRUE;
  4988. }
  4989. #endif
  4990. /* Algorithm: ONREPL!!!
  4991. */
  4992. struct replica_hash_entry
  4993. {
  4994. ReplicaId rid; /* replica id */
  4995. PRBool sendChanges; /* indicates whether changes should be sent for this replica */
  4996. };
  4997. static int _cl5PositionCursorForReplay (ReplicaId consumerRID, const RUV *consumerRuv,
  4998. Object *replica, Object *fileObj, CL5ReplayIterator **iterator)
  4999. {
  5000. CLC_Buffer *clcache = NULL;
  5001. CL5DBFile *file;
  5002. int i;
  5003. CSN **csns = NULL;
  5004. CSN *startCSN = NULL;
  5005. char csnStr [CSN_STRSIZE];
  5006. int rc = CL5_SUCCESS;
  5007. Object *supplierRuvObj = NULL;
  5008. RUV *supplierRuv = NULL;
  5009. PRBool newReplica;
  5010. PRBool haveChanges = PR_FALSE;
  5011. char *agmt_name;
  5012. ReplicaId rid;
  5013. PR_ASSERT (consumerRuv && replica && fileObj && iterator);
  5014. csnStr[0] = '\0';
  5015. file = (CL5DBFile*)object_get_data (fileObj);
  5016. /* get supplier's RUV */
  5017. supplierRuvObj = replica_get_ruv((Replica*)object_get_data(replica));
  5018. PR_ASSERT (supplierRuvObj);
  5019. if (!supplierRuvObj) {
  5020. rc = CL5_UNKNOWN_ERROR;
  5021. goto done;
  5022. }
  5023. supplierRuv = (RUV*)object_get_data (supplierRuvObj);
  5024. PR_ASSERT (supplierRuv);
  5025. agmt_name = get_thread_private_agmtname();
  5026. slapi_log_error(SLAPI_LOG_REPL, NULL, "_cl5PositionCursorForReplay (%s): Consumer RUV:\n", agmt_name);
  5027. ruv_dump (consumerRuv, agmt_name, NULL);
  5028. slapi_log_error(SLAPI_LOG_REPL, NULL, "_cl5PositionCursorForReplay (%s): Supplier RUV:\n", agmt_name);
  5029. ruv_dump (supplierRuv, agmt_name, NULL);
  5030. /*
  5031. * get the sorted list of SupplierMinCSN (if no ConsumerMaxCSN)
  5032. * and ConsumerMaxCSN for those RIDs where consumer is not
  5033. * up-to-date.
  5034. */
  5035. csns = cl5BuildCSNList (consumerRuv, supplierRuv);
  5036. if (csns == NULL)
  5037. {
  5038. rc = CL5_NOTFOUND;
  5039. goto done;
  5040. }
  5041. /* iterate over elements of consumer's (and/or supplier's) ruv */
  5042. for (i = 0; csns[i]; i++)
  5043. {
  5044. CSN *consumerMaxCSN = NULL;
  5045. rid = csn_get_replicaid(csns[i]);
  5046. /*
  5047. * Skip CSN that is originated from the consumer.
  5048. * If RID==65535, the CSN is originated from a
  5049. * legacy consumer. In this case the supplier
  5050. * and the consumer may have the same RID.
  5051. */
  5052. if (rid == consumerRID && rid != MAX_REPLICA_ID)
  5053. continue;
  5054. startCSN = csns[i];
  5055. csn_as_string(startCSN, PR_FALSE, csnStr);
  5056. rc = clcache_get_buffer ( &clcache, file->db, consumerRID, consumerRuv, supplierRuv );
  5057. if ( rc != 0 ) goto done;
  5058. /* This is the first loading of this iteration. For replicas
  5059. * already known to the consumer, we exclude the last entry
  5060. * sent to the consumer by using DB_NEXT. However, for
  5061. * replicas new to the consumer, we include the first change
  5062. * ever generated by that replica.
  5063. */
  5064. newReplica = ruv_get_largest_csn_for_replica (consumerRuv, rid, &consumerMaxCSN);
  5065. csn_free(&consumerMaxCSN);
  5066. rc = clcache_load_buffer (clcache, startCSN, (newReplica ? DB_SET : DB_NEXT));
  5067. /* there is a special case which can occur just after migration - in this case,
  5068. the consumer RUV will contain the last state of the supplier before migration,
  5069. but the supplier will have an empty changelog, or the supplier changelog will
  5070. not contain any entries within the consumer min and max CSN - also, since
  5071. the purge RUV contains no CSNs, the changelog has never been purged
  5072. ASSUMPTIONS - it is assumed that the supplier had no pending changes to send
  5073. to any consumers; that is, we can assume that no changes were lost due to
  5074. either changelog purging or database reload - bug# 603061 - [email protected]
  5075. */
  5076. if ((rc == DB_NOTFOUND) && !ruv_has_csns(file->purgeRUV))
  5077. {
  5078. /* use the supplier min csn for the buffer start csn - we know
  5079. this csn is in our changelog */
  5080. if ((RUV_SUCCESS == ruv_get_min_csn(supplierRuv, &startCSN)) &&
  5081. startCSN)
  5082. { /* must now free startCSN */
  5083. csn_as_string(startCSN, PR_FALSE, csnStr);
  5084. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5085. "%s: CSN %s not found and no purging, probably a reinit\n",
  5086. agmt_name, csnStr);
  5087. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5088. "%s: Will try to use supplier min CSN %s to load changelog\n",
  5089. agmt_name, csnStr);
  5090. rc = clcache_load_buffer (clcache, startCSN, DB_SET);
  5091. }
  5092. else
  5093. {
  5094. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5095. "%s: CSN %s not found and no purging, probably a reinit\n",
  5096. agmt_name, csnStr);
  5097. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5098. "%s: Could not get the min csn from the supplier RUV\n",
  5099. agmt_name);
  5100. rc = CL5_RUV_ERROR;
  5101. goto done;
  5102. }
  5103. }
  5104. if (rc == 0) {
  5105. haveChanges = PR_TRUE;
  5106. rc = CL5_SUCCESS;
  5107. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5108. "%s: CSN %s found, position set for replay\n", agmt_name, csnStr);
  5109. if (startCSN != csns[i]) {
  5110. csn_free(&startCSN);
  5111. }
  5112. break;
  5113. }
  5114. else if (rc == DB_NOTFOUND) /* entry not found */
  5115. {
  5116. /* check whether this csn should be present */
  5117. rc = _cl5CheckMissingCSN (startCSN, supplierRuv, file);
  5118. if (startCSN != csns[i]) {
  5119. csn_free(&startCSN);
  5120. }
  5121. if (rc == CL5_MISSING_DATA) /* we should have had the change but we don't */
  5122. {
  5123. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5124. "%s: CSN %s not found, seems to be missing\n", agmt_name, csnStr);
  5125. break;
  5126. }
  5127. else /* we are not as up to date or we purged */
  5128. {
  5129. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5130. "%s: CSN %s not found, we aren't as up to date, or we purged\n",
  5131. agmt_name, csnStr);
  5132. continue;
  5133. }
  5134. }
  5135. else
  5136. {
  5137. if (startCSN != csns[i]) {
  5138. csn_free(&startCSN);
  5139. }
  5140. /* db error */
  5141. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5142. "%s: Failed to retrieve change with CSN %s; db error - %d %s\n",
  5143. agmt_name, csnStr, rc, db_strerror(rc));
  5144. rc = CL5_DB_ERROR;
  5145. break;
  5146. }
  5147. } /* end for */
  5148. /* setup the iterator */
  5149. if (haveChanges)
  5150. {
  5151. *iterator = (CL5ReplayIterator*) slapi_ch_calloc (1, sizeof (CL5ReplayIterator));
  5152. if (*iterator == NULL)
  5153. {
  5154. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5155. "%s: _cl5PositionCursorForReplay: failed to allocate iterator\n", agmt_name);
  5156. rc = CL5_MEMORY_ERROR;
  5157. goto done;
  5158. }
  5159. /* ONREPL - should we make a copy of both RUVs here ?*/
  5160. (*iterator)->fileObj = fileObj;
  5161. (*iterator)->clcache = clcache; clcache = NULL;
  5162. (*iterator)->consumerRID = consumerRID;
  5163. (*iterator)->consumerRuv = consumerRuv;
  5164. (*iterator)->supplierRuvObj = supplierRuvObj;
  5165. }
  5166. else if (rc == CL5_SUCCESS)
  5167. {
  5168. /* we have no changes to send */
  5169. rc = CL5_NOTFOUND;
  5170. }
  5171. done:
  5172. if ( clcache )
  5173. clcache_return_buffer ( &clcache );
  5174. if (csns)
  5175. cl5DestroyCSNList (&csns);
  5176. if (rc != CL5_SUCCESS)
  5177. {
  5178. if (supplierRuvObj)
  5179. object_release (supplierRuvObj);
  5180. }
  5181. return rc;
  5182. }
  5183. struct ruv_it
  5184. {
  5185. CSN **csns; /* csn list */
  5186. int alloc; /* allocated size */
  5187. int pos; /* position in the list */
  5188. };
  5189. static int ruv_consumer_iterator (const ruv_enum_data *enum_data, void *arg)
  5190. {
  5191. struct ruv_it *data = (struct ruv_it*)arg;
  5192. PR_ASSERT (data);
  5193. /* check if we have space for one more element */
  5194. if (data->pos >= data->alloc - 2)
  5195. {
  5196. data->alloc += 4;
  5197. data->csns = (CSN**) slapi_ch_realloc ((void*)data->csns, data->alloc * sizeof (CSN*));
  5198. }
  5199. data->csns [data->pos] = csn_dup (enum_data->csn);
  5200. data->pos ++;
  5201. return 0;
  5202. }
  5203. static int ruv_supplier_iterator (const ruv_enum_data *enum_data, void *arg)
  5204. {
  5205. int i;
  5206. PRBool found = PR_FALSE;
  5207. ReplicaId rid;
  5208. struct ruv_it *data = (struct ruv_it*)arg;
  5209. PR_ASSERT (data);
  5210. rid = csn_get_replicaid (enum_data->min_csn);
  5211. /* check if the replica that generated the csn is already in the list */
  5212. for (i = 0; i < data->pos; i++)
  5213. {
  5214. if (rid == csn_get_replicaid (data->csns[i]))
  5215. {
  5216. found = PR_TRUE;
  5217. /* remove datacsn[i] if it is greater or equal to the supplier's maxcsn */
  5218. if ( csn_compare ( data->csns[i], enum_data->csn ) >= 0 )
  5219. {
  5220. int j;
  5221. csn_free ( & data->csns[i] );
  5222. for (j = i+1; j < data->pos; j++)
  5223. {
  5224. data->csns [j-1] = data->csns [j];
  5225. }
  5226. data->pos --;
  5227. }
  5228. break;
  5229. }
  5230. }
  5231. if (!found)
  5232. {
  5233. /* check if we have space for one more element */
  5234. if (data->pos >= data->alloc - 2)
  5235. {
  5236. data->alloc += 4;
  5237. data->csns = (CSN**)slapi_ch_realloc ((void*)data->csns,
  5238. data->alloc * sizeof (CSN*));
  5239. }
  5240. data->csns [data->pos] = csn_dup (enum_data->min_csn);
  5241. data->pos ++;
  5242. }
  5243. return 0;
  5244. }
  5245. static int
  5246. my_csn_compare(const void *arg1, const void *arg2)
  5247. {
  5248. return(csn_compare(*((CSN **)arg1), *((CSN **)arg2)));
  5249. }
  5250. /* builds CSN ordered list of all csns in the RUV */
  5251. CSN** cl5BuildCSNList (const RUV *consRuv, const RUV *supRuv)
  5252. {
  5253. struct ruv_it data;
  5254. int count, rc;
  5255. CSN **csns;
  5256. PR_ASSERT (consRuv);
  5257. count = ruv_replica_count (consRuv);
  5258. csns = (CSN**)slapi_ch_calloc (count + 1, sizeof (CSN*));
  5259. data.csns = csns;
  5260. data.alloc = count + 1;
  5261. data.pos = 0;
  5262. /* add consumer elements to the list */
  5263. rc = ruv_enumerate_elements (consRuv, ruv_consumer_iterator, &data);
  5264. if (rc == 0 && supRuv)
  5265. {
  5266. /* add supplier elements to the list */
  5267. rc = ruv_enumerate_elements (supRuv, ruv_supplier_iterator, &data);
  5268. }
  5269. /* we have no csns */
  5270. if (data.csns[0] == NULL)
  5271. {
  5272. /* csns might have been realloced in ruv_supplier_iterator() */
  5273. slapi_ch_free ((void**)&data.csns);
  5274. csns = NULL;
  5275. }
  5276. else
  5277. {
  5278. csns = data.csns;
  5279. data.csns [data.pos] = NULL;
  5280. if (rc == 0)
  5281. {
  5282. qsort (csns, data.pos, sizeof (CSN*), my_csn_compare);
  5283. }
  5284. else
  5285. {
  5286. cl5DestroyCSNList (&csns);
  5287. }
  5288. }
  5289. return csns;
  5290. }
  5291. void cl5DestroyCSNList (CSN*** csns)
  5292. {
  5293. if (csns && *csns)
  5294. {
  5295. int i;
  5296. for (i = 0; (*csns)[i]; i++)
  5297. {
  5298. csn_free (&(*csns)[i]);
  5299. }
  5300. slapi_ch_free ((void**)csns);
  5301. }
  5302. }
  5303. /* A csn should be in the changelog if it is larger than purge vector csn for the same
  5304. replica and is smaller than the csn in supplier's ruv for the same replica.
  5305. The functions returns
  5306. CL5_PURGED if data was purged from the changelog or was never logged
  5307. because it was loaded as part of replica initialization
  5308. CL5_MISSING if the data erouneously missing
  5309. CL5_SUCCESS if that has not and should not been seen by the server
  5310. */
  5311. static int _cl5CheckMissingCSN (const CSN *csn, const RUV *supplierRuv, CL5DBFile *file)
  5312. {
  5313. ReplicaId rid;
  5314. CSN *supplierCsn = NULL;
  5315. CSN *purgeCsn = NULL;
  5316. int rc = CL5_SUCCESS;
  5317. char csnStr [CSN_STRSIZE];
  5318. PR_ASSERT (csn && supplierRuv && file);
  5319. rid = csn_get_replicaid (csn);
  5320. ruv_get_largest_csn_for_replica (supplierRuv, rid, &supplierCsn);
  5321. if (supplierCsn == NULL)
  5322. {
  5323. /* we have not seen any changes from this replica so it is
  5324. ok not to have this csn */
  5325. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
  5326. "can't locate %s csn: we have not seen any changes for replica %d\n",
  5327. csn_as_string (csn, PR_FALSE, csnStr), rid);
  5328. return CL5_SUCCESS;
  5329. }
  5330. ruv_get_largest_csn_for_replica (file->purgeRUV, rid, &purgeCsn);
  5331. if (purgeCsn == NULL)
  5332. {
  5333. /* changelog never contained any changes for this replica */
  5334. if (csn_compare (csn, supplierCsn) <= 0)
  5335. {
  5336. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
  5337. "the change with %s csn was never logged because it was imported "
  5338. "during replica initialization\n", csn_as_string (csn, PR_FALSE, csnStr));
  5339. rc = CL5_PURGED_DATA; /* XXXggood is that the correct return value? */
  5340. }
  5341. else
  5342. {
  5343. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
  5344. "change with %s csn has not yet been seen by this server; "
  5345. " last csn seen from that replica is %s\n",
  5346. csn_as_string (csn, PR_FALSE, csnStr),
  5347. csn_as_string (supplierCsn, PR_FALSE, csnStr));
  5348. rc = CL5_SUCCESS;
  5349. }
  5350. }
  5351. else /* we have both purge and supplier csn */
  5352. {
  5353. if (csn_compare (csn, purgeCsn) < 0) /* the csn is below the purge point */
  5354. {
  5355. rc = CL5_PURGED_DATA;
  5356. }
  5357. else
  5358. {
  5359. if (csn_compare (csn, supplierCsn) <= 0) /* we should have the data but we don't */
  5360. {
  5361. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
  5362. "change with %s csn has been purged by this server; "
  5363. "the current purge point for that replica is %s\n",
  5364. csn_as_string (csn, PR_FALSE, csnStr),
  5365. csn_as_string (purgeCsn, PR_FALSE, csnStr));
  5366. rc = CL5_MISSING_DATA;
  5367. }
  5368. else
  5369. {
  5370. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
  5371. "change with %s csn has not yet been seen by this server; "
  5372. " last csn seen from that replica is %s\n",
  5373. csn_as_string (csn, PR_FALSE, csnStr),
  5374. csn_as_string (supplierCsn, PR_FALSE, csnStr));
  5375. rc = CL5_SUCCESS;
  5376. }
  5377. }
  5378. }
  5379. if (supplierCsn)
  5380. csn_free (&supplierCsn);
  5381. if (purgeCsn)
  5382. csn_free (&purgeCsn);
  5383. return rc;
  5384. }
  5385. /* Helper functions that work with individual changelog files */
  5386. /* file name format : <replica name>_<replica generation>db{2,3} */
  5387. static PRBool _cl5FileName2Replica (const char *file_name, Object **replica)
  5388. {
  5389. Replica *r;
  5390. char *repl_name, *file_gen, *repl_gen;
  5391. int len;
  5392. PR_ASSERT (file_name && replica);
  5393. *replica = NULL;
  5394. /* this is database file */
  5395. if (_cl5FileEndsWith (file_name, DB_EXTENSION) ||
  5396. _cl5FileEndsWith (file_name, DB_EXTENSION_DB3) )
  5397. {
  5398. repl_name = slapi_ch_strdup (file_name);
  5399. file_gen = strstr(repl_name, FILE_SEP);
  5400. if (file_gen)
  5401. {
  5402. int extlen = strlen(DB_EXTENSION);
  5403. *file_gen = '\0';
  5404. file_gen += strlen (FILE_SEP);
  5405. len = strlen (file_gen);
  5406. if (len <= extlen + 1)
  5407. {
  5408. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5FileName2Replica "
  5409. "invalid file name (%s)\n", file_name);
  5410. }
  5411. else
  5412. {
  5413. /* get rid of the file extension */
  5414. file_gen [len - extlen - 1] = '\0';
  5415. *replica = replica_get_by_name (repl_name);
  5416. if (*replica)
  5417. {
  5418. /* check that generation matches the one in replica object */
  5419. r = (Replica*)object_get_data (*replica);
  5420. repl_gen = replica_get_generation (r);
  5421. PR_ASSERT (repl_gen);
  5422. if (strcmp (file_gen, repl_gen) != 0)
  5423. {
  5424. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5FileName2Replica "
  5425. "replica generation mismatch for replica at (%s), "
  5426. "file generation %s, new replica generation %s\n",
  5427. slapi_sdn_get_dn (replica_get_root (r)), file_gen, repl_gen);
  5428. object_release (*replica);
  5429. *replica = NULL;
  5430. }
  5431. slapi_ch_free ((void**)&repl_gen);
  5432. }
  5433. }
  5434. slapi_ch_free ((void**)&repl_name);
  5435. }
  5436. else
  5437. {
  5438. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5FileName2Replica "
  5439. "malformed file name - %s\n", file_name);
  5440. }
  5441. return PR_TRUE;
  5442. }
  5443. else
  5444. return PR_FALSE;
  5445. }
  5446. /* file name format : <replica name>_<replica generation>db{2,3} */
  5447. static char* _cl5Replica2FileName (Object *replica)
  5448. {
  5449. const char *replName;
  5450. char *replGen, *fileName;
  5451. Replica *r;
  5452. PR_ASSERT (replica);
  5453. r = (Replica*)object_get_data (replica);
  5454. PR_ASSERT (r);
  5455. replName = replica_get_name (r);
  5456. replGen = replica_get_generation (r);
  5457. fileName = _cl5MakeFileName (replName, replGen) ;
  5458. slapi_ch_free ((void**)&replGen);
  5459. return fileName;
  5460. }
  5461. static char* _cl5MakeFileName (const char *replName, const char *replGen)
  5462. {
  5463. char *fileName = slapi_ch_smprintf("%s%s%s.%s", replName, FILE_SEP, replGen, DB_EXTENSION);
  5464. return fileName;
  5465. }
  5466. /* open file that corresponds to a particular database */
  5467. static int _cl5DBOpenFile (Object *replica, Object **obj, PRBool checkDups)
  5468. {
  5469. int rc;
  5470. const char *replName;
  5471. char *replGen;
  5472. Replica *r;
  5473. PR_ASSERT (replica);
  5474. r = (Replica*)object_get_data (replica);
  5475. replName = replica_get_name (r);
  5476. PR_ASSERT (replName);
  5477. replGen = replica_get_generation (r);
  5478. PR_ASSERT (replGen);
  5479. rc = _cl5DBOpenFileByReplicaName (replName, replGen, obj, checkDups);
  5480. slapi_ch_free ((void**)&replGen);
  5481. return rc;
  5482. }
  5483. static int _cl5DBOpenFileByReplicaName (const char *replName, const char *replGen,
  5484. Object **obj, PRBool checkDups)
  5485. {
  5486. int rc = CL5_SUCCESS;
  5487. Object *tmpObj;
  5488. CL5DBFile *file;
  5489. char *file_name;
  5490. PR_ASSERT (replName && replGen);
  5491. if (checkDups)
  5492. {
  5493. PR_Lock (s_cl5Desc.fileLock);
  5494. file_name = _cl5MakeFileName (replName, replGen);
  5495. tmpObj = objset_find (s_cl5Desc.dbFiles, _cl5CompareDBFile, file_name);
  5496. slapi_ch_free((void **)&file_name);
  5497. file_name = NULL;
  5498. if (tmpObj) /* this file already exist */
  5499. {
  5500. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5501. "_cl5DBOpenFileByReplicaName: Found DB object %p for replica %s\n", tmpObj, replName);
  5502. /* if we were asked for file handle - keep the handle */
  5503. if (obj)
  5504. {
  5505. *obj = tmpObj;
  5506. }
  5507. else
  5508. {
  5509. object_release (tmpObj);
  5510. }
  5511. rc = CL5_SUCCESS;
  5512. goto done;
  5513. }
  5514. }
  5515. rc = _cl5NewDBFile (replName, replGen, &file);
  5516. if (rc == CL5_SUCCESS)
  5517. {
  5518. /* This creates the file but doesn't set the init flag
  5519. * The flag is set later when the purge and max ruvs are set.
  5520. * This is to prevent some thread to get file access before the
  5521. * structure is fully initialized */
  5522. rc = _cl5AddDBFile (file, &tmpObj);
  5523. if (rc == CL5_SUCCESS)
  5524. {
  5525. /* read purge RUV - done here because it needs file object rather than file pointer */
  5526. rc = _cl5ReadRUV (replGen, tmpObj, PR_TRUE);
  5527. if (rc != CL5_SUCCESS)
  5528. {
  5529. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5530. "_cl5DBOpenFileByReplicaName: failed to get purge RUV\n");
  5531. goto done;
  5532. }
  5533. /* read ruv that represents the upper bound of the changes stored in the file */
  5534. rc = _cl5ReadRUV (replGen, tmpObj, PR_FALSE);
  5535. if (rc != CL5_SUCCESS)
  5536. {
  5537. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5538. "_cl5DBOpenFileByReplicaName: failed to get upper bound RUV\n");
  5539. goto done;
  5540. }
  5541. /* Mark the DB File initialize */
  5542. _cl5DBFileInitialized(tmpObj);
  5543. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5544. "_cl5DBOpenFileByReplicaName: created new DB object %p\n", tmpObj);
  5545. if (obj)
  5546. {
  5547. *obj = tmpObj;
  5548. }
  5549. else
  5550. {
  5551. object_release (tmpObj);
  5552. }
  5553. }
  5554. }
  5555. done:;
  5556. if (rc != CL5_SUCCESS)
  5557. {
  5558. if (file)
  5559. _cl5DBCloseFile ((void**)&file);
  5560. }
  5561. if (checkDups)
  5562. {
  5563. PR_Unlock (s_cl5Desc.fileLock);
  5564. }
  5565. return rc;
  5566. }
  5567. /* adds file to the db file list */
  5568. static int _cl5AddDBFile (CL5DBFile *file, Object **obj)
  5569. {
  5570. int rc;
  5571. Object *tmpObj;
  5572. PR_ASSERT (file);
  5573. tmpObj = object_new (file, _cl5DBCloseFile);
  5574. rc = objset_add_obj(s_cl5Desc.dbFiles, tmpObj);
  5575. if (rc != OBJSET_SUCCESS)
  5576. {
  5577. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5578. "_cl5AddDBFile: failed to add db file to the list; "
  5579. "repl_objset error - %d\n", rc);
  5580. object_release (tmpObj);
  5581. return CL5_OBJSET_ERROR;
  5582. }
  5583. else
  5584. {
  5585. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5586. "_cl5AddDBFile: Added new DB object %p\n", tmpObj);
  5587. }
  5588. if (obj)
  5589. {
  5590. *obj = tmpObj;
  5591. }
  5592. else
  5593. object_release (tmpObj);
  5594. return CL5_SUCCESS;
  5595. }
  5596. static int _cl5NewDBFile (const char *replName, const char *replGen, CL5DBFile** dbFile)
  5597. {
  5598. int rc;
  5599. DB *db = NULL;
  5600. char *name;
  5601. char *semadir;
  5602. #ifdef HPUX
  5603. char cwd [PATH_MAX+1];
  5604. #endif
  5605. PR_ASSERT (replName && replGen && dbFile);
  5606. if (!dbFile) {
  5607. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5608. "_cl5NewDBFile: NULL dbFile\n");
  5609. return CL5_UNKNOWN_ERROR;
  5610. }
  5611. (*dbFile) = (CL5DBFile *)slapi_ch_calloc (1, sizeof (CL5DBFile));
  5612. if (*dbFile == NULL)
  5613. {
  5614. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5615. "_cl5NewDBFile: memory allocation failed\n");
  5616. return CL5_MEMORY_ERROR;
  5617. }
  5618. name = _cl5MakeFileName (replName, replGen);
  5619. {
  5620. /* The subname argument allows applications to have
  5621. * subdatabases, i.e., multiple databases inside of a single
  5622. * physical file. This is useful when the logical databases
  5623. * are both numerous and reasonably small, in order to
  5624. * avoid creating a large number of underlying files.
  5625. */
  5626. char *subname = NULL;
  5627. DB_ENV *dbEnv = s_cl5Desc.dbEnv;
  5628. rc = db_create(&db, dbEnv, 0);
  5629. if (0 != rc) {
  5630. goto out;
  5631. }
  5632. rc = db->set_pagesize(
  5633. db,
  5634. s_cl5Desc.dbConfig.pageSize);
  5635. if (0 != rc) {
  5636. goto out;
  5637. }
  5638. #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 3300
  5639. rc = db->set_malloc(db, (void *)slapi_ch_malloc);
  5640. if (0 != rc) {
  5641. goto out;
  5642. }
  5643. #endif
  5644. DB_OPEN(s_cl5Desc.dbEnvOpenFlags,
  5645. db, NULL /* txnid */, name, subname, DB_BTREE,
  5646. DB_CREATE | DB_THREAD, s_cl5Desc.dbConfig.fileMode, rc);
  5647. }
  5648. out:
  5649. if (rc != 0)
  5650. {
  5651. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5652. "_cl5NewDBFile: db_open failed; db error - %d %s\n",
  5653. rc, db_strerror(rc));
  5654. rc = CL5_DB_ERROR;
  5655. goto done;
  5656. }
  5657. (*dbFile)->db = db;
  5658. (*dbFile)->name = name;
  5659. (*dbFile)->replName = slapi_ch_strdup (replName);
  5660. (*dbFile)->replGen = slapi_ch_strdup (replGen);
  5661. /*
  5662. * Considerations for setting up cl semaphore:
  5663. * (1) The NT version of SleepyCat uses test-and-set mutexes
  5664. * at the DB page level instead of blocking mutexes. That has
  5665. * proven to be a killer for the changelog DB, as this DB is
  5666. * accessed by multiple a reader threads (the repl thread) and
  5667. * writer threads (the server ops threads) usually at the last
  5668. * pages of the DB, due to the sequential nature of the changelog
  5669. * keys. To avoid the test-and-set mutexes, we could use semaphore
  5670. * to serialize the writers and avoid the high mutex contention
  5671. * that SleepyCat is unable to avoid.
  5672. * (2) [610948] Linux master hangs for 2 hours
  5673. * [611239] _cl5DeadlockMain: lock_detect succeeded
  5674. * (3) DS 6.2 introduced the semaphore on all platforms (replaced
  5675. * the serial lock used on Windows and Linux described above).
  5676. * The number of the concurrent writes now is configurable by
  5677. * nsslapd-changelogmaxconcurrentwrites (the server needs to
  5678. * be restarted).
  5679. */
  5680. semadir = s_cl5Desc.dbDir;
  5681. #ifdef HPUX
  5682. /*
  5683. * HP sem_open() does not allow pathname component "./" or "../"
  5684. * in the semaphore name. For simplicity and to avoid doing
  5685. * chdir() in multi-thread environment, current working dir
  5686. * (log dir) is used to replace the original semaphore dir
  5687. * if it contains "./".
  5688. */
  5689. if ( strstr ( semadir, "./" ) != NULL && getcwd ( cwd, PATH_MAX+1 ) != NULL )
  5690. {
  5691. semadir = cwd;
  5692. }
  5693. #endif
  5694. if ( semadir != NULL )
  5695. {
  5696. (*dbFile)->semaName = slapi_ch_smprintf("%s/%s.sema", semadir, replName);
  5697. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5698. "_cl5NewDBFile: semaphore %s\n", (*dbFile)->semaName);
  5699. (*dbFile)->sema = PR_OpenSemaphore((*dbFile)->semaName,
  5700. PR_SEM_CREATE | PR_SEM_EXCL, 0666,
  5701. s_cl5Desc.dbConfig.maxConcurrentWrites );
  5702. slapi_log_error (SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5NewDBFile: maxConcurrentWrites=%d\n", s_cl5Desc.dbConfig.maxConcurrentWrites );
  5703. }
  5704. if ((*dbFile)->sema == NULL )
  5705. {
  5706. /* If the semaphore was left around due
  5707. * to an unclean exit last time, remove
  5708. * and re-create it.
  5709. */
  5710. if (PR_GetError() == PR_FILE_EXISTS_ERROR) {
  5711. PR_DeleteSemaphore((*dbFile)->semaName);
  5712. (*dbFile)->sema = PR_OpenSemaphore((*dbFile)->semaName,
  5713. PR_SEM_CREATE | PR_SEM_EXCL, 0666,
  5714. s_cl5Desc.dbConfig.maxConcurrentWrites );
  5715. }
  5716. /* If we still failed to create the semaphore,
  5717. * we should just error out. */
  5718. if ((*dbFile)->sema == NULL )
  5719. {
  5720. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5721. "_cl5NewDBFile: failed to create semaphore %s; NSPR error - %d\n",
  5722. (*dbFile)->semaName ? (*dbFile)->semaName : "(nil)", PR_GetError());
  5723. rc = CL5_SYSTEM_ERROR;
  5724. goto done;
  5725. }
  5726. }
  5727. /* compute number of entries in the file */
  5728. /* ONREPL - to improve performance, we keep entry count in memory
  5729. and write it down during shutdown. Problem: this will not
  5730. work with multiple processes. Do we have to worry about that?
  5731. */
  5732. if (s_cl5Desc.dbOpenMode == CL5_OPEN_NORMAL)
  5733. {
  5734. rc = _cl5GetEntryCount (*dbFile);
  5735. if (rc != CL5_SUCCESS)
  5736. {
  5737. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
  5738. "_cl5NewDBFile: failed to get entry count\n");
  5739. goto done;
  5740. }
  5741. }
  5742. done:
  5743. if (rc != CL5_SUCCESS)
  5744. {
  5745. _cl5DBCloseFile ((void**)dbFile);
  5746. /* slapi_ch_free accepts NULL pointer */
  5747. slapi_ch_free ((void**)&name);
  5748. slapi_ch_free ((void**)dbFile);
  5749. }
  5750. return rc;
  5751. }
  5752. static void _cl5DBCloseFile (void **data)
  5753. {
  5754. CL5DBFile *file;
  5755. char fullpathname[MAXPATHLEN];
  5756. PR_ASSERT (data);
  5757. file = *(CL5DBFile**)data;
  5758. PR_ASSERT (file);
  5759. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBCloseFile: "
  5760. "Closing database %s/%s\n", s_cl5Desc.dbDir, file->name);
  5761. /* close the file */
  5762. /* if this is normal close or close after import, update entry count */
  5763. if ((s_cl5Desc.dbOpenMode == CL5_OPEN_NORMAL && s_cl5Desc.dbState == CL5_STATE_CLOSING) ||
  5764. s_cl5Desc.dbOpenMode == CL5_OPEN_LDIF2CL)
  5765. {
  5766. _cl5WriteEntryCount (file);
  5767. _cl5WriteRUV (file, PR_TRUE);
  5768. _cl5WriteRUV (file, PR_FALSE);
  5769. }
  5770. /* close the db */
  5771. if (file->db) {
  5772. file->db->close(file->db, 0);
  5773. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBCloseFile: "
  5774. "Closed the changelog database handle for %s/%s\n", s_cl5Desc.dbDir, file->name);
  5775. file->db = NULL;
  5776. }
  5777. if (file->flags & DB_FILE_DELETED)
  5778. {
  5779. int rc = 0;
  5780. /* We need to use the libdb API to delete the files, otherwise we'll
  5781. * run into problems when we try to checkpoint transactions later. */
  5782. PR_snprintf(fullpathname, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, file->name);
  5783. rc = s_cl5Desc.dbEnv->dbremove(s_cl5Desc.dbEnv, 0, fullpathname, 0, 0);
  5784. if (rc != 0)
  5785. {
  5786. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBCloseFile: "
  5787. "failed to remove (%s) file; libdb error - %d (%s)\n",
  5788. fullpathname, rc, db_strerror(rc));
  5789. } else {
  5790. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBCloseFile: "
  5791. "Deleted the changelog database file %s/%s\n", s_cl5Desc.dbDir, file->name);
  5792. }
  5793. }
  5794. /* slapi_ch_free accepts NULL pointer */
  5795. slapi_ch_free ((void**)&file->name);
  5796. slapi_ch_free ((void**)&file->replName);
  5797. slapi_ch_free ((void**)&file->replGen);
  5798. ruv_destroy(&file->maxRUV);
  5799. ruv_destroy(&file->purgeRUV);
  5800. if (file->sema) {
  5801. PR_CloseSemaphore (file->sema);
  5802. PR_DeleteSemaphore (file->semaName);
  5803. file->sema = NULL;
  5804. }
  5805. slapi_ch_free ((void**)&file->semaName);
  5806. slapi_ch_free (data);
  5807. }
  5808. static int _cl5GetDBFile (Object *replica, Object **obj)
  5809. {
  5810. char *fileName;
  5811. PR_ASSERT (replica && obj);
  5812. fileName = _cl5Replica2FileName (replica);
  5813. *obj = objset_find(s_cl5Desc.dbFiles, _cl5CompareDBFile, fileName);
  5814. if (*obj)
  5815. {
  5816. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5GetDBFile: "
  5817. "found DB object %p for database %s\n", *obj, fileName);
  5818. slapi_ch_free_string(&fileName);
  5819. return CL5_SUCCESS;
  5820. }
  5821. else
  5822. {
  5823. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5GetDBFile: "
  5824. "no DB object found for database %s\n", fileName);
  5825. slapi_ch_free_string(&fileName);
  5826. return CL5_NOTFOUND;
  5827. }
  5828. }
  5829. static int _cl5GetDBFileByReplicaName (const char *replName, const char *replGen,
  5830. Object **obj)
  5831. {
  5832. char *fileName;
  5833. PR_ASSERT (replName && replGen && obj);
  5834. fileName = _cl5MakeFileName (replName, replGen);
  5835. *obj = objset_find(s_cl5Desc.dbFiles, _cl5CompareDBFile, fileName);
  5836. if (*obj)
  5837. {
  5838. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5GetDBFileByReplicaName: "
  5839. "found DB object %p for database %s\n", *obj, fileName);
  5840. slapi_ch_free_string(&fileName);
  5841. return CL5_SUCCESS;
  5842. }
  5843. else
  5844. {
  5845. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5GetDBFileByReplicaName: "
  5846. "no DB object found for database %s\n", fileName);
  5847. slapi_ch_free_string(&fileName);
  5848. return CL5_NOTFOUND;
  5849. }
  5850. }
  5851. static void _cl5DBDeleteFile (Object *obj)
  5852. {
  5853. CL5DBFile *file;
  5854. int rc = 0;
  5855. PR_ASSERT (obj);
  5856. file = (CL5DBFile*)object_get_data (obj);
  5857. PR_ASSERT (file);
  5858. file->flags |= DB_FILE_DELETED;
  5859. rc = objset_remove_obj(s_cl5Desc.dbFiles, obj);
  5860. if (rc != OBJSET_SUCCESS) {
  5861. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBDeleteFile: "
  5862. "could not find DB object %p\n", obj);
  5863. } else {
  5864. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBDeleteFile: "
  5865. "removed DB object %p\n", obj);
  5866. }
  5867. object_release (obj);
  5868. }
  5869. static void _cl5DBFileInitialized (Object *obj)
  5870. {
  5871. CL5DBFile *file;
  5872. PR_ASSERT (obj);
  5873. file = (CL5DBFile*)object_get_data (obj);
  5874. PR_ASSERT (file);
  5875. file->flags |= DB_FILE_INIT;
  5876. }
  5877. static int _cl5CompareDBFile (Object *el1, const void *el2)
  5878. {
  5879. CL5DBFile *file;
  5880. const char *name;
  5881. PR_ASSERT (el1 && el2);
  5882. file = (CL5DBFile*) object_get_data (el1);
  5883. name = (const char*) el2;
  5884. return ((file->flags & DB_FILE_INIT) ? strcmp (file->name, name) : 1);
  5885. }
  5886. static int _cl5CopyDBFiles (const char *srcDir, const char *destDir, Object **replicas)
  5887. {
  5888. char srcFile [MAXPATHLEN + 1];
  5889. char destFile[MAXPATHLEN + 1];
  5890. int rc;
  5891. Object *obj;
  5892. CL5DBFile *file;
  5893. /* ONREPL currently, dbidlist is ignored because db code can't handle discrepancy between
  5894. transaction log and present files; this should change before 5.0 ships */
  5895. obj = objset_first_obj (s_cl5Desc.dbFiles);
  5896. while (obj)
  5897. {
  5898. file = (CL5DBFile*)object_get_data (obj);
  5899. PR_ASSERT (file);
  5900. PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", srcDir, file->name);
  5901. PR_snprintf(destFile, MAXPATHLEN, "%s/%s", destDir, file->name);
  5902. rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
  5903. if (rc != 0)
  5904. {
  5905. object_release (obj);
  5906. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5907. "_cl5CopyDBFiles: failed to copy %s from %s to %s\n",
  5908. file->name, srcDir, destDir);
  5909. return CL5_SYSTEM_ERROR;
  5910. }
  5911. obj = objset_next_obj (s_cl5Desc.dbFiles, obj);
  5912. }
  5913. return CL5_SUCCESS;
  5914. }
  5915. /*
  5916. * return 1: true (the "filename" ends with "ext")
  5917. * return 0: false
  5918. */
  5919. static int _cl5FileEndsWith(const char *filename, const char *ext)
  5920. {
  5921. char *p = NULL;
  5922. int flen = strlen(filename);
  5923. int elen = strlen(ext);
  5924. if (0 == flen || 0 == elen)
  5925. {
  5926. return 0;
  5927. }
  5928. p = strstr(filename, ext);
  5929. if (NULL == p)
  5930. {
  5931. return 0;
  5932. }
  5933. if (p - filename + elen == flen)
  5934. {
  5935. return 1;
  5936. }
  5937. return 0;
  5938. }
  5939. static int _cl5ExportFile (PRFileDesc *prFile, Object *obj)
  5940. {
  5941. int rc;
  5942. void *iterator = NULL;
  5943. slapi_operation_parameters op = {0};
  5944. char *buff;
  5945. PRInt32 len, wlen;
  5946. CL5Entry entry;
  5947. CL5DBFile *file;
  5948. PR_ASSERT (prFile && obj);
  5949. file = (CL5DBFile*)object_get_data (obj);
  5950. PR_ASSERT (file);
  5951. ruv_dump (file->purgeRUV, "clpurgeruv", prFile);
  5952. ruv_dump (file->maxRUV, "clmaxruv", prFile);
  5953. slapi_write_buffer (prFile, "\n", strlen("\n"));
  5954. entry.op = &op;
  5955. rc = _cl5GetFirstEntry (obj, &entry, &iterator, NULL);
  5956. while (rc == CL5_SUCCESS)
  5957. {
  5958. rc = _cl5Operation2LDIF (&op, file->replGen, &buff, &len);
  5959. if (rc != CL5_SUCCESS)
  5960. {
  5961. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5962. "_cl5ExportLDIF: failed to convert operation to ldif\n");
  5963. operation_parameters_done (&op);
  5964. break;
  5965. }
  5966. wlen = slapi_write_buffer (prFile, buff, len);
  5967. slapi_ch_free((void **)&buff);
  5968. if (wlen < len)
  5969. {
  5970. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5971. "_cl5ExportLDIF: failed to write to ldif file\n");
  5972. rc = CL5_SYSTEM_ERROR;
  5973. operation_parameters_done (&op);
  5974. break;
  5975. }
  5976. cl5_operation_parameters_done (&op);
  5977. rc = _cl5GetNextEntry (&entry, iterator);
  5978. }
  5979. cl5_operation_parameters_done (&op);
  5980. if (iterator)
  5981. cl5DestroyIterator (iterator);
  5982. if (rc != CL5_NOTFOUND)
  5983. {
  5984. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  5985. "_cl5ExportLDIF: failed to retrieve changelog entry\n");
  5986. }
  5987. else
  5988. {
  5989. rc = CL5_SUCCESS;
  5990. }
  5991. return rc;
  5992. }
  5993. static PRBool _cl5ReplicaInList (Object *replica, Object **replicas)
  5994. {
  5995. int i;
  5996. PR_ASSERT (replica && replicas);
  5997. /* ONREPL I think it should be sufficient to just compare replica pointers */
  5998. for (i=0; replicas[i]; i++)
  5999. {
  6000. if (replica == replicas[i])
  6001. return PR_TRUE;
  6002. }
  6003. return PR_FALSE;
  6004. }
  6005. static char* _cl5GetHelperEntryKey (int type, char *csnStr)
  6006. {
  6007. CSN *csn= csn_new();
  6008. char *rt;
  6009. csn_set_time(csn, (time_t)type);
  6010. csn_set_replicaid(csn, 0);
  6011. rt = csn_as_string(csn, PR_FALSE, csnStr);
  6012. csn_free(&csn);
  6013. return rt;
  6014. }
  6015. static Object* _cl5GetReplica (const slapi_operation_parameters *op, const char* replGen)
  6016. {
  6017. Slapi_DN *sdn;
  6018. Object *replObj;
  6019. Replica *replica;
  6020. char *newGen;
  6021. PR_ASSERT (op && replGen);
  6022. sdn = slapi_sdn_new_dn_byref(op->target_address.dn);
  6023. replObj = replica_get_replica_from_dn (sdn);
  6024. if (replObj)
  6025. {
  6026. /* check to see if replica generation has not change */
  6027. replica = (Replica*)object_get_data (replObj);
  6028. PR_ASSERT (replica);
  6029. newGen = replica_get_generation (replica);
  6030. PR_ASSERT (newGen);
  6031. if (strcmp (replGen, newGen) != 0)
  6032. {
  6033. object_release (replObj);
  6034. replObj = NULL;
  6035. }
  6036. slapi_ch_free ((void**)&replGen);
  6037. }
  6038. slapi_sdn_free (&sdn);
  6039. return replObj;
  6040. }
  6041. int
  6042. cl5_is_diskfull()
  6043. {
  6044. int rc;
  6045. PR_Lock(cl5_diskfull_lock);
  6046. rc = cl5_diskfull_flag;
  6047. PR_Unlock(cl5_diskfull_lock);
  6048. return rc;
  6049. }
  6050. static void
  6051. cl5_set_diskfull()
  6052. {
  6053. PR_Lock(cl5_diskfull_lock);
  6054. cl5_diskfull_flag = 1;
  6055. PR_Unlock(cl5_diskfull_lock);
  6056. }
  6057. static void
  6058. cl5_set_no_diskfull()
  6059. {
  6060. PR_Lock(cl5_diskfull_lock);
  6061. cl5_diskfull_flag = 0;
  6062. PR_Unlock(cl5_diskfull_lock);
  6063. }
  6064. int
  6065. cl5_diskspace_is_available()
  6066. {
  6067. int rval = 1;
  6068. #if defined( OS_solaris ) || defined( hpux )
  6069. struct statvfs fsbuf;
  6070. if (statvfs(s_cl5Desc.dbDir, &fsbuf) < 0)
  6071. {
  6072. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  6073. "cl5_diskspace_is_available: Cannot get file system info\n");
  6074. rval = 0;
  6075. }
  6076. else
  6077. {
  6078. unsigned long fsiz = fsbuf.f_bavail * fsbuf.f_frsize;
  6079. if (fsiz < NO_DISK_SPACE)
  6080. {
  6081. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  6082. "cl5_diskspace_is_available: No enough diskspace for changelog: (%u bytes free)\n", fsiz);
  6083. rval = 0;
  6084. }
  6085. else if (fsiz > MIN_DISK_SPACE)
  6086. {
  6087. /* assume recovered */
  6088. cl5_set_no_diskfull();
  6089. }
  6090. }
  6091. #endif
  6092. #if defined( linux )
  6093. struct statfs fsbuf;
  6094. if (statfs(s_cl5Desc.dbDir, &fsbuf) < 0)
  6095. {
  6096. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  6097. "cl5_diskspace_is_available: Cannot get file system info\n");
  6098. rval = 0;
  6099. }
  6100. else
  6101. {
  6102. unsigned long fsiz = fsbuf.f_bavail * fsbuf.f_bsize;
  6103. if (fsiz < NO_DISK_SPACE)
  6104. {
  6105. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
  6106. "cl5_diskspace_is_available: No enough diskspace for changelog: (%lu bytes free)\n", fsiz);
  6107. rval = 0;
  6108. }
  6109. else if (fsiz > MIN_DISK_SPACE)
  6110. {
  6111. /* assume recovered */
  6112. cl5_set_no_diskfull();
  6113. }
  6114. }
  6115. #endif
  6116. return rval;
  6117. }
  6118. int
  6119. cl5DbDirIsEmpty(const char *dir)
  6120. {
  6121. PRDir *prDir;
  6122. PRDirEntry *prDirEntry;
  6123. int isempty = 1;
  6124. if (!dir || !*dir) {
  6125. return isempty;
  6126. }
  6127. /* assume failure means it does not exist - other failure
  6128. cases will be handled by code which attempts to create the
  6129. db in this directory */
  6130. if (PR_Access(dir, PR_ACCESS_EXISTS)) {
  6131. return isempty;
  6132. }
  6133. prDir = PR_OpenDir(dir);
  6134. if (prDir == NULL) {
  6135. return isempty; /* assume failure means does not exist */
  6136. }
  6137. while (NULL != (prDirEntry = PR_ReadDir(prDir, PR_SKIP_DOT | PR_SKIP_DOT_DOT))) {
  6138. if (NULL == prDirEntry->name) { /* NSPR doesn't behave like the docs say it should */
  6139. break;
  6140. }
  6141. isempty = 0; /* found at least one "real" file */
  6142. break;
  6143. }
  6144. PR_CloseDir(prDir);
  6145. return isempty;
  6146. }