aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs130
1 files changed, 106 insertions, 24 deletions
diff --git a/src/main.rs b/src/main.rs
index d0a2763..b77fabb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -290,8 +290,19 @@ fn run_command(command: Commands) -> Result<()> {
290 stop_daemon(&args.id, args.timeout, &root_dir) 290 stop_daemon(&args.id, args.timeout, &root_dir)
291 } 291 }
292 Commands::Tail(args) => { 292 Commands::Tail(args) => {
293 let show_stdout = !args.stderr || args.stdout; 293 // Clearer logic for determining which streams to show:
294 let show_stderr = !args.stdout || args.stderr; 294 // - If no flags are specified, show both streams
295 // - If one or both flags are specified, show only the requested streams
296 let show_stdout = if args.stdout || args.stderr {
297 args.stdout
298 } else {
299 true // Show both when no flags are specified
300 };
301 let show_stderr = if args.stdout || args.stderr {
302 args.stderr
303 } else {
304 true // Show both when no flags are specified
305 };
295 let root_dir = resolve_root_dir(&args.global)?; 306 let root_dir = resolve_root_dir(&args.global)?;
296 tail_logs( 307 tail_logs(
297 &args.id, 308 &args.id,
@@ -303,8 +314,19 @@ fn run_command(command: Commands) -> Result<()> {
303 ) 314 )
304 } 315 }
305 Commands::Cat(args) => { 316 Commands::Cat(args) => {
306 let show_stdout = !args.stderr || args.stdout; 317 // Clearer logic for determining which streams to show:
307 let show_stderr = !args.stdout || args.stderr; 318 // - If no flags are specified, show both streams
319 // - If one or both flags are specified, show only the requested streams
320 let show_stdout = if args.stdout || args.stderr {
321 args.stdout
322 } else {
323 true // Show both when no flags are specified
324 };
325 let show_stderr = if args.stdout || args.stderr {
326 args.stderr
327 } else {
328 true // Show both when no flags are specified
329 };
308 let root_dir = resolve_root_dir(&args.global)?; 330 let root_dir = resolve_root_dir(&args.global)?;
309 cat_logs(&args.id, show_stdout, show_stderr, &root_dir) 331 cat_logs(&args.id, show_stdout, show_stderr, &root_dir)
310 } 332 }
@@ -332,7 +354,9 @@ fn run_command(command: Commands) -> Result<()> {
332} 354}
333 355
334fn find_git_root() -> Result<PathBuf> { 356fn find_git_root() -> Result<PathBuf> {
335 let mut current = std::env::current_dir()?; 357 let mut current = std::env::current_dir().with_context(
358 || "Failed to get current working directory. Please check your file system permissions",
359 )?;
336 360
337 // Find the git root directory 361 // Find the git root directory
338 let git_root = loop { 362 let git_root = loop {
@@ -345,7 +369,10 @@ fn find_git_root() -> Result<PathBuf> {
345 Some(parent) => current = parent.to_path_buf(), 369 Some(parent) => current = parent.to_path_buf(),
346 None => { 370 None => {
347 return Err(anyhow::anyhow!( 371 return Err(anyhow::anyhow!(
348 "No git repository found. Please specify --root-dir or run from within a git repository" 372 "No git repository found in current directory or any parent directories.\n\
373 Please either:\n\
374 1. Run demon from within a git repository, or\n\
375 2. Specify a root directory with --root-dir <path>"
349 )); 376 ));
350 } 377 }
351 } 378 }
@@ -358,17 +385,30 @@ fn find_git_root() -> Result<PathBuf> {
358 if demon_dir.exists() { 385 if demon_dir.exists() {
359 if !demon_dir.is_dir() { 386 if !demon_dir.is_dir() {
360 return Err(anyhow::anyhow!( 387 return Err(anyhow::anyhow!(
361 "Path {} exists but is not a directory. Please remove it or specify --root-dir", 388 "Path {} exists but is not a directory.\n\
389 Please either:\n\
390 1. Remove the existing file: rm {}\n\
391 2. Specify a different root directory with --root-dir <path>",
392 demon_dir.display(),
362 demon_dir.display() 393 demon_dir.display()
363 )); 394 ));
364 } 395 }
365 // .demon exists and is a directory, we can use it 396 // .demon exists and is a directory, we can use it
397 tracing::debug!("Using existing daemon directory: {}", demon_dir.display());
366 return Ok(demon_dir); 398 return Ok(demon_dir);
367 } 399 }
368 400
369 // Create .demon directory 401 // Create .demon directory
370 std::fs::create_dir(&demon_dir) 402 std::fs::create_dir(&demon_dir).with_context(|| {
371 .with_context(|| format!("Failed to create daemon directory {}", demon_dir.display()))?; 403 format!(
404 "Failed to create daemon directory {}.\n\
405 This may be due to:\n\
406 1. Insufficient permissions in the git root directory\n\
407 2. File system errors\n\
408 Please check permissions or specify --root-dir with a writable directory",
409 demon_dir.display()
410 )
411 })?;
372 412
373 tracing::info!("Created daemon directory: {}", demon_dir.display()); 413 tracing::info!("Created daemon directory: {}", demon_dir.display());
374 414
@@ -378,24 +418,55 @@ fn find_git_root() -> Result<PathBuf> {
378fn resolve_root_dir(global: &Global) -> Result<PathBuf> { 418fn resolve_root_dir(global: &Global) -> Result<PathBuf> {
379 match &global.root_dir { 419 match &global.root_dir {
380 Some(dir) => { 420 Some(dir) => {
381 if !dir.exists() { 421 // Validate the specified root directory
382 return Err(anyhow::anyhow!( 422 validate_root_directory(dir)
383 "Specified root directory does not exist: {}",
384 dir.display()
385 ));
386 }
387 if !dir.is_dir() {
388 return Err(anyhow::anyhow!(
389 "Specified root path is not a directory: {}",
390 dir.display()
391 ));
392 }
393 Ok(dir.clone())
394 } 423 }
395 None => find_git_root(), 424 None => find_git_root(),
396 } 425 }
397} 426}
398 427
428/// Validates that a directory path is suitable for use as a root directory
429fn validate_root_directory(dir: &Path) -> Result<PathBuf> {
430 // First check if the path exists
431 if !dir.exists() {
432 return Err(anyhow::anyhow!(
433 "Specified root directory does not exist: {}\nPlease create the directory first or specify a different path",
434 dir.display()
435 ));
436 }
437
438 // Check if it's actually a directory
439 if !dir.is_dir() {
440 return Err(anyhow::anyhow!(
441 "Specified root path is not a directory: {}\nPlease specify a directory path, not a file",
442 dir.display()
443 ));
444 }
445
446 // Try to canonicalize the path to resolve symlinks and make it absolute
447 let canonical_dir = dir.canonicalize().with_context(|| {
448 format!(
449 "Failed to resolve path {}: path may contain invalid components or broken symlinks",
450 dir.display()
451 )
452 })?;
453
454 // Check if we can write to the directory by attempting to create a temporary file
455 let temp_file_path = canonical_dir.join(".demon_write_test");
456 if let Err(e) = std::fs::write(&temp_file_path, "test") {
457 return Err(anyhow::anyhow!(
458 "Cannot write to specified root directory {}: {}\nPlease check directory permissions",
459 canonical_dir.display(),
460 e
461 ));
462 }
463 // Clean up the test file
464 let _ = std::fs::remove_file(&temp_file_path);
465
466 tracing::debug!("Validated root directory: {}", canonical_dir.display());
467 Ok(canonical_dir)
468}
469
399fn build_file_path(root_dir: &Path, id: &str, extension: &str) -> PathBuf { 470fn build_file_path(root_dir: &Path, id: &str, extension: &str) -> PathBuf {
400 root_dir.join(format!("{id}.{extension}")) 471 root_dir.join(format!("{id}.{extension}"))
401} 472}
@@ -440,8 +511,19 @@ fn run_daemon(id: &str, command: &[String], root_dir: &Path) -> Result<()> {
440 let pid_file_data = PidFile::new(child.id(), command.to_vec()); 511 let pid_file_data = PidFile::new(child.id(), command.to_vec());
441 pid_file_data.write_to_file(&pid_file)?; 512 pid_file_data.write_to_file(&pid_file)?;
442 513
443 // Don't wait for the child - let it run detached 514 // Properly detach the child process
444 std::mem::forget(child); 515 // Instead of using std::mem::forget which prevents proper cleanup,
516 // we spawn a background thread to handle the child process lifecycle
517 std::thread::spawn(move || {
518 // The child process is moved into this thread
519 // When this thread ends, the Child's Drop implementation will run
520 // This ensures proper resource cleanup while still detaching the process
521
522 // We don't need to wait for the child since we want it to run independently
523 // But by letting the Child's Drop trait run, we ensure proper cleanup
524 // The process will become the child of init (PID 1) which will reap it
525 drop(child);
526 });
445 527
446 println!( 528 println!(
447 "Started daemon '{}' with PID written to {}", 529 "Started daemon '{}' with PID written to {}",