Node: if the node is started twice, abort and tell the sysadmin the PID of the other instance.

This commit is contained in:
Mike Hearn 2016-02-05 17:32:00 +01:00
parent 7a70cdd4de
commit 5d0d926568

View File

@ -14,8 +14,12 @@ import core.messaging.*
import core.serialization.deserialize import core.serialization.deserialize
import core.serialization.serialize import core.serialization.serialize
import core.utilities.loggerFor import core.utilities.loggerFor
import java.io.RandomAccessFile
import java.lang.management.ManagementFactory
import java.nio.channels.FileLock
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardOpenOption
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.util.* import java.util.*
@ -65,17 +69,27 @@ class Node(val dir: Path, val myNetAddr: HostAndPort, val configuration: NodeCon
override val identityService: IdentityService get() = identity override val identityService: IdentityService get() = identity
} }
// TODO: Implement mutual exclusion so we can't start the node twice by accident. val storage: StorageService
val smm: StateMachineManager
val storage = makeStorageService(dir) val net: ArtemisMessagingService
val smm = StateMachineManager(services, serverThread) val wallet: WalletService
val net = ArtemisMessagingService(dir, myNetAddr) val keyManagement: E2ETestKeyManagementService
val wallet: WalletService = E2ETestWalletService(services)
val keyManagement = E2ETestKeyManagementService()
val inNodeTimestampingService: TimestamperNodeService? val inNodeTimestampingService: TimestamperNodeService?
val identity: IdentityService val identity: IdentityService
// Avoid the lock being garbage collected. We don't really need to release it as the OS will do so for us
// when our process shuts down, but we try in stop() anyway just to be nice.
private var nodeFileLock: FileLock? = null
init { init {
alreadyRunningNodeCheck()
storage = makeStorageService(dir)
smm = StateMachineManager(services, serverThread)
net = ArtemisMessagingService(dir, myNetAddr)
wallet = E2ETestWalletService(services)
keyManagement = E2ETestKeyManagementService()
// Insert a network map entry for the timestamper: this is all temp scaffolding and will go away. If we are // Insert a network map entry for the timestamper: this is all temp scaffolding and will go away. If we are
// given the details, the timestamping node is somewhere else. Otherwise, we do our own timestamping. // given the details, the timestamping node is somewhere else. Otherwise, we do our own timestamping.
val tsid = if (timestamperAddress != null) { val tsid = if (timestamperAddress != null) {
@ -101,6 +115,7 @@ class Node(val dir: Path, val myNetAddr: HostAndPort, val configuration: NodeCon
fun stop() { fun stop() {
net.stop() net.stop()
serverThread.shutdownNow() serverThread.shutdownNow()
nodeFileLock!!.release()
} }
fun makeStorageService(dir: Path): StorageService { fun makeStorageService(dir: Path): StorageService {
@ -152,6 +167,30 @@ class Node(val dir: Path, val myNetAddr: HostAndPort, val configuration: NodeCon
} }
} }
private fun alreadyRunningNodeCheck() {
// Write out our process ID (which may or may not resemble a UNIX process id - to us it's just a string) to a
// file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already
// exists, we try to take the file lock first before replacing it and if that fails it means we're being started
// twice with the same directory: that's a user error and we should bail out.
val pidPath = dir.resolve("process-id")
val file = pidPath.toFile()
if (file.exists()) {
val f = RandomAccessFile(file, "rw")
val l = f.channel.tryLock()
if (l == null) {
println("It appears there is already a node running with the specified data directory $dir")
println("Shut that other node down and try again. It may have process ID ${file.readText()}")
System.exit(1)
}
nodeFileLock = l
}
val ourProcessID: String = ManagementFactory.getRuntimeMXBean().name.split("@")[0]
Files.write(pidPath, ourProcessID.toByteArray(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
pidPath.toFile().deleteOnExit()
if (nodeFileLock == null)
nodeFileLock = RandomAccessFile(file, "rw").channel.lock()
}
companion object { companion object {
val PRIVATE_KEY_FILE_NAME = "identity-private-key" val PRIVATE_KEY_FILE_NAME = "identity-private-key"
val PUBLIC_IDENTITY_FILE_NAME = "identity-public" val PUBLIC_IDENTITY_FILE_NAME = "identity-public"