1 /*
2  * PROJECT:     ReactOS Services
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/applications/mscutils/servman/propsheet_recovery.c
5  * PURPOSE:     Recovery property page
6  * COPYRIGHT:   Eric Kohl
7  */
8 
9 #include "precomp.h"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 typedef struct _RECOVERYDATA
15 {
16     ENUM_SERVICE_STATUS_PROCESS *pService;
17     LPSERVICE_FAILURE_ACTIONS pServiceFailure;
18     BOOL bChanged;
19 } RECOVERYDATA, *PRECOVERYDATA;
20 
21 static
22 VOID
23 InitRecoveryPage(
24     HWND hwndDlg)
25 {
26     LPWSTR lpAction;
27     INT id;
28 
29     for (id = IDS_NO_ACTION; id <= IDS_RESTART_COMPUTER; id++)
30     {
31         if (AllocAndLoadString(&lpAction,
32                                hInstance,
33                                id))
34         {
35             SendDlgItemMessageW(hwndDlg,
36                                 IDC_FIRST_FAILURE,
37                                 CB_ADDSTRING,
38                                 0,
39                                 (LPARAM)lpAction);
40 
41             SendDlgItemMessageW(hwndDlg,
42                                 IDC_SECOND_FAILURE,
43                                 CB_ADDSTRING,
44                                 0,
45                                 (LPARAM)lpAction);
46 
47             SendDlgItemMessageW(hwndDlg,
48                                 IDC_SUBSEQUENT_FAILURES,
49                                 CB_ADDSTRING,
50                                 0,
51                                 (LPARAM)lpAction);
52 
53             LocalFree(lpAction);
54         }
55     }
56 
57     SendDlgItemMessageW(hwndDlg,
58                         IDC_FIRST_FAILURE,
59                         CB_SETCURSEL,
60                         0,
61                         0);
62 
63     SendDlgItemMessageW(hwndDlg,
64                         IDC_SECOND_FAILURE,
65                         CB_SETCURSEL,
66                         0,
67                         0);
68 
69     SendDlgItemMessageW(hwndDlg,
70                         IDC_SUBSEQUENT_FAILURES,
71                         CB_SETCURSEL,
72                         0,
73                         0);
74 
75     SendDlgItemMessageW(hwndDlg,
76                         IDC_RESET_TIME,
77                         WM_SETTEXT,
78                         0,
79                         (LPARAM)L"0");
80 
81     SendDlgItemMessageW(hwndDlg,
82                         IDC_RESTART_TIME,
83                         WM_SETTEXT,
84                         0,
85                         (LPARAM)L"1");
86 
87     for (id = IDC_RESTART_TEXT1; id <= IDC_RESTART_OPTIONS; id++)
88         EnableWindow(GetDlgItem(hwndDlg, id), FALSE);
89 }
90 
91 
92 static
93 BOOL
94 GetServiceFailure(
95     PRECOVERYDATA pRecoveryData)
96 {
97     LPSERVICE_FAILURE_ACTIONS pServiceFailure = NULL;
98     SC_HANDLE hManager = NULL;
99     SC_HANDLE hService = NULL;
100     BOOL bResult = TRUE;
101     DWORD cbBytesNeeded = 0;
102 
103     hManager = OpenSCManager(NULL,
104                              NULL,
105                              SC_MANAGER_CONNECT);
106     if (hManager == NULL)
107     {
108         bResult = FALSE;
109         goto done;
110     }
111 
112     hService = OpenService(hManager, pRecoveryData->pService->lpServiceName, SERVICE_QUERY_CONFIG);
113     if (hService == NULL)
114     {
115         bResult = FALSE;
116         goto done;
117     }
118 
119     if (!QueryServiceConfig2(hService,
120                              SERVICE_CONFIG_FAILURE_ACTIONS,
121                              NULL,
122                              0,
123                              &cbBytesNeeded))
124     {
125         if (cbBytesNeeded == 0)
126         {
127             bResult = FALSE;
128             goto done;
129         }
130     }
131 
132     pServiceFailure = HeapAlloc(GetProcessHeap(), 0, cbBytesNeeded);
133     if (pServiceFailure == NULL)
134     {
135         SetLastError(ERROR_OUTOFMEMORY);
136         bResult = FALSE;
137         goto done;
138     }
139 
140     if (!QueryServiceConfig2(hService,
141                              SERVICE_CONFIG_FAILURE_ACTIONS,
142                              (LPBYTE)pServiceFailure,
143                              cbBytesNeeded,
144                              &cbBytesNeeded))
145     {
146         bResult = FALSE;
147         goto done;
148     }
149 
150     pRecoveryData->pServiceFailure = pServiceFailure;
151 
152 done:
153     if (bResult == FALSE && pServiceFailure != NULL)
154         HeapFree(GetProcessHeap(), 0, pServiceFailure);
155 
156     if (hService)
157         CloseServiceHandle(hService);
158 
159     if (hManager)
160         CloseServiceHandle(hManager);
161 
162     return bResult;
163 }
164 
165 static
166 VOID
167 ShowFailureActions(
168     HWND hwndDlg,
169     PRECOVERYDATA pRecoveryData)
170 {
171     WCHAR szBuffer[256];
172     PWSTR startPtr, endPtr;
173     INT index, id, length;
174     DWORD i;
175 
176     for (i = 0; i < min(pRecoveryData->pServiceFailure->cActions, 3); i++)
177     {
178         index = -1;
179 
180         switch (pRecoveryData->pServiceFailure->lpsaActions[i].Type)
181         {
182             case SC_ACTION_NONE:
183                 index = 0;
184                 break;
185 
186             case SC_ACTION_RESTART:
187                 index = 1;
188 
189                 wsprintf(szBuffer, L"%lu", pRecoveryData->pServiceFailure->lpsaActions[i].Delay / 60000);
190                 SendDlgItemMessageW(hwndDlg,
191                                     IDC_RESTART_TIME,
192                                     WM_SETTEXT,
193                                     0,
194                                     (LPARAM)szBuffer);
195 
196                 for (id = IDC_RESTART_TEXT1; id <= IDC_RESTART_TEXT2; id++)
197                      EnableWindow(GetDlgItem(hwndDlg, id), TRUE);
198                 break;
199 
200             case SC_ACTION_REBOOT:
201                 index = 3;
202 
203                 EnableWindow(GetDlgItem(hwndDlg, IDC_RESTART_OPTIONS), TRUE);
204                 break;
205 
206             case SC_ACTION_RUN_COMMAND:
207                 index = 2;
208 
209                 for (id = IDC_RUN_GROUPBOX; id <= IDC_ADD_FAILCOUNT; id++)
210                     EnableWindow(GetDlgItem(hwndDlg, id), TRUE);
211                 break;
212         }
213 
214         if (index != -1)
215         {
216             SendDlgItemMessageW(hwndDlg,
217                                 IDC_FIRST_FAILURE + i,
218                                 CB_SETCURSEL,
219                                 index,
220                                 0);
221         }
222     }
223 
224     wsprintf(szBuffer, L"%lu", pRecoveryData->pServiceFailure->dwResetPeriod / 86400);
225     SendDlgItemMessageW(hwndDlg,
226                         IDC_RESET_TIME,
227                         WM_SETTEXT,
228                         0,
229                         (LPARAM)szBuffer);
230 
231     if (pRecoveryData->pServiceFailure->lpCommand != NULL)
232     {
233         ZeroMemory(szBuffer, sizeof(szBuffer));
234 
235         startPtr = pRecoveryData->pServiceFailure->lpCommand;
236         if (*startPtr == L'\"')
237             startPtr++;
238 
239         endPtr = wcschr(startPtr, L'\"');
240         if (endPtr != NULL)
241         {
242             length = (INT)((LONG_PTR)endPtr - (LONG_PTR)startPtr);
243             CopyMemory(szBuffer, startPtr, length);
244         }
245         else
246         {
247             wcscpy(szBuffer, startPtr);
248         }
249 
250         SendDlgItemMessageW(hwndDlg,
251                             IDC_PROGRAM,
252                             WM_SETTEXT,
253                             0,
254                             (LPARAM)szBuffer);
255 
256         ZeroMemory(szBuffer, sizeof(szBuffer));
257 
258         if (endPtr != NULL)
259         {
260             startPtr = endPtr + 1;
261             while (iswspace(*startPtr))
262                 startPtr++;
263 
264             endPtr = wcsstr(pRecoveryData->pServiceFailure->lpCommand, L"/fail=%1%");
265             if (endPtr != NULL)
266             {
267                 while (iswspace(*(endPtr - 1)))
268                     endPtr--;
269 
270                 length = (INT)((LONG_PTR)endPtr - (LONG_PTR)startPtr);
271                 CopyMemory(szBuffer, startPtr, length);
272             }
273             else
274             {
275                 wcscpy(szBuffer, startPtr);
276             }
277 
278             SendDlgItemMessageW(hwndDlg,
279                                 IDC_PARAMETERS,
280                                 WM_SETTEXT,
281                                 0,
282                                 (LPARAM)szBuffer);
283 
284             endPtr = wcsstr(pRecoveryData->pServiceFailure->lpCommand, L"/fail=%1%");
285             if (endPtr != NULL)
286             {
287                 SendDlgItemMessageW(hwndDlg,
288                                     IDC_ADD_FAILCOUNT,
289                                     BM_SETCHECK,
290                                     BST_CHECKED,
291                                     0);
292             }
293         }
294     }
295 }
296 
297 
298 static
299 VOID
300 UpdateFailureActions(
301     HWND hwndDlg,
302     PRECOVERYDATA pRecoveryData)
303 {
304     INT id, index;
305     BOOL bRestartService = FALSE;
306     BOOL bRunProgram = FALSE;
307     BOOL bRebootComputer = FALSE;
308 
309     for (id = IDC_FIRST_FAILURE; id <= IDC_SUBSEQUENT_FAILURES; id++)
310     {
311         index = SendDlgItemMessageW(hwndDlg,
312                                     id,
313                                     CB_GETCURSEL,
314                                     0,
315                                     0);
316         switch (index)
317         {
318             case 1: /* Restart Service */
319                 bRestartService = TRUE;
320                 break;
321 
322             case 2: /* Run Program */
323                 bRunProgram = TRUE;
324                 break;
325 
326             case 3: /* Reboot Computer */
327                 bRebootComputer = TRUE;
328                 break;
329         }
330     }
331 
332     for (id = IDC_RESTART_TEXT1; id <= IDC_RESTART_TEXT2; id++)
333          EnableWindow(GetDlgItem(hwndDlg, id), bRestartService);
334 
335     for (id = IDC_RUN_GROUPBOX; id <= IDC_ADD_FAILCOUNT; id++)
336          EnableWindow(GetDlgItem(hwndDlg, id), bRunProgram);
337 
338     EnableWindow(GetDlgItem(hwndDlg, IDC_RESTART_OPTIONS), bRebootComputer);
339 }
340 
341 
342 static
343 VOID
344 BrowseFile(
345     HWND hwndDlg)
346 {
347     WCHAR szFile[MAX_PATH] = {'\0'};
348     PWCHAR pszFilter = L"Executable Files (*.exe;*.com;*.cmd;*.bat)\0*.exe;*.com;*.cmd;*.bat\0";
349     OPENFILENAME ofn;
350 
351     ZeroMemory(&ofn, sizeof(ofn));
352 
353     ofn.lStructSize = sizeof(ofn);
354     ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_ENABLESIZING;
355     ofn.hwndOwner = hwndDlg;
356     ofn.lpstrFilter = pszFilter;
357     ofn.lpstrFile = szFile;
358     ofn.nMaxFile = MAX_PATH;
359 
360     if (GetOpenFileName(&ofn))
361     {
362         SendDlgItemMessageW(hwndDlg,
363                             IDC_PROGRAM,
364                             WM_SETTEXT,
365                             0,
366                             (LPARAM)szFile);
367     }
368 }
369 
370 
371 static
372 VOID
373 SetFailureActions(
374     HWND hwndDlg)
375 {
376     SERVICE_FAILURE_ACTIONS FailureActions;
377     BOOL bRestartService = FALSE;
378     BOOL bRunProgram = FALSE;
379     BOOL bRebootComputer = FALSE;
380     INT id, index;
381 
382     ZeroMemory(&FailureActions, sizeof(FailureActions));
383 
384     /* Count the number of valid failure actions */
385     for (id = IDC_FIRST_FAILURE; id <= IDC_SUBSEQUENT_FAILURES; id++)
386     {
387         index = SendDlgItemMessageW(hwndDlg,
388                                     id,
389                                     CB_GETCURSEL,
390                                     0,
391                                     0);
392         switch (index)
393         {
394             case 1: /* Restart Service */
395                 bRestartService = TRUE;
396                 FailureActions.cActions++;
397                 break;
398 
399             case 2: /* Run Program */
400                 bRunProgram = TRUE;
401                 FailureActions.cActions++;
402                 break;
403 
404             case 3: /* Reboot Computer */
405                 bRebootComputer = TRUE;
406                 FailureActions.cActions++;
407                 break;
408         }
409     }
410 
411     if (bRestartService)
412     {
413         // IDC_RESTART_TIME
414     }
415 
416     if (bRunProgram)
417     {
418         // IDC_RESTART_TIME
419     }
420 
421     if (bRebootComputer)
422     {
423         // IDC_RESTART_TIME
424     }
425 
426 
427 #if 0
428 typedef struct _SERVICE_FAILURE_ACTIONS {
429   DWORD     dwResetPeriod;
430   LPTSTR    lpRebootMsg;
431   LPTSTR    lpCommand;
432   DWORD     cActions;
433   SC_ACTION *lpsaActions;
434 } SERVICE_FAILURE_ACTIONS, *LPSERVICE_FAILURE_ACTIONS;
435 #endif
436 }
437 
438 
439 INT_PTR
440 CALLBACK
441 RecoveryPageProc(
442     HWND hwndDlg,
443     UINT uMsg,
444     WPARAM wParam,
445     LPARAM lParam)
446 {
447     PRECOVERYDATA pRecoveryData;
448 
449     /* Get the window context */
450     pRecoveryData = (PRECOVERYDATA)GetWindowLongPtr(hwndDlg,
451                                                     GWLP_USERDATA);
452     if (pRecoveryData == NULL && uMsg != WM_INITDIALOG)
453         return FALSE;
454 
455     switch (uMsg)
456     {
457         case WM_INITDIALOG:
458             pRecoveryData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RECOVERYDATA));
459             if (pRecoveryData != NULL)
460             {
461                 SetWindowLongPtr(hwndDlg,
462                                  GWLP_USERDATA,
463                                  (LONG_PTR)pRecoveryData);
464 
465                 pRecoveryData->pService = ((PSERVICEPROPSHEET)(((LPPROPSHEETPAGE)lParam)->lParam))->pService;
466 
467                 InitRecoveryPage(hwndDlg);
468 
469                 if (GetServiceFailure(pRecoveryData))
470                 {
471                     ShowFailureActions(hwndDlg, pRecoveryData);
472                 }
473             }
474             break;
475 
476         case WM_DESTROY:
477             if (pRecoveryData != NULL)
478             {
479                 if (pRecoveryData->pServiceFailure != NULL)
480                     HeapFree(GetProcessHeap(), 0, pRecoveryData->pServiceFailure);
481 
482                 HeapFree(GetProcessHeap(), 0, pRecoveryData);
483             }
484             break;
485 
486         case WM_COMMAND:
487             switch(LOWORD(wParam))
488             {
489                 case IDC_FIRST_FAILURE:
490                 case IDC_SECOND_FAILURE:
491                 case IDC_SUBSEQUENT_FAILURES:
492                     if (HIWORD(wParam) == CBN_SELCHANGE)
493                     {
494                         UpdateFailureActions(hwndDlg, pRecoveryData);
495                         pRecoveryData->bChanged = TRUE;
496                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
497                     }
498                     break;
499 
500                 case IDC_RESET_TIME:
501                 case IDC_RESTART_TIME:
502                 case IDC_PROGRAM:
503                 case IDC_PARAMETERS:
504                     if (HIWORD(wParam) == EN_CHANGE)
505                     {
506                         pRecoveryData->bChanged = TRUE;
507                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
508                     }
509                     break;
510 
511                 case IDC_ADD_FAILCOUNT:
512                     if (HIWORD(wParam) == BN_CLICKED)
513                     {
514                         pRecoveryData->bChanged = TRUE;
515                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
516                     }
517                     break;
518 
519                 case IDC_BROWSE_PROGRAM:
520                     BrowseFile(hwndDlg);
521                     break;
522 
523                 case IDC_RESTART_OPTIONS:
524                     break;
525             }
526             break;
527 
528         case WM_NOTIFY:
529             switch (((LPNMHDR)lParam)->code)
530             {
531                 case PSN_APPLY:
532                     if (pRecoveryData->bChanged)
533                     {
534                         SetFailureActions(hwndDlg);
535                         pRecoveryData->bChanged = FALSE;
536                     }
537                     break;
538             }
539             break;
540     }
541 
542     return FALSE;
543 }
544