Blame


1 0cde9354 2023-03-15 contact use std::{
2 81da6bfb 2023-03-16 contact borrow::Cow,
3 494084c5 2023-03-19 contact io::{self, Write},
4 0cde9354 2023-03-15 contact iter::FusedIterator,
5 eb075e35 2023-03-21 contact time::UNIX_EPOCH,
6 0cde9354 2023-03-15 contact };
7 0cde9354 2023-03-15 contact
8 494084c5 2023-03-19 contact use crate::s6::{ServiceDir, Status, SupervisedStatus};
9 ecefe0f2 2023-03-16 contact
10 55948249 2023-03-16 contact fn escape(string: &str) -> Cow<str> {
11 3ef8fec0 2023-03-16 contact let escapes = string
12 3ef8fec0 2023-03-16 contact .chars()
13 3ef8fec0 2023-03-16 contact .filter(|c| *c == '\n' || *c == '"' || *c == '\\')
14 3ef8fec0 2023-03-16 contact .count();
15 3ef8fec0 2023-03-16 contact if escapes == 0 {
16 3ef8fec0 2023-03-16 contact Cow::Borrowed(string)
17 3ef8fec0 2023-03-16 contact } else {
18 3ef8fec0 2023-03-16 contact let mut escaped = String::with_capacity(string.len() + escapes);
19 3ef8fec0 2023-03-16 contact for c in string.chars() {
20 3ef8fec0 2023-03-16 contact match c {
21 3ef8fec0 2023-03-16 contact '\n' => escaped.push_str("\\n"),
22 3ef8fec0 2023-03-16 contact '"' => escaped.push_str("\\\""),
23 3ef8fec0 2023-03-16 contact '\\' => escaped.push_str("\\\\"),
24 3ef8fec0 2023-03-16 contact _ => escaped.push(c),
25 81da6bfb 2023-03-16 contact }
26 81da6bfb 2023-03-16 contact }
27 3ef8fec0 2023-03-16 contact Cow::Owned(escaped)
28 81da6bfb 2023-03-16 contact }
29 81da6bfb 2023-03-16 contact }
30 81da6bfb 2023-03-16 contact
31 0cde9354 2023-03-15 contact pub struct Writer<'w, W: Write, I: Iterator<Item = ServiceDir>> {
32 0cde9354 2023-03-15 contact writer: &'w mut W,
33 0cde9354 2023-03-15 contact iter: I,
34 0cde9354 2023-03-15 contact first: bool,
35 0cde9354 2023-03-15 contact fused: bool,
36 0cde9354 2023-03-15 contact }
37 0cde9354 2023-03-15 contact
38 494084c5 2023-03-19 contact static DESCRIPTORS: &[u8] = concat!(
39 494084c5 2023-03-19 contact "# TYPE s6_service_status stateset\n",
40 494084c5 2023-03-19 contact "# HELP s6_service_status Status of the service.\n",
41 494084c5 2023-03-19 contact "# TYPE s6_service_pid gauge\n",
42 494084c5 2023-03-19 contact "# HELP s6_service_pid Process id of the service.\n",
43 234f6a5a 2023-03-19 contact "# TYPE s6_service_up_down_since_seconds gauge\n",
44 234f6a5a 2023-03-19 contact "# HELP s6_service_up_down_since_seconds Since the service was up or down.\n",
45 234f6a5a 2023-03-19 contact "# UNIT s6_service_up_down_since_seconds seconds\n",
46 234f6a5a 2023-03-19 contact "# TYPE s6_service_ready_since_seconds gauge\n",
47 234f6a5a 2023-03-19 contact "# HELP s6_service_ready_since_seconds Since the service was ready.\n",
48 234f6a5a 2023-03-19 contact "# UNIT s6_service_ready_since_seconds seconds\n",
49 64c44899 2023-03-19 contact "# TYPE s6_service_exit_code stateset\n",
50 64c44899 2023-03-19 contact "# HELP s6_service_exit_code Exit code of the service.\n",
51 64c44899 2023-03-19 contact "# TYPE s6_service_signal_number stateset\n",
52 64c44899 2023-03-19 contact "# HELP s6_service_signal_number Signal number of the service.\n",
53 494084c5 2023-03-19 contact )
54 494084c5 2023-03-19 contact .as_bytes();
55 494084c5 2023-03-19 contact
56 494084c5 2023-03-19 contact static EOF: &[u8] = b"# EOF\n";
57 494084c5 2023-03-19 contact
58 0cde9354 2023-03-15 contact impl<'w, W: Write, I: Iterator<Item = ServiceDir>> Writer<'w, W, I> {
59 0cde9354 2023-03-15 contact pub fn new(writer: &'w mut W, iter: I) -> Self {
60 0cde9354 2023-03-15 contact Self {
61 0cde9354 2023-03-15 contact writer,
62 0cde9354 2023-03-15 contact iter,
63 0cde9354 2023-03-15 contact first: true,
64 0cde9354 2023-03-15 contact fused: false,
65 0cde9354 2023-03-15 contact }
66 0cde9354 2023-03-15 contact }
67 494084c5 2023-03-19 contact
68 494084c5 2023-03-19 contact fn write_descriptors(&mut self) -> io::Result<()> {
69 494084c5 2023-03-19 contact self.writer.write_all(DESCRIPTORS)
70 494084c5 2023-03-19 contact }
71 494084c5 2023-03-19 contact
72 494084c5 2023-03-19 contact fn write_status(&mut self, service: &str, status: &Status) -> io::Result<()> {
73 494084c5 2023-03-19 contact match status {
74 494084c5 2023-03-19 contact Status::Unsupervised => self.write_unsupervised_status(service),
75 494084c5 2023-03-19 contact Status::Supervised(status) => self.write_supervised_status(service, status),
76 494084c5 2023-03-19 contact }
77 494084c5 2023-03-19 contact }
78 494084c5 2023-03-19 contact
79 494084c5 2023-03-19 contact fn write_unsupervised_status(&mut self, service: &str) -> io::Result<()> {
80 494084c5 2023-03-19 contact writeln!(
81 494084c5 2023-03-19 contact self.writer,
82 494084c5 2023-03-19 contact "s6_service_status{{service=\"{service}\",s6_service_status=\"supervised\"}} 0"
83 494084c5 2023-03-19 contact )
84 494084c5 2023-03-19 contact }
85 494084c5 2023-03-19 contact
86 494084c5 2023-03-19 contact fn write_supervised_status(
87 494084c5 2023-03-19 contact &mut self,
88 494084c5 2023-03-19 contact service: &str,
89 494084c5 2023-03-19 contact status: &SupervisedStatus,
90 494084c5 2023-03-19 contact ) -> io::Result<()> {
91 494084c5 2023-03-19 contact write!(
92 494084c5 2023-03-19 contact self.writer,
93 494084c5 2023-03-19 contact concat!(
94 494084c5 2023-03-19 contact "s6_service_status{{service=\"{}\",s6_service_status=\"supervised\"}} 1\n",
95 494084c5 2023-03-19 contact "s6_service_status{{service=\"{0}\",s6_service_status=\"up\"}} {}\n",
96 234f6a5a 2023-03-19 contact "s6_service_status{{service=\"{0}\",s6_service_status=\"wanted_up\"}} {}\n",
97 234f6a5a 2023-03-19 contact "s6_service_status{{service=\"{0}\",s6_service_status=\"normally_up\"}} {}\n",
98 494084c5 2023-03-19 contact "s6_service_status{{service=\"{0}\",s6_service_status=\"ready\"}} {}\n",
99 494084c5 2023-03-19 contact "s6_service_status{{service=\"{0}\",s6_service_status=\"paused\"}} {}\n",
100 494084c5 2023-03-19 contact "s6_service_pid{{service=\"{0}\"}} {}\n",
101 234f6a5a 2023-03-19 contact "s6_service_up_down_since_seconds{{service=\"{0}\"}} {}\n",
102 234f6a5a 2023-03-19 contact "s6_service_ready_since_seconds{{service=\"{0}\"}} {}\n",
103 494084c5 2023-03-19 contact ),
104 494084c5 2023-03-19 contact service,
105 494084c5 2023-03-19 contact if status.up { 1 } else { 0 },
106 234f6a5a 2023-03-19 contact if status.wanted_up { 1 } else { 0 },
107 234f6a5a 2023-03-19 contact if status.normally_up { 1 } else { 0 },
108 494084c5 2023-03-19 contact if status.ready { 1 } else { 0 },
109 494084c5 2023-03-19 contact if status.paused { 1 } else { 0 },
110 494084c5 2023-03-19 contact status.pid,
111 eb075e35 2023-03-21 contact status
112 eb075e35 2023-03-21 contact .up_down_since
113 eb075e35 2023-03-21 contact .duration_since(UNIX_EPOCH)
114 eb075e35 2023-03-21 contact .expect("positive timestamp")
115 eb075e35 2023-03-21 contact .as_secs(),
116 eb075e35 2023-03-21 contact status
117 eb075e35 2023-03-21 contact .ready_since
118 eb075e35 2023-03-21 contact .duration_since(UNIX_EPOCH)
119 eb075e35 2023-03-21 contact .expect("positive timestamp")
120 eb075e35 2023-03-21 contact .as_secs(),
121 494084c5 2023-03-19 contact )?;
122 234f6a5a 2023-03-19 contact if let Some(exit_code) = status.exit_code {
123 494084c5 2023-03-19 contact writeln!(
124 494084c5 2023-03-19 contact self.writer,
125 234f6a5a 2023-03-19 contact concat!("s6_service_exit_code{{service=\"{}\",s6_service_exit_code=\"{}\"}} 1"),
126 234f6a5a 2023-03-19 contact service, exit_code
127 494084c5 2023-03-19 contact )?
128 494084c5 2023-03-19 contact }
129 234f6a5a 2023-03-19 contact if let Some(signum) = status.signal_number {
130 494084c5 2023-03-19 contact writeln!(
131 494084c5 2023-03-19 contact self.writer,
132 234f6a5a 2023-03-19 contact concat!(
133 234f6a5a 2023-03-19 contact "s6_service_signal_number{{service=\"{}\",s6_service_signal_number=\"{}\"}} 1"
134 234f6a5a 2023-03-19 contact ),
135 494084c5 2023-03-19 contact service, signum
136 494084c5 2023-03-19 contact )?
137 494084c5 2023-03-19 contact }
138 494084c5 2023-03-19 contact Ok(())
139 494084c5 2023-03-19 contact }
140 494084c5 2023-03-19 contact
141 494084c5 2023-03-19 contact fn write_eof(&mut self) -> io::Result<()> {
142 494084c5 2023-03-19 contact self.writer.write_all(EOF)
143 494084c5 2023-03-19 contact }
144 0cde9354 2023-03-15 contact }
145 0cde9354 2023-03-15 contact
146 0cde9354 2023-03-15 contact impl<'w, W: Write, I: Iterator<Item = ServiceDir>> Iterator for Writer<'w, W, I> {
147 494084c5 2023-03-19 contact type Item = io::Result<()>;
148 0cde9354 2023-03-15 contact
149 0cde9354 2023-03-15 contact fn next(&mut self) -> Option<Self::Item> {
150 0cde9354 2023-03-15 contact if self.fused {
151 494084c5 2023-03-19 contact None
152 494084c5 2023-03-19 contact } else {
153 494084c5 2023-03-19 contact self.iter
154 494084c5 2023-03-19 contact .next()
155 494084c5 2023-03-19 contact .map(|service_dir| {
156 6376d1c8 2023-04-01 contact let unescaped = service_dir.name().to_str().unwrap();
157 6376d1c8 2023-04-01 contact let service = escape(unescaped);
158 494084c5 2023-03-19 contact let status = service_dir.status()?;
159 494084c5 2023-03-19 contact if self.first {
160 494084c5 2023-03-19 contact self.first = false;
161 494084c5 2023-03-19 contact self.write_descriptors()?;
162 0cde9354 2023-03-15 contact }
163 494084c5 2023-03-19 contact self.write_status(&service, &status)
164 494084c5 2023-03-19 contact })
165 494084c5 2023-03-19 contact .or_else(|| {
166 494084c5 2023-03-19 contact self.fused = true;
167 494084c5 2023-03-19 contact Some(self.write_eof())
168 494084c5 2023-03-19 contact })
169 494084c5 2023-03-19 contact }
170 0cde9354 2023-03-15 contact }
171 0cde9354 2023-03-15 contact }
172 0cde9354 2023-03-15 contact
173 0cde9354 2023-03-15 contact impl<'w, W: Write, I: Iterator<Item = ServiceDir>> FusedIterator for Writer<'w, W, I> {}