Comparison

util/datamanager.lua @ 5049:5d685f123332

util.datamanager: Write to a temporary file and atomically move it into place
author Kim Alvefur <zash@zash.se>
date Sun, 29 Jul 2012 03:26:03 +0200
parent 5045:4ba6940deed0
child 5051:71253db26fda
comparison
equal deleted inserted replaced
5048:e02161ba20e0 5049:5d685f123332
13 local char = string.char; 13 local char = string.char;
14 local pcall = pcall; 14 local pcall = pcall;
15 local log = require "util.logger".init("datamanager"); 15 local log = require "util.logger".init("datamanager");
16 local io_open = io.open; 16 local io_open = io.open;
17 local os_remove = os.remove; 17 local os_remove = os.remove;
18 local os_rename = os.rename;
18 local tostring, tonumber = tostring, tonumber; 19 local tostring, tonumber = tostring, tonumber;
19 local error = error; 20 local error = error;
20 local next = next; 21 local next = next;
21 local t_insert = table.insert; 22 local t_insert = table.insert;
22 local append = require "util.serialization".append; 23 local t_concat = table.concat;
23 local envloadfile = require"util.envload".envloadfile; 24 local envloadfile = require"util.envload".envloadfile;
24 local serialize = require "util.serialization".serialize; 25 local serialize = require "util.serialization".serialize;
25 local path_separator = assert ( package.config:match ( "^([^\n]+)" ) , "package.config not in standard form" ) -- Extract directory seperator from package.config (an undocumented string that comes with lua) 26 local path_separator = assert ( package.config:match ( "^([^\n]+)" ) , "package.config not in standard form" ) -- Extract directory seperator from package.config (an undocumented string that comes with lua)
26 local lfs = require "lfs"; 27 local lfs = require "lfs";
27 local prosody = prosody; 28 local prosody = prosody;
147 return nil, "Error reading storage"; 148 return nil, "Error reading storage";
148 end 149 end
149 return ret; 150 return ret;
150 end 151 end
151 152
153 local function atomic_store(filename, data)
154 local scratch = filename.."~";
155 local f, ok, msg;
156 repeat
157 f, msg = io_open(scratch, "w");
158 if not f then break end
159
160 ok, msg = f:write(data);
161 if not ok then break end
162
163 ok, msg = f:close();
164 if not ok then break end
165
166 return os_rename(scratch, filename);
167 until false;
168
169 -- Cleanup
170 if f then f:close(); end
171 os_remove(scratch);
172 return nil, msg;
173 end
174
152 function store(username, host, datastore, data) 175 function store(username, host, datastore, data)
153 if not data then 176 if not data then
154 data = {}; 177 data = {};
155 end 178 end
156 179
158 if username == false then 181 if username == false then
159 return true; -- Don't save this data at all 182 return true; -- Don't save this data at all
160 end 183 end
161 184
162 -- save the datastore 185 -- save the datastore
163 local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+"); 186 local d = "return " .. serialize(data) .. ";\n";
164 if not f then 187 local ok, msg = atomic_store(getpath(username, host, datastore, nil, true), d);
188 if not ok then
165 log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); 189 log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
166 return nil, "Error saving to storage"; 190 return nil, "Error saving to storage";
167 end 191 end
168 f:write("return ");
169 append(f, data);
170 f:close();
171 if next(data) == nil then -- try to delete empty datastore 192 if next(data) == nil then -- try to delete empty datastore
172 log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil"); 193 log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil");
173 os_remove(getpath(username, host, datastore)); 194 os_remove(getpath(username, host, datastore));
174 end 195 end
175 -- we write data even when we are deleting because lua doesn't have a 196 -- we write data even when we are deleting because lua doesn't have a
204 if not data then 225 if not data then
205 data = {}; 226 data = {};
206 end 227 end
207 if callback(username, host, datastore) == false then return true; end 228 if callback(username, host, datastore) == false then return true; end
208 -- save the datastore 229 -- save the datastore
209 local f, msg = io_open(getpath(username, host, datastore, "list", true), "w+"); 230 local d = {};
210 if not f then 231 for _, item in ipairs(data) do
232 d[#d+1] = "item(" .. serialize(item) .. ");\n";
233 end
234 local ok, msg = atomic_store(getpath(username, host, datastore, "list", true), t_concat(d));
235 if not ok then
211 log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); 236 log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
212 return; 237 return;
213 end 238 end
214 for _, d in ipairs(data) do
215 f:write("item(");
216 append(f, d);
217 f:write(");\n");
218 end
219 f:close();
220 if next(data) == nil then -- try to delete empty datastore 239 if next(data) == nil then -- try to delete empty datastore
221 log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil"); 240 log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil");
222 os_remove(getpath(username, host, datastore, "list")); 241 os_remove(getpath(username, host, datastore, "list"));
223 end 242 end
224 -- we write data even when we are deleting because lua doesn't have a 243 -- we write data even when we are deleting because lua doesn't have a