diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/ConfigManager.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/ConfigManager.java index c3ee042f4cd..8784a96581b 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/ConfigManager.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/ConfigManager.java @@ -12,9 +12,13 @@ import datadog.trace.util.RandomUtils; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -157,8 +161,8 @@ public StoredConfig build() { private ConfigManager() {} - private static String getBaseName(Path path) { - String filename = path.getFileName().toString(); + private static String getBaseName(File file) { + String filename = file.getName(); int dotIndex = filename.lastIndexOf('.'); if (dotIndex == -1) { return filename; @@ -185,18 +189,20 @@ private static void writeEntry(BufferedWriter writer, CharSequence key, CharSequ writer.newLine(); } - public static void writeConfigToPath(Path scriptPath, String... additionalEntries) { - String cfgFileName = getBaseName(scriptPath) + PID_PREFIX + PidHelper.getPid() + ".cfg"; - Path cfgPath = scriptPath.resolveSibling(cfgFileName); - writeConfigToFile(Config.get(), cfgPath, additionalEntries); + public static void writeConfigToPath(File scriptFile, String... additionalEntries) { + String cfgFileName = getBaseName(scriptFile) + PID_PREFIX + PidHelper.getPid() + ".cfg"; + File cfgFile = new File(scriptFile.getParentFile(), cfgFileName); + writeConfigToFile(Config.get(), cfgFile, additionalEntries); } // @VisibleForTesting - static void writeConfigToFile(Config config, Path cfgPath, String... additionalEntries) { + static void writeConfigToFile(Config config, File cfgFile, String... additionalEntries) { final WellKnownTags wellKnownTags = config.getWellKnownTags(); - LOGGER.debug("Writing config file: {}", cfgPath); - try (BufferedWriter bw = Files.newBufferedWriter(cfgPath)) { + LOGGER.debug("Writing config file: {}", cfgFile); + try (BufferedWriter bw = + new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(cfgFile), StandardCharsets.UTF_8))) { for (int i = 0; i < additionalEntries.length; i += 2) { writeEntry(bw, additionalEntries[i], additionalEntries[i + 1]); } @@ -217,27 +223,21 @@ static void writeConfigToFile(Config config, Path cfgPath, String... additionalE new Thread( AGENT_THREAD_GROUP, () -> { - try { - LOGGER.debug("Deleting config file: {}", cfgPath); - Files.deleteIfExists(cfgPath); - } catch (IOException e) { - LOGGER.warn(SEND_TELEMETRY, "Failed deleting config file: {}", cfgPath, e); - } + LOGGER.debug("Deleting config file: {}", cfgFile); + cfgFile.delete(); })); - LOGGER.debug("Config file written: {}", cfgPath); + LOGGER.debug("Config file written: {}", cfgFile); } catch (IOException e) { - LOGGER.warn(SEND_TELEMETRY, "Failed writing config file: {}", cfgPath); - try { - Files.deleteIfExists(cfgPath); - } catch (IOException ignored) { - // ignore - } + LOGGER.warn(SEND_TELEMETRY, "Failed writing config file: {}", cfgFile); + cfgFile.delete(); } } @Nullable - public static StoredConfig readConfig(Config config, Path scriptPath) { - try (final BufferedReader reader = Files.newBufferedReader(scriptPath)) { + public static StoredConfig readConfig(Config config, File scriptFile) { + try (final BufferedReader reader = + new BufferedReader( + new InputStreamReader(new FileInputStream(scriptFile), StandardCharsets.UTF_8))) { final StoredConfig.Builder cfgBuilder = new StoredConfig.Builder(config); String line; while ((line = reader.readLine()) != null) { @@ -284,7 +284,7 @@ public static StoredConfig readConfig(Config config, Path scriptPath) { } return cfgBuilder.build(); } catch (Throwable t) { - LOGGER.error("Failed to read config file: {}", scriptPath, t); + LOGGER.error("Failed to read config file: {}", scriptFile, t); } return null; } diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploaderScriptInitializer.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploaderScriptInitializer.java index fb67fcec9ae..26be13fd311 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploaderScriptInitializer.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/CrashUploaderScriptInitializer.java @@ -2,13 +2,9 @@ import static datadog.crashtracking.ConfigManager.writeConfigToPath; import static datadog.crashtracking.Initializer.LOG; -import static datadog.crashtracking.Initializer.RWXRWXRWX; -import static datadog.crashtracking.Initializer.R_XR_XR_X; import static datadog.crashtracking.Initializer.findAgentJar; import static datadog.crashtracking.Initializer.getCrashUploaderTemplate; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; -import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute; -import static java.nio.file.attribute.PosixFilePermissions.fromString; import static java.util.Locale.ROOT; import datadog.environment.SystemProperties; @@ -16,13 +12,13 @@ import datadog.trace.util.Strings; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; public final class CrashUploaderScriptInitializer { private static final String SETUP_FAILURE_MESSAGE = "Crash tracking will not work properly."; @@ -54,71 +50,70 @@ static void initialize(String onErrorVal, String onErrorFile, String javacorePat return; } - Path scriptPath = Paths.get(onErrorVal.replace(" %p", "")); + File scriptFile = new File(onErrorVal.replace(" %p", "")); boolean isDDCrashUploader = - scriptPath.getFileName().toString().toLowerCase(ROOT).contains("dd_crash_uploader"); - if (isDDCrashUploader && !copyCrashUploaderScript(scriptPath, onErrorFile, agentJar)) { + scriptFile.getName().toLowerCase(ROOT).contains("dd_crash_uploader"); + if (isDDCrashUploader && !copyCrashUploaderScript(scriptFile, onErrorFile, agentJar)) { return; } if (javacorePath != null && !javacorePath.isEmpty()) { - writeConfigToPath(scriptPath, "agent", agentJar, "javacore_path", javacorePath); + writeConfigToPath(scriptFile, "agent", agentJar, "javacore_path", javacorePath); } else { - writeConfigToPath(scriptPath, "agent", agentJar, "hs_err", onErrorFile); + writeConfigToPath(scriptFile, "agent", agentJar, "hs_err", onErrorFile); } } private static boolean copyCrashUploaderScript( - Path scriptPath, String onErrorFile, String agentJar) { - Path scriptDirectory = scriptPath.getParent(); - try { - Files.createDirectories(scriptDirectory, asFileAttribute(fromString(RWXRWXRWX))); - } catch (UnsupportedOperationException e) { - LOG.warn( - SEND_TELEMETRY, - "Unsupported permissions '" + RWXRWXRWX + "' for {}. " + SETUP_FAILURE_MESSAGE, - scriptDirectory); - return false; - } catch (FileAlreadyExistsException ignored) { - // can be safely ignored; if the folder exists we will just reuse it - if (!Files.isWritable(scriptDirectory)) { + File scriptFile, String onErrorFile, String agentJar) { + File scriptDirectory = scriptFile.getParentFile(); + if (!scriptDirectory.exists()) { + if (!scriptDirectory.mkdirs()) { LOG.warn( - SEND_TELEMETRY, "Read only directory {}. " + SETUP_FAILURE_MESSAGE, scriptDirectory); + SEND_TELEMETRY, + "Failed to create writable crash tracking script folder {}. " + SETUP_FAILURE_MESSAGE, + scriptDirectory); return false; } - } catch (IOException e) { - LOG.warn( - SEND_TELEMETRY, - "Failed to create writable crash tracking script folder {}. " + SETUP_FAILURE_MESSAGE, - scriptDirectory); + scriptDirectory.setReadable(true, false); + scriptDirectory.setWritable(true, false); + scriptDirectory.setExecutable(true, false); + } + if (!scriptDirectory.canWrite()) { + LOG.warn(SEND_TELEMETRY, "Read only directory {}. " + SETUP_FAILURE_MESSAGE, scriptDirectory); return false; } try { - LOG.debug("Writing crash uploader script: {}", scriptPath); - writeCrashUploaderScript(getCrashUploaderTemplate(), scriptPath, agentJar, onErrorFile); + LOG.debug("Writing crash uploader script: {}", scriptFile); + writeCrashUploaderScript(getCrashUploaderTemplate(), scriptFile, agentJar, onErrorFile); } catch (IOException e) { LOG.warn( SEND_TELEMETRY, "Failed to copy crash tracking script {}. " + SETUP_FAILURE_MESSAGE, - scriptPath); + scriptFile); return false; } return true; } private static void writeCrashUploaderScript( - InputStream template, Path scriptPath, String execClass, String crashFile) + InputStream template, File scriptFile, String execClass, String crashFile) throws IOException { - if (!Files.exists(scriptPath)) { + if (!scriptFile.exists()) { try (BufferedReader br = new BufferedReader(new InputStreamReader(template)); - BufferedWriter bw = Files.newBufferedWriter(scriptPath)) { + BufferedWriter bw = + new BufferedWriter( + new OutputStreamWriter( + new FileOutputStream(scriptFile), StandardCharsets.UTF_8))) { String line; while ((line = br.readLine()) != null) { bw.write(template(line, execClass, crashFile)); bw.newLine(); } } - Files.setPosixFilePermissions(scriptPath, fromString(R_XR_XR_X)); + scriptFile.setReadable(true, false); + scriptFile.setWritable(false, false); + scriptFile.setExecutable(true, false); } } diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/Initializer.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/Initializer.java index f7e0be0c48c..0aeca69ed00 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/Initializer.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/Initializer.java @@ -1,7 +1,6 @@ package datadog.crashtracking; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; -import static java.util.Comparator.reverseOrder; import static java.util.Locale.ROOT; import com.datadoghq.profiler.JVMAccess; @@ -12,25 +11,19 @@ import datadog.libs.ddprof.DdprofLibraryLoader; import datadog.trace.api.Platform; import datadog.trace.util.TempLocationManager; -import java.io.IOException; +import java.io.File; import java.io.InputStream; import java.lang.management.ManagementFactory; import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.Arrays; import java.util.List; import java.util.StringTokenizer; -import java.util.function.Predicate; -import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class Initializer { static final Logger LOG = LoggerFactory.getLogger(Initializer.class); static final String PID_PREFIX = "_pid"; - static final String RWXRWXRWX = "rwxrwxrwx"; - static final String R_XR_XR_X = "r-xr-xr-x"; private interface FlagAccess { String getValue(String flagName); @@ -251,8 +244,8 @@ private static boolean isXdumpToolConfigured() { */ private static String getJ9CrashUploaderScriptPath() { String scriptFileName = getScriptFileName("dd_crash_uploader"); - Path scriptPath = TempLocationManager.getInstance().getTempDir().resolve(scriptFileName); - return scriptPath.toString(); + String tempDir = TempLocationManager.getInstance().getTempDir().toString(); + return tempDir + File.separator + scriptFileName; } static InputStream getCrashUploaderTemplate() { @@ -280,19 +273,11 @@ static String findAgentJar() { else if (selfClass.startsWith("file:")) { int idx = selfClass.lastIndexOf("dd-java-agent"); if (idx > -1) { - Path libsPath = Paths.get(selfClass.substring(5, idx + 13), "build", "libs"); - try (Stream files = Files.walk(libsPath)) { - Predicate isJarFile = - p -> p.getFileName().toString().toLowerCase(ROOT).endsWith(".jar"); - agentPath = - files - .sorted(reverseOrder()) - .filter(isJarFile) - .findFirst() - .map(Path::toString) - .orElse(null); - } catch (IOException ignored) { - // Ignore failure to get agent path + File libsDir = new File(selfClass.substring(5, idx + 13), "build/libs"); + File[] jars = libsDir.listFiles(f -> f.getName().toLowerCase(ROOT).endsWith(".jar")); + if (jars != null && jars.length > 0) { + Arrays.sort(jars, (a, b) -> b.getName().compareTo(a.getName())); + agentPath = jars[0].getAbsolutePath(); } } } diff --git a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/OOMENotifierScriptInitializer.java b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/OOMENotifierScriptInitializer.java index 668dfecd612..c558d3a83a5 100644 --- a/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/OOMENotifierScriptInitializer.java +++ b/dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/OOMENotifierScriptInitializer.java @@ -2,26 +2,17 @@ import static datadog.crashtracking.ConfigManager.writeConfigToPath; import static datadog.crashtracking.Initializer.LOG; -import static datadog.crashtracking.Initializer.RWXRWXRWX; -import static datadog.crashtracking.Initializer.R_XR_XR_X; import static datadog.crashtracking.Initializer.findAgentJar; import static datadog.crashtracking.Initializer.getOomeNotifierTemplate; import static datadog.crashtracking.Initializer.getScriptPathFromArg; import static datadog.crashtracking.Initializer.pidFromSpecialFileName; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; -import static java.nio.file.FileVisitResult.CONTINUE; -import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute; -import static java.nio.file.attribute.PosixFilePermissions.fromString; import datadog.trace.util.PidHelper; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.FileVisitResult; -import java.nio.file.FileVisitor; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.BasicFileAttributes; +import java.io.InputStream; import java.util.Set; public final class OOMENotifierScriptInitializer { @@ -37,8 +28,8 @@ static void initialize(String onOutOfMemoryVal) { "'-XX:OnOutOfMemoryError' argument was not provided. OOME tracking is disabled."); return; } - Path scriptPath = getOOMEScriptPath(onOutOfMemoryVal); - if (scriptPath == null) { + File scriptFile = getOOMEScriptFile(onOutOfMemoryVal); + if (scriptFile == null) { LOG.error( SEND_TELEMETRY, "OOME notifier script value ({}) does not follow the expected format: /dd_oome_notifier.(sh|bat) %p. OOME tracking is disabled.", @@ -52,123 +43,99 @@ static void initialize(String onOutOfMemoryVal) { "Unable to locate the agent jar. OOME notification will not work properly."); return; } - if (!copyOOMEscript(scriptPath)) { + if (!copyOOMEscript(scriptFile)) { return; } - writeConfigToPath(scriptPath, "agent", agentJar); + writeConfigToPath(scriptFile, "agent", agentJar); } - private static Path getOOMEScriptPath(String onOutOfMemoryVal) { + private static File getOOMEScriptFile(String onOutOfMemoryVal) { String path = getScriptPathFromArg(onOutOfMemoryVal, OOME_NOTIFIER_SCRIPT_PREFIX); - return path == null ? null : Paths.get(path); + return path == null ? null : new File(path); } - private static boolean copyOOMEscript(Path scriptPath) { - Path scriptDirectory = scriptPath.getParent(); + private static boolean copyOOMEscript(File scriptFile) { + File scriptDirectory = scriptFile.getParentFile(); // cleanup all stale process-specific generated files in the parent folder of the given OOME // notifier script - ScriptCleanupVisitor.run(scriptDirectory); - - try { - if (Files.exists(scriptDirectory)) { - // can be safely ignored; if the folder exists we will just reuse it - if (!Files.isWritable(scriptDirectory)) { - LOG.warn( - SEND_TELEMETRY, - "Read only directory {}. OOME notification will not work properly.", - scriptDirectory); - return false; - } - } else { - Files.createDirectories(scriptDirectory, asFileAttribute(fromString(RWXRWXRWX))); + runScriptCleanup(scriptDirectory); + + if (scriptDirectory.exists()) { + // can be safely ignored; if the folder exists we will just reuse it + if (!scriptDirectory.canWrite()) { + LOG.warn( + SEND_TELEMETRY, + "Read only directory {}. OOME notification will not work properly.", + scriptDirectory); + return false; } - } catch (UnsupportedOperationException e) { - LOG.warn( - SEND_TELEMETRY, - "Unsupported permissions '{" - + RWXRWXRWX - + "' for {}. OOME notification will not work properly.", - scriptDirectory); - return false; - } catch (FileAlreadyExistsException ignored) { - LOG.warn(SEND_TELEMETRY, "Path {} already exists and is not a directory.", scriptDirectory); - return false; - } catch (IOException e) { - LOG.warn( - SEND_TELEMETRY, - "Failed to create writable OOME script folder {}. OOME notification will not work properly.", - scriptDirectory); - return false; + } else { + if (!scriptDirectory.mkdirs()) { + LOG.warn( + SEND_TELEMETRY, + "Failed to create writable OOME script folder {}. OOME notification will not work properly.", + scriptDirectory); + return false; + } + scriptDirectory.setReadable(true, false); + scriptDirectory.setWritable(true, false); + scriptDirectory.setExecutable(true, false); } try { // do not overwrite existing - if (!Files.exists(scriptPath)) { - Files.copy(getOomeNotifierTemplate(), scriptPath); + if (!scriptFile.exists()) { + copyStream(getOomeNotifierTemplate(), scriptFile); } - Files.setPosixFilePermissions(scriptPath, fromString(R_XR_XR_X)); + scriptFile.setReadable(true, false); + scriptFile.setWritable(false, false); + scriptFile.setExecutable(true, false); } catch (IOException e) { LOG.warn( SEND_TELEMETRY, "Failed to copy OOME script {}. OOME notification will not work properly.", - scriptPath); + scriptFile); return false; } return true; } - private static class ScriptCleanupVisitor implements FileVisitor { - private Set pidSet; - - static void run(Path dir) { - try { - if (Files.exists(dir)) { - Files.walkFileTree(dir, new ScriptCleanupVisitor()); - } - } catch (IOException e) { - if (LOG.isDebugEnabled()) { - LOG.info("Failed cleaning up process specific files in {}", dir, e); - } else { - LOG.info("Failed cleaning up process specific files in {}: {}", dir, e.toString()); - } + private static void copyStream(InputStream in, File dest) throws IOException { + try (InputStream src = in; + FileOutputStream out = new FileOutputStream(dest)) { + byte[] buf = new byte[4096]; + int n; + while ((n = src.read(buf)) >= 0) { + out.write(buf, 0, n); } } + } - private ScriptCleanupVisitor() {} - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { - return CONTINUE; + private static void runScriptCleanup(File dir) { + if (!dir.exists()) { + return; } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String fileName = file.getFileName().toString(); - String pid = pidFromSpecialFileName(fileName); + File[] files = dir.listFiles(); + if (files == null) { + return; + } + Set pidSet = null; + for (File file : files) { + if (!file.isFile()) { + continue; + } + String pid = pidFromSpecialFileName(file.getName()); if (pid != null && !pid.equals(PidHelper.getPid())) { - if (this.pidSet == null) { - // if pidSet is not initialized, initialize it - // this will fork jps to get the list of Java PIDs - this.pidSet = PidHelper.getJavaPids(); + if (pidSet == null) { + // lazy init: forks jps to get the list of running Java PIDs + pidSet = PidHelper.getJavaPids(); } - if (!this.pidSet.contains(pid)) { + if (!pidSet.contains(pid)) { LOG.debug("Cleaning process specific file {}", file); - Files.delete(file); + file.delete(); } } - return CONTINUE; - } - - @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) { - LOG.debug("Failed to delete file {}", file, exc); - return CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) { - return CONTINUE; } } } diff --git a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/ConfigManagerTest.java b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/ConfigManagerTest.java index e46150f4979..a850cb9fc43 100644 --- a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/ConfigManagerTest.java +++ b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/ConfigManagerTest.java @@ -28,9 +28,8 @@ public void testConfigWriteAndRead() throws IOException { when(config.getMergedCrashTrackingTags()).thenReturn(Collections.singletonMap("key", "value")); File tmpFile = File.createTempFile("ConfigManagerTest", null); tmpFile.deleteOnExit(); - ConfigManager.writeConfigToFile(config, tmpFile.toPath()); - ConfigManager.StoredConfig deserialized = - ConfigManager.readConfig(Config.get(), tmpFile.toPath()); + ConfigManager.writeConfigToFile(config, tmpFile); + ConfigManager.StoredConfig deserialized = ConfigManager.readConfig(Config.get(), tmpFile); Assertions.assertNotNull(deserialized); assertEquals("service", deserialized.service); assertEquals("version", deserialized.version); diff --git a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentCLI.java b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentCLI.java index cbb065eecb5..e18aca9da42 100644 --- a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentCLI.java +++ b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentCLI.java @@ -84,12 +84,12 @@ public static void uploadCrash(final String configFile, final String file) throw String error = null; ConfigManager.StoredConfig storedConfig = null; if (configFile != null) { - Path configPath = Paths.get(configFile); - if (!Files.exists(configPath)) { + File configFilePath = new File(configFile); + if (!configFilePath.exists()) { log.error("Config file {} does not exist", configFile); error = "Config file does not exist"; } - storedConfig = readConfig(Config.get(), configPath); + storedConfig = readConfig(Config.get(), configFilePath); if (storedConfig == null) { log.error("Unable to parse config file {}", configFile); if (error == null) {