nautilus-monitor.c 8.9 KB
Newer Older
1 2 3 4
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-

   nautilus-monitor.c: file and directory change monitoring for nautilus
 
Darin Adler's avatar
Darin Adler committed
5
   Copyright (C) 2000, 2001 Eazel, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
  
   You should have received a copy of the GNU General Public
   License along with this program; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
  
Darin Adler's avatar
Darin Adler committed
22 23
   Authors: Seth Nickell <seth@eazel.com>
            Darin Adler <darin@eazel.com>
24 25
*/

Darin Adler's avatar
Darin Adler committed
26
#include <config.h>
27
#include "nautilus-monitor.h"
28 29

#include <eel/eel-glib-extensions.h>
30

Alexander Larsson's avatar
Alexander Larsson committed
31
#ifdef HAVE_LIBFAM
32

Darin Adler's avatar
Darin Adler committed
33
#include "nautilus-file-changes-queue.h"
34
#include "nautilus-volume-monitor.h"
Darin Adler's avatar
Darin Adler committed
35 36
#include <fam.h>
#include <gdk/gdk.h>
37
#include <gmodule.h>
Darin Adler's avatar
Darin Adler committed
38 39 40
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-util.h>
#include <libgnomevfs/gnome-vfs-utils.h>
41

Darin Adler's avatar
Darin Adler committed
42 43 44
struct NautilusMonitor {
	FAMRequest request;
};
45

Darin Adler's avatar
Darin Adler committed
46
static gboolean got_connection;
47

48 49 50
static gboolean process_fam_notifications (GIOChannel *channel,
					   GIOCondition cond,
					   gpointer callback_data);
51 52 53

/* singleton object, instantiate and connect if it doesn't already exist */
static FAMConnection *
Darin Adler's avatar
Darin Adler committed
54
get_fam_connection (void)
55
{
Darin Adler's avatar
Darin Adler committed
56 57
	static gboolean tried_connection;
	static FAMConnection connection;
58
	GIOChannel *ioc;
Darin Adler's avatar
Darin Adler committed
59 60
	
	/* Only try once. */
61 62 63
        if (tried_connection) {
		if (!got_connection) {
			return NULL;
Darin Adler's avatar
Darin Adler committed
64
		}
65
	} else {
66
                tried_connection = TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
67
		if (FAMOpen2 (&connection, "Nautilus") != 0) {
68 69 70 71 72 73
			return NULL;
		}

		/* Make the main loop's select function watch the FAM
                 * connection's file descriptor for us.
		 */
74 75 76
		ioc = g_io_channel_unix_new (FAMCONNECTION_GETFD (&connection));
		g_io_add_watch (ioc, G_IO_IN | G_IO_HUP, process_fam_notifications, NULL);
		g_io_channel_unref (ioc);
77 78

		got_connection = TRUE;
Darin Adler's avatar
Darin Adler committed
79 80
	}
	return &connection;
81 82
}

Darin Adler's avatar
Darin Adler committed
83 84 85 86
static GHashTable *
get_request_hash_table (void)
{
	static GHashTable *table;
87

Darin Adler's avatar
Darin Adler committed
88 89
	if (table == NULL) {
		table = eel_g_hash_table_new_free_at_exit
90
			(NULL, NULL, "nautilus-monitor.c: FAM requests");
Darin Adler's avatar
Darin Adler committed
91 92
	}
	return table;
93 94 95
}

static char *
Darin Adler's avatar
Darin Adler committed
96
get_event_uri (const FAMEvent *event)
97
{
Darin Adler's avatar
Darin Adler committed
98 99 100
        const char *base_path;
	char *path, *uri;

101
        /* FAM doesn't tell us when something is a full path and when
Darin Adler's avatar
Darin Adler committed
102 103 104 105 106
	 * it's just partial so we have to look and see if it starts
	 * with a /.
	 */
        if (event->filename[0] == '/') {
                return gnome_vfs_get_uri_from_local_path (event->filename);
107
        }
Darin Adler's avatar
Darin Adler committed
108 109 110 111 112

	/* Look up the directory registry that was used for this file
	 * notification and tack that on.
	 */
	base_path = g_hash_table_lookup (get_request_hash_table (),
113
					 GINT_TO_POINTER (FAMREQUEST_GETREQNUM (&event->fr)));
Darin Adler's avatar
Darin Adler committed
114 115 116 117 118
	g_return_val_if_fail (base_path != NULL, NULL);
	path = g_concat_dir_and_file (base_path, event->filename);
	uri = gnome_vfs_get_uri_from_local_path (path);
	g_free (path);
        return uri;
119 120
}

121 122 123 124
static gboolean
process_fam_notifications (GIOChannel *channel,
			   GIOCondition cond,
			   gpointer callback_data)
125
{
Darin Adler's avatar
Darin Adler committed
126 127 128
        FAMConnection *connection;
        FAMEvent event;
	char *uri;
129

Darin Adler's avatar
Darin Adler committed
130
        connection = get_fam_connection ();
131
	g_return_val_if_fail (connection != NULL, FALSE);
132

Darin Adler's avatar
Darin Adler committed
133
        /* Process all the pending events right now. */
134

Alexander Larsson's avatar
Alexander Larsson committed
135 136
        while (FAMPending (connection)) {
                if (FAMNextEvent (connection, &event) != 1) {
137
                        g_warning ("connection to FAM died");
Alexander Larsson's avatar
Alexander Larsson committed
138
                        FAMClose (connection);
Darin Adler's avatar
Darin Adler committed
139
                        got_connection = FALSE;
140
                        return FALSE;
Darin Adler's avatar
Darin Adler committed
141
                }
142

Darin Adler's avatar
Darin Adler committed
143 144 145 146 147 148 149 150
                switch (event.code) {
                case FAMChanged:
			uri = get_event_uri (&event);
			if (uri == NULL) {
				break;
			}
                        nautilus_file_changes_queue_file_changed (uri);
			g_free (uri);
151 152
                        break;

Darin Adler's avatar
Darin Adler committed
153 154 155 156 157 158 159
                case FAMDeleted:
			uri = get_event_uri (&event);
			if (uri == NULL) {
				break;
			}
                        nautilus_file_changes_queue_file_removed (uri);
			g_free (uri);
160 161
                        break;

Darin Adler's avatar
Darin Adler committed
162 163 164 165 166 167 168
                case FAMCreated:                
			uri = get_event_uri (&event);
			if (uri == NULL) {
				break;
			}
                        nautilus_file_changes_queue_file_added (uri);
			g_free (uri);
169
                        break;
Darin Adler's avatar
Darin Adler committed
170

171
                case FAMStartExecuting:
Darin Adler's avatar
Darin Adler committed
172 173 174 175 176
			/* Emitted when a file you are monitoring is
			 * executed. This should work for both
			 * binaries and shell scripts. Nautilus is not
			 * doing anything with this yet.
			 */
177
			break;
Darin Adler's avatar
Darin Adler committed
178

179
                case FAMStopExecuting:
Darin Adler's avatar
Darin Adler committed
180 181 182 183
			/* Emitted when a file you are monitoring
			 * ceases execution. Nautilus is not doing
			 * anything with this yet.
			 */
184
			break;
Darin Adler's avatar
Darin Adler committed
185

186
                case FAMAcknowledge:
Darin Adler's avatar
Darin Adler committed
187 188 189 190
			/* Called in response to a successful
			 * CancelMonitor. We don't need to do anything
			 * with this information.
			 */
191
			break;
Darin Adler's avatar
Darin Adler committed
192

193
                case FAMExists:
Darin Adler's avatar
Darin Adler committed
194 195 196 197 198 199 200
			/* Emitted when you start monitoring a
			 * directory. It tells you what's in the
			 * directory. Unhandled because Nautilus
			 * already handles this by calling
			 * gnome_vfs_directory_load, which gives us
			 * more information than merely the file name.
			 */
201
			break;
Darin Adler's avatar
Darin Adler committed
202

203
                case FAMEndExist:
Darin Adler's avatar
Darin Adler committed
204
			/* Emitted at the end of a FAMExists stream. */
205
			break;
Darin Adler's avatar
Darin Adler committed
206

207
                case FAMMoved:
Darin Adler's avatar
Darin Adler committed
208 209 210 211 212 213 214
			/* FAMMoved doesn't need to be handled because
			 * FAM never seems to generate this event on
			 * Linux systems (w/ or w/o IMON). Instead it
			 * generates a FAMDeleted followed by a
			 * FAMCreated.
			 */
			g_warning ("unexpected FAMMoved notification");
215 216 217
			break;
                }
        }
218 219

	nautilus_file_changes_consume_changes (TRUE);
220 221
	
	return TRUE;
222 223
}

Alexander Larsson's avatar
Alexander Larsson committed
224
#endif /* HAVE_LIBFAM */
225

226 227 228
gboolean
nautilus_monitor_active (void)
{
Alexander Larsson's avatar
Alexander Larsson committed
229
#ifndef HAVE_LIBFAM
230 231 232 233 234 235
	return FALSE;
#else
	return get_fam_connection () != NULL;
#endif
}

236 237 238 239 240 241 242 243 244 245 246
static gboolean
path_is_on_readonly_volume (const char *path)
{
	NautilusVolumeMonitor *volume_monitor;
	NautilusVolume *volume;

	volume_monitor = nautilus_volume_monitor_get ();
	volume = nautilus_volume_monitor_get_volume_for_path (volume_monitor, path);
	return (volume != NULL) && nautilus_volume_is_read_only (volume);
}

Darin Adler's avatar
Darin Adler committed
247 248
NautilusMonitor *
nautilus_monitor_file (const char *uri)
249
{
Alexander Larsson's avatar
Alexander Larsson committed
250
#ifndef HAVE_LIBFAM
Darin Adler's avatar
Darin Adler committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	return NULL;
#else
        FAMConnection *connection;
        char *path;
	NautilusMonitor *monitor;

        connection = get_fam_connection ();
	if (connection == NULL) {
		return NULL;
	}

	path = gnome_vfs_get_local_path_from_uri (uri);
	if (path == NULL) {
		return NULL;
	}
266 267 268 269 270 271 272 273 274 275

	/* Check to see if the file system is readonly. If so, don't monitor --
	 * there is no point, and we'll just keep the file system busy for
	 * no reason, preventing unmounting
	 */
        if (path_is_on_readonly_volume (path)) {
		g_free (path);
		return NULL;
	}
	
Darin Adler's avatar
Darin Adler committed
276
	monitor = g_new0 (NautilusMonitor, 1);
Alexander Larsson's avatar
Alexander Larsson committed
277
	FAMMonitorFile (connection, path, &monitor->request, NULL);
278

Darin Adler's avatar
Darin Adler committed
279
	g_free (path);
280

Darin Adler's avatar
Darin Adler committed
281 282
	return monitor;
#endif
283 284 285 286
}



Darin Adler's avatar
Darin Adler committed
287 288
NautilusMonitor *
nautilus_monitor_directory (const char *uri)
289
{
Alexander Larsson's avatar
Alexander Larsson committed
290
#ifndef HAVE_LIBFAM
Darin Adler's avatar
Darin Adler committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
	return NULL;
#else
        FAMConnection *connection;
        char *path;
	NautilusMonitor *monitor;

        connection = get_fam_connection ();
	if (connection == NULL) {
		return NULL;
	}

	path = gnome_vfs_get_local_path_from_uri (uri);
	if (path == NULL) {
		return NULL;
	}
306
        
307 308 309 310 311 312 313 314 315
	/* Check to see if the file system is readonly. If so, don't monitor --
	 * there is no point, and we'll just keep the file system busy for
	 * no reason, preventing unmounting
	 */
        if (path_is_on_readonly_volume (path)) {
		g_free (path);
		return NULL;
	}
	
Darin Adler's avatar
Darin Adler committed
316
	monitor = g_new0 (NautilusMonitor, 1);
Alexander Larsson's avatar
Alexander Larsson committed
317
	FAMMonitorDirectory (connection, path, &monitor->request, NULL);
318

Darin Adler's avatar
Darin Adler committed
319
	g_assert (g_hash_table_lookup (get_request_hash_table (),
320
				       GINT_TO_POINTER (FAMREQUEST_GETREQNUM (&monitor->request))) == NULL);
321

Darin Adler's avatar
Darin Adler committed
322
	g_hash_table_insert (get_request_hash_table (),
323
			     GINT_TO_POINTER (FAMREQUEST_GETREQNUM (&monitor->request)),
Darin Adler's avatar
Darin Adler committed
324 325 326 327
			     path);

	return monitor;
#endif
328 329 330
}

void
Darin Adler's avatar
Darin Adler committed
331
nautilus_monitor_cancel (NautilusMonitor *monitor)
332
{       
Alexander Larsson's avatar
Alexander Larsson committed
333
#ifndef HAVE_LIBFAM
Darin Adler's avatar
Darin Adler committed
334 335 336 337 338
	g_return_if_fail (monitor == NULL);
#else
        FAMConnection *connection;
	int reqnum;
	char *path;
339

Darin Adler's avatar
Darin Adler committed
340 341 342
	if (monitor == NULL) {
		return;
	}
343

344
	reqnum = FAMREQUEST_GETREQNUM (&monitor->request);
Darin Adler's avatar
Darin Adler committed
345 346 347 348
	path = g_hash_table_lookup (get_request_hash_table (),
				    GINT_TO_POINTER (reqnum));
	g_hash_table_remove (get_request_hash_table (),
			     GINT_TO_POINTER (reqnum));
349
	g_free (path);
350

Darin Adler's avatar
Darin Adler committed
351 352
        connection = get_fam_connection ();
	g_return_if_fail (connection != NULL);
353

Alexander Larsson's avatar
Alexander Larsson committed
354
	FAMCancelMonitor (connection, &monitor->request);
Darin Adler's avatar
Darin Adler committed
355 356
	g_free (monitor);
#endif
357
}