ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / doc / html / gnome-vfs-writing-modules.html
1 <html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Writing Modules</title><meta name="generator" content="DocBook XSL Stylesheets V1.61.0"><meta name="generator" content="GTK-Doc V1.1 (XML mode)"><style type="text/css">
2         .synopsis, .classsynopsis {
3             background: #eeeeee;
4             border: solid 1px #aaaaaa;
5             padding: 0.5em;
6         }
7         .programlisting {
8             background: #eeeeff;
9             border: solid 1px #aaaaff;
10             padding: 0.5em;
11         }
12         .variablelist {
13             padding: 4px;
14             margin-left: 3em;
15         }
16         .navigation {
17             background: #ffeeee;
18             border: solid 1px #ffaaaa;
19             margin-top: 0.5em;
20             margin-bottom: 0.5em;
21         }
22         .navigation a {
23             color: #770000;
24         }
25         .navigation a:visited {
26             color: #550000;
27         }
28         .navigation .title {
29             font-size: 200%;
30         }
31       </style><link rel="home" href="index.html" title="GnomeVFS - Filesystem Abstraction library"><link rel="up" href="modules.html" title="Filesystem Modules"><link rel="previous" href="modules.html" title="Filesystem Modules"><link rel="next" href="gnome-vfs-20-gnome-vfs-mime.html" title="MIME typing"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><table class="navigation" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2"><tr valign="middle"><td><a accesskey="p" href="modules.html"><img src="left.png" width="24" height="24" border="0" alt="Prev"></a></td><td><a accesskey="u" href="modules.html"><img src="up.png" width="24" height="24" border="0" alt="Up"></a></td><td><a accesskey="h" href="index.html"><img src="home.png" width="24" height="24" border="0" alt="Home"></a></td><th width="100%" align="center">GnomeVFS - Filesystem Abstraction library</th><td><a accesskey="n" href="gnome-vfs-20-gnome-vfs-mime.html"><img src="right.png" width="24" height="24" border="0" alt="Next"></a></td></tr></table><div class="refentry" lang="en"><a name="gnome-vfs-writing-modules"></a><div class="titlepage"><div></div><div></div></div><div class="refnamediv"><h2><span class="refentrytitle">Writing Modules</span></h2><p>Writing Modules &#8212; basic gnome-vfs module concepts</p></div><div class="refsect1" lang="en"><a name="Introduction"></a><h2>Introduction</h2><p>This section will introduce the basic concepts that are
32       needed for writing GNOME Virtual File System modules.</p><div class="refsect2" lang="en"><a name="uris"></a><h3>GNOME VFS URIs (Uniform Resource Identifiers)</h3><p>The GNOME Virtual file system uses URIs similiar to the
33         standard WWW URIs.  The basic difference between a VFS URI and
34         WWW URI is that, while with WWW URIs you can only use a single
35         protocol for accessing a certain file, with GNOME VFS URIs you
36         can combine different access methods in sequence.</p><p>For example, suppose you want to access file
37         <tt class="filename">hello.c</tt> in a <tt class="filename">tar.gz</tt>
38         file which is in turn accessible through FTP from a remote
39         machine.  In order to access this file, you would need to:</p><div class="orderedlist"><ol type="1"><li><p>Connect to the FTP site.</p></li><li><p>Fetch the <tt class="filename">tar.gz</tt>
40         file.</p></li><li><p>Decompress the <tt class="filename">tar.gz</tt> file using
41           GZIP.</p></li><li><p>Extract <tt class="filename">hello.c</tt> from the resulting
42           uncompressed <tt class="filename">tar</tt> file.</p></li></ol></div><p>The GNOME Virtual File System lets you express this by
43         combining the three access methods (i.e. tar, GZIP and FTP)
44         into a single URI.  Access methods are combined in the URI by
45         using the `#' character, followed by the name for the access
46         method and the subpath for that specific access method.  The
47         subpath can be omitted for those storage methods that do not
48         need a path to retrieve the file.  (For example, a GZIP file
49         always contains a single uncompressed file, so no path is
50         needed to locate the uncompressed file within the GZIP file.
51         But on the other hand, the TAR method requires a path to
52         locate a specific file or directory.)</p><p>For example, in the case we outlined above, the URI would
53         look something like:</p><pre class="programlisting">
54
55         ftp://username:password@host.net/path/to/file.tar.gz#gzip#tar/path/to/hello.c</pre><p>Each method/subpath couple is called a <i class="firstterm">URI
56       element</i>.  When URI elements are combined like this,
57       each URI element uses the previous one to access a base resource
58       into which it will look up a file, using the subpath
59       information.  For this reason, we will say that each element is
60       the <i class="firstterm">parent</i> element for the following one.</p><p>The first URI element, the one which has no parent, is
61       called the <i class="firstterm">toplevel element</i>.  It does not
62       use the `#' character; instead, it uses the standard syntax of
63       WWW URIs: </p><pre class="programlisting">
64
65         method://user:password@host/path/to/file</pre><p>This way, normal WWW URIs can be used with the GNOME Virtual
66       File System.</p><p>Toplevel elements are also special because they let users
67         specify user names, passwords and host names, while
68         non-toplevel elements don't.</p></div><hr><div class="refsect2" lang="en"><h3>The <span class="structname">GnomeVFSURI</span> type</h3><p>Within the GNOME Virtual File System library, URI elements
69       are represented by a special type,
70       <span class="structname">GnomeVFSURI</span>, which is meant to represent
71       user-provided URIs in a machine-optimized way.  </p><p>Every <span class="structname">GnomeVFSURI</span> contains the
72       following information:</p><div class="itemizedlist"><ul type="disc"><li><p>A reference counter</p></li><li><p>A pointer to the parent
73         <span class="structname">GnomeVFSURI</span> URI element.</p></li><li><p>The subpath.</p></li><li><p>The name of the access method.</p></li><li><p>A pointer to a
74         <span class="structname">GnomeVFSMethod</span> object, describing the
75         access method (see below).</p></li></ul></div></div></div><div class="refsect1" lang="en"><h2>GNOME Virtual File System access method implementation</h2><p>In the GNOME Virtual File System, the implementations for
76     all the access methods are loaded at runtime, as shared library
77     modules.  The modules are loaded during parsing of the string URI:
78     if the parser encounters an access method for which no
79     implementation is currently loaded, it retrieves the corresponding
80     library file, dynamically links it into the executable, and
81     initializes it.</p><p>After initialization, the module returns a special
82     <span class="structname">GnomeVFSMethod</span> object that contains
83     pointers to the various implementation functions for that specific
84     method.  By storing a pointer to this object into the
85     <span class="structname">GnomeVFSURI</span> type, the VFS library is then
86     able to use these functions for file access.</p><div class="refsect2" lang="en"><h3>How file access is performed</h3><p>When the VFS library needs to perform some file operation,
87       it performs the following steps:</p><div class="itemizedlist"><ul type="disc"><li><p>If the URI is given in textual form (i.e. as a
88         string), it parses it and activates the necessary access method
89         modules.</p></li><li><p>It retrieves a pointer to the lowmost
90         level URI element.</p></li><li><p>It retrieves a pointer to the
91         <span class="structname">GnomeVFSMethod</span> object that corresponds
92         to the access method for that URI element.</p></li><li><p>It retrieves a pointer to the implementation
93         function for that operation from the
94         <span class="structname">GnomeVFSMethod</span>object.</p></li><li><p>It invokes that implementation function
95         passing the pointer to the lowmost level URI
96         element.</p></li></ul></div><p>Combining the access methods is always done within the
97       method implementation.  If the method implementation needs to do
98       some file operation on the the parent URI element, it can do so
99       by simply invoking the corresponding VFS function in, by using
100       the parent pointer in the <span class="structname">GnomeVFSURI</span>
101       object. </p><p>For example, suppose you have to read a simple URI like
102       the following:</p><pre class="programlisting">
103
104         file:/home/ettore/file.gz#gzip</pre><p>In this case, the GZIP method will be invoked with a
105       pointer to the <span class="structname">GnomeVFSURI</span> describing the
106       `gzip' part; then the GZIP method will be able to read
107       <tt class="filename">file.gz</tt> by just invoking the corresponding
108       GNOME VFS library function on its parent, and decompress it on
109       the fly. </p></div></div><div class="refsect1" lang="en"><h2>Implementing an access method in practice</h2><p>Implementing a new access method is really not difficult at
110     all.  This section explains how this is done.</p><div class="refsect2" lang="en"><h3>Using shared libraries</h3><p>Every module must be compiled as a shared library (i.e. a
111       <tt class="filename">.so</tt> file).</p><p>The current way for accessing the right module for the
112       right method is very simple, and is based on file names.  In
113       practice, a module implementing access method named
114       <tt class="filename">foo</tt> must be named
115       <tt class="filename">libfoo.so</tt>.  For example, the module
116       implementing the <tt class="filename">ftp:</tt> access method is
117       called <tt class="filename">libftp.so</tt>, the module implementing
118       <tt class="filename">#gzip</tt> access is called
119       <tt class="filename">libgzip.so</tt> and so on.</p><p>This might change in the future.</p></div><hr><div class="refsect2" lang="en"><h3>The initialization/shutdown functions</h3><p>Every shared library module must provide two functions:</p><pre class="programlisting">
120
121 GnomeVFSMethod *vfs_module_init (void);
122 void vfs_module_shutdown (GnomeVFSMethod *method);</pre><p>These are the only functions that the VFS library will
123       access directly.  All the other symbols (i.e. functions and
124       variables) in the module should be made static. </p><p><tt class="function">vfs_module_init()</tt> is called
125       as soon as the module is loaded in memory.  It will have to
126       return a pointer to a <span class="structname">GnomeVFSMethod</span>
127       object that will contain the pointers to the method's
128       implementation functions.  We will describe this later. </p><p><tt class="function">vfs_module_shutdown</tt>, instead,
129       is called before the module is unloaded or the program that uses
130       it dies.  This functions should:</p><div class="itemizedlist"><ul type="disc"><li><p>Deallocate all the memory allocated by the
131         module.</p></li><li><p>Close all the file descriptors associated with
132         the module.</p></li><li><p>Kill any external process spawned by the
133         module.</p></li><li><p>In general, make sure that any operation that
134         was going on before this function was called will be
135         interrupted correctly, as soon as possible and without any
136         leaks.</p></li></ul></div></div><hr><div class="refsect2" lang="en"><h3>The <span class="structname">GnomeVFSMethod</span> object</h3><p>This object is contains pointers to the module
137       implementation functions.</p><pre class="programlisting">
138
139 GnomeVFSResult (* open)              (GnomeVFSMethodHandle **method_handle_return,
140                                       GnomeVFSURI *uri,
141                                       GnomeVFSOpenMode mode
142                                       GnomeVFSCancellation *cancellation);
143
144 GnomeVFSResult (* create)            (GnomeVFSMethodHandle **method_handle_return,
145                                       GnomeVFSURI *uri,
146                                       GnomeVFSOpenMode mode,
147                                       gboolean exclusive,
148                                       guint perm
149                                       GnomeVFSCancellation *cancellation);
150
151 GnomeVFSResult (* close)             (GnomeVFSMethodHandle *method_handle
152                                       GnomeVFSCancellation *cancellation);
153
154 GnomeVFSResult (* read)              (GnomeVFSMethodHandle *method_handle,
155                                       gpointer buffer,
156                                       GnomeVFSFileSize num_bytes,
157                                       GnomeVFSFileSize *bytes_read_return
158                                       GnomeVFSCancellation *cancellation);
159
160 GnomeVFSResult (* write)             (GnomeVFSMethodHandle *method_handle,
161                                       gconstpointer buffer,
162                                       GnomeVFSFileSize num_bytes,
163                                       GnomeVFSFileSize *bytes_written_return
164                                       GnomeVFSCancellation *cancellation);
165
166 GnomeVFSResult (* seek)              (GnomeVFSMethodHandle *method_handle,
167                                       GnomeVFSSeekPosition  whence,
168                                       GnomeVFSFileOffset    offset
169                                       GnomeVFSCancellation *cancellation);
170
171 GnomeVFSResult (* tell)              (GnomeVFSMethodHandle *method_handle,
172                                       GnomeVFSFileOffset *offset_return);
173
174 GnomeVFSResult (* truncate)          (GnomeVFSMethodHandle *method_handle,
175                                       GnomeVFSFileSize where
176                                       GnomeVFSCancellation *cancellation);
177
178 GnomeVFSResult (* open_directory)    (GnomeVFSMethodHandle **method_handle,
179                                       GnomeVFSURI *uri,
180                                       GnomeVFSFileInfoOptions options,
181                                       const GList *meta_keys,
182                                       const GnomeVFSDirectoryFilter *filter
183                                       GnomeVFSCancellation *cancellation);
184
185 GnomeVFSResult (* close_directory)   (GnomeVFSMethodHandle *method_handle
186                                       GnomeVFSCancellation *cancellation);
187
188 GnomeVFSResult (* read_directory)    (GnomeVFSMethodHandle *method_handle,
189                                       GnomeVFSFileInfo *file_info
190                                       GnomeVFSCancellation *cancellation);
191
192 GnomeVFSResult (* get_file_info)     (GnomeVFSURI *uri,
193                                       GnomeVFSFileInfo *file_info,
194                                       GnomeVFSFileInfoOptions options,
195                                       const GList *meta_keys
196                                       GnomeVFSCancellation *cancellation);
197
198 GnomeVFSResult (* get_file_info_from_handle)
199                                      (GnomeVFSMethodHandle *method_handle,
200                                       GnomeVFSFileInfo *file_info,
201                                       GnomeVFSFileInfoOptions options,
202                                       const GList *meta_keys
203                                       GnomeVFSCancellation *cancellation);
204
205 gboolean       (* is_local)          (const GnomeVFSURI *uri
206                                       GnomeVFSCancellation *cancellation);
207
208 GnomeVFSResult (* rename)            (GnomeVFSURI *uri, const gchar *new_name
209                                       GnomeVFSCancellation *cancellation);
210
211 GnomeVFSResult (* make_directory)    (GnomeVFSURI *uri, guint perm
212                                       GnomeVFSCancellation *cancellation);
213
214 GnomeVFSResult (* remove_directory)  (GnomeVFSURI *uri
215                                       GnomeVFSCancellation *cancellation);
216
217 GnomeVFSResult (* unlink)            (GnomeVFSURI *uri
218                                       GnomeVFSCancellation *cancellation);
219 </pre></div></div><div class="refsect1" lang="en"><h2>Handling cancellation</h2><p>As VFS operations might be very long, especially in the case
220     of transient errors (such as a network server that has gone down),
221     the GNOME Virtual File System Library provides a standard way for
222     handling cancellation of VFS operations.</p><div class="refsect2" lang="en"><h3>The <span class="structname">GnomeVFSCancellation</span> object</h3><p>The object that encapsulates this functionality is
223       <span class="structname">GnomeVFSCancellation</span>.  Most
224       implementation functions get a pointer to such an object, and are
225       expected to use this object to recognize when an operation should
226       be interrupted.</p><p>The most simple way to check for a cancellation request is
227       to poll the object with
228       <tt class="function">gnome_vfs_cancellation_check()</tt>:</p><pre class="programlisting">
229   
230 gboolean gnome_vfs_cancellation_check (GnomeVFSCancellation *cancellation);</pre><p>This function will return a nonzero value if the current
231       operation should be cancelled.</p><p>Notice that cancellation is an asynchronous operation that
232       might happen outside your function, in parallel with the code that
233       you are writing.  For example, in the case of threads, the request
234       will be set in the master thread; in the case of slave
235       CORBA-driven processes, the request will be activated by a Unix
236       signal.  So you can expect a cancellation request to happen (and
237       consequently be signalled in
238       <span class="structname">GnomeVFSCancellation</span>) at any time.</p><p>For this reason, you should be calling this function
239       periodically, whenever you are going to perform several
240       iterations of the same task, or execute a single expensive task.
241       When the function returns a nonzero value, the correct way to
242       react is:</p><div class="orderedlist"><ol type="1"><li><p>Clean things up so that the result of the
243         operations that have been performed are all
244         cancelled.</p></li><li><p>Return the
245         <span class="symbol">GNOME_VFS_ERROR_CANCELLED</span> error
246         code.</p></li></ol></div><p>But there are some other situations in which you want to
247       be able to interrupt a I/O operation when a cancellation request
248       is performed.  In such cases, polling is not a viable option.</p><p>For this reason,
249       <span class="structname">GnomeVFSCancellation</span> provides an
250       alternative way of sending notifications, using a file
251       descriptor.  To use this feature, you should use the following
252       function:</p><pre class="programlisting">
253
254 gint gnome_vfs_cancellation_get_fd (GnomeVFSCancellation *cancellation); </pre><p>When this function is called, it will return an open file
255       descriptor, which is the read-side of a pipe.  The pipe will be
256       given a character from the write side as soon as a cancellation
257       request is sent.  For this reason, you can check for a
258       cancellation by using the <tt class="function">select()</tt> system
259       call: as soon as <tt class="function">select</tt> reports that some
260       data is available on the file descriptor, you know that a
261       cancellation is being requested.</p><p>For example, if you are reading from a file descriptor and
262       you want to check for a pending cancellation at the same time,
263       you can set up <tt class="function">select</tt>for checking if data
264       is available on both the cancellation file descriptor and the
265       file descriptor you are reading from.</p></div><hr><div class="refsect2" lang="en"><h3>Dealing with <span class="symbol">EINTR</span></h3><p>In order to maximize the chance of cancelling an operation
266       immediately, the GNOME Virtual File System can sends a signal to
267       the asynchronous thread or process.  This does not happen on all
268       the systems and setups, though.</p><p>The result of this is that, if a process is in the middle
269       of a Unix system call while receiving this signal, the system
270       call might be interrupted and return a <span class="symbol">EINTR</span>
271       error.</p><p>For this reason, when you receive <span class="symbol">EINTR</span>
272       you should check if a cancellation request is pending, using
273       <tt class="function">gnome_vfs_cancellation_check()</tt> on the
274       <span class="structname">GnomeVFSCancellation</span> object that the
275       implementation function received:</p><div class="itemizedlist"><ul type="disc"><li><p>If a cancellation is indeed pending
276         (<tt class="function">gnome_vfs_cancellation_check()</tt> returns a
277         nonzero value), you should cancel the operation, cleaning up
278         all the effects, and return
279         <span class="symbol">GNOME_VFS_ERROR_INTERRUPTED</span> or
280         <span class="symbol">GNOME_VFS_ERROR_CANCELLED</span></p></li><li><p>Otherwise, retry the system call as you would
281         normally do.</p></li></ul></div></div></div><div class="refsect1" lang="en"><h2>Basic guidelines for writing a module</h2><p>Writing GNOME VFS modules is easy, but there are a few
282     things that you must keep in mind when hacking them:</p><div class="itemizedlist"><ul type="disc"><li><p>All of the code must be completely thread safe.
283       The reason for this is that the asynchronous GNOME VFS engine
284       will use threads when available; if you don't make sure that the
285       code is thread-safe, every kind of weird and unexpected errors
286       will happen.  As debugging these problems can be very hard, it's
287       important to write the code with threads in mind right from the
288       start.</p></li><li><p>Use the special
289       <tt class="function">gnome_vfs_*_cancellable()</tt> VFS functions
290       instead of the standard non-cancellable ones, passing them the
291       same <span class="structname">GnomeVFSCancellation</span> object you
292       are given, so that the operation can always be interrrupted at
293       any time.</p></li><li><p>The code should respect the basic GNOME
294       guidelines for source code indentation and
295       style.</p></li></ul></div><div class="refsect2" lang="en"><h3>How to make the code thread safe</h3><p>Although it might sound scary at first, making the code
296       for the modules thread safe is not complicated at all.</p><p>First of all, make sure the amount of global variables is
297       kept to the bare minimum.  If possible, you should avoid them at
298       all cost.</p><p>For those cases where globals are inevitable (such as
299       caches, connection pools or things like that), you have to make
300       sure every variable is properly associated with a mutex, and
301       that the mutex is locked before every access to this variable
302       and released afterwards.  You can also use
303       <tt class="function">G_LOCK_DEFINE_STATIC</tt>,
304       <tt class="function">G_LOCK</tt> and <tt class="function">G_UNLOCK</tt>
305       for this.
306       </p><p>Generally speaking, if you are going to dynamically
307       allocate structures that are shared by more than one
308       operation/file, you should provide all of them with their nice
309       mutex locks.</p><p>Finally, make sure mutexes are used only if they are
310       available.  One way to do so is to use macros like the
311       following:</p><pre class="programlisting">
312
313 #ifdef G_THREADS_ENABLED
314 #define MUTEX_NEW()     g_mutex_new ()
315 #define MUTEX_FREE(a)   g_mutex_free (a)
316 #define MUTEX_LOCK(a)   if ((a) != NULL) g_mutex_lock (a)
317 #define MUTEX_UNLOCK(a) if ((a) != NULL) g_mutex_unlock (a)
318 #else
319 #define MUTEX_NEW()     NULL
320 #define MUTEX_FREE(a)
321 #define MUTEX_LOCK(a)
322 #define MUTEX_UNLOCK(a)
323 #endif</pre><p><tt class="function">G_LOCK_DEFINE_STATIC</tt>,
324       <tt class="function">G_LOCK</tt> and <tt class="function">G_UNLOCK</tt> in
325       GLib are always safe to use, as they are already defined to be
326       nothing when thread support is not available.</p><p>(Probably it would be a good idea to have something in the
327       private GNOME VFS API that does this stuff for all the
328       modules.)</p></div></div></div><table class="navigation" width="100%" summary="Navigation footer" cellpadding="2" cellspacing="0"><tr valign="middle"><td align="left"><a accesskey="p" href="modules.html"><b>&lt;&lt; Filesystem Modules</b></a></td><td align="right"><a accesskey="n" href="gnome-vfs-20-gnome-vfs-mime.html"><b>MIME typing &gt;&gt;</b></a></td></tr></table></body></html>