/toolkit/xre/nsWindowsRestart.cpp

http://github.com/zpao/v8monkey · C++ · 327 lines · 213 code · 38 blank · 76 comment · 28 complexity · 3900729af384ea236de302c5bea33a10 MD5 · raw file

  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an "AS IS" basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is Mozilla XULRunner bootstrap.
  15. *
  16. * The Initial Developer of the Original Code is
  17. * Benjamin Smedberg <benjamin@smedbergs.us>.
  18. *
  19. * Portions created by the Initial Developer are Copyright (C) 2005
  20. * the Mozilla Foundation. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Robert Strong <robert.bugzilla@gmail.com>
  24. * Brian R. Bondy <netzen@gmail.com>
  25. *
  26. * Alternatively, the contents of this file may be used under the terms of
  27. * either the GNU General Public License Version 2 or later (the "GPL"), or
  28. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29. * in which case the provisions of the GPL or the LGPL are applicable instead
  30. * of those above. If you wish to allow use of your version of this file only
  31. * under the terms of either the GPL or the LGPL, and not to allow others to
  32. * use your version of this file under the terms of the MPL, indicate your
  33. * decision by deleting the provisions above and replace them with the notice
  34. * and other provisions required by the GPL or the LGPL. If you do not delete
  35. * the provisions above, a recipient may use your version of this file under
  36. * the terms of any one of the MPL, the GPL or the LGPL.
  37. *
  38. * ***** END LICENSE BLOCK ***** */
  39. // This file is not build directly. Instead, it is included in multiple
  40. // shared objects.
  41. #ifdef nsWindowsRestart_cpp
  42. #error "nsWindowsRestart.cpp is not a header file, and must only be included once."
  43. #else
  44. #define nsWindowsRestart_cpp
  45. #endif
  46. #include "nsUTF8Utils.h"
  47. #include <shellapi.h>
  48. // Needed for CreateEnvironmentBlock
  49. #include <userenv.h>
  50. #pragma comment(lib, "userenv.lib")
  51. /**
  52. * Get the length that the string will take and takes into account the
  53. * additional length if the string needs to be quoted and if characters need to
  54. * be escaped.
  55. */
  56. static int ArgStrLen(const PRUnichar *s)
  57. {
  58. int backslashes = 0;
  59. int i = wcslen(s);
  60. BOOL hasDoubleQuote = wcschr(s, L'"') != NULL;
  61. // Only add doublequotes if the string contains a space or a tab
  62. BOOL addDoubleQuotes = wcspbrk(s, L" \t") != NULL;
  63. if (addDoubleQuotes) {
  64. i += 2; // initial and final duoblequote
  65. }
  66. if (hasDoubleQuote) {
  67. while (*s) {
  68. if (*s == '\\') {
  69. ++backslashes;
  70. } else {
  71. if (*s == '"') {
  72. // Escape the doublequote and all backslashes preceding the doublequote
  73. i += backslashes + 1;
  74. }
  75. backslashes = 0;
  76. }
  77. ++s;
  78. }
  79. }
  80. return i;
  81. }
  82. /**
  83. * Copy string "s" to string "d", quoting the argument as appropriate and
  84. * escaping doublequotes along with any backslashes that immediately precede
  85. * doublequotes.
  86. * The CRT parses this to retrieve the original argc/argv that we meant,
  87. * see STDARGV.C in the MSVC CRT sources.
  88. *
  89. * @return the end of the string
  90. */
  91. static PRUnichar* ArgToString(PRUnichar *d, const PRUnichar *s)
  92. {
  93. int backslashes = 0;
  94. BOOL hasDoubleQuote = wcschr(s, L'"') != NULL;
  95. // Only add doublequotes if the string contains a space or a tab
  96. BOOL addDoubleQuotes = wcspbrk(s, L" \t") != NULL;
  97. if (addDoubleQuotes) {
  98. *d = '"'; // initial doublequote
  99. ++d;
  100. }
  101. if (hasDoubleQuote) {
  102. int i;
  103. while (*s) {
  104. if (*s == '\\') {
  105. ++backslashes;
  106. } else {
  107. if (*s == '"') {
  108. // Escape the doublequote and all backslashes preceding the doublequote
  109. for (i = 0; i <= backslashes; ++i) {
  110. *d = '\\';
  111. ++d;
  112. }
  113. }
  114. backslashes = 0;
  115. }
  116. *d = *s;
  117. ++d; ++s;
  118. }
  119. } else {
  120. wcscpy(d, s);
  121. d += wcslen(s);
  122. }
  123. if (addDoubleQuotes) {
  124. *d = '"'; // final doublequote
  125. ++d;
  126. }
  127. return d;
  128. }
  129. /**
  130. * Creates a command line from a list of arguments. The returned
  131. * string is allocated with "malloc" and should be "free"d.
  132. *
  133. * argv is UTF8
  134. */
  135. PRUnichar*
  136. MakeCommandLine(int argc, PRUnichar **argv)
  137. {
  138. int i;
  139. int len = 0;
  140. // The + 1 of the last argument handles the allocation for null termination
  141. for (i = 0; i < argc; ++i)
  142. len += ArgStrLen(argv[i]) + 1;
  143. // Protect against callers that pass 0 arguments
  144. if (len == 0)
  145. len = 1;
  146. PRUnichar *s = (PRUnichar*) malloc(len * sizeof(PRUnichar));
  147. if (!s)
  148. return NULL;
  149. PRUnichar *c = s;
  150. for (i = 0; i < argc; ++i) {
  151. c = ArgToString(c, argv[i]);
  152. if (i + 1 != argc) {
  153. *c = ' ';
  154. ++c;
  155. }
  156. }
  157. *c = '\0';
  158. return s;
  159. }
  160. /**
  161. * Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
  162. * can't link to updater.exe.
  163. */
  164. static PRUnichar*
  165. AllocConvertUTF8toUTF16(const char *arg)
  166. {
  167. // UTF16 can't be longer in units than UTF8
  168. int len = strlen(arg);
  169. PRUnichar *s = new PRUnichar[(len + 1) * sizeof(PRUnichar)];
  170. if (!s)
  171. return NULL;
  172. ConvertUTF8toUTF16 convert(s);
  173. convert.write(arg, len);
  174. convert.write_terminator();
  175. return s;
  176. }
  177. static void
  178. FreeAllocStrings(int argc, PRUnichar **argv)
  179. {
  180. while (argc) {
  181. --argc;
  182. delete [] argv[argc];
  183. }
  184. delete [] argv;
  185. }
  186. /**
  187. * Launch a child process with the specified arguments.
  188. * @note argv[0] is ignored
  189. * @note The form of this function that takes char **argv expects UTF-8
  190. */
  191. BOOL
  192. WinLaunchChild(const PRUnichar *exePath,
  193. int argc, PRUnichar **argv,
  194. HANDLE userToken = NULL);
  195. BOOL
  196. WinLaunchChild(const PRUnichar *exePath,
  197. int argc, char **argv,
  198. HANDLE userToken)
  199. {
  200. PRUnichar** argvConverted = new PRUnichar*[argc];
  201. if (!argvConverted)
  202. return FALSE;
  203. for (int i = 0; i < argc; ++i) {
  204. argvConverted[i] = AllocConvertUTF8toUTF16(argv[i]);
  205. if (!argvConverted[i]) {
  206. FreeAllocStrings(i, argvConverted);
  207. return FALSE;
  208. }
  209. }
  210. BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken);
  211. FreeAllocStrings(argc, argvConverted);
  212. return ok;
  213. }
  214. BOOL
  215. WinLaunchChild(const PRUnichar *exePath,
  216. int argc,
  217. PRUnichar **argv,
  218. HANDLE userToken)
  219. {
  220. PRUnichar *cl;
  221. BOOL ok;
  222. cl = MakeCommandLine(argc, argv);
  223. if (!cl) {
  224. return FALSE;
  225. }
  226. STARTUPINFOW si = {0};
  227. si.cb = sizeof(STARTUPINFOW);
  228. si.lpDesktop = L"winsta0\\Default";
  229. PROCESS_INFORMATION pi = {0};
  230. if (userToken == NULL) {
  231. ok = CreateProcessW(exePath,
  232. cl,
  233. NULL, // no special security attributes
  234. NULL, // no special thread attributes
  235. FALSE, // don't inherit filehandles
  236. 0, // creation flags
  237. NULL, // inherit my environment
  238. NULL, // use my current directory
  239. &si,
  240. &pi);
  241. } else {
  242. // Create an environment block for the process we're about to start using
  243. // the user's token.
  244. LPVOID environmentBlock = NULL;
  245. if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) {
  246. environmentBlock = NULL;
  247. }
  248. ok = CreateProcessAsUserW(userToken,
  249. exePath,
  250. cl,
  251. NULL, // no special security attributes
  252. NULL, // no special thread attributes
  253. FALSE, // don't inherit filehandles
  254. 0, // creation flags
  255. environmentBlock,
  256. NULL, // use my current directory
  257. &si,
  258. &pi);
  259. if (environmentBlock) {
  260. DestroyEnvironmentBlock(environmentBlock);
  261. }
  262. }
  263. if (ok) {
  264. CloseHandle(pi.hProcess);
  265. CloseHandle(pi.hThread);
  266. } else {
  267. LPVOID lpMsgBuf = NULL;
  268. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  269. FORMAT_MESSAGE_FROM_SYSTEM |
  270. FORMAT_MESSAGE_IGNORE_INSERTS,
  271. NULL,
  272. GetLastError(),
  273. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  274. (LPTSTR) &lpMsgBuf,
  275. 0,
  276. NULL);
  277. wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
  278. if (lpMsgBuf)
  279. LocalFree(lpMsgBuf);
  280. }
  281. free(cl);
  282. return ok;
  283. }