| package jnr.posix; |
| |
| import jnr.ffi.Library; |
| import jnr.ffi.LibraryLoader; |
| import jnr.ffi.LibraryOption; |
| import jnr.ffi.Struct; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| |
| import jnr.ffi.mapper.FunctionMapper; |
| import jnr.posix.util.DefaultPOSIXHandler; |
| import jnr.posix.util.Platform; |
| |
| import java.util.Map; |
| |
| public class POSIXFactory { |
| // Weird inner-class resolution problem work-around FIXME: JRUBY-5889. Someone fix JAFFL! |
| private static final Class<Struct> BOGUS_HACK = Struct.class; |
| public static final jnr.ffi.Platform NATIVE_PLATFORM = jnr.ffi.Platform.getNativePlatform(); |
| public static final String STANDARD_C_LIBRARY_NAME = NATIVE_PLATFORM.getStandardCLibraryName(); |
| |
| /** |
| * Get a POSIX instance. If useNativePosix is true, this works just like |
| * POSIXFactory#getPOSIX(). If useNativePosix is false, this works like |
| * POSIXFactory#getJavaPOSIX() |
| * |
| * @param handler a POSIXHandler implementation |
| * @param useNativePOSIX whether to attempt to use native code for better functionality |
| * @return a POSIX implementation, attempting to use native code if useNativePosix is true |
| */ |
| public static POSIX getPOSIX(POSIXHandler handler, boolean useNativePOSIX) { |
| return new LazyPOSIX(handler, useNativePOSIX); |
| } |
| |
| /** |
| * This will use {@link DefaultPOSIXHandler} and the native POSIX implementation, |
| * falling back on the pure-Java implementation if native support is not available. |
| * |
| * @return a POSIX implementation, native if possible and pure-Java otherwise. |
| */ |
| public static POSIX getPOSIX() { |
| return getPOSIX(new DefaultPOSIXHandler(), true); |
| } |
| |
| /** |
| * Get a pure-Java POSIX instance. Functionality will be limited to that which can |
| * be provided by pure-Java/JDK features or shelling out to external commands. |
| * |
| * @param handler a POSIXHandler implementation |
| * @return a pure-Java POSIX implementation |
| */ |
| public static POSIX getJavaPOSIX(POSIXHandler handler) { |
| return new JavaPOSIX(handler); |
| } |
| |
| /** |
| * Get a pure-Java POSIX instance. Functionality will be limited to that which can |
| * be provided by pure-Java/JDK features or shelling out to external commands. |
| * |
| * @return a pure-Java POSIX implementation |
| */ |
| public static POSIX getJavaPOSIX() { |
| return getJavaPOSIX(new DefaultPOSIXHandler()); |
| } |
| |
| /** |
| * Get a POSIX instance. If a true native implementation can't be loaded, allow that |
| * error to propagate rather than falling back on the pure-Java version. |
| * |
| * @param handler a POSIXHandler implementation |
| * @return a native POSIX implementation, raising errors if the native version can't load |
| */ |
| public static POSIX getNativePOSIX(POSIXHandler handler) { |
| return loadNativePOSIX(handler); |
| } |
| |
| /** |
| * Get a POSIX instance. If a true native implementation can't be loaded, allow that |
| * error to propagate rather than falling back on the pure-Java version. |
| * |
| * @return a native POSIX implementation, raising errors if the native version can't load |
| */ |
| public static POSIX getNativePOSIX() { |
| return getNativePOSIX(new DefaultPOSIXHandler()); |
| } |
| |
| static POSIX loadPOSIX(POSIXHandler handler, boolean useNativePOSIX) { |
| POSIX posix = null; |
| |
| if (useNativePOSIX) { |
| try { |
| posix = loadNativePOSIX(handler); |
| posix = posix != null ? new CheckedPOSIX(posix, handler) : null; |
| // ENEBO: Should printing be done through a handler+log method? |
| if (handler.isVerbose()) { |
| if (posix != null) { |
| System.err.println("Successfully loaded native POSIX impl."); |
| } else { |
| System.err.println("Failed to load native POSIX impl; falling back on Java impl. Unsupported OS."); |
| } |
| } |
| } catch (Throwable t) { |
| if (handler.isVerbose()) { |
| System.err.println("Failed to load native POSIX impl; falling back on Java impl. Stacktrace follows."); |
| t.printStackTrace(); |
| } |
| } |
| } |
| |
| if (posix == null) { |
| posix = getJavaPOSIX(handler); |
| } |
| |
| return posix; |
| } |
| |
| private static POSIX loadNativePOSIX(POSIXHandler handler) { |
| switch (NATIVE_PLATFORM.getOS()) { |
| case DARWIN: |
| return loadMacOSPOSIX(handler); |
| |
| case LINUX: |
| return loadLinuxPOSIX(handler); |
| |
| case FREEBSD: |
| return loadFreeBSDPOSIX(handler); |
| |
| case DRAGONFLY: |
| return loadDragonFlyPOSIX(handler); |
| |
| case OPENBSD: |
| return loadOpenBSDPOSIX(handler); |
| |
| case SOLARIS: |
| return loadSolarisPOSIX(handler); |
| |
| case AIX: |
| return loadAixPOSIX(handler); |
| |
| case WINDOWS: |
| return loadWindowsPOSIX(handler); |
| } |
| |
| return null; |
| } |
| |
| public static POSIX loadLinuxPOSIX(POSIXHandler handler) { |
| return new LinuxPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| public static POSIX loadMacOSPOSIX(POSIXHandler handler) { |
| return new MacOSPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| public static POSIX loadSolarisPOSIX(POSIXHandler handler) { |
| return new SolarisPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| public static POSIX loadFreeBSDPOSIX(POSIXHandler handler) { |
| return new FreeBSDPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| public static POSIX loadDragonFlyPOSIX(POSIXHandler handler) { |
| return new DragonFlyPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| public static POSIX loadOpenBSDPOSIX(POSIXHandler handler) { |
| return new OpenBSDPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| public static POSIX loadWindowsPOSIX(POSIXHandler handler) { |
| return new WindowsPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| public static POSIX loadAixPOSIX(POSIXHandler handler) { |
| return new AixPOSIX(DefaultLibCProvider.INSTANCE, handler); |
| } |
| |
| private static String[] libraries() { |
| switch (NATIVE_PLATFORM.getOS()) { |
| case LINUX: |
| return new String[] {STANDARD_C_LIBRARY_NAME}; |
| |
| case SOLARIS: |
| return new String[] { "socket", "nsl", STANDARD_C_LIBRARY_NAME}; |
| |
| case DRAGONFLY: |
| case FREEBSD: |
| case NETBSD: |
| return new String[] {STANDARD_C_LIBRARY_NAME}; |
| |
| case AIX: |
| return jnr.ffi.Runtime.getSystemRuntime().addressSize() == 4 |
| ? new String[] { "libc.a(shr.o)" } |
| : new String[] { "libc.a(shr_64.o)" }; |
| |
| case WINDOWS: |
| return new String[] { "msvcrt", "kernel32" }; |
| |
| default: |
| return new String[] {STANDARD_C_LIBRARY_NAME}; |
| } |
| } |
| |
| private static Class<? extends LibC> libraryInterface() { |
| switch (NATIVE_PLATFORM.getOS()) { |
| case LINUX: |
| return LinuxLibC.class; |
| |
| case AIX: |
| return AixLibC.class; |
| |
| case SOLARIS: |
| return SolarisLibC.class; |
| |
| case WINDOWS: |
| return WindowsLibC.class; |
| |
| default: |
| return UnixLibC.class; |
| } |
| } |
| |
| private static FunctionMapper functionMapper() { |
| switch (NATIVE_PLATFORM.getOS()) { |
| case AIX: |
| return new SimpleFunctionMapper.Builder() |
| .map("stat", "stat64x") |
| .map("fstat", "fstat64x") |
| .map("lstat", "lstat64x") |
| .map("stat64", "stat64x") |
| .map("fstat64", "fstat64x") |
| .map("lstat64", "lstat64x") |
| .build(); |
| |
| case WINDOWS: |
| return new SimpleFunctionMapper.Builder() |
| .map("getpid", "_getpid") |
| .map("chmod", "_chmod") |
| .map("fstat", "_fstat64") |
| .map("stat", "_stat64") |
| .map("umask", "_umask") |
| .map("isatty", "_isatty") |
| .map("read", "_read") |
| .map("write", "_write") |
| .map("close", "_close") |
| .map("getcwd", "_getcwd") |
| .map("unlink", "_unlink") |
| .map("access", "_access") |
| .map("open", "_open") |
| .map("dup", "_dup") |
| .map("dup2", "_dup2") |
| .map("lseek", "_lseek") |
| .map("ftruncate", "_chsize") |
| .build(); |
| |
| case SOLARIS: |
| return Platform.IS_32_BIT |
| ? new SimpleFunctionMapper.Builder() |
| .map("stat", "stat64") |
| .map("fstat", "fstat64") |
| .map("lstat", "lstat64") |
| .build() |
| : null; |
| default: |
| return null; |
| } |
| } |
| |
| private static Map<LibraryOption, Object> options() { |
| Map<LibraryOption, Object> options = new HashMap<LibraryOption, Object>(); |
| |
| FunctionMapper functionMapper = functionMapper(); |
| if (functionMapper != null) { |
| options.put(LibraryOption.FunctionMapper, functionMapper); |
| } |
| |
| options.put(LibraryOption.TypeMapper, POSIXTypeMapper.INSTANCE); |
| options.put(LibraryOption.LoadNow, Boolean.TRUE); |
| |
| return Collections.unmodifiableMap(options); |
| } |
| |
| private static final class DefaultLibCProvider implements LibCProvider { |
| public static final LibCProvider INSTANCE = new DefaultLibCProvider(); |
| |
| private static final class SingletonHolder { |
| public static LibC libc; |
| public static Crypt crypt; |
| static { |
| LibraryLoader<? extends LibC> libcLoader = LibraryLoader.create(libraryInterface()); |
| |
| // always do a default search |
| libcLoader.searchDefault(); |
| |
| for (String library : libraries()) { |
| libcLoader.library(library); |
| } |
| |
| for (Map.Entry<LibraryOption, Object> entry : options().entrySet()) { |
| libcLoader.option(entry.getKey(), entry.getValue()); |
| } |
| |
| libcLoader.failImmediately(); |
| |
| libc = libcLoader.load(); |
| |
| Crypt c = null; |
| |
| // FIXME: This is kinda gross but there's no way to tell jnr-ffi that some libraries are ok to fail |
| // See jruby/jruby#5447. |
| try { |
| LibraryLoader<Crypt> loader = LibraryLoader.create(Crypt.class).failImmediately(); |
| c = loader.load("libcrypt.so.1"); |
| } catch (UnsatisfiedLinkError ule) { |
| try { |
| LibraryLoader<Crypt> loader = LibraryLoader.create(Crypt.class).failImmediately(); |
| c = loader.load("crypt"); |
| } catch (UnsatisfiedLinkError ule2) { |
| try { |
| LibraryLoader<Crypt> loader = LibraryLoader.create(Crypt.class).failImmediately(); |
| c = loader.load(STANDARD_C_LIBRARY_NAME); |
| } catch (UnsatisfiedLinkError ule3) { |
| // TODO: log somewhere? Warning? |
| } |
| } |
| } |
| |
| crypt = c; |
| } |
| } |
| |
| public final LibC getLibC() { |
| return SingletonHolder.libc; |
| } |
| |
| public final Crypt getCrypt() { |
| return SingletonHolder.crypt; |
| } |
| } |
| } |