Skip to content
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

Merged
merged 9 commits into from
Jan 19, 2016
55 changes: 55 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,61 @@ public boolean ReadEventLog(HANDLE hEventLog, int dwReadFlags,
public boolean GetOldestEventLogRecord(HANDLE hEventLog,
IntByReference OldestRecord);


/**
* Changes the optional configuration parameters of a service.
*
* @param hService
* A handle to the service. This handle is returned by the
* OpenService or CreateService function and must have the
* SERVICE_CHANGE_CONFIG access right. For more information,
* see <a
* href="http://msdn.microsoft.com/en-us/library/ms685981.aspx"
* >Service Security and Access Rights</a>.
* If the service controller handles the SC_ACTION_RESTART
* action, hService must have the SERVICE_START access right.
* @param dwInfoLevel
* The configuration information to be changed.
* @param lpInfo
* A pointer to the new value to be set for the configuration
* information. The format of this data depends on the value
* of the dwInfoLevel parameter. If this value is NULL, the
* information remains unchanged.
* @return If the function succeeds, the return value is nonzero.
* If the function fails, the return value is zero. To get extended
* error information, call GetLastError.
*/
public boolean ChangeServiceConfig2(SC_HANDLE hService, int dwInfoLevel,
Structure lpInfo);
Copy link
Contributor

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...

Copy link
Contributor

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.


/**
* Retrieves the optional configuration parameters of the specified service.
*
* @param hService
* A handle to the service. This handle is returned by the OpenService or
* CreateService function and must have the SERVICE_QUERY_CONFIG access right. For
* more information, see Service Security and Access Rights.
* @param dwInfoLevel
* The configuration information to be queried.
* @param lpBuffer
* A pointer to the buffer that receives the service configuration information. The
* format of this data depends on the value of the dwInfoLevel parameter.
* The maximum size of this array is 8K bytes. To determine the required size,
* specify NULL for this parameter and 0 for the cbBufSize parameter. The function
* fails and GetLastError returns ERROR_INSUFFICIENT_BUFFER. The pcbBytesNeeded
* parameter receives the needed size.
* @param cbBufSize
* The size of the structure pointed to by the lpBuffer parameter, in bytes.
* @param pcbBytesNeeded
* A pointer to a variable that receives the number of bytes required to store the
* configuration information, if the function fails with ERROR_INSUFFICIENT_BUFFER.
* @return If the function succeeds, the return value is nonzero.
* If the function fails, the return value is zero. To get extended error information,
* call GetLastError.
*/
public boolean QueryServiceConfig2(SC_HANDLE hService, int dwInfoLevel,
Pointer lpBuffer, int cbBufSize, IntByReference pcbBytesNeeded);

/**
* Retrieves the current status of the specified service based on the
* specified information level.
Expand Down
118 changes: 117 additions & 1 deletion contrib/platform/src/com/sun/jna/platform/win32/W32Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should make the read() call part of the SERVICE_FAILURE_ACTIONS(Pointer) constructor.

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
Expand Down
139 changes: 139 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Winsvc.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call read() here.

}

public SERVICE_FAILURE_ACTIONS(Pointer p, int alignment) {
super(p, alignment);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call read() here.

}

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
//
Expand Down Expand Up @@ -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
Expand Down
Loading