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