1 /*
2     Copyright 2007, 2008, 2009, 2010 Geyer Klaus
3 
4     This file is part of Cat'sEyE.
5 
6     Cat'sEyE is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     Cat'sEyE is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with Cat'sEyE.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 #ifndef NO_GIO
20 
21 #include <gtk/gtk.h>
22 #ifndef NO_GIO
23 #include <gio/gio.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h> 	//e.g. chdir, usleep
28 
29 #include "helpers.h"
30 #include "Lists.h"
31 #include "userdata.h"
32 #include "myfm.h"
33 #include "automaticUpdate.h"
34 #include "mytrace.h"
35 
36 #include "allInclude.h"
37 
38 
39 #define AUTOUPDATE_MINTIME 500000 //us
40 #define MONITORRATELIMIT 5 //ms
41 
42 
43 struct autoUpdateList *g_listAutoUpdateList;
44 
45 
autoUpdateList_UpdateThread(struct AllInformationAllUseStruct * info)46 void autoUpdateList_UpdateThread (struct AllInformationAllUseStruct *info){
47     //we will not update emediatly but waiting for AUTOUPDATE_MINTIME useconds before udating.
48     //the monitor signal can check for the flag in buflist
49     //infostruct:
50     //Info_1 = struct autoUpdateList
51     //Info_2 = monitor
52     TRACEIT(11,"autoUpdateList_UpdateThread	    start");
53 
54     if (info == NULL){
55         TRACEIT (2, "autoUpdateList_UpdateThread        parameter NULL");
56         g_thread_exit(0);
57     }
58 
59     struct autoUpdateList *buflist = (struct autoUpdateList *)(info->Info_1);
60     GFileMonitor *monitor = (GFileMonitor *)info->Info_2;
61     free (info);
62     info = NULL;
63 
64     if (buflist == NULL){
65         TRACEIT (2, "autoUpdateList_UpdateThread        parameter 2 NULL");
66         g_thread_exit(0);
67     }
68 
69     usleep (AUTOUPDATE_MINTIME);
70 
71     if ( 1 != autoUpdateList_ValidateItem (buflist, monitor)){
72         TRACEIT(11,"autoUpdateList_UpdateThread	    end, invalid");
73         g_thread_exit(0);
74     }
75 
76     myWrap_gdk_threads_enter ("autoUpdateList_UpdateThread");
77     updateObjectsViewingFolder(buflist->gstrPath->str, TRUE);
78     myWrap_gdk_threads_leave ("autoUpdateList_UpdateThread");
79 
80     buflist->iUpdateThreadStarted=0;
81 
82     TRACEIT(11,"autoUpdateList_UpdateThread	    end");
83     g_thread_exit(0);
84 }
85 
86 
autoUpdateList_monitoredDirChanged(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,struct autoUpdateList * buflist)87 void autoUpdateList_monitoredDirChanged (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, struct autoUpdateList *buflist){
88     TRACEIT(11,"autoUpdateList_monitoredDirChanged	    start");
89     GError *Error=NULL;
90 
91     if ( 1 != autoUpdateList_ValidateItem (buflist, monitor)){
92         TRACEIT(2,"autoUpdateList_monitoredDirChanged	    invalid list item");
93         return;
94     }
95 
96     if (buflist->iUpdateThreadStarted == 0){
97         if ( TRUE == g_mutex_trylock (buflist->mtxJoinUpdateThread)){
98             if (buflist->gthreadUpdateThread != NULL){
99                 g_thread_join (buflist->gthreadUpdateThread);
100                 buflist->gthreadUpdateThread = NULL;
101             }
102             g_mutex_unlock (buflist->mtxJoinUpdateThread);
103         }
104         else {
105             TRACEIT(11,"autoUpdateList_monitoredDirChanged	    end 1");
106             return;
107         }
108 
109         struct AllInformationAllUseStruct *info = malloc (sizeof (struct AllInformationAllUseStruct));
110         if (info == NULL){
111             TRACEIT(2,"autoUpdateList_monitoredDirChanged	    unable to allocate memory");
112             return;
113         }
114         info->Info_1 = (void *)buflist;
115         info->Info_2 = (void *)monitor;
116 
117         buflist->iUpdateThreadStarted = 1; //this should be close to the thread creating call
118         buflist->gthreadUpdateThread = g_thread_create((GThreadFunc)autoUpdateList_UpdateThread, (void *)info , TRUE, &Error);
119 
120         if (Error != NULL){
121             buflist->iUpdateThreadStarted = 0;
122             free (info);
123             TRACEIT(2,"autoUpdateList_monitoredDirChanged	    error starting update thread:");
124             TRACEIT(2,Error->message);
125             g_error_free (Error);
126             Error = NULL;
127         }
128     }
129 
130     //printf("monitor: update signal end %s\n",buflist->gstrPath->str);
131 
132     TRACEIT(11,"autoUpdateList_monitoredDirChanged	    end");
133 }
134 
135 
autoUpdateList_DeleteObject(struct autoUpdateList * Object)136 void autoUpdateList_DeleteObject(struct autoUpdateList *Object){
137 	TRACEIT(10,"autoUpdateList_DeleteObject	    start");
138 	if (Object == NULL){
139         TRACEIT (2, "autoUpdateList_DeleteObject    parameter NULL");
140         return;
141 	}
142     GError *Error=NULL;
143 
144 	if (Object->PrevItem != NULL){
145 		Object->PrevItem->NextItem = Object->NextItem;
146 	}
147 	else{
148 		g_listAutoUpdateList = Object->NextItem;
149 		if (g_listAutoUpdateList != NULL){
150             g_listAutoUpdateList->PrevItem = NULL;
151 		}
152 	}
153 
154 	if (Object->NextItem != NULL){
155 		Object->NextItem->PrevItem = Object->PrevItem;
156 	}
157     //printf("autoUpdateList_DeleteObject: going to start delete thread\n");
158     //g_thread_create((GThreadFunc)autoUpdateList_deleteItemMembersAndItem, (void *)Object , FALSE, &Error);
159     g_thread_create((GThreadFunc)autoUpdateList_deleteItemMembersAndItem_threadWrapper, (void *)Object , FALSE, &Error);
160 
161     if (Error != NULL){
162         TRACEIT(2,"autoUpdateList_DeleteObject	    error starting delete thread:");
163         TRACEIT(2,Error->message);
164         g_error_free (Error);
165         Error = NULL;
166     }
167     //why we create this thread:
168     //remember the bug where we unpacked some tar file which created a directory which we entered emediatly after it apeared and
169     //CatsEyE froze?
170     //The error was: while such a directory creation fires 4 monitor-callbacks waiting 800ms (default now its 5ms) between each
171     //callback call. The user (we) entered the directory that leads to the deletion of the monitor in some of our UpdateViewWith ...
172     //functions. However, this functions are thread save because they are signal driven (remember the mainloop). But the monitor-
173     //callback is called in between and the update thread has allready started, the update thread then, tries to gdk_thread_enter
174     //but can not because the UpdateView... fucntion is running. But the UpdateView function waits for the monitor list to delete
175     //this monitor, and the function to delete this monitor waits for the updateThread to finish. And there it is, the deadlock.
176 
177     //TZo solve this, I decided to create anohter thread when deleting a monitor, the monitor will be extracted from the list
178     //and the thread will delete the item after the update thread has quit. In this case, the deletefunction will not block
179     //if the updatethread is running and the UpdateView... function can continue.
180 
181     //But, in this case, the update thread has to validate the monitor item before using it, this is done too.
182 
183 
184 	TRACEIT(10,"autoUpdateList_DeleteObject	    ends");
185 	return;
186 }
187 
188 
autoUpdateList_deleteItemMembersAndItem_threadWrapper(struct autoUpdateList * item)189 void autoUpdateList_deleteItemMembersAndItem_threadWrapper (struct autoUpdateList *item){
190     autoUpdateList_deleteItemMembersAndItem (item);
191     g_thread_exit(0);
192 }
193 
194 
autoUpdateList_deleteItemMembersAndItem(struct autoUpdateList * item)195 void autoUpdateList_deleteItemMembersAndItem (struct autoUpdateList *item){
196     //this function will delete (free) all the members of item and after that will delete item itself
197     //this function WILL NOT care about the list and linked items, this is the matter of the functions calling this
198     //please mind, that this function could be a thread
199     TRACEIT(10,"autoUpdateList_deleteItemMembersAndItem		start");
200     if (item == NULL){
201         TRACEIT(2,"autoUpdateList_deleteItemMembersAndItem		parameter NULL");
202         return;
203     }
204 
205     if (FALSE == g_file_monitor_cancel ( item->monitor )){
206         TRACEIT(2,"autoUpdateList_DeleteObject	    monitor could not be cancelled");
207     }
208 
209     if (item->mtxJoinUpdateThread != NULL){
210         g_mutex_lock (item->mtxJoinUpdateThread);
211         if (item->gthreadUpdateThread != NULL){
212             g_thread_join (item->gthreadUpdateThread);
213             item->gthreadUpdateThread = NULL;
214         }
215         g_mutex_unlock (item->mtxJoinUpdateThread);
216     }
217     //printf("deleting monitor for: %s\n",item->gstrPath->str);
218 
219     g_string_free(item->gstrPath, TRUE);
220     g_object_unref(item->monitor);
221     g_object_unref(item->gfilePath);
222 
223     g_thread_yield (); //this gives CPU time to other threads
224     g_mutex_free (item->mtxJoinUpdateThread);
225     item->mtxJoinUpdateThread = NULL;
226     //printf ("autoUpdateList_deleteItemMembersAndItem: deleting item: %p\n",item);
227 
228 	free(item);
229 
230     char cpBuf[41];
231     getcwd (cpBuf, 40);
232     //printf("current working directory: %s\n", cpBuf);
233 
234     TRACEIT(10,"autoUpdateList_deleteItemMembersAndItem		end");
235     return;
236 }
237 
238 
autoUpdateList_deleteList()239 void autoUpdateList_deleteList (){
240     TRACEIT(10,"autoUpdateList_deleteList		start");
241     //printf("monitor: deleting LIST\n");
242 
243     struct autoUpdateList *ThisObject;
244     struct autoUpdateList *ObjectBuf;
245 
246     //autoUpdateList_debugOut ();
247 
248     ThisObject = autoUpdateList_getFirstItem (g_listAutoUpdateList);
249     if (ThisObject == NULL && g_listAutoUpdateList!=NULL){
250         TRACEIT (2,"autoUpdateList_deleteList       NoFirstItem?");
251         ThisObject = g_listAutoUpdateList;
252     }
253 
254 	while (ThisObject){
255 	    ObjectBuf = ThisObject->NextItem;
256 
257         //delete ThisObject from global list as our updatethread will try to validate its object, therefor
258         //we have to make sure that the global variable does not point to an allready freed memory.
259         //TODO, think about this again, its very late now.
260 	    if (ThisObject->PrevItem != NULL){
261             ThisObject->PrevItem->NextItem = ThisObject->NextItem;
262         }
263         else{
264             g_listAutoUpdateList = ThisObject->NextItem;
265             if (g_listAutoUpdateList != NULL){
266                 g_listAutoUpdateList->PrevItem = NULL;
267             }
268         }
269         if (ThisObject->NextItem != NULL){
270             ThisObject->NextItem->PrevItem = ThisObject->PrevItem;
271         }
272 
273 		autoUpdateList_deleteItemMembersAndItem (ThisObject);
274         ThisObject = ObjectBuf;
275 	}
276 	g_listAutoUpdateList = NULL;
277     //printf("monitor: LIST deleted\n");
278     TRACEIT(10,"autoUpdateList_deleteList		end");
279 }
280 
281 
autoUpdateList_RemoveMonitor(char * Path)282 int autoUpdateList_RemoveMonitor (char *Path){
283     TRACEIT(10,"autoUpdateList_RemoveMonitor		start");
284 
285     if (g_listAutoUpdateList == NULL){
286         TRACEIT(9,"autoUpdateList_RemoveMonitor		end, No Monitor at all");
287         return 0;
288     }
289 
290     if (Path == NULL){
291         TRACEIT(2,"autoUpdateList_RemoveMonitor		end, Parameter NULL");
292         return -1;
293     }
294     //printf("monitor: deleting %s\n",Path);
295     //search list for path,
296     struct autoUpdateList *buflist = autoUpdateList_getFirstItem (g_listAutoUpdateList);
297     while (buflist){
298         if (strcmp(Path, buflist->gstrPath->str) == 0){
299             buflist->iCountRef--;
300             //printf("monitor: countRef decreased\n");
301             break;
302         }
303         buflist = buflist->NextItem;
304     }
305     if (buflist == NULL){
306         TRACEIT(4,"autoUpdateList_RemoveMonitor		end, path not found, not monitored");
307         return -1;
308     }
309 
310     //printf("autoUpdateList_RemoveMonitor: buflist->iCountRef= %i\n",buflist->iCountRef);
311 
312     if (buflist->iCountRef == 0){
313         //printf("monitor: deleting monitor\n");
314         autoUpdateList_DeleteObject(buflist);
315     }
316 
317     TRACEIT(10,"autoUpdateList_RemoveMonitor		end");
318     return 0;
319 }
320 
321 
autoUpdateList_AddMonitor(char * Path)322 int autoUpdateList_AddMonitor (char *Path){
323     TRACEIT(10,"autoUpdateList_AddMonitor		start");
324     if (Path == NULL){
325         TRACEIT(2,"autoUpdateList_AddMonitor		end, Parameter NULL");
326         return -1;
327     }
328     //printf("monitor: adding %s\n",Path);
329 
330     struct autoUpdateList *buf2list=NULL;
331     //search list for path, if we allready monitor it we increase the reference count
332     struct autoUpdateList *buflist = g_listAutoUpdateList;
333 
334     while (buflist){
335         if (strcmp(Path, buflist->gstrPath->str) == 0){
336             buflist->iCountRef++;
337             //printf("monitor: CountRef increased\n");
338             TRACEIT(10,"autoUpdateList_AddMonitor		end");
339             return 0;
340         }
341         buflist = buflist->NextItem;
342     }
343 
344     //if we dont find the path, start monitoring it
345     if (buflist == NULL){
346        // printf("monitor: new monitor\n");
347         buflist = malloc( sizeof(struct autoUpdateList) );
348 
349         //printf ("autoUpdateList_AddMonitor: added item: %p, path: %s\n",buflist, Path);
350 
351         if (buflist){
352             buflist->iUpdateThreadStarted = 0;
353             buflist->gthreadUpdateThread = NULL;
354             buflist->mtxJoinUpdateThread = g_mutex_new();
355 
356             buflist->gfilePath = g_file_new_for_path (Path);
357             buflist->monitor = g_file_monitor_directory (buflist->gfilePath,G_FILE_MONITOR_NONE,NULL,NULL);
358             if (buflist->monitor == NULL){
359                 g_object_unref(buflist->gfilePath);
360                 free(buflist);
361                 TRACEIT(2,"autoUpdateList_AddMonitor		end, cant get monitor");
362                 return -1;
363             }
364             g_file_monitor_set_rate_limit (buflist->monitor, MONITORRATELIMIT); //default value is 800, but this does not mean, that
365             //the monitor skipps any messages but simply waits for X ms before calling the callback function.
366             //In a matter of fact, there are several messages for each change fired. This leads to a porblem in
367             //our case, and the only solution so far is to set the rate limit to very low value, where the define for
368             // our AUTOUPDATE_MINTIME is much bigger than this.
369 
370             buflist->iCountRef=1;
371             buflist->gstrPath = g_string_new(Path);
372             g_signal_connect(buflist->monitor,"changed",G_CALLBACK(autoUpdateList_monitoredDirChanged),buflist);
373 
374             //add new item to the list
375             buflist->NextItem = NULL;
376             buflist->PrevItem = NULL;
377             if (NULL == g_listAutoUpdateList){
378                 g_listAutoUpdateList = buflist;
379             }
380             else{
381                 //find the last item
382                 for (buf2list = g_listAutoUpdateList; buf2list->NextItem; buf2list=buf2list->NextItem );
383                 //printf("buf2list=%p\tbuf2list->NextItem=%p",buf2list,buf2list->NextItem);
384 
385                 buf2list->NextItem = buflist;
386                 buflist->PrevItem = buf2list;
387             }
388         }
389         else{
390             TRACEIT(2,"autoUpdateList_AddMonitor		end, malloc failed");
391             return -1;
392         }
393     }
394 
395     //printf("monitor: added\n");
396     TRACEIT(10,"autoUpdateList_AddMonitor		end");
397     return 0;
398 }
399 
400 
autoUpdateList_ValidateItem(struct autoUpdateList * item,GFileMonitor * monitor)401 int autoUpdateList_ValidateItem (struct autoUpdateList *item, GFileMonitor *monitor){
402     //call this function to validate a given item.
403     //monitor is used as an additional indicator as the pointer adress may exist (after it was deleted and newly allocated)
404     //but then, the monitor pointer is hopefully different.
405     //returns -1=error, 0=invalid, 1=valid
406     //monitor can be NULL
407     TRACEIT(11,"autoUpdateList_ValidateItem		start");
408     if (item == NULL){
409         TRACEIT (2, "autoUpdateList_ValidateItem        parameter NULL");
410         return -1;
411     }
412     int iRet = 0;
413 
414     struct autoUpdateList *buflist = autoUpdateList_getFirstItem (g_listAutoUpdateList);
415 
416     while (buflist){
417         if (buflist == item){
418             if (monitor != NULL){
419                 if (monitor == buflist->monitor){
420                     iRet = 1;
421                 }
422             }
423             else{
424                 iRet = 1;
425             }
426         }
427         buflist = buflist->NextItem;
428     }
429 
430     TRACEIT(11,"autoUpdateList_ValidateItem		end");
431     return iRet;
432 }
433 
434 
autoUpdateList_getFirstItem(struct autoUpdateList * theList)435 struct autoUpdateList *autoUpdateList_getFirstItem (struct autoUpdateList *theList){
436     TRACEIT(11,"autoUpdateList_getFirstItem		start");
437     if (theList == NULL){
438         TRACEIT (2, "autoUpdateList_getFirstItem        parameter NULL");
439         return NULL;
440     }
441     while (theList->PrevItem != NULL){
442         theList = theList->PrevItem;
443     }
444 
445     TRACEIT(11,"autoUpdateList_getFirstItem		end");
446     return theList;
447 }
448 
449 
autoUpdateList_debugOut()450 void autoUpdateList_debugOut (){
451     struct autoUpdateList *theList = autoUpdateList_getFirstItem (g_listAutoUpdateList);
452     int i=0;
453 
454     printf("Debug Out Start\n");
455     while (theList){
456         printf("autoUpdateList [%d]: %p\n",i++, theList);
457 
458         theList = theList->NextItem;
459     }
460     printf("Debug Out End\n");
461 
462 }
463 
464 
465 #endif
466 
467 
468 
469 
470 
471 
472 
473 
474 
475 
476 
477 
478 
479