platform_util_linux.cc 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/common/platform_util.h"
  5. #include <stdio.h>
  6. #include "base/cancelable_callback.h"
  7. #include "base/environment.h"
  8. #include "base/files/file_util.h"
  9. #include "base/nix/xdg_util.h"
  10. #include "base/process/kill.h"
  11. #include "base/process/launch.h"
  12. #include "base/threading/thread_restrictions.h"
  13. #include "ui/gtk/gtk_util.h"
  14. #include "url/gurl.h"
  15. #define ELECTRON_TRASH "ELECTRON_TRASH"
  16. namespace {
  17. // Descriptions pulled from https://linux.die.net/man/1/xdg-open
  18. std::string GetErrorDescription(int error_code) {
  19. switch (error_code) {
  20. case 1:
  21. return "Error in command line syntax";
  22. case 2:
  23. return "The item does not exist";
  24. case 3:
  25. return "A required tool could not be found";
  26. case 4:
  27. return "The action failed";
  28. default:
  29. return "";
  30. }
  31. }
  32. bool XDGUtil(const std::vector<std::string>& argv,
  33. const bool wait_for_exit,
  34. platform_util::OpenCallback callback) {
  35. base::LaunchOptions options;
  36. options.allow_new_privs = true;
  37. // xdg-open can fall back on mailcap which eventually might plumb through
  38. // to a command that needs a terminal. Set the environment variable telling
  39. // it that we definitely don't have a terminal available and that it should
  40. // bring up a new terminal if necessary. See "man mailcap".
  41. options.environment["MM_NOTTTY"] = "1";
  42. base::Process process = base::LaunchProcess(argv, options);
  43. if (!process.IsValid())
  44. return false;
  45. if (wait_for_exit) {
  46. base::ScopedAllowBaseSyncPrimitivesForTesting
  47. allow_sync; // required by WaitForExit
  48. int exit_code = -1;
  49. bool success = process.WaitForExit(&exit_code);
  50. if (!callback.is_null())
  51. std::move(callback).Run(GetErrorDescription(exit_code));
  52. return success ? (exit_code == 0) : false;
  53. }
  54. base::EnsureProcessGetsReaped(std::move(process));
  55. return true;
  56. }
  57. bool XDGOpen(const std::string& path,
  58. const bool wait_for_exit,
  59. platform_util::OpenCallback callback) {
  60. return XDGUtil({"xdg-open", path}, wait_for_exit, std::move(callback));
  61. }
  62. bool XDGEmail(const std::string& email, const bool wait_for_exit) {
  63. return XDGUtil({"xdg-email", email}, wait_for_exit,
  64. platform_util::OpenCallback());
  65. }
  66. } // namespace
  67. namespace platform_util {
  68. void ShowItemInFolder(const base::FilePath& full_path) {
  69. base::FilePath dir = full_path.DirName();
  70. if (!base::DirectoryExists(dir))
  71. return;
  72. XDGOpen(dir.value(), false, platform_util::OpenCallback());
  73. }
  74. void OpenPath(const base::FilePath& full_path, OpenCallback callback) {
  75. // This is async, so we don't care about the return value.
  76. XDGOpen(full_path.value(), true, std::move(callback));
  77. }
  78. void OpenExternal(const GURL& url,
  79. const OpenExternalOptions& options,
  80. OpenCallback callback) {
  81. // Don't wait for exit, since we don't want to wait for the browser/email
  82. // client window to close before returning
  83. if (url.SchemeIs("mailto")) {
  84. bool success = XDGEmail(url.spec(), false);
  85. std::move(callback).Run(success ? "" : "Failed to open path");
  86. } else {
  87. bool success = XDGOpen(url.spec(), false, platform_util::OpenCallback());
  88. std::move(callback).Run(success ? "" : "Failed to open path");
  89. }
  90. }
  91. bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
  92. std::unique_ptr<base::Environment> env(base::Environment::Create());
  93. // find the trash method
  94. std::string trash;
  95. if (!env->GetVar(ELECTRON_TRASH, &trash)) {
  96. // Determine desktop environment and set accordingly.
  97. const auto desktop_env(base::nix::GetDesktopEnvironment(env.get()));
  98. if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
  99. desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
  100. trash = "kioclient5";
  101. } else if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3) {
  102. trash = "kioclient";
  103. }
  104. }
  105. // build the invocation
  106. std::vector<std::string> argv;
  107. const auto& filename = full_path.value();
  108. if (trash == "kioclient5" || trash == "kioclient") {
  109. argv = {trash, "move", filename, "trash:/"};
  110. } else if (trash == "trash-cli") {
  111. argv = {"trash-put", filename};
  112. } else if (trash == "gvfs-trash") {
  113. argv = {"gvfs-trash", filename}; // deprecated, but still exists
  114. } else {
  115. argv = {"gio", "trash", filename};
  116. }
  117. return XDGUtil(argv, true, platform_util::OpenCallback());
  118. }
  119. void Beep() {
  120. // echo '\a' > /dev/console
  121. FILE* fp = fopen("/dev/console", "a");
  122. if (fp == nullptr) {
  123. fp = fopen("/dev/tty", "a");
  124. }
  125. if (fp != nullptr) {
  126. fprintf(fp, "\a");
  127. fclose(fp);
  128. }
  129. }
  130. bool GetDesktopName(std::string* setme) {
  131. return base::Environment::Create()->GetVar("CHROME_DESKTOP", setme);
  132. }
  133. } // namespace platform_util