1b55eb4a23edd6e38648f0cfdc6128ea6eaa8b1a
[synfig.git] / autorecover.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file autorecover.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "autorecover.h"
34
35 #ifdef HAVE_SYS_ERRNO_H
36 #include <sys/errno.h>
37 #endif
38 //#include <unistd.h>
39 #include "app.h"
40 #include <synfig/savecanvas.h>
41 #include <synfig/loadcanvas.h>
42 #include <fstream>
43 #include <iostream>
44 #include "instance.h"
45
46 #ifdef HAVE_SYS_STAT_H
47 #include <sys/stat.h>
48 #endif
49
50 #ifdef HAVE_SYS_TIME_H
51 #include <sys/time.h>
52 #endif
53
54 #ifdef HAVE_SYS_WAIT_H
55 #include <sys/wait.h>
56 #endif
57
58 #ifdef HAVE_SYS_RESOURCE_H
59 #include <sys/resource.h>
60 #endif
61
62 #ifdef HAVE_UNISTD_H
63 #include <unistd.h>
64 #endif
65
66 #include "general.h"
67
68 #endif
69
70 /* === U S I N G =========================================================== */
71
72 using namespace std;
73 using namespace etl;
74 using namespace synfig;
75 using namespace studio;
76
77 /* === M A C R O S ========================================================= */
78
79 #ifdef _WIN32
80 #define mkdir(x,y) mkdir(x)
81 #endif
82
83 /* === G L O B A L S ======================================================= */
84
85 /* === P R O C E D U R E S ================================================= */
86
87 /* === M E T H O D S ======================================================= */
88
89 AutoRecover::AutoRecover()
90 {
91         // Three Minutes
92         set_timeout(3*60*1000);
93
94         if(mkdir(get_shadow_directory().c_str(),ACCESSPERMS)<0)
95         {
96                 if(errno!=EEXIST)
97                         synfig::error("UNABLE TO CREATE \"%s\"",get_shadow_directory().c_str());
98         }
99         else
100         {
101                 synfig::info("Created directory \"%s\"",get_shadow_directory().c_str());
102         }
103 }
104
105 AutoRecover::~AutoRecover()
106 {
107 }
108
109 synfig::String
110 AutoRecover::get_shadow_directory()
111 {
112         return Glib::build_filename(App::get_user_app_directory(),"tmp");
113 }
114
115 int
116 AutoRecover::pid()
117 {
118 //      return getpid();
119         return 0;
120 }
121
122 void
123 AutoRecover::set_timeout(int milliseconds)
124 {
125         timeout=milliseconds;
126         auto_backup_connect.disconnect();
127         if(timeout)
128                 auto_backup_connect=Glib::signal_timeout().connect(sigc::ptr_fun(&AutoRecover::auto_backup),timeout);
129 //              auto_backup_connect=App::main.get_context()->signal_timeout().connect(sigc::mem_fun(&AutoRecover::auto_backup),timeout);
130 }
131
132 synfig::String
133 AutoRecover::get_shadow_file_name(const synfig::String& filename)
134 {
135         unsigned int hash1(0xdeadbeef);
136         unsigned int hash2(0x83502529);
137         char* str_hash1(reinterpret_cast<char*>(&hash1));
138         char* str_hash2(reinterpret_cast<char*>(&hash2));
139
140         // First we need to hash up the directory
141         {
142                 String pool(dirname(filename));
143
144                 while(pool.size()>4)
145                 {
146                         str_hash1[0]^=pool[1];str_hash1[1]^=pool[2];str_hash1[2]^=pool[3];str_hash1[3]^=pool[0];
147                         str_hash2[3]+=pool[0];str_hash2[2]+=pool[1];str_hash2[1]+=pool[2];str_hash2[0]+=pool[3];
148                         swap(hash1,hash2);
149                         pool=String(pool,4,pool.size());
150                 }
151                 while(pool.size())
152                 {
153                         str_hash1[0]^=pool[0];
154                         str_hash1[2]^=pool[0];
155                         str_hash2[1]^=pool[0];
156                         str_hash2[3]^=pool[0];
157                         swap(hash1,hash2);
158                         pool=String(pool,1,pool.size());
159                 }
160         }
161         hash1^=hash2;
162
163         return Glib::build_filename(get_shadow_directory(),strprintf("%08X-%s",hash1,basename(filename).c_str()));
164
165 //      return dirname(filename) + ETL_DIRECTORY_SEPARATOR + ".shadow_" + basename(filename);
166 }
167
168 bool
169 AutoRecover::cleanup_pid(int pid)
170 {
171 #ifdef HAVE_FORK
172         int status=0;
173         if(waitpid(pid,&status,WNOHANG)==-1)
174         {
175                 synfig::info("PID %d isn't a zombie yet",pid);
176                 return true;
177         }
178         if(WEXITSTATUS(status)!=0)
179         {
180                 synfig::error("Autobackup seems to have failed! (PID=%d)",pid);
181         }
182 //      else
183 //              synfig::info("PID=%d has been cleaned up",pid);
184 #endif
185         return false;
186 }
187
188 bool
189 AutoRecover::auto_backup()
190 {
191         int pid(0);
192
193 #ifdef HAVE_FORK
194         pid=fork();
195 #endif
196
197         if(pid<=0)
198         {
199 #ifdef HAVE_SETPRIORITY
200                 // make us low priority so that we don't
201                 // cause the machine to slow down too much
202                 setpriority(PRIO_PROCESS,0,15);
203 #endif
204
205                 try
206                 {
207                         std::list<etl::handle<Instance> >::iterator iter;
208
209                         std::string filename=App::get_config_file("autorecovery");
210                         std::ofstream file(filename.c_str());
211
212                         int savecount(0);
213
214                         for(iter=App::instance_list.begin();iter!=App::instance_list.end();++iter)
215                         {
216                                 // If this file hasn't even been changed
217                                 // since it was last saved, then don't bother
218                                 // backing it up.
219                                 if((*iter)->get_action_count()==0)
220                                         continue;
221
222                                 Canvas::Handle canvas((*iter)->get_canvas());
223                                 file<<canvas->get_file_name()<<endl;
224                                 save_canvas(get_shadow_file_name(canvas->get_file_name()),canvas);
225                                 savecount++;
226                         }
227
228 //                      if(savecount)
229 //                              synfig::info("AutoRecover::auto_backup(): %d Files backed up.",savecount);
230                 }
231                 catch(...)
232                 {
233                         synfig::error("AutoRecover::auto_backup(): UNKNOWN EXCEPTION THROWN.");
234                         synfig::error("AutoRecover::auto_backup(): FILES NOT BACKED UP.");
235                 }
236
237 #ifdef HAVE_FORK
238                 if(pid==0)
239                 {
240                         _exit(0);
241                 }
242 #endif
243         }
244
245 #ifdef HAVE_FORK
246         Glib::signal_timeout().connect(
247                 sigc::bind(
248                         sigc::ptr_fun(&AutoRecover::cleanup_pid),
249                         pid
250                 ),
251                 60*1000
252         );
253 #endif
254
255         // Also go ahead and save the settings
256         App::save_settings();
257
258         return true;
259 }
260
261 bool
262 AutoRecover::recovery_needed()const
263 {
264         std::string filename=App::get_config_file("autorecovery");
265         std::ifstream file(filename.c_str());
266         if(!file)
267                 return false;
268
269         while(file)
270         {
271                 std::string filename;
272                 getline(file,filename);
273                 if(!filename.empty())
274                         return true;
275         }
276
277         return false;
278 }
279
280 bool
281 AutoRecover::recover(int& number_recovered)
282 {
283         std::string filename=App::get_config_file("autorecovery");
284         std::ifstream file(filename.c_str());
285         number_recovered = 0;
286         if(!file)
287                 return false;
288         bool success=true;
289
290         while(file)
291         {
292                 std::string filename;
293                 getline(file,filename);
294                 if(filename.empty())
295                         continue;
296
297                 // Open the file
298                 if(App::open_as(get_shadow_file_name(filename),filename))
299                 {
300                         // Correct the file name
301                         App::instance_list.back()->set_file_name(filename);
302
303                         // This file isn't saved! mark it as such
304                         App::instance_list.back()->inc_action_count();
305
306                         number_recovered++;
307                 }
308                 else
309                         success=false;
310         }
311
312         return success;
313 }
314
315 void
316 AutoRecover::normal_shutdown()
317 {
318         // Turn off the timer
319         auto_backup_connect.disconnect();
320
321         std::string filename=App::get_config_file("autorecovery");
322         remove(filename.c_str());
323 }
324
325 void
326 AutoRecover::clear_backup(synfig::Canvas::Handle canvas)
327 {
328         if(canvas)
329                 remove(get_shadow_file_name(canvas->get_file_name()).c_str());
330 }