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.

877 lines
24 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. CfgAPI2.cxx
  5. Abstract:
  6. This file contains the Service Controller's extended Config API.
  7. RChangeServiceConfig2W
  8. RQueryServiceConfig2W
  9. COutBuf
  10. CUpdateOptionalString::Update
  11. CUpdateOptionalString::~CUpdateOptionalString
  12. PrintConfig2Parms
  13. Author:
  14. Anirudh Sahni (AnirudhS) 11-Oct-96
  15. Environment:
  16. User Mode - Win32
  17. Revision History:
  18. 11-Oct-1996 AnirudhS
  19. Created.
  20. --*/
  21. //
  22. // INCLUDES
  23. //
  24. #include "precomp.hxx"
  25. #include <tstr.h> // Unicode string macros
  26. #include <align.h> // COUNT_IS_ALIGNED
  27. #include <valid.h> // ACTION_TYPE_INVALID
  28. #include <sclib.h> // ScImagePathsMatch
  29. #include <scwow.h> // 32/64-bit interop structures
  30. #include "scconfig.h" // ScOpenServiceConfigKey, etc.
  31. #include "scsec.h" // ScPrivilegeCheckAndAudit
  32. #include "smartp.h" // CHeapPtr
  33. #if DBG == 1
  34. VOID
  35. PrintConfig2Parms(
  36. IN SC_RPC_HANDLE hService,
  37. IN SC_RPC_CONFIG_INFOW Info
  38. );
  39. #endif
  40. #define FLDREF(s,f) (s.f)
  41. //
  42. // Class definitions
  43. //
  44. //+-------------------------------------------------------------------------
  45. //
  46. // Class: COutBuf
  47. //
  48. // Purpose: Abstraction of an output buffer that is written sequentially
  49. //
  50. // History: 22-Nov-96 AnirudhS Created.
  51. //
  52. //--------------------------------------------------------------------------
  53. class COutBuf
  54. {
  55. public:
  56. COutBuf(LPBYTE lpBuffer) :
  57. _Start(lpBuffer),
  58. _Used(0)
  59. { }
  60. LPBYTE Next() const { return (_Start + _Used); }
  61. DWORD OffsetNext() const { return _Used; }
  62. void AddUsed(DWORD Bytes) { _Used += Bytes; }
  63. void AppendBytes(void * Source, DWORD Bytes)
  64. {
  65. RtlCopyMemory(Next(), Source, Bytes);
  66. AddUsed(Bytes);
  67. }
  68. private:
  69. LPBYTE _Start;
  70. DWORD _Used;
  71. };
  72. //+-------------------------------------------------------------------------
  73. //
  74. // Class: CUpdateOptionalString
  75. //
  76. // Purpose: An object of this class represents an update of an optional
  77. // string value in the registry. The update takes place when
  78. // the Update() method is called. When the object is destroyed
  79. // the operation is undone, unless the Commit() method has been
  80. // called.
  81. //
  82. // This class simplifies the writing of APIs like
  83. // ChangeServiceConfig2.
  84. //
  85. // History: 27-Nov-96 AnirudhS Created.
  86. //
  87. //--------------------------------------------------------------------------
  88. class CUpdateOptionalString
  89. {
  90. public:
  91. CUpdateOptionalString (HKEY Key, LPCWSTR ValueName) :
  92. _Key(Key),
  93. _ValueName(ValueName),
  94. _UndoNeeded(FALSE)
  95. { }
  96. ~CUpdateOptionalString();
  97. DWORD Update (LPCWSTR NewValue);
  98. void Commit ()
  99. { _UndoNeeded = FALSE; }
  100. private:
  101. HKEY _Key;
  102. LPCWSTR _ValueName;
  103. CHeapPtr< LPWSTR > _OldValue;
  104. BOOL _UndoNeeded;
  105. };
  106. DWORD
  107. CUpdateOptionalString::Update(
  108. IN LPCWSTR NewValue
  109. )
  110. /*++
  111. Routine Description:
  112. See class definition.
  113. --*/
  114. {
  115. // This method should be called only once in the object's lifetime
  116. SC_ASSERT(_UndoNeeded == FALSE && _OldValue == NULL);
  117. //
  118. // Read the old value.
  119. //
  120. DWORD Error = ScReadOptionalString(_Key, _ValueName, &_OldValue);
  121. if (Error != ERROR_SUCCESS)
  122. {
  123. return Error;
  124. }
  125. //
  126. // Write the new value. Note that NULL means no change.
  127. //
  128. Error = ScWriteOptionalString(_Key, _ValueName, NewValue);
  129. //
  130. // Remember whether the change needs to be undone.
  131. //
  132. if (Error == ERROR_SUCCESS && NewValue != NULL)
  133. {
  134. _UndoNeeded = TRUE;
  135. }
  136. return Error;
  137. }
  138. CUpdateOptionalString::~CUpdateOptionalString(
  139. )
  140. /*++
  141. Routine Description:
  142. See class definition.
  143. --*/
  144. {
  145. if (_UndoNeeded)
  146. {
  147. DWORD Error = ScWriteOptionalString(
  148. _Key,
  149. _ValueName,
  150. _OldValue ? _OldValue : L""
  151. );
  152. if (Error != ERROR_SUCCESS)
  153. {
  154. // Nothing we can do about it
  155. SC_LOG3(ERROR, "Couldn't roll back update to %ws value, error %lu."
  156. " Old value was \"%ws\".\n",
  157. _ValueName, Error, _OldValue);
  158. }
  159. }
  160. }
  161. DWORD
  162. RChangeServiceConfig2W(
  163. IN SC_RPC_HANDLE hService,
  164. IN SC_RPC_CONFIG_INFOW Info
  165. )
  166. /*++
  167. Routine Description:
  168. Arguments:
  169. Return Value:
  170. --*/
  171. {
  172. SC_LOG(CONFIG_API, "In RChangeServiceConfig2W for service handle %#lx\n", hService);
  173. #if DBG == 1
  174. PrintConfig2Parms(hService,
  175. Info);
  176. #endif // DBG == 1
  177. if (ScShutdownInProgress)
  178. {
  179. return ERROR_SHUTDOWN_IN_PROGRESS;
  180. }
  181. //
  182. // Check the handle.
  183. //
  184. if (!ScIsValidServiceHandle(hService))
  185. {
  186. return ERROR_INVALID_HANDLE;
  187. }
  188. //
  189. // Do we have permission to do this?
  190. //
  191. if (!RtlAreAllAccessesGranted(
  192. ((LPSC_HANDLE_STRUCT) hService)->AccessGranted,
  193. SERVICE_CHANGE_CONFIG
  194. ))
  195. {
  196. return ERROR_ACCESS_DENIED;
  197. }
  198. //
  199. // Lock database, as we want to add stuff without other threads tripping
  200. // on our feet.
  201. //
  202. CServiceRecordExclusiveLock RLock;
  203. //
  204. // Find the service record for this handle.
  205. //
  206. LPSERVICE_RECORD serviceRecord =
  207. ((LPSC_HANDLE_STRUCT) hService)->Type.ScServiceObject.ServiceRecord;
  208. SC_ASSERT(serviceRecord != NULL);
  209. SC_ASSERT(serviceRecord->Signature == SERVICE_SIGNATURE);
  210. //
  211. // Disallow this call if record is marked for delete.
  212. //
  213. if (DELETE_FLAG_IS_SET(serviceRecord))
  214. {
  215. return ERROR_SERVICE_MARKED_FOR_DELETE;
  216. }
  217. //-----------------------------
  218. //
  219. // Begin Updating the Registry
  220. //
  221. //-----------------------------
  222. HKEY ServiceNameKey = NULL;
  223. DWORD ApiStatus = ScOpenServiceConfigKey(
  224. serviceRecord->ServiceName,
  225. KEY_WRITE | KEY_READ,
  226. FALSE, // don't create if missing
  227. &ServiceNameKey
  228. );
  229. if (ApiStatus != NO_ERROR)
  230. {
  231. goto Cleanup;
  232. }
  233. switch (FLDREF(Info,dwInfoLevel))
  234. {
  235. //-----------------------------
  236. //
  237. // Service Description
  238. //
  239. //-----------------------------
  240. case SERVICE_CONFIG_DESCRIPTION:
  241. //
  242. // NULL means no change
  243. //
  244. if (FLDREF(Info,psd) == NULL)
  245. {
  246. ApiStatus = NO_ERROR;
  247. break;
  248. }
  249. ApiStatus = ScWriteDescription(ServiceNameKey, FLDREF(Info,psd->lpDescription));
  250. break;
  251. //-----------------------------
  252. //
  253. // Service Failure Actions
  254. //
  255. //-----------------------------
  256. case SERVICE_CONFIG_FAILURE_ACTIONS:
  257. {
  258. LPSERVICE_FAILURE_ACTIONSW psfa = FLDREF(Info,psfa);
  259. //
  260. // NULL means no change
  261. //
  262. if (psfa == NULL)
  263. {
  264. ApiStatus = NO_ERROR;
  265. break;
  266. }
  267. //
  268. // Validate the structure and permissions
  269. //
  270. if (psfa->lpsaActions != NULL &&
  271. psfa->cActions != 0)
  272. {
  273. //
  274. // These settings are only valid for Win32 services
  275. //
  276. if (! (serviceRecord->ServiceStatus.dwServiceType & SERVICE_WIN32))
  277. {
  278. ApiStatus = ERROR_CANNOT_DETECT_DRIVER_FAILURE;
  279. break;
  280. }
  281. BOOL RebootRequested = FALSE;
  282. BOOL RestartRequested = FALSE;
  283. for (DWORD i = 0; i < psfa->cActions; i++)
  284. {
  285. SC_ACTION_TYPE Type = psfa->lpsaActions[i].Type;
  286. if (Type == SC_ACTION_RESTART)
  287. {
  288. RestartRequested = TRUE;
  289. }
  290. else if (Type == SC_ACTION_REBOOT)
  291. {
  292. RebootRequested = TRUE;
  293. }
  294. else if (ACTION_TYPE_INVALID(Type))
  295. {
  296. SC_LOG(ERROR, "RChangeServiceConfig2W: invalid action type %#lx\n", Type);
  297. ApiStatus = ERROR_INVALID_PARAMETER;
  298. goto Cleanup;
  299. }
  300. }
  301. if (RestartRequested)
  302. {
  303. if (!RtlAreAllAccessesGranted(
  304. ((LPSC_HANDLE_STRUCT) hService)->AccessGranted,
  305. SERVICE_START
  306. ))
  307. {
  308. SC_LOG0(ERROR, "Service handle lacks start access\n");
  309. ApiStatus = ERROR_ACCESS_DENIED;
  310. break;
  311. }
  312. }
  313. if (RebootRequested)
  314. {
  315. NTSTATUS Status = ScPrivilegeCheckAndAudit(
  316. SE_SHUTDOWN_PRIVILEGE,
  317. hService,
  318. SERVICE_CHANGE_CONFIG
  319. );
  320. if (!NT_SUCCESS(Status))
  321. {
  322. SC_LOG0(ERROR, "Caller lacks shutdown privilege\n");
  323. ApiStatus = ERROR_ACCESS_DENIED;
  324. break;
  325. }
  326. }
  327. //
  328. // Get the service's image path
  329. //
  330. CHeapPtr<LPWSTR> ImageName;
  331. ApiStatus = ScGetImageFileName(serviceRecord->ServiceName, &ImageName);
  332. if (ApiStatus != NO_ERROR)
  333. {
  334. SC_LOG(ERROR,"RChangeServiceConfig2W: GetImageFileName failed %lu\n", ApiStatus);
  335. break;
  336. }
  337. //
  338. // If the service runs in services.exe, we certainly won't
  339. // detect if the service process dies, so don't pretend we will
  340. //
  341. if (ScImagePathsMatch(ImageName, ScGlobalThisExePath))
  342. {
  343. ApiStatus = ERROR_CANNOT_DETECT_PROCESS_ABORT;
  344. break;
  345. }
  346. }
  347. //
  348. // Write the string values, followed by the non-string values.
  349. // If anything fails, the values written up to that point will be
  350. // backed out.
  351. // (Backing out the update of the non-string values is a little
  352. // more complicated than backing out the string updates. So we
  353. // do the non-string update last, to avoid having to back it out.)
  354. //
  355. CUpdateOptionalString UpdateRebootMessage
  356. (ServiceNameKey, REBOOTMESSAGE_VALUENAME_W);
  357. CUpdateOptionalString UpdateFailureCommand
  358. (ServiceNameKey, FAILURECOMMAND_VALUENAME_W);
  359. if ((ApiStatus = UpdateRebootMessage.Update(psfa->lpRebootMsg)) == ERROR_SUCCESS &&
  360. (ApiStatus = UpdateFailureCommand.Update(psfa->lpCommand)) == ERROR_SUCCESS &&
  361. (ApiStatus = ScWriteFailureActions(ServiceNameKey, psfa)) == ERROR_SUCCESS)
  362. {
  363. UpdateRebootMessage.Commit();
  364. UpdateFailureCommand.Commit();
  365. }
  366. }
  367. break;
  368. //-----------------------------
  369. //
  370. // Other (invalid)
  371. //
  372. //-----------------------------
  373. default:
  374. ApiStatus = ERROR_INVALID_LEVEL;
  375. break;
  376. }
  377. Cleanup:
  378. if (ServiceNameKey != NULL)
  379. {
  380. ScRegFlushKey(ServiceNameKey);
  381. ScRegCloseKey(ServiceNameKey);
  382. }
  383. SC_LOG1(CONFIG_API, "RChangeServiceConfig2W returning %lu\n", ApiStatus);
  384. return ApiStatus;
  385. }
  386. DWORD
  387. RQueryServiceConfig2W(
  388. IN SC_RPC_HANDLE hService,
  389. IN DWORD dwInfoLevel,
  390. OUT LPBYTE lpBuffer,
  391. IN DWORD cbBufSize,
  392. OUT LPDWORD pcbBytesNeeded
  393. )
  394. /*++
  395. Routine Description:
  396. Arguments:
  397. Return Value:
  398. --*/
  399. {
  400. SC_LOG(CONFIG_API, "In RQueryServiceConfig2W for service handle %#lx\n", hService);
  401. if (ScShutdownInProgress)
  402. {
  403. return ERROR_SHUTDOWN_IN_PROGRESS;
  404. }
  405. //
  406. // Check the handle.
  407. //
  408. if (!ScIsValidServiceHandle(hService))
  409. {
  410. return ERROR_INVALID_HANDLE;
  411. }
  412. //
  413. // MIDL doesn't support optional [out] parameters efficiently, so
  414. // we require these parameters.
  415. //
  416. if (lpBuffer == NULL || pcbBytesNeeded == NULL)
  417. {
  418. SC_ASSERT(!"RPC passed NULL for [out] pointers");
  419. return ERROR_INVALID_PARAMETER;
  420. }
  421. //
  422. // Do we have permission to do this?
  423. //
  424. if (!RtlAreAllAccessesGranted(
  425. ((LPSC_HANDLE_STRUCT) hService)->AccessGranted,
  426. SERVICE_QUERY_CONFIG
  427. ))
  428. {
  429. return ERROR_ACCESS_DENIED;
  430. }
  431. //
  432. // Initialize *pcbBytesNeeded. It is incremented below.
  433. // (For consistency with QueryServiceConfig, it is set even on a success
  434. // return.)
  435. //
  436. *pcbBytesNeeded = 0;
  437. //
  438. // Lock database, as we want to look at stuff without other threads changing
  439. // fields at the same time.
  440. //
  441. CServiceRecordSharedLock RLock;
  442. LPSERVICE_RECORD serviceRecord =
  443. ((LPSC_HANDLE_STRUCT) hService)->Type.ScServiceObject.ServiceRecord;
  444. SC_ASSERT(serviceRecord != NULL);
  445. //
  446. // Open the service name key.
  447. //
  448. HKEY ServiceNameKey = NULL;
  449. DWORD ApiStatus = ScOpenServiceConfigKey(
  450. serviceRecord->ServiceName,
  451. KEY_READ,
  452. FALSE, // Create if missing
  453. &ServiceNameKey
  454. );
  455. if (ApiStatus != NO_ERROR)
  456. {
  457. return ApiStatus;
  458. }
  459. switch (dwInfoLevel)
  460. {
  461. //-----------------------------
  462. //
  463. // Service Description
  464. //
  465. //-----------------------------
  466. case SERVICE_CONFIG_DESCRIPTION:
  467. {
  468. *pcbBytesNeeded = sizeof(SERVICE_DESCRIPTION_WOW64);
  469. //
  470. // Read the string from the registry
  471. //
  472. CHeapPtr< LPWSTR > pszDescription;
  473. ApiStatus = ScReadDescription(
  474. ServiceNameKey,
  475. &pszDescription,
  476. pcbBytesNeeded
  477. );
  478. SC_ASSERT(ApiStatus != ERROR_INSUFFICIENT_BUFFER);
  479. if (ApiStatus != NO_ERROR)
  480. {
  481. break;
  482. }
  483. //
  484. // Check for sufficient buffer space
  485. //
  486. if (cbBufSize < *pcbBytesNeeded)
  487. {
  488. ApiStatus = ERROR_INSUFFICIENT_BUFFER;
  489. break;
  490. }
  491. //
  492. // Copy the information to the output buffer
  493. // The format is:
  494. // SERVICE_DESCRIPTION_WOW64 struct
  495. // description string
  496. // Pointers in the struct are replaced with offsets based at the
  497. // start of the buffer. NULL pointers remain NULL.
  498. //
  499. COutBuf OutBuf(lpBuffer);
  500. LPSERVICE_DESCRIPTION_WOW64 psdOut =
  501. (LPSERVICE_DESCRIPTION_WOW64) OutBuf.Next();
  502. OutBuf.AddUsed(sizeof(SERVICE_DESCRIPTION_WOW64));
  503. SC_ASSERT(COUNT_IS_ALIGNED(OutBuf.OffsetNext(), sizeof(WCHAR)));
  504. if (pszDescription != NULL)
  505. {
  506. psdOut->dwDescriptionOffset = OutBuf.OffsetNext();
  507. OutBuf.AppendBytes(pszDescription, (DWORD) WCSSIZE(pszDescription));
  508. }
  509. else
  510. {
  511. psdOut->dwDescriptionOffset = 0;
  512. }
  513. }
  514. break;
  515. //-----------------------------
  516. //
  517. // Service Failure Actions
  518. //
  519. //-----------------------------
  520. case SERVICE_CONFIG_FAILURE_ACTIONS:
  521. {
  522. //
  523. // Read the non-string info
  524. //
  525. CHeapPtr< LPSERVICE_FAILURE_ACTIONS_WOW64 > psfa;
  526. ApiStatus = ScReadFailureActions(ServiceNameKey, &psfa, pcbBytesNeeded);
  527. if (ApiStatus != NO_ERROR)
  528. {
  529. break;
  530. }
  531. if (psfa == NULL)
  532. {
  533. SC_ASSERT(*pcbBytesNeeded == 0);
  534. *pcbBytesNeeded = sizeof(SERVICE_FAILURE_ACTIONS_WOW64);
  535. }
  536. SC_ASSERT(COUNT_IS_ALIGNED(*pcbBytesNeeded, sizeof(WCHAR)));
  537. //
  538. // Read the string values
  539. //
  540. CHeapPtr< LPWSTR > RebootMessage;
  541. ApiStatus = ScReadRebootMessage(
  542. ServiceNameKey,
  543. &RebootMessage,
  544. pcbBytesNeeded
  545. );
  546. if (ApiStatus != NO_ERROR)
  547. {
  548. break;
  549. }
  550. SC_ASSERT(COUNT_IS_ALIGNED(*pcbBytesNeeded, sizeof(WCHAR)));
  551. CHeapPtr< LPWSTR > FailureCommand;
  552. ApiStatus = ScReadFailureCommand(
  553. ServiceNameKey,
  554. &FailureCommand,
  555. pcbBytesNeeded
  556. );
  557. if (ApiStatus != NO_ERROR)
  558. {
  559. break;
  560. }
  561. //
  562. // Check for sufficient buffer space
  563. //
  564. if (cbBufSize < *pcbBytesNeeded)
  565. {
  566. ApiStatus = ERROR_INSUFFICIENT_BUFFER;
  567. break;
  568. }
  569. //
  570. // Copy the information to the output buffer
  571. // The format is:
  572. // SERVICE_FAILURE_ACTIONS_WOW64 struct
  573. // SC_ACTIONS array
  574. // strings
  575. // Pointers in the struct are replaced with offsets based at the
  576. // start of the buffer. NULL pointers remain NULL.
  577. //
  578. COutBuf OutBuf(lpBuffer);
  579. LPSERVICE_FAILURE_ACTIONS_WOW64 psfaOut =
  580. (LPSERVICE_FAILURE_ACTIONS_WOW64) OutBuf.Next();
  581. if (psfa != NULL)
  582. {
  583. psfaOut->dwResetPeriod = ((LPSERVICE_FAILURE_ACTIONS_WOW64) psfa)->dwResetPeriod;
  584. psfaOut->cActions = ((LPSERVICE_FAILURE_ACTIONS_WOW64) psfa)->cActions;
  585. }
  586. else
  587. {
  588. psfaOut->dwResetPeriod = 0;
  589. psfaOut->cActions = 0;
  590. }
  591. OutBuf.AddUsed(sizeof(SERVICE_FAILURE_ACTIONS_WOW64));
  592. if (psfaOut->cActions != 0)
  593. {
  594. psfaOut->dwsaActionsOffset = OutBuf.OffsetNext();
  595. OutBuf.AppendBytes(psfa + 1,
  596. psfaOut->cActions * sizeof(SC_ACTION));
  597. }
  598. else
  599. {
  600. psfaOut->dwsaActionsOffset = 0;
  601. }
  602. SC_ASSERT(COUNT_IS_ALIGNED(OutBuf.OffsetNext(), sizeof(WCHAR)));
  603. if (RebootMessage != NULL)
  604. {
  605. psfaOut->dwRebootMsgOffset = OutBuf.OffsetNext();
  606. OutBuf.AppendBytes(RebootMessage, (DWORD) WCSSIZE(RebootMessage));
  607. }
  608. else
  609. {
  610. psfaOut->dwRebootMsgOffset = 0;
  611. }
  612. if (FailureCommand != NULL)
  613. {
  614. psfaOut->dwCommandOffset = OutBuf.OffsetNext();
  615. OutBuf.AppendBytes(FailureCommand, (DWORD) WCSSIZE(FailureCommand));
  616. }
  617. else
  618. {
  619. psfaOut->dwCommandOffset = 0;
  620. }
  621. }
  622. break;
  623. //-----------------------------
  624. //
  625. // Other (invalid)
  626. //
  627. //-----------------------------
  628. default:
  629. ApiStatus = ERROR_INVALID_LEVEL;
  630. break;
  631. }
  632. ScRegCloseKey(ServiceNameKey);
  633. SC_LOG1(CONFIG_API, "RQueryServiceConfig2W returning %lu\n", ApiStatus);
  634. return ApiStatus;
  635. }
  636. #if DBG == 1
  637. VOID
  638. PrintConfig2Parms(
  639. IN SC_RPC_HANDLE hService,
  640. IN SC_RPC_CONFIG_INFOW Info
  641. )
  642. {
  643. KdPrintEx((DPFLTR_SCSERVER_ID,
  644. DEBUG_CONFIG_API,
  645. "Parameters to RChangeServiceConfig2W:\n"));
  646. LPSTR psz;
  647. switch (Info.dwInfoLevel)
  648. {
  649. case SERVICE_CONFIG_DESCRIPTION:
  650. psz = "SERVICE_CONFIG_DESCRIPTION";
  651. break;
  652. case SERVICE_CONFIG_FAILURE_ACTIONS:
  653. psz = "SERVICE_CONFIG_FAILURE_ACTIONS";
  654. break;
  655. default:
  656. psz = "invalid";
  657. break;
  658. }
  659. KdPrintEx((DPFLTR_SCSERVER_ID,
  660. DEBUG_CONFIG_API,
  661. " dwInfoLevel = %ld (%s)\n", Info.dwInfoLevel,
  662. psz));
  663. switch (Info.dwInfoLevel)
  664. {
  665. case SERVICE_CONFIG_DESCRIPTION:
  666. if (Info.psd == NULL)
  667. {
  668. KdPrintEx((DPFLTR_SCSERVER_ID,
  669. DEBUG_CONFIG_API,
  670. " NULL information pointer -- no action requested\n\n"));
  671. break;
  672. }
  673. KdPrintEx((DPFLTR_SCSERVER_ID,
  674. DEBUG_CONFIG_API,
  675. " pszDescription = \"%ws\"\n",
  676. Info.psd->lpDescription));
  677. break;
  678. case SERVICE_CONFIG_FAILURE_ACTIONS:
  679. if (Info.psfa == NULL)
  680. {
  681. KdPrintEx((DPFLTR_SCSERVER_ID,
  682. DEBUG_CONFIG_API,
  683. " NULL information pointer -- no action requested\n\n"));
  684. break;
  685. }
  686. KdPrintEx((DPFLTR_SCSERVER_ID,
  687. DEBUG_CONFIG_API,
  688. " dwResetPeriod = %ld\n",
  689. Info.psfa->dwResetPeriod));
  690. KdPrintEx((DPFLTR_SCSERVER_ID,
  691. DEBUG_CONFIG_API,
  692. " lpRebootMsg = \"%ws\"\n",
  693. Info.psfa->lpRebootMsg));
  694. KdPrintEx((DPFLTR_SCSERVER_ID,
  695. DEBUG_CONFIG_API,
  696. " lpCommand = \"%ws\"\n",
  697. Info.psfa->lpCommand));
  698. KdPrintEx((DPFLTR_SCSERVER_ID,
  699. DEBUG_CONFIG_API,
  700. " %ld failure %s\n",
  701. Info.psfa->cActions,
  702. Info.psfa->cActions == 0 ? "actions." :
  703. Info.psfa->cActions == 1 ? "action:" : "actions:"));
  704. if (Info.psfa->lpsaActions == NULL)
  705. {
  706. KdPrintEx((DPFLTR_SCSERVER_ID,
  707. DEBUG_CONFIG_API,
  708. " NULL array pointer -- no change in fixed info\n\n"));
  709. }
  710. else
  711. {
  712. for (DWORD i = 0; i < Info.psfa->cActions; i++)
  713. {
  714. SC_ACTION& sa = Info.psfa->lpsaActions[i];
  715. KdPrintEx((DPFLTR_SCSERVER_ID,
  716. DEBUG_CONFIG_API,
  717. " %ld: Action %ld, Delay %ld\n",
  718. i,
  719. sa.Type,
  720. sa.Delay));
  721. }
  722. }
  723. break;
  724. }
  725. KdPrintEx((DPFLTR_SCSERVER_ID, DEBUG_CONFIG_API, "\n"));
  726. }
  727. #endif // DBG == 1