main.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // -*- coding: utf-8 -*-
  2. //
  3. // Simple CMS
  4. //
  5. // Copyright (C) 2011-2024 Michael Büsch <m@bues.ch>
  6. //
  7. // Licensed under the Apache License version 2.0
  8. // or the MIT license, at your option.
  9. // SPDX-License-Identifier: Apache-2.0 OR MIT
  10. #![forbid(unsafe_code)]
  11. mod anchor;
  12. mod args;
  13. mod backend;
  14. mod cache;
  15. mod comm;
  16. mod config;
  17. mod cookie;
  18. mod formfields;
  19. mod index;
  20. mod itertools;
  21. mod navtree;
  22. mod numparse;
  23. mod pagegen;
  24. mod query;
  25. mod reply;
  26. mod resolver;
  27. mod sitemap;
  28. use crate::{
  29. args::{CmsGetArgs, CmsPostArgs},
  30. backend::CmsBack,
  31. cache::CmsCache,
  32. config::CmsConfig,
  33. cookie::Cookie,
  34. query::Query,
  35. reply::CmsReply,
  36. };
  37. use anyhow::{self as ah, format_err as err, Context as _};
  38. use clap::Parser;
  39. use cms_socket::{CmsSocket, CmsSocketConn, MsgSerde};
  40. use cms_socket_back::{Msg, SOCK_FILE};
  41. use std::{
  42. num::NonZeroUsize,
  43. path::PathBuf,
  44. sync::Arc,
  45. time::{Duration, Instant},
  46. };
  47. use tokio::{
  48. runtime,
  49. signal::unix::{signal, SignalKind},
  50. sync, task,
  51. };
  52. #[derive(Parser, Debug, Clone)]
  53. struct Opts {
  54. /// The run directory for runtime data.
  55. #[arg(long, default_value = "/run")]
  56. rundir: PathBuf,
  57. /// The number of elements held in the cache.
  58. #[arg(long, default_value = "1024")]
  59. cache_size: usize,
  60. /// Always run in non-systemd mode.
  61. #[arg(long, default_value = "false")]
  62. no_systemd: bool,
  63. /// Set the number async worker threads.
  64. #[arg(long, default_value = "3")]
  65. worker_threads: NonZeroUsize,
  66. }
  67. async fn process_conn(
  68. mut conn: CmsSocketConn,
  69. config: Arc<CmsConfig>,
  70. cache: Arc<CmsCache>,
  71. opts: Arc<Opts>,
  72. ) -> ah::Result<()> {
  73. let mut back = CmsBack::new(Arc::clone(&config), cache, &opts.rundir).await;
  74. loop {
  75. let msg = conn.recv_msg(Msg::try_msg_deserialize).await?;
  76. let start_stamp = if config.debug() {
  77. Some(Instant::now())
  78. } else {
  79. None
  80. };
  81. let mut reply: CmsReply = match msg {
  82. Some(Msg::Get {
  83. host,
  84. path,
  85. https,
  86. cookie,
  87. query,
  88. }) => {
  89. let path = path.into_cleaned_path().into_checked_sys()?;
  90. back.get(&CmsGetArgs {
  91. _host: host,
  92. path,
  93. _cookie: Cookie::new(cookie),
  94. query: Query::new(query),
  95. https,
  96. })
  97. .await
  98. }
  99. Some(Msg::Post {
  100. host,
  101. path,
  102. https,
  103. cookie,
  104. query,
  105. body,
  106. body_mime,
  107. }) => {
  108. let path = path.into_cleaned_path().into_checked()?;
  109. back.post(
  110. &CmsGetArgs {
  111. _host: host,
  112. path,
  113. _cookie: Cookie::new(cookie),
  114. query: Query::new(query),
  115. https,
  116. },
  117. &CmsPostArgs { body, body_mime },
  118. )
  119. .await
  120. }
  121. Some(Msg::Reply { .. }) => {
  122. eprintln!("Received unsupported message.");
  123. continue;
  124. }
  125. None => {
  126. #[cfg(debug_assertions)]
  127. eprintln!("Client disconnected.");
  128. return Ok(());
  129. }
  130. };
  131. if let Some(start_stamp) = start_stamp {
  132. let runtime = (Instant::now() - start_stamp).as_micros();
  133. reply.add_http_header(&format!("X-CMS-Backend-Runtime: {runtime} us"));
  134. }
  135. let reply_msg: Msg = reply.into();
  136. conn.send_msg(&reply_msg).await?;
  137. }
  138. }
  139. async fn async_main(opts: Arc<Opts>) -> ah::Result<()> {
  140. let (main_exit_tx, mut main_exit_rx) = sync::mpsc::channel(1);
  141. let mut sigterm = signal(SignalKind::terminate()).unwrap();
  142. let mut sigint = signal(SignalKind::interrupt()).unwrap();
  143. let mut sighup = signal(SignalKind::hangup()).unwrap();
  144. let mut sock = CmsSocket::from_systemd_or_path(opts.no_systemd, &opts.rundir.join(SOCK_FILE))?;
  145. let config = Arc::new(CmsConfig::new().context("backd.conf")?);
  146. //TODO install seccomp filter.
  147. let cache = Arc::new(CmsCache::new(opts.cache_size));
  148. // Task: Socket handler.
  149. task::spawn({
  150. let config = Arc::clone(&config);
  151. let cache = Arc::clone(&cache);
  152. let opts = Arc::clone(&opts);
  153. async move {
  154. loop {
  155. let config = Arc::clone(&config);
  156. let cache = Arc::clone(&cache);
  157. let opts = Arc::clone(&opts);
  158. match sock.accept().await {
  159. Ok(conn) => {
  160. // Socket connection handler.
  161. task::spawn(async move {
  162. if let Err(e) = process_conn(conn, config, cache, opts).await {
  163. eprintln!("Client error: {e}");
  164. }
  165. });
  166. }
  167. Err(e) => {
  168. let _ = main_exit_tx.send(Err(e)).await;
  169. break;
  170. }
  171. }
  172. }
  173. }
  174. });
  175. // Main task.
  176. let exitcode;
  177. loop {
  178. tokio::select! {
  179. _ = sigterm.recv() => {
  180. eprintln!("SIGTERM: Terminating.");
  181. exitcode = Ok(());
  182. break;
  183. }
  184. _ = sigint.recv() => {
  185. exitcode = Err(err!("Interrupted by SIGINT."));
  186. break;
  187. }
  188. _ = sighup.recv() => {
  189. eprintln!("SIGHUP: Reloading.");
  190. cache.clear().await;
  191. }
  192. code = main_exit_rx.recv() => {
  193. if let Some(code) = code {
  194. exitcode = code;
  195. } else {
  196. exitcode = Err(err!("Unknown error code."));
  197. }
  198. break;
  199. }
  200. }
  201. }
  202. exitcode
  203. }
  204. fn main() -> ah::Result<()> {
  205. let opts = Arc::new(Opts::parse());
  206. runtime::Builder::new_multi_thread()
  207. .worker_threads(opts.worker_threads.into())
  208. .max_blocking_threads(opts.worker_threads.into())
  209. .thread_keep_alive(Duration::from_millis(1000))
  210. .enable_all()
  211. .build()
  212. .context("Tokio runtime builder")?
  213. .block_on(async_main(opts))
  214. }
  215. // vim: ts=4 sw=4 expandtab