about summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2018-10-09T08·52+0200
committerVincent Ambo <github@tazj.in>2018-10-09T09·38+0200
commitc1ab78c05a8d0274f80f17d1207fed26fdd88f12 (patch)
treecce1ab5d830341369fe01731cc374a93155a2588 /src/main.rs
parent7ec507f8cb6554db3a02336bcb8096239d361a75 (diff)
fix: Write cursor into temporary file and move it
This deals with a potential issue where creating a new file in place
of an existing cursor position file may cause position files to be
empty.

The cause for this is that the newly created file will truncate the
previous content, if journaldriver is then terminated before it
completes the cursor write to this file, it will not have written a
valid cursor (or anything at all).

Potentially relates to #2
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs29
1 files changed, 21 insertions, 8 deletions
diff --git a/src/main.rs b/src/main.rs
index 29a71974b8..b8eeb6b9fb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2018  Aprila Bank ASA (contact: vincent@aprila.no)
+// Copyright (C) 2018 Vincent Ambo <mail@tazj.in>
 //
 // journaldriver is free software: you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
@@ -49,7 +49,7 @@ use chrono::prelude::*;
 use failure::ResultExt;
 use serde_json::{from_str, Value};
 use std::env;
-use std::fs::{self, File};
+use std::fs::{self, File, rename};
 use std::io::{self, Read, ErrorKind, Write};
 use std::mem;
 use std::path::PathBuf;
@@ -105,9 +105,16 @@ lazy_static! {
 
     /// Path to the file in which journaldriver should persist its
     /// cursor state.
-    static ref POSITION_FILE: PathBuf = env::var("CURSOR_POSITION_FILE")
+    static ref CURSOR_FILE: PathBuf = env::var("CURSOR_POSITION_FILE")
         .unwrap_or("/var/lib/journaldriver/cursor.pos".into())
         .into();
+
+    /// Path to the temporary file used for cursor position writes.
+    static ref CURSOR_TMP_FILE: PathBuf = {
+        let mut tmp_path = CURSOR_FILE.clone();
+        tmp_path.set_extension("pos.tmp");
+        tmp_path
+    };
 }
 
 /// Convenience helper for retrieving values from the metadata server.
@@ -482,10 +489,16 @@ fn receiver_loop(mut journal: Journal) -> Result<()> {
     }
 }
 
-/// Writes the current cursor into `/var/journaldriver/cursor.pos`.
+/// Writes the current cursor into `/var/journaldriver/cursor.pos`. To
+/// avoid issues with journaldriver being terminated while the cursor
+/// is still being written, this will first write the cursor into a
+/// temporary file and then move it.
 fn persist_cursor(cursor: String) -> Result<()> {
-    let mut file = File::create(&*POSITION_FILE)?;
-    write!(file, "{}", cursor).map_err(Into::into)
+    let mut file = File::create(&*CURSOR_TMP_FILE)?;
+    write!(file, "{}", cursor).context("Failed to write cursor file")?;
+    rename(&*CURSOR_TMP_FILE, &*CURSOR_FILE)
+        .context("Failed to move cursor file")
+        .map_err(Into::into)
 }
 
 /// Flushes all drained records to Stackdriver. Any Stackdriver
@@ -564,7 +577,7 @@ fn write_entries(token: &Token, request: Value) -> Result<()> {
 fn initial_cursor() -> Result<JournalSeek> {
     let read_result: io::Result<String> = (|| {
         let mut contents = String::new();
-        let mut file = File::open(&*POSITION_FILE)?;
+        let mut file = File::open(&*CURSOR_FILE)?;
         file.read_to_string(&mut contents)?;
         Ok(contents.trim().into())
     })();
@@ -586,7 +599,7 @@ fn main () {
 
     // If the cursor file does not yet exist, the directory structure
     // leading up to it should be created:
-    let cursor_position_dir = POSITION_FILE.parent()
+    let cursor_position_dir = CURSOR_FILE.parent()
         .expect("Invalid cursor position file path");
 
     fs::create_dir_all(cursor_position_dir)