/indra/viewer_components/updater/llupdaterservice.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 621 lines · 477 code · 101 blank · 43 comment · 43 complexity · e63910457a92d22c6e81be8c51b7290b MD5 · raw file

  1. /**
  2. * @file llupdaterservice.cpp
  3. *
  4. * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  5. * Second Life Viewer Source Code
  6. * Copyright (C) 2010, Linden Research, Inc.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation;
  11. * version 2.1 of the License only.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. *
  22. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  23. * $/LicenseInfo$
  24. */
  25. #include "linden_common.h"
  26. #include "llupdaterservice.h"
  27. #include "llupdatedownloader.h"
  28. #include "llevents.h"
  29. #include "lltimer.h"
  30. #include "llupdatechecker.h"
  31. #include "llupdateinstaller.h"
  32. #include "llversionviewer.h"
  33. #include <boost/scoped_ptr.hpp>
  34. #include <boost/weak_ptr.hpp>
  35. #include "lldir.h"
  36. #include "llsdserialize.h"
  37. #include "llfile.h"
  38. #if LL_WINDOWS
  39. #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  40. #endif
  41. namespace
  42. {
  43. boost::weak_ptr<LLUpdaterServiceImpl> gUpdater;
  44. const std::string UPDATE_MARKER_FILENAME("SecondLifeUpdateReady.xml");
  45. std::string update_marker_path()
  46. {
  47. return gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
  48. UPDATE_MARKER_FILENAME);
  49. }
  50. std::string install_script_path(void)
  51. {
  52. #ifdef LL_WINDOWS
  53. std::string scriptFile = "update_install.bat";
  54. #else
  55. std::string scriptFile = "update_install";
  56. #endif
  57. return gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, scriptFile);
  58. }
  59. LLInstallScriptMode install_script_mode(void)
  60. {
  61. #ifdef LL_WINDOWS
  62. return LL_COPY_INSTALL_SCRIPT_TO_TEMP;
  63. #else
  64. return LL_RUN_INSTALL_SCRIPT_IN_PLACE;
  65. #endif
  66. };
  67. }
  68. class LLUpdaterServiceImpl :
  69. public LLUpdateChecker::Client,
  70. public LLUpdateDownloader::Client
  71. {
  72. static const std::string sListenerName;
  73. std::string mProtocolVersion;
  74. std::string mUrl;
  75. std::string mPath;
  76. std::string mChannel;
  77. std::string mVersion;
  78. unsigned int mCheckPeriod;
  79. bool mIsChecking;
  80. bool mIsDownloading;
  81. LLUpdateChecker mUpdateChecker;
  82. LLUpdateDownloader mUpdateDownloader;
  83. LLTimer mTimer;
  84. LLUpdaterService::app_exit_callback_t mAppExitCallback;
  85. LLUpdaterService::eUpdaterState mState;
  86. LOG_CLASS(LLUpdaterServiceImpl);
  87. public:
  88. LLUpdaterServiceImpl();
  89. virtual ~LLUpdaterServiceImpl();
  90. void initialize(const std::string& protocol_version,
  91. const std::string& url,
  92. const std::string& path,
  93. const std::string& channel,
  94. const std::string& version);
  95. void setCheckPeriod(unsigned int seconds);
  96. void setBandwidthLimit(U64 bytesPerSecond);
  97. void startChecking(bool install_if_ready);
  98. void stopChecking();
  99. bool isChecking();
  100. LLUpdaterService::eUpdaterState getState();
  101. void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;}
  102. std::string updatedVersion(void);
  103. bool checkForInstall(bool launchInstaller); // Test if a local install is ready.
  104. bool checkForResume(); // Test for resumeable d/l.
  105. // LLUpdateChecker::Client:
  106. virtual void error(std::string const & message);
  107. virtual void optionalUpdate(std::string const & newVersion,
  108. LLURI const & uri,
  109. std::string const & hash);
  110. virtual void requiredUpdate(std::string const & newVersion,
  111. LLURI const & uri,
  112. std::string const & hash);
  113. virtual void upToDate(void);
  114. // LLUpdateDownloader::Client
  115. void downloadComplete(LLSD const & data);
  116. void downloadError(std::string const & message);
  117. bool onMainLoop(LLSD const & event);
  118. private:
  119. std::string mNewVersion;
  120. void restartTimer(unsigned int seconds);
  121. void setState(LLUpdaterService::eUpdaterState state);
  122. void stopTimer();
  123. };
  124. const std::string LLUpdaterServiceImpl::sListenerName = "LLUpdaterServiceImpl";
  125. LLUpdaterServiceImpl::LLUpdaterServiceImpl() :
  126. mIsChecking(false),
  127. mIsDownloading(false),
  128. mCheckPeriod(0),
  129. mUpdateChecker(*this),
  130. mUpdateDownloader(*this),
  131. mState(LLUpdaterService::INITIAL)
  132. {
  133. }
  134. LLUpdaterServiceImpl::~LLUpdaterServiceImpl()
  135. {
  136. LL_INFOS("UpdaterService") << "shutting down updater service" << LL_ENDL;
  137. LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName);
  138. }
  139. void LLUpdaterServiceImpl::initialize(const std::string& protocol_version,
  140. const std::string& url,
  141. const std::string& path,
  142. const std::string& channel,
  143. const std::string& version)
  144. {
  145. if(mIsChecking || mIsDownloading)
  146. {
  147. throw LLUpdaterService::UsageError("LLUpdaterService::initialize call "
  148. "while updater is running.");
  149. }
  150. mProtocolVersion = protocol_version;
  151. mUrl = url;
  152. mPath = path;
  153. mChannel = channel;
  154. mVersion = version;
  155. }
  156. void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds)
  157. {
  158. mCheckPeriod = seconds;
  159. }
  160. void LLUpdaterServiceImpl::setBandwidthLimit(U64 bytesPerSecond)
  161. {
  162. mUpdateDownloader.setBandwidthLimit(bytesPerSecond);
  163. }
  164. void LLUpdaterServiceImpl::startChecking(bool install_if_ready)
  165. {
  166. if(mUrl.empty() || mChannel.empty() || mVersion.empty())
  167. {
  168. throw LLUpdaterService::UsageError("Set params before call to "
  169. "LLUpdaterService::startCheck().");
  170. }
  171. mIsChecking = true;
  172. // Check to see if an install is ready.
  173. bool has_install = checkForInstall(install_if_ready);
  174. if(!has_install)
  175. {
  176. checkForResume(); // will set mIsDownloading to true if resuming
  177. if(!mIsDownloading)
  178. {
  179. setState(LLUpdaterService::CHECKING_FOR_UPDATE);
  180. // Checking can only occur during the mainloop.
  181. // reset the timer to 0 so that the next mainloop event
  182. // triggers a check;
  183. restartTimer(0);
  184. }
  185. else
  186. {
  187. setState(LLUpdaterService::DOWNLOADING);
  188. }
  189. }
  190. }
  191. void LLUpdaterServiceImpl::stopChecking()
  192. {
  193. if(mIsChecking)
  194. {
  195. mIsChecking = false;
  196. stopTimer();
  197. }
  198. if(mIsDownloading)
  199. {
  200. mUpdateDownloader.cancel();
  201. mIsDownloading = false;
  202. }
  203. setState(LLUpdaterService::TERMINAL);
  204. }
  205. bool LLUpdaterServiceImpl::isChecking()
  206. {
  207. return mIsChecking;
  208. }
  209. LLUpdaterService::eUpdaterState LLUpdaterServiceImpl::getState()
  210. {
  211. return mState;
  212. }
  213. std::string LLUpdaterServiceImpl::updatedVersion(void)
  214. {
  215. return mNewVersion;
  216. }
  217. bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller)
  218. {
  219. bool foundInstall = false; // return true if install is found.
  220. llifstream update_marker(update_marker_path(),
  221. std::ios::in | std::ios::binary);
  222. if(update_marker.is_open())
  223. {
  224. // Found an update info - now lets see if its valid.
  225. LLSD update_info;
  226. LLSDSerialize::fromXMLDocument(update_info, update_marker);
  227. update_marker.close();
  228. // Get the path to the installer file.
  229. LLSD path = update_info.get("path");
  230. if(update_info["current_version"].asString() != ll_get_version())
  231. {
  232. // This viewer is not the same version as the one that downloaded
  233. // the update. Do not install this update.
  234. if(!path.asString().empty())
  235. {
  236. llinfos << "ignoring update dowloaded by different client version" << llendl;
  237. LLFile::remove(path.asString());
  238. LLFile::remove(update_marker_path());
  239. }
  240. else
  241. {
  242. ; // Nothing to clean up.
  243. }
  244. foundInstall = false;
  245. }
  246. else if(path.isDefined() && !path.asString().empty())
  247. {
  248. if(launchInstaller)
  249. {
  250. setState(LLUpdaterService::INSTALLING);
  251. LLFile::remove(update_marker_path());
  252. int result = ll_install_update(install_script_path(),
  253. update_info["path"].asString(),
  254. update_info["required"].asBoolean(),
  255. install_script_mode());
  256. if((result == 0) && mAppExitCallback)
  257. {
  258. mAppExitCallback();
  259. } else if(result != 0) {
  260. llwarns << "failed to run update install script" << LL_ENDL;
  261. } else {
  262. ; // No op.
  263. }
  264. }
  265. foundInstall = true;
  266. }
  267. }
  268. return foundInstall;
  269. }
  270. bool LLUpdaterServiceImpl::checkForResume()
  271. {
  272. bool result = false;
  273. std::string download_marker_path = mUpdateDownloader.downloadMarkerPath();
  274. if(LLFile::isfile(download_marker_path))
  275. {
  276. llifstream download_marker_stream(download_marker_path,
  277. std::ios::in | std::ios::binary);
  278. if(download_marker_stream.is_open())
  279. {
  280. LLSD download_info;
  281. LLSDSerialize::fromXMLDocument(download_info, download_marker_stream);
  282. download_marker_stream.close();
  283. if(download_info["current_version"].asString() == ll_get_version())
  284. {
  285. mIsDownloading = true;
  286. mNewVersion = download_info["update_version"].asString();
  287. mUpdateDownloader.resume();
  288. result = true;
  289. }
  290. else
  291. {
  292. // The viewer that started this download is not the same as this viewer; ignore.
  293. llinfos << "ignoring partial download from different viewer version" << llendl;
  294. std::string path = download_info["path"].asString();
  295. if(!path.empty()) LLFile::remove(path);
  296. LLFile::remove(download_marker_path);
  297. }
  298. }
  299. }
  300. return result;
  301. }
  302. void LLUpdaterServiceImpl::error(std::string const & message)
  303. {
  304. if(mIsChecking)
  305. {
  306. setState(LLUpdaterService::TEMPORARY_ERROR);
  307. restartTimer(mCheckPeriod);
  308. }
  309. }
  310. void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion,
  311. LLURI const & uri,
  312. std::string const & hash)
  313. {
  314. stopTimer();
  315. mNewVersion = newVersion;
  316. mIsDownloading = true;
  317. setState(LLUpdaterService::DOWNLOADING);
  318. mUpdateDownloader.download(uri, hash, newVersion, false);
  319. }
  320. void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion,
  321. LLURI const & uri,
  322. std::string const & hash)
  323. {
  324. stopTimer();
  325. mNewVersion = newVersion;
  326. mIsDownloading = true;
  327. setState(LLUpdaterService::DOWNLOADING);
  328. mUpdateDownloader.download(uri, hash, newVersion, true);
  329. }
  330. void LLUpdaterServiceImpl::upToDate(void)
  331. {
  332. if(mIsChecking)
  333. {
  334. restartTimer(mCheckPeriod);
  335. }
  336. setState(LLUpdaterService::UP_TO_DATE);
  337. }
  338. void LLUpdaterServiceImpl::downloadComplete(LLSD const & data)
  339. {
  340. mIsDownloading = false;
  341. // Save out the download data to the SecondLifeUpdateReady
  342. // marker file.
  343. llofstream update_marker(update_marker_path());
  344. LLSDSerialize::toPrettyXML(data, update_marker);
  345. LLSD event;
  346. event["pump"] = LLUpdaterService::pumpName();
  347. LLSD payload;
  348. payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE);
  349. payload["required"] = data["required"];
  350. payload["version"] = mNewVersion;
  351. event["payload"] = payload;
  352. LLEventPumps::instance().obtain("mainlooprepeater").post(event);
  353. setState(LLUpdaterService::TERMINAL);
  354. }
  355. void LLUpdaterServiceImpl::downloadError(std::string const & message)
  356. {
  357. LL_INFOS("UpdaterService") << "Error downloading: " << message << LL_ENDL;
  358. mIsDownloading = false;
  359. // Restart the timer on error
  360. if(mIsChecking)
  361. {
  362. restartTimer(mCheckPeriod);
  363. }
  364. LLSD event;
  365. event["pump"] = LLUpdaterService::pumpName();
  366. LLSD payload;
  367. payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_ERROR);
  368. payload["message"] = message;
  369. event["payload"] = payload;
  370. LLEventPumps::instance().obtain("mainlooprepeater").post(event);
  371. setState(LLUpdaterService::FAILURE);
  372. }
  373. void LLUpdaterServiceImpl::restartTimer(unsigned int seconds)
  374. {
  375. LL_INFOS("UpdaterService") << "will check for update again in " <<
  376. seconds << " seconds" << LL_ENDL;
  377. mTimer.start();
  378. mTimer.setTimerExpirySec(seconds);
  379. LLEventPumps::instance().obtain("mainloop").listen(
  380. sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1));
  381. }
  382. void LLUpdaterServiceImpl::setState(LLUpdaterService::eUpdaterState state)
  383. {
  384. if(state != mState)
  385. {
  386. mState = state;
  387. LLSD event;
  388. event["pump"] = LLUpdaterService::pumpName();
  389. LLSD payload;
  390. payload["type"] = LLSD(LLUpdaterService::STATE_CHANGE);
  391. payload["state"] = state;
  392. event["payload"] = payload;
  393. LLEventPumps::instance().obtain("mainlooprepeater").post(event);
  394. LL_INFOS("UpdaterService") << "setting state to " << state << LL_ENDL;
  395. }
  396. else
  397. {
  398. ; // State unchanged; noop.
  399. }
  400. }
  401. void LLUpdaterServiceImpl::stopTimer()
  402. {
  403. mTimer.stop();
  404. LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName);
  405. }
  406. bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event)
  407. {
  408. if(mTimer.getStarted() && mTimer.hasExpired())
  409. {
  410. stopTimer();
  411. // Check for failed install.
  412. if(LLFile::isfile(ll_install_failed_marker_path()))
  413. {
  414. int requiredValue = 0;
  415. {
  416. llifstream stream(ll_install_failed_marker_path());
  417. stream >> requiredValue;
  418. if(stream.fail()) requiredValue = 0;
  419. }
  420. // TODO: notify the user.
  421. llinfos << "found marker " << ll_install_failed_marker_path() << llendl;
  422. llinfos << "last install attempt failed" << llendl;
  423. LLFile::remove(ll_install_failed_marker_path());
  424. LLSD event;
  425. event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR);
  426. event["required"] = LLSD(requiredValue);
  427. LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event);
  428. setState(LLUpdaterService::TERMINAL);
  429. }
  430. else
  431. {
  432. mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion);
  433. setState(LLUpdaterService::CHECKING_FOR_UPDATE);
  434. }
  435. }
  436. else
  437. {
  438. // Keep on waiting...
  439. }
  440. return false;
  441. }
  442. //-----------------------------------------------------------------------
  443. // Facade interface
  444. std::string const & LLUpdaterService::pumpName(void)
  445. {
  446. static std::string name("updater_service");
  447. return name;
  448. }
  449. bool LLUpdaterService::updateReadyToInstall(void)
  450. {
  451. return LLFile::isfile(update_marker_path());
  452. }
  453. LLUpdaterService::LLUpdaterService()
  454. {
  455. if(gUpdater.expired())
  456. {
  457. mImpl =
  458. boost::shared_ptr<LLUpdaterServiceImpl>(new LLUpdaterServiceImpl());
  459. gUpdater = mImpl;
  460. }
  461. else
  462. {
  463. mImpl = gUpdater.lock();
  464. }
  465. }
  466. LLUpdaterService::~LLUpdaterService()
  467. {
  468. }
  469. void LLUpdaterService::initialize(const std::string& protocol_version,
  470. const std::string& url,
  471. const std::string& path,
  472. const std::string& channel,
  473. const std::string& version)
  474. {
  475. mImpl->initialize(protocol_version, url, path, channel, version);
  476. }
  477. void LLUpdaterService::setCheckPeriod(unsigned int seconds)
  478. {
  479. mImpl->setCheckPeriod(seconds);
  480. }
  481. void LLUpdaterService::setBandwidthLimit(U64 bytesPerSecond)
  482. {
  483. mImpl->setBandwidthLimit(bytesPerSecond);
  484. }
  485. void LLUpdaterService::startChecking(bool install_if_ready)
  486. {
  487. mImpl->startChecking(install_if_ready);
  488. }
  489. void LLUpdaterService::stopChecking()
  490. {
  491. mImpl->stopChecking();
  492. }
  493. bool LLUpdaterService::isChecking()
  494. {
  495. return mImpl->isChecking();
  496. }
  497. LLUpdaterService::eUpdaterState LLUpdaterService::getState()
  498. {
  499. return mImpl->getState();
  500. }
  501. void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb)
  502. {
  503. return mImpl->setAppExitCallback(aecb);
  504. }
  505. std::string LLUpdaterService::updatedVersion(void)
  506. {
  507. return mImpl->updatedVersion();
  508. }
  509. std::string const & ll_get_version(void) {
  510. static std::string version("");
  511. if (version.empty()) {
  512. std::ostringstream stream;
  513. stream << LL_VERSION_MAJOR << "."
  514. << LL_VERSION_MINOR << "."
  515. << LL_VERSION_PATCH << "."
  516. << LL_VERSION_BUILD;
  517. version = stream.str();
  518. }
  519. return version;
  520. }