From acddd1bba5d34653146dd96d5e76abef0e8ae39a Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 7 Sep 2023 09:54:44 +0200 Subject: [PATCH] [platform] revising startup-time system directory tests - dropping obsolete file lock test - postponing the no-exec test as it is not critical (IDEA-331084) GitOrigin-RevId: e304fae08f82dedb896d24836869a3a3dd7bee15 --- .../resources/messages/IdeBundle.properties | 1 + .../src/com/intellij/idea/StartupUtil.kt | 64 ++----------------- .../com/intellij/idea/SystemHealthMonitor.kt | 20 +++++- .../messages/BootstrapBundle.properties | 4 -- 4 files changed, 27 insertions(+), 62 deletions(-) diff --git a/platform/platform-api/resources/messages/IdeBundle.properties b/platform/platform-api/resources/messages/IdeBundle.properties index cf110b2e243a..2096cb4d9938 100644 --- a/platform/platform-api/resources/messages/IdeBundle.properties +++ b/platform/platform-api/resources/messages/IdeBundle.properties @@ -926,6 +926,7 @@ shell.env.loading.failed={0} has failed to load the environment from ''{1}''. \ Integration with tools that rely on environment variables may work incorrectly. shell.env.loading.learn.more=Learn more +temp.dir.exec.failed=The IDE cannot execute a test script in the temporary directory ''{0}''. Some things may work incorrectly. temp.dir.env.invalid=Environment variable ''{0}'' points to a non-existing directory (''{1}''). Some things may work incorrectly. unsupported.windows=This version of Windows is no longer supported. Some features may not work correctly. Please use Windows 10 1809 or newer. diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.kt b/platform/platform-impl/src/com/intellij/idea/StartupUtil.kt index d5e1a14d0fe1..c55b791a23c5 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.kt +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.kt @@ -51,13 +51,10 @@ import java.io.IOException import java.lang.invoke.MethodHandles import java.lang.invoke.MethodType import java.lang.management.ManagementFactory -import java.nio.channels.FileChannel import java.nio.file.Files import java.nio.file.InvalidPathException import java.nio.file.Path import java.nio.file.StandardOpenOption -import java.nio.file.attribute.PosixFileAttributeView -import java.nio.file.attribute.PosixFilePermission import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.* @@ -69,6 +66,7 @@ import java.util.logging.ConsoleHandler import java.util.logging.Level import javax.swing.* import kotlin.coroutines.CoroutineContext +import kotlin.io.path.deleteIfExists import kotlin.system.exitProcess internal const val IDE_STARTED: String = "------------------------------------------------------ IDE STARTED ------------------------------------------------------" @@ -491,48 +489,22 @@ private suspend fun doCheckSystemDirs(configPath: Path, systemPath: Path): Boole listOf( async { - checkDirectory(directory = configPath, - kind = "Config", - property = PathManager.PROPERTY_CONFIG_PATH, - checkWrite = true, - checkLock = true, - checkExec = false) + checkDirectory(configPath, kind = "Config", property = PathManager.PROPERTY_CONFIG_PATH, checkWrite = true) }, async { - checkDirectory(directory = systemPath, - kind = "System", - property = PathManager.PROPERTY_SYSTEM_PATH, - checkWrite = true, - checkLock = true, - checkExec = false) + checkDirectory(systemPath, kind = "System", property = PathManager.PROPERTY_SYSTEM_PATH, checkWrite = true) }, async { - checkDirectory(directory = logPath, - kind = "Log", - property = PathManager.PROPERTY_LOG_PATH, - checkWrite = !logPath.startsWith(systemPath), - checkLock = false, - checkExec = false) + checkDirectory(logPath, kind = "Log", property = PathManager.PROPERTY_LOG_PATH, checkWrite = true) }, async { - checkDirectory(directory = tempPath, - kind = "Temp", - property = PathManager.PROPERTY_SYSTEM_PATH, - checkWrite = !tempPath.startsWith(systemPath), - checkLock = false, - checkExec = SystemInfoRt.isUnix && !SystemInfoRt.isMac) - + checkDirectory(tempPath, kind = "Temp", property = PathManager.PROPERTY_SYSTEM_PATH, checkWrite = !tempPath.startsWith(systemPath)) } ).awaitAll().all { it } } } -private fun checkDirectory(directory: Path, - kind: String, - property: String, - checkWrite: Boolean, - checkLock: Boolean, - checkExec: Boolean): Boolean { +private fun checkDirectory(directory: Path, kind: String, property: String, checkWrite: Boolean): Boolean { var problem = "bootstrap.error.message.check.ide.directory.problem.cannot.create.the.directory" var reason = "bootstrap.error.message.check.ide.directory.possible.reason.path.is.incorrect" var tempFile: Path? = null @@ -542,33 +514,11 @@ private fun checkDirectory(directory: Path, reason = "bootstrap.error.message.check.ide.directory.possible.reason.directory.is.read.only.or.the.user.lacks.necessary.permissions" Files.createDirectories(directory) } - - if (checkWrite || checkLock || checkExec) { + if (checkWrite) { problem = "bootstrap.error.message.check.ide.directory.problem.the.ide.cannot.create.a.temporary.file.in.the.directory" reason = "bootstrap.error.message.check.ide.directory.possible.reason.directory.is.read.only.or.the.user.lacks.necessary.permissions" tempFile = directory.resolve("ij${Random().nextInt(Int.MAX_VALUE)}.tmp") Files.writeString(tempFile, "#!/bin/sh\nexit 0", StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) - if (checkLock) { - problem = "bootstrap.error.message.check.ide.directory.problem.the.ide.cannot.create.a.lock.in.directory" - reason = "bootstrap.error.message.check.ide.directory.possible.reason.the.directory.is.located.on.a.network.disk" - FileChannel.open(tempFile, EnumSet.of(StandardOpenOption.WRITE)).use { channel -> - channel.tryLock().use { lock -> - if (lock == null) { - throw IOException("File is locked") - } - } - } - } - else if (checkExec) { - problem = "bootstrap.error.message.check.ide.directory.problem.the.ide.cannot.execute.test.script" - reason = "bootstrap.error.message.check.ide.directory.possible.reason.partition.is.mounted.with.no.exec.option" - Files.getFileAttributeView(tempFile!!, PosixFileAttributeView::class.java) - .setPermissions(EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE)) - val exitCode = ProcessBuilder(tempFile.toAbsolutePath().toString()).start().waitFor() - if (exitCode != 0) { - throw IOException("Unexpected exit value: $exitCode") - } - } } return true } diff --git a/platform/platform-impl/src/com/intellij/idea/SystemHealthMonitor.kt b/platform/platform-impl/src/com/intellij/idea/SystemHealthMonitor.kt index cfc30400a5b9..2efe4b06dfb2 100644 --- a/platform/platform-impl/src/com/intellij/idea/SystemHealthMonitor.kt +++ b/platform/platform-impl/src/com/intellij/idea/SystemHealthMonitor.kt @@ -29,6 +29,7 @@ import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.SystemInfoRt +import com.intellij.openapi.util.io.NioFiles import com.intellij.platform.ide.CoreUiCoroutineScopeHolder import com.intellij.platform.ide.customization.ExternalProductResourceUrls import com.intellij.util.SystemProperties @@ -45,6 +46,7 @@ import java.nio.file.FileStore import java.nio.file.Files import java.nio.file.InvalidPathException import java.nio.file.Path +import java.util.concurrent.TimeUnit import kotlin.system.exitProcess import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -61,6 +63,7 @@ internal suspend fun startSystemHealthMonitor() { checkEnvironment() withContext(Dispatchers.IO) { checkSignalBlocking() + checkTempDirSanity() checkTempDirEnvVars() checkAncientOs() } @@ -280,6 +283,21 @@ private fun checkSignalBlocking() { } } +private fun checkTempDirSanity() { + if (SystemInfoRt.isUnix && !SystemInfoRt.isMac) { + try { + val probe = Files.createTempFile(Path.of(PathManager.getTempPath()), "ij-exec-check-", ".sh") + NioFiles.setExecutable(probe) + val process = ProcessBuilder(probe.toString()).start() + if (!process.waitFor(1, TimeUnit.MINUTES)) throw IOException("${probe} timed out") + if (process.exitValue() != 0) throw IOException("${probe} returned ${process.exitValue()}") + } + catch (e: Exception) { + showNotification("temp.dir.exec.failed", suppressable = false, action = null, shorten(PathManager.getTempPath())) + } + } +} + private fun checkTempDirEnvVars() { val envVars = if (SystemInfoRt.isWindows) sequenceOf("TMP", "TEMP") else sequenceOf("TMPDIR") for (name in envVars) { @@ -297,7 +315,7 @@ private fun checkTempDirEnvVars() { } private fun checkAncientOs() { - if (SystemInfo.isWindows) { + if (SystemInfoRt.isWindows) { val buildNumber = SystemInfo.getWinBuildNumber() if (buildNumber != null && buildNumber < 10000) { // 10 1507 = 10240, Server 2016 = 14393 showNotification("unsupported.windows", suppressable = true, null) diff --git a/platform/service-container/resources/messages/BootstrapBundle.properties b/platform/service-container/resources/messages/BootstrapBundle.properties index 61cf38d4d96a..fb4a438eaea4 100644 --- a/platform/service-container/resources/messages/BootstrapBundle.properties +++ b/platform/service-container/resources/messages/BootstrapBundle.properties @@ -31,13 +31,9 @@ bootstrap.error.title.configuration.wizard.failed=Configuration Wizard Failed bootstrap.error.message.check.ide.directory.problem.cannot.create.the.directory=The IDE cannot create the directory. bootstrap.error.message.check.ide.directory.problem.the.ide.cannot.create.a.temporary.file.in.the.directory=The IDE cannot create a temporary file in the directory. -bootstrap.error.message.check.ide.directory.problem.the.ide.cannot.create.a.lock.in.directory=The IDE cannot create a lock file in the directory. -bootstrap.error.message.check.ide.directory.problem.the.ide.cannot.execute.test.script=The IDE cannot execute a test script in the directory. bootstrap.error.message.check.ide.directory.possible.reason.path.is.incorrect=Possible reason: path is incorrect. bootstrap.error.message.check.ide.directory.possible.reason.directory.is.read.only.or.the.user.lacks.necessary.permissions=Possible reason: parent directory is read-only or the user lacks necessary permissions. -bootstrap.error.message.check.ide.directory.possible.reason.the.directory.is.located.on.a.network.disk=Possible reason: the directory is located on a network disk. -bootstrap.error.message.check.ide.directory.possible.reason.partition.is.mounted.with.no.exec.option=Possible reason: the partition is mounted with 'no exec' option. bootstrap.error.title.jetbrains.marketplace.boot.failure=JetBrains Marketplace Boot Failure