/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.bootstrap; import com.sun.jna.IntegerType; import com.sun.jna.Native; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.win32.StdCallLibrary; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.Constants; import org.elasticsearch.common.logging.Loggers; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Library for Windows/Kernel32 */ final class JNAKernel32Library { private static final Logger logger = Loggers.getLogger(JNAKernel32Library.class); // Callbacks must be kept around in order to be able to be called later, // when the Windows ConsoleCtrlHandler sends an event. private List<NativeHandlerCallback> callbacks = new ArrayList<>(); // Native library instance must be kept around for the same reason. private static final class Holder { private static final JNAKernel32Library instance = new JNAKernel32Library(); } private JNAKernel32Library() { if (Constants.WINDOWS) { try { Native.register("kernel32"); logger.debug("windows/Kernel32 library loaded"); } catch (NoClassDefFoundError e) { logger.warn("JNA not found. native methods and handlers will be disabled."); } catch (UnsatisfiedLinkError e) { logger.warn("unable to link Windows/Kernel32 library. native methods and handlers will be disabled."); } } } static JNAKernel32Library getInstance() { return Holder.instance; } /** * Adds a Console Ctrl Handler. * * @return true if the handler is correctly set * @throws java.lang.UnsatisfiedLinkError if the Kernel32 library is not loaded or if the native function is not found * @throws java.lang.NoClassDefFoundError if the library for native calls is missing */ boolean addConsoleCtrlHandler(ConsoleCtrlHandler handler) { boolean result = false; if (handler != null) { NativeHandlerCallback callback = new NativeHandlerCallback(handler); result = SetConsoleCtrlHandler(callback, true); if (result) { callbacks.add(callback); } } return result; } List<Object> getCallbacks() { return Collections.<Object>unmodifiableList(callbacks); } /** * Native call to the Kernel32 API to set a new Console Ctrl Handler. * * @return true if the handler is correctly set * @throws java.lang.UnsatisfiedLinkError if the Kernel32 library is not loaded or if the native function is not found * @throws java.lang.NoClassDefFoundError if the library for native calls is missing */ native boolean SetConsoleCtrlHandler(StdCallLibrary.StdCallCallback handler, boolean add); /** * Handles consoles event with WIN API * <p> * See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683242%28v=vs.85%29.aspx */ class NativeHandlerCallback implements StdCallLibrary.StdCallCallback { private final ConsoleCtrlHandler handler; NativeHandlerCallback(ConsoleCtrlHandler handler) { this.handler = handler; } public boolean callback(long dwCtrlType) { int event = (int) dwCtrlType; if (logger.isDebugEnabled()) { logger.debug("console control handler receives event [{}@{}]", event, dwCtrlType); } return handler.handle(event); } } /** * Memory protection constraints * * https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786%28v=vs.85%29.aspx */ public static final int PAGE_NOACCESS = 0x0001; public static final int PAGE_GUARD = 0x0100; public static final int MEM_COMMIT = 0x1000; /** * Contains information about a range of pages in the virtual address space of a process. * The VirtualQuery and VirtualQueryEx functions use this structure. * * https://msdn.microsoft.com/en-us/library/windows/desktop/aa366775%28v=vs.85%29.aspx */ public static class MemoryBasicInformation extends Structure { public Pointer BaseAddress; public Pointer AllocationBase; public NativeLong AllocationProtect; public SizeT RegionSize; public NativeLong State; public NativeLong Protect; public NativeLong Type; @Override protected List<String> getFieldOrder() { return Arrays.asList("BaseAddress", "AllocationBase", "AllocationProtect", "RegionSize", "State", "Protect", "Type"); } } public static class SizeT extends IntegerType { // JNA requires this no-arg constructor to be public, // otherwise it fails to register kernel32 library public SizeT() { this(0); } SizeT(long value) { super(Native.SIZE_T_SIZE, value); } } /** * Locks the specified region of the process's virtual address space into physical * memory, ensuring that subsequent access to the region will not incur a page fault. * * https://msdn.microsoft.com/en-us/library/windows/desktop/aa366895%28v=vs.85%29.aspx * * @param address A pointer to the base address of the region of pages to be locked. * @param size The size of the region to be locked, in bytes. * @return true if the function succeeds */ native boolean VirtualLock(Pointer address, SizeT size); /** * Retrieves information about a range of pages within the virtual address space of a specified process. * * https://msdn.microsoft.com/en-us/library/windows/desktop/aa366907%28v=vs.85%29.aspx * * @param handle A handle to the process whose memory information is queried. * @param address A pointer to the base address of the region of pages to be queried. * @param memoryInfo A pointer to a structure in which information about the specified page range is returned. * @param length The size of the buffer pointed to by the memoryInfo parameter, in bytes. * @return the actual number of bytes returned in the information buffer. */ native int VirtualQueryEx(Pointer handle, Pointer address, MemoryBasicInformation memoryInfo, int length); /** * Sets the minimum and maximum working set sizes for the specified process. * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms686234%28v=vs.85%29.aspx * * @param handle A handle to the process whose working set sizes is to be set. * @param minSize The minimum working set size for the process, in bytes. * @param maxSize The maximum working set size for the process, in bytes. * @return true if the function succeeds. */ native boolean SetProcessWorkingSetSize(Pointer handle, SizeT minSize, SizeT maxSize); /** * Retrieves a pseudo handle for the current process. * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms683179%28v=vs.85%29.aspx * * @return a pseudo handle to the current process. */ native Pointer GetCurrentProcess(); /** * Closes an open object handle. * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211%28v=vs.85%29.aspx * * @param handle A valid handle to an open object. * @return true if the function succeeds. */ native boolean CloseHandle(Pointer handle); /** * Creates or opens a new job object * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682409%28v=vs.85%29.aspx * * @param jobAttributes security attributes * @param name job name * @return job handle if the function succeeds */ native Pointer CreateJobObjectW(Pointer jobAttributes, String name); /** * Associates a process with an existing job * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms681949%28v=vs.85%29.aspx * * @param job job handle * @param process process handle * @return true if the function succeeds */ native boolean AssignProcessToJobObject(Pointer job, Pointer process); /** * Basic limit information for a job object * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms684147%28v=vs.85%29.aspx */ public static class JOBOBJECT_BASIC_LIMIT_INFORMATION extends Structure implements Structure.ByReference { public long PerProcessUserTimeLimit; public long PerJobUserTimeLimit; public int LimitFlags; public SizeT MinimumWorkingSetSize; public SizeT MaximumWorkingSetSize; public int ActiveProcessLimit; public Pointer Affinity; public int PriorityClass; public int SchedulingClass; @Override protected List<String> getFieldOrder() { return Arrays.asList("PerProcessUserTimeLimit", "PerJobUserTimeLimit", "LimitFlags", "MinimumWorkingSetSize", "MaximumWorkingSetSize", "ActiveProcessLimit", "Affinity", "PriorityClass", "SchedulingClass"); } } /** * Constant for JOBOBJECT_BASIC_LIMIT_INFORMATION in Query/Set InformationJobObject */ static final int JOBOBJECT_BASIC_LIMIT_INFORMATION_CLASS = 2; /** * Constant for LimitFlags, indicating a process limit has been set */ static final int JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 8; /** * Get job limit and state information * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms684925%28v=vs.85%29.aspx * * @param job job handle * @param infoClass information class constant * @param info pointer to information structure * @param infoLength size of information structure * @param returnLength length of data written back to structure (or null if not wanted) * @return true if the function succeeds */ native boolean QueryInformationJobObject(Pointer job, int infoClass, Pointer info, int infoLength, Pointer returnLength); /** * Set job limit and state information * * https://msdn.microsoft.com/en-us/library/windows/desktop/ms686216%28v=vs.85%29.aspx * * @param job job handle * @param infoClass information class constant * @param info pointer to information structure * @param infoLength size of information structure * @return true if the function succeeds */ native boolean SetInformationJobObject(Pointer job, int infoClass, Pointer info, int infoLength); }