ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / doc / writing-modules.sgml
1 <refentry id="gnome-vfs-writing-modules" revision="6 Sep 2001">
2 <refmeta>
3 <refentrytitle>Writing Modules</refentrytitle>
4 <manvolnum>3</manvolnum>
5 <refmiscinfo>GNOME-VFS Library</refmiscinfo>
6 </refmeta>
7
8 <refnamediv>
9 <refname>Writing Modules</refname><refpurpose>basic gnome-vfs module concepts</refpurpose>
10 </refnamediv>
11
12   <refsect1 id="Introduction">
13     <title>Introduction</title>
14
15     <para>This section will introduce the basic concepts that are
16       needed for writing GNOME Virtual File System modules.</para>
17
18     <refsect2 id="uris">
19       <title>GNOME VFS URIs (Uniform Resource Identifiers)</title>
20
21       <para>The GNOME Virtual file system uses URIs similiar to the
22         standard WWW URIs.  The basic difference between a VFS URI and
23         WWW URI is that, while with WWW URIs you can only use a single
24         protocol for accessing a certain file, with GNOME VFS URIs you
25         can combine different access methods in sequence.</para>
26
27       <para>For example, suppose you want to access file
28         <filename>hello.c</filename> in a <filename>tar.gz</filename>
29         file which is in turn accessible through FTP from a remote
30         machine.  In order to access this file, you would need to:</para>
31
32       <orderedlist>
33         <listitem><para>Connect to the FTP site.</para></listitem>
34         
35         <listitem><para>Fetch the <filename>tar.gz</filename>
36         file.</para></listitem>
37         
38         <listitem><para>Decompress the <filename>tar.gz</filename> file using
39           GZIP.</para></listitem>
40
41         <listitem><para>Extract <filename>hello.c</filename> from the resulting
42           uncompressed <filename>tar</filename> file.</para></listitem>
43       </orderedlist>
44
45       <para>The GNOME Virtual File System lets you express this by
46         combining the three access methods (i.e. tar, GZIP and FTP)
47         into a single URI.  Access methods are combined in the URI by
48         using the `#' character, followed by the name for the access
49         method and the subpath for that specific access method.  The
50         subpath can be omitted for those storage methods that do not
51         need a path to retrieve the file.  (For example, a GZIP file
52         always contains a single uncompressed file, so no path is
53         needed to locate the uncompressed file within the GZIP file.
54         But on the other hand, the TAR method requires a path to
55         locate a specific file or directory.)</para>
56
57       <para>For example, in the case we outlined above, the URI would
58         look something like:</para>
59
60       <!-- FixMe what should I use here instead of programlisting? -->
61       <programlisting>
62
63         ftp://username:password@host.net/path/to/file.tar.gz#gzip#tar/path/to/hello.c</programlisting>
64
65       <para>Each method/subpath couple is called a <firstterm>URI
66       element</firstterm>.  When URI elements are combined like this,
67       each URI element uses the previous one to access a base resource
68       into which it will look up a file, using the subpath
69       information.  For this reason, we will say that each element is
70       the <firstterm>parent</firstterm> element for the following one.</para>
71
72       <para>The first URI element, the one which has no parent, is
73       called the <firstterm>toplevel element</firstterm>.  It does not
74       use the `#' character; instead, it uses the standard syntax of
75       WWW URIs: </para>
76
77       <programlisting>
78
79         method://user:password@host/path/to/file</programlisting>
80
81       <para>This way, normal WWW URIs can be used with the GNOME Virtual
82       File System.</para>
83
84       <para>Toplevel elements are also special because they let users
85         specify user names, passwords and host names, while
86         non-toplevel elements don't.</para>
87     </refsect2>
88
89     <refsect2>
90       <title>The <structname>GnomeVFSURI</structname> type</title>
91
92       <para>Within the GNOME Virtual File System library, URI elements
93       are represented by a special type,
94       <structname>GnomeVFSURI</structname>, which is meant to represent
95       user-provided URIs in a machine-optimized way.  </para>
96
97       <para>Every <structname>GnomeVFSURI</structname> contains the
98       following information:</para>
99
100       <itemizedlist>
101         <listitem><para>A reference counter</para></listitem>
102       
103         <listitem><para>A pointer to the parent
104         <structname>GnomeVFSURI</structname> URI element.</para></listitem>
105       
106         <listitem><para>The subpath.</para></listitem>
107       
108         <listitem><para>The name of the access method.</para></listitem>
109       
110         <listitem><para>A pointer to a
111         <structname>GnomeVFSMethod</structname> object, describing the
112         access method (see below).</para></listitem>
113       </itemizedlist>
114
115     </refsect2>
116
117   </refsect1>
118
119   <refsect1>
120     <title>GNOME Virtual File System access method implementation</title>
121
122     <para>In the GNOME Virtual File System, the implementations for
123     all the access methods are loaded at runtime, as shared library
124     modules.  The modules are loaded during parsing of the string URI:
125     if the parser encounters an access method for which no
126     implementation is currently loaded, it retrieves the corresponding
127     library file, dynamically links it into the executable, and
128     initializes it.</para>
129
130     <para>After initialization, the module returns a special
131     <structname>GnomeVFSMethod</structname> object that contains
132     pointers to the various implementation functions for that specific
133     method.  By storing a pointer to this object into the
134     <structname>GnomeVFSURI</structname> type, the VFS library is then
135     able to use these functions for file access.</para>
136
137     <refsect2>
138       <title>How file access is performed</title>
139
140       <para>When the VFS library needs to perform some file operation,
141       it performs the following steps:</para>
142
143       <itemizedlist>
144
145         <listitem><para>If the URI is given in textual form (i.e. as a
146         string), it parses it and activates the necessary access method
147         modules.</para></listitem>
148
149         <listitem><para>It retrieves a pointer to the lowmost
150         level URI element.</para></listitem>
151
152         <listitem><para>It retrieves a pointer to the
153         <structname>GnomeVFSMethod</structname> object that corresponds
154         to the access method for that URI element.</para></listitem>
155         
156         <listitem><para>It retrieves a pointer to the implementation
157         function for that operation from the
158         <structname>GnomeVFSMethod</structname>object.</para></listitem>
159
160         <listitem><para>It invokes that implementation function
161         passing the pointer to the lowmost level URI
162         element.</para></listitem>
163         
164       </itemizedlist>
165
166       <para>Combining the access methods is always done within the
167       method implementation.  If the method implementation needs to do
168       some file operation on the the parent URI element, it can do so
169       by simply invoking the corresponding VFS function in, by using
170       the parent pointer in the <structname>GnomeVFSURI</structname>
171       object. </para>
172
173       <para>For example, suppose you have to read a simple URI like
174       the following:</para>
175
176       <!-- FixMe what should I use here instead of programlisting? -->
177       <programlisting>
178
179         file:/home/ettore/file.gz#gzip</programlisting>
180
181       <para>In this case, the GZIP method will be invoked with a
182       pointer to the <structname>GnomeVFSURI</structname> describing the
183       `gzip' part; then the GZIP method will be able to read
184       <filename>file.gz</filename> by just invoking the corresponding
185       GNOME VFS library function on its parent, and decompress it on
186       the fly. </para>
187
188     </refsect2>
189
190   </refsect1>
191
192   <refsect1>
193     <title>Implementing an access method in practice</title>
194
195     <para>Implementing a new access method is really not difficult at
196     all.  This section explains how this is done.</para>
197
198     <refsect2>
199       <title>Using shared libraries</title>
200
201       <para>Every module must be compiled as a shared library (i.e. a
202       <filename>.so</filename> file).</para>
203
204       <para>The current way for accessing the right module for the
205       right method is very simple, and is based on file names.  In
206       practice, a module implementing access method named
207       <filename>foo</filename> must be named
208       <filename>libfoo.so</filename>.  For example, the module
209       implementing the <filename>ftp:</filename> access method is
210       called <filename>libftp.so</filename>, the module implementing
211       <filename>#gzip</filename> access is called
212       <filename>libgzip.so</filename> and so on.</para>
213
214       <para>This might change in the future.</para>
215
216     </refsect2>
217
218     <refsect2>
219       <title>The initialization/shutdown functions</title>
220
221       <para>Every shared library module must provide two functions:</para>
222
223       <programlisting role="c">
224
225 GnomeVFSMethod *vfs_module_init (void);
226 void vfs_module_shutdown (GnomeVFSMethod *method);</programlisting>
227
228       <para>These are the only functions that the VFS library will
229       access directly.  All the other symbols (i.e. functions and
230       variables) in the module should be made static. </para>
231
232       <para><function>vfs_module_init()</function> is called
233       as soon as the module is loaded in memory.  It will have to
234       return a pointer to a <structname>GnomeVFSMethod</structname>
235       object that will contain the pointers to the method's
236       implementation functions.  We will describe this later. </para>
237
238       <para><function>vfs_module_shutdown</function>, instead,
239       is called before the module is unloaded or the program that uses
240       it dies.  This functions should:</para>
241
242       <itemizedlist>
243
244         <listitem><para>Deallocate all the memory allocated by the
245         module.</para></listitem>
246
247         <listitem><para>Close all the file descriptors associated with
248         the module.</para></listitem>
249
250         <listitem><para>Kill any external process spawned by the
251         module.</para></listitem>
252
253         <listitem><para>In general, make sure that any operation that
254         was going on before this function was called will be
255         interrupted correctly, as soon as possible and without any
256         leaks.</para></listitem>
257
258       </itemizedlist>
259
260     </refsect2>
261
262     <refsect2>
263       <title>The <structname>GnomeVFSMethod</structname> object</title>
264
265       <para>This object is contains pointers to the module
266       implementation functions.</para>
267
268       <programlisting role="c">
269
270 GnomeVFSResult (* open)              (GnomeVFSMethodHandle **method_handle_return,
271                                       GnomeVFSURI *uri,
272                                       GnomeVFSOpenMode mode
273                                       GnomeVFSCancellation *cancellation);
274
275 GnomeVFSResult (* create)            (GnomeVFSMethodHandle **method_handle_return,
276                                       GnomeVFSURI *uri,
277                                       GnomeVFSOpenMode mode,
278                                       gboolean exclusive,
279                                       guint perm
280                                       GnomeVFSCancellation *cancellation);
281
282 GnomeVFSResult (* close)             (GnomeVFSMethodHandle *method_handle
283                                       GnomeVFSCancellation *cancellation);
284
285 GnomeVFSResult (* read)              (GnomeVFSMethodHandle *method_handle,
286                                       gpointer buffer,
287                                       GnomeVFSFileSize num_bytes,
288                                       GnomeVFSFileSize *bytes_read_return
289                                       GnomeVFSCancellation *cancellation);
290
291 GnomeVFSResult (* write)             (GnomeVFSMethodHandle *method_handle,
292                                       gconstpointer buffer,
293                                       GnomeVFSFileSize num_bytes,
294                                       GnomeVFSFileSize *bytes_written_return
295                                       GnomeVFSCancellation *cancellation);
296
297 GnomeVFSResult (* seek)              (GnomeVFSMethodHandle *method_handle,
298                                       GnomeVFSSeekPosition  whence,
299                                       GnomeVFSFileOffset    offset
300                                       GnomeVFSCancellation *cancellation);
301
302 GnomeVFSResult (* tell)              (GnomeVFSMethodHandle *method_handle,
303                                       GnomeVFSFileOffset *offset_return);
304
305 GnomeVFSResult (* truncate)          (GnomeVFSMethodHandle *method_handle,
306                                       GnomeVFSFileSize where
307                                       GnomeVFSCancellation *cancellation);
308
309 GnomeVFSResult (* open_directory)    (GnomeVFSMethodHandle **method_handle,
310                                       GnomeVFSURI *uri,
311                                       GnomeVFSFileInfoOptions options,
312                                       const GList *meta_keys,
313                                       const GnomeVFSDirectoryFilter *filter
314                                       GnomeVFSCancellation *cancellation);
315
316 GnomeVFSResult (* close_directory)   (GnomeVFSMethodHandle *method_handle
317                                       GnomeVFSCancellation *cancellation);
318
319 GnomeVFSResult (* read_directory)    (GnomeVFSMethodHandle *method_handle,
320                                       GnomeVFSFileInfo *file_info
321                                       GnomeVFSCancellation *cancellation);
322
323 GnomeVFSResult (* get_file_info)     (GnomeVFSURI *uri,
324                                       GnomeVFSFileInfo *file_info,
325                                       GnomeVFSFileInfoOptions options,
326                                       const GList *meta_keys
327                                       GnomeVFSCancellation *cancellation);
328
329 GnomeVFSResult (* get_file_info_from_handle)
330                                      (GnomeVFSMethodHandle *method_handle,
331                                       GnomeVFSFileInfo *file_info,
332                                       GnomeVFSFileInfoOptions options,
333                                       const GList *meta_keys
334                                       GnomeVFSCancellation *cancellation);
335
336 gboolean       (* is_local)          (const GnomeVFSURI *uri
337                                       GnomeVFSCancellation *cancellation);
338
339 GnomeVFSResult (* rename)            (GnomeVFSURI *uri, const gchar *new_name
340                                       GnomeVFSCancellation *cancellation);
341
342 GnomeVFSResult (* make_directory)    (GnomeVFSURI *uri, guint perm
343                                       GnomeVFSCancellation *cancellation);
344
345 GnomeVFSResult (* remove_directory)  (GnomeVFSURI *uri
346                                       GnomeVFSCancellation *cancellation);
347
348 GnomeVFSResult (* unlink)            (GnomeVFSURI *uri
349                                       GnomeVFSCancellation *cancellation);
350 </programlisting>
351
352     </refsect2>
353
354   </refsect1>
355
356   <refsect1>
357     <title>Handling cancellation</title>
358
359     <para>As VFS operations might be very long, especially in the case
360     of transient errors (such as a network server that has gone down),
361     the GNOME Virtual File System Library provides a standard way for
362     handling cancellation of VFS operations.</para>
363
364     <refsect2>
365       <title>The <structname>GnomeVFSCancellation</structname> object</title>
366     
367       <para>The object that encapsulates this functionality is
368       <structname>GnomeVFSCancellation</structname>.  Most
369       implementation functions get a pointer to such an object, and are
370       expected to use this object to recognize when an operation should
371       be interrupted.</para>
372   
373       <para>The most simple way to check for a cancellation request is
374       to poll the object with
375       <function>gnome_vfs_cancellation_check()</function>:</para>
376   
377       <programlisting role="c">
378   
379 gboolean gnome_vfs_cancellation_check (GnomeVFSCancellation *cancellation);</programlisting>
380   
381       <para>This function will return a nonzero value if the current
382       operation should be cancelled.</para>
383   
384       <para>Notice that cancellation is an asynchronous operation that
385       might happen outside your function, in parallel with the code that
386       you are writing.  For example, in the case of threads, the request
387       will be set in the master thread; in the case of slave
388       CORBA-driven processes, the request will be activated by a Unix
389       signal.  So you can expect a cancellation request to happen (and
390       consequently be signalled in
391       <structname>GnomeVFSCancellation</structname>) at any time.</para>
392
393       <para>For this reason, you should be calling this function
394       periodically, whenever you are going to perform several
395       iterations of the same task, or execute a single expensive task.
396       When the function returns a nonzero value, the correct way to
397       react is:</para>
398
399       <orderedlist>
400         <listitem><para>Clean things up so that the result of the
401         operations that have been performed are all
402         cancelled.</para></listitem>
403         <listitem><para>Return the
404         <symbol>GNOME_VFS_ERROR_CANCELLED</symbol> error
405         code.</para></listitem>
406       </orderedlist>
407
408       <para>But there are some other situations in which you want to
409       be able to interrupt a I/O operation when a cancellation request
410       is performed.  In such cases, polling is not a viable option.</para>
411
412       <para>For this reason,
413       <structname>GnomeVFSCancellation</structname> provides an
414       alternative way of sending notifications, using a file
415       descriptor.  To use this feature, you should use the following
416       function:</para>
417
418       <programlisting>
419
420 gint gnome_vfs_cancellation_get_fd (GnomeVFSCancellation *cancellation); </programlisting>
421
422       <para>When this function is called, it will return an open file
423       descriptor, which is the read-side of a pipe.  The pipe will be
424       given a character from the write side as soon as a cancellation
425       request is sent.  For this reason, you can check for a
426       cancellation by using the <function>select()</function> system
427       call: as soon as <function>select</function> reports that some
428       data is available on the file descriptor, you know that a
429       cancellation is being requested.</para>
430
431       <para>For example, if you are reading from a file descriptor and
432       you want to check for a pending cancellation at the same time,
433       you can set up <function>select</function>for checking if data
434       is available on both the cancellation file descriptor and the
435       file descriptor you are reading from.</para>
436     </refsect2>
437
438     <refsect2>
439       <title>Dealing with <symbol>EINTR</symbol></title>
440
441       <para>In order to maximize the chance of cancelling an operation
442       immediately, the GNOME Virtual File System can sends a signal to
443       the asynchronous thread or process.  This does not happen on all
444       the systems and setups, though.</para>
445
446       <para>The result of this is that, if a process is in the middle
447       of a Unix system call while receiving this signal, the system
448       call might be interrupted and return a <symbol>EINTR</symbol>
449       error.</para>
450
451       <para>For this reason, when you receive <symbol>EINTR</symbol>
452       you should check if a cancellation request is pending, using
453       <function>gnome_vfs_cancellation_check()</function> on the
454       <structname>GnomeVFSCancellation</structname> object that the
455       implementation function received:</para>
456
457       <itemizedlist>
458         <listitem><para>If a cancellation is indeed pending
459         (<function>gnome_vfs_cancellation_check()</function> returns a
460         nonzero value), you should cancel the operation, cleaning up
461         all the effects, and return
462         <symbol>GNOME_VFS_ERROR_INTERRUPTED</symbol> or
463         <symbol>GNOME_VFS_ERROR_CANCELLED</symbol></para></listitem>
464
465         <listitem><para>Otherwise, retry the system call as you would
466         normally do.</para></listitem>
467       </itemizedlist>
468     </refsect2>
469
470   </refsect1>
471
472   <refsect1>
473     <title>Basic guidelines for writing a module</title>
474
475     <para>Writing GNOME VFS modules is easy, but there are a few
476     things that you must keep in mind when hacking them:</para>
477
478     <itemizedlist>
479       <listitem><para>All of the code must be completely thread safe.
480       The reason for this is that the asynchronous GNOME VFS engine
481       will use threads when available; if you don't make sure that the
482       code is thread-safe, every kind of weird and unexpected errors
483       will happen.  As debugging these problems can be very hard, it's
484       important to write the code with threads in mind right from the
485       start.</para></listitem>
486
487       <listitem><para>Use the special
488       <function>gnome_vfs_*_cancellable()</function> VFS functions
489       instead of the standard non-cancellable ones, passing them the
490       same <structname>GnomeVFSCancellation</structname> object you
491       are given, so that the operation can always be interrrupted at
492       any time.</para></listitem>
493
494       <listitem><para>The code should respect the basic GNOME
495       guidelines for source code indentation and
496       style.</para></listitem>
497     </itemizedlist>
498
499     <refsect2>
500       <title>How to make the code thread safe</title>
501
502       <para>Although it might sound scary at first, making the code
503       for the modules thread safe is not complicated at all.</para>
504
505       <para>First of all, make sure the amount of global variables is
506       kept to the bare minimum.  If possible, you should avoid them at
507       all cost.</para>
508
509       <para>For those cases where globals are inevitable (such as
510       caches, connection pools or things like that), you have to make
511       sure every variable is properly associated with a mutex, and
512       that the mutex is locked before every access to this variable
513       and released afterwards.  You can also use
514       <function>G_LOCK_DEFINE_STATIC</function>,
515       <function>G_LOCK</function> and <function>G_UNLOCK</function>
516       for this.
517       </para>
518
519       <para>Generally speaking, if you are going to dynamically
520       allocate structures that are shared by more than one
521       operation/file, you should provide all of them with their nice
522       mutex locks.</para>
523
524       <para>Finally, make sure mutexes are used only if they are
525       available.  One way to do so is to use macros like the
526       following:</para>
527
528       <programlisting>
529
530 #ifdef G_THREADS_ENABLED
531 #define MUTEX_NEW()     g_mutex_new ()
532 #define MUTEX_FREE(a)   g_mutex_free (a)
533 #define MUTEX_LOCK(a)   if ((a) != NULL) g_mutex_lock (a)
534 #define MUTEX_UNLOCK(a) if ((a) != NULL) g_mutex_unlock (a)
535 #else
536 #define MUTEX_NEW()     NULL
537 #define MUTEX_FREE(a)
538 #define MUTEX_LOCK(a)
539 #define MUTEX_UNLOCK(a)
540 #endif</programlisting>
541
542       <para><function>G_LOCK_DEFINE_STATIC</function>,
543       <function>G_LOCK</function> and <function>G_UNLOCK</function> in
544       GLib are always safe to use, as they are already defined to be
545       nothing when thread support is not available.</para>
546
547       <para>(Probably it would be a good idea to have something in the
548       private GNOME VFS API that does this stuff for all the
549       modules.)</para>
550
551     </refsect2>
552   </refsect1>
553
554 </refentry>