/*
* Autopsy Forensic Browser
*
* Copyright 2012-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed 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.sleuthkit.autopsy.coreutils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.filechooser.FileSystemView;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.ptql.ProcessFinder;
import org.openide.modules.InstalledFileLocator;
import org.openide.modules.Places;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* Platform utilities
*/
public class PlatformUtil {
private static final String PYTHON_MODULES_SUBDIRECTORY = "python_modules";
private static String javaPath = null;
public static final String OS_NAME_UNKNOWN = NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.nameUnknown");
public static final String OS_VERSION_UNKNOWN = NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.verUnknown");
public static final String OS_ARCH_UNKNOWN = NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.archUnknown");
private static volatile long pid = -1;
private static volatile Sigar sigar = null;
private static volatile MemoryMXBean memoryManager = null;
/**
* Get root path where the application is installed
*
* @return absolute path string to the install root dir
*/
public static String getInstallPath() {
File coreFolder = InstalledFileLocator.getDefault().locate("core", PlatformUtil.class.getPackage().getName(), false); //NON-NLS
File rootPath = coreFolder.getParentFile().getParentFile();
return rootPath.getAbsolutePath();
}
/**
* Get root path where the application modules are installed
*
* @return absolute path string to the install modules root dir, or null if
* not found
*/
public static String getInstallModulesPath() {
File coreFolder = InstalledFileLocator.getDefault().locate("core", PlatformUtil.class.getPackage().getName(), false); //NON-NLS
File rootPath = coreFolder.getParentFile();
String modulesPath = rootPath.getAbsolutePath() + File.separator + "modules";
File modulesPathF = new File(modulesPath);
if (modulesPathF.exists() && modulesPathF.isDirectory()) {
return modulesPath;
} else {
rootPath = rootPath.getParentFile();
modulesPath = rootPath.getAbsolutePath() + File.separator + "modules";
modulesPathF = new File(modulesPath);
if (modulesPathF.exists() && modulesPathF.isDirectory()) {
return modulesPath;
} else {
return null;
}
}
}
/**
* Get root path where the user modules are installed
*
* @return absolute path string to the install modules root dir, or null if
* not found
*/
public static String getUserModulesPath() {
return getUserDirectory().getAbsolutePath() + File.separator + "modules";
}
/**
* Get root path where the user Python modules are installed.
*
* @return Absolute path to the Python modules root directory.
*/
public static String getUserPythonModulesPath() {
return getUserDirectory().getAbsolutePath() + File.separator + PYTHON_MODULES_SUBDIRECTORY;
}
/**
* get file path to the java executable binary use embedded java if
* available, otherwise use system java in PATH no validation is done if
* java exists in PATH
*
* @return file path to java binary
*/
public synchronized static String getJavaPath() {
if (javaPath != null) {
return javaPath;
}
File jrePath = new File(getInstallPath() + File.separator + "jre");
if (jrePath.exists() && jrePath.isDirectory()) {
System.out.println(
NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.jrePath.jreDir.msg",
jrePath.getAbsolutePath()));
javaPath = jrePath.getAbsolutePath() + File.separator + "bin" + File.separator + "java"; //NON-NLS
} else {
//else use system installed java in PATH env variable
javaPath = "java"; //NON-NLS
}
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.jrePath.usingJavaPath.msg", javaPath));
return javaPath;
}
/**
* Get user directory where application wide user settings, cache, temp
* files are stored
*
* @return File object representing user directory
*/
public static File getUserDirectory() {
return Places.getUserDirectory();
}
/**
* Get RCP project dirs
*
* @return
*/
public static List<String> getProjectsDirs() {
List<String> ret = new ArrayList<>();
String projectDir = System.getProperty("netbeans.dirs");
if (projectDir == null) {
return ret;
}
String[] split = projectDir.split(";");
if (split == null || split.length == 0) {
return ret;
}
ret.addAll(Arrays.asList(split));
return ret;
}
/**
* Get user config directory path
*
* @return Get user config directory path string
*/
public static String getUserConfigDirectory() {
return Places.getUserDirectory() + File.separator + "config"; //NON-NLS
}
/**
* Get log directory path
*
* @return Get log directory path string
*/
public static String getLogDirectory() {
return Places.getUserDirectory().getAbsolutePath() + File.separator
+ "var" + File.separator + "log" + File.separator; //NON-NLS
}
public static String getDefaultPlatformFileEncoding() {
return System.getProperty("file.encoding");
}
public static String getDefaultPlatformCharset() {
return Charset.defaultCharset().name();
}
public static String getLogFileEncoding() {
return Charset.forName("UTF-8").name();
}
/**
* Utility to extract a resource file to a user configuration directory, if
* it does not exist - useful for setting up default configurations.
*
* @param resourceClass class in the same package as the resourceFile to
* extract
* @param resourceFile resource file name to extract
* @param overWrite true to overwrite an existing resource
* @return true if extracted, false otherwise (if file already exists)
* @throws IOException exception thrown if extract the file failed for IO
* reasons
*/
public static <T> boolean extractResourceToUserConfigDir(final Class<T> resourceClass, final String resourceFile, boolean overWrite) throws IOException {
final File userDir = new File(getUserConfigDirectory());
final File resourceFileF = new File(userDir + File.separator + resourceFile);
if (resourceFileF.exists() && !overWrite) {
return false;
}
resourceFileF.getParentFile().mkdirs();
InputStream inputStream = resourceClass.getResourceAsStream(resourceFile);
OutputStream out = null;
try (InputStream in = new BufferedInputStream(inputStream)) {
OutputStream outFile = new FileOutputStream(resourceFileF);
out = new BufferedOutputStream(outFile);
int readBytes;
while ((readBytes = in.read()) != -1) {
out.write(readBytes);
}
} finally {
if (out != null) {
out.flush();
out.close();
}
}
return true;
}
/**
* Get operating system name, or OS_NAME_UNKNOWN
*
* @return OS name string
*/
public static String getOSName() {
return System.getProperty("os.name", OS_NAME_UNKNOWN); //NON-NLS
}
/**
* Get operating system version, or OS_VERSION_UNKNOWN
*
* @return OS version string
*/
public static String getOSVersion() {
return System.getProperty("os.version", OS_VERSION_UNKNOWN); //NON-NLS
}
/**
* Get OS arch details, or OS_ARCH_UNKNOWN
*
* @return OS arch string
*/
public static String getOSArch() {
return System.getProperty("os.arch", OS_ARCH_UNKNOWN); //NON-NLS
}
/**
* Check if running on Windows OS
*
* @return true if running on Windows OS
*/
public static boolean isWindowsOS() {
return PlatformUtil.getOSName().toLowerCase().contains("windows"); //NON-NLS
}
/**
* Convert file path (quote) for OS specific
*
* @param origFilePath
* @return converted file path
*/
public static String getOSFilePath(String origFilePath) {
if (isWindowsOS()) {
return "\"" + origFilePath + "\"";
} else {
return origFilePath;
}
}
/**
* Attempts to determine whether the operating system is a 64-bit operating
* system. May not be completely reliable for non-Windows operating systems.
*
* @return True if the operating system is definitely a 64-bit operating
* system, false otherwise.
*/
public static boolean is64BitOS() {
if (System.getProperty("os.name").contains("Windows")) {
return (System.getenv("ProgramFiles(x86)") != null);
} else {
return (System.getProperty("os.arch").contains("64"));
}
}
/**
* Get a list of all physical drives attached to the client's machine. Error
* threshold of 4 non-existent physical drives before giving up.
*
* @return list of physical drives
*/
public static List<LocalDisk> getPhysicalDrives() {
List<LocalDisk> drives = new ArrayList<>();
// Windows drives
if (PlatformUtil.isWindowsOS()) {
int n = 0;
int breakCount = 0;
while (true) {
String path = "\\\\.\\PhysicalDrive" + n; //NON-NLS
if (canReadDrive(path)) {
try {
drives.add(new LocalDisk("Drive " + n, path, SleuthkitJNI.findDeviceSize(path))); //NON-NLS
} catch (TskCoreException ex) {
// Don't add the drive because we can't read the size
}
n++;
} else {
if (breakCount > 4) { // Give up after 4 non-existent drives
break;
}
breakCount++;
n++;
}
}
// Linux drives
} else {
File dev = new File("/dev/");
File[] files = dev.listFiles();
for (File f : files) {
String name = f.getName();
if ((name.contains("hd") || name.contains("sd")) && f.canRead() && name.length() == 3) { //NON-NLS
String path = "/dev/" + name; //NON-NLS
if (canReadDrive(path)) {
try {
drives.add(new LocalDisk(path, path, SleuthkitJNI.findDeviceSize(path)));
} catch (TskCoreException ex) {
// Don't add the drive because we can't read the size
}
}
}
}
}
return drives;
}
/**
* Get a list all all the local drives and partitions on the client's
* machine.
*
* @return list of local drives and partitions
*/
public static List<LocalDisk> getPartitions() {
List<LocalDisk> drives = new ArrayList<>();
FileSystemView fsv = FileSystemView.getFileSystemView();
if (PlatformUtil.isWindowsOS()) {
File[] f = File.listRoots();
for (File f1 : f) {
String name = fsv.getSystemDisplayName(f1);
// Check if it is a drive, readable, and not mapped to the network
if (f1.canRead() && !name.contains("\\\\") && (fsv.isDrive(f1) || fsv.isFloppyDrive(f1))) {
String path = f1.getPath();
String diskPath = "\\\\.\\" + path.substring(0, path.length() - 1);
if (canReadDrive(diskPath)) {
drives.add(new LocalDisk(fsv.getSystemDisplayName(f1), diskPath, f1.getTotalSpace()));
}
}
}
} else {
File dev = new File("/dev/");
File[] files = dev.listFiles();
for (File f : files) {
String name = f.getName();
if ((name.contains("hd") || name.contains("sd")) && f.canRead() && name.length() == 4) { //NON-NLS
String path = "/dev/" + name; //NON-NLS
if (canReadDrive(path)) {
drives.add(new LocalDisk(path, path, f.getTotalSpace()));
}
}
}
}
return drives;
}
/**
* Are we able to read this drive? Usually related to admin permissions.
*
* For all drives and partitions, we are using Java's ability to read the
* first byte of a drive to determine if TSK would be able to read the drive
* during the add image process. This returns whether the drive is readable
* or not far faster than validating if TSK can open the drive. We are
* assuming the results are almost exactly the same.
*
* @param diskPath path to the disk we want to read
* @return true if we successfully read the first byte
* @throws IOException if we fail to read
*/
private static boolean canReadDrive(String diskPath) {
BufferedInputStream br = null;
try {
File tmp = new File(diskPath);
br = new BufferedInputStream(new FileInputStream(tmp));
int b = br.read();
return b != -1;
} catch (IOException ex) {
return false;
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException ex) {
}
}
}
/**
* Query and get PID of this process
*
* @return PID of this process or -1 if it couldn't be determined
*/
public static synchronized long getPID() {
if (pid != -1) {
return pid;
}
try {
if (sigar == null) {
sigar = org.sleuthkit.autopsy.corelibs.SigarLoader.getSigar();
}
if (sigar != null) {
pid = sigar.getPid();
} else {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getPID.sigarNotInit.msg"));
}
} catch (Exception e) {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getPID.gen.msg", e.toString()));
}
return pid;
}
/**
* Query and get PID of another java process
*
* @param sigarSubQuery a sigar subquery to identify a unique java process
* among other java processes, for example, by class name, use:
* Args.*.eq=org.jboss.Main more examples here:
* http://support.hyperic.com/display/SIGAR/PTQL
*
* @return PID of a java process or -1 if it couldn't be determined
*/
public static synchronized long getJavaPID(String sigarSubQuery) {
long jpid = -1;
final String sigarQuery = "State.Name.sw=java," + sigarSubQuery; //NON-NLS
try {
if (sigar == null) {
sigar = org.sleuthkit.autopsy.corelibs.SigarLoader.getSigar();
}
if (sigar != null) {
ProcessFinder finder = new ProcessFinder(sigar);
jpid = finder.findSingleProcess(sigarQuery);
} else {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getJavaPID.sigarNotInit.msg"));
}
} catch (Exception e) {
System.out.println(
NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getJavaPID.gen.msg", sigarQuery, e.toString()));
}
return jpid;
}
/**
* Query and get PIDs of another java processes matching a query
*
* @param sigarSubQuery a sigar subquery to identify a java processes among
* other java processes, for example, by class name, use:
* Args.*.eq=org.jboss.Main more examples here:
* http://support.hyperic.com/display/SIGAR/PTQL
*
* @return array of PIDs of a java processes matching the query or null if
* it couldn't be determined
*/
public static synchronized long[] getJavaPIDs(String sigarSubQuery) {
long[] jpids = null;
final String sigarQuery = "State.Name.sw=java," + sigarSubQuery; //NON-NLS
try {
if (sigar == null) {
sigar = org.sleuthkit.autopsy.corelibs.SigarLoader.getSigar();
}
if (sigar != null) {
ProcessFinder finder = new ProcessFinder(sigar);
jpids = finder.find(sigarQuery);
} else {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getJavaPIDs.sigarNotInit"));
}
} catch (Exception e) {
System.out.println(
NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getJavaPIDs.gen.msg", sigarQuery, e.toString()));
}
return jpids;
}
/**
* Kill a process by PID by sending signal to it using Sigar
*
* @param pid pid of the process to kill
*/
public static synchronized void killProcess(long pid) {
try {
if (sigar == null) {
sigar = org.sleuthkit.autopsy.corelibs.SigarLoader.getSigar();
}
if (sigar != null) {
sigar.kill(pid, 9);
} else {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.killProcess.sigarNotInit.msg"));
}
} catch (Exception e) {
System.out.println(
NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.killProcess.gen.msg", pid, e.toString()));
}
}
/**
* Query and return virtual memory used by the process
*
* @return virt memory used in bytes or -1 if couldn't be queried
*/
public static synchronized long getProcessVirtualMemoryUsed() {
long virtMem = -1;
try {
if (sigar == null) {
sigar = org.sleuthkit.autopsy.corelibs.SigarLoader.getSigar();
}
if (sigar == null || getPID() == -1) {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getProcVmUsed.sigarNotInit.msg"));
return -1;
}
virtMem = sigar.getProcMem(getPID()).getSize();
} catch (Exception e) {
System.out.println(NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.getProcVmUsed.gen.msg", e.toString()));
}
return virtMem;
}
/**
* Return formatted string with Jvm heap and non-heap memory usage
*
* @return formatted string with jvm memory usage
*/
public static String getJvmMemInfo() {
synchronized (PlatformUtil.class) {
if (memoryManager == null) {
memoryManager = ManagementFactory.getMemoryMXBean();
}
}
final MemoryUsage heap = memoryManager.getHeapMemoryUsage();
final MemoryUsage nonHeap = memoryManager.getNonHeapMemoryUsage();
return NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.getJvmMemInfo.usageText",
heap.toString(), nonHeap.toString());
}
/**
* Return formatted string with physical memory usage
*
* @return formatted string with physical memory usage
*/
public static String getPhysicalMemInfo() {
final Runtime runTime = Runtime.getRuntime();
final long maxMemory = runTime.maxMemory();
final long totalMemory = runTime.totalMemory();
final long freeMemory = runTime.freeMemory();
return NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.getPhysicalMemInfo.usageText",
Long.toString(maxMemory), Long.toString(totalMemory), Long.toString(freeMemory));
}
/**
* Return formatted string with all memory usage (jvm, physical, native)
*
* @return formatted string with all memory usage info
*/
public static String getAllMemUsageInfo() {
return NbBundle.getMessage(PlatformUtil.class,
"PlatformUtil.getAllMemUsageInfo.usageText",
PlatformUtil.getPhysicalMemInfo(), PlatformUtil.getJvmMemInfo(),
PlatformUtil.getProcessVirtualMemoryUsed());
}
}