package com.zillabyte.motherbrain.container;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.file.Files;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.newsclub.net.unix.AFUNIXServerSocket;
import org.newsclub.net.unix.AFUNIXSocketAddress;
import com.google.monitoring.runtime.instrumentation.common.com.google.common.base.Throwables;
import com.zillabyte.motherbrain.utils.FileLockUtil;
import com.zillabyte.motherbrain.utils.FileLockUtil.MultiLock;
import com.zillabyte.motherbrain.utils.Utils;
public class UnixSocketHelper {
private static Logger _log = Utils.getLogger(UnixSocketHelper.class);
/*****
* We override AFUNIXServerSocket here because we want to expose the `getSocketFile` method
*/
public static class AFUNIXServerSocketWrapper extends AFUNIXServerSocket {
private AFUNIXSocketAddress _endpoint;
protected AFUNIXServerSocketWrapper() throws IOException {
super();
}
public void bind(SocketAddress endpoint, int backlog) throws IOException {
super.bind(endpoint, backlog);
_endpoint = (AFUNIXSocketAddress) endpoint;
}
public String getSocketFile() {
return _endpoint.getSocketFile();
}
}
/**
* Synchronized doesn't help in a multi-process env.
* @throws Exception
*/
public synchronized static void maybeUnpackNativeLibraries() throws Exception {
File destDir = new File("/tmp/junixsocket");
File destDirReady = new File("/tmp/junixsocket/ready");
if(destDirReady.exists()){
// If the ready file exists, set path and move on.
_log.info("socket dir was already ready. Setting unix library path: " + destDir.getAbsolutePath());
System.setProperty("org.newsclub.net.unix.library.path", destDir.getAbsolutePath());
return;
}
MultiLock lock = FileLockUtil.lock("/tmp/junixsocket.lock");
try {
if(destDirReady.exists()){
// Try again, just in case of race conditions...
_log.info("socket dir was already ready. Setting unix library path: " + destDir.getAbsolutePath());
System.setProperty("org.newsclub.net.unix.library.path", destDir.getAbsolutePath());
return;
}
try {
FileUtils.deleteQuietly(destDir);
destDir.mkdirs();
InputStream stream = UnixSocketHelper.class.getResourceAsStream("/lib-native/junixsocket.zip");
File zip = new File(destDir, "junixsocket.zip");
Files.copy(stream, zip.toPath());
Utils.bash("cd " + destDir.getAbsolutePath() + "; unzip " + zip.getAbsolutePath());
// Now remove the lock.
destDirReady.createNewFile(); // Release the lock
} catch(Exception e) {
FileUtils.deleteQuietly(destDir);
Throwables.propagate(e);
}
_log.info("Setting unix library path: " + destDir.getAbsolutePath());
System.setProperty("org.newsclub.net.unix.library.path", destDir.getAbsolutePath());
} finally {
lock.release();
}
}
public static ServerSocket createUnixSocket(File socketFile) throws ContainerException {
try {
maybeUnpackNativeLibraries();
AFUNIXServerSocket server = new AFUNIXServerSocketWrapper();
server.bind(new AFUNIXSocketAddress(socketFile));
return server;
} catch (Exception e) {
throw (ContainerException) new ContainerException(e).setUserMessage("Failed to initialize Unix Socket.").adviseRetry();
}
}
}