blob: a1ea7035044e001fd5e9a8aadd957ef6cd9f8cd7 [file] [log] [blame]
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;
}
}
}