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