PageRenderTime 47ms CodeModel.GetById 9ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 1ms

/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
 26#include "linden_common.h"
 27
 28#include "llupdaterservice.h"
 29
 30#include "llupdatedownloader.h"
 31#include "llevents.h"
 32#include "lltimer.h"
 33#include "llupdatechecker.h"
 34#include "llupdateinstaller.h"
 35#include "llversionviewer.h"
 36
 37#include <boost/scoped_ptr.hpp>
 38#include <boost/weak_ptr.hpp>
 39#include "lldir.h"
 40#include "llsdserialize.h"
 41#include "llfile.h"
 42
 43#if LL_WINDOWS
 44#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
 45#endif
 46
 47
 48namespace 
 49{
 50	boost::weak_ptr<LLUpdaterServiceImpl> gUpdater;
 51
 52	const std::string UPDATE_MARKER_FILENAME("SecondLifeUpdateReady.xml");
 53	std::string update_marker_path()
 54	{
 55		return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, 
 56											  UPDATE_MARKER_FILENAME);
 57	}
 58	
 59	std::string install_script_path(void)
 60	{
 61#ifdef LL_WINDOWS
 62		std::string scriptFile = "update_install.bat";
 63#else
 64		std::string scriptFile = "update_install";
 65#endif
 66		return gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, scriptFile);
 67	}
 68	
 69	LLInstallScriptMode install_script_mode(void) 
 70	{
 71#ifdef LL_WINDOWS
 72		return LL_COPY_INSTALL_SCRIPT_TO_TEMP;
 73#else
 74		return LL_RUN_INSTALL_SCRIPT_IN_PLACE;
 75#endif
 76	};
 77	
 78}
 79
 80class LLUpdaterServiceImpl : 
 81	public LLUpdateChecker::Client,
 82	public LLUpdateDownloader::Client
 83{
 84	static const std::string sListenerName;
 85	
 86	std::string mProtocolVersion;
 87	std::string mUrl;
 88	std::string mPath;
 89	std::string mChannel;
 90	std::string mVersion;
 91	
 92	unsigned int mCheckPeriod;
 93	bool mIsChecking;
 94	bool mIsDownloading;
 95	
 96	LLUpdateChecker mUpdateChecker;
 97	LLUpdateDownloader mUpdateDownloader;
 98	LLTimer mTimer;
 99
100	LLUpdaterService::app_exit_callback_t mAppExitCallback;
101	
102	LLUpdaterService::eUpdaterState mState;
103	
104	LOG_CLASS(LLUpdaterServiceImpl);
105	
106public:
107	LLUpdaterServiceImpl();
108	virtual ~LLUpdaterServiceImpl();
109
110	void initialize(const std::string& protocol_version,
111				   const std::string& url, 
112				   const std::string& path,
113				   const std::string& channel,
114				   const std::string& version);
115	
116	void setCheckPeriod(unsigned int seconds);
117	void setBandwidthLimit(U64 bytesPerSecond);
118
119	void startChecking(bool install_if_ready);
120	void stopChecking();
121	bool isChecking();
122	LLUpdaterService::eUpdaterState getState();
123	
124	void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;}
125	std::string updatedVersion(void);
126
127	bool checkForInstall(bool launchInstaller); // Test if a local install is ready.
128	bool checkForResume(); // Test for resumeable d/l.
129
130	// LLUpdateChecker::Client:
131	virtual void error(std::string const & message);
132	virtual void optionalUpdate(std::string const & newVersion,
133								LLURI const & uri,
134								std::string const & hash);
135	virtual void requiredUpdate(std::string const & newVersion,
136								LLURI const & uri,
137								std::string const & hash);
138	virtual void upToDate(void);
139	
140	// LLUpdateDownloader::Client
141	void downloadComplete(LLSD const & data);
142	void downloadError(std::string const & message);
143
144	bool onMainLoop(LLSD const & event);
145
146private:
147	std::string mNewVersion;
148	
149	void restartTimer(unsigned int seconds);
150	void setState(LLUpdaterService::eUpdaterState state);
151	void stopTimer();
152};
153
154const std::string LLUpdaterServiceImpl::sListenerName = "LLUpdaterServiceImpl";
155
156LLUpdaterServiceImpl::LLUpdaterServiceImpl() :
157	mIsChecking(false),
158	mIsDownloading(false),
159	mCheckPeriod(0),
160	mUpdateChecker(*this),
161	mUpdateDownloader(*this),
162	mState(LLUpdaterService::INITIAL)
163{
164}
165
166LLUpdaterServiceImpl::~LLUpdaterServiceImpl()
167{
168	LL_INFOS("UpdaterService") << "shutting down updater service" << LL_ENDL;
169	LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName);
170}
171
172void LLUpdaterServiceImpl::initialize(const std::string& protocol_version,
173									  const std::string& url, 
174									  const std::string& path,
175									  const std::string& channel,
176									  const std::string& version)
177{
178	if(mIsChecking || mIsDownloading)
179	{
180		throw LLUpdaterService::UsageError("LLUpdaterService::initialize call "
181										   "while updater is running.");
182	}
183		
184	mProtocolVersion = protocol_version;
185	mUrl = url;
186	mPath = path;
187	mChannel = channel;
188	mVersion = version;
189}
190
191void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds)
192{
193	mCheckPeriod = seconds;
194}
195
196void LLUpdaterServiceImpl::setBandwidthLimit(U64 bytesPerSecond)
197{
198	mUpdateDownloader.setBandwidthLimit(bytesPerSecond);
199}
200
201void LLUpdaterServiceImpl::startChecking(bool install_if_ready)
202{
203	if(mUrl.empty() || mChannel.empty() || mVersion.empty())
204	{
205		throw LLUpdaterService::UsageError("Set params before call to "
206			"LLUpdaterService::startCheck().");
207	}
208
209	mIsChecking = true;
210
211    // Check to see if an install is ready.
212	bool has_install = checkForInstall(install_if_ready);
213	if(!has_install)
214	{
215		checkForResume(); // will set mIsDownloading to true if resuming
216
217		if(!mIsDownloading)
218		{
219			setState(LLUpdaterService::CHECKING_FOR_UPDATE);
220			
221			// Checking can only occur during the mainloop.
222			// reset the timer to 0 so that the next mainloop event 
223			// triggers a check;
224			restartTimer(0); 
225		} 
226		else
227		{
228			setState(LLUpdaterService::DOWNLOADING);
229		}
230	}
231}
232
233void LLUpdaterServiceImpl::stopChecking()
234{
235	if(mIsChecking)
236	{
237		mIsChecking = false;
238		stopTimer();
239	}
240
241    if(mIsDownloading)
242    {
243        mUpdateDownloader.cancel();
244		mIsDownloading = false;
245    }
246	
247	setState(LLUpdaterService::TERMINAL);
248}
249
250bool LLUpdaterServiceImpl::isChecking()
251{
252	return mIsChecking;
253}
254
255LLUpdaterService::eUpdaterState LLUpdaterServiceImpl::getState()
256{
257	return mState;
258}
259
260std::string LLUpdaterServiceImpl::updatedVersion(void)
261{
262	return mNewVersion;
263}
264
265bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller)
266{
267	bool foundInstall = false; // return true if install is found.
268
269	llifstream update_marker(update_marker_path(), 
270							 std::ios::in | std::ios::binary);
271
272	if(update_marker.is_open())
273	{
274		// Found an update info - now lets see if its valid.
275		LLSD update_info;
276		LLSDSerialize::fromXMLDocument(update_info, update_marker);
277		update_marker.close();
278
279		// Get the path to the installer file.
280		LLSD path = update_info.get("path");
281		if(update_info["current_version"].asString() != ll_get_version())
282		{
283			// This viewer is not the same version as the one that downloaded
284			// the update.  Do not install this update.
285			if(!path.asString().empty())
286			{
287				llinfos << "ignoring update dowloaded by different client version" << llendl;
288				LLFile::remove(path.asString());
289				LLFile::remove(update_marker_path());
290			}
291			else
292			{
293				; // Nothing to clean up.
294			}
295			
296			foundInstall = false;
297		} 
298		else if(path.isDefined() && !path.asString().empty())
299		{
300			if(launchInstaller)
301			{
302				setState(LLUpdaterService::INSTALLING);
303				
304				LLFile::remove(update_marker_path());
305
306				int result = ll_install_update(install_script_path(),
307											   update_info["path"].asString(),
308											   update_info["required"].asBoolean(),
309											   install_script_mode());	
310				
311				if((result == 0) && mAppExitCallback)
312				{
313					mAppExitCallback();
314				} else if(result != 0) {
315					llwarns << "failed to run update install script" << LL_ENDL;
316				} else {
317					; // No op.
318				}
319			}
320			
321			foundInstall = true;
322		}
323	}
324	return foundInstall;
325}
326
327bool LLUpdaterServiceImpl::checkForResume()
328{
329	bool result = false;
330	std::string download_marker_path = mUpdateDownloader.downloadMarkerPath();
331	if(LLFile::isfile(download_marker_path))
332	{
333		llifstream download_marker_stream(download_marker_path, 
334								 std::ios::in | std::ios::binary);
335		if(download_marker_stream.is_open())
336		{
337			LLSD download_info;
338			LLSDSerialize::fromXMLDocument(download_info, download_marker_stream);
339			download_marker_stream.close();
340			if(download_info["current_version"].asString() == ll_get_version())
341			{
342				mIsDownloading = true;
343				mNewVersion = download_info["update_version"].asString();
344				mUpdateDownloader.resume();
345				result = true;
346			}
347			else 
348			{
349				// The viewer that started this download is not the same as this viewer; ignore.
350				llinfos << "ignoring partial download from different viewer version" << llendl;
351				std::string path = download_info["path"].asString();
352				if(!path.empty()) LLFile::remove(path);
353				LLFile::remove(download_marker_path);
354			}
355		} 
356	}
357	return result;
358}
359
360void LLUpdaterServiceImpl::error(std::string const & message)
361{
362	if(mIsChecking)
363	{
364		setState(LLUpdaterService::TEMPORARY_ERROR);
365		restartTimer(mCheckPeriod);
366	}
367}
368
369void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion,
370										  LLURI const & uri,
371										  std::string const & hash)
372{
373	stopTimer();
374	mNewVersion = newVersion;
375	mIsDownloading = true;
376	setState(LLUpdaterService::DOWNLOADING);
377	mUpdateDownloader.download(uri, hash, newVersion, false);
378}
379
380void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion,
381										  LLURI const & uri,
382										  std::string const & hash)
383{
384	stopTimer();
385	mNewVersion = newVersion;
386	mIsDownloading = true;
387	setState(LLUpdaterService::DOWNLOADING);
388	mUpdateDownloader.download(uri, hash, newVersion, true);
389}
390
391void LLUpdaterServiceImpl::upToDate(void)
392{
393	if(mIsChecking)
394	{
395		restartTimer(mCheckPeriod);
396	}
397	
398	setState(LLUpdaterService::UP_TO_DATE);
399}
400
401void LLUpdaterServiceImpl::downloadComplete(LLSD const & data) 
402{ 
403	mIsDownloading = false;
404
405	// Save out the download data to the SecondLifeUpdateReady
406	// marker file. 
407	llofstream update_marker(update_marker_path());
408	LLSDSerialize::toPrettyXML(data, update_marker);
409	
410	LLSD event;
411	event["pump"] = LLUpdaterService::pumpName();
412	LLSD payload;
413	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE);
414	payload["required"] = data["required"];
415	payload["version"] = mNewVersion;
416	event["payload"] = payload;
417	LLEventPumps::instance().obtain("mainlooprepeater").post(event);
418	
419	setState(LLUpdaterService::TERMINAL);
420}
421
422void LLUpdaterServiceImpl::downloadError(std::string const & message) 
423{ 
424	LL_INFOS("UpdaterService") << "Error downloading: " << message << LL_ENDL;
425
426	mIsDownloading = false;
427
428	// Restart the timer on error
429	if(mIsChecking)
430	{
431		restartTimer(mCheckPeriod); 
432	}
433
434	LLSD event;
435	event["pump"] = LLUpdaterService::pumpName();
436	LLSD payload;
437	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_ERROR);
438	payload["message"] = message;
439	event["payload"] = payload;
440	LLEventPumps::instance().obtain("mainlooprepeater").post(event);
441
442	setState(LLUpdaterService::FAILURE);
443}
444
445void LLUpdaterServiceImpl::restartTimer(unsigned int seconds)
446{
447	LL_INFOS("UpdaterService") << "will check for update again in " << 
448	seconds << " seconds" << LL_ENDL; 
449	mTimer.start();
450	mTimer.setTimerExpirySec(seconds);
451	LLEventPumps::instance().obtain("mainloop").listen(
452		sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1));
453}
454
455void LLUpdaterServiceImpl::setState(LLUpdaterService::eUpdaterState state)
456{
457	if(state != mState)
458	{
459		mState = state;
460		
461		LLSD event;
462		event["pump"] = LLUpdaterService::pumpName();
463		LLSD payload;
464		payload["type"] = LLSD(LLUpdaterService::STATE_CHANGE);
465		payload["state"] = state;
466		event["payload"] = payload;
467		LLEventPumps::instance().obtain("mainlooprepeater").post(event);
468		
469		LL_INFOS("UpdaterService") << "setting state to " << state << LL_ENDL;
470	}
471	else 
472	{
473		; // State unchanged; noop.
474	}
475}
476
477void LLUpdaterServiceImpl::stopTimer()
478{
479	mTimer.stop();
480	LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName);
481}
482
483bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event)
484{
485	if(mTimer.getStarted() && mTimer.hasExpired())
486	{
487		stopTimer();
488
489		// Check for failed install.
490		if(LLFile::isfile(ll_install_failed_marker_path()))
491		{
492			int requiredValue = 0; 
493			{
494				llifstream stream(ll_install_failed_marker_path());
495				stream >> requiredValue;
496				if(stream.fail()) requiredValue = 0;
497			}
498			// TODO: notify the user.
499			llinfos << "found marker " << ll_install_failed_marker_path() << llendl;
500			llinfos << "last install attempt failed" << llendl;
501			LLFile::remove(ll_install_failed_marker_path());
502			
503			LLSD event;
504			event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR);
505			event["required"] = LLSD(requiredValue);
506			LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event);
507			
508			setState(LLUpdaterService::TERMINAL);
509		}
510		else
511		{
512			mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion);
513			setState(LLUpdaterService::CHECKING_FOR_UPDATE);
514		}
515	} 
516	else 
517	{
518		// Keep on waiting...
519	}
520	
521	return false;
522}
523
524
525//-----------------------------------------------------------------------
526// Facade interface
527
528std::string const & LLUpdaterService::pumpName(void)
529{
530	static std::string name("updater_service");
531	return name;
532}
533
534bool LLUpdaterService::updateReadyToInstall(void)
535{
536	return LLFile::isfile(update_marker_path());
537}
538
539LLUpdaterService::LLUpdaterService()
540{
541	if(gUpdater.expired())
542	{
543		mImpl = 
544			boost::shared_ptr<LLUpdaterServiceImpl>(new LLUpdaterServiceImpl());
545		gUpdater = mImpl;
546	}
547	else
548	{
549		mImpl = gUpdater.lock();
550	}
551}
552
553LLUpdaterService::~LLUpdaterService()
554{
555}
556
557void LLUpdaterService::initialize(const std::string& protocol_version,
558								 const std::string& url, 
559								 const std::string& path,
560								 const std::string& channel,
561								 const std::string& version)
562{
563	mImpl->initialize(protocol_version, url, path, channel, version);
564}
565
566void LLUpdaterService::setCheckPeriod(unsigned int seconds)
567{
568	mImpl->setCheckPeriod(seconds);
569}
570
571void LLUpdaterService::setBandwidthLimit(U64 bytesPerSecond)
572{
573	mImpl->setBandwidthLimit(bytesPerSecond);
574}
575	
576void LLUpdaterService::startChecking(bool install_if_ready)
577{
578	mImpl->startChecking(install_if_ready);
579}
580
581void LLUpdaterService::stopChecking()
582{
583	mImpl->stopChecking();
584}
585
586bool LLUpdaterService::isChecking()
587{
588	return mImpl->isChecking();
589}
590
591LLUpdaterService::eUpdaterState LLUpdaterService::getState()
592{
593	return mImpl->getState();
594}
595
596void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb)
597{
598	return mImpl->setAppExitCallback(aecb);
599}
600
601std::string LLUpdaterService::updatedVersion(void)
602{
603	return mImpl->updatedVersion();
604}
605
606
607std::string const & ll_get_version(void) {
608	static std::string version("");
609	
610	if (version.empty()) {
611		std::ostringstream stream;
612		stream << LL_VERSION_MAJOR << "."
613		<< LL_VERSION_MINOR << "."
614		<< LL_VERSION_PATCH << "."
615		<< LL_VERSION_BUILD;
616		version = stream.str();
617	}
618	
619	return version;
620}
621