-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added changing optional config of W32 services #489
Changes from 4 commits
909d68b
0165741
2b8b0a8
40abaef
472ab5e
c3e540b
35277d1
fe2e9dd
c092b77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,11 +13,29 @@ | |
|
||
package com.sun.jna.platform.win32; | ||
|
||
import java.util.List; | ||
|
||
import com.sun.jna.Memory; | ||
import com.sun.jna.Native; | ||
import com.sun.jna.Pointer; | ||
import com.sun.jna.Structure; | ||
import com.sun.jna.WString; | ||
import com.sun.jna.platform.win32.WinDef.DWORD; | ||
import com.sun.jna.platform.win32.WinNT; | ||
import com.sun.jna.platform.win32.WinNT.HANDLE; | ||
import com.sun.jna.platform.win32.WinNT.HANDLEByReference; | ||
import com.sun.jna.platform.win32.WinNT.LUID; | ||
import com.sun.jna.platform.win32.WinNT.LUID_AND_ATTRIBUTES; | ||
import com.sun.jna.platform.win32.WinNT.TOKEN_PRIVILEGES; | ||
import com.sun.jna.platform.win32.Winsvc.SC_ACTION; | ||
import com.sun.jna.platform.win32.Winsvc.SC_HANDLE; | ||
import com.sun.jna.platform.win32.Winsvc.SC_STATUS_TYPE; | ||
import com.sun.jna.platform.win32.Winsvc.SERVICE_FAILURE_ACTIONS; | ||
import com.sun.jna.platform.win32.Winsvc.SERVICE_FAILURE_ACTIONS_FLAG; | ||
import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS; | ||
import com.sun.jna.ptr.IntByReference; | ||
|
||
|
||
/** | ||
* Win32 Service wrapper | ||
* @author EugineLev | ||
|
@@ -46,7 +64,105 @@ public void close() { | |
_handle = null; | ||
} | ||
} | ||
|
||
|
||
private void addShutdownPrivilegeToProcess() { | ||
HANDLEByReference hToken = new HANDLEByReference(); | ||
LUID luid = new LUID(); | ||
Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), | ||
WinNT.TOKEN_ADJUST_PRIVILEGES, hToken); | ||
Advapi32.INSTANCE.LookupPrivilegeValue("", WinNT.SE_SHUTDOWN_NAME, luid); | ||
TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES(1); | ||
tp.Privileges[0] = new LUID_AND_ATTRIBUTES(luid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED)); | ||
Advapi32.INSTANCE.AdjustTokenPrivileges(hToken.getValue(), false, tp, tp.size(), null, | ||
new IntByReference()); | ||
} | ||
|
||
/** | ||
* Set the failure actions of the specified service. Corresponds to | ||
* <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988.aspx">ChangeServiceConfig2</a> | ||
* with parameter dwInfoLevel set to SERVICE_CONFIG_FAILURE_ACTIONS. | ||
*/ | ||
public void setFailureActions(List<SC_ACTION> actions, int resetPeriod, String rebootMsg, | ||
String command) { | ||
SERVICE_FAILURE_ACTIONS.ByReference actionStruct = new SERVICE_FAILURE_ACTIONS.ByReference(); | ||
actionStruct.dwResetPeriod = resetPeriod; | ||
actionStruct.lpRebootMsg = new WString(rebootMsg); | ||
actionStruct.lpCommand = new WString(command); | ||
actionStruct.cActions = actions.size(); | ||
|
||
actionStruct.lpsaActions = new SC_ACTION.ByReference(); | ||
SC_ACTION[] actionArray = (SC_ACTION[])actionStruct.lpsaActions.toArray(actions.size()); | ||
boolean hasShutdownPrivilege = false; | ||
int i = 0; | ||
for (SC_ACTION action : actions) { | ||
if (!hasShutdownPrivilege && action.type == Winsvc.SC_ACTION_REBOOT) { | ||
addShutdownPrivilegeToProcess(); | ||
hasShutdownPrivilege = true; | ||
} | ||
actionArray[i].type = action.type; | ||
actionArray[i].delay = action.delay; | ||
i++; | ||
} | ||
|
||
if (!Advapi32.INSTANCE.ChangeServiceConfig2(_handle, Winsvc.SERVICE_CONFIG_FAILURE_ACTIONS, | ||
actionStruct)) { | ||
throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); | ||
} | ||
} | ||
|
||
private Pointer queryServiceConfig2(int type) { | ||
IntByReference bufferSize = new IntByReference(); | ||
Advapi32.INSTANCE.QueryServiceConfig2(_handle, type, Pointer.NULL, 0, bufferSize); | ||
|
||
Pointer buffer = new Memory(bufferSize.getValue()); | ||
|
||
if (!Advapi32.INSTANCE.QueryServiceConfig2(_handle, type, buffer, bufferSize.getValue(), | ||
new IntByReference())) { | ||
throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); | ||
} | ||
|
||
return buffer; | ||
} | ||
|
||
/** | ||
* Get the failure actions of the specified service. Corresponds to | ||
* <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988.aspx">QueryServiceConfig2</a> | ||
* with parameter dwInfoLevel set to SERVICE_CONFIG_FAILURE_ACTIONS. | ||
*/ | ||
public SERVICE_FAILURE_ACTIONS getFailureActions() { | ||
Pointer buffer = queryServiceConfig2(Winsvc.SERVICE_CONFIG_FAILURE_ACTIONS); | ||
SERVICE_FAILURE_ACTIONS result = new SERVICE_FAILURE_ACTIONS(buffer); | ||
result.read(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should make the |
||
return result; | ||
} | ||
|
||
/** | ||
* Set the failure action flag of the specified service. Corresponds to | ||
* <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988.aspx">ChangeServiceConfig2</a> | ||
* with parameter dwInfoLevel set to SERVICE_CONFIG_FAILURE_ACTIONS_FLAG. | ||
*/ | ||
public void setFailureActionsFlag(boolean flagValue) { | ||
SERVICE_FAILURE_ACTIONS_FLAG flag = new SERVICE_FAILURE_ACTIONS_FLAG(); | ||
flag.fFailureActionsOnNonCrashFailures = flagValue ? 1 : 0; | ||
|
||
if (!Advapi32.INSTANCE.ChangeServiceConfig2(_handle, Winsvc.SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, | ||
flag)) { | ||
throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); | ||
} | ||
} | ||
|
||
/** | ||
* Get the failure actions flag of the specified service. Corresponds to | ||
* <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988.aspx">QueryServiceConfig2</a> | ||
* with parameter dwInfoLevel set to SERVICE_CONFIG_FAILURE_ACTIONS_FLAG. | ||
*/ | ||
public boolean getFailureActionsFlag() { | ||
Pointer buffer = queryServiceConfig2(Winsvc.SERVICE_CONFIG_FAILURE_ACTIONS_FLAG); | ||
SERVICE_FAILURE_ACTIONS_FLAG result = new SERVICE_FAILURE_ACTIONS_FLAG(buffer); | ||
result.read(); | ||
return result.fFailureActionsOnNonCrashFailures != 0; | ||
} | ||
|
||
/** | ||
* Retrieves the current status of the specified service based on the specified information level. | ||
* @return | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,9 @@ | |
import java.util.List; | ||
|
||
import com.sun.jna.Memory; | ||
import com.sun.jna.Pointer; | ||
import com.sun.jna.Structure; | ||
import com.sun.jna.WString; | ||
import com.sun.jna.platform.win32.WinNT.HANDLE; | ||
import com.sun.jna.win32.StdCallLibrary; | ||
|
||
|
@@ -200,6 +202,121 @@ public SERVICE_STATUS_PROCESS(int size) { | |
} | ||
} | ||
|
||
/** | ||
* Represents the action the service controller should take on each failure of a service. A | ||
* service is considered failed when it terminates without reporting a status of SERVICE_STOPPED | ||
* to the service controller. | ||
* To configure additional circumstances under which the failure actions are to be executed, see | ||
* SERVICE_FAILURE_ACTIONS_FLAG. | ||
*/ | ||
public class SERVICE_FAILURE_ACTIONS extends Structure { | ||
public static class ByReference extends SERVICE_FAILURE_ACTIONS implements Structure.ByReference {} | ||
/** | ||
* The time after which to reset the failure count to zero if there are no failures, in | ||
* seconds. Specify INFINITE to indicate that this value should never be reset. | ||
*/ | ||
public int dwResetPeriod; | ||
/** | ||
* The message to be broadcast to server users before rebooting in response to the | ||
* SC_ACTION_REBOOT service controller action. | ||
* If this value is NULL, the reboot message is unchanged. If the value is an empty string | ||
* (""), the reboot message is deleted and no message is broadcast. | ||
* This member can specify a localized string using the following format: | ||
* @[path\]dllname,-strID | ||
* The string with identifier strID is loaded from dllname; the path is optional. For more | ||
* information, see RegLoadMUIString. | ||
* Windows Server 2003 and Windows XP: Localized strings are not supported until Windows | ||
* Vista. | ||
*/ | ||
public WString lpRebootMsg; | ||
/** | ||
* The command line of the process for the CreateProcess function to execute in response to | ||
* the SC_ACTION_RUN_COMMAND service controller action. This process runs under the same | ||
* account as the service. | ||
* If this value is NULL, the command is unchanged. If the value is an empty string (""), | ||
* the command is deleted and no program is run when the service fails. | ||
*/ | ||
public WString lpCommand; | ||
/** | ||
* The number of elements in the lpsaActions array. | ||
* If this value is 0, but lpsaActions is not NULL, the reset period and array of failure | ||
* actions are deleted. | ||
*/ | ||
public int cActions; | ||
/** | ||
* A pointer to an array of SC_ACTION structures. | ||
* If this value is NULL, the cActions and dwResetPeriod members are ignored. | ||
*/ | ||
public SC_ACTION.ByReference lpsaActions; | ||
|
||
public SERVICE_FAILURE_ACTIONS() { | ||
super(); | ||
} | ||
|
||
public SERVICE_FAILURE_ACTIONS(Pointer p) { | ||
super(p); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Call |
||
} | ||
|
||
public SERVICE_FAILURE_ACTIONS(Pointer p, int alignment) { | ||
super(p, alignment); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Call |
||
} | ||
|
||
protected List getFieldOrder() { | ||
return Arrays.asList(new String[] { "dwResetPeriod", "lpRebootMsg", "lpCommand", "cActions", "lpsaActions" }); | ||
} | ||
} | ||
|
||
/** | ||
* Represents an action that the service control manager can perform. | ||
*/ | ||
public class SC_ACTION extends Structure { | ||
public static class ByReference extends SC_ACTION implements Structure.ByReference {} | ||
/** | ||
* The action to be performed. This member can be one of the following values from the | ||
* SC_ACTION_TYPE enumeration type. | ||
*/ | ||
public int type; | ||
/** | ||
* The time to wait before performing the specified action, in milliseconds. | ||
*/ | ||
public int delay; | ||
|
||
protected List getFieldOrder() { | ||
return Arrays.asList(new String[] { "type", "delay" }); | ||
} | ||
} | ||
|
||
/** | ||
* Contains the failure actions flag setting of a service. This setting determines when failure | ||
* actions are to be executed. | ||
*/ | ||
public class SERVICE_FAILURE_ACTIONS_FLAG extends Structure { | ||
/** | ||
* If this member is TRUE and the service has configured failure actions, the failure | ||
* actions are queued if the service process terminates without reporting a status of | ||
* SERVICE_STOPPED or if it enters the SERVICE_STOPPED state but the dwWin32ExitCode member | ||
* of the SERVICE_STATUS structure is not ERROR_SUCCESS (0). | ||
* If this member is FALSE and the service has configured failure actions, the failure | ||
* actions are queued only if the service terminates without reporting a status of | ||
* SERVICE_STOPPED. | ||
* This setting is ignored unless the service has configured failure actions. For | ||
* information on configuring failure actions, see ChangeServiceConfig2. | ||
*/ | ||
public int fFailureActionsOnNonCrashFailures; | ||
|
||
protected List getFieldOrder() { | ||
return Arrays.asList(new String[] { "fFailureActionsOnNonCrashFailures" }); | ||
} | ||
|
||
public SERVICE_FAILURE_ACTIONS_FLAG() { | ||
super(); | ||
} | ||
|
||
public SERVICE_FAILURE_ACTIONS_FLAG(Pointer p) { | ||
super(p); | ||
} | ||
} | ||
|
||
// | ||
// Service flags for QueryServiceStatusEx | ||
// | ||
|
@@ -289,6 +406,28 @@ public static class SC_HANDLE extends HANDLE { } | |
int SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100; | ||
int SERVICE_ACCEPT_TIMECHANGE = 0x00000200; | ||
int SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400; | ||
|
||
// | ||
// ChangeServiceConfig2 dwInfoLevel values | ||
// | ||
int SERVICE_CONFIG_DESCRIPTION = 0x00000001; | ||
int SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002; | ||
int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 0x00000003; | ||
int SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 0x00000004; | ||
int SERVICE_CONFIG_SERVICE_SID_INFO = 0x00000005; | ||
int SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 0x00000006; | ||
int SERVICE_CONFIG_PRESHUTDOWN_INFO = 0x00000007; | ||
int SERVICE_CONFIG_TRIGGER_INFO = 0x00000008; | ||
int SERVICE_CONFIG_PREFERRED_NODE = 0x00000009; | ||
int SERVICE_CONFIG_LAUNCH_PROTECTED = 0x0000000c; | ||
|
||
// | ||
// Service failure actions | ||
// | ||
int SC_ACTION_NONE = 0x00000000; | ||
int SC_ACTION_RESTART = 0x00000001; | ||
int SC_ACTION_REBOOT = 0x00000002; | ||
int SC_ACTION_RUN_COMMAND = 0x00000003; | ||
|
||
/** | ||
* The SC_STATUS_TYPE enumeration type contains values | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the generic Structure is a bit too general - whenever the API expects several variants we "overload" the method - see GetVersionEx as an example. In this case there should be a match between the dwInfoLevel and the actual structure type, but it is up to the user to make sure that the level matches the type. Another alternative would be to define an abstract ServiceConfigStructure that extends Structure and have all the possible variants extend the ServiceConfigStructure - this would allow some degree of type safety...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please make one variant per supported structure type. Since I don't believe there is any shared structure between the service config structs, using a ServiceConfigStructure would probably be not much better than Structure.