about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authoryuzu <yuzu@b9215c17-b818-4693-b096-d1e41a411fef>2025-07-12 22:17:26 +0000
committeryuzu <yuzu@b9215c17-b818-4693-b096-d1e41a411fef>2025-07-12 22:17:26 +0000
commite33f9a59f875edf1240ca80c1014235296ff3cbf (patch)
tree5777c00bafad650d6be84f42f51ba4f873d92c53 /src
parent78608add1c69a877b76a05147f6c26b7abe66669 (diff)
downloadsalaryman-e33f9a59f875edf1240ca80c1014235296ff3cbf.tar.gz
salaryman-e33f9a59f875edf1240ca80c1014235296ff3cbf.tar.bz2
salaryman-e33f9a59f875edf1240ca80c1014235296ff3cbf.zip
add additional endpoints; change out mutexes for rwlocks
git-svn-id: svn+ssh://diminuette.aengel.lesbianunix.dev/salaryman/trunk@15 b9215c17-b818-4693-b096-d1e41a411fef
Diffstat (limited to 'src')
-rw-r--r--src/cli/main.rs4
-rw-r--r--src/lib.rs3
-rw-r--r--src/model.rs31
-rw-r--r--src/server/context.rs47
-rw-r--r--src/server/endpoints.rs359
-rw-r--r--src/server/main.rs (renamed from src/smd/main.rs)38
-rw-r--r--src/smd/context.rs47
-rw-r--r--src/smd/endpoints.rs205
8 files changed, 470 insertions, 264 deletions
diff --git a/src/cli/main.rs b/src/cli/main.rs
new file mode 100644
index 0000000..1b85472
--- /dev/null
+++ b/src/cli/main.rs
@@ -0,0 +1,4 @@
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    Ok(())
+}
diff --git a/src/lib.rs b/src/lib.rs
index 1f278a4..95486cd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1 +1,4 @@
 pub mod service;
+
+#[cfg(feature = "models")]
+pub mod model;
diff --git a/src/model.rs b/src/model.rs
new file mode 100644
index 0000000..578d9b4
--- /dev/null
+++ b/src/model.rs
@@ -0,0 +1,31 @@
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+use std::net::IpAddr;
+use std::path::PathBuf;
+use uuid::Uuid;
+
+#[derive(Serialize, Deserialize, JsonSchema, Debug)]
+pub struct UpdateConf {
+    pub address: Option<IpAddr>,
+    pub port: Option<u16>,
+}
+
+#[derive(Serialize, Deserialize, JsonSchema, Debug)]
+pub struct StdinBuffer {
+    pub stdin: String,
+    pub endl: Option<bool>,
+}
+
+#[derive(Serialize, Deserialize, JsonSchema, Debug)]
+pub struct ServicePath {
+    pub service_uuid: Uuid,
+}
+
+#[derive(Serialize, Deserialize, JsonSchema, Debug)]
+pub struct NewService {
+    pub name: Option<String>,
+    pub command: Option<String>,
+    pub args: Option<Option<String>>,
+    pub directory: Option<Option<PathBuf>>,
+    pub autostart: Option<bool>,
+}
diff --git a/src/server/context.rs b/src/server/context.rs
new file mode 100644
index 0000000..132e2dc
--- /dev/null
+++ b/src/server/context.rs
@@ -0,0 +1,47 @@
+use super::Config;
+use salaryman::service::{Service, ServiceConf};
+use std::path::PathBuf;
+use std::sync::Arc;
+use tokio::sync::RwLock;
+
+pub struct SalarymanService {
+    pub config: ServiceConf,
+    pub service: Arc<RwLock<Service>>,
+}
+impl SalarymanService {
+    pub fn new() -> Self {
+        Self {
+            config: ServiceConf::new(),
+            service: Arc::new(RwLock::new(Service::new())),
+        }
+    }
+    pub fn from_parts(config: ServiceConf, service: Arc<RwLock<Service>>) -> Self {
+        Self { config, service }
+    }
+}
+
+pub struct SalarymanDContext {
+    pub services: RwLock<Vec<Arc<SalarymanService>>>,
+    pub save_file: PathBuf,
+    pub config: Arc<RwLock<Config>>,
+}
+impl SalarymanDContext {
+    pub fn new() -> Self {
+        Self {
+            services: RwLock::new(Vec::new()),
+            save_file: PathBuf::from(""),
+            config: Arc::new(RwLock::new(Config::new())),
+        }
+    }
+    pub fn from_parts(
+        services: RwLock<Vec<Arc<SalarymanService>>>,
+        save_file: PathBuf,
+        config: Arc<RwLock<Config>>,
+    ) -> Self {
+        Self {
+            services,
+            save_file,
+            config,
+        }
+    }
+}
diff --git a/src/server/endpoints.rs b/src/server/endpoints.rs
new file mode 100644
index 0000000..8ebc42e
--- /dev/null
+++ b/src/server/endpoints.rs
@@ -0,0 +1,359 @@
+use super::Config;
+use super::context::{SalarymanDContext, SalarymanService};
+use dropshot::{HttpError, HttpResponseOk, Path, RequestContext, TypedBody, endpoint};
+use salaryman::model::{NewService, ServicePath, StdinBuffer, UpdateConf};
+use salaryman::service::{Service, ServiceConf};
+use std::sync::Arc;
+use tokio::fs::File;
+use tokio::io::AsyncWriteExt;
+use tokio::sync::RwLock;
+
+#[endpoint {
+    method = GET,
+    path = "/config",
+}]
+pub async fn endpoint_get_config(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+) -> Result<HttpResponseOk<Config>, HttpError> {
+    let ctx = rqctx.context();
+    let lock = ctx.config.read().await;
+    let conf = lock.clone();
+    drop(lock);
+    Ok(HttpResponseOk(conf))
+}
+#[endpoint {
+    method = PUT,
+    path = "/config",
+}]
+pub async fn endpoint_put_config(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+    body: TypedBody<UpdateConf>,
+) -> Result<HttpResponseOk<()>, HttpError> {
+    let ctx = rqctx.context();
+    let update = body.into_inner();
+    if let Some(addr) = update.address {
+        let mut lock = ctx.config.write().await;
+        lock.address = Some(addr);
+        drop(lock);
+    }
+    if let Some(port) = update.port {
+        let mut lock = ctx.config.write().await;
+        lock.port = Some(port);
+        drop(lock);
+    }
+    Ok(HttpResponseOk(()))
+}
+#[endpoint {
+    method = GET,
+    path = "/config/save"
+}]
+pub async fn endpoint_get_config_save(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+) -> Result<HttpResponseOk<()>, HttpError> {
+    let ctx = rqctx.context();
+    let mut v: Vec<ServiceConf> = Vec::new();
+    let rlock = ctx.services.read().await;
+    for i in 0..rlock.len() {
+        v.push(rlock[i].config.clone());
+    }
+    drop(rlock);
+    let rlock = ctx.config.read().await;
+    let save = Config {
+        address: rlock.address,
+        port: rlock.port,
+        user: rlock.user.clone(),
+        service: v,
+    };
+    drop(rlock);
+    let mut f = match File::open(&ctx.save_file).await {
+        Ok(f) => f,
+        Err(_) => {
+            return Err(HttpError::for_internal_error(String::from(
+                "cannot open desired file",
+            )));
+        }
+    };
+    let save = match toml::to_string(&save) {
+        Ok(s) => s,
+        Err(_) => {
+            return Err(HttpError::for_internal_error(String::from(
+                "cannot serialize config!",
+            )));
+        }
+    };
+    match f.write_all(save.as_str().as_bytes()).await {
+        Ok(_) => (),
+        Err(_) => {
+            return Err(HttpError::for_internal_error(String::from(
+                "could not write to file!",
+            )));
+        }
+    }
+    Ok(HttpResponseOk(()))
+}
+#[endpoint {
+    method = GET,
+    path = "/service",
+}]
+pub async fn endpoint_get_services(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+) -> Result<HttpResponseOk<Vec<ServiceConf>>, HttpError> {
+    let ret = {
+        let ctx = rqctx.context();
+        let mut v: Vec<ServiceConf> = Vec::new();
+        let lock = ctx.services.read().await;
+        for i in 0..lock.len() {
+            v.push(lock[i].config.clone());
+        }
+        v
+    };
+    Ok(HttpResponseOk(ret))
+}
+#[endpoint {
+    method = POST,
+    path = "/service",
+}]
+pub async fn endpoint_post_service(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+    body: TypedBody<NewService>,
+) -> Result<HttpResponseOk<ServiceConf>, HttpError> {
+    let ctx = rqctx.context();
+    let body = body.into_inner();
+    let mut s: ServiceConf = ServiceConf::new();
+    if let Some(name) = &body.name {
+        s.name = name.clone().to_owned();
+    } else {
+        return Err(HttpError::for_bad_request(
+            None,
+            String::from("name field is required!"),
+        ));
+    }
+    if let Some(command) = &body.command {
+        s.command = command.clone().to_owned();
+    } else {
+        return Err(HttpError::for_bad_request(
+            None,
+            String::from("command field is required!"),
+        ));
+    }
+    if let Some(args) = &body.args {
+        if let Some(args) = args {
+            s.args = Some(args.clone().to_owned());
+        }
+    }
+    if let Some(dir) = &body.directory {
+        if let Some(dir) = dir {
+            s.directory = Some(dir.clone().to_owned());
+        }
+    }
+    if let Some(auto) = &body.autostart {
+        s.autostart = auto.clone().to_owned();
+    } else {
+        s.autostart = false;
+    }
+    let service: SalarymanService =
+        SalarymanService::from_parts(s.clone(), Arc::new(RwLock::new(Service::from_conf(&s))));
+    if service.config.autostart {
+        let mut lock = service.service.write().await;
+        match lock.start_with_output().await {
+            Ok(_) => (),
+            Err(_) => (),
+        }
+        drop(lock);
+    }
+    let mut lock = ctx.config.write().await;
+    lock.service.push(s.clone());
+    drop(lock);
+    let mut lock = ctx.services.write().await;
+    lock.push(Arc::new(service));
+    drop(lock);
+    Ok(HttpResponseOk(ServiceConf::new()))
+}
+#[endpoint {
+    method = GET,
+    path = "/service/{service_uuid}",
+}]
+pub async fn endpoint_get_service(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+    path_params: Path<ServicePath>,
+) -> Result<HttpResponseOk<ServiceConf>, HttpError> {
+    let u = path_params.into_inner().service_uuid;
+    let ctx = rqctx.context();
+    let mut service: Option<Arc<SalarymanService>> = None;
+    let lock = ctx.services.read().await;
+    for i in 0..lock.len() {
+        if lock[i].config.uuid == u {
+            service = Some(lock[i].clone());
+        } else {
+            continue;
+        }
+    }
+    let s = match service {
+        Some(s) => s.config.clone(),
+        None => {
+            return Err(HttpError::for_unavail(
+                None,
+                String::from("Service Not Found"),
+            ));
+        }
+    };
+    Ok(HttpResponseOk(s))
+}
+#[endpoint {
+    method = GET,
+    path = "/service/{service_uuid}/start",
+}]
+pub async fn endpoint_start_service(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+    path_params: Path<ServicePath>,
+) -> Result<HttpResponseOk<()>, HttpError> {
+    let u = path_params.into_inner().service_uuid;
+    let ctx = rqctx.context();
+    let mut service: Option<Arc<SalarymanService>> = None;
+    let lock = ctx.services.read().await;
+    for i in 0..lock.len() {
+        if lock[i].config.uuid == u {
+            service = Some(lock[i].clone());
+        } else {
+            continue;
+        }
+    }
+    match service {
+        Some(s) => {
+            let mut lock = s.service.write().await;
+            match lock.start_with_output().await {
+                Ok(_) => (),
+                Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
+            }
+        }
+        None => {
+            return Err(HttpError::for_unavail(
+                None,
+                String::from("Service Not Found"),
+            ));
+        }
+    };
+    Ok(HttpResponseOk(()))
+}
+#[endpoint {
+    method = GET,
+    path = "/service/{service_uuid}/stop",
+}]
+pub async fn endpoint_stop_service(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+    path_params: Path<ServicePath>,
+) -> Result<HttpResponseOk<()>, HttpError> {
+    let u = path_params.into_inner().service_uuid;
+    let ctx = rqctx.context();
+    let mut service: Option<Arc<SalarymanService>> = None;
+    let lock = ctx.services.read().await;
+    for i in 0..lock.len() {
+        if lock[i].config.uuid == u {
+            service = Some(lock[i].clone());
+        } else {
+            continue;
+        }
+    }
+    match service {
+        Some(s) => {
+            let mut lock = s.service.write().await;
+            match lock.stop().await {
+                Ok(_) => (),
+                Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
+            }
+        }
+        None => {
+            return Err(HttpError::for_unavail(
+                None,
+                String::from("Service Not Found"),
+            ));
+        }
+    };
+    Ok(HttpResponseOk(()))
+}
+#[endpoint {
+    method = GET,
+    path = "/service/{service_uuid}/restart",
+}]
+pub async fn endpoint_restart_service(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+    path_params: Path<ServicePath>,
+) -> Result<HttpResponseOk<()>, HttpError> {
+    let u = path_params.into_inner().service_uuid;
+    let ctx = rqctx.context();
+    let mut service: Option<Arc<SalarymanService>> = None;
+    let lock = ctx.services.read().await;
+    for i in 0..lock.len() {
+        if lock[i].config.uuid == u {
+            service = Some(lock[i].clone());
+        } else {
+            continue;
+        }
+    }
+    match service {
+        Some(s) => {
+            let mut lock = s.service.write().await;
+            match lock.restart_with_output().await {
+                Ok(_) => (),
+                Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
+            }
+        }
+        None => {
+            return Err(HttpError::for_unavail(
+                None,
+                String::from("Service Not Found"),
+            ));
+        }
+    };
+    Ok(HttpResponseOk(()))
+}
+#[endpoint {
+    method = PUT,
+    path = "/service/{service_uuid}/write"
+}]
+pub async fn endpoint_post_stdin(
+    rqctx: RequestContext<Arc<SalarymanDContext>>,
+    path_params: Path<ServicePath>,
+    update: TypedBody<StdinBuffer>,
+) -> Result<HttpResponseOk<()>, HttpError> {
+    let ctx = rqctx.context();
+    let stdin_str = update.into_inner();
+    let u = path_params.into_inner().service_uuid;
+    let mut service: Option<Arc<SalarymanService>> = None;
+    let lock = ctx.services.read().await;
+    for i in 0..lock.len() {
+        if lock[i].config.uuid == u {
+            service = Some(lock[i].clone());
+        } else {
+            continue;
+        }
+    }
+    match service {
+        Some(s) => {
+            let mut lock = s.service.write().await;
+            if lock.started().await {
+                let b = if let Some(endl) = stdin_str.endl {
+                    if endl {
+                        lock.writeln_stdin(stdin_str.stdin.clone()).await //TODO: PROPERLY HANDLE ERROR!
+                    } else {
+                        lock.write_stdin(stdin_str.stdin.clone()).await //TODO: PROPERLY HANDLE ERROR!
+                    }
+                } else {
+                    lock.writeln_stdin(stdin_str.stdin.clone()).await //TODO: PROPERLY HANDLE ERROR!
+                };
+                match b {
+                    Ok(_) => (),
+                    Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
+                }
+            }
+            drop(lock);
+        }
+        None => {
+            return Err(HttpError::for_unavail(
+                None,
+                String::from("Service Not Found"),
+            ));
+        }
+    }
+    Ok(HttpResponseOk(()))
+}
diff --git a/src/smd/main.rs b/src/server/main.rs
index b81df2f..7557145 100644
--- a/src/smd/main.rs
+++ b/src/server/main.rs
@@ -6,7 +6,7 @@ use dropshot::{ApiDescription, ConfigDropshot, ConfigLogging, ConfigLoggingLevel
 use salaryman::service::{Service, ServiceConf};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use tokio::{fs::read_to_string, sync::Mutex};
+use tokio::{fs::read_to_string, sync::RwLock};
 
 use std::{
     net::{IpAddr, SocketAddr},
@@ -16,7 +16,8 @@ use std::{
 
 use crate::context::{SalarymanDContext, SalarymanService};
 use crate::endpoints::{
-    endpoint_get_service, endpoint_get_services, endpoint_post_stdin, endpoint_restart_service,
+    endpoint_get_config, endpoint_get_config_save, endpoint_get_service, endpoint_get_services,
+    endpoint_post_service, endpoint_post_stdin, endpoint_put_config, endpoint_restart_service,
     endpoint_start_service, endpoint_stop_service,
 };
 
@@ -107,26 +108,35 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
         args.port
     };
     let bind = SocketAddr::new(addr, port);
-    let mut services: Vec<Arc<SalarymanService>> = Vec::new();
+    let services: RwLock<Vec<Arc<SalarymanService>>> = RwLock::new(Vec::new());
     for i in 0..conf.service.len() {
-        services.push(Arc::new(SalarymanService::from_parts(
+        let mut lock = services.write().await;
+        lock.push(Arc::new(SalarymanService::from_parts(
             conf.service[i].clone(),
-            Arc::new(Mutex::new(Service::from_conf(&conf.service[i]))),
+            Arc::new(RwLock::new(Service::from_conf(&conf.service[i]))),
         )));
+        drop(lock);
     }
-    for i in 0..services.len() {
-        if services[i].config.autostart {
-            let mut lock = services[i].service.lock().await;
-            lock.start().await?;
-            lock.scan_stdout().await?;
-            lock.scan_stderr().await?;
+    let lock = services.write().await;
+    for i in 0..lock.len() {
+        if lock[i].config.autostart {
+            let mut l = lock[i].service.write().await;
+            l.start().await?;
+            l.scan_stdout().await?;
+            l.scan_stderr().await?;
+            drop(l);
         }
     }
+    drop(lock);
     let log_conf = ConfigLogging::StderrTerminal {
         level: ConfigLoggingLevel::Info,
     };
     let log = log_conf.to_logger("smd")?;
-    let ctx = Arc::new(SalarymanDContext::from_vec(services));
+    let ctx = Arc::new(SalarymanDContext::from_parts(
+        services,
+        args.config,
+        Arc::new(RwLock::new(conf)),
+    ));
     let config = ConfigDropshot {
         bind_address: bind,
         ..Default::default()
@@ -138,6 +148,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     api.register(endpoint_stop_service)?;
     api.register(endpoint_restart_service)?;
     api.register(endpoint_post_stdin)?;
+    api.register(endpoint_post_service)?;
+    api.register(endpoint_get_config)?;
+    api.register(endpoint_put_config)?;
+    api.register(endpoint_get_config_save)?;
     api.openapi("Salaryman", semver::Version::new(1, 0, 0))
         .write(&mut std::io::stdout())?;
     let server = ServerBuilder::new(api, ctx.clone(), log)
diff --git a/src/smd/context.rs b/src/smd/context.rs
deleted file mode 100644
index 5d8038c..0000000
--- a/src/smd/context.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use salaryman::service::{Service, ServiceConf};
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-use std::sync::Arc;
-use tokio::sync::Mutex;
-use uuid::Uuid;
-
-pub struct SalarymanService {
-    pub config: ServiceConf,
-    pub service: Arc<Mutex<Service>>,
-}
-impl SalarymanService {
-    pub fn new() -> Self {
-        Self {
-            config: ServiceConf::new(),
-            service: Arc::new(Mutex::new(Service::new())),
-        }
-    }
-    pub fn from_parts(config: ServiceConf, service: Arc<Mutex<Service>>) -> Self {
-        Self { config, service }
-    }
-}
-
-pub struct SalarymanDContext {
-    pub services: Vec<Arc<SalarymanService>>,
-}
-impl SalarymanDContext {
-    pub fn new() -> Self {
-        Self {
-            services: Vec::new(),
-        }
-    }
-    pub fn from_vec(services: Vec<Arc<SalarymanService>>) -> Self {
-        Self { services }
-    }
-}
-
-#[derive(Serialize, Deserialize, JsonSchema, Debug)]
-pub struct StdinBuffer {
-    pub stdin: String,
-    pub endl: Option<bool>,
-}
-
-#[derive(Serialize, Deserialize, JsonSchema, Debug)]
-pub struct ServicePath {
-    pub service_uuid: Uuid,
-}
diff --git a/src/smd/endpoints.rs b/src/smd/endpoints.rs
deleted file mode 100644
index f0ba0ea..0000000
--- a/src/smd/endpoints.rs
+++ /dev/null
@@ -1,205 +0,0 @@
-use super::context::{SalarymanDContext, SalarymanService, ServicePath, StdinBuffer};
-use dropshot::{HttpError, HttpResponseOk, Path, RequestContext, TypedBody, endpoint};
-use salaryman::service::ServiceConf;
-use std::sync::Arc;
-
-#[endpoint {
-    method = GET,
-    path = "/service",
-}]
-pub async fn endpoint_get_services(
-    rqctx: RequestContext<Arc<SalarymanDContext>>,
-) -> Result<HttpResponseOk<Vec<ServiceConf>>, HttpError> {
-    let ret = {
-        let ctx = rqctx.context();
-        let mut v: Vec<ServiceConf> = Vec::new();
-        for i in 0..ctx.services.len() {
-            v.push(ctx.services[i].config.clone());
-        }
-        v
-    };
-    Ok(HttpResponseOk(ret))
-}
-#[endpoint {
-    method = GET,
-    path = "/service/{service_uuid}",
-}]
-pub async fn endpoint_get_service(
-    rqctx: RequestContext<Arc<SalarymanDContext>>,
-    path_params: Path<ServicePath>,
-) -> Result<HttpResponseOk<ServiceConf>, HttpError> {
-    let u = path_params.into_inner().service_uuid;
-    let ctx = rqctx.context();
-    let mut service: Option<Arc<SalarymanService>> = None;
-    for i in 0..ctx.services.len() {
-        if ctx.services[i].config.uuid == u {
-            service = Some(ctx.services[i].clone());
-        } else {
-            continue;
-        }
-    }
-    let s = match service {
-        Some(s) => s.config.clone(),
-        None => {
-            return Err(HttpError::for_unavail(
-                None,
-                String::from("Service Not Found"),
-            ));
-        }
-    };
-    Ok(HttpResponseOk(s))
-}
-#[endpoint {
-    method = GET,
-    path = "/service/{service_uuid}/start",
-}]
-pub async fn endpoint_start_service(
-    rqctx: RequestContext<Arc<SalarymanDContext>>,
-    path_params: Path<ServicePath>,
-) -> Result<HttpResponseOk<()>, HttpError> {
-    let u = path_params.into_inner().service_uuid;
-    let ctx = rqctx.context();
-    let mut service: Option<Arc<SalarymanService>> = None;
-    for i in 0..ctx.services.len() {
-        if ctx.services[i].config.uuid == u {
-            service = Some(ctx.services[i].clone());
-        } else {
-            continue;
-        }
-    }
-    match service {
-        Some(s) => {
-            let mut lock = s.service.lock().await;
-            match lock.start_with_output().await {
-                Ok(_) => (),
-                Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
-            }
-        }
-        None => {
-            return Err(HttpError::for_unavail(
-                None,
-                String::from("Service Not Found"),
-            ));
-        }
-    };
-    Ok(HttpResponseOk(()))
-}
-#[endpoint {
-    method = GET,
-    path = "/service/{service_uuid}/stop",
-}]
-pub async fn endpoint_stop_service(
-    rqctx: RequestContext<Arc<SalarymanDContext>>,
-    path_params: Path<ServicePath>,
-) -> Result<HttpResponseOk<()>, HttpError> {
-    let u = path_params.into_inner().service_uuid;
-    let ctx = rqctx.context();
-    let mut service: Option<Arc<SalarymanService>> = None;
-    for i in 0..ctx.services.len() {
-        if ctx.services[i].config.uuid == u {
-            service = Some(ctx.services[i].clone());
-        } else {
-            continue;
-        }
-    }
-    match service {
-        Some(s) => {
-            let mut lock = s.service.lock().await;
-            match lock.stop().await {
-                Ok(_) => (),
-                Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
-            }
-        }
-        None => {
-            return Err(HttpError::for_unavail(
-                None,
-                String::from("Service Not Found"),
-            ));
-        }
-    };
-    Ok(HttpResponseOk(()))
-}
-#[endpoint {
-    method = GET,
-    path = "/service/{service_uuid}/restart",
-}]
-pub async fn endpoint_restart_service(
-    rqctx: RequestContext<Arc<SalarymanDContext>>,
-    path_params: Path<ServicePath>,
-) -> Result<HttpResponseOk<()>, HttpError> {
-    let u = path_params.into_inner().service_uuid;
-    let ctx = rqctx.context();
-    let mut service: Option<Arc<SalarymanService>> = None;
-    for i in 0..ctx.services.len() {
-        if ctx.services[i].config.uuid == u {
-            service = Some(ctx.services[i].clone());
-        } else {
-            continue;
-        }
-    }
-    match service {
-        Some(s) => {
-            let mut lock = s.service.lock().await;
-            match lock.restart_with_output().await {
-                Ok(_) => (),
-                Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
-            }
-        }
-        None => {
-            return Err(HttpError::for_unavail(
-                None,
-                String::from("Service Not Found"),
-            ));
-        }
-    };
-    Ok(HttpResponseOk(()))
-}
-#[endpoint {
-    method = PUT,
-    path = "/service/{service_uuid}/write"
-}]
-pub async fn endpoint_post_stdin(
-    rqctx: RequestContext<Arc<SalarymanDContext>>,
-    path_params: Path<ServicePath>,
-    update: TypedBody<StdinBuffer>,
-) -> Result<HttpResponseOk<()>, HttpError> {
-    let ctx = rqctx.context();
-    let stdin_str = update.into_inner();
-    let u = path_params.into_inner().service_uuid;
-    let mut service: Option<Arc<SalarymanService>> = None;
-    for i in 0..ctx.services.len() {
-        if ctx.services[i].config.uuid == u {
-            service = Some(ctx.services[i].clone());
-        } else {
-            continue;
-        }
-    }
-    match service {
-        Some(s) => {
-            let mut lock = s.service.lock().await;
-            if lock.started().await {
-                let b = if let Some(endl) = stdin_str.endl {
-                    if endl {
-                        lock.writeln_stdin(stdin_str.stdin.clone()).await //TODO: PROPERLY HANDLE ERROR!
-                    } else {
-                        lock.write_stdin(stdin_str.stdin.clone()).await //TODO: PROPERLY HANDLE ERROR!
-                    }
-                } else {
-                    lock.writeln_stdin(stdin_str.stdin.clone()).await //TODO: PROPERLY HANDLE ERROR!
-                };
-                match b {
-                    Ok(_) => (),
-                    Err(e) => return Err(HttpError::for_internal_error(e.to_string())),
-                }
-            }
-            drop(lock);
-        }
-        None => {
-            return Err(HttpError::for_unavail(
-                None,
-                String::from("Service Not Found"),
-            ));
-        }
-    }
-    Ok(HttpResponseOk(()))
-}