Repositories / gitweb2.git
gitweb2.git
Clone (read-only): git clone http://git.guha-anderson.com/git/gitweb2.git
@@ -1,5 +1,3 @@ -@file:OptIn(ExperimentalForeignApi::class) - package gitweb import io.ktor.http.ContentType @@ -15,13 +13,6 @@ import io.ktor.server.routing.get import io.ktor.server.routing.routing import io.ktor.utils.io.charsets.Charsets import io.matthewnelson.kmp.process.Process as KmpProcess -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.IntVar -import kotlinx.cinterop.alloc -import kotlinx.cinterop.memScoped -import kotlinx.cinterop.ptr -import kotlinx.cinterop.staticCFunction -import kotlinx.cinterop.value import kotlinx.git.Blob import kotlinx.git.Branch import kotlinx.git.BranchType @@ -31,14 +22,6 @@ import kotlinx.git.Repository import kotlinx.git.TreeEntryKind import okio.FileSystem import okio.Path.Companion.toPath -import platform.posix.SIGINT -import platform.posix.SIGKILL -import platform.posix.SIGTERM -import platform.posix._exit -import platform.posix.fork -import platform.posix.kill -import platform.posix.signal -import platform.posix.waitpid import kotlin.system.exitProcess private const val DefaultHost = "127.0.0.1" @@ -48,7 +31,7 @@ private const val DefaultPygmentsCommand = "pygmentize" fun runGitWeb2(args: Array<String>) { val config = parseArgs(args) ?: return - runServerProcess(config) + GitWebServer(config.root, config.host, config.port, config.pygmentsCommand).start() } private data class Config(val root: String, val host: String, val port: Int, val pygmentsCommand: String) @@ -83,7 +66,9 @@ internal class GitWebServer( } } try { - server.start(wait = true) + withTerminateSignalsHandled(onTerminate = { server.stop(500, 1000) }) { + server.start(wait = true) + } } finally { server.stop() Git.shutdown() @@ -372,34 +357,6 @@ private fun readCommandOutput(command: String, input: String? = null): String? { return output.stdout.replace("\r\n", "\n").replace('\r', '\n') } -private var childPidForSignal: Int = 0 - -private val parentSignalHandler = staticCFunction<Int, Unit> { signal -> - val childPid = childPidForSignal - if (childPid > 0) { - kill(childPid, SIGKILL) - } - _exit(128 + signal) -} - -private fun runServerProcess(config: Config) { - val childPid = fork() - when { - childPid < 0 -> GitWebServer(config.root, config.host, config.port, config.pygmentsCommand).start() - childPid == 0 -> GitWebServer(config.root, config.host, config.port, config.pygmentsCommand).start() - else -> waitForServerProcess(childPid) - } -} - -private fun waitForServerProcess(childPid: Int): Nothing = memScoped { - childPidForSignal = childPid - signal(SIGINT, parentSignalHandler) - signal(SIGTERM, parentSignalHandler) - val status = alloc<IntVar>() - waitpid(childPid, status.ptr, 0) - exitProcess(0) -} - private fun discoverRepos(root: String): List<RepoInfo> { val fileSystem = FileSystem.SYSTEM val found = mutableListOf<RepoInfo>()
@@ -0,0 +1,39 @@ +@file:OptIn(ExperimentalForeignApi::class) + +package gitweb + +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.staticCFunction +import platform.posix.SIG_DFL +import platform.posix.SIGINT +import platform.posix.SIGTERM +import platform.posix.signal +import kotlin.system.exitProcess + +/** + * Runs [block] with SIGINT and SIGTERM wired to [onTerminate]. Restores default handlers and + * clears state afterward. All POSIX / [staticCFunction] details stay in this file. + */ +internal inline fun withTerminateSignalsHandled(crossinline onTerminate: () -> Unit, block: () -> Unit) { + terminateAction = { onTerminate() } + signal(SIGINT, terminateSignalHandler) + signal(SIGTERM, terminateSignalHandler) + try { + block() + } finally { + signal(SIGINT, SIG_DFL) + signal(SIGTERM, SIG_DFL) + terminateAction = null + } +} + +private var terminateAction: (() -> Unit)? = null + +private val terminateSignalHandler = staticCFunction<Int, Unit> { _ -> + val action = terminateAction + if (action != null) { + action() + } else { + exitProcess(0) + } +}