Nothing
# R/osrm_stop_server.R
#' List OSRM servers
#'
#' @description
#' `r lifecycle::badge("stable")`
#'
#' Lists `osrm-routed` processes. By default, it returns a snapshot of servers
#' started by the current R session (registered via [osrm_start_server()] or [osrm_start()]).
#' You can optionally list all `osrm-routed` processes running on the system,
#' including those started by other sessions or manually.
#'
#' You can stop a server by passing its `id`, `port`, or `pid` to [osrm_stop()].
#'
#' @param include_all Logical; if `TRUE`, scans the system process table for
#' all `osrm-routed` processes, including those not started by this package
#' in the current session. Default is `FALSE`.
#' @param output Character string specifying the return format. Either `"data.frame"`
#' (the default) which returns a tabular summary with a custom print method,
#' or `"list"` which returns a detailed list of server metadata objects.
#'
#' @return If `output = "data.frame"`, returns a `data.frame` (class `osrm_server_list`)
#' of OSRM job processes with columns:
#' `id`, `pid`, `port`, `algorithm`, `started_at`, `alive`, `has_handle`, `log`, `input_osm`,
#' `center_lon`, `center_lat`. External servers will have `id` prefixed with `sys-` and
#' `log` set to `<external>`. If `output = "list"`, returns a named list of server metadata.
#' @examples
#' \donttest{
#' if (identical(Sys.getenv("OSRM_EXAMPLES"), "true")) {
#' install_dir <- osrm_install(
#' version = "latest",
#' path_action = "session",
#' quiet = TRUE
#' )
#'
#' # copy example OSM PBF into a temporary workspace to avoid polluting pkg data
#' pbf_path <- system.file("extdata/cur.osm.pbf", package = "osrm.backend")
#' osrm_dir <- file.path(tempdir(), paste0("osrm-", Sys.getpid()))
#' dir.create(osrm_dir, recursive = TRUE)
#' tmp_pbf <- file.path(osrm_dir, "cur.osm.pbf")
#' file.copy(from = pbf_path, to = tmp_pbf, overwrite = TRUE)
#' graph <- osrm_prepare_graph(tmp_pbf, overwrite = TRUE, threads = 1L)
#'
#' srv <- osrm_start_server(graph$osrm_job_artifact, port = 6000, threads = 1L)
#' osrm_servers()
#' osrm_stop(srv)
#'
#' osrm_uninstall(
#' dest_dir = install_dir,
#' clear_path = TRUE,
#' force = TRUE,
#' quiet = TRUE
#' )
#' unlink(osrm_dir, recursive = TRUE)
#' }
#' }
#' @export
osrm_servers <- function(include_all = FALSE, output = c("data.frame", "list")) {
output <- match.arg(output)
# 1. Background Cleanup: Always scan other registries to GC dead files
# This ensures orphaned registry files from crashed sessions are pruned.
orphans <- tryCatch(.osrm_registry_scan_others(), error = function(e) list())
# 2. Internal Registry (Current Session)
reg <- .osrm_state$registry
# Helper to build an empty/structured data.frame
build_df <- function(ids, pids, ports, algos, starts, alives, handles, logs, inputs, center_lons, center_lats) {
df <- data.frame(
id = as.character(ids),
pid = as.integer(pids),
port = as.integer(ports),
algorithm = as.character(algos),
started_at = as.POSIXct(starts),
alive = as.logical(alives),
has_handle = as.logical(handles),
log = as.character(logs),
input_osm = as.character(inputs),
center_lon = as.numeric(center_lons),
center_lat = as.numeric(center_lats),
stringsAsFactors = FALSE
)
class(df) <- c("osrm_server_list", "data.frame")
df
}
out_reg <- if (length(reg)) {
alive_vec <- logical(length(reg))
handle_vec <- logical(length(reg))
for (i in seq_along(reg)) {
e <- reg[[i]]
if (!is.null(e$proc) && inherits(e$proc, "process")) {
handle_vec[i] <- TRUE
alive_vec[i] <- tryCatch(e$proc$is_alive(), error = function(...) FALSE)
} else {
handle_vec[i] <- FALSE
alive_vec[i] <- .osrm_pid_is_running(e$pid)
}
}
build_df(
ids = vapply(reg, `[[`, "", "id"),
pids = vapply(reg, function(e) as.integer(e$pid %||% NA_integer_), 0L),
ports = vapply(reg, function(e) as.integer(e$port %||% NA_integer_), 0L),
algos = vapply(reg, function(e) as.character(e$algorithm %||% ""), ""),
starts = vapply(reg, function(e) as.character(e$started_at %||% NA_character_), ""),
alives = alive_vec,
handles = handle_vec,
logs = vapply(reg, function(e) as.character(e$log %||% ""), ""),
inputs = vapply(reg, function(e) as.character(e$input_osm %||% ""), ""),
center_lons = vapply(reg, function(e) as.numeric(e$center_lon %||% NA_real_), 0.0),
center_lats = vapply(reg, function(e) as.numeric(e$center_lat %||% NA_real_), 0.0)
)
} else {
build_df(character(), integer(), integer(), character(), character(), logical(), logical(), character(), character(), numeric(), numeric())
}
# Merge: Current + Orphans + System
res <- out_reg
if (isTRUE(include_all)) {
# 3. Process Orphans/Foreign (already scanned in Step 1)
out_orph <- if (length(orphans)) {
build_df(
ids = vapply(orphans, `[[`, "", "id"),
pids = vapply(orphans, `[[`, 0L, "pid"),
ports = vapply(orphans, `[[`, 0L, "port"),
algos = vapply(orphans, function(e) as.character(e$algorithm %||% ""), ""),
starts = vapply(orphans, function(e) as.character(e$started_at %||% NA_character_), ""),
alives = rep(TRUE, length(orphans)),
handles = rep(FALSE, length(orphans)),
logs = vapply(orphans, function(e) as.character(e$log %||% ""), ""),
inputs = vapply(orphans, function(e) as.character(e$input_osm %||% ""), ""),
center_lons = vapply(orphans, function(e) as.numeric(e$center_lon %||% NA_real_), 0.0),
center_lats = vapply(orphans, function(e) as.numeric(e$center_lat %||% NA_real_), 0.0)
)
} else {
build_df(character(), integer(), integer(), character(), character(), logical(), logical(), character(), character(), numeric(), numeric())
}
# 3. System Process Discovery via ps
sys_procs <- tryCatch(ps::ps(), error = function(e) NULL)
sys_rows <- if (!is.null(sys_procs)) {
# Filter for osrm-routed
is_osrm <- grepl("osrm-routed", sys_procs$name, ignore.case = TRUE)
sys_osrm <- sys_procs[is_osrm, ]
if (nrow(sys_osrm) > 0) {
ext_ids <- character()
ext_pids <- integer()
ext_ports <- integer()
ext_algos <- character()
ext_starts <- as.POSIXct(character())
ext_alives <- logical()
ext_handles <- logical()
ext_logs <- character()
ext_inputs <- character()
ext_center_lons <- numeric()
ext_center_lats <- numeric()
# PIDs we already know about (Current + Orphans)
known_pids <- c(out_reg$pid, out_orph$pid)
for (i in seq_len(nrow(sys_osrm))) {
p <- sys_osrm[i, ]
this_pid <- p$pid
if (this_pid %in% known_pids) next
# Parse command line
cmd <- tryCatch(ps::ps_cmdline(p), error = function(e) character())
# Defaults
port <- 5000L
algo <- "CH"
input <- NA_character_
# Simple parsing logic
if (length(cmd) > 1) {
# Port
idx_p <- which(cmd %in% c("-p", "--port"))
if (length(idx_p) && (idx_p[1] + 1 <= length(cmd))) {
val <- suppressWarnings(as.integer(cmd[idx_p[1] + 1]))
if (!is.na(val)) port <- val
}
# Algorithm
idx_a <- which(cmd %in% c("-a", "--algorithm"))
if (length(idx_a) && (idx_a[1] + 1 <= length(cmd))) {
algo <- cmd[idx_a[1] + 1]
}
# Input: heuristic - last arg that doesn't start with "-"
args <- cmd[-1]
non_flag <- args[!startsWith(args, "-")]
if (length(non_flag) > 0) {
input <- utils::tail(non_flag, 1)
}
}
ext_ids <- c(ext_ids, paste0("sys-", this_pid))
ext_pids <- c(ext_pids, this_pid)
ext_ports <- c(ext_ports, port)
ext_algos <- c(ext_algos, algo)
ext_starts <- c(ext_starts, NA)
ext_alives <- c(ext_alives, TRUE)
ext_handles <- c(ext_handles, FALSE)
ext_logs <- c(ext_logs, "<external>")
ext_inputs <- c(ext_inputs, if (is.na(input)) "" else input)
ext_center_lons <- c(ext_center_lons, NA_real_)
ext_center_lats <- c(ext_center_lats, NA_real_)
}
if (length(ext_pids) > 0) {
build_df(ext_ids, ext_pids, ext_ports, ext_algos, ext_starts, ext_alives, ext_handles, ext_logs, ext_inputs, ext_center_lons, ext_center_lats)
} else {
NULL
}
} else {
NULL
}
} else {
NULL
}
if (nrow(out_orph) > 0) res <- rbind(res, out_orph)
if (!is.null(sys_rows) && nrow(sys_rows) > 0) res <- rbind(res, sys_rows)
}
if (output == "list") {
if (nrow(res) == 0) return(list())
# Strip the custom class so split/as.list behave like a plain data.frame
df_raw <- res
class(df_raw) <- "data.frame"
# We must explicitly convert the data.frame to a list of lists.
# split() on plain data.frame creates a list of 1-row dataframes.
row_dfs <- split(df_raw, seq_len(nrow(df_raw)))
out_list <- lapply(row_dfs, function(df) {
lst <- as.list(df)
# Flatten 1-element vectors to scalars to match 'metadata list' expectation
lapply(lst, function(x) x[[1]])
})
names(out_list) <- res$id
return(out_list)
}
res
}
#' @export
print.osrm_server_list <- function(x, ...) {
n <- nrow(x)
if (n == 0) {
cat("\u2139 No OSRM servers registered.\n")
return(invisible(x))
}
cat("\u2139 This is a standard data.frame. Access full columns with `$` (e.g., `x$log`).\n\n")
for (i in seq_len(n)) {
status <- if (x$alive[i]) "RUNNING" else "DEAD (Orphaned)"
has_handle <- if (x$has_handle[i]) "Yes" else "No"
center_str <- if (is.na(x$center_lon[i]) || is.na(x$center_lat[i])) {
"NA, NA"
} else {
sprintf("%.4f, %.4f", x$center_lon[i], x$center_lat[i])
}
started <- if (is.na(x$started_at[i])) "Unknown" else format(x$started_at[i], "%Y-%m-%d %H:%M:%S")
cat(sprintf("[%d] OSRM Server (pid: %d, port: %d)\n", i, x$pid[i], x$port[i]))
cat(sprintf(" Status : %s\n", status))
cat(sprintf(" Algorithm : %s\n", x$algorithm[i]))
cat(sprintf(" Started : %s\n", started))
cat(sprintf(" Input OSM : %s\n", x$input_osm[i]))
cat(sprintf(" Log File : %s\n", x$log[i]))
cat(sprintf(" Center : %s\n", center_str))
cat(sprintf(" Handle : %s\n", has_handle))
if (i < n) cat("\n")
}
invisible(x)
}
#' Stop an OSRM Server
#'
#' @description
#' `r lifecycle::badge("stable")`
#'
#' Terminates an `osrm-routed` process launched by `osrm_start()` or
#' `osrm_start_server()`. Can also stop external servers by PID or ID.
#'
#' @details
#' This function provides a flexible way to stop a running OSRM process. If no
#' arguments are specified, it defaults to stopping the most recently started
#' server that is still alive in the current session.
#'
#' You can also stop a specific server by providing:
#' \itemize{
#' \item The OSRM job process (a `processx::process` object) returned by
#' `osrm_start()` or `osrm_start_server()`.
#' \item The server's `id`, `port`, or `pid` (use `osrm_servers()` to find these).
#' }
#'
#' **Advanced Use:**
#' You can stop an external `osrm-routed` process (one not started by the current
#' R session) by passing its PID, or by finding it via `osrm_servers(include_all = TRUE)`
#' and passing its `id` or `port`. This requires permission to signal the process.
#'
#' @param server Optional OSRM job process (an `osrm_server` object
#' inheriting from `processx::process`) returned by `osrm_start_server()`.
#' @param id Optional character id from `osrm_servers()`.
#' @param port Optional integer TCP port.
#' @param pid Optional integer process id.
#' @param wait Integer milliseconds to wait for clean shutdown (default `1000`).
#' @param quiet Logical; suppress messages (default `FALSE`).
#'
#' @return A list with fields `id`, `pid`, `port`, `stopped` (logical).
#' @export
#' @examples
#' \donttest{
#' if (identical(Sys.getenv("OSRM_EXAMPLES"), "true")) {
#' install_dir <- osrm_install(
#' version = "latest",
#' path_action = "session",
#' quiet = TRUE
#' )
#'
#' # copy example OSM PBF into a temporary workspace to avoid polluting pkg data
#' pbf_path <- system.file("extdata/cur.osm.pbf", package = "osrm.backend")
#' osrm_dir <- file.path(tempdir(), paste0("osrm-", Sys.getpid()))
#' dir.create(osrm_dir, recursive = TRUE)
#' tmp_pbf <- file.path(osrm_dir, "cur.osm.pbf")
#' file.copy(from = pbf_path, to = tmp_pbf, overwrite = TRUE)
#' graph <- osrm_prepare_graph(tmp_pbf, overwrite = TRUE, threads = 1L)
#'
#' srv <- osrm_start_server(graph$osrm_job_artifact, port = 6000, threads = 1L)
#'
#' # Stop by passing the process object
#' osrm_stop(srv)
#'
#' # Or stop by port after the process is registered
#' osrm_stop(port = 6000)
#'
#' osrm_uninstall(
#' dest_dir = install_dir,
#' clear_path = TRUE,
#' force = TRUE,
#' quiet = TRUE
#' )
#' unlink(osrm_dir, recursive = TRUE)
#' }
#'
#' \dontrun{
#' # Advanced: Stop an external server by PID
#' # 1. Find the PID of an external server
#' srvs <- osrm_servers(include_all = TRUE)
#' # 2. Stop it by PID
#' if (nrow(srvs) > 0) {
#' osrm_stop(pid = srvs$pid[1])
#' }
#'
#' # Or stop by its external ID (e.g., "sys-12345")
#' osrm_stop(id = "sys-12345")
#' }
#' }
#' @seealso [osrm_start()], [osrm_servers()], [osrm_stop_all()]
#'
osrm_stop <- function(
server = NULL,
id = NULL,
port = NULL,
pid = NULL,
wait = 1000L,
quiet = FALSE
) {
quiet <- isTRUE(quiet)
# Case 1: user supplied a process object directly
if (!is.null(server)) {
if (!inherits(server, "process")) {
stop("'server' must be a processx::process object", call. = FALSE)
}
targ_pid <- suppressWarnings(try(server$get_pid(), silent = TRUE))
if (!inherits(targ_pid, "try-error")) {
# Try to find and deregister by pid from the internal registry
reg <- .osrm_state$registry
hit <- names(reg)[vapply(
reg,
function(e) identical(e$pid, targ_pid),
logical(1)
)]
if (server$is_alive()) {
try(server$kill(), silent = TRUE)
try(server$wait(as.integer(wait)), silent = TRUE)
}
if (length(hit)) {
try(.osrm_deregister(hit[[1]]), silent = TRUE)
}
if (!quiet) {
message("Stopped OSRM server (pid ", targ_pid, ").")
}
return(list(
id = if (length(hit)) hit[[1]] else NA_character_,
pid = as.integer(targ_pid),
port = NA_integer_,
stopped = TRUE
))
} else {
# Could not read pid; best effort kill
try(server$kill(), silent = TRUE)
try(server$wait(as.integer(wait)), silent = TRUE)
if (!quiet) {
message("Stopped OSRM server (unknown pid).")
}
return(list(
id = NA_character_,
pid = NA_integer_,
port = NA_integer_,
stopped = TRUE
))
}
}
# Case 2: Direct PID (External/Manual stop)
if (!is.null(pid)) {
pid_int <- as.integer(pid)
# Check if it's in our local registry first (to clean up properly)
reg <- .osrm_state$registry
hit <- names(reg)[vapply(
reg,
function(e) identical(e$pid, pid_int),
logical(1)
)]
if (length(hit)) {
# Redirect to local stop logic (Case 3 logic will handle it if we just fall through,
# but let's be explicit)
return(osrm_stop(id = hit[1], wait = wait, quiet = quiet))
}
# External stop
if (.osrm_pid_is_running(pid_int)) {
.osrm_kill_pid(pid_int)
# Wait a moment
Sys.sleep(wait / 1000)
still_alive <- .osrm_pid_is_running(pid_int)
if (!quiet) {
if (!still_alive) {
message("Stopped external OSRM server (pid ", pid_int, ").")
} else {
message("Attempted to stop external OSRM server (pid ", pid_int, "), but it may still be running.")
}
}
return(list(
id = NA_character_,
pid = pid_int,
port = NA_integer_,
stopped = !still_alive
))
} else {
if (!quiet) message("Process with PID ", pid_int, " not found.")
return(list(
id = NA_character_,
pid = pid_int,
port = NA_integer_,
stopped = TRUE
))
}
}
# Case 3: Selection via Registry (Local OR External)
reg <- .osrm_state$registry
# Search Local Registry
idx <- NULL
if (length(reg)) {
pick_index <- function() {
if (!is.null(id)) {
which(names(reg) == id)[1]
} else if (!is.null(port)) {
hits <- which(vapply(
reg,
function(e) identical(e$port, as.integer(port)),
logical(1)
))
if (length(hits)) utils::tail(hits, 1) else integer()
} else {
# Default: most recently started alive server
alive <- vapply(
reg,
function(e) {
if (!is.null(e$proc) && inherits(e$proc, "process")) {
tryCatch(e$proc$is_alive(), error = function(...) FALSE)
} else {
.osrm_pid_is_running(e$pid)
}
},
logical(1)
)
utils::tail(which(alive), 1)
}
}
idx <- pick_index()
}
# Found in Local Registry?
if (length(idx) && !is.na(idx) && idx >= 1) {
entry <- reg[[idx]]
stopped <- FALSE
# Prefer to use the live process handle if it exists in the session
if (!is.null(entry$proc) && inherits(entry$proc, "process")) {
if (tryCatch(entry$proc$is_alive(), error = function(...) FALSE)) {
try(entry$proc$kill(), silent = TRUE)
try(entry$proc$wait(as.integer(wait)), silent = TRUE)
stopped <- TRUE
} else {
stopped <- TRUE
}
} else if (!is.null(entry$pid)) {
if (.osrm_pid_is_running(entry$pid)) {
.osrm_kill_pid(entry$pid)
stopped <- TRUE
} else {
stopped <- TRUE
}
}
# Deregister regardless to keep the registry clean
try(.osrm_deregister(entry$id), silent = TRUE)
if (!quiet) {
msg <- sprintf(
"Stopped OSRM server id=%s (pid %s, port %s).",
entry$id,
as.character(entry$pid %||% "NA"),
as.character(entry$port %||% "NA")
)
message(msg)
}
return(list(
id = entry$id,
pid = as.integer(entry$pid %||% NA_integer_),
port = as.integer(entry$port %||% NA_integer_),
stopped = isTRUE(stopped)
))
}
# Case 4: Not found locally. Try External Discovery.
# Only try if specific criteria were given (don't default to killing random external servers)
if (!is.null(id) || !is.null(port)) {
# Special case: id="sys-PID" -> Kill PID
if (!is.null(id) && grepl("^sys-(\\d+)$", id)) {
pid_from_id <- as.integer(sub("^sys-(\\d+)$", "\\1", id))
return(osrm_stop(pid = pid_from_id, wait = wait, quiet = quiet))
}
# Search all
all_srvs <- osrm_servers(include_all = TRUE)
target_pid <- NULL
target_id <- NA_character_
if (!is.null(id)) {
match <- all_srvs[all_srvs$id == id, ]
if (nrow(match) > 0) {
target_pid <- match$pid[1]
target_id <- match$id[1]
}
} else if (!is.null(port)) {
# Find by port (most recent first if multiple? usually only one per port)
match <- all_srvs[all_srvs$port == as.integer(port), ]
if (nrow(match) > 0) {
target_pid <- utils::tail(match$pid, 1)
target_id <- utils::tail(match$id, 1)
}
}
if (!is.null(target_pid)) {
return(osrm_stop(pid = target_pid, wait = wait, quiet = quiet))
}
}
# Case 5: Failure
if (!is.null(id) || !is.null(port)) {
stop(
"Could not identify a server to stop with the specified criteria (scanned local and external).",
call. = FALSE
)
}
if (!quiet) {
message("No running OSRM servers to stop in the current session.")
}
list(stopped = FALSE)
}
#' Stop all running OSRM servers started via this package
#'
#' @description
#' `r lifecycle::badge("stable")`
#'
#' @param include_all A logical value. If `TRUE`, stop all OSRM servers found
#' running on the system, including those not started by the current R session.
#' Requires `ps` and `jsonlite` packages. Defaults to `FALSE`.
#' @return The number of servers attempted.
#' @export
#' @examples
#' \donttest{
#' if (identical(Sys.getenv("OSRM_EXAMPLES"), "true")) {
#' install_dir <- osrm_install(
#' version = "latest",
#' path_action = "session",
#' quiet = TRUE
#' )
#'
#' pbf_path <- system.file("extdata/cur.osm.pbf", package = "osrm.backend")
#' osrm_dir <- file.path(tempdir(), paste0("osrm-", Sys.getpid()))
#' dir.create(osrm_dir, recursive = TRUE)
#' tmp_pbf <- file.path(osrm_dir, "cur.osm.pbf")
#' file.copy(from = pbf_path, to = tmp_pbf, overwrite = TRUE)
#' graph <- osrm_prepare_graph(tmp_pbf, overwrite = TRUE, threads = 1L)
#'
#' srv <- osrm_start_server(graph$osrm_job_artifact, port = 6000, threads = 1L)
#' stopped <- osrm_stop_all()
#' stopped
#'
#' osrm_uninstall(
#' dest_dir = install_dir,
#' clear_path = TRUE,
#' force = TRUE,
#' quiet = TRUE
#' )
#' unlink(osrm_dir, recursive = TRUE)
#' }
#' }
#'
osrm_stop_all <- function(include_all = FALSE) {
# Get all servers (registry + optional external)
srvs <- osrm_servers(include_all = include_all)
if (nrow(srvs) == 0) {
return(0L)
}
n <- 0L
# 1. Stop registry servers first (by ID)
reg_srvs <- srvs[srvs$id != "" & !startsWith(srvs$id, "sys-"), ]
for (id in reg_srvs$id) {
n <- n + 1L
try(osrm_stop(id = id, quiet = TRUE), silent = TRUE)
}
# 2. Stop external servers (by PID)
ext_srvs <- srvs[startsWith(srvs$id, "sys-"), ]
for (pid in ext_srvs$pid) {
n <- n + 1L
try(osrm_stop(pid = pid, quiet = TRUE), silent = TRUE)
}
n
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.