From c1ab78c05a8d0274f80f17d1207fed26fdd88f12 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 9 Oct 2018 10:52:54 +0200 Subject: 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 --- src/main.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 29a71974b89b..b8eeb6b9fb5c 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 // // 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 { let read_result: io::Result = (|| { 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) -- cgit 1.4.1