21036504 Move libgamin from Desktop to Userland consolidation
authorVladimir Marek <Vladimir.Marek@oracle.com>
Wed, 19 Aug 2015 10:12:59 +0200
changeset 4805 ad8cc19e4aec
parent 4802 62e47da66002
child 4806 b4a4216682b3
21036504 Move libgamin from Desktop to Userland consolidation
components/gamin/Makefile
components/gamin/Solaris/gam_server.1
components/gamin/Solaris/libgamin-1.3
components/gamin/gamin.license
components/gamin/gamin.p5m
components/gamin/patches/gamin-01-all.patch
components/gamin/patches/gamin-02-gamin.patch
components/gamin/patches/gamin-03-const.patch
components/gamin/test/results-32.master
make-rules/prep.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/Makefile	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,81 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../make-rules/shared-macros.mk
+
+COMPONENT_NAME         = gamin
+COMPONENT_VERSION      = 0.1.10
+COMPONENT_PROJECT_URL  = http://www.gnome.org/~veillard/gamin/
+COMPONENT_SRC          = $(COMPONENT_NAME)-$(COMPONENT_VERSION)
+COMPONENT_ARCHIVE      = $(COMPONENT_SRC).tar.gz
+COMPONENT_ARCHIVE_HASH = \
+    sha256:28085f0ae8be10eab582ff186af4fb0be92cc6c62b5cc19cd09b295c7c2899a1
+COMPONENT_ARCHIVE_URL  = https://people.gnome.org/~veillard/gamin/sources/gamin-0.1.10.tar.gz
+COMPONENT_BUGDB        = library/gamin
+
+TPNO                   = 9483
+
+include $(WS_MAKE_RULES)/prep.mk
+include $(WS_MAKE_RULES)/configure.mk
+include $(WS_MAKE_RULES)/ips.mk
+
+ASLR_MODE = $(ASLR_ENABLE)
+
+COMPONENT_PREP_ACTION = (cd $(@D) ; autoreconf -if; bzip2 NEWS; bzip2 ChangeLog; bzip2 COPYING)
+
+# The tests can leave a socket behind, which makes the tests fail next time
+COMPONENT_PRE_TEST_ACTION += rm -f /tmp/fam-$$LOGNAME/fam-test
+
+# The test suite sets permission on the temp dir to 000 and 'gmake clean' is
+# not able to remove it afterwards
+PRE_CLEAN_ACTION = chmod 777 $(BUILD_DIR)/*/python/tests/temp_dir &> /dev/null || :
+
+# Remove times from the test output
+COMPONENT_TEST_TRANSFORMS += '-e "s/^\(--- result\.[0-9][0-9]*\).*/\1/" '
+
+# Since the tests don't pass, remove mostly all output of the tests
+COMPONENT_TEST_TRANSFORMS += '-e "/^ /d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^Error/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^error/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^Traceback/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^IndexError/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^Got callback/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^No differences encountered/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^+/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^\-/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^\*/d"'
+COMPONENT_TEST_TRANSFORMS += '-e "/^!/d"'
+
+configure:	$(CONFIGURE_32)
+
+build:          $(BUILD_32)
+
+install:        $(INSTALL_32)
+
+test:           $(TEST_32)
+
+REQUIRED_PACKAGES += library/glib2
+REQUIRED_PACKAGES += runtime/python-27
+REQUIRED_PACKAGES += system/library
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/Solaris/gam_server.1	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,39 @@
+'\" te
+.TH gam_server 1 "25 Mar 2008" "SunOS 5.11" "User Commands"
+.SH "NAME"
+gam_server \- The Gamin server gathers file events from the kernel and sends
+these events to clients\&.
+.SH "SYNOPSIS"
+.PP
+/usr/lib/\fBgam_server\fR
+.SH "DESCRIPTION"
+.PP
+Gamin uses a client-server model\&.  gam_server is the server which centralizes
+client requests for monitoring files, gathers file events via File Event
+Notification kernel API methods (see
+\fBport_create\fR(3C)), 
+and then sends modification events to application clients\&.  It also shares
+file events when multiple applications monitor the same file\&.
+  
+.PP
+For configuring gam_server refer to
+\fBhttp://www\&.gnome\&.org/~veillard/gamin/config\&.html\fR
+  
+.SH "ATTRIBUTES"
+.PP
+See \fBattributes\fR(5)
+for descriptions of the following attributes:
+  
+.sp
+.SH "SEE ALSO"
+.PP
+\fBport_create\fR(3C),
+\fBlibgamin-1\fR(3),
+\fBattributes\fR(5)
+.PP
+Gamin Homepage: \fBhttp://www\&.gnome\&.org/~veillard/gamin\fR
+.SH "NOTES"
+.PP
+Written by Lin Ma, Sun Microsystems Inc\&., 2008\&.
+...\" created by instant / solbook-to-man, Tue 27 Jan 2015, 17:22
+...\" LSARC 2007/398 Gamin - the File Alteration Monitor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/Solaris/libgamin-1.3	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,29 @@
+'\" te
+.TH libgamin-1 3 "25 Mar 2008" "SunOS 5.11" "C Library Functions"
+.SH "NAME"
+libgamin-1 \- library interfaces to detect when a file or a directory has been modified
+.SH "DESCRIPTION"
+.PP
+\fBlibgamin-1\fR is compatible with FAM 2\&.6\&.8 and allows to detect when a file or a directory has been modified\&.
+  
+.SH "ATTRIBUTES"
+.PP
+See \fBattributes\fR(5)
+for descriptions of the following attributes:
+  
+.sp
+.SH "SEE ALSO"
+.PP
+\fBgam_server\fR(1),
+\fBattributes\fR(5)
+.PP
+Developers want to monitor the changes of a file may use the interfaces exported by FAM project \fBhttp://oss\&.sgi\&.com/projects/fam\fR and link the application to \fBlibgamin-1\fR\&.
+.PP
+For the detail information of FAM interfaces refer to \fBhttp://oss\&.sgi\&.com/projects/fam/doc\&.html\fR\&.
+.PP
+Gamin Homepage: \fBhttp://www\&.gnome\&.org/~veillard/gamin\fR
+.SH "NOTES"
+.PP
+Written by Lin Ma, Sun Microsystems Inc\&., 2008\&.
+...\" created by instant / solbook-to-man, Tue 27 Jan 2015, 17:22
+...\" LSARC 2007/398 Gamin - the File Alteration Monitor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/gamin.license	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,486 @@
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+    		    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    Gamin is a file and directory monitoring system defined to be
+    a subset of the FAM (File Alteration Monitor) system.
+    Copyright (C) 2004  Daniel Veillard <[email protected]>
+    also partly
+    Copyright (C) 2003  James Willcox  <[email protected]>
+    Copyright (C) 2003  Corey Bowers  <[email protected]>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the 
+    Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+    Boston, MA  02111-1307  USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Red Hat, Inc., hereby disclaims all copyright interest in the
+  library `gamin' (a file alteration monitor) written by Daniel Veillard.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/gamin.p5m	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,56 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+#
+
+<transform file path=usr.*/man/.+ -> default mangler.man.stability uncommitted>
+set name=pkg.fmri \
+    value=pkg:/library/file-monitor/[email protected]$(IPS_COMPONENT_VERSION),$(BUILD_VERSION)
+set name=pkg.summary value=Gamin
+set name=com.oracle.info.description \
+    value="Library providing the FAM File Alteration Monitor API"
+set name=com.oracle.info.tpno value=$(TPNO)
+set name=info.classification \
+    value=org.opensolaris.category.2008:System/Libraries
+set name=info.source-url value=$(COMPONENT_ARCHIVE_URL)
+set name=org.opensolaris.arc-caseid value=PSARC/2010/143
+set name=org.opensolaris.consolidation value=$(CONSOLIDATION)
+file path=usr/include/fam.h
+file usr/libexec/gam_server path=usr/lib/gam_server
+link path=usr/lib/libfam.so target=libfam.so.0.0.0
+link path=usr/lib/libfam.so.0 target=libfam.so.0.0.0
+file path=usr/lib/libfam.so.0.0.0
+link path=usr/lib/libgamin-1.so target=libgamin-1.so.0.1.10
+link path=usr/lib/libgamin-1.so.0 target=libgamin-1.so.0.1.10
+file path=usr/lib/libgamin-1.so.0.1.10
+file path=usr/lib/pkgconfig/gamin.pc
+file path=usr/lib/python2.7/site-packages/_gamin.so
+file path=usr/lib/python2.7/site-packages/gamin.py
+file AUTHORS path=usr/share/doc/SUNWgamin/AUTHORS
+file COPYING.bz2 path=usr/share/doc/SUNWgamin/COPYING.bz2
+file ChangeLog.bz2 path=usr/share/doc/SUNWgamin/ChangeLog.bz2
+file NEWS.bz2 path=usr/share/doc/SUNWgamin/NEWS.bz2
+file README path=usr/share/doc/SUNWgamin/README
+file Solaris/gam_server.1 path=usr/share/man/man1/gam_server.1
+file Solaris/libgamin-1.3 path=usr/share/man/man3/libgamin-1.3
+license gamin.license license=LGPLv2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/patches/gamin-01-all.patch	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,2373 @@
+Solaris FEN support in Gamin
+
+https://bugzilla.gnome.org/show_bug.cgi?id=730679
+http://osdyson.org/issues/172
+http://hg.osdyson.org/solaris-desktop-spec-files/raw-file/8f7c0cd200a9/patches/gamin-01-all.diff
+
+diff --git a/configure.in b/configure.in
+index e4b684e..5836bb7 100644
+--- a/configure.in
++++ b/configure.in
[email protected]@ -42,6 +42,12 @@ if test -z "$ENV_CFLAGS"; then
+ 	CFLAGS=""
+ fi
+ 
++dnl If the user set no CFLAGS, then don't assume the autotools defaults of
++dnl "-g -O2". We set default CFLAGS later based on the --disable-debug flag.
++if test -z "$ENV_CFLAGS"; then
++	CFLAGS=""
++fi
++
+ dnl for the spec file
+ RELDATE=`date +'%a %b %e %Y'`
+ AC_SUBST(RELDATE)
[email protected]@ -279,6 +285,43 @@ if test x$kqueue = xtrue; then
+ 	backends="${backends}, kqueue"
+ fi
+ 
++case "$os" in
++    solaris*)
++	AM_CONDITIONAL(ON_SOLARIS, true)
++	AC_COMPILE_IFELSE([
++		#include <port.h>
++		#ifndef PORT_SOURCE_FILE
++		#error "Please upgrade to Nevada 72 or above to suppoert FEN"
++		#endif
++		int main() { return 0; }
++		],[have_fen=1],)
++	if test x$have_fen = x1 ; then
++            AC_ARG_ENABLE(fen,
++		AC_HELP_STRING([--disable-fen], [Disable the FEN backend]),
++                       [fen="${enableval}"], [fen=true])
++
++		if test x$fen = xyes; then
++                       fen=true
++		elif test x$fen = xno; then
++                       fen=false
++		elif test x$fen != xtrue; then
++                       AC_MSG_ERROR(bad value ${enableval} for --disable-fen)
++		fi
++	fi
++	break;
++	;;
++    *)
++	fen=false
++	break;
++	;;
++esac
++
++AM_CONDITIONAL(ENABLE_FEN, test x$fen = xtrue)
++if test x$fen = xtrue; then
++	AC_DEFINE(ENABLE_FEN,1,[Use Solaris FEN as backend])
++	backends="${backends}, FEN"
++fi
++
+ dnl pthread support for reentrance of the client library.
+ AC_ARG_WITH(threads,
+ [  --with-threads          add multithread support(on)])
[email protected]@ -387,6 +430,14 @@ if test x$dbus_have_struct_cmsgcred = xyes; then
+     AC_DEFINE(HAVE_CMSGCRED,1,[Have cmsgcred structure])
+ fi
+ 
++dnl Check for getpeerucred support - Solaris
++
++AC_CHECK_HEADER(ucred.h,
++    AC_CHECK_LIB(c, getpeerucred,[
++        AC_DEFINE([HAVE_GETPEERUCRED],[],[Define if has getpeerucred])
++        AC_DEFINE([HAVE_UCRED_H],[],[Define if <ucred.h> exists])]))
++
++
+ #### Abstract sockets
+ 
+ AC_MSG_CHECKING(abstract socket namespace)
[email protected]@ -531,6 +582,16 @@ AC_SUBST(PYTHON_VERSION)
+ AC_SUBST(PYTHON_INCLUDES)
+ AC_SUBST(PYTHON_SITE_PACKAGES)
+ 
++dnl Check for -lsocket -lnsl
++
++AC_CHECK_FUNC(gethostent, , AC_CHECK_LIB(nsl, gethostent))
++AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt))
++
++dnl Check for <sys/mnttab.h>
++
++AC_CHECK_HEADER(sys/mnttab.h,
++	AC_DEFINE([HAVE_SYS_MNTTAB_H], [], [Define if <sys/mnttab.h> is there]))
++
+ dnl After all config-related tweaking of CFLAGS, set it to its "build" value
+ 
+ AC_MSG_CHECKING(for more compiler warnings)
+diff --git a/libgamin/Makefile.am b/libgamin/Makefile.am
+index 35aa740..4f725a2 100644
+--- a/libgamin/Makefile.am
++++ b/libgamin/Makefile.am
[email protected]@ -39,13 +39,24 @@ CLEANFILES=gam_error.c gam_event.c
+ 
+ libgamin_1_la_LIBADD =
+ 
++if ON_SOLARIS
++libgamin_1_la_LDFLAGS = -Wl,-M$(srcdir)/gamin_sym.version \
++                        -version-info @[email protected] @[email protected]
++else
+ libgamin_1_la_LDFLAGS = -Wl,--version-script=$(srcdir)/gamin_sym.version \
+                         -version-info @[email protected] @[email protected]
++endif
+ 
+ libfam_la_SOURCES = $(libgamin_1_la_SOURCES)
+ libfam_la_LIBADD = $(libgamin_1_la_LIBADD)
+-libfam_la_LDFLAGS = -Wl,--version-script=$(srcdir)/gamin_sym.version	\
++
++if ON_SOLARIS
++libfam_la_LDFLAGS = -Wl,-M$(srcdir)/gamin_sym.version	\
+                     -version-info @[email protected] @[email protected]
++else
++libfam_la_LDFLAGS = -Wl,--version-script=$(srcdir)/gamin_sym.version   \
++                    -version-info @[email protected] @[email protected]
++endif
+ 
+ #
+ # Compile a program locally to check
+diff --git a/libgamin/gam_api.c b/libgamin/gam_api.c
+index 4e87e63..3630213 100644
+--- a/libgamin/gam_api.c
++++ b/libgamin/gam_api.c
[email protected]@ -14,6 +14,12 @@
+ #include <sys/socket.h>
+ #include <sys/un.h>
+ #include <sys/uio.h>
++#if defined(sun)
++#include <string.h>
++#endif
++#if defined(HAVE_UCRED_H)
++#include <ucred.h>
++#endif defined(HAVE_UCRED_H)
+ #include "fam.h"
+ #include "gam_protocol.h"
+ #include "gam_data.h"
[email protected]@ -660,6 +666,10 @@ gamin_check_cred(GAMDataPtr conn, int fd)
+     } cmsg;
+ #endif
+ 
++#if defined(HAVE_GETPEERUCRED)
++    ucred_t *creds;
++#endif
++
+     s_uid = getuid();
+ 
+ #if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
[email protected]@ -726,11 +736,25 @@ retry:
+                       fd, cr_len, (int) sizeof(cr));
+             goto failed;
+         }
++#elif defined(HAVE_GETPEERUCRED)
++        if ((creds = (ucred_t *)malloc(ucred_size()))==(ucred_t *)NULL){
++            GAM_DEBUG(DEBUG_INFO,"Malloc failed for ucreds");
++            goto failed;
++        }
++
++        if (getpeerucred(fd, &creds)!=0){
++            GAM_DEBUG(DEBUG_INFO,"getpeerucred call failed");
++            goto failed;
++        }
++        c_uid = ucred_getruid(creds);
++        c_gid = ucred_getrgid(creds);
++        c_pid = ucred_getpid(creds);
++        ucred_free(creds);
+ #elif defined(HAVE_CMSGCRED)
+         c_pid = cmsg.cred.cmcred_pid;
+         c_uid = cmsg.cred.cmcred_euid;
+         c_gid = cmsg.cred.cmcred_groups[0];
+-#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
++#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEERUCRED */
+         GAM_DEBUG(DEBUG_INFO,
+                   "Socket credentials not supported on this OS\n");
+         goto failed;
+diff --git a/libgamin/gamin_sym.version b/libgamin/gamin_sym.version
+index dba5f20..5c6d661 100644
+--- a/libgamin/gamin_sym.version
++++ b/libgamin/gamin_sym.version
[email protected]@ -2,8 +2,6 @@
+    global:
+        FAMCancelMonitor;
+        FAMClose;
+-       FAMDebugLevel;
+-       FAMDebug;
+        FamErrlist;
+        FAMErrno;
+        FAMMonitorCollection;
+diff --git a/server/Makefile.am b/server/Makefile.am
+index 37aed8b..7964d17 100644
+--- a/server/Makefile.am
++++ b/server/Makefile.am
[email protected]@ -10,7 +10,7 @@ INCLUDES =						\
+ 	-DG_DISABLE_DEPRECATED				
+ 
+ if GAMIN_DEBUG
+-INCLUDES += -DGAM_DEBUG_ENABLED
++INCLUDES += -DGAM_DEBUG_ENABLED -g
+ endif
+ 
+ 
[email protected]@ -68,6 +68,18 @@ if ENABLE_KQUEUE
+ gam_server_SOURCES += gam_kqueue.c gam_kqueue.h
+ endif
+ 
++if ENABLE_FEN
++gam_server_SOURCES += gam_fen.c gam_fen.h		\
++	fen-dump.c		\
++	fen-dump.h		\
++	fen-kernel.c 		\
++	fen-kernel.h 		\
++	fen-helper.c 		\
++	fen-helper.h		\
++	fen-node.c		\
++	fen-node.h
++endif
++
+ if ENABLE_HURD_MACH_NOTIFY
+ gam_server_SOURCES += gam_hurd_mach_notify.c gam_hurd_mach_notify.h
+ 
+diff --git a/server/fen-dump.c b/server/fen-dump.c
+new file mode 100644
+index 0000000..98d20eb
+--- /dev/null
++++ b/server/fen-dump.c
[email protected]@ -0,0 +1,77 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#include "config.h"
++#include <glib.h>
++#include <glib/gprintf.h>
++#include "fen-node.h"
++#include "fen-dump.h"
++
++G_LOCK_EXTERN (fen_lock);
++
++/*-------------------- node ------------------*/
++static void
++dump_node (node_t* node, gpointer data)
++{
++    g_printf ("n:0x%p ds:0x%p s:0x%p %s\n", node, node->dir_subs, node->subs, NODE_NAME(node));
++}
++
++static void
++dump_tree (node_t* node)
++{
++    if (G_TRYLOCK (fen_lock)) {
++        node_traverse(NULL, dump_node, NULL);
++        G_UNLOCK (fen_lock);
++    }
++}
++
++/* ------------------ fdata port hash --------------------*/
++void
++dump_hash_cb (gpointer key,
++  gpointer value,
++  gpointer user_data)
++{
++    g_printf ("k:0x%p v:0x%p >\n", key, value);
++}
++
++gboolean
++dump_hash (GHashTable* hash, gpointer user_data)
++{
++    if (G_TRYLOCK (fen_lock)) {
++        if (g_hash_table_size (hash) > 0) {
++            g_hash_table_foreach (hash, dump_hash_cb, user_data);
++        }
++        G_UNLOCK (fen_lock);
++    }
++    return TRUE;
++}
++
++/* ------------------ event --------------------*/
++void
++dump_event (node_event_t* ev, gpointer user_data)
++{
++    node_t* node = ev->user_data;
++    g_printf ("ne:0x%p e:%p n:0x%p ds:0x%p s:0x%p s\n", ev, ev->e, node, node->dir_subs, node->subs, NODE_NAME(node));
++}
++
+diff --git a/server/fen-dump.h b/server/fen-dump.h
+new file mode 100644
+index 0000000..ffac822
+--- /dev/null
++++ b/server/fen-dump.h
[email protected]@ -0,0 +1,29 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#ifndef _FEN_DUMP_H_
++#define _FEN_DUMP_H_
++
++
++#endif /* _FEN_DUMP_H_ */
+diff --git a/server/fen-helper.c b/server/fen-helper.c
+new file mode 100644
+index 0000000..e68e7c3
+--- /dev/null
++++ b/server/fen-helper.c
[email protected]@ -0,0 +1,197 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#include "config.h"
++#include <glib.h>
++#include "fen-helper.h"
++#include "fen-kernel.h"
++#ifdef GIO_COMPILATION
++#include "gfilemonitor.h"
++#else
++#include "gam_event.h"
++#include "gam_server.h"
++#include "gam_protocol.h"
++#endif
++
++#ifdef GIO_COMPILATION
++#define FH_W if (fh_debug_enabled) g_debug
++static gboolean fh_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FH_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++G_LOCK_EXTERN (fen_lock);
++
++/* misc */
++static void
++scan_children_init(node_t *f, gpointer sub)
++{
++    gboolean emit;
++    gint event;
++
++    FH_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
++
++#ifdef GIO_COMPILATION
++    emit = FALSE;
++    event = G_FILE_MONITOR_EVENT_CREATED;
++#else
++    emit = TRUE;
++    event = GAMIN_EVENT_EXISTS;
++#endif
++
++    if (!NODE_HAS_FLAG(f, NODE_FLAG_SNAPSHOT_UPDATED)) {
++        /* TODO snapshot should also compare to the sub created timestamp. */
++        /* GIO initially doesn't emit created/existed events. */
++        node_create_children_snapshot(f, event, emit);
++    } else {
++        GHashTableIter iter;
++        gpointer value;
++
++        g_hash_table_iter_init (&iter, f->children);
++        while (g_hash_table_iter_next (&iter, NULL, &value)) {
++            node_t *child = (node_t *)value;
++
++#ifdef GIO_COMPILATION
++            /* GIO initially doesn't emit created/existed events. */
++            /* g_file_monitor_emit_event(G_FILE_MONITOR(sub), child->gfile, NULL, event); */
++#else
++            gam_server_emit_one_event(NODE_NAME(child), gam_subscription_is_dir(sub), event, sub, 1);
++#endif
++        }
++    }
++}
++
++/**
++ * fen_add
++ * 
++ * Won't hold a ref, we have a timout callback to clean unused node_t.
++ * If there is no value for a key, add it and return it; else return the old
++ * one.
++ */
++void
++fen_add (const gchar *filename, gpointer sub, gboolean is_mondir)
++{
++	node_t* f;
++
++    g_assert (filename);
++    g_assert (sub);
++
++    G_LOCK (fen_lock);
++	f = node_find(NULL, filename, TRUE);
++    FH_W ("%s 0x%p sub[0x%p] %s\n", __func__, f, sub, filename);
++    g_assert (f);
++
++    /* Update timestamp, the events in global queue will compare itself to this
++     * timestamp to decide if be emitted. TODO, timestamp should be per sub.
++     */
++    if (!NODE_IS_ACTIVE(f)) {
++        g_get_current_time(&f->atv);
++    }
++
++    if (is_mondir) {
++        f->dir_subs = g_list_prepend(f->dir_subs, sub);
++    } else {
++        f->subs = g_list_prepend(f->subs, sub);
++    }
++    
++    if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED) ||
++      (node_lstat(f) == 0 && port_add(f) == 0)) {
++#ifndef GIO_COMPILATION
++        gam_server_emit_one_event (NODE_NAME(f),
++          gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1);
++#endif
++        if (is_mondir) {
++            scan_children_init (f, sub);
++        }
++    } else {
++#ifndef GIO_COMPILATION
++        gam_server_emit_one_event (NODE_NAME(f),
++          gam_subscription_is_dir (sub), GAMIN_EVENT_DELETED, sub, 1);
++#endif
++        node_adjust_deleted (f);
++    }
++#ifndef GIO_COMPILATION
++    gam_server_emit_one_event (NODE_NAME(f),
++      gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1);
++#endif
++    G_UNLOCK (fen_lock);
++}
++
++void
++fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir)
++{
++    node_t* f;
++    
++    g_assert (filename);
++    g_assert (sub);
++
++    G_LOCK (fen_lock);
++	f = node_find(NULL, filename, FALSE);
++    FH_W ("%s 0x%p sub[0x%p] %s\n", __func__, f, sub, filename);
++
++    if (f) {
++        if (is_mondir) {
++            f->dir_subs = g_list_remove(f->dir_subs, sub);
++        } else {
++            f->subs = g_list_remove(f->subs, sub);
++        }
++
++        if (!NODE_IS_ACTIVE(f)) {
++            node_try_delete (f);
++        }
++    }
++    G_UNLOCK (fen_lock);
++}
++
++/**
++ * fen_init:
++ * 
++ * FEN subsystem initializing.
++ */
++gboolean
++fen_init ()
++{
++    static gboolean initialized = FALSE;
++    static gboolean result = FALSE;
++
++    G_LOCK (fen_lock);
++    if (initialized) {
++        G_UNLOCK (fen_lock);
++        return result;
++    }
++
++    result = node_class_init();
++
++    if (!result) {
++        G_UNLOCK (fen_lock);
++        return result;
++    }
++
++    initialized = TRUE;
++
++    G_UNLOCK (fen_lock);
++    return result;
++}
++
+diff --git a/server/fen-helper.h b/server/fen-helper.h
+new file mode 100644
+index 0000000..25df38f
+--- /dev/null
++++ b/server/fen-helper.h
[email protected]@ -0,0 +1,33 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#ifndef _FEN_HELPER_H_
++#define _FEN_HELPER_H_
++
++void fen_add (const gchar *filename, gpointer sub, gboolean is_mondir);
++void fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir);
++
++gboolean fen_init ();
++
++#endif /* _FEN_HELPER_H_ */
+diff --git a/server/fen-kernel.c b/server/fen-kernel.c
+new file mode 100644
+index 0000000..8b9c58b
+--- /dev/null
++++ b/server/fen-kernel.c
[email protected]@ -0,0 +1,557 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#include "config.h"
++#include <rctl.h>
++#include <strings.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <glib.h>
++#include "fen-kernel.h"
++#include "fen-dump.h"
++
++#ifdef GIO_COMPILATION
++#define FK_W if (fk_debug_enabled) g_debug
++static gboolean fk_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FK_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++G_GNUC_INTERNAL G_LOCK_DEFINE (fen_lock);
++
++static ulong max_port_events = 512;
++static GList *pn_visible_list;	/* the queue of ports which don't have the max objs */
++static GQueue *g_eventq = NULL;
++static timespec_t zero_wait;
++static void (*user_process_events_cb) (gpointer, node_event_t*);
++static port_event_t *pevents = NULL;
++static gint PE_ALLOC = 2048;
++static GHashTable *renamed_hash = NULL; /* <parent node, ev> */
++
++typedef struct _PSource {
++    GSource  source;            /* Inherit from GSource, must be the first. */
++    GPollFD  gfd;
++    gboolean pending;
++    uint_t   event_growing_factor;
++    uint_t   pending_events;
++} PSource;
++
++#define PGPFD(s)             (&((PSource *)(s))->gfd)
++#define SLEEP_BASE_TIME      10	/* in milliseconds */
++#define EXPECT_INC_EVENTS(pn)  (1 << (pn->event_growing_factor))
++
++#define RENAME_EVENTS_INTERVAL 500 /* in milliseconds */
++#define PROCESS_PORT_EVENTS_TIME 1000 /* in milliseconds */
++guint process_port_event_id = 0;
++
++static gchar* _event_strings(int event);
++static const gchar* _event_string (int event);
++static GSource *psource_new();
++
++static gboolean port_prepare(GSource *source, gint *timeout_);
++static gboolean port_check(GSource *source);
++static gboolean port_dispatch(GSource *source, GSourceFunc callback, gpointer user_data);
++static GSourceFuncs fen_source_func = {
++    port_prepare,
++    port_check,
++    port_dispatch,
++    NULL
++};
++
++static gboolean
++port_prepare(GSource *source, gint *timeout_)
++{
++    return FALSE;
++}
++
++static gboolean
++port_check(GSource *source)
++{
++	PSource *pn = (PSource *)source;
++    uint_t nget;
++    
++    if (pn->pending) {
++        pn->pending = FALSE;
++        g_source_add_poll(source, PGPFD(source));
++        g_source_unref(source);
++        return FALSE;
++    }
++
++    if (!(PGPFD(pn)->revents & G_IO_IN))
++        return FALSE;
++
++    if (port_getn(PGPFD(source)->fd, NULL, 0, &nget, 0) == 0) {
++        if (nget - pn->pending_events > EXPECT_INC_EVENTS(pn)) {
++            /* Sleep for a while. */
++            pn->pending_events = nget;
++            pn->event_growing_factor ++;
++
++            pn->pending = TRUE;
++            g_source_ref(source);
++            g_source_remove_poll(source, PGPFD(source));
++            g_timeout_add(SLEEP_BASE_TIME,
++              (GSourceFunc)port_check,
++              (gpointer)pn);
++            return FALSE;
++        }
++    }
++
++    pn->pending_events = 0;
++    pn->event_growing_factor = 0;
++
++    return TRUE;
++}
++
++static gboolean
++port_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
++{
++    node_t *f;
++	uint_t nget = 0;
++	uint_t total = 0;
++
++    FK_W ("%s 0x%p fd %d\n", __func__, source, PGPFD(source)->fd);
++
++    G_LOCK (fen_lock);
++    do {
++        nget = 1;
++        if (port_getn(PGPFD(source)->fd, pevents, PE_ALLOC, &nget, &zero_wait) == 0) {
++            int i;
++            for (i = 0; i < nget; i++) {
++                f = (node_t *)pevents[i].portev_user;
++
++                if (pevents[i].portev_source == PORT_SOURCE_FILE) {
++
++                    NODE_CLE_STATE(f, NODE_STATE_ASSOCIATED);
++                    NODE_SET_STATE(f, NODE_STATE_HAS_EVENTS);
++
++                    if (HAS_NO_EXCEPTION_EVENTS(pevents[i].portev_events)) {
++                        /* If the events do not show it's deleted, update
++                         * file timestamp to avoid missing events next time.
++                         */
++                        if (node_lstat(f) != 0 /* || port_add(f) != 0 */) {
++                            /* Included deleted event. */
++                            pevents[i].portev_events |= FILE_DELETE;
++                        }
++                    }
++
++                    /* Queue it and waiting for processing. */
++                    g_queue_push_tail(g_eventq,
++                      node_event_new(pevents[i].portev_events, (gpointer)f));
++
++                } else {
++                    FK_W ("[kernel] unknown portev_source %d\n", pevents[i].portev_source);
++                }
++            }
++
++            total += nget;
++
++        } else {
++            FK_W ("[kernel] port_getn %s\n", g_strerror (errno));
++            break;
++        }
++    } while (nget == PE_ALLOC);
++
++    G_UNLOCK (fen_lock);
++
++    if (total > 0 && callback) {
++        FK_W ("[kernel] get total %ld events\n", total);
++        return callback (user_data);
++    }
++    return TRUE;
++}
++
++static gboolean
++process_renamed_hash_cb(gpointer key, gpointer value, gpointer user_data)
++{
++    node_event_t *ev = value;
++
++#if 0
++    node_add_event(ev->user_data, ev);
++#else
++    user_process_events_cb(ev->user_data, ev);
++#endif
++    /* Always delete self from hash. */
++    return TRUE;
++}
++
++static gboolean
++port_events_process_cb(gpointer user_data)
++{
++    node_event_t *ev;
++    
++    G_LOCK (fen_lock);
++
++	/* Processing g_eventq */
++    while ((ev = (node_event_t*)g_queue_pop_head (g_eventq)) != NULL) {
++
++        /* FK_W ("[%s] 0x%p %s\n", __func__, ev, _event_string (ev->e)); */
++
++        {
++            gchar *log = _event_strings(ev->e);
++            FK_W ("%s %s %s\n", __func__, NODE_NAME(ev->user_data), log);
++            g_free(log);
++        }
++
++#ifdef GIO_COMPILATION
++        /* Use the parent node as a hash, because only the dir_subs in the
++         * parent node should receive MOVE event.
++         */
++        if (NODE_PARENT(ev->user_data)) {
++            if (ev->e == FILE_RENAME_TO) {
++                g_hash_table_insert(renamed_hash, NODE_PARENT(ev->user_data), ev);
++                g_time_val_add(&ev->rename_tv, RENAME_EVENTS_INTERVAL);
++                continue;
++            }
++            if (ev->e == FILE_RENAME_FROM) {
++                node_event_t *pair_ev;
++
++                pair_ev = g_hash_table_lookup(renamed_hash, NODE_PARENT(ev->user_data));
++                if (pair_ev && node_timeval_lt(&ev->ctv, &pair_ev->rename_tv)) {
++                    g_hash_table_remove(renamed_hash, NODE_PARENT(ev->user_data));
++                    pair_ev->pair_data = ev->user_data;
++                    /* Free ev, exchange pair_ev and ev. */
++                    node_event_delete(ev);
++                    ev = pair_ev;
++                }
++            }
++        }
++#endif
++    
++#if 0
++        node_add_event(ev->user_data, ev);
++#else
++        user_process_events_cb(ev->user_data, ev);
++#endif
++    }
++
++    /* Processing the events in renamed_hash. TODO we should delay it and wait
++     * for more possible pair.
++     */
++    g_hash_table_foreach_remove(renamed_hash, process_renamed_hash_cb, NULL);
++
++    G_UNLOCK (fen_lock);
++
++    process_port_event_id = 0;
++    return FALSE;
++}
++
++static gboolean
++port_events_read_cb(gpointer user_data)
++{
++
++    if (process_port_event_id == 0) {
++        process_port_event_id = g_timeout_add(PROCESS_PORT_EVENTS_TIME,
++          port_events_process_cb,
++          NULL);
++    }
++
++	return TRUE;
++}
++
++/*
++ * malloc PSource and port_create, start thread at pnode_ref.
++ * if psource_new succeeded, the PSource will never
++ * be freed. So PSource can be freed only in psource_new.
++ * Note pnode_monitor_remove_all can also free PSource, but currently no one
++ * invork it.
++ */
++static GSource*
++psource_new()
++{
++    GSource *source = NULL;
++    int fd;
++
++    if ((fd = port_create()) >= 0) {
++        source = g_source_new(&fen_source_func, sizeof(PSource));
++        PGPFD(source)->fd = fd;
++        PGPFD(source)->events = G_IO_IN | G_IO_HUP | G_IO_ERR;
++        g_source_set_callback(source, port_events_read_cb, NULL, NULL);
++        g_source_attach(source, NULL);
++        g_source_unref(source);
++        g_source_add_poll(source, PGPFD(source));
++
++        FK_W ("%s 0x%p fd %d\n", __func__, source, PGPFD(source)->fd);
++    } else {
++        FK_W ("PORT_CREATE %s\n", g_strerror(errno));
++        g_return_val_if_reached(NULL);
++    }
++
++	return source;
++}
++
++/**
++ * port_add:
++ * @f:
++ *
++ * Unsafe, need lock fen_lock.
++ * port_add will associate a GSource to @f->source
++ */
++gint
++port_add(node_t *f)
++{
++	GSource *source = f->source;
++
++    FK_W ("%s [0x%p] %s\n", __func__, f, NODE_NAME(f));
++
++    g_assert(f);
++    g_assert(NODE_HAS_FLAG(f, NODE_FLAG_STAT_UPDATED));
++
++    /* if (!NODE_HAS_FLAG(f, NODE_FLAG_STAT_DONE)) { */
++    /*     if (NODE_STAT(f) != 0) { */
++    /*         return errno; */
++    /*     } */
++    /* } */
++
++    /* Try re-use f->pn. f->pn may be used by other request, e.g. f is deleted
++     * for a long time. So if pn is full, we try to open a new one.
++     */
++    if (!source) {
++start_over:
++        /* Try the next visible source. */
++        if (pn_visible_list) {
++            source = (GSource *)pn_visible_list->data;
++        } else {
++            if ((source = psource_new()) != NULL) {
++                g_assert (g_list_find (pn_visible_list, source) == NULL);
++                pn_visible_list = g_list_prepend (pn_visible_list, source);
++            }
++        }
++    }
++
++    if (port_associate(PGPFD(source)->fd, PORT_SOURCE_FILE, (uintptr_t)FILE_OBJECT(f),
++        CONCERNED_EVENTS,
++        (void *)f) == 0) {
++        f->source = source;
++        NODE_SET_STATE(f, NODE_STATE_ASSOCIATED);
++        NODE_CLE_FLAG(f, NODE_FLAG_STAT_UPDATED);
++        FK_W ("PORT_ASSOCIATE 0x%p OK\n", f);
++        return 0;
++    } else if (errno == EAGAIN) {
++        /* Full, remove it. */
++        pn_visible_list = g_list_remove (pn_visible_list, source);
++        /* Re-add to port */
++        goto start_over;
++
++    } else if (errno == ENOENT) {
++        /* File is not exist */
++    } else if (errno == ENOTSUP) {
++        /* FS is not supported. Currently we think it no longer make sense to
++         * monitor it, so clean the stat info and return 0 to ignore this
++         * node. If there are requirement, we can consider to add polling
++         * method.
++         */
++        NODE_CLE_FLAG(f, NODE_FLAG_STAT_UPDATED);
++        return 0;
++    } else {
++        FK_W ("PORT_ASSOCIATE 0x%p %s\n", f, g_strerror (errno));
++    }
++
++    /* No matter if associated successfully, stat info is out-of-date, so clean it. */
++    NODE_CLE_FLAG(f, NODE_FLAG_STAT_UPDATED);
++    return errno;
++}
++
++/**
++ * port_remove
++ *
++ * < private >
++ * Unsafe, need lock fen_lock.
++ */
++void
++port_remove (node_t *f)
++{
++    /* g_assert(f->source); */
++
++    if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)) {
++        /* Mark unregisted. */
++        if (port_dissociate(PGPFD(f->source)->fd, PORT_SOURCE_FILE, (uintptr_t)FILE_OBJECT(f)) == 0) {
++            /*
++             * Note, we can run foode_delete if dissociating is failed,
++             * because there may be some pending events (mostly like
++             * FILE_DELETE) in the port_get. If we delete the foode
++             * the fnode may be deleted, then port_get will run on an invalid
++             * address.
++             */
++            NODE_CLE_STATE(f, NODE_STATE_ASSOCIATED);
++            FK_W ("PORT_DISSOCIATE 0x%p OK\n", f);
++        } else if (errno == ENOENT) {
++            /* The file has been removed from port, after port_get or before
++             * port_get but DELETED event has been generated.
++             * Ignored. */
++        } else {
++            FK_W ("PORT_DISSOCIATE 0x%p %s\n", f, g_strerror (errno));
++            g_return_if_reached();
++        }
++    }
++}
++
++/**
++ * Get Solaris resouce values.
++ *
++ */
++
++extern gboolean
++port_class_init (void (*user_process_events_callback) (gpointer, node_event_t*))
++{
++	rctlblk_t *rblk;
++
++	if ((rblk = malloc (rctlblk_size ())) == NULL) {
++        FK_W ("[kernel] rblk malloc %s\n", g_strerror (errno));
++		return FALSE;
++	}
++	if (getrctl ("process.max-port-events", NULL, rblk, RCTL_FIRST) == -1) {
++        FK_W ("[kernel] getrctl %s\n", g_strerror (errno));
++        free (rblk);
++        return FALSE;
++	} else {
++        max_port_events = rctlblk_get_value(rblk);
++		FK_W ("max_port_events = %u\n", max_port_events);
++        free (rblk);
++	}
++    renamed_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
++      NULL, NULL);
++    if (renamed_hash == NULL) {
++		FK_W ("[kernel] FEN global renamed queue initializing faild\n");
++        return FALSE;
++    }
++    if ((g_eventq = g_queue_new ()) == NULL) {
++		FK_W ("[kernel] FEN global event queue initializing faild\n");
++        return FALSE;
++    }
++    if (user_process_events_callback == NULL) {
++		FK_W ("[kernel] FEN global no user_process_events_callback\n");
++        return FALSE;
++    }
++    user_process_events_cb = user_process_events_callback;
++    memset (&zero_wait, 0, sizeof (timespec_t));
++
++    pevents = g_malloc(PE_ALLOC * sizeof(port_event_t));
++    if (pevents == NULL) {
++		FK_W ("[kernel] FEN global alloc pevents failed\n");
++        return FALSE;
++    }
++
++	return TRUE;
++}
++
++static gchar*
++printevent (const char *pname, int event, const char *tag)
++{
++    static gchar	*event_string = NULL;
++    GString			*str;
++
++    if (event_string) {
++        g_free(event_string);
++    }
++
++    str = g_string_new ("");
++    g_string_printf (str, "[%s] [%-20s]", tag, pname);
++    if (event & FILE_ACCESS) {
++        str = g_string_append (str, " ACCESS");
++    }
++    if (event & FILE_MODIFIED) {
++        str = g_string_append (str, " MODIFIED");
++    }
++    if (event & FILE_ATTRIB) {
++        str = g_string_append (str, " ATTRIB");
++    }
++    if (event & FILE_DELETE) {
++        str = g_string_append (str, " DELETE");
++    }
++    if (event & FILE_RENAME_TO) {
++        str = g_string_append (str, " RENAME_TO");
++    }
++    if (event & FILE_RENAME_FROM) {
++        str = g_string_append (str, " RENAME_FROM");
++    }
++    if (event & UNMOUNTED) {
++        str = g_string_append (str, " UNMOUNTED");
++    }
++    if (event & MOUNTEDOVER) {
++        str = g_string_append (str, " MOUNTEDOVER");
++    }
++    event_string = str->str;
++    g_string_free (str, FALSE);
++    return event_string;
++}
++
++static gchar *
++_event_strings(int event)
++{
++    GString *str = g_string_sized_new(80);
++
++    if (event & FILE_DELETE)
++        g_string_append(str, " FILE_DELETE");
++
++    if (event & FILE_RENAME_FROM)
++        g_string_append(str, " FILE_RENAME_FROM");
++
++    if (event & FILE_MODIFIED)
++        g_string_append(str, " FILE_MODIFIED");
++
++    if (event & FILE_RENAME_TO)
++        g_string_append(str, " FILE_RENAME_TO");
++
++    if (event & MOUNTEDOVER)
++        g_string_append(str, " MOUNTEDOVER");
++
++    if (event & FILE_ATTRIB)
++        g_string_append(str, " FILE_ATTRIB");
++
++    if (event & UNMOUNTED)
++        g_string_append(str, " UNMOUNTED");
++
++    if (event & FILE_ACCESS)
++        g_string_append(str, " FILE_ACCESS");
++
++    return g_string_free(str, FALSE);
++}
++
++static const gchar *
++_event_string (int event)
++{
++    switch (event) {
++    case FILE_DELETE:
++        return "FILE_DELETE";
++    case FILE_RENAME_FROM:
++        return "FILE_RENAME_FROM";
++    case FILE_MODIFIED:
++        return "FILE_MODIFIED";
++    case FILE_RENAME_TO:
++        return "FILE_RENAME_TO";
++    case MOUNTEDOVER:
++        return "MOUNTEDOVER";
++    case FILE_ATTRIB:
++        return "FILE_ATTRIB";
++    case UNMOUNTED:
++        return "UNMOUNTED";
++    case FILE_ACCESS:
++        return "FILE_ACCESS";
++    default:
++        return "EVENT_UNKNOWN";
++    }
++}
++
+diff --git a/server/fen-kernel.h b/server/fen-kernel.h
+new file mode 100644
+index 0000000..6d2c49b
+--- /dev/null
++++ b/server/fen-kernel.h
[email protected]@ -0,0 +1,43 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#include <sys/types.h>
++#include <errno.h>
++
++#include "fen-node.h"
++
++#ifndef _FEN_KERNEL_H_
++#define _FEN_KERNEL_H_
++
++#define CONCERNED_EVENTS (FILE_MODIFIED | FILE_ATTRIB | FILE_NOFOLLOW)
++#define EXCEPTION_EVENTS (FILE_DELETE | FILE_RENAME_FROM)
++#define HAS_EXCEPTION_EVENTS(e) ((e & EXCEPTION_EVENTS) != 0)
++#define HAS_NO_EXCEPTION_EVENTS(e) ((e & EXCEPTION_EVENTS) == 0)
++
++gint port_add (node_t* f);
++void port_remove (node_t *f);
++
++gboolean port_class_init ();
++
++#endif /* _FEN_KERNEL_H_ */
+diff --git a/server/fen-node.c b/server/fen-node.c
+new file mode 100644
+index 0000000..d4d7ddb
+--- /dev/null
++++ b/server/fen-node.c
[email protected]@ -0,0 +1,642 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#include "config.h"
++#include <sys/stat.h>
++#include <errno.h>
++#include <strings.h>
++#include <glib.h>
++#include "fen-kernel.h"
++#include "fen-node.h"
++#include "fen-dump.h"
++
++#ifdef GIO_COMPILATION
++#include "gfilemonitor.h"
++#else
++#include "gam_event.h"
++#include "gam_server.h"
++#include "gam_protocol.h"
++#endif
++
++#ifdef GIO_COMPILATION
++#define FN_W if (fn_debug_enabled) g_debug
++static gboolean fn_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FN_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++G_LOCK_EXTERN (fen_lock);
++
++/* Must continue monitoring if:
++ * 1) I'm subscribed,
++ * 2) The subscribed children (one of the children has subs) are missing,
++ * 3) my parent is subscribed (monitoring directory).
++ */
++#define NODE_NEED_MONITOR(f)                                            \
++    (NODE_IS_ACTIVE(f) || node_children_num(f) > 0 || NODE_IS_REQUIRED_BY_PARENT(f))
++
++static int concern_events[] = {
++    FILE_DELETE,
++    FILE_RENAME_FROM,
++    UNMOUNTED,
++    MOUNTEDOVER,
++#ifdef GIO_COMPILATION
++    FILE_MODIFIED,
++    FILE_ATTRIB,
++#else
++    FILE_MODIFIED | FILE_ATTRIB,
++#endif
++    FILE_RENAME_TO,
++};
++
++node_t *ROOT = NULL;
++
++static void node_emit_one_event(node_t *f, GList *subs, node_t *other, int event);
++static void node_emit_events(node_t *f, const node_event_t *ne);
++static int node_event_translate(int event, gboolean pair);
++static void node_add_event (node_t *f, node_event_t *ev);
++static node_t* node_new (node_t* parent, const gchar* basename);
++static void node_delete (node_t* parent);
++static node_t* node_get_child (node_t *f, const gchar *basename);
++static void children_add (node_t *p, node_t *f);
++static void children_remove (node_t *p, node_t *f);
++static gboolean children_remove_cb (gpointer key, gpointer value, gpointer user_data);
++static guint node_children_num (node_t *f);
++
++gboolean
++node_timeval_lt(const GTimeVal *val1, const GTimeVal *val2)
++{
++    if (val1->tv_sec < val2->tv_sec)
++        return TRUE;
++  
++    if (val1->tv_sec > val2->tv_sec)
++        return FALSE;
++  
++    /* val1->tv_sec == val2->tv_sec */
++    if (val1->tv_usec < val2->tv_usec)
++        return TRUE;
++  
++    return FALSE;
++}
++
++void
++node_traverse (node_t* node, void(*traverse_cb)(node_t*, gpointer), gpointer user_data)
++{
++    GHashTableIter iter;
++    gpointer value;
++
++    g_assert(traverse_cb);
++    if (node == NULL) {
++        node = ROOT;
++    }
++
++    if (node) {
++        traverse_cb(node, user_data);
++    }
++
++    g_hash_table_iter_init (&iter, node->children);
++    while (g_hash_table_iter_next (&iter, NULL, &value)) {
++        node_traverse((node_t *)value, traverse_cb, user_data);
++    }
++}
++
++node_t*
++node_find(node_t* node, const gchar* filename, gboolean create_on_missing)
++{
++    gchar* str;
++    gchar* token;
++    gchar* lasts;
++    node_t* parent;
++    node_t* child;
++    
++    g_assert (filename && filename[0] == '/');
++
++    if (node == NULL) {
++        node = ROOT;
++    }
++    
++    FN_W ("%s %s\n", __func__, filename);
++
++    parent = child = node;
++    str = g_strdup (filename);
++    
++    for (token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts);
++         token != NULL && child != NULL;
++         token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) {
++        FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
++        child = node_get_child(parent, token);
++        if (child) {
++            parent = child;
++        } else if (create_on_missing) {
++            child = node_new (parent, token);
++            if (child) {
++                children_add (parent, child);
++                parent = child;
++                continue;
++            } else {
++                FN_W ("%s create %s failed", __func__, token);
++            }
++        } else {
++            break;
++        }
++    }
++    
++    g_free (str);
++    return child;
++}
++
++gint
++node_lstat(node_t *f)
++{
++    struct stat buf;
++
++    g_assert(!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED));
++
++    if (lstat(NODE_NAME(f), &buf) == 0) {
++        FN_W ("%s %s\n", __func__, NODE_NAME(f));
++        FILE_OBJECT(f)->fo_atime = buf.st_atim;
++        FILE_OBJECT(f)->fo_mtime = buf.st_mtim;
++        FILE_OBJECT(f)->fo_ctime = buf.st_ctim;
++        NODE_SET_FLAG(f, NODE_FLAG_STAT_UPDATED |
++          (S_ISDIR (buf.st_mode) ? NODE_FLAG_DIR : NODE_FLAG_NONE));
++        return 0;
++    } else {
++        FN_W ("%s(lstat) %s %s\n", __func__, NODE_NAME(f), g_strerror (errno));
++    }
++    return errno;
++}
++
++void
++node_create_children_snapshot(node_t *f, gint created_event, gboolean emit)
++{
++	GDir *dir;
++	GError *err = NULL;
++    
++    FN_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
++
++    dir = g_dir_open (NODE_NAME(f), 0, &err);
++    if (dir) {
++        const char *basename;
++        node_t *child = NULL;
++        
++        while ((basename = g_dir_read_name (dir))) {
++            node_t* data;
++            GList *idx;
++
++            child = node_get_child (f, basename);
++            if (child == NULL) {
++                gchar *filename;
++            
++                child = node_new (f, basename);
++                children_add (f, child);
++            }
++
++            if (f->dir_subs) {
++                /* We need monitor the new children, or the existed child which
++                 * is in the DELETED mode.
++                 */
++                if (!NODE_HAS_STATE(child, NODE_STATE_ASSOCIATED) &&
++                  node_lstat(child) == 0 && port_add(child) == 0) {
++                    if (emit) {
++                        /* Emit the whatever event for the new found file. */
++                        node_emit_one_event(child, child->dir_subs, NULL, created_event);
++                        node_emit_one_event(child, child->subs, NULL, created_event);
++                        node_emit_one_event(child, f->dir_subs, NULL, created_event);
++                        node_emit_one_event(child, f->subs, NULL, created_event);
++                    }
++                }
++                /* else ignore, because it may be deleted. */
++            }
++        }
++        g_dir_close (dir);
++
++        /* We have finished children snapshot. Any other new added subs should
++         * directory iterate the snapshot instead of scan directory again.
++         */
++        NODE_SET_FLAG(f, NODE_FLAG_SNAPSHOT_UPDATED);
++
++    } else {
++        FN_W (err->message);
++        g_error_free (err);
++    }
++}
++
++/**
++ * If all active children nodes are ported, then cancel monitor the parent
++ * node. If we know how many children are created, then we can stop accordingly.
++ *
++ * Unsafe, need lock. 
++ */
++static void
++foreach_known_children_scan(gpointer key, gpointer value, gpointer user_data)
++{
++    node_t* f = (node_t*)value;
++    
++    FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
++
++    if (!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)) {
++        if (node_lstat(f) == 0 && port_add(f) == 0) {
++            node_emit_one_event(f, f->dir_subs, NULL, FN_EVENT_CREATED);
++            node_emit_one_event(f, f->subs, NULL, FN_EVENT_CREATED);
++            if (NODE_PARENT(f)) {
++                node_emit_one_event(f, NODE_PARENT(f)->dir_subs, NULL, FN_EVENT_CREATED);
++                node_emit_one_event(f, NODE_PARENT(f)->subs, NULL, FN_EVENT_CREATED);
++            }
++        }
++    }
++}
++
++gboolean
++node_try_delete(node_t* node)
++{
++    g_assert (node);
++
++    FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
++
++    /* Try clean children */
++    if (node_children_num (node) > 0) {
++        g_hash_table_foreach_remove(node->children, children_remove_cb, NULL);
++    }
++    if (!NODE_NEED_MONITOR(node)) {
++        /* Clean some flags. */
++        /* NODE_CLE_FLAG(node, NODE_FLAG_HAS_SNAPSHOT | NODE_FLAG_STAT_DONE); */
++        node->flag = 0;
++
++        /* Now we handle the state. */
++        if (NODE_HAS_STATE(node, NODE_STATE_ASSOCIATED)) {
++            port_remove(node);
++        }
++        /* Actually ignore the ROOT node. */
++        if (node->state == 0 && NODE_PARENT(node)) {
++            children_remove(NODE_PARENT(node), node);
++            /* Do clean instead of returning TRUE. */
++            node_delete (node);
++        }
++        /* else, we have events, clean event queue? */
++    }
++    return FALSE;
++}
++
++static node_t*
++node_new (node_t* parent, const gchar* basename)
++{
++	node_t *f = NULL;
++
++    g_assert (basename && basename[0]);
++
++    if ((f = g_new0(node_t, 1)) != NULL) {
++        if (parent) {
++            NODE_NAME(f) = g_build_filename(NODE_NAME(parent), basename, NULL);
++        } else {
++            NODE_NAME(f) = g_strdup(G_DIR_SEPARATOR_S);
++        }
++        f->basename = g_strdup (basename);
++        /* f->children = g_hash_table_new_full (g_str_hash, g_str_equal, */
++        /*   NULL, (GDestroyNotify)node_delete); */
++        f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
++          NULL, NULL);
++#ifdef GIO_COMPILATION
++        f->gfile = g_file_new_for_path (NODE_NAME(f));
++#endif
++        FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
++    }
++	return f;
++}
++
++static void
++node_delete (node_t *f)
++{
++    FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
++    g_assert(f->state == 0);
++    g_assert(!NODE_IS_ACTIVE(f));
++    g_assert(g_hash_table_size (f->children) == 0);
++    g_assert(NODE_PARENT(f) == NULL);
++    g_hash_table_unref(f->children);
++#ifdef GIO_COMPILATION
++    g_object_unref (f->gfile);
++#endif
++    g_free(f->basename);
++    g_free(NODE_NAME(f));
++    g_free (f);
++}
++
++static void
++children_add (node_t *p, node_t *f)
++{
++    FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
++    g_hash_table_insert (p->children, f->basename, f);
++    NODE_PARENT(f) = p;
++}
++
++static void
++children_remove (node_t *p, node_t *f)
++{
++    FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
++    g_hash_table_steal (p->children, f->basename);
++    NODE_PARENT(f) = NULL;
++}
++
++static node_t *
++node_get_child (node_t *f, const gchar *basename)
++{
++    if (f->children) {
++        return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
++    }
++    return NULL;
++}
++
++static guint
++node_children_num (node_t *f)
++{
++    return g_hash_table_size (f->children);
++}
++
++/**
++ * depth first delete recursively
++ */
++static gboolean
++children_remove_cb (gpointer key, gpointer value, gpointer user_data)
++{
++    return node_try_delete ((node_t*)value);
++}
++
++gboolean
++node_class_init()
++{
++    ROOT = node_new (NULL, G_DIR_SEPARATOR_S);
++    if (ROOT == NULL) {
++        FN_W ("[node] Create ROOT node failed.\n");
++        return FALSE;
++    }
++
++    return port_class_init (node_add_event);
++}
++
++/**
++ * Adjust self on failing to Port
++ */
++void
++node_adjust_deleted(node_t* f)
++{
++    node_t *ancestor;
++
++    FN_W ("%s %s\n", __func__, NODE_NAME(f));
++
++    for (ancestor = NODE_PARENT(f);
++         ancestor != NULL;
++         ancestor = NODE_PARENT(ancestor)) {
++        /* Stop if we find a node which been already associated or is existed
++         * and can be associated.
++         */
++        if (NODE_HAS_STATE(ancestor, NODE_STATE_ASSOCIATED) ||
++          (node_lstat(ancestor) == 0 && port_add(ancestor) == 0)) {
++            break;
++        }
++    }
++
++    /* We assume we shouldn't reach here, because Root is always existed and
++     * associated. But given bugster#6955199, if PORT FS has problems on root,
++     * we may reach here. So just return ROOT and the whole GIO fen backend will
++     * fail.
++     */
++    /* g_assert(ancestor != NULL); */
++}
++
++
++static void
++node_emit_events(node_t *f, const node_event_t *ne)
++{
++    gsize num = sizeof(concern_events)/sizeof(int);
++    gint i;
++    int translated_e;
++    node_t *p;
++
++    if (node_timeval_lt(&f->atv, &ne->ctv)) {
++        int event = ne->e;
++
++        /* Emit DELETED on the pair_data */
++        if (ne->pair_data) {
++            node_t *from = ne->pair_data;
++            node_emit_one_event(from, from->dir_subs, NULL, node_event_translate(FILE_DELETE, FALSE));
++            node_emit_one_event(from, from->subs, NULL, node_event_translate(FILE_DELETE, FALSE));
++        }
++
++        for (i = 0; i < num; i++) {
++            if (event & concern_events[i]) {
++                translated_e = node_event_translate(concern_events[i], FALSE);
++                /* Neither GIO or gamin cares about modified events on a
++                 * directory.
++                 */
++#ifdef GIO_COMPILATION
++                if ((concern_events[i] & FILE_MODIFIED) == 0) {
++                    node_emit_one_event(f, f->dir_subs, NULL, translated_e);
++                }
++#else
++                /* Gamin doesn't care about attrib changed events on a directory
++                 * either.
++                 */
++                if ((concern_events[i] & (FILE_MODIFIED | FILE_ATTRIB)) == 0) {
++                    node_emit_one_event(f, f->dir_subs, NULL, translated_e);
++                }
++#endif
++                node_emit_one_event(f, f->subs, NULL, translated_e);
++            }
++            event &= ~concern_events[i];
++        }
++    }
++
++    p = NODE_PARENT(f);
++    if (p != NULL && node_timeval_lt(&p->atv, &ne->ctv)) {
++        int event = ne->e;
++        for (i = 0; i < num; i++) {
++            if (event & concern_events[i]) {
++                translated_e = node_event_translate(concern_events[i], ne->pair_data != NULL);
++                node_emit_one_event(f, p->dir_subs, ne->pair_data, translated_e);
++                node_emit_one_event(f, p->subs, ne->pair_data, translated_e);
++            }
++            event &= ~concern_events[i];
++        }
++    }
++}
++
++/**
++ * node_add_event:
++ *
++ */
++static void
++node_add_event (node_t *f, node_event_t *ev)
++{
++    FN_W ("%s %d\n", __func__, ev->e);
++
++    /* Clean the events flag early, because all received events need be
++     * processed in this function.
++     */
++    NODE_CLE_STATE(f, NODE_STATE_HAS_EVENTS);
++
++    /*
++     * Node the node has been created, so we can delete create event in
++     * optimizing. To reduce the statings, we add it to Port on discoving
++     * it then emit CREATED event. So we don't need to do anything here.
++     */
++    if (NODE_NEED_MONITOR(f)) {
++        if (HAS_NO_EXCEPTION_EVENTS(ev->e)) {
++            if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED) || port_add(f) == 0) {
++                if ((ev->e & FILE_MODIFIED) && NODE_HAS_FLAG(f, NODE_FLAG_DIR)) {
++                    if (f->dir_subs) {
++                        node_create_children_snapshot(f, FN_EVENT_CREATED, TRUE);
++                    } else {
++                        g_hash_table_foreach(f->children, foreach_known_children_scan, NULL);
++                    }
++                }
++            } else {
++                /* Emit delete event */
++                ev->e |= FILE_DELETE;
++
++                node_adjust_deleted(f);
++            }
++
++        } else {
++            node_adjust_deleted(f);
++        }
++
++        /* Send events to clients. */
++        node_emit_events (f, ev);
++        
++    } else {
++        /* Send events to clients. */
++        node_emit_events (f, ev);
++
++        node_try_delete(f);
++    }
++
++    if (ev->pair_data) {
++        node_t *from = ev->pair_data;
++        g_assert(ev->e == FILE_RENAME_TO);
++
++        if (NODE_NEED_MONITOR(from)) {
++            /* Clean the events flag, since it may block free this node. */
++            NODE_CLE_STATE(from, NODE_STATE_HAS_EVENTS);
++            node_adjust_deleted(from);
++        } else {
++            node_try_delete(from);
++        }
++    }
++
++    node_event_delete (ev);
++}
++
++static void
++node_emit_one_event(node_t *f, GList *subs, node_t *other, int event)
++{
++    GList* idx;
++    
++    FN_W ("%s %s %d\n", __func__, NODE_NAME(f), event);
++
++#ifdef GIO_COMPILATION
++    for (idx = subs; idx; idx = idx->next) {
++        g_file_monitor_emit_event(G_FILE_MONITOR(idx->data), f->gfile,
++          (other == NULL ? NULL : other->gfile), event);
++    }
++#else
++    for (idx = subs; idx; idx = idx->next) {
++        gam_server_emit_one_event(NODE_NAME(f), gam_subscription_is_dir(idx->data), event, idx->data, 1);
++    }
++#endif
++}
++
++static int
++node_event_translate(int event, gboolean pair)
++{
++#ifdef GIO_COMPILATION
++    switch (event) {
++    case FILE_DELETE:
++    case FILE_RENAME_FROM:
++        return G_FILE_MONITOR_EVENT_DELETED;
++    case UNMOUNTED:
++        return G_FILE_MONITOR_EVENT_UNMOUNTED;
++    case FILE_ATTRIB:
++        return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
++    case MOUNTEDOVER:
++    case FILE_MODIFIED:
++        return G_FILE_MONITOR_EVENT_CHANGED;
++    case FILE_RENAME_TO:
++        if (pair) {
++            return G_FILE_MONITOR_EVENT_MOVED;
++        } else {
++            return G_FILE_MONITOR_EVENT_CREATED;
++        }
++    default:
++        /* case FILE_ACCESS: */
++        g_assert_not_reached ();
++        return -1;
++    }
++#else
++    switch (event) {
++    case FILE_DELETE:
++    case FILE_RENAME_FROM:
++        return GAMIN_EVENT_DELETED;
++    case MOUNTEDOVER:
++    case UNMOUNTED:
++        return GAMIN_EVENT_CHANGED;
++    case FILE_RENAME_TO:
++        if (pair) {
++            return GAMIN_EVENT_MOVED;
++        } else {
++            return GAMIN_EVENT_CREATED;
++        }
++    default:
++        if (event & (FILE_ATTRIB | FILE_MODIFIED)) {
++            return GAMIN_EVENT_CHANGED;
++        }
++        /* case FILE_ACCESS: */
++        g_assert_not_reached ();
++        return -1;
++    }
++#endif
++}
++
++node_event_t*
++node_event_new (int event, gpointer user_data)
++{
++    node_event_t *ev;
++    
++    if ((ev = g_new (node_event_t, 1)) != NULL) {
++        g_assert (ev);
++        ev->e = event;
++        ev->user_data = user_data;
++        ev->pair_data = NULL;   /* For renamed file. */
++        /* Created timestamp */
++        g_get_current_time(&ev->ctv);
++        ev->rename_tv = ev->ctv;
++    }
++    return ev;
++}
++
++void
++node_event_delete (node_event_t* ev)
++{
++    g_free (ev);
++}
++
+diff --git a/server/fen-node.h b/server/fen-node.h
+new file mode 100644
+index 0000000..7e99032
+--- /dev/null
++++ b/server/fen-node.h
[email protected]@ -0,0 +1,104 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
++ * reserved.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#include <port.h>
++#include <gio/gio.h>
++
++#ifndef _FEN_NODE_H_
++#define _FEN_NODE_H_
++
++#ifdef GIO_COMPILATION
++#define FN_EVENT_CREATED G_FILE_MONITOR_EVENT_CREATED
++#else
++#define FN_EVENT_CREATED GAMIN_EVENT_CREATED
++#endif
++
++#define NODE_STATE_NONE       0x00000000
++#define NODE_STATE_ASSOCIATED 0x00000001 /* This is a confilct to NODE_FLAG_STAT_DONE */
++#define NODE_STATE_HAS_EVENTS 0x00000002
++
++#define NODE_FLAG_NONE             0x00000000
++#define NODE_FLAG_SNAPSHOT_UPDATED 0x00000001
++#define NODE_FLAG_DIR              0x00000002
++#define NODE_FLAG_STAT_UPDATED     0x00000004
++
++#define	NODE_CLE_STATE(f, st)  (f->state &= ~(st))
++#define	NODE_SET_STATE(f, st)  (f->state = ((f->state & ~(st)) | (st)))
++#define	NODE_HAS_STATE(f, st)  (f->state & (st))
++
++#define	NODE_CLE_FLAG(f, fl)  (f->flag &= ~(fl))
++#define	NODE_SET_FLAG(f, fl)  (f->flag = ((f->flag & ~(fl)) | (fl)))
++#define	NODE_HAS_FLAG(f, fl)  (f->flag & (fl))
++
++typedef struct node node_t;
++struct node
++{
++    file_obj_t  fobj;           /* Inherit from file_obj_t, must be the first. */
++    GSource    *source;
++    gchar      *basename;
++    guint32     state;
++	guint32     flag;
++    GTimeVal    atv;            /* Timestamp for the first added sub. */
++
++	/* the parent and children of node */
++    node_t *parent;
++    GHashTable *children; /* children in basename */
++
++	/* List of subscriptions monitoring this fdata/path */
++	GList *subs;
++	GList *dir_subs;
++
++#ifdef GIO_COMPILATION
++    GFile* gfile;
++#endif
++};
++
++#define FILE_OBJECT(f)                ((file_obj_t *)(f))
++#define NODE_NAME(f)                  (FILE_OBJECT(f)->fo_name)
++#define NODE_PARENT(f)                (((node_t *)f)->parent)
++#define	NODE_IS_ACTIVE(f)             (f->dir_subs || f->subs)
++#define	NODE_IS_REQUIRED_BY_PARENT(f) (NODE_PARENT(f) && NODE_PARENT(f)->dir_subs)
++
++gboolean node_timeval_lt(const GTimeVal *val1, const GTimeVal *val2);
++gboolean node_try_delete(node_t* node);
++void     node_traverse(node_t* node, void(*traverse_cb)(node_t*, gpointer), gpointer user_data);
++node_t*  node_find(node_t* node, const gchar* filename, gboolean create_on_missing);
++gint     node_lstat(node_t *f);
++void     node_create_children_snapshot(node_t *f, gint created_event, gboolean emit);
++void     node_adjust_deleted(node_t *f);
++gboolean node_class_init();
++
++typedef struct node_event
++{
++    int e;
++    gpointer user_data;
++    gpointer pair_data;
++    GTimeVal ctv;               /* Created timestamp */
++    GTimeVal rename_tv;         /* Possible rename timestamp */
++} node_event_t;
++
++node_event_t* node_event_new (int event, gpointer user_data);
++void node_event_delete (node_event_t* ev);
++
++#endif /* _FEN_NODE_H_ */
+diff --git a/server/gam_channel.c b/server/gam_channel.c
+index 56c3ea7..6db1692 100644
+--- a/server/gam_channel.c
++++ b/server/gam_channel.c
[email protected]@ -7,6 +7,12 @@
+ #include <sys/stat.h>
+ #include <sys/un.h>
+ #include <sys/uio.h>
++#if defined(sun)
++#include <string.h>
++#endif 
++#if defined(HAVE_UCRED_H)
++#include <ucred.h>
++#endif defined(HAVE_UCRED_H)
+ #include "gam_error.h"
+ #include "gam_connection.h"
+ #include "gam_channel.h"
[email protected]@ -101,6 +107,10 @@ gam_client_conn_check_cred(GIOChannel * source, int fd,
+     } cmsg;
+ #endif
+ 
++#if defined(HAVE_GETPEERUCRED)
++    ucred_t *creds;
++#endif
++
+     s_uid = getuid();
+ 
+ #if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
[email protected]@ -167,11 +177,25 @@ gam_client_conn_check_cred(GIOChannel * source, int fd,
+                       fd, cr_len, (int) sizeof(cr));
+             goto failed;
+         }
++#elif defined(HAVE_GETPEERUCRED)
++	if ((creds = (ucred_t *)malloc(ucred_size()))==(ucred_t *)NULL){
++            GAM_DEBUG(DEBUG_INFO,"Malloc failed for ucreds");
++	    goto failed;  
++	}
++
++	if (getpeerucred(fd, &creds)!=0){
++            GAM_DEBUG(DEBUG_INFO,"getpeerucred call failed");
++	    goto failed;
++	}
++	c_uid = ucred_getruid(creds);
++	c_gid = ucred_getrgid(creds);
++	c_pid = ucred_getpid(creds);
++	ucred_free(creds);
+ #elif defined(HAVE_CMSGCRED)
+ 	c_pid = cmsg.cred.cmcred_pid;
+ 	c_uid = cmsg.cred.cmcred_euid;
+ 	c_gid = cmsg.cred.cmcred_groups[0];
+-#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
++#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEERUCRED */
+         GAM_DEBUG(DEBUG_INFO,
+                   "Socket credentials not supported on this OS\n");
+         goto failed;
+diff --git a/server/gam_fen.c b/server/gam_fen.c
+new file mode 100644
+index 0000000..21476e1
+--- /dev/null
++++ b/server/gam_fen.c
[email protected]@ -0,0 +1,140 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++/*
++ * Design:
++ * A Solaris port has a resource limit of events (port_max_events) which 
++ * limits the number of objects (fds) that can be actively associated objects
++ * whith the port. The default is (65536), but can be changed.
++ *
++ * project.max-port-ids identify the max number of ports
++ * process.max-port-events identify the max objs of a port
++ * process.max-file-descriptor identify the max fds of a process
++ *
++ * For a user server process, process.max-file-descriptor seems a bottleneck.
++ * I will use a port list for monitor fds to avoid process.max-file-descriptor
++ * is greater than process.max-port-events.
++ */
++#include "config.h"
++#include "gam_error.h"
++#include "gam_fen.h"
++#include "gam_event.h"
++#include "gam_server.h"
++#include "gam_protocol.h"
++#include <glib.h>
++#include "fen-helper.h"
++
++/**
++ * Initializes the FEN system.  This must be called before
++ * any other functions in this module.
++ *
++ * @returns TRUE if initialization succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_init (void)
++{
++    if (!fen_init ())
++        return FALSE;
++	
++	gam_server_install_kernel_hooks (GAMIN_K_FEN,
++      gam_fen_add_subscription,
++      gam_fen_remove_subscription,
++      gam_fen_remove_all_for,
++      NULL, NULL);
++	return TRUE;
++}
++
++/**
++ * Adds a subscription to be monitored.
++ *
++ * @param sub a #GamSubscription to be polled
++ * @returns TRUE if adding the subscription succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_add_subscription (GamSubscription *sub)
++{
++    g_debug ("[ %s ] sub[0x%p]\n", __func__, sub);
++
++	gam_listener_add_subscription (gam_subscription_get_listener (sub), sub);
++    fen_add (gam_subscription_get_path(sub),
++      sub,
++      gam_subscription_is_dir (sub));
++	return TRUE;
++}
++
++/**
++ * Removes a subscription which was being monitored.
++ *
++ * @param sub a #GamSubscription to remove
++ * @returns TRUE if removing the subscription succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_remove_subscription (GamSubscription *sub)
++{
++    g_debug ("[ %s ] sub[0x%p]\n", __func__, sub);
++
++    fen_remove (gam_subscription_get_path(sub),
++      sub,
++      gam_subscription_is_dir (sub));
++	/* free subscription */
++    gam_subscription_cancel(sub);
++	gam_subscription_free(sub);
++	return TRUE;
++}
++
++/**
++ * Stop monitoring all subscriptions for a given listener.
++ *
++ * @param listener a #GamListener
++ * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_remove_all_for (GamListener *listener)
++{
++	GList *subs;
++	GList *idx;
++	gboolean success = TRUE;
++	
++	subs = gam_listener_get_subscriptions (listener);
++	
++	if (subs == NULL)
++		return FALSE;
++
++	for (idx = subs; idx != NULL; idx = idx->next) {
++		GamSubscription *sub = (GamSubscription *)idx->data;
++		g_assert (sub);
++		if (!gam_fen_remove_subscription (sub))
++			success = FALSE;
++	}
++	
++	if (subs) {
++		g_list_free (subs);
++		return TRUE;
++	} else {
++		return FALSE;
++	}
++}
+diff --git a/server/gam_fen.h b/server/gam_fen.h
+new file mode 100644
+index 0000000..47e952d
+--- /dev/null
++++ b/server/gam_fen.h
[email protected]@ -0,0 +1,40 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <[email protected]>
++ */
++
++#ifndef __GAM_FEN_H__
++#define __GAM_FEN_H__
++
++#include <glib.h>
++#include "gam_subscription.h"
++
++G_BEGIN_DECLS
++
++gboolean gam_fen_init (void);
++gboolean gam_fen_add_subscription (GamSubscription *sub);
++gboolean gam_fen_remove_subscription (GamSubscription *sub);
++gboolean gam_fen_remove_all_for (GamListener *listener);
++
++G_END_DECLS
++
++#endif /* __GAM_FEN_H__ */
+diff --git a/server/gam_fs.c b/server/gam_fs.c
+index c8ca704..1d2f2c8 100644
+--- a/server/gam_fs.c
++++ b/server/gam_fs.c
[email protected]@ -7,9 +7,20 @@
+ #include <string.h>
+ #include <errno.h>
+ #include <glib.h>
++#ifdef HAVE_SYS_MNTTAB_H
++#include <sys/mnttab.h>
++#endif
+ #include "gam_error.h"
+ #include "gam_fs.h"
+ 
++#ifdef HAVE_SYS_MNTTAB_H
++#define MTAB	MNTTAB
++#define MTABDEL	"\t"
++#else
++#define MTAB	"/etc/mtab"
++#define MTABDEL	"\t"
++#endif
++
+ #define DEFAULT_POLL_TIMEOUT 0
+ 
+ typedef struct _gam_fs_properties {
[email protected]@ -119,7 +130,7 @@ gam_fs_scan_mtab (void)
+ 	gam_fs *fs = NULL;
+ 	int i;
+ 
+-	g_file_get_contents ("/etc/mtab", &contents, &len, NULL);
++	g_file_get_contents (MTAB, &contents, &len, NULL);
+ 	if (contents == NULL)
+ 		return;
+ 
[email protected]@ -133,7 +144,7 @@ gam_fs_scan_mtab (void)
+ 			if (line[0] == '\0')
+ 				continue;
+ 
+-			words = g_strsplit (line, " ", 0);
++			words = g_strsplit (line, MTABDEL, 0);
+ 
+ 			if (words == NULL)
+ 				continue;
[email protected]@ -176,19 +187,25 @@ gam_fs_init (void)
+ 		gam_fs_set ("ext2", GFS_MT_DEFAULT, 0);
+ 		gam_fs_set ("reiser4", GFS_MT_DEFAULT, 0);
+ 		gam_fs_set ("reiserfs", GFS_MT_DEFAULT, 0);
++		gam_fs_set ("zfs", GFS_MT_DEFAULT, 0);
++		gam_fs_set ("ufs", GFS_MT_DEFAULT, 0);
+ 		gam_fs_set ("novfs", GFS_MT_POLL, 30);
++#ifdef ENABLE_FEN
++		gam_fs_set ("nfs", GFS_MT_DEFAULT, 0);
++#else
+ 		gam_fs_set ("nfs", GFS_MT_POLL, 5);
+-		if (stat("/etc/mtab", &mtab_sbuf) != 0)
++#endif
++		if (stat(MTAB, &mtab_sbuf) != 0)
+ 		{
+-			GAM_DEBUG(DEBUG_INFO, "Could not stat /etc/mtab\n");
++			GAM_DEBUG(DEBUG_INFO, "Could not stat %s\n",MTAB);
+ 		}
+ 		gam_fs_scan_mtab ();
+ 	} else {
+ 		struct stat sbuf;
+ 
+-		if (stat("/etc/mtab", &sbuf) != 0)
++		if (stat(MTAB, &sbuf) != 0)
+ 		{
+-			GAM_DEBUG(DEBUG_INFO, "Could not stat /etc/mtab\n");
++			GAM_DEBUG(DEBUG_INFO, "Could not stat %s\n",MTAB);
+ 		}
+ 
+ 		/* /etc/mtab has changed */
+diff --git a/server/gam_fs.h b/server/gam_fs.h
+index bc2d538..94e70fd 100644
+--- a/server/gam_fs.h
++++ b/server/gam_fs.h
[email protected]@ -8,6 +8,7 @@ typedef enum {
+ #if !defined(ENABLE_DNOTIFY) && \
+     !defined(ENABLE_INOTIFY) && \
+     !defined(ENABLE_KQUEUE) && \
++    !defined(ENABLE_FEN) && \
+     !defined(ENABLE_HURD_MACH_NOTIFY)
+ 	GFS_MT_DEFAULT = GFS_MT_POLL,
+ #else
+diff --git a/server/gam_server.c b/server/gam_server.c
+index f92a691..e5da29f 100644
+--- a/server/gam_server.c
++++ b/server/gam_server.c
[email protected]@ -45,6 +45,9 @@
+ #ifdef ENABLE_HURD_MACH_NOTIFY
+ #include "gam_hurd_mach_notify.h"
+ #endif
++#ifdef ENABLE_FEN
++#include "gam_fen.h"
++#endif
+ #include "gam_excludes.h"
+ #include "gam_fs.h"
+ #include "gam_conf.h" 
[email protected]@ -162,6 +165,12 @@ gam_init_subscriptions(void)
+ 			return(TRUE);
+ 		}
+ #endif	
++#ifdef ENABLE_FEN
++		if (gam_fen_init()) {
++			GAM_DEBUG(DEBUG_INFO, "Using fen as backend\n");
++			return(TRUE);
++		}
++#endif
+ 	}
+ 
+ 	if (gam_poll_basic_init()) {
[email protected]@ -627,6 +636,10 @@ main(int argc, const char *argv[])
+     signal(SIGQUIT, gam_exit);
+     signal(SIGTERM, gam_exit);
+     signal(SIGPIPE, SIG_IGN);
++#ifdef ENABLE_FEN
++    signal(SIGUSR1, SIG_IGN);
++    signal(SIGUSR2, SIG_IGN);
++#endif
+ 
+     if (!gam_init_subscriptions()) {
+ 	GAM_DEBUG(DEBUG_INFO, "Could not initialize the subscription system.\n");
+diff --git a/server/gam_server.h b/server/gam_server.h
+index bc99e09..313dd84 100644
+--- a/server/gam_server.h
++++ b/server/gam_server.h
[email protected]@ -16,7 +16,8 @@ typedef enum {
+ 	GAMIN_K_INOTIFY = 2,
+ 	GAMIN_K_KQUEUE = 3,
+ 	GAMIN_K_MACH = 4,
+-	GAMIN_K_INOTIFY2 = 5
++	GAMIN_K_INOTIFY2 = 5,
++	GAMIN_K_FEN = 6
+ } GamKernelHandler;
+ 
+ typedef enum {
+diff --git a/tests/testing.c b/tests/testing.c
+index 9926c0a..4c08740 100644
+--- a/tests/testing.c
++++ b/tests/testing.c
[email protected]@ -1,3 +1,4 @@
++#include "config.h"
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
[email protected]@ -31,6 +32,11 @@ static struct testState {
+ 
+ #define IS_BLANK(p) ((*(p) == ' ') || (*(p) == '\t') ||		\
+                      (*(p) == '\n') || (*(p) == '\r'))
++#ifdef ENABLE_FEN
++#define KILLCMD	"pkill"
++#else
++#define KILLCMD	"killall"
++#endif
+ 
+ static int
+ scanCommand(char *line, char **command, char **arg, char **arg2)
[email protected]@ -268,7 +274,7 @@ processCommand(char *line, int no)
+          * okay, it's heavy but that's the simplest way since we do not have
+          * the pid(s) of the servers running.
+          */
+-        ret = system("killall gam_server");
++        ret = system(KILLCMD" gam_server");
+         if (ret < 0) {
+             fprintf(stderr, "kill line %d: failed to killall gam_server\n",
+                     no);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/patches/gamin-02-gamin.patch	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,13 @@
+expilcitly use python2.7 in dlpitest.py privrbactest.py ucredtest.py gami.py
+
+http://hg.osdyson.org/solaris-desktop-spec-files/raw-file/8f7c0cd200a9/patches/gamin-02-gamin.diff
+https://java.net/projects/solaris-desktop/sources/spec-files/history?path=patches/gamin-02-gamin.diff
+
+--- a/python/gamin.py.orig	2009-12-04 13:19:30.527419772 +0800
++++ b/python/gamin.py	2009-12-04 13:19:43.189517582 +0800
[email protected]@ -1,4 +1,4 @@
+-#!/usr/bin/env python
++#!/usr/bin/python2.7
+ 
+ import _gamin
+ import os.path
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/patches/gamin-03-const.patch	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,52 @@
+Fix compilation of recent glib removing G_CONST_RETURN
+
+https://bugzilla.gnome.org/show_bug.cgi?id=658884
+
+diff -ur gamin-0.1.10/server.orig/gam_node.c gamin-0.1.10/server/gam_node.c
+--- gamin-0.1.10/server.orig/gam_node.c	Mon Aug 26 15:25:23 2013
++++ gamin-0.1.10/server/gam_node.c	Mon Aug 26 15:25:54 2013
[email protected]@ -122,7 +122,7 @@
+  * it has finished with the string.  If it must keep it longer, it
+  * should makes its own copy.  The returned string must not be freed.
+  */
+-G_CONST_RETURN char *
++const char *
+ gam_node_get_path(GamNode * node)
+ {
+     g_assert(node);
+diff -ur gamin-0.1.10/server.orig/gam_node.h gamin-0.1.10/server/gam_node.h
+--- gamin-0.1.10/server.orig/gam_node.h	Mon Aug 26 15:25:22 2013
++++ gamin-0.1.10/server/gam_node.h	Mon Aug 26 15:25:54 2013
[email protected]@ -58,7 +58,7 @@
+ void                  gam_node_set_is_dir          (GamNode         *node,
+ 						   gboolean        is_dir);
+ 	
+-G_CONST_RETURN char  *gam_node_get_path            (GamNode         *node);
++const char  *gam_node_get_path            (GamNode         *node);
+ 
+ GList                *gam_node_get_subscriptions   (GamNode         *node);
+ 
+diff -ur gamin-0.1.10/server.orig/gam_subscription.c gamin-0.1.10/server/gam_subscription.c
+--- gamin-0.1.10/server.orig/gam_subscription.c	Mon Aug 26 15:25:22 2013
++++ gamin-0.1.10/server/gam_subscription.c	Mon Aug 26 15:25:54 2013
[email protected]@ -141,7 +141,7 @@
+  * @param sub the GamSubscription
+  * @returns The path being monitored.  It should not be freed.
+  */
+-G_CONST_RETURN char *
++const char *
+ gam_subscription_get_path(GamSubscription * sub)
+ {
+     if (sub == NULL)
+diff -ur gamin-0.1.10/server.orig/gam_subscription.h gamin-0.1.10/server/gam_subscription.h
+--- gamin-0.1.10/server.orig/gam_subscription.h	Mon Aug 26 15:25:22 2013
++++ gamin-0.1.10/server/gam_subscription.h	Mon Aug 26 15:25:54 2013
[email protected]@ -21,7 +21,7 @@
+ 
+ int                  gam_subscription_get_reqno    (GamSubscription *sub);
+ 
+-G_CONST_RETURN char *gam_subscription_get_path     (GamSubscription *sub);
++const char *gam_subscription_get_path     (GamSubscription *sub);
+ 
+ GamListener         *gam_subscription_get_listener (GamSubscription *sub);
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gamin/test/results-32.master	Wed Aug 19 10:12:59 2015 +0200
@@ -0,0 +1,88 @@
+make[1]: Entering directory `$(@D)'
+Making check in lib
+make[2]: Entering directory `$(@D)/lib'
+make[2]: Nothing to be done for `check'.
+make[2]: Leaving directory `$(@D)/lib'
+Making check in doc
+make[2]: Entering directory `$(@D)/doc'
+make[2]: Nothing to be done for `check'.
+make[2]: Leaving directory `$(@D)/doc'
+Making check in libgamin
+make[2]: Entering directory `$(@D)/libgamin'
+make[2]: Nothing to be done for `check'.
+make[2]: Leaving directory `$(@D)/libgamin'
+Making check in python
+make[2]: Entering directory `$(@D)/python'
+Making check in tests
+make[3]: Entering directory `$(@D)/python/tests'
+/usr/gnu/bin/make  check-local
+make[4]: Entering directory `$(@D)/python/tests'
+basic.py
+basic2.py
+basic3.py
+basic4.py
+basic5.py
+basic6.py
+bigfile.py
+noexists.py
+dnotify.py
+dnotify2.py
+dnotify3.py
+dnotify4.py
+dnotify5.py
+dnotify6.py
+dnotify7.py
+dnotify8.py
+dnotify9.py
+dnotify10.py
+dnotify11.py
+dnotify12.py
+dnotify13.py
+dnotify15.py
+flood.py
+flood2.py
+flood3.py
+flood4.py
+level.py
+multiple.py
+multiple2.py
+multiple3.py
+nokernel.py
+readonly.py
+make[4]: Leaving directory `$(@D)/python/tests'
+make[3]: Leaving directory `$(@D)/python/tests'
+make[3]: Entering directory `$(@D)/python'
+make[3]: Nothing to be done for `check-am'.
+make[3]: Leaving directory `$(@D)/python'
+make[2]: Leaving directory `$(@D)/python'
+Making check in lib
+make[2]: Entering directory `$(@D)/lib'
+make[2]: Nothing to be done for `check'.
+make[2]: Leaving directory `$(@D)/lib'
+Making check in server
+make[2]: Entering directory `$(@D)/server'
+/usr/gnu/bin/make  check-am
+make[3]: Entering directory `$(@D)/server'
+make[3]: Nothing to be done for `check-am'.
+make[3]: Leaving directory `$(@D)/server'
+make[2]: Leaving directory `$(@D)/server'
+Making check in tests
+make[2]: Entering directory `$(@D)/tests'
+/usr/gnu/bin/make  check-local
+make[3]: Entering directory `$(@D)/tests'
+running test 1
+running test 10
+running test 11
+running test 2
+running test 3
+running test 4
+running test 5
+running test 6
+running test 7
+running test 8
+running test 9
+make[3]: Leaving directory `$(@D)/tests'
+make[2]: Leaving directory `$(@D)/tests'
+make[2]: Entering directory `$(@D)'
+make[2]: Leaving directory `$(@D)'
+make[1]: Leaving directory `$(@D)'
--- a/make-rules/prep.mk	Thu Aug 20 14:26:17 2015 -0700
+++ b/make-rules/prep.mk	Wed Aug 19 10:12:59 2015 +0200
@@ -43,6 +43,7 @@
 prep::	$(SOURCE_DIR)/.prep
 
 clean::
+	$(PRE_CLEAN_ACTION)
 	$(RM) -r $(CLEAN_PATHS)
 
 clobber::	clean