12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #include <string>
- #include "deploy.hpp"
- #include "jcrc.hpp"
- #include "jexcept.hpp"
- #include "jfile.hpp"
- #include "jptree.hpp"
- #include "jstring.hpp"
- #include "jmutex.hpp"
- #include "xslprocessor.hpp"
- offset_t getDirSize(const char* path, bool subdirs = true)
- {
- offset_t size = 0;
- if (!path || !*path)
- return size;
- Owned<IFile> pFile = createIFile(path);
- if (pFile->exists())
- {
- if (pFile->isDirectory() == foundYes)
- {
- Owned<IDirectoryIterator> it = pFile->directoryFiles(NULL, false, true);
- ForEach(*it)
- {
- if (it->isDir())
- {
- if (subdirs)
- {
- StringBuffer subdir;
- it->getName(subdir);
- StringBuffer dir(path);
- dir.append(PATHSEPCHAR).append(subdir);
- size += getDirSize(dir.str(), subdirs);
- }
- }
- else
- size += it->getFileSize();
- }
- }
- else
- size = pFile->size();
- }
- return size;
- }
- class CDeployTask : public CInterface, implements IDeployTask
- {
- public:
- IMPLEMENT_IINTERFACE;
- //---------------------------------------------------------------------------
- // CDeployTask
- //---------------------------------------------------------------------------
- CDeployTask(IDeploymentCallback& callback,
- const char* caption, const char* processType, const char* comp,
- const char* instance, const char* source, const char* target,
- const char* sshUser, const char* sshKeyFile, const char* sshKeyPassphrase,
- bool useSsh, EnvMachineOS os, const char* processName=NULL)
- : m_caption(caption), m_compName(comp),
- m_instanceName(instance), m_processed(false), m_errorCode(0),
- m_abort(false), m_dummy(false), m_machineOS(os), m_flags(0),
- m_processType(processType),
- m_processName(processName),
- m_targetFileWithWrongCase(false),
- m_sshUser(sshUser),
- m_sshKeyFile(sshKeyFile),
- m_sshKeyPassphrase(sshKeyPassphrase),
- m_useSsh(useSsh),
- m_updateProgress(true)
- {
- m_pCallback.set(&callback);
- m_fileSpec[DT_SOURCE].set(source ? source : "");
- m_fileSpec[DT_TARGET].set(target ? target : "");
- }
- //---------------------------------------------------------------------------
- // getCaption
- //---------------------------------------------------------------------------
- const char* getCaption() const
- {
- return m_caption;
- }
- //---------------------------------------------------------------------------
- // getCaption
- //---------------------------------------------------------------------------
- void setCaption(const char* caption)
- {
- m_caption.set(caption);
- }
- //---------------------------------------------------------------------------
- // getCompName
- //---------------------------------------------------------------------------
- const char* getCompName() const
- {
- return m_compName;
- }
- //---------------------------------------------------------------------------
- // getInstanceName
- //---------------------------------------------------------------------------
- const char* getInstanceName() const
- {
- return m_instanceName;
- }
- //---------------------------------------------------------------------------
- // getFileSpec
- //---------------------------------------------------------------------------
- const char* getFileSpec(int idx) const
- {
- assertex(idx >= 0 && idx < 2);
- return m_fileSpec[idx];
- }
- //---------------------------------------------------------------------------
- // getFileName
- //---------------------------------------------------------------------------
- const char* getFileName(int idx) const
- {
- assertex(idx >= 0 && idx < 2);
- return pathTail(m_fileSpec[idx]);
- }
- //---------------------------------------------------------------------------
- // getFileExt
- //---------------------------------------------------------------------------
- const char* getFileExt(int idx) const
- {
- return findFileExtension(m_fileSpec[idx]);
- }
- //---------------------------------------------------------------------------
- // getFilePath
- //---------------------------------------------------------------------------
- StringAttr getFilePath(int idx) const
- {
- assertex(idx >= 0 && idx < 2);
- StringBuffer dir;
- splitDirTail(m_fileSpec[idx], dir);
- unsigned int dirLen = dir.length();
- if (dirLen-- && isPathSepChar(dir.charAt(dirLen)))//remove trailing \ or /
- dir.setLength(dirLen);
- return dir.str();
- }
- //---------------------------------------------------------------------------
- // getFileExists
- //---------------------------------------------------------------------------
- bool getFileExists(int idx) const
- {
- assertex(idx >= 0 && idx < 2);
- if (m_machineOS == MachineOsLinux && m_sshUser.length() && m_sshKeyFile.length())
- return checkSSHFileExists(m_fileSpec[idx]);
- else
- {
- Owned<IFile> pFile = createIFile(m_fileSpec[idx]);
- return pFile->exists();
- }
- }
- //---------------------------------------------------------------------------
- // getFileSize
- //---------------------------------------------------------------------------
- offset_t getFileSize(int idx) const
- {
- assertex(idx >= 0 && idx < 2);
- Owned<IFile> pFile = createIFile(m_fileSpec[idx]);
- return pFile->size();
- }
- EnvMachineOS getMachineOS() const
- {
- return m_machineOS;
- }
- const char* getSSHUser() const
- {
- return m_sshUser.str();
- }
-
- const char* getSSHKeyFile() const
- {
- return m_sshKeyFile.str();
- }
- const char* getSSHKeyPassphrase() const
- {
- return m_sshKeyPassphrase.str();
- }
- bool checkSSHFileExists(const char* filename) const
- {
- bool flag = false;
- Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Ensure Path", m_processType, m_compName,
- m_instanceName, filename, NULL, m_sshUser.str(), m_sshKeyFile.str(), m_sshKeyPassphrase.str(),
- true, m_machineOS);
- try
- {
- StringBuffer destpath, destip, cmd, output, tmp, err;
- stripNetAddr(filename, destpath, destip);
- tmp.appendf("%d", msTick());
- cmd.clear().appendf("[ -e %s ] && echo %s", destpath.str(), tmp.str());
- task->execSSHCmd(destip.str(), cmd, output, err);
- flag = strstr(output.str(), tmp.str()) == output.str();
- }
- catch(IException* e)
- {
- throw e;
- }
- return flag;
- }
- void setFileSpec(int idx, const char* file)
- {
- assertex(idx >= 0 && idx < 2);
- m_fileSpec[idx].set(file && *file ? file : "");
- }
- //---------------------------------------------------------------------------
- // isProcessed
- //---------------------------------------------------------------------------
- bool isProcessed() const
- {
- return m_processed;
- }
- //---------------------------------------------------------------------------
- // getErrorCode
- //---------------------------------------------------------------------------
- DWORD getErrorCode() const
- {
- return m_errorCode;
- }
- //---------------------------------------------------------------------------
- // getErrorString
- //---------------------------------------------------------------------------
- const char* getErrorString() const
- {
- return m_errorString.str();
- }
- //---------------------------------------------------------------------------
- // getWarnings
- //---------------------------------------------------------------------------
- const char* getWarnings() const
- {
- return m_warnings.str();
- }
- //---------------------------------------------------------------------------
- // getAbort
- //---------------------------------------------------------------------------
- virtual bool getAbort() const
- {
- return m_abort;
- }
- //---------------------------------------------------------------------------
- // getFlags
- //---------------------------------------------------------------------------
- unsigned getFlags() const
- {
- return m_flags;
- }
- //---------------------------------------------------------------------------
- // setFlags
- //---------------------------------------------------------------------------
- void setFlags(unsigned flags)
- {
- m_flags = flags;
- }
- void setUpdateProgress(bool flag)
- {
- m_updateProgress = flag;
- }
- //---------------------------------------------------------------------------
- // createFile
- //---------------------------------------------------------------------------
- bool createFile(const char* text)
- {
- const char* szOpenMode = m_machineOS == MachineOsLinux ? "wb" : "wt";
- m_processed = true;
- assertex(text);
- const char* target = getFileSpec(DT_TARGET);
- while (true)
- {
- m_errorCode = 0;
- m_warnings.clear();
- m_errorString.clear();
- if (m_dummy) break;
- FILE* fp = fopen(target, szOpenMode);
- if (fp)
- {
- DWORD bytesWritten = 0;
- bytesWritten = fwrite(text, 1, strlen(text), fp);
- if (bytesWritten == strlen(text))
- {
- fflush(fp);
- fclose(fp);
- break;
- }
- }
- // Prompt to retry on error
- m_errorCode = getLastError();
- m_errorString.appendf("Cannot create %s: ", target);
- if (fp) fclose(fp); // do this after retrieving last error
- if (showErrorMsg("Error creating file")) //ignore or abort?
- break;
- }
- return (m_errorCode == 0);
- }
- //---------------------------------------------------------------------------
- // transformFile
- //---------------------------------------------------------------------------
- bool transformFile(IXslProcessor& processor, IXslTransform& transform, const char* tempPath)
- {
- m_processed = true;
- const char* xsl = getFileSpec(DT_SOURCE);
- const char* target = getFileSpec(DT_TARGET);
- while (true)
- {
- try
- {
- m_errorCode = 0;
- m_warnings.clear();
- m_errorString.clear();
- const char* processName = m_processName ? m_processName : m_compName;
- transform.setParameter("process", StringBuffer("'").append(processName).append("'").str());
- transform.setParameter("outputFilePath", StringBuffer("'").append(target).append("'").str());
- transform.setParameter("tempPath", StringBuffer("'").append(tempPath).append("'").str());
- if (m_machineOS == MachineOsLinux)
- transform.setParameter("isLinuxInstance", "1");//set optional parameter
- if (m_instanceName.length())
- transform.setParameter("instance", StringBuffer("'").append(m_instanceName).append("'").str());
- transform.loadXslFromFile(xsl);
- if (!m_dummy)
- {
- if(m_machineOS != MachineOsLinux)
- {
- transform.setResultTarget(target);
- Owned<IFile> pTargetFile = createIFile(target);
- if (pTargetFile->exists() && pTargetFile->isReadOnly())
- pTargetFile->setReadOnly(false);
- pTargetFile.clear();
- processor.execute(&transform);
- }
- else
- {
- char tempfile[_MAX_PATH];
- StringBuffer sb;
- StringBuffer sbtmp;
- sbtmp.appendf("%d", msTick());
- bool flag = false;
- {
- Owned<IFile> pTargetFile;
- StringBuffer tmpoutbuf;
- transform.transform(tmpoutbuf);
- tmpoutbuf.replaceString("\r\n", "\n");
-
- if (m_useSsh && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
- {
- StringBuffer destpath, ip;
- stripNetAddr(target, destpath,ip);
- if (ip.length())
- {
- getTempPath(tempfile, sizeof(tempfile), m_compName);
- sb.append(tempfile).append(sbtmp);
- pTargetFile.setown(createIFile(sb.str()));
- flag = true;
- }
- }
- if (!flag)
- pTargetFile.setown(createIFile(target));
-
- if (pTargetFile->exists() && pTargetFile->isReadOnly())
- pTargetFile->setReadOnly(false);
- Owned<IFileIO> pTargetFileIO = pTargetFile->openShared(IFOcreate, IFSHfull);
- pTargetFileIO->write( 0, tmpoutbuf.length(), tmpoutbuf.str());
- }
- if (flag)
- {
- Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Copy File",
- m_processType, m_compName, m_instanceName, sb.str(), target, m_sshUser.str(),
- m_sshKeyFile.str(), m_sshKeyPassphrase.str(), true, m_machineOS);
- try
- {
- task->setUpdateProgress(false);
- task->copyFile(DEFLAGS_CONFIGFILES);
- }
- catch(IException* e)
- {
- if (!DeleteFile(sb.str()))
- WARNLOG("Couldn't delete file %s", sb.str());
- throw e;
- }
- if (!DeleteFile(sb.str()))
- WARNLOG("Couldn't delete file %s", sb.str());
- }
- }
- m_warnings.clear().append(transform.getMessages());
- transform.closeResultTarget();
- #ifdef _WIN32
- //Samba maps the Windows archive, system, and hidden file attributes to the owner, group, and world
- //execute bits of the file respectively. So if we are deploying to a Linux box then make the file
- //executable by setting its archive bit.
- //
- if (m_machineOS == MachineOsLinux && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty())
- ::SetFileAttributes(target, FILE_ATTRIBUTE_ARCHIVE);
- #else
- //UNIMPLEMENTED;
- #endif
- }
- break;
- }
- catch (IException* e)
- {
- StringBuffer warning;
- e->errorMessage(warning);
- //for better readability of warning, remove redundant prefix of "[XSLT warning: ", if present
- const char* pattern = "[XSLT warning: ";
- int len = strlen(pattern);
- if (!strncmp(warning, pattern, len))
- warning.remove(0, len);
- //remove the excessive info about XSLT context when this was thrown
- const char* begin = warning.str();
- const char* end = strstr(begin, ", style tree node:");
- if (end)
- warning.setLength(end-begin);
- m_errorString.appendf("Cannot create %s\nusing XSL transform %s\n\n%s", target, xsl, warning.str());
- e->Release();
- //remove incomplete (invalid) output file produced thus far
- if (!DeleteFile(target))
- WARNLOG("Couldn't delete file %s", target);
- }
- catch (...)
- {
- m_errorString.appendf("Cannot create %s\nusing XSL transform %s\n\n", target, xsl);
- m_errorString.append("Unspecified XSL error");
- //remove incomplete (invalid) output file produced thus far
- if (!DeleteFile(target))
- WARNLOG("Couldn't delete file %s", target);
- }
- // Prompt to retry on error
- m_errorCode = (DWORD) -1;//don't format m_errorString based on last error
- if (showErrorMsg("Error transforming file"))//ignore or abort?
- break;
- }
- return (m_errorCode == 0);
- }
- //---------------------------------------------------------------------------
- // copyFile
- //---------------------------------------------------------------------------
- bool copyFile(unsigned mode)
- {
- if (m_updateProgress)
- m_pCallback->printStatus(this);
- m_errorCode = 0;
- const char* source = getFileSpec(DT_SOURCE);
- const char* target = getFileSpec(DT_TARGET);
- m_msgBoxOwner = false; //this thread hasn't shown msg box yet
- while (true)
- {
- m_warnings.clear();
- if (m_pCallback->getAbortStatus())
- break;
- m_errorCode = 0;
- m_errorString.clear();
- if (m_dummy)
- break;
- unsigned dtcFlags = mode >> 2; //gets DTC_TIME | DTC_SIZE | DTC_CRC
- if (dtcFlags)//deploy only if different checked ??
- {
- if (compareFile(dtcFlags | DTC_DEL_WRONG_CASE) || m_pCallback->getAbortStatus()) //flags specified and files match - ignore
- {
- Owned<IFile> f = createIFile(source);
- m_pCallback->fileSizeCopied(f->size(),true);
- break;
- }
- else if (m_targetFileWithWrongCase)
- break;
- }
- else if (m_machineOS != MachineOsW2K && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty() &&
- (targetFileExistsWithWrongCaseSensitivity(true) || m_pCallback->getAbortStatus()))
- {
- break;
- }
- //clear file comparison failure errors generated by compareFile above
- m_processed = false;
- m_errorString.clear();
- m_errorCode = 0;
- Owned<IFile> pSrcFile;
- Owned<IFile> pDstFile;
- bool bCopyRC = false;
- try
- {
- pSrcFile.setown(createIFile(source));
- pDstFile.setown(createIFile(target));
- class CCopyFileProgress : implements ICopyFileProgress
- {
- public:
- CCopyFileProgress(): m_percentDone(0),m_sizeReported(0){}
- CCopyFileProgress(CDeployTask* pTask)
- : m_percentDone(0),m_sizeReported(0)
- {
- m_pTask.set(pTask);
- m_pCallback.set(&pTask->getCallback());
- m_originalCaption.append( pTask->getCaption() );
- }
- virtual CFPmode onProgress(unsigned __int64 sizeDone, unsigned __int64 totalSize)
- {
- CFPmode rc = m_pCallback->getAbortStatus() ? CFPcancel : CFPcontinue;
- StringBuffer newCaption;
- newCaption.append(m_originalCaption);
- if (sizeDone && sizeDone != totalSize)
- {
- unsigned int percentDone = (unsigned int) ((sizeDone*100)/totalSize);
- const unsigned int DELTA = 25;
- const unsigned int rounded = (percentDone / DELTA)*DELTA;
- if (rounded > m_percentDone )
- m_percentDone = rounded;
- else
- return rc;
- newCaption.appendf(" [%d%%]", m_percentDone);
- }
- if (strcmp(newCaption.str(), m_pTask->getCaption()))
- {
- m_pTask->setCaption(newCaption.str());
- m_pCallback->printStatus(m_pTask);
- }
- // report size copied
- if (sizeDone==totalSize)
- m_pCallback->fileSizeCopied(sizeDone-m_sizeReported, true);
- else if (sizeDone - m_sizeReported>1024) // 1k
- {
- m_pCallback->fileSizeCopied(sizeDone-m_sizeReported, false);
- m_sizeReported = sizeDone;
- }
- return rc;
- }
- virtual ~CCopyFileProgress()
- {
- if (m_pTask)
- if (0 != strcmp(m_originalCaption.str(), m_pTask->getCaption()))
- {
- m_pTask->setCaption(m_originalCaption);
- m_pCallback->printStatus(m_pTask);
- }
- }
- virtual void setDeployTask(CDeployTask* pTask)
- {
- m_pTask.set(pTask);
- m_pCallback.set(&pTask->getCallback());
- m_originalCaption.append( pTask->getCaption() );
- }
- private:
- Owned<CDeployTask> m_pTask;
- Owned<IDeploymentCallback> m_pCallback;
- StringBuffer m_originalCaption;
- unsigned int m_percentDone;
- offset_t m_sizeReported;
- };
- CCopyFileProgress copyProgress;
- if (m_updateProgress)
- copyProgress.setDeployTask(this);
- if (m_useSsh && m_machineOS == MachineOsLinux && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
- {
- int retcode;
- StringBuffer outbuf, cmdline, errbuf;
- StringBuffer destpath,ip;
- StringBuffer passphr;
- getKeyPassphrase(passphr);
- StringBuffer sb(source);
- bool flag = (sb.charAt(sb.length() - 1) == '*');
- if (flag)
- sb.setLength(sb.length() - 1);
- offset_t dirsize = getDirSize(sb.str());
- if (flag && m_updateProgress)
- copyProgress.onProgress(0, dirsize);
-
- stripNetAddr(target, destpath, ip);
- cmdline.appendf("pscp -p -noagent -q %s -i %s -l %s %s \"%s\" %s:%s", flag?"-r":"",
- m_sshKeyFile.str(), m_sshUser.str(), passphr.str(), source, ip.str(), destpath.str());
- retcode = pipeSSHCmd(cmdline.str(), outbuf, errbuf);
- if (retcode)
- {
- m_errorCode = -1;
- String err(errbuf.str());
- int index = err.indexOf('\n');
- String* perr = err.substring(0, index > 0? index : err.length());
- m_errorString.clear().appendf("%s", perr->str());
- delete perr;
- bCopyRC = false;
- }
- else
- {
- if (flag && m_updateProgress)
- copyProgress.onProgress(dirsize, dirsize);
- else if (m_updateProgress)
- copyProgress.onProgress(pSrcFile->size(), pSrcFile->size());
- Owned<IDeployTask> task = createDeployTask(*m_pCallback, "Chmod", m_processType, m_compName, m_instanceName,
- destpath.str(), NULL, m_sshUser.str(), m_sshKeyFile.str(), m_sshKeyPassphrase.str(), true, m_machineOS);
- try
- {
- StringBuffer cmd;
- cmd.clear().appendf("chmod -R 755 %s", destpath.str());
- task->execSSHCmd(ip.str(), cmd, outbuf, errbuf);
- }
- catch(IException* e)
- {
- throw e;
- }
- bCopyRC = true;
- }
- }
- else
- {
- ::copyFile(pDstFile, pSrcFile, 0x100000, ©Progress);
- bCopyRC = true;
- }
- }
- catch (IException* e)
- {
- e->Release();
- bCopyRC = false;
- m_errorCode = getLastError();
- }
- catch (...)
- {
- bCopyRC = false;
- m_errorCode = getLastError();
- }
- //if copy has failed due to access denied error, then change destination
- //file's attributes to normal (reset read only flag, if set) and retry
- if (m_machineOS != MachineOsLinux && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty())
- {
- if (!bCopyRC && (pDstFile.get() != NULL) && pDstFile->exists() && pDstFile->isReadOnly()/*&& m_machineOS != MachineOsLinux*/)
- {
- try
- {
- //BUG#48891 Handle the exception here to allow the user to retry or ignore.
- pDstFile->setReadOnly(false);
- continue;
- }
- catch(IException* e)
- {
- e->Release();
- }
- }
- }
- /* This method is invoked from multiple threads concurrently and implements the
- following logic. There are global retry and abort flags that can be set by
- any thread and are shared among threads to coordinate file copying process.
- The first thread that has an error and grabs the mutex pops up the error
- message box to the user. If the user selects "abort", the global abort flag is
- set and the thread exits. Other threads with or without errors check this
- abort flag and exit if it is set. If the user selected "ignore", the thread
- simply exits with error condition and other threads operate unaffected. However,
- if the user selected "retry" then we don't want other pending threads with error
- conditions to keep popping up error messages asking for abort/retry/ignore. The
- first thread becomes a "master retry thread" for lack of a better term. This
- thread dominates popping of the message boxes and other threads abide by user's
- wishes for the master retry thread. The idea is that the user should not be asked
- for every single file copy error since the cause of error in most scenarios could
- simply be of a global nature, for instance, that the target machine is not powered
- on or the target files are locked since the process is already running and needs
- to be stopped. An event semaphore is used by master retry thread to wake up all
- other threads with errors, which sense the retry flag and silently retry operation.
- */
- if (!bCopyRC)//file copy failed
- {
- // Prompt to retry on error
- m_errorString.appendf("Cannot copy %s to %s: ", source, target);
- synchronized block(s_monitor);
- if (m_pCallback->getAbortStatus())//has some other thread set the global abort flag?
- break; //go back to beginning of loop where we exit on abort
- enum responseType { r_abort = 1, r_retry, r_ignore };
- responseType rc;
- if (!s_msgBoxActive || m_msgBoxOwner) //no other thread has an active message box
- {
- //block other threads if they encounter error state until user responds
- s_msgBoxActive = true;
- m_msgBoxOwner = true;
- if (showErrorMsg("Error copying file")) //ignore or abort?
- {
- setRetryStatus(false);
- if (m_pCallback->getAbortStatus())
- {
- m_errorCode = (DWORD) -1;
- m_errorString.append("Aborted");
- rc = r_abort;
- }
- else
- rc = r_ignore;
- }
- else
- {
- setRetryStatus(true);
- rc = r_retry;
- }
- }
- else
- {
- //some other thread has an active message box
- //wait for that thread to signal us after it either successfully retries,
- //or if the user ignores that error or aborts deployment
- //
- s_monitor.wait();
- //respect user's wish to retry all queued failed operations
- rc = m_pCallback->getAbortStatus() ? r_abort : getRetryStatus() ? r_retry : r_ignore;
- }
- if (r_retry != rc)
- break;
- }
- else
- {
- //copy was successful
- if (m_machineOS != MachineOsLinux && !m_useSsh && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty())
- {
- pDstFile->setReadOnly(false);
- #ifdef _WIN32
- //Samba maps the Windows archive, system, and hidden file attributes to the owner, group, and world
- //execute bits of the file respectively. So if we are deploying to a Linux box then make the file
- //executable by setting its archive bit.
- //
- if (m_machineOS == MachineOsLinux)
- ::SetFileAttributes(target, FILE_ATTRIBUTE_ARCHIVE);
- #else
- //UNIMPLEMENTED;
- #endif
- }
- break;
- }
- }//while
- if (m_msgBoxOwner)//did this thread show the message box in last iteration of this loop?
- {
- synchronized block(s_monitor);
- s_msgBoxActive = false;
- m_msgBoxOwner = false;//up for grabs by other threads
- s_monitor.notifyAll();
- }
- else if (m_pCallback->getAbortStatus())
- {
- m_errorCode = (DWORD) -1;
- m_errorString.append("Aborted");
- }
- m_processed = true;
- return (m_errorCode == 0);
- }
- //---------------------------------------------------------------------------
- // renameFile
- //---------------------------------------------------------------------------
- bool renameFile()
- {
- m_processed = true;
- //IFile::move fails if you rename "\\ip\path\dir1" to "\\ip\path\dir2"
- //so try to rename to "dir2" if both source and dest have same path prefix
- //
- const char* src = getFileSpec(DT_SOURCE);
- const char* dst = getFileSpec(DT_TARGET);
- StringBuffer dstPath;
- bool linremote=(memcmp(src,"//",2)==0);
- if (linremote || !memcmp(src,"\\\\",2)) // see if remote path
- {
- RemoteFilename rfn;
- rfn.setRemotePath(dst);
- if (rfn.isLocal())
- {
- rfn.getLocalPath(dstPath);
- if (m_machineOS == MachineOsLinux)
- {
- dstPath.replace('\\', '/');
- dstPath.replace(':', '$');
- }
- else
- {
- dstPath.replace('/', '\\');
- dstPath.replace('$', ':');
- }
- dst = dstPath.str();
- }
- }
- Owned<IFile> pFile = createIFile(src);
- while (true)
- {
- m_errorCode = 0;
- m_warnings.clear();
- m_errorString.clear();
- try
- {
- if (m_dummy)
- break;
- pFile->move(dst);
- break;
- }
- catch(IException* e)
- {
- e->Release();
- }
- catch(...)
- {
- }
- // Prompt to retry on error
- m_errorString.clear().appendf("Cannot rename %s to %s: ", src, dst);
- if (showErrorMsg("Error renaming file"))//ignore or abort?
- break;
- }
- return (m_errorCode == 0);
- }
- //---------------------------------------------------------------------------
- // deleteFile
- //---------------------------------------------------------------------------
- bool deleteFile()
- {
- m_processed = true;
- const char* target = getFileSpec(DT_TARGET);
- m_errorCode = 0;
- m_warnings.clear();
- m_errorString.clear();
- bool rc = m_dummy || DeleteFile(target)==TRUE;
- m_errorCode = rc ? 0 : (DWORD) -1;
- return rc;
- }
- bool getFileAttributes(IFile* pFile, CDateTime& dt, offset_t& size, unsigned mode)
- {
- bool rc = true;
- try
- {
- if (mode & DTC_SIZE)
- size = pFile->size();
- if (mode & DTC_TIME)
- rc = pFile->getTime(NULL, &dt, NULL);
- }
- catch(IException* e)
- {
- e->Release();
- rc = false;
- }
- catch (...)
- {
- rc = false;
- }
- if (!rc)
- {
- m_errorCode = getLastError();
- m_errorString.clear().appendf("File %s does not exist or cannot be accessed: ", pFile->queryFilename());
- formatSystemError(m_errorString, m_errorCode);
- }
- return rc;
- }
- bool targetFileExistsWithWrongCaseSensitivity(bool bDeleteIfFound)
- {
- const char* target = getFileSpec(DT_TARGET);
- StringBuffer targetPath;
- const char* targetFileName = splitDirTail(target, targetPath);
- bool bLoop;
- bool rc = false;
- do
- {
- bLoop = false;
- Owned<IDirectoryIterator> pDirIter = createDirectoryIterator( targetPath, targetFileName );
- ForEach(*pDirIter)
- {
- StringBuffer existingFileName;
- pDirIter->getName(existingFileName);
- if (0 != strcmp(targetFileName, existingFileName))
- {
- StringBuffer msg;
- msg.appendf("File '%s' exists as '%s' and may interfere!", targetFileName, existingFileName.str());
- m_errorString.clear().append(msg);
- m_errorCode = (DWORD)-1;
- if (bDeleteIfFound)
- msg.append(" Would you like to delete the file and try again?");
- try
- {
- bool bIgnore = m_pCallback->processException( m_processType.get(), m_compName.get(), m_instanceName.get(), NULL,
- msg.str(), "File name conflict!", this);
- if (bIgnore)
- {
- m_processed = true;
- rc = true;
- }
- else
- {
- if (m_pCallback->getAbortStatus())
- {
- m_abort = true;
- rc = true;
- }
- else
- {
- if (bDeleteIfFound)
- {
- StringBuffer path;
- path.append(targetPath).append(existingFileName);
- Owned<IFile> pDstFile = createIFile(path.str());
- pDstFile->remove();
- }
- m_errorString.clear();
- m_errorCode = 0;
- bLoop = true;
- rc = false;
- }
- }
- }
- catch (IException* e)
- {
- e->Release();
- m_abort = m_pCallback->getAbortStatus();
- }
- catch (...)
- {
- m_abort = m_pCallback->getAbortStatus();
- }
- break; //found conflict so don't iterate directory any more
- }
- }//ForEach
- } while (bLoop);
- if (rc)
- m_targetFileWithWrongCase = true;
- return rc;
- }
- //---------------------------------------------------------------------------
- // compareFile
- //---------------------------------------------------------------------------
- bool compareFile(unsigned mode)
- {
- m_errorCode = 0;
- m_warnings.clear();
- if (m_dummy)
- {
- m_processed = true;
- return true;
- }
- const char* source = getFileSpec(DT_SOURCE);
- const char* target = getFileSpec(DT_TARGET);
- if (m_machineOS != MachineOsW2K)
- {
- if (m_useSsh && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
- {
- char digestStr[33];
- char* pStr = getMD5Checksum(source, digestStr);
- int retcode;
- StringBuffer outbuf, cmdline, errbuf;
- StringBuffer destpath,ip;
- stripNetAddr(target, destpath, ip);
- StringBuffer passphr;
- getKeyPassphrase(passphr);
- cmdline.appendf("plink -i %s -l %s %s %s md5sum %s", m_sshKeyFile.str(), m_sshUser.str(), passphr.str(), ip.str(), destpath.str());
- retcode = pipeSSHCmd(cmdline, outbuf, errbuf);
- m_processed = true;
- if (retcode == 0)
- {
- if (strncmp(outbuf.str(), pStr, 32))
- {
- m_errorCode = (DWORD) -1;
- m_errorString.clear().append("md5sum values do not compare");
- return false;
- }
- m_errorCode = 0;
- return true;
- }
- else
- {
- m_errorCode = (DWORD) -1;
- String err(errbuf.str());
- int index = err.indexOf('\n');
- String* perr = err.substring(0, index > 0? index : err.length());
- m_errorString.clear().appendf("%s", perr->str());
- delete perr;
- return false;
- }
- }
- const bool bDeleteIfFound = (mode & DTC_DEL_WRONG_CASE) != 0;
- if (!m_useSsh && targetFileExistsWithWrongCaseSensitivity(bDeleteIfFound))
- return false;
- }
- Owned<IFile> pSrcFile = createIFile(source);
- Owned<IFile> pDstFile = createIFile(target);
- CDateTime dtSrc;
- CDateTime dtDst;
- offset_t szSrc;
- offset_t szDst;
- if (!getFileAttributes(pSrcFile, dtSrc, szSrc, mode) ||
- !getFileAttributes(pDstFile, dtDst, szDst, mode))
- {
- m_processed = true;
- return false;
- }
- // Compare size
- if (mode & DTC_SIZE)
- {
- if (szSrc != szDst)
- {
- m_errorCode = (DWORD) -1;
- m_errorString.clear().append("File sizes do not compare");
- m_processed = true;
- return false;
- }
- }
- // Compare timestamp
- if (mode & DTC_TIME)
- {
- if (dtSrc.compare(dtDst))
- {
- m_errorCode = (DWORD) -1;
- m_errorString.clear().append("File timestamps do not compare");
- m_processed = true;
- return false;
- }
- }
- // Compare CRC
- if (mode & DTC_CRC)
- {
- unsigned x = getFileCRC(source);
- unsigned y = getFileCRC(target);
- if (x != y)
- {
- m_errorCode = (DWORD) -1;
- m_errorString.clear().append("File CRC values do not compare");
- m_processed = true;
- return false;
- }
- }
- // Everything checked out
- m_processed = true;
- return true;
- }
- bool createDirectoryRecursive(const char* path)
- {
- StringBuffer machine;
- StringBuffer localpath;
- StringBuffer tail;
- StringBuffer ext;
- if (splitUNCFilename(path, &machine, &localpath, &tail, &ext))
- {
- if (machine.length() && localpath.length())
- {
- int len = localpath.length();
- if (len && isPathSepChar(localpath[--len]))
- localpath.setLength(len);
- if (len == 0)
- return true;
- StringBuffer path2;
- path2.append(machine).append(localpath);
- if (createDirectoryRecursive(path2))
- {
- if (m_useSsh && m_machineOS == MachineOsLinux && !m_sshUser.isEmpty() && !m_sshKeyFile.isEmpty())
- {
- int retcode;
- Owned<IPipeProcess> pipe = createPipeProcess();
- StringBuffer outbuf, cmdline, errbuf;
- StringBuffer destpath,ip;
- stripNetAddr(path, destpath, ip);
- StringBuffer passphr;
- getKeyPassphrase(passphr);
- cmdline.appendf("plink -i %s -l %s %s %s %s %s", m_sshKeyFile.str(),
- m_sshUser.str(), passphr.str(), ip.str(), "mkdir -p", destpath.str());
- retcode = pipeSSHCmd(cmdline.str(), outbuf, errbuf);
- if (retcode && retcode != 1)
- {
- m_errorCode = retcode;
- return false;
- }
- else
- return true;
- }
- else
- {
- // Check if directory already exists
- Owned<IFile> pFile = createIFile(path);
- if (pFile->exists())
- {
- if (!pFile->isDirectory())
- throw MakeStringException(-1, "%s exists and is not a directory!", path);
- }
- else
- pFile->createDirectory();//throws
- return true;
- }
- }
- }
- return false;
- }
- else
- return recursiveCreateDirectory(path);
- }
- //---------------------------------------------------------------------------
- // createDirectory
- //---------------------------------------------------------------------------
- bool createDirectory()
- {
- m_processed = true;
- m_errorCode = 0;
-
- const char* target = getFileSpec(DT_TARGET);
- while (true)
- {
- m_errorCode = 0;
- m_warnings.clear();
- m_errorString.clear();
- if (m_dummy)
- break;
- StringBuffer path(target);
- int len = path.length();
- if (len && isPathSepChar(path[--len]))
- path.setLength(len);
- if (createDirectoryRecursive(path.str()))
- break;
- // Prompt to retry on error
- m_errorString.clear().appendf("Cannot create directory %s: ", path.str());
- if (showErrorMsg("Error creating directory"))//ignore or abort?
- break;
- }
- return (m_errorCode == 0);
- }
- void copyRecursive(IFile& src, IFile& dest) //can throw IException
- {
- const char* srcPath = src.queryFilename();
- const char* dstPath = dest.queryFilename();
- while (true)
- {
- try
- {
- if (m_pCallback->getAbortStatus())
- {
- m_errorCode = (DWORD) -1;
- m_errorString.append("Aborted");
- break;
- }
- m_errorCode = 0;
- m_errorString.clear();
- if (m_dummy)
- break;
- if (src.isDirectory())
- {
- if (!dest.exists() && !dest.createDirectory())
- throw MakeStringException(-1, "Failed to create directory %s", dest.queryFilename());
- Owned<IDirectoryIterator> iSrcDirEntry = src.directoryFiles();
- ForEach(*iSrcDirEntry)
- {
- IFile& srcDirEntry = iSrcDirEntry->query();
- const char* dirEntryName = pathTail(srcDirEntry.queryFilename());
- StringBuffer path;
- path.append(dstPath).append(PATHSEPCHAR).append(dirEntryName);
- Owned<IFile> destDirEntry = createIFile(path.str());
- copyRecursive(srcDirEntry, *destDirEntry);
- }
- }
- else
- ::copyFile(&dest, &src);
- break;
- }
- catch(IException* e)
- {
- e->Release();
- }
- catch(...)
- {
- }
- m_errorString.appendf("Cannot copy %s to %s: ", srcPath, dstPath);
- // Prompt to retry on error
- if (showErrorMsg("Error copying file/directory"))//ignore or abort?
- break;
- }//while
- }
- //---------------------------------------------------------------------------
- // copyDirectory
- //---------------------------------------------------------------------------
- bool copyDirectory()
- {
- m_processed = true;
- m_warnings.clear();
- Owned<IFile> pSrcFile = createIFile(getFileSpec(DT_SOURCE));
- Owned<IFile> pDstFile = createIFile(getFileSpec(DT_TARGET));
- copyRecursive(*pSrcFile, *pDstFile);
-
- return (m_errorCode == 0);
- }
- //---------------------------------------------------------------------------
- // createProcess
- //---------------------------------------------------------------------------
- bool createProcess(bool wait, const char* user, const char* pwd)
- {
- m_processed = true;
- m_errorCode = 0;
- #ifdef _WINDOWS
- const char* target = getFileSpec(DT_TARGET);
- std::string cmdLine = target;
- std::string displayLine;
- // Make sure target file exists
- char cmdPath[_MAX_PATH];
- strcpy(cmdPath, target);
- char* pchSpace = strchr(cmdPath, ' ');// find command line parameters
- if (pchSpace)
- *pchSpace = '\0';// remove command line parameters
- if (m_machineOS == MachineOsLinux)
- {
- const char* extension = findFileExtension(cmdPath);
- if (extension && (!stricmp(extension, ".bat") || !stricmp(extension, ".exe")))
- cmdPath[extension-cmdPath] = '\0';//rename programs like starup.bat to startup
- }
- Owned<IFile> pFile = createIFile(cmdPath);
- while (!pFile->exists())
- {
- // Prompt to retry
- m_errorCode = (DWORD) -1; //don't format m_errorString based on last error
- m_warnings.clear();
- m_errorString.clear().appendf("File not found: '%s'", cmdPath);
- if (showErrorMsg("Error creating process"))//ignore or abort?
- return false;
- }
- char modulePath[_MAX_PATH+1] = "";
- //find out where configenv is running from and remove file name 'Configenv.exe'
- #ifdef _WIN32
- if (GetModuleFileName(NULL, modulePath, _MAX_PATH))
- {
- char* pch = strrchr(modulePath, '\\');
- if (pch)
- *++pch = '\0';
- }
- #endif
- // Determine if process is remote or local
- if (strlen(cmdPath)>2 && cmdPath[0]=='\\' && cmdPath[1]=='\\')
- {
- // Parse target into computer, dir, and cmd
- char computer[_MAX_PATH];
- strcpy(computer, cmdPath+2);
- char* pchSlash = strchr(computer, '\\');
- assert(pchSlash);
- char* dir = pchSlash+1;
- char* cmd = (char*)pathTail(dir);
- assertex(computer && (computer < dir) && (dir < cmd));
- *(dir-1) = '\0';
- *(cmd-1) = '\0';
- std::string sUser = user;
- StringBuffer sPswd(pwd);
- if (m_machineOS != MachineOsLinux)
- {
- // Replace '$' with ':' in dir part
- char* x = dir;
- while (x = strchr(x, '$')) *x++ = ':';
- }
-
- // Use psexec as default remote control program
- #ifdef _WIN32
- if (m_machineOS != MachineOsLinux)
- {
- if (!checkFileExists(".\\psexec.exe"))
- throw MakeStringException(-1, "Configenv cannot find psexec.exe to execute the remote program!");
- cmdLine = modulePath;
- cmdLine.append("psexec.exe \\\\%computer -u %user -p %pwd -i %dir\\%cmd %dir");
- }
- else
- {
- if (!checkFileExists(".\\plink.exe"))
- throw MakeStringException(-1, "Configenv cannot find plink.exe to execute the remote program!");
- sUser = pathTail(user); //if user name is domain\user1 then just get user1
- //replace all '\\' by '/'
- char* x = dir;
- while (x = strchr(x, '\\'))
- *x++ = '/';
- /* note that if we use plink (cmd line ssh client) for the first time with a computer,
- it generates the following message:
- The server's host key is not cached in the registry. You have no guarantee that the
- server is the computer you think it is. The server's key fingerprint is:
- 1024 aa:bb:cc:dd:ee:ff:gg:hh:ii:jj:kk:ll:mm:nn:oo:pp
- If you trust this host, enter "y" to add the key to
- PuTTY's cache and carry on connecting. If you want to carry on connecting just once,
- without adding the key to the cache, enter "n".If you do not trust this host, press
- Return to abandon the connection.
- To get around this, we pipe "n" to plink without using its -batch parameter (since
- that simply aborts the connection attempt). We need help from cmd.exe to do this though...
- */
- /* fix for bug# 6590: Error when trying to start/stop components from configenv
- if the command being invoked is using cmd.exe and if the configenv has been invoked
- with a UNC path (like \\machine\dir1\...\configenv.exe) then this would fail since
- cmd.exe does not like to invoke commands with UNC names.
- So we cannot use cmd.exe with UNC paths. Redirecting input file with 'n' does not
- work either. So we cannot inhibit the prompt as shown above in this case and the
- user must enter 'y' or 'n' when asked to cache the key. However, hitting enter
- aborts the connection with return code 0 with the result that the user is notified
- that command succeeded since plink returns success even though it aborted the connection.
- Find out how configenv was invoked and use the following commands respectively:
- if UNC : cmd /c \"echo y | .\\plink.exe -ssh -l %user -pw %pwd %computer /%dir/%cmd /%dir\"
- otherwise: .\\plink.exe -ssh -l %user -pw %pwd %computer /%dir/%cmd /%dir
- */
- cmdLine.erase();
- cmdLine = "cmd /c \"echo y | \"";
- cmdLine.append(modulePath).append("plink\" -ssh -l %user -pw %pwd %computer sudo bash -c '/%dir/%cmd /%dir'\"");
- StringBuffer sshUserid;
- m_pCallback->getSshAccountInfo(sshUserid, sPswd);
- sUser = sshUserid.str();
- }//linux
- #else
- //TODO
- #endif
- // Replace known tokens except the password
- const char* const tokens[] = { "%component", "%computer", "%user", "%dir", "%cmd" };
- const char* values[] = { m_compName, computer, sUser.c_str(), dir, cmd };
- const int count = sizeof(tokens) / sizeof(char*);
- std::string::size_type pos;
- for (int i = 0; i < count; i++)
- {
- while ((pos = cmdLine.find(tokens[i])) != std::string::npos)
- cmdLine.replace(pos, strlen(tokens[i]), values[i] ? values[i] : "");
- }
- //replace %pwd by pwd in cmdLine but by <password> in displayLine
- displayLine = cmdLine;
- if ((pos = cmdLine.find("%pwd")) != std::string::npos)
- cmdLine.replace(pos, strlen("%pwd"), sPswd.str() ? sPswd.str() : "" );
- if ((pos = displayLine.find("%pwd")) != std::string::npos)
- displayLine.replace(pos, strlen("%pwd"), "<password>");
- }
- bool rc = false;
- const bool bCD = modulePath[0] == '\\' && modulePath[1] == '\\';
- try
- {
- if (bCD)
- {
- char winDir[_MAX_PATH+1];
- GetWindowsDirectory(winDir, _MAX_PATH);
- _chdir(winDir);
- }
- rc = createProcess(cmdLine.c_str(), displayLine.c_str(), wait, m_machineOS == MachineOsLinux);
- }
- catch (IException* e)
- {
- if (bCD)
- _chdir(modulePath);
- throw e;
- }
- catch (...)
- {
- if (bCD)
- _chdir(modulePath);
- throw MakeStringException(-1, "Invalid exception!");
- }
- if (bCD)
- _chdir(modulePath);
- return rc;
- #else
- return true;
- #endif
- }
- //---------------------------------------------------------------------------
- // createProcess
- //---------------------------------------------------------------------------
- bool createProcess(const char* cmdLine, const char* displayLine, bool wait, bool captureOutput)
- {
- #ifdef _WINDOWS
- char tempfile[_MAX_PATH];
- const char* processName = m_processName ? m_processName : m_compName;
- getTempPath(tempfile, sizeof(tempfile), processName);
- strcat(tempfile, "rexec");
- // Make sure file name is unique - at least during this session
- Owned<IEnvDeploymentEngine> pEnvDepEngine = m_pCallback->getEnvDeploymentEngine();
- sprintf(&tempfile[strlen(tempfile)], "%d.out", pEnvDepEngine->incrementTempFileCount());
- while (true)
- {
- m_errorCode = 0;
- m_warnings.clear();
- m_errorString.clear();
- // Launch the process
- if (m_dummy)
- break;
- DeleteFile( tempfile );
- if (invoke_program(cmdLine, m_errorCode, wait, captureOutput ? tempfile : NULL))
- {
- if (m_errorCode)
- {
- m_errorString.appendf("Process '%s' returned exit code of %d: ", displayLine, m_errorCode);
- m_errorCode = -1; //so showErrorMsg() does not reformat m_errorString
- }
- StringBuffer& outputStr = m_errorCode ? m_errorString : m_warnings;
- Owned<IFile> pFile = createIFile( tempfile );
- if (pFile->exists())
- {
- Owned<IFileIO> pFileIO = pFile->open(IFOread);
- offset_t sz = pFile->size();
- if (sz)
- {
- if (m_errorCode)
- m_errorString.append("\n\n");
- unsigned int len = outputStr.length();
- outputStr.ensureCapacity(len+sz);
- if (sz == pFileIO->read(0, sz, (void*)(outputStr.str() + len)))
- outputStr.setLength(sz+len);
- }
- }
- DeleteFile( tempfile );
- }
- else
- m_errorString.appendf("Cannot create process '%s': ", displayLine);
- if (!m_errorString.length() || showErrorMsg("Error executing process"))//ignore or abort?
- break;
- }
- return (m_errorCode == 0);
- #else
- return true;
- #endif
- }
- virtual bool execSSHCmd(const char* ip, const char* cmd, StringBuffer& output, StringBuffer& errmsg)
- {
- if (!ip || !*ip || !cmd || !*cmd || m_sshUser.isEmpty() || m_sshKeyFile.isEmpty())
- {
- errmsg.append("Invalid SSH params");
- return false;
- }
- int retcode;
- Owned<IPipeProcess> pipe = createPipeProcess();
- StringBuffer cmdline;
- StringBuffer passphr;
- getKeyPassphrase(passphr);
- cmdline.appendf("plink -i %s -l %s %s %s %s", m_sshKeyFile.str(), m_sshUser.str(), passphr.str(), ip, cmd);
- retcode = pipeSSHCmd(cmdline.str(), output, errmsg);
- m_processed = true;
- if (retcode && retcode != 1)
- {
- m_errorCode = retcode;
- String err(errmsg.str());
- int index = err.indexOf('\n');
- String* perr = err.substring(0, index > 0? index : err.length());
- m_errorString.clear().appendf("%s", perr->str());
- delete perr;
- return false;
- }
- else
- return true;
- }
- int pipeSSHCmd(const char* cmdline, StringBuffer& outbuf, StringBuffer& errbuf)
- {
- int retcode = 1;
- Owned<IPipeProcess> pipe = createPipeProcess();
- StringBuffer workdir("");
- if (pipe->run("ConfigEnv", cmdline, workdir, FALSE, true, true))
- {
- byte buf[4096*2];
- size32_t read = pipe->read(sizeof(buf), buf);
- outbuf.append(read,(const char *)buf);
- if (strstr(outbuf.str(), "Passphrase for key "))
- {
- retcode = 5;
- errbuf.clear().append("Invalid or missing key passphrase. ");
- pipe->abort();
- return retcode;
- }
- retcode = pipe->wait();
- read = pipe->readError(sizeof(buf), buf);
- errbuf.append(read,(const char *)buf);
- if (strstr(errbuf.str(), "Wrong passphrase"))
- {
- retcode = -1;
- errbuf.clear().append("Invalid or missing key passphrase");
- }
- else if (strstr(errbuf.str(), "The server's host key is not cached in the registry."))
- {
- Owned<IPipeProcess> pipe1 = createPipeProcess();
- StringBuffer cmd;
- cmd.appendf("cmd /c \" echo y | %s \"", cmdline);
- if (pipe1->run("ConfigEnv",cmd.str(),workdir,FALSE,true,true))
- {
- size32_t read = pipe1->read(sizeof(buf), buf);
- outbuf.append(read,(const char *)buf);
- retcode = pipe1->wait();
- read = pipe->readError(sizeof(buf), buf);
- if (read)
- {
- errbuf.append(" ").append(read,(const char *)buf);
- retcode = 7;
- }
- else
- errbuf.clear();
- }
- }
- else if (errbuf.length())
- {
- const char* psz = strstr(errbuf.str(), "Authenticating with public key \"");
- if (!psz || psz != errbuf.str())
- retcode = 2;
- else
- {
- const char* psz1 = psz + strlen("Authenticating with public key \"");
- const char* psz2 = strstr(psz1, "\"\r\n");
- psz1 = psz2 + strlen("\"\r\n");
- if (strlen(psz1))
- retcode = 2;
- }
- }
- }
- return retcode;
- }
- void DisconnectNetworkConnection(const char* remoteNameOrIp)
- {
- #ifdef _WINDOWS
- IpAddress ip;
- if (!ip.ipset(remoteNameOrIp))
- throw MakeStringException(-1, "Cannot resolve %s", remoteNameOrIp);
- StringBuffer remoteIP;
- ip.getIpText(remoteIP);
- HANDLE hEnum;
- DWORD dwResult = WNetOpenEnum( RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL, &hEnum );
- if (dwResult != NO_ERROR)
- throw MakeStringException(-1, "Cannot enumerate existing network connections!" );
- else
- {
- do
- {
- DWORD cbBuffer = 16384;
- LPNETRESOURCE lpnrDrv = (LPNETRESOURCE) GlobalAlloc( GPTR, cbBuffer );
- DWORD cEntries = 0xFFFFFFFF;
- dwResult = WNetEnumResource( hEnum, &cEntries, lpnrDrv, &cbBuffer);
- if (dwResult == NO_ERROR)
- {
- for(DWORD i = 0; i < cEntries; i++ )
- {
- char nameOrIp[MAX_PATH];
- strcpy(nameOrIp, lpnrDrv[i].lpRemoteName+2);
- char* pch = strchr(nameOrIp, '\\');
- if (pch)
- *pch = '\0';
- if (!ip.ipset(nameOrIp))
- {
- GlobalFree( (HGLOBAL) lpnrDrv );
- WNetCloseEnum(hEnum);
- throw MakeStringException(-1, "Cannot resolve host %s", nameOrIp);
- }
- StringBuffer ipAddr;
- ip.getIpText(ipAddr);
- if (!stricmp(remoteIP.str(), ipAddr.str()))
- {
- //we are already connected to this network resource with another user id
- //so disconnect from it...and attempt to reconnect
- //
- m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Disconnecting from %s...", lpnrDrv[i].lpRemoteName);
- m_pCallback->printStatus(this);
- dwResult = ::WNetCancelConnection2(lpnrDrv[i].lpRemoteName, CONNECT_UPDATE_PROFILE, FALSE);
- if (dwResult != NO_ERROR)
- {
- m_errorCode = dwResult;
- m_errorString.appendf("Error disconnecting from %s: ", lpnrDrv[i].lpRemoteName);
- formatSystemError(m_errorString, m_errorCode);
- throw MakeStringException(-1, m_errorString.str());
- }
- m_pCallback->printStatus(this);
- m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL);
- dwResult = ERROR_NO_MORE_ITEMS; //to break out of outer loop
- break;//only disconnect from the first one since this method will be invoked again if more exist
- }
- }//for
- }
- else
- if( dwResult != ERROR_NO_MORE_ITEMS )
- {
- GlobalFree( (HGLOBAL) lpnrDrv );
- WNetCloseEnum(hEnum);
- throw MakeStringException(-1, "Cannot complete enumeration for connections to \\%s", remoteNameOrIp);
- }
- GlobalFree( (HGLOBAL) lpnrDrv );
- } while( dwResult != ERROR_NO_MORE_ITEMS );
- WNetCloseEnum(hEnum);
- }
- #endif
- }
- //---------------------------------------------------------------------------
- // connectTarget
- //---------------------------------------------------------------------------
- bool connectTarget(const char* user, const char* pwd, bool bInteractiveMode/*=true*/)
- {
- m_processed = true;
- #if defined( _WIN32) && !defined (_DEBUG)
- if (getenv("LN_CFG_SkipConnectTarget") != NULL)
- {
- m_errorCode = 0;
- m_errorString.clear();
- m_warnings.clear();
- return (m_errorCode == 0);
- }
- NETRESOURCE netRes;
- memset(&netRes, 0, sizeof(netRes));
- netRes.dwType = RESOURCETYPE_DISK;
- StringBuffer uncPath(getFileSpec(DT_TARGET));
- unsigned int uncPathLen = uncPath.length();
- if (uncPathLen-- && isPathSepChar(uncPath.charAt(uncPathLen)))//remove trailing \ or /
- uncPath.setLength(uncPathLen);
- netRes.lpRemoteName = (char*) uncPath.str();
- while (true)
- {
- m_errorCode = 0;
- m_errorString.clear();
- m_warnings.clear();
- if (m_dummy) return true;
- HWND hCallback = (HWND) m_pCallback->getWindowHandle();
- DWORD errCode = ::WNetAddConnection3(hCallback, &netRes, pwd, user, bInteractiveMode ? CONNECT_INTERACTIVE : 0);
- if (errCode != NO_ERROR)
- {
- if (errCode == ERROR_SESSION_CREDENTIAL_CONFLICT)
- {
- char hostNameOrIp[MAX_PATH];
- strcpy(hostNameOrIp, netRes.lpRemoteName+2);
- char* pch = strchr(hostNameOrIp, '\\');
- if (pch)
- *pch = '\0';
- try {
- DisconnectNetworkConnection(hostNameOrIp);
- }
- catch (IException* e) {
- e->Release();
- m_errorCode = (DWORD) -1;
- }
- catch (...) {
- m_errorCode = (DWORD) -1;
- }
- if (m_errorCode)
- {
- StringBuffer caption;
- caption.appendf("Error connecting to %s: ", netRes.lpRemoteName);
- m_errorString.clear().append("The target machine is already connected as a different user name.\n"
- "Please disconnect any mapped network drive or active sessions.");
- if (showErrorMsg(caption.str()))//ignore or abort?
- break;
- }
- if (m_pCallback->getAbortStatus())
- {
- m_errorCode = (DWORD) -1;
- m_errorString.append("Aborted");
- break;
- }
- continue;
- }
- m_errorCode = errCode;
- m_errorString.clear().appendf("Error connecting to %s: ", netRes.lpRemoteName);
- if (showErrorMsg("Error connecting to target"))//ignore or abort?
- break;
- }
- else
- break;
- }
- #else
- m_errorCode = 0;
- m_errorString.clear();
- m_warnings.clear();
- #endif
- return (m_errorCode == 0);
- }
- //---------------------------------------------------------------------------
- // disconnectTarget
- //---------------------------------------------------------------------------
- bool disconnectTarget()
- {
- m_processed = true;
- m_errorCode = 0;
- m_errorString.clear();
- m_warnings.clear();
- #ifdef _WIN32
- if (m_dummy) return true;
- DWORD errCode = ::WNetCancelConnection2(getFileSpec(DT_TARGET), CONNECT_UPDATE_PROFILE, FALSE);
- if (errCode != NO_ERROR)
- {
- m_errorCode = errCode;
- m_errorString.appendf("Error disconnecting from %s: ", getFileSpec(DT_TARGET));
- formatSystemError(m_errorString, m_errorCode);
- //don't display error since deployment of one component (like esp) may involve
- //deploying multiple others (like esp service modules) and we would get disconnection
- //failures as a result in any case.
- }
- #endif
- return (m_errorCode == 0);
- }
- //returns true if the caller needs to ignore or abort error; false to retry
- //
- bool showErrorMsg(const char* szTitle)
- {
- if (m_machineOS != MachineOsLinux || (m_machineOS == MachineOsLinux && m_sshUser.isEmpty() && m_sshKeyFile.isEmpty()))
- {
- if (m_errorCode == 0)
- m_errorCode = getLastError();
- if (m_errorCode != (DWORD) -1)
- formatSystemError(m_errorString, m_errorCode);
- }
- bool rc = true;//ignore
- try
- {
- rc = m_pCallback->processException( m_processType.get(), m_compName.get(), m_instanceName.get(), NULL,
- m_errorString, szTitle, this);
- }
- catch (IException* e)
- {
- e->Release();
- m_abort = m_pCallback->getAbortStatus();
- }
- catch (...)
- {
- m_abort = m_pCallback->getAbortStatus();
- }
- return rc;
- }
- virtual void setErrorCode(DWORD code) { m_errorCode = code; }
- virtual void setErrorString(const char* msg) { m_errorString = msg; }
- virtual void setWarnings(const char* warnings) { m_warnings = warnings; }
- virtual void setProcessed(bool bProcessed = true){ m_processed = bProcessed; }
- static bool getRetryStatus() { return s_retry; }
- static void setRetryStatus(bool status) { s_retry = status; }
- virtual IDeploymentCallback& getCallback() const { return *m_pCallback; }
- private:
- char* getMD5Checksum(StringBuffer filename, char* digestStr)
- {
- if (filename.length() < 1)
- return NULL;
- if (!checkFileExists(filename.str()))
- return NULL;
- OwnedIFile ifile = createIFile(filename);
- if (!ifile)
- return NULL;
- OwnedIFileIO ifileio = ifile->open(IFOread);
- if (!ifileio)
- return NULL;
- size32_t len = (size32_t) ifileio->size();
- if (len < 1)
- return NULL;
- char * buff = new char[1+len];
- size32_t len0 = ifileio->read(0, len, buff);
- buff[len0] = 0;
- md5_state_t md5;
- md5_byte_t digest[16];
- md5_init(&md5);
- md5_append(&md5, (const md5_byte_t *)buff, len0);
- md5_finish(&md5, digest);
- for (int i = 0; i < 16; i++)
- sprintf(&digestStr[i*2],"%02x", digest[i]);
- delete[] buff;
- return digestStr;
- }
- void getKeyPassphrase(StringBuffer& passphr)
- {
- passphr.clear().append("-pw ");
- if (m_sshKeyPassphrase.isEmpty())
- passphr.clear();
- else
- {
- StringBuffer sb;
- decrypt(sb, m_sshKeyPassphrase.str());
- passphr.append(sb.str());
- }
- }
- private:
- Owned<IDeploymentCallback> m_pCallback;
- StringAttr m_caption;
- StringAttr m_compName;
- StringAttr m_instanceName;
- StringAttr m_fileSpec[2];
- StringAttr m_processName;
- StringAttr m_processType;
- StringAttr m_sshUser;
- StringAttr m_sshKeyFile;
- StringAttr m_sshKeyPassphrase;
- int m_func;
- bool m_processed;
- DWORD m_errorCode;
- StringBuffer m_errorString;
- StringBuffer m_warnings;
- bool m_targetFileWithWrongCase;
- bool m_abort;
- bool m_msgBoxOwner;//global flag set (used for multithreaded copying only)
- bool m_dummy;
- bool m_updateProgress;
- bool m_useSsh;
- EnvMachineOS m_machineOS;
- unsigned m_flags;
- static Monitor s_monitor;
- static bool s_retry;
- static bool s_msgBoxActive;
- };
- /*static*/ Monitor CDeployTask::s_monitor;
- /*static*/ bool CDeployTask::s_retry = false;
- /*static*/ bool CDeployTask::s_msgBoxActive = false;
- //the following class implements a thread that asynchronously
- //copies a given file. The current implementation of IDeployTask
- //does not seemlessly lend itself to polymorphism since each task
- //type has its own characteristic parameters and the caller must
- //specifically invoke different methods like copyFile based on
- //task type. Ideally, there needs to be only one worker method.
- //
- class CDeployTaskThread : public CInterface,
- implements IDeployTaskThread
- {
- public:
- IMPLEMENT_IINTERFACE;
- CDeployTaskThread()
- {
- }
- virtual ~CDeployTaskThread()
- {
- }
- void init(void *startInfo)
- {
- m_pTask.set((IDeployTask*)startInfo);
- }
- void main()
- {
- m_pTask->copyFile( m_pTask->getFlags() );
- static Mutex m;
- m.lock();
- try
- {
- m_pTask->getCallback().printStatus(m_pTask);
- }
- catch(IException* e)
- {
- e->Release();
- }
- m.unlock();
- if (m_pTask && m_pTask->getAbort())
- {
- m_pTask->getCallback().printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Aborting, please wait...");
- throw MakeStringException(0, "Abort");
- }
- }
- bool canReuse()
- {
- return true;
- }
- bool stop()
- {
- return true;
- }
-
- virtual IDeployTask* getTask () const { return m_pTask; }
- virtual void setTask (IDeployTask* pTask) { m_pTask.set(pTask); }
- virtual bool getAbort() const { return s_abort; }
- virtual void setAbort(bool bAbort) { s_abort = bAbort; }
-
- private:
- Owned<IDeployTask> m_pTask;
- static bool s_abort;
- };
- class CDeployTaskThreadFactory : public CInterface, public IThreadFactory
- {
- public:
- IMPLEMENT_IINTERFACE;
- IPooledThread *createNew()
- {
- return new CDeployTaskThread();
- }
- };
- bool CDeployTaskThread::s_abort = false;
- //---------------------------------------------------------------------------
- // Factory functions
- //---------------------------------------------------------------------------
- IDeployTask* createDeployTask(IDeploymentCallback& callback, const char* caption,
- const char* processType, const char* comp,
- const char* instance, const char* source,
- const char* target, const char* sshUser, const char* sshKeyFile,
- const char* sshKeyPassphrase, bool useSsh, EnvMachineOS os/* = MachineOsUnknown*/,
- const char* processName/*=NULL*/)
- {
- return new CDeployTask(callback, caption, processType, comp, instance, source, target, sshUser, sshKeyFile,
- sshKeyPassphrase, useSsh, os, processName);
- }
- IThreadFactory* createDeployTaskThreadFactory()
- {
- return new CDeployTaskThreadFactory();
- }
- void initializeMultiThreadedCopying()
- {
- CDeployTask::setRetryStatus(false);
- }
|