1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
//! Constants and configuration values for the systemg daemon.
//!
//! This module centralizes all magic numbers, strings, and configuration values
//! used throughout the daemon to improve maintainability and clarity.
use ;
/// Permission mode for runtime directories: owner read/write/execute only (`rwx------`).
///
/// Applied to state and log directories so other local users cannot traverse or
/// read the control socket, PID file, or logs.
pub const PRIVATE_DIR_MODE: u32 = 0o700;
/// Permission mode for sensitive runtime files: owner read/write only (`rw-------`).
///
/// Applied to the supervisor PID file, config hint, and control socket.
pub const PRIVATE_FILE_MODE: u32 = 0o600;
/// Typed lock abstraction for enforcing proper lock acquisition order in the daemon.
///
/// This enum ensures that locks are always acquired in a consistent order to prevent
/// deadlocks. The ordering is enforced through the `Ord` trait implementation.
///
/// # Lock Acquisition Rules
///
/// Locks MUST be acquired in ascending order of their discriminant values:
/// 1. `Processes` - Child process management
/// 2. `PidFile` - Process ID persistence
/// 3. `StateFile` - Service state persistence
/// 4. `RestartCounts` - Restart attempt tracking
/// 5. `ManualStopFlags` - Manual stop flag tracking
/// 6. `RestartSuppressed` - Restart suppression flags
///
/// # Example
/// ```ignore
/// // Correct: Acquiring in order
/// let _proc_lock = daemon.lock(DaemonLock::Processes)?;
/// let _pid_lock = daemon.lock(DaemonLock::PidFile)?;
///
/// // Incorrect: Would cause deadlock potential
/// // let _pid_lock = daemon.lock(DaemonLock::PidFile)?;
/// // let _proc_lock = daemon.lock(DaemonLock::Processes)?; // WRONG!
/// ```
/// Name of the PID file stored in the state directory.
/// Contains mappings of service names to process IDs.
pub const PID_FILE_NAME: &str = "pid.xml";
/// Lock file suffix for PID file to ensure exclusive access.
pub const PID_LOCK_SUFFIX: &str = ".lock";
/// Name of the service state file stored in the state directory.
/// Contains the current state and metadata for all managed services.
pub const STATE_FILE_NAME: &str = "state.xml";
/// Default shell used for executing service commands and hooks.
pub const DEFAULT_SHELL: &str = "sh";
/// `PATH` installed for a privilege-dropped service started from a clean
/// environment, so it can still resolve system binaries without inheriting the
/// supervisor's (root's) `PATH`.
pub const DEFAULT_SERVICE_PATH: &str = "/usr/local/bin:/usr/bin:/bin";
/// Shell argument flag for executing command strings.
pub const SHELL_COMMAND_FLAG: &str = "-c";
/// Caller/session-scoped environment variables that are stripped from
/// long-lived service environments by default. These describe the SSH session
/// of whoever ran `sysg` and must not leak into daemonized services, where they
/// pin a stale `ssh-agent` and orphan it under PID 1.
pub const SESSION_SCOPED_ENV_VARS: & = &;
/// Number of checks to perform when waiting for a process to become ready.
/// Used in conjunction with PROCESS_CHECK_INTERVAL.
pub const PROCESS_READY_CHECKS: usize = 10;
/// Interval between process readiness checks.
pub const PROCESS_CHECK_INTERVAL: Duration = from_millis;
/// Maximum time to wait for a service to start before timing out.
/// Applied during service initialization and health checks.
pub const SERVICE_START_TIMEOUT: Duration = from_secs;
/// Minimum continuous survival time required before a process without a health
/// check is reported ready. This prevents immediate bind and startup failures
/// from being mistaken for a successful launch between process probes.
pub const SERVICE_START_STABILITY: Duration = from_millis;
/// Default maximum duration of one health-check probe.
pub const DEFAULT_HEALTH_ATTEMPT_TIMEOUT: Duration = from_secs;
/// Default delay between health-check probes.
pub const DEFAULT_HEALTH_INTERVAL: Duration = from_secs;
/// Default minimum number of health-check probes before readiness fails.
pub const DEFAULT_HEALTH_RETRIES: u32 = 3;
/// Maximum time a `pre_start` command may run before it is killed and the start
/// fails. Pre-starts run inside the supervisor's single-writer owner thread, so
/// an UNBOUNDED pre-start that hangs (e.g. a network/proxy call that never
/// returns) would freeze EVERY subsequent mutation across ALL projects. Generous
/// enough for real migrations/DB-waits, but finite so one hung op cannot wedge
/// the whole supervisor.
pub const PRE_START_TIMEOUT: Duration = from_secs;
/// How long a foreground log-follow keeps retrying a project it has not seen
/// yet. The control socket (and so `supervisor_running()`) goes live before any
/// service is spawned, so a project absent from the first status snapshot is
/// still booting rather than gone. Must outlast a slow project's first unit
/// registration, including `pre_start` work such as builds and DB waits.
pub const FOREGROUND_ATTACH_GRACE: Duration = from_secs;
/// How long an attaching `start` waits for a queued project boot to settle
/// before reporting which services never came up.
///
/// Must exceed a realistic pre-start budget: a service whose `pre_start` waits
/// on a DB/tunnel can legitimately take minutes, and cutting across that window
/// reports a FALSE failure for a service that was still coming up. Observed
/// live: a 60s `wait-for-db.sh` plus a 5s restart backoff made a 120s grace
/// straddle the retry cycle, blaming a service that then ran healthily. Sized
/// against [`PRE_START_TIMEOUT`] plus room for one retry.
pub const START_SETTLE_GRACE: Duration = from_secs;
/// How long a synchronous project attach waits for the freshly-booted project
/// to record its first PID before seeding the status cache. Bounded so a project
/// whose services exit immediately cannot wedge the attaching caller.
pub const PROJECT_PID_SETTLE_TIMEOUT: Duration = from_secs;
/// How long a stop waits to CONFIRM a signalled process actually died before
/// reporting failure. A stop that records `stopped` without verifying death is
/// how a project came to report itself down while its services still held their
/// ports; the wait covers a slow shutdown without hiding a failed kill.
pub const STOP_VERIFY_TIMEOUT: Duration = from_secs;
/// Narrowest terminal width the progress spinner will believe. Anything smaller
/// is treated as a failed probe rather than a real terminal, so a zero/unset
/// winsize cannot truncate the status line down to a useless stub.
pub const MIN_SPINNER_WIDTH: usize = 40;
/// Terminal width assumed when the real width cannot be determined.
pub const DEFAULT_TERMINAL_WIDTH: usize = 80;
/// Polling interval when waiting for service state changes.
pub const SERVICE_POLL_INTERVAL: Duration = from_millis;
/// Number of attempts to verify a service is running after restart.
pub const POST_RESTART_VERIFY_ATTEMPTS: usize = 2;
/// Delay between post-restart verification attempts.
pub const POST_RESTART_VERIFY_DELAY: Duration = from_millis;
/// Maximum number of log lines to display in status output.
/// Prevents overwhelming the terminal with excessive log data.
pub const MAX_STATUS_LOG_LINES: usize = 50;
/// Buffer size for log output streams (stdout/stderr).
pub const LOG_BUFFER_SIZE: usize = 8192;
/// Maximum size of a single newline-framed control-socket command.
///
/// Caps the buffer `read_command` allocates so one connection cannot exhaust
/// supervisor memory by streaming bytes without a newline.
pub const MAX_CONTROL_LINE: u64 = 1024 * 1024;
/// Format string for hook labels combining stage and outcome.
/// Example: "pre_start.pending", "post_start.success"
pub const HOOK_LABEL_FORMAT: &str = "{}.{}";
/// Error message for malformed environment file lines.
pub const ENV_FILE_MALFORMED_MSG: &str =
"Ignoring malformed line in env file for '{}': {}";
/// Error message for environment file read failures.
pub const ENV_FILE_READ_ERROR_MSG: &str = "Failed to read env file for '{}': {}";
/// Error message for hook timeout parsing failures.
pub const HOOK_TIMEOUT_PARSE_ERROR_MSG: &str =
"Invalid timeout '{}' for hook {} on '{}': {}";
/// Error message for insufficient process signal permissions.
pub const INSUFFICIENT_SIGNAL_PERMISSIONS_MSG: &str =
"Insufficient permissions to signal process group {} for '{}'";
/// Error message for process tree termination failures.
pub const PROCESS_TREE_TERM_FAILURE_MSG: &str =
"Failed to terminate process tree rooted at PID {} for '{}'";
/// Deployment strategies for service restarts.
///
/// This enum provides type-safe handling of deployment strategies, ensuring
/// that only valid strategies can be used throughout the codebase.
/// Default deployment strategy when not specified in configuration.
pub const DEFAULT_DEPLOYMENT_STRATEGY: &str = "immediate";
/// Rolling deployment strategy identifier.
pub const ROLLING_DEPLOYMENT: &str = "rolling";
/// Immediate deployment strategy identifier.
pub const IMMEDIATE_DEPLOYMENT: &str = "immediate";
/// Message logged when skipping cron-managed services during bulk operations.
pub const SKIP_CRON_SERVICE_MSG: &str = "Skipping cron-managed service '{}' during bulk start; scheduled execution will launch it";
/// Message logged when skipping cron services during restart.
pub const SKIP_CRON_RESTART_MSG: &str = "Skipping cron-managed service '{}' during restart; scheduled execution will launch it";