linux_updater.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:22k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file linux_updater.cpp
  3.  * @author Kyle Ambroff <ambroff@lindenlab.com>, Tofu Linden
  4.  * @brief Viewer update program for unix platforms that support GTK+
  5.  *
  6.  * $LicenseInfo:firstyear=2008&license=viewergpl$
  7.  * 
  8.  * Copyright (c) 2008-2010, Linden Research, Inc.
  9.  * 
  10.  * Second Life Viewer Source Code
  11.  * The source code in this file ("Source Code") is provided by Linden Lab
  12.  * to you under the terms of the GNU General Public License, version 2.0
  13.  * ("GPL"), unless you have obtained a separate licensing agreement
  14.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  15.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17.  * 
  18.  * There are special exceptions to the terms and conditions of the GPL as
  19.  * it is applied to this Source Code. View the full text of the exception
  20.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  21.  * online at
  22.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23.  * 
  24.  * By copying, modifying or distributing this software, you acknowledge
  25.  * that you have read and understood your obligations described above,
  26.  * and agree to abide by those obligations.
  27.  * 
  28.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30.  * COMPLETENESS OR PERFORMANCE.
  31.  * $/LicenseInfo$
  32.  */
  33. #include <unistd.h>
  34. #include <signal.h>
  35. #include <errno.h>
  36. #include "linden_common.h"
  37. #include "llerrorcontrol.h"
  38. #include "llfile.h"
  39. #include "lldir.h"
  40. #include "llxmlnode.h"
  41. #include "lltrans.h"
  42. #include <curl/curl.h>
  43. extern "C" {
  44. #include <gtk/gtk.h>
  45. }
  46. const guint UPDATE_PROGRESS_TIMEOUT = 100;
  47. const guint UPDATE_PROGRESS_TEXT_TIMEOUT = 1000;
  48. const guint ROTATE_IMAGE_TIMEOUT = 8000;
  49. typedef struct _updater_app_state {
  50. std::string app_name;
  51. std::string url;
  52. std::string image_dir;
  53. std::string dest_dir;
  54. std::string strings_dirs;
  55. std::string strings_file;
  56. GtkWidget *window;
  57. GtkWidget *progress_bar;
  58. GtkWidget *image;
  59. double progress_value;
  60. bool activity_mode;
  61. guint image_rotation_timeout_id;
  62. guint progress_update_timeout_id;
  63. guint update_progress_text_timeout_id;
  64. bool failure;
  65. } UpdaterAppState;
  66. // List of entries from strings.xml to always replace
  67. static std::set<std::string> default_trans_args;
  68. void init_default_trans_args()
  69. {
  70.         default_trans_args.insert("SECOND_LIFE"); // World
  71.         default_trans_args.insert("APP_NAME");
  72.         default_trans_args.insert("SECOND_LIFE_GRID");
  73.         default_trans_args.insert("SUPPORT_SITE");
  74. }
  75. bool translate_init(std::string comma_delim_path_list,
  76.     std::string base_xml_name)
  77. {
  78. init_default_trans_args();
  79. // extract paths string vector from comma-delimited flat string
  80. std::vector<std::string> paths;
  81. LLStringUtil::getTokens(comma_delim_path_list, paths, ","); // split over ','
  82. // suck the translation xml files into memory
  83. LLXMLNodePtr root;
  84. bool success = LLXMLNode::getLayeredXMLNode(base_xml_name, root, paths);
  85. if (!success)
  86. {
  87. // couldn't load string table XML
  88. return false;
  89. }
  90. else
  91. {
  92. // get those strings out of the XML
  93. LLTrans::parseStrings(root, default_trans_args);
  94. return true;
  95. }
  96. }
  97. void updater_app_ui_init(void);
  98. void updater_app_quit(UpdaterAppState *app_state);
  99. void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state);
  100. std::string next_image_filename(std::string& image_path);
  101. void display_error(GtkWidget *parent, std::string title, std::string message);
  102. BOOL install_package(std::string package_file, std::string destination);
  103. BOOL spawn_viewer(UpdaterAppState *app_state);
  104. extern "C" {
  105. void on_window_closed(GtkWidget *sender, gpointer state);
  106. gpointer worker_thread_cb(gpointer *data);
  107. int download_progress_cb(gpointer data, double t, double d, double utotal, double ulnow);
  108. gboolean rotate_image_cb(gpointer data);
  109. gboolean progress_update_timeout(gpointer data);
  110. gboolean update_progress_text_timeout(gpointer data);
  111. }
  112. void updater_app_ui_init(UpdaterAppState *app_state)
  113. {
  114. GtkWidget *vbox;
  115. GtkWidget *summary_label;
  116. GtkWidget *description_label;
  117. GtkWidget *frame;
  118. llassert(app_state != NULL);
  119. // set up window and main container
  120. std::string window_title = LLTrans::getString("UpdaterWindowTitle");
  121. app_state->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  122. gtk_window_set_title(GTK_WINDOW(app_state->window),
  123.      window_title.c_str());
  124. gtk_window_set_resizable(GTK_WINDOW(app_state->window), FALSE);
  125. gtk_window_set_position(GTK_WINDOW(app_state->window),
  126. GTK_WIN_POS_CENTER_ALWAYS);
  127. gtk_container_set_border_width(GTK_CONTAINER(app_state->window), 12);
  128. g_signal_connect(G_OBJECT(app_state->window), "delete-event", 
  129.  G_CALLBACK(on_window_closed), app_state);
  130. vbox = gtk_vbox_new(FALSE, 6);
  131. gtk_container_add(GTK_CONTAINER(app_state->window), vbox);
  132. // set top label
  133. std::ostringstream label_ostr;
  134. label_ostr << "<big><b>"
  135.    << LLTrans::getString("UpdaterNowUpdating")
  136.    << "</b></big>";
  137. summary_label = gtk_label_new(NULL);
  138. gtk_label_set_use_markup(GTK_LABEL(summary_label), TRUE);
  139. gtk_label_set_markup(GTK_LABEL(summary_label), 
  140.      label_ostr.str().c_str());
  141. gtk_misc_set_alignment(GTK_MISC(summary_label), 0, 0.5);
  142. gtk_box_pack_start(GTK_BOX(vbox), summary_label, FALSE, FALSE, 0);
  143. // create the description label
  144. description_label = gtk_label_new(LLTrans::getString("UpdaterUpdatingDescriptive").c_str());
  145. gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
  146. gtk_misc_set_alignment(GTK_MISC(description_label), 0, 0.5);
  147. gtk_box_pack_start(GTK_BOX(vbox), description_label, FALSE, FALSE, 0);
  148. // If an image path has been set, load the background images
  149. if (!app_state->image_dir.empty()) {
  150. frame = gtk_frame_new(NULL);
  151. gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
  152. gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
  153. // load the first image
  154. app_state->image = gtk_image_new_from_file
  155. (next_image_filename(app_state->image_dir).c_str());
  156. gtk_widget_set_size_request(app_state->image, 340, 310);
  157. gtk_container_add(GTK_CONTAINER(frame), app_state->image);
  158. // rotate the images every 5 seconds
  159. app_state->image_rotation_timeout_id = g_timeout_add
  160. (ROTATE_IMAGE_TIMEOUT, rotate_image_cb, app_state);
  161. }
  162. // set up progress bar, and update it roughly every 1/10 of a second
  163. app_state->progress_bar = gtk_progress_bar_new();
  164. gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar), 
  165.   LLTrans::getString("UpdaterProgressBarTextWithEllipses").c_str());
  166. gtk_box_pack_start(GTK_BOX(vbox), 
  167.    app_state->progress_bar, FALSE, TRUE, 0);
  168. app_state->progress_update_timeout_id = g_timeout_add
  169. (UPDATE_PROGRESS_TIMEOUT, progress_update_timeout, app_state);
  170. app_state->update_progress_text_timeout_id = g_timeout_add
  171. (UPDATE_PROGRESS_TEXT_TIMEOUT, update_progress_text_timeout, app_state);
  172. gtk_widget_show_all(app_state->window);
  173. }
  174. gboolean rotate_image_cb(gpointer data)
  175. {
  176. UpdaterAppState *app_state;
  177. std::string filename;
  178. llassert(data != NULL);
  179. app_state = (UpdaterAppState *) data;
  180. filename = next_image_filename(app_state->image_dir);
  181. gdk_threads_enter();
  182. gtk_image_set_from_file(GTK_IMAGE(app_state->image), filename.c_str());
  183. gdk_threads_leave();
  184. return TRUE;
  185. }
  186. std::string next_image_filename(std::string& image_path)
  187. {
  188. std::string image_filename;
  189. gDirUtilp->getNextFileInDir(image_path, "/*.jpg", image_filename, true);
  190. return image_path + "/" + image_filename;
  191. }
  192. void on_window_closed(GtkWidget *sender, gpointer data)
  193. {
  194. UpdaterAppState *app_state;
  195. llassert(data != NULL);
  196. app_state = (UpdaterAppState *) data;
  197. updater_app_quit(app_state);
  198. }
  199. void updater_app_quit(UpdaterAppState *app_state)
  200. {
  201. if (app_state != NULL)
  202. {
  203. g_source_remove(app_state->progress_update_timeout_id);
  204. if (!app_state->image_dir.empty())
  205. {
  206. g_source_remove(app_state->image_rotation_timeout_id);
  207. }
  208. }
  209. gtk_main_quit();
  210. }
  211. void display_error(GtkWidget *parent, std::string title, std::string message)
  212. {
  213. GtkWidget *dialog;
  214. dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
  215. GTK_DIALOG_DESTROY_WITH_PARENT,
  216. GTK_MESSAGE_ERROR,
  217. GTK_BUTTONS_OK,
  218. "%s", message.c_str());
  219. gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
  220. gtk_dialog_run(GTK_DIALOG(dialog));
  221. gtk_widget_destroy(dialog);
  222. }
  223. gpointer worker_thread_cb(gpointer data)
  224. {
  225. UpdaterAppState *app_state;
  226. CURL *curl;
  227. CURLcode result;
  228. FILE *package_file;
  229. GError *error = NULL;
  230. char *tmp_filename = NULL;
  231. int fd;
  232. //g_return_val_if_fail (data != NULL, NULL);
  233. app_state = (UpdaterAppState *) data;
  234. try {
  235. // create temporary file to store the package.
  236. fd = g_file_open_tmp
  237. ("secondlife-update-XXXXXX", &tmp_filename, &error);
  238. if (error != NULL)
  239. {
  240. llerrs << "Unable to create temporary file: "
  241.        << error->message
  242.        << llendl;
  243. g_error_free(error);
  244. throw 0;
  245. }
  246. package_file = fdopen(fd, "wb");
  247. if (package_file == NULL)
  248. {
  249. llerrs << "Failed to create temporary file: "
  250.        << tmp_filename
  251.        << llendl;
  252. gdk_threads_enter();
  253. display_error(app_state->window,
  254.       LLTrans::getString("UpdaterFailDownloadTitle"),
  255.       LLTrans::getString("UpdaterFailUpdateDescriptive"));
  256. gdk_threads_leave();
  257. throw 0;
  258. }
  259. // initialize curl and start downloading the package
  260. llinfos << "Downloading package: " << app_state->url << llendl;
  261. curl = curl_easy_init();
  262. if (curl == NULL)
  263. {
  264. llerrs << "Failed to initialize libcurl" << llendl;
  265. gdk_threads_enter();
  266. display_error(app_state->window,
  267.       LLTrans::getString("UpdaterFailDownloadTitle"),
  268.       LLTrans::getString("UpdaterFailUpdateDescriptive"));
  269. gdk_threads_leave();
  270. throw 0;
  271. }
  272. curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str());
  273. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE);
  274. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
  275. curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file);
  276. curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
  277. curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, 
  278.  &download_progress_cb);
  279. curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state);
  280. result = curl_easy_perform(curl);
  281. fclose(package_file);
  282. curl_easy_cleanup(curl);
  283. if (result)
  284. {
  285. llerrs << "Failed to download update: " 
  286.        << app_state->url 
  287.        << llendl;
  288. gdk_threads_enter();
  289. display_error(app_state->window,
  290.       LLTrans::getString("UpdaterFailDownloadTitle"),
  291.       LLTrans::getString("UpdaterFailUpdateDescriptive"));
  292. gdk_threads_leave();
  293. throw 0;
  294. }
  295. // now pulse the progres bar back and forth while the package is
  296. // being unpacked
  297. gdk_threads_enter();
  298. std::string installing_msg = LLTrans::getString("UpdaterNowInstalling");
  299. gtk_progress_bar_set_text(
  300. GTK_PROGRESS_BAR(app_state->progress_bar),
  301. installing_msg.c_str());
  302. app_state->activity_mode = TRUE;
  303. gdk_threads_leave();
  304. // *TODO: if the destination is not writable, terminate this
  305. // thread and show file chooser?
  306. if (!install_package(tmp_filename, app_state->dest_dir))
  307. {
  308. llwarns << "Failed to install package to destination: "
  309. << app_state->dest_dir
  310. << llendl;
  311. gdk_threads_enter();
  312. display_error(app_state->window,
  313.       LLTrans::getString("UpdaterFailInstallTitle"),
  314.       LLTrans::getString("UpdaterFailUpdateDescriptive"));
  315. //"Failed to update " + app_state->app_name,
  316. gdk_threads_leave();
  317. throw 0;
  318. }
  319. // try to spawn the new viewer
  320. if (!spawn_viewer(app_state))
  321. {
  322. llwarns << "Viewer was not installed properly in : "
  323. << app_state->dest_dir
  324. << llendl;
  325. gdk_threads_enter();
  326. display_error(app_state->window,
  327.       LLTrans::getString("UpdaterFailStartTitle"),
  328.       LLTrans::getString("UpdaterFailUpdateDescriptive"));
  329. gdk_threads_leave();
  330. throw 0;
  331. }
  332. }
  333. catch (...)
  334. {
  335. app_state->failure = TRUE;
  336. }
  337. // FIXME: delete package file also if delete-event is raised on window
  338. if (tmp_filename != NULL)
  339. {
  340. if (gDirUtilp->fileExists(tmp_filename))
  341. {
  342. LLFile::remove(tmp_filename);
  343. }
  344. }
  345. gdk_threads_enter();
  346. updater_app_quit(app_state);
  347. gdk_threads_leave();
  348. return NULL;
  349. }
  350. gboolean less_anal_gspawnsync(gchar **argv,
  351.       gchar **stderr_output,
  352.       gint *child_exit_status,
  353.       GError **spawn_error)
  354. {
  355. // store current SIGCHLD handler if there is one, replace with default
  356. // handler to make glib happy
  357. struct sigaction sigchld_backup;
  358. struct sigaction sigchld_appease_glib;
  359. sigchld_appease_glib.sa_handler = SIG_DFL;
  360. sigemptyset(&sigchld_appease_glib.sa_mask);
  361. sigchld_appease_glib.sa_flags = 0;
  362. sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
  363. gboolean rtn = g_spawn_sync(NULL,
  364.     argv,
  365.     NULL,
  366.     (GSpawnFlags) (G_SPAWN_STDOUT_TO_DEV_NULL),
  367.     NULL,
  368.     NULL,
  369.     NULL,
  370.     stderr_output,
  371.     child_exit_status,
  372.     spawn_error);
  373. // restore SIGCHLD handler
  374. sigaction(SIGCHLD, &sigchld_backup, NULL);
  375. return rtn;
  376. }
  377. // perform a rename, or perform a (prompted) root rename if that fails
  378. int
  379. rename_with_sudo_fallback(const std::string& filename, const std::string& newname)
  380. {
  381. int rtncode = ::rename(filename.c_str(), newname.c_str());
  382. lldebugs << "rename result is: " << rtncode << " / " << errno << llendl;
  383. if (rtncode && (EACCES == errno || EPERM == errno || EXDEV == errno))
  384. {
  385. llinfos << "Permission problem in rename, or moving between different mount points.  Retrying as a mv under a sudo." << llendl;
  386. // failed due to permissions, try again as a gksudo or kdesu mv wrapper hack
  387. char *sudo_cmd = NULL;
  388. sudo_cmd = g_find_program_in_path("gksudo");
  389. if (!sudo_cmd)
  390. {
  391. sudo_cmd = g_find_program_in_path("kdesu");
  392. }
  393. if (sudo_cmd)
  394. {
  395. char *mv_cmd = NULL;
  396. mv_cmd = g_find_program_in_path("mv");
  397. if (mv_cmd)
  398. {
  399. char *src_string_copy = g_strdup(filename.c_str());
  400. char *dst_string_copy = g_strdup(newname.c_str());
  401. char* argv[] = 
  402. {
  403. sudo_cmd,
  404. mv_cmd,
  405. src_string_copy,
  406. dst_string_copy,
  407. NULL
  408. };
  409. gchar *stderr_output = NULL;
  410. gint child_exit_status = 0;
  411. GError *spawn_error = NULL;
  412. if (!less_anal_gspawnsync(argv, &stderr_output,
  413.   &child_exit_status, &spawn_error))
  414. {
  415. llwarns << "Failed to spawn child process: " 
  416. << spawn_error->message 
  417. << llendl;
  418. }
  419. else if (child_exit_status)
  420. {
  421. llwarns << "mv command failed: "
  422. << (stderr_output ? stderr_output : "(no reason given)")
  423. << llendl;
  424. }
  425. else
  426. {
  427. // everything looks good, clear the error code
  428. rtncode = 0;
  429. }
  430. g_free(src_string_copy);
  431. g_free(dst_string_copy);
  432. if (spawn_error) g_error_free(spawn_error);
  433. }
  434. }
  435. }
  436. return rtncode;
  437. }
  438. gboolean install_package(std::string package_file, std::string destination)
  439. {
  440. char *tar_cmd = NULL;
  441. std::ostringstream command;
  442. // Find the absolute path to the 'tar' command.
  443. tar_cmd = g_find_program_in_path("tar");
  444. if (!tar_cmd)
  445. {
  446. llerrs << "`tar' was not found in $PATH" << llendl;
  447. return FALSE;
  448. }
  449. llinfos << "Found tar command: " << tar_cmd << llendl;
  450. // Unpack the tarball in a temporary place first, then move it to 
  451. // its final destination
  452. std::string tmp_dest_dir = gDirUtilp->getTempFilename();
  453. if (LLFile::mkdir(tmp_dest_dir, 0744))
  454. {
  455. llerrs << "Failed to create directory: "
  456.        << destination
  457.        << llendl;
  458. return FALSE;
  459. }
  460. char *package_file_string_copy = g_strdup(package_file.c_str());
  461. char *tmp_dest_dir_string_copy = g_strdup(tmp_dest_dir.c_str());
  462. gchar *argv[8] = {
  463. tar_cmd,
  464. const_cast<gchar*>("--strip"), const_cast<gchar*>("1"),
  465. const_cast<gchar*>("-xjf"),
  466. package_file_string_copy,
  467. const_cast<gchar*>("-C"), tmp_dest_dir_string_copy,
  468. NULL,
  469. };
  470. llinfos << "Untarring package: " << package_file << llendl;
  471. // store current SIGCHLD handler if there is one, replace with default
  472. // handler to make glib happy
  473. struct sigaction sigchld_backup;
  474. struct sigaction sigchld_appease_glib;
  475. sigchld_appease_glib.sa_handler = SIG_DFL;
  476. sigemptyset(&sigchld_appease_glib.sa_mask);
  477. sigchld_appease_glib.sa_flags = 0;
  478. sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
  479. gchar *stderr_output = NULL;
  480. gint child_exit_status = 0;
  481. GError *untar_error = NULL;
  482. if (!less_anal_gspawnsync(argv, &stderr_output,
  483.   &child_exit_status, &untar_error))
  484. {
  485. llwarns << "Failed to spawn child process: " 
  486. << untar_error->message 
  487. << llendl;
  488. return FALSE;
  489. }
  490. if (child_exit_status)
  491. {
  492.   llwarns << "Untar command failed: "
  493. << (stderr_output ? stderr_output : "(no reason given)")
  494. << llendl;
  495. return FALSE;
  496. }
  497. g_free(tar_cmd);
  498. g_free(package_file_string_copy);
  499. g_free(tmp_dest_dir_string_copy);
  500. g_free(stderr_output);
  501. if (untar_error) g_error_free(untar_error);
  502. // move the existing package out of the way if it exists
  503. if (gDirUtilp->fileExists(destination))
  504. {
  505. std::string backup_dir = destination + ".backup";
  506. int oldcounter = 1;
  507. while (gDirUtilp->fileExists(backup_dir))
  508. {
  509. // find a foo.backup.N folder name that isn't taken yet
  510. backup_dir = destination + ".backup." + llformat("%d", oldcounter);
  511. ++oldcounter;
  512. }
  513. if (rename_with_sudo_fallback(destination, backup_dir))
  514. {
  515. llwarns << "Failed to move directory: '" 
  516. << destination << "' -> '" << backup_dir 
  517. << llendl;
  518. return FALSE;
  519. }
  520. }
  521. // The package has been unpacked in a staging directory, now we just
  522. // need to move it to its destination.
  523. if (rename_with_sudo_fallback(tmp_dest_dir, destination))
  524. {
  525. llwarns << "Failed to move installation to the destination: "
  526. << destination 
  527. << llendl;
  528. return FALSE;
  529. }
  530. // / Success!
  531. return TRUE;
  532. }
  533. gboolean progress_update_timeout(gpointer data)
  534. {
  535. UpdaterAppState *app_state;
  536. llassert(data != NULL);
  537. app_state = (UpdaterAppState *) data;
  538. gdk_threads_enter();
  539. if (app_state->activity_mode)
  540. {
  541. gtk_progress_bar_pulse
  542. (GTK_PROGRESS_BAR(app_state->progress_bar));
  543. }
  544. else
  545. {
  546. gtk_progress_set_value(GTK_PROGRESS(app_state->progress_bar),
  547.        app_state->progress_value);
  548. }
  549. gdk_threads_leave();
  550. return TRUE;
  551. }
  552. gboolean update_progress_text_timeout(gpointer data)
  553. {
  554. UpdaterAppState *app_state;
  555. llassert(data != NULL);
  556. app_state = (UpdaterAppState *) data;
  557. if (app_state->activity_mode == TRUE)
  558. {
  559. // We no longer need this timeout, it will be removed.
  560. return FALSE;
  561. }
  562. if (!app_state->progress_value)
  563. {
  564. return TRUE;
  565. }
  566. std::string progress_text = llformat((LLTrans::getString("UpdaterProgressBarText")+" (%.0f%%)").c_str(), app_state->progress_value);
  567. gdk_threads_enter();
  568. gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
  569.   progress_text.c_str());
  570. gdk_threads_leave();
  571. return TRUE;
  572. }
  573. int download_progress_cb(gpointer data,
  574.  double t,
  575.  double d,
  576.  double utotal,
  577.  double ulnow)
  578. {
  579. UpdaterAppState *app_state;
  580. llassert(data != NULL);
  581. app_state = (UpdaterAppState *) data;
  582. if (t <= 0.0)
  583. {
  584. app_state->progress_value = 0;
  585. }
  586. else
  587. {
  588. app_state->progress_value = d * 100.0 / t;
  589. }
  590. return 0;
  591. }
  592. BOOL spawn_viewer(UpdaterAppState *app_state)
  593. {
  594. llassert(app_state != NULL);
  595. std::string cmd = app_state->dest_dir + "/secondlife";
  596. GError *error = NULL;
  597. // We want to spawn the Viewer on the same display as the updater app
  598. gboolean success = gdk_spawn_command_line_on_screen
  599. (gtk_widget_get_screen(app_state->window), cmd.c_str(), &error);
  600. if (!success)
  601. {
  602. llwarns << "Failed to launch viewer: " << error->message 
  603. << llendl;
  604. }
  605. if (error) g_error_free(error);
  606. return success;
  607. }
  608. void show_usage_and_exit()
  609. {
  610. std::cout << "Usage: linux-updater --url URL --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE"
  611.   << "[--image-dir PATH]"
  612.   << std::endl;
  613. exit(1);
  614. }
  615. void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
  616. {
  617. int i;
  618. for (i = 1; i < argc; i++)
  619. {
  620. if ((!strcmp(argv[i], "--url")) && (++i < argc))
  621. {
  622. app_state->url = argv[i];
  623. }
  624. else if ((!strcmp(argv[i], "--name")) && (++i < argc))
  625. {
  626. app_state->app_name = argv[i];
  627. }
  628. else if ((!strcmp(argv[i], "--image-dir")) && (++i < argc))
  629. {
  630. app_state->image_dir = argv[i];
  631. }
  632. else if ((!strcmp(argv[i], "--dest")) && (++i < argc))
  633. {
  634. app_state->dest_dir = argv[i];
  635. }
  636. else if ((!strcmp(argv[i], "--stringsdir")) && (++i < argc))
  637. {
  638. app_state->strings_dirs = argv[i];
  639. }
  640. else if ((!strcmp(argv[i], "--stringsfile")) && (++i < argc))
  641. {
  642. app_state->strings_file = argv[i];
  643. }
  644. else
  645. {
  646. // show usage, an invalid option was given.
  647. show_usage_and_exit();
  648. }
  649. }
  650. if (app_state->app_name.empty() 
  651.     || app_state->url.empty() 
  652.     || app_state->dest_dir.empty())
  653. {
  654. show_usage_and_exit();
  655. }
  656. app_state->progress_value = 0.0;
  657. app_state->activity_mode = FALSE;
  658. app_state->failure = FALSE;
  659. translate_init(app_state->strings_dirs, app_state->strings_file);
  660. }
  661. int main(int argc, char **argv)
  662. {
  663. UpdaterAppState app_state;
  664. GThread *worker_thread;
  665. parse_args_and_init(argc, argv, &app_state);
  666. // Initialize logger, and rename old log file
  667. gDirUtilp->initAppDirs("SecondLife");
  668. LLError::initForApplication
  669. (gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
  670. std::string old_log_file = gDirUtilp->getExpandedFilename
  671. (LL_PATH_LOGS, "updater.log.old");
  672. std::string log_file = 
  673. gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log");
  674. LLFile::rename(log_file, old_log_file);
  675. LLError::logToFile(log_file);
  676. // initialize gthreads and gtk+
  677. if (!g_thread_supported())
  678. {
  679. g_thread_init(NULL);
  680. gdk_threads_init();
  681. }
  682. gtk_init(&argc, &argv);
  683. // create UI
  684. updater_app_ui_init(&app_state);
  685. //llinfos << "SAMPLE TRANSLATION IS: " << LLTrans::getString("LoginInProgress") << llendl;
  686. // create download thread
  687. worker_thread = g_thread_create
  688. (GThreadFunc(worker_thread_cb), &app_state, FALSE, NULL);
  689. gdk_threads_enter();
  690. gtk_main();
  691. gdk_threads_leave();
  692. return (app_state.failure == FALSE) ? 0 : 1;
  693. }