PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/linux_updater/linux_updater.cpp

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