Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

790 lines
28 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: reason.c
  3. *
  4. * Copyright (c) 1985 - 2000, Microsoft Corporation
  5. *
  6. * This module contains the (private) APIs for the shutdown reason stuff.
  7. *
  8. * History:
  9. * ??-??-???? HughLeat Wrote it as part of msgina.dll
  10. * 11-15-2000 JasonSch Moved from msgina.dll to its new, temporary home in
  11. * user32.dll. Ultimately this code should live in
  12. * advapi32.dll, but that's contingent upon LoadString
  13. * being moved to ntdll.dll.
  14. \***************************************************************************/
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. #include <regstr.h>
  18. REASON_INITIALISER g_rgReasonInits[] = {
  19. { UCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_UNPLANNED_HARDWARE_MAINTENANCE_TITLE, IDS_REASON_HARDWARE_MAINTENANCE_DESCRIPTION },
  20. { PCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_PLANNED_HARDWARE_MAINTENANCE_TITLE, IDS_REASON_HARDWARE_MAINTENANCE_DESCRIPTION },
  21. { UCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_INSTALLATION, IDS_REASON_UNPLANNED_HARDWARE_INSTALLATION_TITLE, IDS_REASON_HARDWARE_INSTALLATION_DESCRIPTION },
  22. { PCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_INSTALLATION, IDS_REASON_PLANNED_HARDWARE_INSTALLATION_TITLE, IDS_REASON_HARDWARE_INSTALLATION_DESCRIPTION },
  23. // { UCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_UPGRADE_TITLE, IDS_REASON_OPERATINGSYSTEM_UPGRADE_DESCRIPTION },
  24. // { PCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE, IDS_REASON_PLANNED_OPERATINGSYSTEM_UPGRADE_TITLE, IDS_REASON_OPERATINGSYSTEM_UPGRADE_DESCRIPTION },
  25. { UCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_RECONFIG_TITLE, IDS_REASON_OPERATINGSYSTEM_RECONFIG_DESCRIPTION },
  26. { PCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG, IDS_REASON_PLANNED_OPERATINGSYSTEM_RECONFIG_TITLE, IDS_REASON_OPERATINGSYSTEM_RECONFIG_DESCRIPTION },
  27. { UCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_HUNG, IDS_REASON_APPLICATION_HUNG_TITLE, IDS_REASON_APPLICATION_HUNG_DESCRIPTION },
  28. { UCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_UNSTABLE, IDS_REASON_APPLICATION_UNSTABLE_TITLE, IDS_REASON_APPLICATION_UNSTABLE_DESCRIPTION },
  29. { PCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION, IDS_REASON_PLANNED_APPLICATION_INSTALLATION_TITLE, IDS_REASON_APPLICATION_INSTALLATION_DESCRIPTION },
  30. { UCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_APPLICATION_MAINTENANCE_TITLE, IDS_REASON_APPLICATION_MAINTENANCE_DESCRIPTION },
  31. { PCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_APPLICATION_PM_TITLE, IDS_REASON_APPLICATION_PM_DESCRIPTION },
  32. { UCLEANUI | SHTDN_REASON_FLAG_COMMENT_REQUIRED | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER, IDS_REASON_UNPLANNED_OTHER_TITLE, IDS_REASON_OTHER_DESCRIPTION },
  33. { PCLEANUI | SHTDN_REASON_FLAG_COMMENT_REQUIRED | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER, IDS_REASON_PLANNED_OTHER_TITLE, IDS_REASON_OTHER_DESCRIPTION },
  34. { UDIRTYUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_BLUESCREEN, IDS_REASON_SYSTEMFAILURE_BLUESCREEN_TITLE, IDS_REASON_SYSTEMFAILURE_BLUESCREEN_DESCRIPTION },
  35. { UDIRTYUI | SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_CORDUNPLUGGED, IDS_REASON_POWERFAILURE_CORDUNPLUGGED_TITLE, IDS_REASON_POWERFAILURE_CORDUNPLUGGED_DESCRIPTION },
  36. { UDIRTYUI | SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_ENVIRONMENT, IDS_REASON_POWERFAILURE_ENVIRONMENT_TITLE, IDS_REASON_POWERFAILURE_ENVIRONMENT_DESCRIPTION },
  37. { UDIRTYUI | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_HUNG, IDS_REASON_OTHERFAILURE_HUNG_TITLE, IDS_REASON_OTHERFAILURE_HUNG_DESCRIPTION },
  38. { UDIRTYUI | SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER, IDS_REASON_UNPLANNED_OTHER_TITLE, IDS_REASON_OTHER_DESCRIPTION },
  39. { SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE, IDS_REASON_PLANNED_OPERATINGSYSTEM_UPGRADE_TITLE, IDS_REASON_OPERATINGSYSTEM_UPGRADE_DESCRIPTION },
  40. { SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SERVICEPACK, IDS_REASON_PLANNED_OPERATINGSYSTEM_SERVICEPACK_TITLE, IDS_REASON_OPERATINGSYSTEM_SERVICEPACK_DESCRIPTION },
  41. { SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_HOTFIX, IDS_REASON_PLANNED_OPERATINGSYSTEM_HOTFIX_TITLE, IDS_REASON_OPERATINGSYSTEM_HOTFIX_DESCRIPTION },
  42. { SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SECURITYFIX, IDS_REASON_PLANNED_OPERATINGSYSTEM_SECURITYFIX_TITLE, IDS_REASON_OPERATINGSYSTEM_SECURITYFIX_DESCRIPTION },
  43. { SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_HOTFIX, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_HOTFIX_TITLE, IDS_REASON_OPERATINGSYSTEM_HOTFIX_DESCRIPTION },
  44. { SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SECURITYFIX, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_SECURITYFIX_TITLE,IDS_REASON_OPERATINGSYSTEM_SECURITYFIX_DESCRIPTION },
  45. { SHTDN_REASON_LEGACY_API, IDS_REASON_LEGACY_API_TITLE, IDS_REASON_LEGACY_API_DESCRIPTION },
  46. { UCLEANUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
  47. { PCLEANUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
  48. { UDIRTYUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
  49. // { PDIRTYUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
  50. { UCLEANUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_NETWORK_CONNECTIVITY, IDS_REASON_LOSS_OF_NETWORK_TITLE, IDS_REASON_LOSS_OF_NETWORK_DESCRIPTION }
  51. };
  52. BOOL ReasonCodeNeedsComment(DWORD dwCode)
  53. {
  54. return (dwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED) != 0;
  55. }
  56. BOOL ReasonCodeNeedsBugID(DWORD dwCode)
  57. {
  58. return (dwCode & SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED) != 0;
  59. }
  60. /*
  61. * Here is the regular expression used to parse the user defined reason codes
  62. * in the registry:
  63. *
  64. * S -> 's' | 'S' { Set For Clean UI }
  65. * D -> 'd' | 'D' { Set For Dirty UI }
  66. * P -> 'p' | 'P' { Set Planned }
  67. * C -> 'c' | 'C' { Set Comment Required }
  68. * B -> 'b' | 'B' { Set Problem Id Required in Dirty Mode }
  69. *
  70. *
  71. * Delim -> ';'
  72. * Flag -> S | D | P | C | B
  73. * Flags -> ( Flag )*
  74. *
  75. * Digit -> '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | ''8' | '9'
  76. * Num -> Digit*
  77. * Maj -> Num { Set Major Code }
  78. * Min -> Num { Set Minor Code }
  79. *
  80. * ValidSentence -> Flags . Delim . Maj . Delim . Min
  81. *
  82. * All initial states are false for each flag and 0 for minor and major reason
  83. * codes.
  84. * If neither S nor D are specified it is invaliad.
  85. */
  86. BOOL
  87. ParseReasonCode(
  88. PWCHAR lpString,
  89. LPDWORD lpdwCode)
  90. {
  91. WCHAR c;
  92. UINT major = 0, minor = 0;
  93. UINT cnt = 0;
  94. if (!lpString || !lpdwCode)
  95. return FALSE;
  96. *lpdwCode = SHTDN_REASON_FLAG_USER_DEFINED;
  97. /*
  98. * Read the flags part.
  99. */
  100. c = *lpString;
  101. while ( c != 0 && c != L';') {
  102. switch( c ) {
  103. case L'P' :
  104. case L'p' :
  105. if (*lpdwCode & SHTDN_REASON_FLAG_PLANNED)
  106. return FALSE;
  107. *lpdwCode |= SHTDN_REASON_FLAG_PLANNED;
  108. break;
  109. case L'C' :
  110. case L'c' :
  111. if (*lpdwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED)
  112. return FALSE;
  113. *lpdwCode |= SHTDN_REASON_FLAG_COMMENT_REQUIRED;
  114. break;
  115. case L'S' :
  116. case L's' :
  117. if (*lpdwCode & SHTDN_REASON_FLAG_CLEAN_UI)
  118. return FALSE;
  119. *lpdwCode |= SHTDN_REASON_FLAG_CLEAN_UI;
  120. break;
  121. case L'D' :
  122. case L'd' :
  123. if (*lpdwCode & SHTDN_REASON_FLAG_DIRTY_UI)
  124. return FALSE;
  125. *lpdwCode |= SHTDN_REASON_FLAG_DIRTY_UI;
  126. break;
  127. case L'B' :
  128. case L'b' :
  129. if (*lpdwCode & SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED)
  130. return FALSE;
  131. *lpdwCode |= SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED;
  132. break;
  133. default :
  134. return FALSE;
  135. }
  136. c = *++lpString;
  137. }
  138. if (!(*lpdwCode & SHTDN_REASON_FLAG_DIRTY_UI)
  139. && (*lpdwCode & SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED)) {
  140. return FALSE;
  141. }
  142. if (!(*lpdwCode & SHTDN_REASON_FLAG_CLEAN_UI)
  143. && (*lpdwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED)) {
  144. return FALSE;
  145. }
  146. /*
  147. * You must have clean, or dirty or both flags set.
  148. */
  149. if (!(*lpdwCode & ( SHTDN_REASON_FLAG_CLEAN_UI | SHTDN_REASON_FLAG_DIRTY_UI))) {
  150. return FALSE;
  151. }
  152. /*
  153. * You must have reason code.
  154. */
  155. if (c == 0) {
  156. return FALSE;
  157. }
  158. if (c != L';') {
  159. return FALSE;
  160. }
  161. c = *++lpString; /* Skip delimiter.*/
  162. /*
  163. * Parse major reason
  164. */
  165. while (c != 0 && c != L';') {
  166. if (c < L'0' || c > L'9') {
  167. return FALSE;
  168. }
  169. cnt++;
  170. if (cnt > 3) {
  171. return FALSE;
  172. }
  173. major = major * 10 + c - L'0';
  174. c = *++lpString;
  175. }
  176. /*
  177. * major reason < 0x40 (64) is reserved for Microsoft.
  178. */
  179. if (major > 0xff || major < 0x40) {
  180. return FALSE;
  181. }
  182. *lpdwCode |= major << 16;
  183. /*
  184. * requires minor code.
  185. */
  186. if (c == 0) {
  187. return FALSE;
  188. }
  189. /*
  190. * Should have a delimiter
  191. */
  192. if (c != L';') {
  193. return FALSE;
  194. }
  195. c = *++lpString; /* Skip delimiter. */
  196. /*
  197. * Parse minor reason
  198. */
  199. cnt = 0;
  200. while (c != 0) {
  201. if (c < L'0' || c > L'9') {
  202. return FALSE;
  203. }
  204. cnt++;
  205. if (cnt > 5) {
  206. return FALSE;
  207. }
  208. minor = minor * 10 + c - L'0';
  209. c = *++lpString;
  210. }
  211. if (minor > 0xffff) {
  212. return FALSE;
  213. }
  214. *lpdwCode |= minor;
  215. return TRUE;
  216. }
  217. int
  218. __cdecl
  219. CompareReasons(
  220. CONST VOID *A,
  221. CONST VOID *B)
  222. {
  223. REASON *a = *(REASON **)A;
  224. REASON *b = *(REASON **)B;
  225. /*
  226. * Shift the planned bit out and put it back in the bottom.
  227. * Ignore all ui bits.
  228. */
  229. DWORD dwA = ((a->dwCode & SHTDN_REASON_VALID_BIT_MASK ) << 1) + !!(a->dwCode & SHTDN_REASON_FLAG_PLANNED);
  230. DWORD dwB = ((b->dwCode & SHTDN_REASON_VALID_BIT_MASK ) << 1) + !!(b->dwCode & SHTDN_REASON_FLAG_PLANNED);
  231. if (dwA < dwB) {
  232. return -1;
  233. } else if (dwA == dwB) {
  234. return 0;
  235. } else {
  236. return 1;
  237. }
  238. }
  239. BOOL
  240. SortReasonArray(
  241. REASONDATA *pdata)
  242. {
  243. qsort(pdata->rgReasons, pdata->cReasons, sizeof(REASON *), CompareReasons);
  244. return TRUE;
  245. }
  246. /*
  247. * Append a reason to the reason array.
  248. *
  249. * Return -1 on error, 1 on success, 0 if dup.
  250. */
  251. INT
  252. AppendReason(
  253. REASONDATA *pdata,
  254. REASON *reason)
  255. {
  256. int i;
  257. if (!pdata || !reason) {
  258. return -1;
  259. }
  260. /*
  261. * Remove dup here.
  262. */
  263. if ( reason->dwCode & SHTDN_REASON_FLAG_USER_DEFINED) {
  264. for (i = 0; i < pdata->cReasons; ++i) {
  265. if ( (pdata->rgReasons[i]->dwCode & SHTDN_REASON_FLAG_PLANNED) == (reason->dwCode & SHTDN_REASON_FLAG_PLANNED)) {
  266. if (((pdata->rgReasons[i]->dwCode & SHTDN_REASON_FLAG_CLEAN_UI) && (reason->dwCode & SHTDN_REASON_FLAG_CLEAN_UI))
  267. || ((pdata->rgReasons[i]->dwCode & SHTDN_REASON_FLAG_DIRTY_UI) && (reason->dwCode & SHTDN_REASON_FLAG_DIRTY_UI))) {
  268. if (((pdata->rgReasons[i]->dwCode & 0x00ffffff) == (reason->dwCode & 0x00ffffff))
  269. || (_wcsicmp(pdata->rgReasons[i]->szName, reason->szName) == 0)) {
  270. UserLocalFree(reason);
  271. return 0;
  272. }
  273. }
  274. }
  275. }
  276. }
  277. /*
  278. * Insert the new reason into the list.
  279. */
  280. if (pdata->cReasons < pdata->cReasonCapacity) {
  281. pdata->rgReasons[pdata->cReasons++] = reason;
  282. } else {
  283. /*
  284. * Need to expand the list.
  285. */
  286. REASON **temp_list = (REASON **)UserLocalAlloc(0, sizeof(REASON*)*(pdata->cReasonCapacity*2+1));
  287. if (temp_list == NULL) {
  288. return -1;
  289. }
  290. for (i = 0; i < pdata->cReasons; ++i) {
  291. temp_list[i] = pdata->rgReasons[i];
  292. }
  293. temp_list[pdata->cReasons++] = reason;
  294. if (pdata->rgReasons ) {
  295. UserLocalFree(pdata->rgReasons);
  296. }
  297. pdata->rgReasons = temp_list;
  298. pdata->cReasonCapacity = pdata->cReasonCapacity*2+1;
  299. }
  300. return 1;
  301. }
  302. BOOL
  303. LoadReasonStrings(
  304. int idStringName,
  305. int idStringDesc,
  306. REASON *reason)
  307. {
  308. return (LoadStringW(hmodUser, idStringName, reason->szName, ARRAYSIZE(reason->szName)) != 0)
  309. && (LoadStringW(hmodUser, idStringDesc, reason->szDesc, ARRAYSIZE(reason->szDesc)) != 0);
  310. }
  311. BOOL
  312. BuildPredefinedReasonArray(
  313. REASONDATA *pdata,
  314. BOOL forCleanUI,
  315. BOOL forDirtyUI)
  316. {
  317. int i;
  318. DWORD code;
  319. /*
  320. * If forCleanUI and forDirtyUI are both false, we
  321. * actually will load all reasons (UI or non UI).
  322. */
  323. for (i = 0; i < ARRAYSIZE(g_rgReasonInits); ++i) {
  324. REASON *temp_reason = NULL;
  325. code = g_rgReasonInits[ i ].dwCode;
  326. if ((!forCleanUI && !forDirtyUI) ||
  327. (forCleanUI && (code & SHTDN_REASON_FLAG_CLEAN_UI)) ||
  328. (forDirtyUI && (code & SHTDN_REASON_FLAG_DIRTY_UI))) {
  329. temp_reason = (REASON *)UserLocalAlloc(0, sizeof(REASON));
  330. if (temp_reason == NULL) {
  331. return FALSE;
  332. }
  333. temp_reason->dwCode = g_rgReasonInits[i].dwCode;
  334. if (!LoadReasonStrings(g_rgReasonInits[i].dwNameId, g_rgReasonInits[i].dwDescId, temp_reason)) {
  335. UserLocalFree(temp_reason);
  336. return FALSE;
  337. }
  338. if (AppendReason(pdata, temp_reason) == -1) {
  339. UserLocalFree(temp_reason);
  340. return FALSE;
  341. }
  342. }
  343. }
  344. return TRUE;
  345. }
  346. BOOL
  347. BuildUserDefinedReasonArray(
  348. REASONDATA *pdata,
  349. HKEY hReliabilityKey,
  350. BOOL forCleanUI,
  351. BOOL forDirtyUI
  352. )
  353. {
  354. UINT i;
  355. HKEY hKey = NULL;
  356. DWORD num_values;
  357. DWORD max_value_len;
  358. DWORD rc;
  359. WCHAR szUserDefined[] = L"UserDefined";
  360. UINT uiReasons = 0;
  361. //
  362. // sizeof L"UserDefined" + sizeof(LangID)(Max Chars of ULONG)(11) + 1 ('\')
  363. //
  364. #define MAX_USER_LOCALE_KEY_SIZE (ARRAY_SIZE(szUserDefined) + 12)
  365. WCHAR szUserLocale[MAX_USER_LOCALE_KEY_SIZE + 1];
  366. _snwprintf (szUserLocale, MAX_USER_LOCALE_KEY_SIZE, L"%s\\%d", szUserDefined, GetSystemDefaultLangID());
  367. szUserLocale[ MAX_USER_LOCALE_KEY_SIZE ] = 0;
  368. #undef MAX_USER_LOCALE_KEY_SIZE
  369. if (!pdata || !hReliabilityKey) {
  370. return FALSE;
  371. }
  372. /*
  373. * First try to open the system locale key.
  374. */
  375. rc = RegOpenKeyExW(hReliabilityKey,
  376. szUserLocale,
  377. 0, KEY_READ, &hKey);
  378. /*
  379. * Fall back on userdefined key.
  380. */
  381. if (rc != ERROR_SUCCESS){
  382. rc = RegCreateKeyExW(hReliabilityKey,
  383. szUserDefined,
  384. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ,
  385. NULL, &hKey, NULL);
  386. }
  387. if (rc != ERROR_SUCCESS) {
  388. goto fail;
  389. }
  390. rc = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &num_values, NULL, &max_value_len, NULL, NULL);
  391. if (rc != ERROR_SUCCESS) {
  392. goto fail;
  393. }
  394. /*
  395. * Read the user defined reasons.
  396. */
  397. for (i = 0; i < num_values && uiReasons < MAX_NUM_REASONS; ++i) {
  398. WCHAR name_buffer[ 16 ]; /* the value name for reason should be something like "ccccc;ddd;ddddd". */
  399. DWORD name_buffer_len = ARRAY_SIZE(name_buffer); /* name buffer len is in WCHAR */
  400. DWORD type;
  401. WCHAR data[MAX_REASON_NAME_LEN + MAX_REASON_DESC_LEN + 1]; /* Space for name, desc and a null char.*/
  402. DWORD data_len = (MAX_REASON_NAME_LEN + MAX_REASON_DESC_LEN + 1) * sizeof(WCHAR); /* Data len is in bytes */
  403. DWORD code;
  404. REASON *temp_reason = NULL;
  405. LPWSTR szTemp = NULL;
  406. rc = RegEnumValueW(hKey, i, name_buffer, &name_buffer_len, NULL, &type, (LPBYTE)data, &data_len);
  407. if (rc != ERROR_SUCCESS) {
  408. continue;
  409. }
  410. if (type != REG_MULTI_SZ) { /* Not a multi_string - ignore it.*/
  411. continue;
  412. }
  413. // Tidy up the output from RegEnuValueW.
  414. name_buffer[ARRAY_SIZE(name_buffer) - 1] = 0;
  415. if (data_len/sizeof(WCHAR) + 2 <= ARRAY_SIZE(data)) {
  416. // A reason might not have any description, an extra
  417. // NULL is necessary under some sitations.
  418. data[data_len/sizeof(WCHAR)] = 0;
  419. data[data_len/sizeof(WCHAR) + 1] = 0;
  420. }
  421. if (!ParseReasonCode(name_buffer, &code)) { /* malformed reason code */
  422. continue;
  423. }
  424. /*
  425. * Neither title nor desc can be longer than specified.
  426. */
  427. if (lstrlenW (data) > MAX_REASON_NAME_LEN - 1
  428. || lstrlenW (data + lstrlenW(data) + 1) > MAX_REASON_DESC_LEN - 1) {
  429. continue;
  430. }
  431. if ( (!forCleanUI && !forDirtyUI)
  432. || (forCleanUI && (code & SHTDN_REASON_FLAG_CLEAN_UI) != 0)
  433. || (forDirtyUI && (code & SHTDN_REASON_FLAG_DIRTY_UI) != 0)) {
  434. temp_reason = (REASON *)UserLocalAlloc(0, sizeof(REASON));
  435. if (temp_reason == NULL) {
  436. goto fail;
  437. }
  438. temp_reason->dwCode = code;
  439. // name and desc lenghts are checked already, should be ok here.
  440. lstrcpyW(temp_reason->szName, data);
  441. lstrcpyW(temp_reason->szDesc, data + lstrlenW(data) + 1);
  442. /*
  443. * Don't allow reasons without a title.
  444. */
  445. if (lstrlenW(temp_reason->szName) == 0) {
  446. UserLocalFree(temp_reason);
  447. } else {
  448. /*
  449. * Dont allow reason title with only spaces.
  450. */
  451. szTemp = temp_reason->szName;
  452. while (*szTemp && iswspace (*szTemp)) {
  453. szTemp++;
  454. }
  455. if (! *szTemp) {
  456. UserLocalFree(temp_reason);
  457. } else {
  458. INT ret = AppendReason(pdata, temp_reason);
  459. if (ret == -1) {
  460. UserLocalFree(temp_reason);
  461. goto fail;
  462. } else if (ret == 1) {
  463. uiReasons++;
  464. } // else do nothing when dup.
  465. }
  466. }
  467. }
  468. }
  469. RegCloseKey(hKey);
  470. return TRUE;
  471. fail :
  472. if (hKey != NULL) {
  473. RegCloseKey(hKey);
  474. }
  475. return FALSE;
  476. }
  477. BOOL
  478. BuildReasonArray(
  479. REASONDATA *pdata,
  480. BOOL forCleanUI,
  481. BOOL forDirtyUI)
  482. {
  483. HKEY hReliabilityKey;
  484. DWORD ignore_predefined_reasons = FALSE;
  485. DWORD value_size = sizeof(DWORD);
  486. DWORD rc;
  487. DWORD dwPredefinedReasons;
  488. if (!pdata) {
  489. return FALSE;
  490. }
  491. pdata->rgReasons = (REASON **)UserLocalAlloc(0, sizeof(REASON *) * ARRAYSIZE(g_rgReasonInits));
  492. if (pdata->rgReasons == NULL) {
  493. return FALSE;
  494. }
  495. pdata->cReasonCapacity = ARRAYSIZE(g_rgReasonInits);
  496. pdata->cReasons = 0;
  497. if (!BuildPredefinedReasonArray(pdata, forCleanUI, forDirtyUI)) {
  498. DestroyReasons(pdata);
  499. return FALSE;
  500. }
  501. dwPredefinedReasons = pdata->cReasons;
  502. /*
  503. * Open the reliability key.
  504. */
  505. rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
  506. REGSTR_PATH_RELIABILITY,
  507. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL,
  508. &hReliabilityKey, NULL);
  509. if (rc == ERROR_SUCCESS) {
  510. rc = RegQueryValueEx(hReliabilityKey, REGSTR_VAL_SHUTDOWN_IGNORE_PREDEFINED, NULL, NULL, (UCHAR *)&ignore_predefined_reasons, &value_size);
  511. if (rc != ERROR_SUCCESS) {
  512. ignore_predefined_reasons = FALSE;
  513. }
  514. if (!BuildUserDefinedReasonArray(pdata, hReliabilityKey, forCleanUI, forDirtyUI) || pdata->cReasons == 0) {
  515. ignore_predefined_reasons = FALSE;
  516. }
  517. RegCloseKey(hReliabilityKey);
  518. }
  519. // if ignore predefined reasons, we need to remove them from the list.
  520. // But we do this only if there is some custom reasons.
  521. if (ignore_predefined_reasons && pdata->cReasons > (int)dwPredefinedReasons) {
  522. DWORD i;
  523. for (i = 0; i < dwPredefinedReasons; i++) {
  524. UserLocalFree(pdata->rgReasons[i]);
  525. }
  526. memmove(pdata->rgReasons, &pdata->rgReasons[dwPredefinedReasons], sizeof(REASON*)*(pdata->cReasons-dwPredefinedReasons));
  527. pdata->cReasons -= dwPredefinedReasons;
  528. }
  529. return SortReasonArray(pdata);
  530. }
  531. VOID
  532. DestroyReasons(
  533. REASONDATA *pdata)
  534. {
  535. int i;
  536. if (pdata->rgReasons != 0) {
  537. for (i = 0; i < pdata->cReasons; ++i) {
  538. UserLocalFree( pdata->rgReasons[i]);
  539. }
  540. UserLocalFree(pdata->rgReasons);
  541. pdata->rgReasons = 0;
  542. }
  543. pdata->cReasons = 0;
  544. }
  545. /*
  546. * Get the title from the reason code.
  547. * Returns FALSE on error, TRUE otherwise.
  548. *
  549. * If the reason code cannot be found, then it fills the title with a default
  550. * string.
  551. */
  552. BOOL
  553. GetReasonTitleFromReasonCode(
  554. DWORD code,
  555. WCHAR *lpTitle,
  556. DWORD dwTitleLen)
  557. {
  558. REASONDATA data;
  559. DWORD dwFlagBits = SHTDN_REASON_FLAG_CLEAN_UI | SHTDN_REASON_FLAG_DIRTY_UI;
  560. int i;
  561. if (lpTitle == NULL || dwTitleLen == 0) {
  562. return FALSE;
  563. }
  564. /*
  565. * Load all of the reasons (UI or non UI).
  566. */
  567. if (BuildReasonArray(&data, FALSE, FALSE) == FALSE) {
  568. return FALSE;
  569. }
  570. /*
  571. * Try to find the reason.
  572. */
  573. for (i = 0; i < data.cReasons; ++i) {
  574. if ((code & SHTDN_REASON_VALID_BIT_MASK) == (data.rgReasons[i]->dwCode & SHTDN_REASON_VALID_BIT_MASK)) { // Check valid bits.
  575. if ((!(code & dwFlagBits) && !(data.rgReasons[i]->dwCode & dwFlagBits))
  576. || (code & SHTDN_REASON_FLAG_CLEAN_UI && data.rgReasons[i]->dwCode & SHTDN_REASON_FLAG_CLEAN_UI)
  577. || (code & SHTDN_REASON_FLAG_DIRTY_UI && data.rgReasons[i]->dwCode & SHTDN_REASON_FLAG_DIRTY_UI) ) { // check flag bits.
  578. lstrcpynW(lpTitle, data.rgReasons[i]->szName, dwTitleLen);
  579. lpTitle[dwTitleLen - 1] = '\0';
  580. DestroyReasons(&data);
  581. return TRUE;
  582. }
  583. }
  584. }
  585. /*
  586. * Reason not found. Load the default string and return that.
  587. */
  588. DestroyReasons(&data);
  589. return (LoadStringW(hmodUser, IDS_REASON_DEFAULT_TITLE, lpTitle, dwTitleLen) != 0);
  590. }
  591. /*
  592. * Check to see if SET is enabled.
  593. * return TRUE if yes, FALSE if no.
  594. * Currently SET is controled by policy, but we need to handle the case where
  595. * the policy is not configured as in a clean setup case.
  596. */
  597. BOOL
  598. IsSETEnabled(
  599. VOID)
  600. {
  601. HKEY hKey;
  602. DWORD rc;
  603. DWORD ShowReasonUI = 0;
  604. OSVERSIONINFOEX osVersionInfoEx;
  605. rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RELIABILITY_POLICY, 0, KEY_QUERY_VALUE, &hKey);
  606. if (rc == ERROR_SUCCESS) {
  607. DWORD ValueSize = sizeof (DWORD);
  608. rc = RegQueryValueEx(hKey, REGSTR_PATH_RELIABILITY_POLICY_SHUTDOWNREASONUI, NULL, NULL, (UCHAR*)&ShowReasonUI, &ValueSize);
  609. /*
  610. * Now check the sku to decide whether we should show the dialog
  611. */
  612. if (rc == ERROR_SUCCESS) {
  613. if (ShowReasonUI != POLICY_SHOWREASONUI_NEVER && ShowReasonUI != POLICY_SHOWREASONUI_ALWAYS) {
  614. osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  615. if (GetVersionEx((LPOSVERSIONINFOW)&osVersionInfoEx)) {
  616. /*
  617. * if ShowReasonUI is anything other than 2 or 3, we think it is 0.
  618. */
  619. switch (osVersionInfoEx.wProductType) {
  620. case VER_NT_WORKSTATION:
  621. if (ShowReasonUI == POLICY_SHOWREASONUI_WORKSTATIONONLY) {
  622. ShowReasonUI = 1;
  623. } else {
  624. ShowReasonUI = 0;
  625. }
  626. break;
  627. default:
  628. if (ShowReasonUI == POLICY_SHOWREASONUI_SERVERONLY) {
  629. ShowReasonUI = 1;
  630. } else {
  631. ShowReasonUI = 0;
  632. }
  633. break;
  634. }
  635. }
  636. } else {
  637. ShowReasonUI = ShowReasonUI == POLICY_SHOWREASONUI_ALWAYS ? 1 : 0;
  638. }
  639. } else if (rc == ERROR_FILE_NOT_FOUND) {
  640. /*
  641. * Try to check the ShutdownReasonOn. if absent, it is not configured.
  642. * if it is there, the value must be 0.
  643. */
  644. DWORD dwSROn;
  645. rc = RegQueryValueEx(hKey, L"ShutdownReasonOn", NULL, NULL, (UCHAR*)&dwSROn, &ValueSize);
  646. if (rc == ERROR_FILE_NOT_FOUND) {
  647. osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  648. if (GetVersionEx((LPOSVERSIONINFOW)&osVersionInfoEx)) {
  649. switch (osVersionInfoEx.wProductType) {
  650. case VER_NT_WORKSTATION:
  651. ShowReasonUI = 0;
  652. break;
  653. default:
  654. ShowReasonUI = 1;
  655. break;
  656. }
  657. }
  658. }
  659. }
  660. RegCloseKey (hKey);
  661. } else if (rc == ERROR_FILE_NOT_FOUND) { // Policy not configured.
  662. osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  663. if (GetVersionEx((LPOSVERSIONINFOW)&osVersionInfoEx)) {
  664. switch (osVersionInfoEx.wProductType) {
  665. case VER_NT_WORKSTATION:
  666. ShowReasonUI = 0;
  667. break;
  668. default:
  669. ShowReasonUI = 1;
  670. break;
  671. }
  672. }
  673. }
  674. return (ShowReasonUI != 0);
  675. }