summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Heim <phreak@gentoo.org>2005-12-02 10:56:06 +0000
committerChristian Heim <phreak@gentoo.org>2005-12-02 10:56:06 +0000
commita14ee4ab9b6b622475c9520fb3d08e6f78b08881 (patch)
treeff48cc3f5de2918ab569e3cb391db1ec883a35eb /src
parentImport the latest baselayout changes. Merging revision 1648. (diff)
downloadbaselayout-vserver-a14ee4ab9b6b622475c9520fb3d08e6f78b08881.tar.gz
baselayout-vserver-a14ee4ab9b6b622475c9520fb3d08e6f78b08881.tar.bz2
baselayout-vserver-a14ee4ab9b6b622475c9520fb3d08e6f78b08881.zip
Import the latest baselayout changes. Merging revision 1658.
svn path=/baselayout-vserver/trunk/; revision=127
Diffstat (limited to 'src')
-rw-r--r--src/core/COPYING340
-rw-r--r--src/core/ChangeLog234
-rw-r--r--src/core/ChangeLog.0234
-rw-r--r--src/core/INSTALL236
-rw-r--r--src/core/Makefile83
-rw-r--r--src/core/Makefile.am10
-rw-r--r--src/core/autogen.sh16
-rw-r--r--src/core/configure.ac66
-rw-r--r--src/core/data/Makefile.am4
-rw-r--r--src/core/data/env_whitelist30
-rw-r--r--src/core/debug.h85
-rw-r--r--src/core/depend.c557
-rw-r--r--src/core/depend.h75
-rw-r--r--src/core/depscan.c297
-rw-r--r--src/core/librcscripts/Makefile.am18
-rw-r--r--src/core/librcscripts/debug.h85
-rw-r--r--src/core/librcscripts/depend.c557
-rw-r--r--src/core/librcscripts/depend.h75
-rw-r--r--src/core/librcscripts/list.h446
-rw-r--r--src/core/librcscripts/misc.c659
-rw-r--r--src/core/librcscripts/misc.h282
-rw-r--r--src/core/librcscripts/parse.c982
-rw-r--r--src/core/librcscripts/parse.h100
-rw-r--r--src/core/librcscripts/rcscripts.h54
-rw-r--r--src/core/librcscripts/simple-regex.c827
-rw-r--r--src/core/librcscripts/simple-regex.h86
-rw-r--r--src/core/list.h446
-rw-r--r--src/core/misc.c659
-rw-r--r--src/core/misc.h282
-rw-r--r--src/core/parse.c981
-rw-r--r--src/core/parse.h109
-rw-r--r--src/core/scripts/Makefile.am3
-rw-r--r--src/core/scripts/clean.sh17
-rw-r--r--src/core/scripts/svn2cl.sh130
-rw-r--r--src/core/scripts/svn2cl.xsl295
-rw-r--r--src/core/simple-regex.c827
-rw-r--r--src/core/simple-regex.h86
-rw-r--r--src/core/src/Makefile.am17
-rw-r--r--src/core/src/depscan.c298
-rw-r--r--src/core/src/runscript.c237
-rw-r--r--src/core/test-regex.c80
-rw-r--r--src/core/tests/Makefile.am1
-rw-r--r--src/core/tests/test-regex.c80
43 files changed, 6185 insertions, 4801 deletions
diff --git a/src/core/COPYING b/src/core/COPYING
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/src/core/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, 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 software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, 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 redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), 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 Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/core/ChangeLog b/src/core/ChangeLog
index 5d0754a..e69de29 100644
--- a/src/core/ChangeLog
+++ b/src/core/ChangeLog
@@ -1,234 +0,0 @@
-# ChangeLog for Gentoo System Intialization core utilities.
-# Copyright 1999-2005 Gentoo Foundation; Distributed under the GPLv2
-# $Header$
-
-20 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * parse.c: Hopefully handle interrupted reads/writes properly.
-
-13 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * parse.c: Cleanup the pipe usage and naming in generate_stage2() for
- better readibility.
-
- * parse.c: Remove unused tmp_pid from generate_stage2(), also fixing
- waitpid() not getting called.
-
- * parse.c: Make sure we do not close the pipes twice if the child
- returns with error exit status.
-
- * parse.c: Remove label 'cont_do_read' in favour of just a simple break.
- No need to jump to the error label if the child did not exit cleanly,
- as we already cleaned up.
-
- * parse.c: Cleanup parse_cache(). Do not parse MTIME - we have it
- already.
-
-07 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * debug.h
- * depend.c
- * misc.c
- * misc.h
- * parse.c
- * simple-regex.c
- * simple-regex.h: Misc style and other cleanups.
-
- * TODO: New file.
-
-06 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * parse.c
- * misc.c: Try to cleanup error handling a bit (debug messages, return
- value, etc).
-
- * parse.c: Fix output of write_legacy_stage3() after removing the
- parallel dependency type.
-
- * Makefile: Do not enable bounds checking with DEBUG=1.
-
- * misc.h: Add MIN/MAX macro's.
-
- * parse.c: Cleanup and merge parse_print_*() functions. Move need(),
- etc functions to after sourcing of script, else we run into issues
- with net.* scripts that redefine them. Better checking if scripts
- have issues or not.
-
- * parse.c: Use poll() rather than select() and some other cleanups.
-
- * parse.c: Remove the waitpid() call in the read/write loop of our
- generate_stage2(), as its very slow on amd64.
-
- * parse.c: Remove some redundent variables and other cleanups.
-
- * parse.c: Reset tmp_count between writing and reading.
-
- * depend.c: Also add mtime for 'net' service if we need to add it.
-
-05 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * parse.c: Make sure field in parse_cache() is not used uninitialized.
-
- * misc.h: Remove the unused COUNT_CHAR_* macro's.
-
- * depend.c: Add missing debugging info.
-
-26 Jul 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * depend.c
- * depend.h
- * parse.c
- * parse.h: Remove the "parallel" stuff, as we do not use it anymore.
-
-15 Apr 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * parse.c: Do not source rc.conf for every script - once is enough.
-
-14 Apr 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * depscan.c: Update error comments for stage name changes some time
- back.
-
- * parse.c,
- * parse.h: Do not try to extract the depend() function from the
- scripts, but rather source the whole file. This way we can detect
- syntax errors, etc. Little bit slower, but not much.
-
-07 Apr 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * test-regex.c: Add two more tests.
-
- * depscan.c
- * misc.c
- * misc.h: Add basic klibc support. I need to add a mkstemp
- implementation to get it done properly.
-
-12 Mar 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * Makefile: Also remove the tests in the clean target.
-
-11 Mar 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * test-regex.c: Add a few strings and patterns. Enable tests to
- specify if they should fail or pass.
-
-10 Mar 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * test-regex.c: New file
- * Makefile: Add check target to compile and run tests
- * simple-regex.c (__match_wildcard): Get recursion right so that we
- do not match a wildcard _and_ inc data_p if there are still other
- wildcards (?, *) that could match.
-
- * Makefile
- * depend.c
- * simple-regex.c: Override the debug/warning CFLAGS. Kill a few
- warnings.
-
-23 Feb 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * misc.c: Fix memory leak in mktree().
-
-18 Feb 2005 Martin Schlemmer <azarah@gentoo.org>
-
- * Makefile: Add -fbounds-checking support when DEBUG=1.
-
- * misc.h: Scrap STRING_LIST_FOR_EACH_SAFE() and recode from scratch
- fixing invalid pointer operations.
-
- * misc.c: Remove the last strlen() from strndup() that caused an
- overrun.
-
-17 Feb 2005 Martin Schlemmer <azarah@gentoo.org>:
-
- * misc.c: Fix overrun in strndup(), thanks to report from
- Ned Ludd <solar@gentoo.org>.
-
- * debug.h
- * misc.h
- * simple-regex.c: Print debug/errors to stderr, patch from
- Ned Ludd <solar@gentoo.org>.
-
- * debug.h: Replace invalid EXIT_FAILSTATUS with EXIT_FAILURE.
-
- * parse.c: Modify parse_print_body() to be more ash friendly.
- Suggestions from Ned Ludd <solar@gentoo.org>.
-
- * debug.h: Remove the 'errno = ESPIPE' in DBG_MSG() for now, as it
- seems to be fixed by the select() changes.
-
- * parse.c: Disable write select() for now, as it is not needed.
-
- * depscan.c: Only print EINFO msg if we actually update the cache.
-
- * parse.c: Rename write_output() macro to PRINT_TO_BUFFER().
-
- * parse.c
- * parse.h: Rewrote large parts of generate_stage[12]() and their
- machanics to use select() when writing to the pipes. This fixes a
- buffering issue where too much data would cause the write to be
- truncated, and the read pipe would then wait forever.
-
- * misc.c: Fix gbasename() to compile under gcc-2.95.3.
-
- * parse.c: Switch to stdio based io for reading pipes in
- generate_stage2().
-
- * misc.c
- * misc.h: Add gbasename() that is similar to GNU's basename().
-
- * parse.c: Use gbasename() instead of POSIX version.
-
- * parse.c: Fix write_legacy_stage3() to quote the mtime in its output.
-
- * misc.c
- * parse.c: Change type of length from int to size_t to avoid warnings
- when compiled for darwin.
-
- * misc.c
- misc.h: Add strndup() instead of relying on glibc's implementation
- (should fix some issues on bsd and darwin).
-
- * depend.c
- * simple-regex.c: Do not define _GNU_SOURCE, but rather use our
- strndup() from misc.h.
-
- * parse.c: Do not define _GNU_SOURCE, but rather use our strndup() from
- misc.h. Also change all usage of basename() to conform to POSIX, and
- do not use the GNU variants.
-
-16 Feb 2005; Martin Schlemmer <azarah@gentoo.org>:
-
- * depscan.c: Add uid check and quit if user is not root.
-
- * depend.c
- * depend.h: Change service_type_names declaration in depend.h to extern
- and move the definition to depend.c to avoid warnings.
-
- * README: New file.
-
- * depscan.c: Add delete_var_dirs() to delete volatile directories in
- svcdir. Change 'char *' declarations for create_directory() and
- create_var_dirs() to 'const char*'.
-
- * misc.h
- * misc.c: Add rmtree() function. Fix ls_dir() not to include '.' and
- '..' in its listing. Fix segfault in ls_dir() if the file list is
- empty.
-
- Add Header tags to all source files and Makefile.
-
- * debug.h: perror() set errno to ESPIPE for some reason - restore errno
- after calling perror().
-
- * depscan.c: Add code to create svcdir and co if missing.
-
- * misc.c: Add missing '\n' to DBG_MSG in mktree().
-
- * misc.h: Add a comment about strcatpaths() allocating the memory needed.
-
- Initial checkin. Still rough in some parts, but should be 100% similar
- in output to depscan.sh and co.
-
-
-# vim:expandtab
diff --git a/src/core/ChangeLog.0 b/src/core/ChangeLog.0
new file mode 100644
index 0000000..5d0754a
--- /dev/null
+++ b/src/core/ChangeLog.0
@@ -0,0 +1,234 @@
+# ChangeLog for Gentoo System Intialization core utilities.
+# Copyright 1999-2005 Gentoo Foundation; Distributed under the GPLv2
+# $Header$
+
+20 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * parse.c: Hopefully handle interrupted reads/writes properly.
+
+13 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * parse.c: Cleanup the pipe usage and naming in generate_stage2() for
+ better readibility.
+
+ * parse.c: Remove unused tmp_pid from generate_stage2(), also fixing
+ waitpid() not getting called.
+
+ * parse.c: Make sure we do not close the pipes twice if the child
+ returns with error exit status.
+
+ * parse.c: Remove label 'cont_do_read' in favour of just a simple break.
+ No need to jump to the error label if the child did not exit cleanly,
+ as we already cleaned up.
+
+ * parse.c: Cleanup parse_cache(). Do not parse MTIME - we have it
+ already.
+
+07 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * debug.h
+ * depend.c
+ * misc.c
+ * misc.h
+ * parse.c
+ * simple-regex.c
+ * simple-regex.h: Misc style and other cleanups.
+
+ * TODO: New file.
+
+06 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * parse.c
+ * misc.c: Try to cleanup error handling a bit (debug messages, return
+ value, etc).
+
+ * parse.c: Fix output of write_legacy_stage3() after removing the
+ parallel dependency type.
+
+ * Makefile: Do not enable bounds checking with DEBUG=1.
+
+ * misc.h: Add MIN/MAX macro's.
+
+ * parse.c: Cleanup and merge parse_print_*() functions. Move need(),
+ etc functions to after sourcing of script, else we run into issues
+ with net.* scripts that redefine them. Better checking if scripts
+ have issues or not.
+
+ * parse.c: Use poll() rather than select() and some other cleanups.
+
+ * parse.c: Remove the waitpid() call in the read/write loop of our
+ generate_stage2(), as its very slow on amd64.
+
+ * parse.c: Remove some redundent variables and other cleanups.
+
+ * parse.c: Reset tmp_count between writing and reading.
+
+ * depend.c: Also add mtime for 'net' service if we need to add it.
+
+05 Sep 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * parse.c: Make sure field in parse_cache() is not used uninitialized.
+
+ * misc.h: Remove the unused COUNT_CHAR_* macro's.
+
+ * depend.c: Add missing debugging info.
+
+26 Jul 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * depend.c
+ * depend.h
+ * parse.c
+ * parse.h: Remove the "parallel" stuff, as we do not use it anymore.
+
+15 Apr 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * parse.c: Do not source rc.conf for every script - once is enough.
+
+14 Apr 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * depscan.c: Update error comments for stage name changes some time
+ back.
+
+ * parse.c,
+ * parse.h: Do not try to extract the depend() function from the
+ scripts, but rather source the whole file. This way we can detect
+ syntax errors, etc. Little bit slower, but not much.
+
+07 Apr 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * test-regex.c: Add two more tests.
+
+ * depscan.c
+ * misc.c
+ * misc.h: Add basic klibc support. I need to add a mkstemp
+ implementation to get it done properly.
+
+12 Mar 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * Makefile: Also remove the tests in the clean target.
+
+11 Mar 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * test-regex.c: Add a few strings and patterns. Enable tests to
+ specify if they should fail or pass.
+
+10 Mar 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * test-regex.c: New file
+ * Makefile: Add check target to compile and run tests
+ * simple-regex.c (__match_wildcard): Get recursion right so that we
+ do not match a wildcard _and_ inc data_p if there are still other
+ wildcards (?, *) that could match.
+
+ * Makefile
+ * depend.c
+ * simple-regex.c: Override the debug/warning CFLAGS. Kill a few
+ warnings.
+
+23 Feb 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * misc.c: Fix memory leak in mktree().
+
+18 Feb 2005 Martin Schlemmer <azarah@gentoo.org>
+
+ * Makefile: Add -fbounds-checking support when DEBUG=1.
+
+ * misc.h: Scrap STRING_LIST_FOR_EACH_SAFE() and recode from scratch
+ fixing invalid pointer operations.
+
+ * misc.c: Remove the last strlen() from strndup() that caused an
+ overrun.
+
+17 Feb 2005 Martin Schlemmer <azarah@gentoo.org>:
+
+ * misc.c: Fix overrun in strndup(), thanks to report from
+ Ned Ludd <solar@gentoo.org>.
+
+ * debug.h
+ * misc.h
+ * simple-regex.c: Print debug/errors to stderr, patch from
+ Ned Ludd <solar@gentoo.org>.
+
+ * debug.h: Replace invalid EXIT_FAILSTATUS with EXIT_FAILURE.
+
+ * parse.c: Modify parse_print_body() to be more ash friendly.
+ Suggestions from Ned Ludd <solar@gentoo.org>.
+
+ * debug.h: Remove the 'errno = ESPIPE' in DBG_MSG() for now, as it
+ seems to be fixed by the select() changes.
+
+ * parse.c: Disable write select() for now, as it is not needed.
+
+ * depscan.c: Only print EINFO msg if we actually update the cache.
+
+ * parse.c: Rename write_output() macro to PRINT_TO_BUFFER().
+
+ * parse.c
+ * parse.h: Rewrote large parts of generate_stage[12]() and their
+ machanics to use select() when writing to the pipes. This fixes a
+ buffering issue where too much data would cause the write to be
+ truncated, and the read pipe would then wait forever.
+
+ * misc.c: Fix gbasename() to compile under gcc-2.95.3.
+
+ * parse.c: Switch to stdio based io for reading pipes in
+ generate_stage2().
+
+ * misc.c
+ * misc.h: Add gbasename() that is similar to GNU's basename().
+
+ * parse.c: Use gbasename() instead of POSIX version.
+
+ * parse.c: Fix write_legacy_stage3() to quote the mtime in its output.
+
+ * misc.c
+ * parse.c: Change type of length from int to size_t to avoid warnings
+ when compiled for darwin.
+
+ * misc.c
+ misc.h: Add strndup() instead of relying on glibc's implementation
+ (should fix some issues on bsd and darwin).
+
+ * depend.c
+ * simple-regex.c: Do not define _GNU_SOURCE, but rather use our
+ strndup() from misc.h.
+
+ * parse.c: Do not define _GNU_SOURCE, but rather use our strndup() from
+ misc.h. Also change all usage of basename() to conform to POSIX, and
+ do not use the GNU variants.
+
+16 Feb 2005; Martin Schlemmer <azarah@gentoo.org>:
+
+ * depscan.c: Add uid check and quit if user is not root.
+
+ * depend.c
+ * depend.h: Change service_type_names declaration in depend.h to extern
+ and move the definition to depend.c to avoid warnings.
+
+ * README: New file.
+
+ * depscan.c: Add delete_var_dirs() to delete volatile directories in
+ svcdir. Change 'char *' declarations for create_directory() and
+ create_var_dirs() to 'const char*'.
+
+ * misc.h
+ * misc.c: Add rmtree() function. Fix ls_dir() not to include '.' and
+ '..' in its listing. Fix segfault in ls_dir() if the file list is
+ empty.
+
+ Add Header tags to all source files and Makefile.
+
+ * debug.h: perror() set errno to ESPIPE for some reason - restore errno
+ after calling perror().
+
+ * depscan.c: Add code to create svcdir and co if missing.
+
+ * misc.c: Add missing '\n' to DBG_MSG in mktree().
+
+ * misc.h: Add a comment about strcatpaths() allocating the memory needed.
+
+ Initial checkin. Still rough in some parts, but should be 100% similar
+ in output to depscan.sh and co.
+
+
+# vim:expandtab
diff --git a/src/core/INSTALL b/src/core/INSTALL
new file mode 100644
index 0000000..23e5f25
--- /dev/null
+++ b/src/core/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/src/core/Makefile b/src/core/Makefile
index 0b4c713..e69de29 100644
--- a/src/core/Makefile
+++ b/src/core/Makefile
@@ -1,83 +0,0 @@
-# Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
-#
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-# $Header$
-
-CC = gcc
-override CFLAGS += -Wall
-EXTRA_CFLAGS = -DLEGACY_DEPSCAN
-STRIP = strip
-
-DEPSCAN = depscan
-TEST_REGEX = test-regex
-
-TARGETS = $(DEPSCAN)
-CHECK_TARGETS = $(TEST_REGEX)
-
-all: $(TARGETS)
-
-.ALL: all
-
-OBJS = \
- parse.o \
- depend.o \
- simple-regex.o \
- misc.o
-
-HEADERS = \
- parse.h \
- depend.h \
- simple-regex.h \
- misc.h \
- debug.h
-
-
-# cc-option (from linux kernel sources)
-# Usage: cflags-y += $(call gcc-option, -march=winchip-c6, -march=i586)
-
-cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
- > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
-
-
-ifeq ($(DEBUG),1)
- override CFLAGS += -ggdb3
- EXTRA_CFLAGS += -DRC_DEBUG
-endif
-ifeq ($(BOUNDS),1)
- override CFLAGS += $(call cc-option, -fbounds-checking, -pipe)
-endif
-
-$(DEPSCAN): $(OBJS) $(DEPSCAN).o
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ $^
-
-$(TEST_REGEX): $(TEST_REGEX).o simple-regex.o
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ $^
-
-$(OBJS): $(HEADERS)
-
-.c.o:
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
-
-check: $(CHECK_TARGETS)
- @for x in $^; do \
- ./$${x} || exit 1; \
- done
-
-strip: $(TARGETS)
- $(STRIP) -s --remove-section=.note --remove-section=.comment $(TARGETS)
-
-clean:
- rm -f *.o $(TARGETS) $(CHECK_TARGETS)
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
new file mode 100644
index 0000000..9ab959a
--- /dev/null
+++ b/src/core/Makefile.am
@@ -0,0 +1,10 @@
+AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip
+
+SUBDIRS = \
+ data \
+ scripts \
+ librcscripts \
+ src \
+ tests
+
+EXTRA_DIST = ChangeLog.0
diff --git a/src/core/autogen.sh b/src/core/autogen.sh
new file mode 100644
index 0000000..03951c2
--- /dev/null
+++ b/src/core/autogen.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+if [[ -d .svn && $1 != -n ]] ; then
+ ./scripts/svn2cl.sh
+fi
+
+aclocal-1.9 || exit 1
+libtoolize --automake -c -f || exit 1
+aclocal-1.9 || exit 1
+autoconf || exit 1
+autoheader || exit 1
+automake-1.9 -a -c || exit 1
+
+if [[ -x ./test.sh ]] ; then
+ exec ./test.sh "$@"
+fi
diff --git a/src/core/configure.ac b/src/core/configure.ac
new file mode 100644
index 0000000..e22d9ae
--- /dev/null
+++ b/src/core/configure.ac
@@ -0,0 +1,66 @@
+AC_PREREQ([2.59])
+AC_INIT([rcscript-core], [0.1], [base-system@gentoo.org])
+AM_INIT_AUTOMAKE
+AC_CONFIG_HEADER([config.h])
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_ISC_POSIX
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AC_PROG_AWK
+
+AC_ENABLE_SHARED
+AC_DISABLE_STATIC
+dnl Next four lines is a hack to prevent libtool checking for CXX/F77
+m4_undefine([AC_PROG_CXX])
+m4_defun([AC_PROG_CXX],[])
+m4_undefine([AC_PROG_F77])
+m4_defun([AC_PROG_F77],[])
+AC_PROG_LIBTOOL
+
+AC_PREFIX_DEFAULT([])
+
+dnl Checks for libraries.
+dnl Checks for header files.
+AC_FUNC_ALLOCA
+AC_HEADER_STDC
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_SIZE_T
+
+dnl Checks for library functions.
+AC_FUNC_FORK
+AC_FUNC_LSTAT
+AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+AC_TYPE_SIGNAL
+AC_FUNC_STAT
+
+dnl check if we have 32bit or 64bit output
+AC_ARG_ENABLE([debug],
+ AS_HELP_STRING([--enable-debug],
+ [enable debugging - very verbose (default=disabled)]),
+ [enable_debug="$enableval"],
+ [enable_debug="no"]
+)
+
+if test x"$enable_debug" != xno ; then
+ CFLAGS="$CFLAGS -ggdb -DRC_DEBUG"
+fi
+
+RCSCRIPTS_DEFINES='-DETCDIR="\"$(sysconfdir)\"" -DLIBDIR="\"$(libdir)\"" -DBINDIR="\"$(bindir)\"" -DSBINDIR="\"$(sbindir)\""'
+AC_SUBST([RCSCRIPTS_DEFINES])
+
+AC_OUTPUT([
+ Makefile
+ scripts/Makefile
+ data/Makefile
+ librcscripts/Makefile
+ src/Makefile
+ tests/Makefile
+])
diff --git a/src/core/data/Makefile.am b/src/core/data/Makefile.am
new file mode 100644
index 0000000..900a84e
--- /dev/null
+++ b/src/core/data/Makefile.am
@@ -0,0 +1,4 @@
+confddir = $(libdir)/rcscripts/conf.d
+confd_DATA = env_whitelist
+
+EXTRA_DIST = env_whitelist
diff --git a/src/core/data/env_whitelist b/src/core/data/env_whitelist
new file mode 100644
index 0000000..ef30661
--- /dev/null
+++ b/src/core/data/env_whitelist
@@ -0,0 +1,30 @@
+# /lib/rcscripts/conf.d/env_whitelist: System environment whitelist for rc-system
+
+# See /etc/conf.d/env_whitelist for details.
+
+#
+# Internal variables needed for operation of rc-system
+#
+# NB: Do not modify below this line if you do not know what you are doing!!
+#
+
+# Hotplug
+IN_HOTPLUG
+
+# RC network script support
+IN_BACKGROUND
+RC_INTERFACE_KEEP_CONFIG
+
+# Default shell stuff
+SHELL
+USER
+HOME
+TERM
+
+# From /sbin/init
+PATH
+INIT_VERSION
+RUNLEVEL
+PREVLEVEL
+CONSOLE
+
diff --git a/src/core/debug.h b/src/core/debug.h
index 997e92f..e69de29 100644
--- a/src/core/debug.h
+++ b/src/core/debug.h
@@ -1,85 +0,0 @@
-/*
- * debug.h
- *
- * Simle debugging/logging macro's and functions.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#ifndef _DEBUG_H
-#define _DEBUG_H
-
-#if defined(RC_DEBUG)
-# define DBG_MSG(_format, _arg...) \
- do { \
- int old_errno = errno; \
- fprintf(stderr, "DEBUG(1): in %s, function %s(), line %i:\n", __FILE__, \
- __FUNCTION__, __LINE__); \
- fprintf(stderr, "DEBUG(2): " _format, ## _arg); \
- errno = old_errno; \
- if (0 != errno) { \
- perror("DEBUG(3)"); \
- /* perror() for some reason sets errno to ESPIPE */ \
- errno = old_errno; \
- } \
- } while (0)
-#else
-# define DBG_MSG(_format, _arg...) \
- do { \
- int old_errno = errno; \
- /* Bit of a hack, as how we do things tend to cause seek
- * errors when reading the parent/child pipes */ \
- /* if ((0 != errno) && (ESPIPE != errno)) { */ \
- if (0 != errno) { \
- fprintf(stderr, "DEBUG(1): in %s, function %s(), line %i:\n", \
- __FILE__, __FUNCTION__, __LINE__); \
- fprintf(stderr, "DEBUG(2): " _format, ## _arg); \
- errno = old_errno; \
- perror("DEBUG(3)"); \
- /* perror() for some reason sets errno to ESPIPE */ \
- errno = old_errno; \
- } \
- } while (0)
-#endif
-
-#define FATAL_ERROR() \
- do { \
- int old_errno = errno; \
- fprintf(stderr, "ERROR: file '%s', function '%s', line %i.\n", \
- __FILE__, __FUNCTION__, __LINE__); \
- errno = old_errno; \
- if (0 != errno) \
- perror("ERROR"); \
- exit(EXIT_FAILURE); \
- } while (0)
-
-#define NEG_FATAL_ERROR(_x) \
- do { \
- if (-1 == _x) \
- FATAL_ERROR(); \
- } while (0)
-
-#define NULL_FATAL_ERROR(_x) \
- do { \
- if (NULL == _x) \
- FATAL_ERROR(); \
- } while (0)
-
-#endif /* _DEBUG_H */
-
diff --git a/src/core/depend.c b/src/core/depend.c
index 4946326..e69de29 100644
--- a/src/core/depend.c
+++ b/src/core/depend.c
@@ -1,557 +0,0 @@
-/*
- * depend.c
- *
- * Dependancy engine for Gentoo style rc-scripts.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "debug.h"
-#include "depend.h"
-#include "list.h"
-#include "misc.h"
-
-LIST_HEAD(service_info_list);
-
-/* Names for service types (service_type_t) in depend.h.
- * Note that this should sync with service_type_t */
-char *service_type_names[] = {
- "NEED",
- "NEED_ME",
- "USE",
- "USE_ME",
- "BEFORE",
- "AFTER",
- "BROKEN",
- "PROVIDE",
- NULL
-};
-
-int __service_resolve_dependency(char *servicename, char *dependency, service_type_t type);
-
-service_info_t *service_get_info(char *servicename)
-{
- service_info_t *info;
-
- if ((NULL == servicename) || (0 == strlen(servicename))) {
- DBG_MSG("Invalid argument passed!\n");
- return NULL;
- }
-
- list_for_each_entry(info, &service_info_list, node) {
- if (NULL != info->name)
- if (0 == strcmp(info->name, servicename))
- return info;
- }
-
- /* We use this to check if a service exists, so rather do not
- * add debugging, otherwise it is very noisy! */
- /* DBG_MSG("Invalid service name '%s'!\n", servicename); */
-
- return NULL;
-}
-
-int service_add(char *servicename)
-{
- service_info_t *info;
- service_info_t *sorted;
- int count;
-
- if ((NULL == servicename) || (0 == strlen(servicename))) {
- DBG_MSG("Invalid argument passed!\n");
- return -1;
- }
-
- info = service_get_info(servicename);
- if (NULL == info) {
- DBG_MSG("Adding service '%s'.\n", servicename);
-
- info = malloc(sizeof(service_info_t));
- if (NULL == info) {
- DBG_MSG("Failed to allocate service_info_t!\n");
- return -1;
- }
-
- info->name = strndup(servicename, strlen(servicename));
- if (NULL == info->name) {
- DBG_MSG("Failed to allocate buffer!\n");
- free(info);
- return -1;
- }
-
- for (count = 0; count < ALL_SERVICE_TYPE_T; count++)
- info->depend_info[count] = NULL;
- info->provide = NULL;
-
- /* We want to keep the list sorted */
- list_for_each_entry(sorted, &service_info_list, node) {
- if (strcmp(sorted->name, servicename) > 0) {
- break;
- }
- }
-
- list_add_tail(&info->node, &sorted->node);
-
- return 0;
- } else {
- DBG_MSG("Tried to add duplicate service '%s'!\n", servicename);
- }
-
- return -1;
-}
-
-int service_is_dependency(char *servicename, char *dependency, service_type_t type)
-{
- service_info_t *info;
- char *service;
- int count = 0;
-
- if ((NULL == servicename)
- || (0 == strlen(servicename))
- || (NULL == dependency)
- || (0 == strlen(dependency))) {
- DBG_MSG("Invalid argument passed!\n");
- return -1;
- }
-
- info = service_get_info(servicename);
- if (NULL != info) {
- STRING_LIST_FOR_EACH(info->depend_info[type], service, count) {
- if (0 == strcmp(dependency, service))
- return 0;
- }
- } else {
- DBG_MSG("Invalid service name '%s'!\n", servicename);
- }
-
- return -1;
-}
-
-int service_add_dependency(char *servicename, char *dependency, service_type_t type)
-{
- service_info_t *info;
- char *tmp_buf;
-
- if ((NULL == servicename)
- || (0 == strlen(servicename))
- || (NULL == dependency)
- || (0 == strlen(dependency))) {
- DBG_MSG("Invalid argument passed!\n");
- return -1;
- }
-
- info = service_get_info(servicename);
- if (NULL != info) {
- /* Do not add duplicates */
- if (-1 == service_is_dependency(servicename, dependency, type)) {
- DBG_MSG("Adding dependency '%s' of service '%s', type '%s'.\n",
- dependency, servicename, service_type_names[type]);
-
- tmp_buf = strndup(dependency, strlen(dependency));
- if (NULL == tmp_buf) {
- DBG_MSG("Failed to allocate buffer!\n");
- return -1;
- }
-
- STRING_LIST_ADD_SORT(info->depend_info[type], tmp_buf, error);
- } else {
- DBG_MSG("Duplicate dependency '%s' for service '%s', type '%s'!\n",
- dependency, servicename, service_type_names[type]);
- /* Rather do not fail here, as we add a lot of doubles
- * during resolving of dependencies */
- }
-
- return 0;
- } else {
- DBG_MSG("Invalid service name '%s'!\n", servicename);
- }
-
-error:
- return -1;
-}
-
-int service_del_dependency(char *servicename, char *dependency, service_type_t type)
-{
- service_info_t *info;
-
- if ((NULL == servicename)
- || (0 == strlen(servicename))
- || (NULL == dependency)
- || (0 == strlen(dependency))) {
- DBG_MSG("Invalid argument passed!\n");
- return -1;
- }
-
- if (-1 == service_is_dependency(servicename, dependency, type)) {
- DBG_MSG("Tried to remove invalid dependency '%s'!\n", dependency);
- return -1;
- }
-
- info = service_get_info(servicename);
- if (NULL != info) {
- DBG_MSG("Removing dependency '%s' of service '%s', type '%s'.\n",
- dependency , servicename, service_type_names[type]);
-
- STRING_LIST_DEL(info->depend_info[type], dependency, error);
- return 0;
- } else {
- DBG_MSG("Invalid service name '%s'!\n", servicename);
- }
-
-error:
- return -1;
-}
-
-service_info_t *service_get_virtual(char *virtual)
-{
- service_info_t *info;
-
- if ((NULL == virtual) || (0 == strlen(virtual))) {
- DBG_MSG("Invalid argument passed!\n");
- return NULL;
- }
-
- list_for_each_entry(info, &service_info_list, node) {
- if (NULL != info->provide)
- if (0 == strcmp(info->provide, virtual))
- return info;
- }
-
- /* We use this to check if a virtual exists, so rather do not
- * add debugging, otherwise it is very noisy! */
- /* DBG_MSG("Invalid service name '%s'!\n", virtual); */
-
- return NULL;
-}
-
-int service_add_virtual(char *servicename, char* virtual)
-{
- service_info_t *info;
-
- if ((NULL == servicename)
- || (0 == strlen(servicename))
- || (NULL == virtual )
- || (0 == strlen(virtual))) {
- DBG_MSG("Invalid argument passed!\n");
- return -1;
- }
-
- if (NULL != service_get_info(virtual)) {
- EERROR(" Cannot add provide '%s', as a service with the same name exists!\n",
- virtual);
- /* Do not fail here as we do have a service that resolves
- * the virtual */
- }
-
- info = service_get_virtual(virtual);
- if (NULL != info) {
- /* We cannot have more than one service Providing a virtual */
- EWARN(" Service '%s' already provides '%s'!;\n",
- info->name, virtual);
- EWARN(" Not adding service '%s'...\n", servicename);
- /* Do not fail here as we do have a service that resolves
- * the virtual */
- } else {
- info = service_get_info(servicename);
- if (NULL != info) {
- DBG_MSG("Adding virtual '%s' of service '%s'.\n",
- virtual, servicename);
-
- info->provide = strndup(virtual, strlen(virtual));
- if (NULL == info->provide) {
- DBG_MSG("Failed to allocate buffer!\n");
- return -1;
- }
- } else {
- DBG_MSG("Invalid service name '%s'!\n", servicename);
- return -1;
- }
- }
-
- return 0;
-}
-
-int service_set_mtime(char *servicename, time_t mtime)
-{
- service_info_t *info;
-
- if ((NULL == servicename) || (0 == strlen(servicename))) {
- DBG_MSG("Invalid argument passed!\n");
- return -1;
- }
-
- info = service_get_info(servicename);
- if (NULL != info) {
- DBG_MSG("Setting mtime '%li' of service '%s'.\n",
- mtime, servicename);
-
- info->mtime = mtime;
-
- return 0;
- } else {
- DBG_MSG("Invalid service name '%s'!\n", servicename);
- }
-
- return -1;
-}
-
-int __service_resolve_dependency(char *servicename, char *dependency, service_type_t type)
-{
- service_info_t *info;
- int retval;
-
- if ((NULL == servicename)
- || (0 == strlen(servicename))
- || (NULL == dependency)
- || (0 == strlen(dependency))) {
- DBG_MSG("Invalid argument passed!\n");
- return -1;
- }
-
- info = service_get_info(servicename);
- if (NULL == info) {
- DBG_MSG("Invalid service name passed!\n");
- return -1;
- }
-
- DBG_MSG("Checking dependency '%s' of service '%s', type '%s'.\n",
- dependency, servicename, service_type_names[type]);
-
- /* If there are no existing service 'dependency', try to resolve
- * possible virtual services */
- info = service_get_info(dependency);
- if (NULL == info) {
- info = service_get_virtual(dependency);
- if (NULL != info) {
- DBG_MSG("Virtual '%s' -> '%s' for service '%s', type '%s'.\n",
- dependency, info->name, servicename,
- service_type_names[type]);
-
- retval = service_del_dependency(servicename, dependency, type);
- if (-1 == retval) {
- DBG_MSG("Failed to delete dependency!\n");
- return -1;
- }
-
- /* Add the actual service name for the virtual */
- dependency = info->name;
- retval = service_add_dependency(servicename, dependency, type);
- if (-1 == retval) {
- DBG_MSG("Failed to add dependency!\n");
- return -1;
- }
- }
- }
-
- /* Handle 'need', as it is the only dependency type that should
- * handle invalid database entries currently. */
- if (NULL == info) {
- if ((type == NEED) || (type == NEED_ME)) {
- EWARN(" Can't find service '%s' needed by '%s'; continuing...\n",
- dependency, servicename);
-
- retval = service_add_dependency(servicename, dependency, BROKEN);
- if (-1 == retval) {
- DBG_MSG("Failed to add dependency!\n");
- return -1;
- }
-
- /* Delete invalid entry */
- goto remove;
- }
-
- /* For the rest, if the dependency is not 'net', just silently
- * die without error. Should not be needed as we add a 'net'
- * service manually before we start, but you never know ... */
- if (0 != strcmp(dependency, "net")) {
- /* Delete invalid entry */
- goto remove;
- }
- }
-
- /* Ugly bug ... if a service depends on itself, it creates a
- * 'mini fork bomb' effect, and breaks things horribly ... */
- if (0 == strcmp(servicename, dependency)) {
- /* Dont work too well with the '*' before and after */
- if ((type != BEFORE) && (type != AFTER))
- EWARN(" Service '%s' can't depend on itself; continuing...\n",
- servicename);
-
- /* Delete invalid entry */
- goto remove;
- }
-
- /* Currently only these depend/order types are supported */
- if ((type == NEED)
- || (type == USE)
- || (type == BEFORE)
- || (type == AFTER)) {
- if (type == BEFORE) {
- /* NEED and USE override BEFORE
- * ('servicename' BEFORE 'dependency') */
- if ((0 == service_is_dependency(servicename, dependency, NEED))
- || (0 == service_is_dependency(servicename, dependency, USE))) {
- /* Delete invalid entry */
- goto remove;
- }
- }
-
- if (type == AFTER) {
- /* NEED and USE override AFTER
- * ('servicename' AFTER 'dependency') */
- if ((0 == service_is_dependency(dependency, servicename, NEED))
- || (0 == service_is_dependency(dependency, servicename, USE))) {
- /* Delete invalid entry */
- goto remove;
- }
- }
-
- /* We do not want to add circular dependencies ... */
- if (0 == service_is_dependency(dependency, servicename, type)) {
- EWARN(" Services '%s' and '%s' have circular\n",
- servicename, dependency);
- EWARN(" dependency of type '%s'; continuing...\n",
- service_type_names[type]);
-
- /* For now remove this dependency */
- goto remove;
- }
-
- /* Reverse mapping */
- if (type == NEED) {
- retval = service_add_dependency(dependency, servicename, NEED_ME);
- if (-1 == retval) {
- DBG_MSG("Failed to add dependency!\n");
- return -1;
- }
- }
-
- /* Reverse mapping */
- if (type == USE) {
- retval = service_add_dependency(dependency, servicename, USE_ME);
- if (-1 == retval) {
- DBG_MSG("Failed to add dependency!\n");
- return -1;
- }
- }
-
- /* Reverse mapping */
- if (type == BEFORE) {
- retval = service_add_dependency(dependency, servicename, AFTER);
- if (-1 == retval) {
- DBG_MSG("Failed to add dependency!\n");
- return -1;
- }
- }
-
- /* Reverse mapping */
- if (type == AFTER) {
- retval = service_add_dependency(dependency, servicename, BEFORE);
- if (-1 == retval) {
- DBG_MSG("Failed to add dependency!\n");
- return -1;
- }
- }
- }
-
- return 0;
-
-remove:
- /* Delete invalid entry */
- DBG_MSG("Removing invalid dependency '%s' of service '%s', type '%s'.\n",
- dependency, servicename, service_type_names[type]);
-
- retval = service_del_dependency(servicename, dependency, type);
- if (-1 == retval) {
- DBG_MSG("Failed to delete dependency!\n");
- return -1;
- }
-
- /* Here we should not die with error */
- return 0;
-}
-
-int service_resolve_dependencies(void)
-{
- service_info_t *info;
- char *service;
- char *next = NULL;
- int count;
-
- /* Add our 'net' service */
- if (NULL == service_get_info("net")) {
- if (-1 == service_add("net")) {
- DBG_MSG("Failed to add virtual!\n");
- return -1;
- }
- service_set_mtime("net", 0);
- }
-
- /* Calculate all virtuals */
- list_for_each_entry(info, &service_info_list, node) {
- STRING_LIST_FOR_EACH_SAFE(info->depend_info[PROVIDE], service, next, count)
- if (-1 == service_add_virtual(info->name, service)) {
- DBG_MSG("Failed to add virtual!\n");
- return -1;
- }
- }
-
- /* Now do NEED, USE, BEFORE and AFTER */
- list_for_each_entry(info, &service_info_list, node) {
- STRING_LIST_FOR_EACH_SAFE(info->depend_info[NEED], service, next, count) {
- if (-1 == __service_resolve_dependency(info->name, service, NEED)) {
- DBG_MSG("Failed to resolve dependency!\n");
- return -1;
- }
- }
- }
- list_for_each_entry(info, &service_info_list, node) {
- STRING_LIST_FOR_EACH_SAFE(info->depend_info[USE], service, next, count) {
- if (-1 == __service_resolve_dependency(info->name, service, USE)) {
- DBG_MSG("Failed to resolve dependency!\n");
- return -1;
- }
- }
- }
- list_for_each_entry(info, &service_info_list, node) {
- STRING_LIST_FOR_EACH_SAFE(info->depend_info[BEFORE], service, next, count) {
- if (-1 == __service_resolve_dependency(info->name, service, BEFORE)) {
- DBG_MSG("Failed to resolve dependency!\n");
- return -1;
- }
- }
- }
- list_for_each_entry(info, &service_info_list, node) {
- STRING_LIST_FOR_EACH_SAFE(info->depend_info[AFTER], service, next, count) {
- if (-1 == __service_resolve_dependency(info->name, service, AFTER)) {
- DBG_MSG("Failed to resolve dependency!\n");
- return -1;
- }
- }
- }
-
- return 0;
-}
-
diff --git a/src/core/depend.h b/src/core/depend.h
index 2a7ff77..e69de29 100644
--- a/src/core/depend.h
+++ b/src/core/depend.h
@@ -1,75 +0,0 @@
-/*
- * depend.h
- *
- * Dependancy engine for Gentoo style rc-scripts.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#ifndef _DEPEND_H
-#define _DEPEND_H
-
-#include <sys/types.h>
-#include "list.h"
-
-/* Dependency types supported or still to be implemented */
-typedef enum {
- NEED, /* All dependencies needed by specified service */
- NEED_ME, /* All dependencies that need specified service */
- USE, /* All dependencies used by specified service */
- USE_ME, /* All dependencies that use specified service */
- BEFORE, /* All services started before specified service */
- AFTER, /* All services started after specified service */
- BROKEN, /* All dependencies of type NEED missing for
- specified service */
- PROVIDE, /* All virtual services provided by specified service */
- ALL_SERVICE_TYPE_T
-} service_type_t;
-
-/* Names for above service types (service_type_t).
- * Note that this should sync with above service_type_t */
-extern char *service_type_names[];
-
-typedef struct {
- struct list_head node;
-
- char *name; /* Name of service */
- char **depend_info[ALL_SERVICE_TYPE_T]; /* String lists for each service
- type */
- char *provide; /* Name of virtual service it
- provides. This is only valid
- after we resolving - thus after
- service_resolve_dependencies() */
- time_t mtime; /* Modification time of script */
-} service_info_t;
-
-struct list_head service_info_list;
-
-service_info_t *service_get_info(char *servicename);
-int service_add(char *servicename);
-int service_is_dependency(char *servicename, char *dependency, service_type_t type);
-int service_add_dependency(char *servicename, char *dependency, service_type_t type);
-int service_del_dependency(char *servicename, char *dependency, service_type_t type);
-service_info_t *service_get_virtual(char *virtual);
-int service_add_virtual(char *servicename, char* virtual);
-int service_set_mtime(char *servicename, time_t mtime);
-int service_resolve_dependencies(void);
-
-#endif /* _DEPEND_H */
-
diff --git a/src/core/depscan.c b/src/core/depscan.c
index 5030eb4..e69de29 100644
--- a/src/core/depscan.c
+++ b/src/core/depscan.c
@@ -1,297 +0,0 @@
-/*
- * depscan.c
- *
- * Basic frontend for updating the dependency cache.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#include <errno.h>
-#ifndef __KLIBC__
-# include <locale.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "debug.h"
-#include "depend.h"
-#include "misc.h"
-#include "parse.h"
-
-char* svcdir_subdirs[] = {
- "softscripts",
- "snapshot",
- "options",
- "started",
- "starting",
- "inactive",
- "stopping",
- NULL
-};
-
-char *svcdir_volatile_subdirs[] = {
- "snapshot",
- "broken",
- NULL
-};
-
-int create_directory(const char *name);
-int create_var_dirs(const char *svcdir);
-int delete_var_dirs(const char *svcdir);
-
-int create_directory(const char *name) {
- if ((NULL == name) || (0 == strlen(name))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- /* Check if directory exist, and is not a symlink */
- if (!is_dir(name, 0)) {
- if (exists(name)) {
- /* Remove it if not a directory */
- if (-1 == unlink(name)) {
- DBG_MSG("Failed to remove '%s'!\n", name);
- return -1;
- }
- }
- /* Now try to create the directory */
- if (-1 == mktree(name, 0755)) {
- DBG_MSG("Failed to create '%s'!\n", name);
- return -1;
- }
- }
-
- return 0;
-}
-
-int create_var_dirs(const char *svcdir) {
- char *tmp_path = NULL;
- int i = 0;
-
- if ((NULL == svcdir) || (0 == strlen(svcdir))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- /* Check and create svcdir if needed */
- if (-1 == create_directory(svcdir)) {
- DBG_MSG("Failed to create '%s'!\n", svcdir);
- return -1;
- }
-
- while (NULL != svcdir_subdirs[i]) {
- tmp_path = strcatpaths(svcdir, svcdir_subdirs[i]);
- if (NULL == tmp_path) {
- DBG_MSG("Failed to allocate buffer!\n");
- return -1;
- }
-
- /* Check and create all the subdirs if needed */
- if (-1 == create_directory(tmp_path)) {
- DBG_MSG("Failed to create '%s'!\n", tmp_path);
- free(tmp_path);
- return -1;
- }
-
- free(tmp_path);
- i++;
- }
-
- return 0;
-}
-
-int delete_var_dirs(const char *svcdir) {
- char *tmp_path = NULL;
- int i = 0;
-
- if ((NULL == svcdir) || (0 == strlen(svcdir))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- /* Just quit if svcdir do not exist */
- if (!exists(svcdir)) {
- DBG_MSG("'%s' does not exist!\n", svcdir);
- return 0;
- }
-
- while (NULL != svcdir_volatile_subdirs[i]) {
- tmp_path = strcatpaths(svcdir, svcdir_volatile_subdirs[i]);
- if (NULL == tmp_path) {
- DBG_MSG("Failed to allocate buffer!\n");
- return -1;
- }
-
- /* Skip the directory if it does not exist */
- if (!exists(tmp_path))
- goto _continue;
-
- /* Check and delete all files and sub directories if needed */
- if (-1 == rmtree(tmp_path)) {
- DBG_MSG("Failed to delete '%s'!\n", tmp_path);
- free(tmp_path);
- return -1;
- }
-
-_continue:
- free(tmp_path);
- i++;
- }
-
- return 0;
-}
-
-#if defined(LEGACY_DEPSCAN)
-
-int main() {
- FILE *cachefile_fd = NULL;
- char *data = NULL;
- char *svcdir = NULL;
- char *cachefile = NULL;
- char *tmp_cachefile = NULL;
- int tmp_cachefile_fd = 0;
- int datasize = 0;
-
- /* Make sure we do not run into locale issues */
-#ifndef __KLIBC__
- setlocale (LC_ALL, "C");
-#endif
-
- if (0 != getuid()) {
- EERROR("Must be root!\n");
- exit(EXIT_FAILURE);
- }
-
- svcdir = get_cnf_entry(RC_CONFD_FILE_NAME, SVCDIR_CONFIG_ENTRY);
- if (NULL == svcdir) {
- EERROR("Failed to get config entry '%s'!\n",
- SVCDIR_CONFIG_ENTRY);
- exit(EXIT_FAILURE);
- }
-
- /* Delete (if needed) volatile directories in svcdir */
- if (-1 == delete_var_dirs(svcdir)) {
- /* XXX: Not 100% accurate below message ... */
- EERROR("Failed to delete '%s', %s", svcdir,
- "or one of its sub directories!\n");
- exit(EXIT_FAILURE);
- }
-
- /* Create all needed directories in svcdir */
- if (-1 == create_var_dirs(svcdir)) {
- EERROR("Failed to create '%s', %s", svcdir,
- "or one of its sub directories!\n");
- exit(EXIT_FAILURE);
- }
-
- cachefile = strcatpaths(svcdir, LEGACY_CACHE_FILE_NAME);
- if (NULL == cachefile) {
- DBG_MSG("Failed to allocate buffer!\n");
- exit(EXIT_FAILURE);
- }
-
- tmp_cachefile = strcatpaths(cachefile, "XXXXXX");
- if (NULL == tmp_cachefile) {
- DBG_MSG("Failed to allocate buffer!\n");
- exit(EXIT_FAILURE);
- }
- /* Replace the "/XXXXXX" with ".XXXXXX"
- * Yes, I am lazy. */
- tmp_cachefile[strlen(tmp_cachefile) - strlen(".XXXXXX")] = '.';
-
- if (-1 == get_rcscripts()) {
- EERROR("Failed to get rc-scripts list!\n");
- exit(EXIT_FAILURE);
- }
-
- if (-1 == check_rcscripts_mtime(cachefile)) {
- EINFO("Caching service dependencies ...\n");
- DBG_MSG("Regenerating cache file '%s'.\n", cachefile);
-
- datasize = generate_stage2(&data);
- if (-1 == datasize) {
- EERROR("Failed to generate stage2!\n");
- exit(EXIT_FAILURE);
- }
-
- if (-1 == parse_cache(data, datasize)) {
- EERROR("Failed to parse stage2 output!\n");
- free(data);
- exit(EXIT_FAILURE);
- }
-
-#if 0
- tmp_cachefile_fd = open("foo", O_CREAT | O_TRUNC | O_RDWR, 0600);
- write(tmp_cachefile_fd, data, datasize);
- close(tmp_cachefile_fd);
-#endif
-
- free(data);
-
- if (-1 == service_resolve_dependencies()) {
- EERROR("Failed to resolve dependencies!\n");
- exit(EXIT_FAILURE);
- }
-
-#ifndef __KLIBC__
- tmp_cachefile_fd = mkstemp(tmp_cachefile);
-#else
- /* FIXME: Need to add a mkstemp implementation for klibc */
- tmp_cachefile_fd = open(tmp_cachefile, O_CREAT | O_TRUNC | O_RDWR, 0600);
-#endif
- if (-1 == tmp_cachefile_fd) {
- EERROR("Could not open temporary file for writing!\n");
- exit(EXIT_FAILURE);
- }
- cachefile_fd = fdopen(tmp_cachefile_fd, "w");
- if (NULL == cachefile_fd) {
- EERROR("Could not open temporary file for writing!\n");
- exit(EXIT_FAILURE);
- }
-
- write_legacy_stage3(cachefile_fd);
- fclose(cachefile_fd);
-
- if ((-1 == unlink(cachefile)) && (exists(cachefile))) {
- EERROR("Could not remove '%s'!\n", cachefile);
- unlink(tmp_cachefile);
- exit(EXIT_FAILURE);
- }
-
- if (-1 == rename(tmp_cachefile, cachefile)) {
- EERROR("Could not move temporary file to '%s'!\n",
- cachefile);
- unlink(tmp_cachefile);
- exit(EXIT_FAILURE);
- }
- }
-
- exit(EXIT_SUCCESS);
-}
-
-#endif
-
diff --git a/src/core/librcscripts/Makefile.am b/src/core/librcscripts/Makefile.am
new file mode 100644
index 0000000..8e6b509
--- /dev/null
+++ b/src/core/librcscripts/Makefile.am
@@ -0,0 +1,18 @@
+AUTOMAKE_OPTIONS = foreign
+
+INCLUDES = $(RCSCRIPTS_DEFINES)
+
+lib_LTLIBRARIES = librcscripts.la
+
+librcscripts_la_SOURCES = \
+ rcscripts.h \
+ debug.h \
+ depend.c \
+ depend.h \
+ list.h \
+ misc.c \
+ misc.h \
+ parse.c \
+ parse.h \
+ simple-regex.c \
+ simple-regex.h
diff --git a/src/core/librcscripts/debug.h b/src/core/librcscripts/debug.h
new file mode 100644
index 0000000..997e92f
--- /dev/null
+++ b/src/core/librcscripts/debug.h
@@ -0,0 +1,85 @@
+/*
+ * debug.h
+ *
+ * Simle debugging/logging macro's and functions.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#if defined(RC_DEBUG)
+# define DBG_MSG(_format, _arg...) \
+ do { \
+ int old_errno = errno; \
+ fprintf(stderr, "DEBUG(1): in %s, function %s(), line %i:\n", __FILE__, \
+ __FUNCTION__, __LINE__); \
+ fprintf(stderr, "DEBUG(2): " _format, ## _arg); \
+ errno = old_errno; \
+ if (0 != errno) { \
+ perror("DEBUG(3)"); \
+ /* perror() for some reason sets errno to ESPIPE */ \
+ errno = old_errno; \
+ } \
+ } while (0)
+#else
+# define DBG_MSG(_format, _arg...) \
+ do { \
+ int old_errno = errno; \
+ /* Bit of a hack, as how we do things tend to cause seek
+ * errors when reading the parent/child pipes */ \
+ /* if ((0 != errno) && (ESPIPE != errno)) { */ \
+ if (0 != errno) { \
+ fprintf(stderr, "DEBUG(1): in %s, function %s(), line %i:\n", \
+ __FILE__, __FUNCTION__, __LINE__); \
+ fprintf(stderr, "DEBUG(2): " _format, ## _arg); \
+ errno = old_errno; \
+ perror("DEBUG(3)"); \
+ /* perror() for some reason sets errno to ESPIPE */ \
+ errno = old_errno; \
+ } \
+ } while (0)
+#endif
+
+#define FATAL_ERROR() \
+ do { \
+ int old_errno = errno; \
+ fprintf(stderr, "ERROR: file '%s', function '%s', line %i.\n", \
+ __FILE__, __FUNCTION__, __LINE__); \
+ errno = old_errno; \
+ if (0 != errno) \
+ perror("ERROR"); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+#define NEG_FATAL_ERROR(_x) \
+ do { \
+ if (-1 == _x) \
+ FATAL_ERROR(); \
+ } while (0)
+
+#define NULL_FATAL_ERROR(_x) \
+ do { \
+ if (NULL == _x) \
+ FATAL_ERROR(); \
+ } while (0)
+
+#endif /* _DEBUG_H */
+
diff --git a/src/core/librcscripts/depend.c b/src/core/librcscripts/depend.c
new file mode 100644
index 0000000..4946326
--- /dev/null
+++ b/src/core/librcscripts/depend.c
@@ -0,0 +1,557 @@
+/*
+ * depend.c
+ *
+ * Dependancy engine for Gentoo style rc-scripts.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "debug.h"
+#include "depend.h"
+#include "list.h"
+#include "misc.h"
+
+LIST_HEAD(service_info_list);
+
+/* Names for service types (service_type_t) in depend.h.
+ * Note that this should sync with service_type_t */
+char *service_type_names[] = {
+ "NEED",
+ "NEED_ME",
+ "USE",
+ "USE_ME",
+ "BEFORE",
+ "AFTER",
+ "BROKEN",
+ "PROVIDE",
+ NULL
+};
+
+int __service_resolve_dependency(char *servicename, char *dependency, service_type_t type);
+
+service_info_t *service_get_info(char *servicename)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename) || (0 == strlen(servicename))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return NULL;
+ }
+
+ list_for_each_entry(info, &service_info_list, node) {
+ if (NULL != info->name)
+ if (0 == strcmp(info->name, servicename))
+ return info;
+ }
+
+ /* We use this to check if a service exists, so rather do not
+ * add debugging, otherwise it is very noisy! */
+ /* DBG_MSG("Invalid service name '%s'!\n", servicename); */
+
+ return NULL;
+}
+
+int service_add(char *servicename)
+{
+ service_info_t *info;
+ service_info_t *sorted;
+ int count;
+
+ if ((NULL == servicename) || (0 == strlen(servicename))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL == info) {
+ DBG_MSG("Adding service '%s'.\n", servicename);
+
+ info = malloc(sizeof(service_info_t));
+ if (NULL == info) {
+ DBG_MSG("Failed to allocate service_info_t!\n");
+ return -1;
+ }
+
+ info->name = strndup(servicename, strlen(servicename));
+ if (NULL == info->name) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ free(info);
+ return -1;
+ }
+
+ for (count = 0; count < ALL_SERVICE_TYPE_T; count++)
+ info->depend_info[count] = NULL;
+ info->provide = NULL;
+
+ /* We want to keep the list sorted */
+ list_for_each_entry(sorted, &service_info_list, node) {
+ if (strcmp(sorted->name, servicename) > 0) {
+ break;
+ }
+ }
+
+ list_add_tail(&info->node, &sorted->node);
+
+ return 0;
+ } else {
+ DBG_MSG("Tried to add duplicate service '%s'!\n", servicename);
+ }
+
+ return -1;
+}
+
+int service_is_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+ char *service;
+ int count = 0;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ STRING_LIST_FOR_EACH(info->depend_info[type], service, count) {
+ if (0 == strcmp(dependency, service))
+ return 0;
+ }
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+ return -1;
+}
+
+int service_add_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+ char *tmp_buf;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ /* Do not add duplicates */
+ if (-1 == service_is_dependency(servicename, dependency, type)) {
+ DBG_MSG("Adding dependency '%s' of service '%s', type '%s'.\n",
+ dependency, servicename, service_type_names[type]);
+
+ tmp_buf = strndup(dependency, strlen(dependency));
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return -1;
+ }
+
+ STRING_LIST_ADD_SORT(info->depend_info[type], tmp_buf, error);
+ } else {
+ DBG_MSG("Duplicate dependency '%s' for service '%s', type '%s'!\n",
+ dependency, servicename, service_type_names[type]);
+ /* Rather do not fail here, as we add a lot of doubles
+ * during resolving of dependencies */
+ }
+
+ return 0;
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+error:
+ return -1;
+}
+
+int service_del_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ if (-1 == service_is_dependency(servicename, dependency, type)) {
+ DBG_MSG("Tried to remove invalid dependency '%s'!\n", dependency);
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ DBG_MSG("Removing dependency '%s' of service '%s', type '%s'.\n",
+ dependency , servicename, service_type_names[type]);
+
+ STRING_LIST_DEL(info->depend_info[type], dependency, error);
+ return 0;
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+error:
+ return -1;
+}
+
+service_info_t *service_get_virtual(char *virtual)
+{
+ service_info_t *info;
+
+ if ((NULL == virtual) || (0 == strlen(virtual))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return NULL;
+ }
+
+ list_for_each_entry(info, &service_info_list, node) {
+ if (NULL != info->provide)
+ if (0 == strcmp(info->provide, virtual))
+ return info;
+ }
+
+ /* We use this to check if a virtual exists, so rather do not
+ * add debugging, otherwise it is very noisy! */
+ /* DBG_MSG("Invalid service name '%s'!\n", virtual); */
+
+ return NULL;
+}
+
+int service_add_virtual(char *servicename, char* virtual)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == virtual )
+ || (0 == strlen(virtual))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ if (NULL != service_get_info(virtual)) {
+ EERROR(" Cannot add provide '%s', as a service with the same name exists!\n",
+ virtual);
+ /* Do not fail here as we do have a service that resolves
+ * the virtual */
+ }
+
+ info = service_get_virtual(virtual);
+ if (NULL != info) {
+ /* We cannot have more than one service Providing a virtual */
+ EWARN(" Service '%s' already provides '%s'!;\n",
+ info->name, virtual);
+ EWARN(" Not adding service '%s'...\n", servicename);
+ /* Do not fail here as we do have a service that resolves
+ * the virtual */
+ } else {
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ DBG_MSG("Adding virtual '%s' of service '%s'.\n",
+ virtual, servicename);
+
+ info->provide = strndup(virtual, strlen(virtual));
+ if (NULL == info->provide) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return -1;
+ }
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int service_set_mtime(char *servicename, time_t mtime)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename) || (0 == strlen(servicename))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ DBG_MSG("Setting mtime '%li' of service '%s'.\n",
+ mtime, servicename);
+
+ info->mtime = mtime;
+
+ return 0;
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+ return -1;
+}
+
+int __service_resolve_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+ int retval;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL == info) {
+ DBG_MSG("Invalid service name passed!\n");
+ return -1;
+ }
+
+ DBG_MSG("Checking dependency '%s' of service '%s', type '%s'.\n",
+ dependency, servicename, service_type_names[type]);
+
+ /* If there are no existing service 'dependency', try to resolve
+ * possible virtual services */
+ info = service_get_info(dependency);
+ if (NULL == info) {
+ info = service_get_virtual(dependency);
+ if (NULL != info) {
+ DBG_MSG("Virtual '%s' -> '%s' for service '%s', type '%s'.\n",
+ dependency, info->name, servicename,
+ service_type_names[type]);
+
+ retval = service_del_dependency(servicename, dependency, type);
+ if (-1 == retval) {
+ DBG_MSG("Failed to delete dependency!\n");
+ return -1;
+ }
+
+ /* Add the actual service name for the virtual */
+ dependency = info->name;
+ retval = service_add_dependency(servicename, dependency, type);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+ }
+
+ /* Handle 'need', as it is the only dependency type that should
+ * handle invalid database entries currently. */
+ if (NULL == info) {
+ if ((type == NEED) || (type == NEED_ME)) {
+ EWARN(" Can't find service '%s' needed by '%s'; continuing...\n",
+ dependency, servicename);
+
+ retval = service_add_dependency(servicename, dependency, BROKEN);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+
+ /* Delete invalid entry */
+ goto remove;
+ }
+
+ /* For the rest, if the dependency is not 'net', just silently
+ * die without error. Should not be needed as we add a 'net'
+ * service manually before we start, but you never know ... */
+ if (0 != strcmp(dependency, "net")) {
+ /* Delete invalid entry */
+ goto remove;
+ }
+ }
+
+ /* Ugly bug ... if a service depends on itself, it creates a
+ * 'mini fork bomb' effect, and breaks things horribly ... */
+ if (0 == strcmp(servicename, dependency)) {
+ /* Dont work too well with the '*' before and after */
+ if ((type != BEFORE) && (type != AFTER))
+ EWARN(" Service '%s' can't depend on itself; continuing...\n",
+ servicename);
+
+ /* Delete invalid entry */
+ goto remove;
+ }
+
+ /* Currently only these depend/order types are supported */
+ if ((type == NEED)
+ || (type == USE)
+ || (type == BEFORE)
+ || (type == AFTER)) {
+ if (type == BEFORE) {
+ /* NEED and USE override BEFORE
+ * ('servicename' BEFORE 'dependency') */
+ if ((0 == service_is_dependency(servicename, dependency, NEED))
+ || (0 == service_is_dependency(servicename, dependency, USE))) {
+ /* Delete invalid entry */
+ goto remove;
+ }
+ }
+
+ if (type == AFTER) {
+ /* NEED and USE override AFTER
+ * ('servicename' AFTER 'dependency') */
+ if ((0 == service_is_dependency(dependency, servicename, NEED))
+ || (0 == service_is_dependency(dependency, servicename, USE))) {
+ /* Delete invalid entry */
+ goto remove;
+ }
+ }
+
+ /* We do not want to add circular dependencies ... */
+ if (0 == service_is_dependency(dependency, servicename, type)) {
+ EWARN(" Services '%s' and '%s' have circular\n",
+ servicename, dependency);
+ EWARN(" dependency of type '%s'; continuing...\n",
+ service_type_names[type]);
+
+ /* For now remove this dependency */
+ goto remove;
+ }
+
+ /* Reverse mapping */
+ if (type == NEED) {
+ retval = service_add_dependency(dependency, servicename, NEED_ME);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+
+ /* Reverse mapping */
+ if (type == USE) {
+ retval = service_add_dependency(dependency, servicename, USE_ME);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+
+ /* Reverse mapping */
+ if (type == BEFORE) {
+ retval = service_add_dependency(dependency, servicename, AFTER);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+
+ /* Reverse mapping */
+ if (type == AFTER) {
+ retval = service_add_dependency(dependency, servicename, BEFORE);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+
+remove:
+ /* Delete invalid entry */
+ DBG_MSG("Removing invalid dependency '%s' of service '%s', type '%s'.\n",
+ dependency, servicename, service_type_names[type]);
+
+ retval = service_del_dependency(servicename, dependency, type);
+ if (-1 == retval) {
+ DBG_MSG("Failed to delete dependency!\n");
+ return -1;
+ }
+
+ /* Here we should not die with error */
+ return 0;
+}
+
+int service_resolve_dependencies(void)
+{
+ service_info_t *info;
+ char *service;
+ char *next = NULL;
+ int count;
+
+ /* Add our 'net' service */
+ if (NULL == service_get_info("net")) {
+ if (-1 == service_add("net")) {
+ DBG_MSG("Failed to add virtual!\n");
+ return -1;
+ }
+ service_set_mtime("net", 0);
+ }
+
+ /* Calculate all virtuals */
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[PROVIDE], service, next, count)
+ if (-1 == service_add_virtual(info->name, service)) {
+ DBG_MSG("Failed to add virtual!\n");
+ return -1;
+ }
+ }
+
+ /* Now do NEED, USE, BEFORE and AFTER */
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[NEED], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, NEED)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[USE], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, USE)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[BEFORE], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, BEFORE)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[AFTER], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, AFTER)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/core/librcscripts/depend.h b/src/core/librcscripts/depend.h
new file mode 100644
index 0000000..2a7ff77
--- /dev/null
+++ b/src/core/librcscripts/depend.h
@@ -0,0 +1,75 @@
+/*
+ * depend.h
+ *
+ * Dependancy engine for Gentoo style rc-scripts.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#ifndef _DEPEND_H
+#define _DEPEND_H
+
+#include <sys/types.h>
+#include "list.h"
+
+/* Dependency types supported or still to be implemented */
+typedef enum {
+ NEED, /* All dependencies needed by specified service */
+ NEED_ME, /* All dependencies that need specified service */
+ USE, /* All dependencies used by specified service */
+ USE_ME, /* All dependencies that use specified service */
+ BEFORE, /* All services started before specified service */
+ AFTER, /* All services started after specified service */
+ BROKEN, /* All dependencies of type NEED missing for
+ specified service */
+ PROVIDE, /* All virtual services provided by specified service */
+ ALL_SERVICE_TYPE_T
+} service_type_t;
+
+/* Names for above service types (service_type_t).
+ * Note that this should sync with above service_type_t */
+extern char *service_type_names[];
+
+typedef struct {
+ struct list_head node;
+
+ char *name; /* Name of service */
+ char **depend_info[ALL_SERVICE_TYPE_T]; /* String lists for each service
+ type */
+ char *provide; /* Name of virtual service it
+ provides. This is only valid
+ after we resolving - thus after
+ service_resolve_dependencies() */
+ time_t mtime; /* Modification time of script */
+} service_info_t;
+
+struct list_head service_info_list;
+
+service_info_t *service_get_info(char *servicename);
+int service_add(char *servicename);
+int service_is_dependency(char *servicename, char *dependency, service_type_t type);
+int service_add_dependency(char *servicename, char *dependency, service_type_t type);
+int service_del_dependency(char *servicename, char *dependency, service_type_t type);
+service_info_t *service_get_virtual(char *virtual);
+int service_add_virtual(char *servicename, char* virtual);
+int service_set_mtime(char *servicename, time_t mtime);
+int service_resolve_dependencies(void);
+
+#endif /* _DEPEND_H */
+
diff --git a/src/core/librcscripts/list.h b/src/core/librcscripts/list.h
new file mode 100644
index 0000000..5c478bc
--- /dev/null
+++ b/src/core/librcscripts/list.h
@@ -0,0 +1,446 @@
+/*
+ * Copied from the Linux kernel source tree, version 2.6.0-test1.
+ *
+ * Licensed under the GPL v2 as per the whole kernel source tree.
+ *
+ * Ripped out the rcu stuff, as it's not needed.
+ *
+ * $Header$
+ */
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+//#include <linux/stddef.h>
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+//#include <linux/prefetch.h>
+static inline void prefetch(const void *x) {;}
+
+//#include <asm/system.h>
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static __inline__ int hlist_unhashed(struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static __inline__ int hlist_empty(struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static __inline__ void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static __inline__ void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+static __inline__ void hlist_del_init(struct hlist_node *n)
+{
+ if (n->pprev) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+static __inline__ void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static __inline__ void hlist_add_before(struct hlist_node *n, struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static __inline__ void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ *(next->pprev) = n;
+ n->next = next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+/* Cannot easily do prefetch unfortunately */
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; n = pos ? pos->next : 0, pos; \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+#endif
diff --git a/src/core/librcscripts/misc.c b/src/core/librcscripts/misc.c
new file mode 100644
index 0000000..c589143
--- /dev/null
+++ b/src/core/librcscripts/misc.c
@@ -0,0 +1,659 @@
+/*
+ * misc.c
+ *
+ * Miscellaneous macro's and functions.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "debug.h"
+#include "misc.h"
+
+char *memrepchr(char **str, char old, char new, size_t size)
+{
+ char *str_p;
+
+ if ((NULL == str)
+ || (NULL == *str)
+ || (0 == strlen(*str))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ str_p = memchr(*str, old, size);
+
+ while (NULL != str_p) {
+ str_p[0] = new;
+ str_p = memchr(&str_p[1], old, size - (str_p - *str) - 1);
+ }
+
+ return *str;
+}
+
+char *strcatpaths(const char *pathname1, const char *pathname2)
+{
+ char *new_path = NULL;
+ int lenght;
+
+ if ((NULL == pathname1)
+ || (0 == strlen(pathname1))
+ || (NULL == pathname2)
+ || (0 == strlen(pathname2))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Lenght of pathname1 + lenght of pathname2 + '/' if needed */
+ lenght = strlen(pathname1) + strlen(pathname2) + 1;
+ /* lenght + '\0' */
+ new_path = malloc(lenght + 1);
+ if (NULL == new_path) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return NULL;
+ }
+
+ strncpy(new_path, pathname1, lenght);
+ /* Should we add a '/' ? */
+ if (new_path[strlen(new_path)-1] != '/')
+ strncat(new_path, "/", lenght - strlen(new_path));
+ strncat(new_path, pathname2, lenght - strlen(new_path));
+
+ return new_path;
+}
+
+char *strndup(const char *str, size_t size)
+{
+ char *new_str = NULL;
+ size_t len;
+
+ if (NULL == str) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Check lenght of str without breaching the size limit */
+ for (len = 0; (len < size) && ('\0' != str[len]); len++);
+
+ new_str = malloc(len + 1);
+ if (NULL == new_str) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return NULL;
+ }
+
+ /* Make sure our string is NULL terminated */
+ new_str[len] = '\0';
+
+ return (char *)memcpy(new_str, str, len);
+}
+
+char *gbasename(const char *path)
+{
+ char *new_path = NULL;
+
+ if ((NULL == path) || (0 == strlen(path))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Copied from glibc */
+ new_path = strrchr (path, '/');
+ return new_path ? new_path + 1 : (char *)path;
+}
+
+
+int exists(const char *pathname)
+{
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = lstat(pathname, &buf);
+ if (-1 != retval)
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int is_file(const char *pathname, int follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+ if ((-1 != retval) && (S_ISREG(buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int is_link(const char *pathname)
+{
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = lstat(pathname, &buf);
+ if ((-1 != retval) && (S_ISLNK(buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int is_dir(const char *pathname, int follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+ if ((-1 != retval) && (S_ISDIR(buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+time_t get_mtime(const char *pathname, int follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+ if (-1 != retval)
+ return buf.st_mtime;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+#ifdef __KLIBC__
+int remove(const char *pathname)
+{
+ int retval;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (1 == is_dir(pathname, 0))
+ retval = rmdir(pathname);
+ else
+ retval = unlink(pathname);
+
+ return retval;
+}
+#endif
+
+int mktree(const char *pathname, mode_t mode)
+{
+ char *temp_name = NULL;
+ char *temp_token = NULL;
+ char *token_p;
+ char *token;
+ int retval;
+ int lenght;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Lenght of 'pathname' + extra for "./" if needed */
+ lenght = strlen(pathname) + 2;
+ /* lenght + '\0' */
+ temp_name = malloc(lenght + 1);
+ if (NULL == temp_name) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ return -1;
+ }
+
+ temp_token = strndup(pathname, strlen(pathname));
+ if (NULL == temp_token) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ token_p = temp_token;
+
+ if (pathname[0] == '/')
+ temp_name[0] = '\0';
+ else
+ /* If not an absolute path, make it local */
+ strncpy(temp_name, ".", lenght);
+
+ token = strsep(&token_p, "/");
+ /* First token might be "", but that is OK as it will be when the
+ * pathname starts with '/' */
+ while (NULL != token) {
+ strncat(temp_name, "/", lenght - strlen(temp_name));
+ strncat(temp_name, token, lenght - strlen(temp_name));
+
+ /* If it does not exist, create the dir. If it does exit,
+ * but is not a directory, we will catch it below. */
+ if (1 != exists(temp_name)) {
+ retval = mkdir(temp_name, mode);
+ if (-1 == retval) {
+ DBG_MSG("Failed to create directory!\n");
+ goto error;
+ }
+ /* Not a directory or symlink pointing to a directory */
+ } else if (1 != is_dir(temp_name, 1)) {
+ DBG_MSG("Component in pathname is not a directory!\n");
+ errno = ENOTDIR;
+ goto error;
+ }
+
+ do {
+ token = strsep(&token_p, "/");
+ /* The first "" was Ok, but rather skip double '/' after that */
+ } while ((NULL != token) && (0 == strlen(token)));
+ }
+
+ free(temp_name);
+ free(temp_token);
+
+ return 0;
+
+error:
+ free(temp_name);
+ free(temp_token);
+
+ return -1;
+}
+
+int rmtree(const char *pathname)
+{
+ char **dirlist = NULL;
+ int i = 0;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (1 != exists(pathname)) {
+ DBG_MSG("'%s' does not exists!\n", pathname);
+ errno = ENOENT;
+ return -1;
+ }
+
+ dirlist = ls_dir(pathname, 1);
+ if ((NULL == dirlist) && (0 != errno)) {
+ DBG_MSG("Could not get listing for '%s'!\n", pathname);
+ return -1;
+ }
+
+ while ((NULL != dirlist) && (NULL != dirlist[i])) {
+ /* If it is a directory, call rmtree() again with
+ * it as argument */
+ if (1 == is_dir(dirlist[i], 0)) {
+ if (-1 == rmtree(dirlist[i])) {
+ DBG_MSG("Failed to delete sub directory!\n");
+ goto error;
+ }
+ }
+
+ /* Now actually remove it. Note that if it was a directory,
+ * it should already be removed by above rmtree() call */
+ if ((1 == exists(dirlist[i]) && (-1 == remove(dirlist[i])))) {
+ DBG_MSG("Failed to remove '%s'!\n", dirlist[i]);
+ goto error;
+ }
+ i++;
+ }
+
+ STRING_LIST_FREE(dirlist);
+
+ /* Now remove the parent */
+ if (-1 == remove(pathname)) {
+ DBG_MSG("Failed to remove '%s'!\n", pathname);
+ goto error;
+ }
+
+ return 0;
+error:
+ STRING_LIST_FREE(dirlist);
+
+ return -1;
+}
+
+char **ls_dir(const char *pathname, int hidden)
+{
+ DIR *dirfd;
+ struct dirent *dir_entry;
+ char **dirlist = NULL;
+
+ if ((NULL == pathname) || (0 == strlen(pathname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ dirfd = opendir(pathname);
+ if (NULL == dirfd) {
+ DBG_MSG("Failed to call opendir()!\n");
+ /* errno will be set by opendir() */
+ goto error;
+ }
+
+ do {
+ /* Clear errno to distinguish between EOF and error */
+ errno = 0;
+ dir_entry = readdir(dirfd);
+ /* Only an error if 'errno' != 0, else EOF */
+ if ((NULL == dir_entry) && (0 != errno)) {
+ DBG_MSG("Failed to call readdir()!\n");
+ goto error;
+ }
+ if ((NULL != dir_entry) &&
+ /* Should we display hidden files? */
+ (hidden ? 1 : dir_entry->d_name[0] != '.'))
+ {
+ char *d_name = dir_entry->d_name;
+ char *tmp_p;
+
+ /* Do not list current or parent entries */
+ if ((0 == strcmp(d_name, ".")) ||
+ (0 == strcmp(d_name, "..")))
+ continue;
+
+ tmp_p = strcatpaths(pathname, d_name);
+ if (NULL == tmp_p) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ goto error;
+ }
+
+ STRING_LIST_ADD(dirlist, tmp_p, error);
+ }
+ } while (NULL != dir_entry);
+
+ if ((NULL == dirlist) || (NULL == dirlist[0]))
+ DBG_MSG("Directory is empty.\n");
+
+ closedir(dirfd);
+
+ return dirlist;
+
+error:
+ /* Free dirlist on error */
+ STRING_LIST_FREE(dirlist);
+
+ if (NULL != dirfd) {
+ int old_errno = errno;
+ closedir(dirfd);
+ /* closedir() might have changed it */
+ errno = old_errno;
+ }
+
+ return NULL;
+}
+
+/* This handles simple 'entry="bar"' type variables. If it is more complex
+ * ('entry="$(pwd)"' or such), it will obviously not work, but current behaviour
+ * should be fine for the type of variables we want. */
+char *get_cnf_entry(const char *pathname, const char *entry)
+{
+ char *buf = NULL;
+ char *tmp_buf = NULL;
+ char *tmp_p;
+ char *value = NULL;
+ char *token;
+ size_t lenght;
+ int count;
+ int current = 0;
+
+
+ if ((NULL == pathname)
+ || (0 == strlen(pathname))
+ || (NULL == entry)
+ || (0 == strlen(entry))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* If it is not a file or symlink pointing to a file, bail */
+ if (1 != is_file(pathname, 1)) {
+ DBG_MSG("Given pathname is not a file or do not exist!\n");
+ /* FIXME: Might need to set this to something better? */
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (-1 == file_map(pathname, &buf, &lenght)) {
+ DBG_MSG("Could not open config file for reading!\n");
+ return NULL;
+ }
+
+ while (current < lenght) {
+ count = buf_get_line(buf, lenght, current);
+
+ tmp_buf = strndup(&buf[current], count);
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ tmp_p = tmp_buf;
+
+ /* Strip leading spaces/tabs */
+ while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
+ tmp_p++;
+
+ /* Get entry and value */
+ token = strsep(&tmp_p, "=");
+ /* Bogus entry or value */
+ if (NULL == token)
+ goto _continue;
+
+ /* Make sure we have a string that is larger than 'entry', and
+ * the first part equals 'entry' */
+ if ((strlen(token) > 0) && (0 == strcmp(entry, token)))
+ {
+ do {
+ /* Bash variables are usually quoted */
+ token = strsep(&tmp_p, "\"\'");
+ /* If quoted, the first match will be "" */
+ } while ((NULL != token) && (0 == strlen(token)));
+
+ /* We have a 'entry='. We respect bash rules, so NULL
+ * value for now (if not already) */
+ if (NULL == token) {
+ /* We might have 'entry=' and later 'entry="bar"',
+ * so just continue for now ... we will handle
+ * it below when 'value == NULL' */
+ if (NULL != value) {
+ free(value);
+ value = NULL;
+ }
+ goto _continue;
+ }
+
+ /* If we have already allocated 'value', free it */
+ if (NULL != value)
+ free(value);
+
+ value = strndup(token, strlen(token));
+ if (NULL == value) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+
+ /* We do not break, as there might be more than one entry
+ * defined, and as bash uses the last, so should we */
+ /* break; */
+ }
+
+_continue:
+ current += count + 1;
+ free(tmp_buf);
+ /* Set to NULL in case we error out above and have
+ * to free below */
+ tmp_buf = NULL;
+ }
+
+
+ if (NULL == value)
+ DBG_MSG("Failed to get value for config entry '%s'!\n", entry);
+
+ file_unmap(buf, lenght);
+
+ return value;
+
+error:
+ free(tmp_buf);
+ free(value);
+
+ if (NULL != buf) {
+ int old_errno = errno;
+ file_unmap(buf, lenght);
+ /* unmmap() might have changed it */
+ errno = old_errno;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Below three functions (file_map, file_unmap and buf_get_line) are
+ * from udev-050 (udev_utils.c).
+ * (Some are slightly modified, please check udev for originals.)
+ *
+ * Copyright (C) 2004 Kay Sievers <kay@vrfy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+int file_map(const char *filename, char **buf, size_t *bufsize)
+{
+ struct stat stats;
+ int fd;
+ int old_errno;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ DBG_MSG("Failed to open file!\n");
+ return -1;
+ }
+
+ if (fstat(fd, &stats) < 0) {
+ DBG_MSG("Failed to stat file!\n");
+ old_errno = errno;
+ close(fd);
+ /* close() might have changed it */
+ errno = old_errno;
+ return -1;
+ }
+
+ *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (*buf == MAP_FAILED) {
+ DBG_MSG("Failed to mmap file!\n");
+ old_errno = errno;
+ close(fd);
+ /* close() might have changed it */
+ errno = old_errno;
+ return -1;
+ }
+ *bufsize = stats.st_size;
+
+ close(fd);
+
+ return 0;
+}
+
+void file_unmap(char *buf, size_t bufsize)
+{
+ munmap(buf, bufsize);
+}
+
+size_t buf_get_line(char *buf, size_t buflen, size_t cur)
+{
+ size_t count = 0;
+
+ for (count = cur; count < buflen && buf[count] != '\n'; count++);
+
+ return count - cur;
+}
+
diff --git a/src/core/librcscripts/misc.h b/src/core/librcscripts/misc.h
new file mode 100644
index 0000000..400e580
--- /dev/null
+++ b/src/core/librcscripts/misc.h
@@ -0,0 +1,282 @@
+/*
+ * misc.h
+ *
+ * Miscellaneous macro's and functions.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#ifndef _MISC_H
+#define _MISC_H
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+/* Gentoo style e* printing macro's */
+#define EINFO(_args...) \
+ do { \
+ int old_errno = errno; \
+ printf(" \033[32;01m*\033[0m " _args); \
+ errno = old_errno; \
+ } while (0)
+
+#define EWARN(_args...) \
+ do { \
+ int old_errno = errno; \
+ printf(" \033[33;01m*\033[0m " _args); \
+ errno = old_errno; \
+ } while (0)
+
+#define EERROR(_args...) \
+ do { \
+ int old_errno = errno; \
+ fprintf(stderr, " \033[31;01m*\033[0m " _args); \
+ errno = old_errno; \
+ } while (0)
+
+/* Min/Max macro's */
+#ifdef MAX
+# undef MAX
+#endif
+#define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b))
+#ifdef MIN
+# undef MIN
+#endif
+#define MIN(_a, _b) ((_a) > (_b) ? (_b) : (_a))
+
+/* Return true if filename '_name' ends in '_ext' */
+#define CHECK_FILE_EXTENSION(_name, _ext) \
+ ((strlen(_name) > strlen(_ext)) \
+ && (0 == strncmp(&(_name[strlen(_name) - strlen(_ext)]), \
+ _ext, strlen(_ext))))
+
+/* Add a new item to a string list. If the pointer to the list is NULL,
+ * allocate enough memory for the amount of entries needed. Ditto for
+ * when it already exists, but we add one more entry than it can
+ * contain. The list is NULL terminated.
+ * NOTE: _only_ memory for the list are allocated, and not for the items - that
+ * should be done by relevant code (unlike STRING_LIST_DEL that will
+ * free the memory) */
+#define STRING_LIST_ADD(_string_list, _item, _error) \
+ do { \
+ char **_tmp_p; \
+ int _i = 0; \
+ if ((NULL == _item) || (0 == strlen(_item))) { \
+ DBG_MSG("Invalid argument passed!\n"); \
+ errno = EINVAL; \
+ goto _error; \
+ } \
+ while ((NULL != _string_list) && (NULL != _string_list[_i])) { \
+ _i++; \
+ } \
+ /* Amount of entries + new + terminator */ \
+ _tmp_p = realloc(_string_list, sizeof(char *) * (_i + 2)); \
+ if (NULL == _tmp_p) { \
+ DBG_MSG("Failed to reallocate list!\n"); \
+ goto _error; \
+ } \
+ _string_list = _tmp_p; \
+ _string_list[_i] = _item; \
+ /* Terminator */ \
+ _string_list[_i+1] = NULL; \
+ } while (0)
+
+/* Add a new item to a string list (foundamental the same as above), but make
+ * sure we have all the items alphabetically sorted. */
+#define STRING_LIST_ADD_SORT(_string_list, _item, _error) \
+ do { \
+ char **_tmp_p; \
+ char *_str_p1; \
+ char *_str_p2; \
+ int _i = 0; \
+ if ((NULL == _item) || (0 == strlen(_item))) { \
+ DBG_MSG("Invalid argument passed!\n"); \
+ errno = EINVAL; \
+ goto _error; \
+ } \
+ while ((NULL != _string_list) && (NULL != _string_list[_i])) \
+ _i++; \
+ /* Amount of entries + new + terminator */ \
+ _tmp_p = realloc(_string_list, sizeof(char *) * (_i + 2)); \
+ if (NULL == _tmp_p) { \
+ DBG_MSG("Failed to reallocate list!\n"); \
+ goto _error; \
+ } \
+ _string_list = _tmp_p; \
+ if (0 == _i) \
+ /* Needed so that the end NULL will propagate
+ * (iow, make sure our 'NULL != _str_p1' test below
+ * do not fail) */ \
+ _string_list[_i] = NULL; \
+ /* Actual terminator that needs adding */ \
+ _string_list[_i+1] = NULL; \
+ _i = 0; \
+ /* See where we should insert the new item to have it all \
+ * alphabetically sorted */ \
+ while (NULL != _string_list[_i]) { \
+ if (strcmp(_string_list[_i], _item) > 0) { \
+ break; \
+ } \
+ _i++; \
+ } \
+ /* Now just insert the new item, and shift the rest one over.
+ * '_str_p2' is temporary storage to swap the indexes in a loop,
+ * and 'str_p1' is used to store the old value across the loop */ \
+ _str_p1 = _string_list[_i]; \
+ _string_list[_i] = _item; \
+ do { \
+ _i++;\
+ _str_p2 = _string_list[_i]; \
+ _string_list[_i] = _str_p1; \
+ _str_p1 = _str_p2; \
+ } while (NULL != _str_p1); \
+ } while (0)
+
+/* Delete one entry from the string list, and shift the rest down if the entry
+ * was not at the end. For now we do not resize the amount of entries the
+ * string list can contain, and free the memory for the matching item */
+#define STRING_LIST_DEL(_string_list, _item, _error) \
+ do { \
+ int _i = 0; \
+ if ((NULL == _item) \
+ || (0 == strlen(_item)) \
+ || (NULL == _string_list)) { \
+ DBG_MSG("Invalid argument passed!\n"); \
+ errno = EINVAL; \
+ goto _error; \
+ } \
+ while (NULL != _string_list[_i]) { \
+ if (0 == strcmp(_item, _string_list[_i])) \
+ break; \
+ else \
+ _i++; \
+ } \
+ if (NULL == _string_list[_i]) { \
+ DBG_MSG("Invalid argument passed!\n"); \
+ errno = EINVAL; \
+ goto _error; \
+ } \
+ free(_string_list[_i]); \
+ /* Shift all the following items one forward */ \
+ do { \
+ _string_list[_i] = _string_list[_i+1]; \
+ /* This stupidity is to shutup gcc */ \
+ _i++; \
+ } while (NULL != _string_list[_i]); \
+ } while (0)
+
+/* Step through each entry in the string list, setting '_pos' to the
+ * beginning of the entry. '_counter' is used by the macro as index,
+ * but should not be used by code as index (or if really needed, then
+ * it should usually by +1 from what you expect, and should only be
+ * used in the scope of the macro) */
+#define STRING_LIST_FOR_EACH(_string_list, _pos, _counter) \
+ if ((NULL != _string_list) && (0 == (_counter = 0))) \
+ while (NULL != (_pos = _string_list[_counter++]))
+
+/* Same as above (with the same warning about '_counter'). Now we just
+ * have '_next' that are also used for indexing. Once again rather refrain
+ * from using it if not absolutely needed. The major difference to above,
+ * is that it should be safe from having the item removed from under you. */
+#define STRING_LIST_FOR_EACH_SAFE(_string_list, _pos, _next, _counter) \
+ if ((NULL != _string_list) \
+ && (0 == (_counter = 0))) \
+ /* First part of the while checks if this is the
+ * first loop, and if so setup _pos and _next
+ * and increment _counter */ \
+ while ((((0 == _counter) \
+ && (NULL != (_pos = _string_list[_counter])) \
+ && (_pos != (_next = _string_list[++_counter]))) \
+ /* Second part is when it is not the first loop
+ * and _pos was not removed from under us. We
+ * just increment _counter, and setup _pos and
+ * _next */ \
+ || ((0 != _counter) \
+ && (_pos == _string_list[_counter-1]) \
+ && (_next == _string_list[_counter]) \
+ && (NULL != (_pos = _string_list[_counter])) \
+ && (_pos != (_next = _string_list[++_counter]))) \
+ /* Last part is when _pos was removed from under
+ * us. We basically just setup _pos and _next,
+ * but leave _counter alone */ \
+ || ((0 != _counter) \
+ && (_pos != _string_list[_counter-1]) \
+ && (_next == _string_list[_counter-1]) \
+ && (NULL != (_pos = _string_list[_counter-1])) \
+ && (_pos != (_next = _string_list[_counter])))))
+
+/* Just free the whole string list */
+#define STRING_LIST_FREE(_string_list) \
+ do { \
+ if (NULL != _string_list) { \
+ int _i = 0; \
+ while (NULL != _string_list[_i]) \
+ free(_string_list[_i++]); \
+ free(_string_list); \
+ _string_list = NULL; \
+ } \
+ } while (0)
+
+/* String functions. Return a string on success, or NULL on error
+ * or no action taken. On error errno will be set.*/
+char *memrepchr(char **str, char old, char _new, size_t size);
+/* Concat two paths adding '/' if needed. Memory will be allocated
+ * with the malloc() call. */
+char *strcatpaths(const char *pathname1, const char *pathname2);
+
+/* Compat functions for GNU extensions */
+char *strndup(const char *str, size_t size);
+/* Same as basename(3), but do not modify path */
+char *gbasename(const char *path);
+
+/* The following functions do not care about errors - they only return
+ * 1 if 'pathname' exist, and is the type requested, or else 0.
+ * They also might clear errno */
+int exists(const char *pathname);
+int is_file(const char *pathname, int follow_link);
+int is_link(const char *pathname);
+int is_dir(const char *pathname, int follow_link);
+
+/* The following function do not care about errors - it only returns
+ * the mtime of 'pathname' if it exists, and is the type requested,
+ * or else 0. It also might clear errno */
+time_t get_mtime(const char *pathname, int follow_link);
+
+/* The following functions return 0 on success, or -1 with errno set on error. */
+#ifdef __KLIBC__
+int remove(const char *pathname);
+#endif
+int mktree(const char *pathname, mode_t mode);
+int rmtree(const char *pathname);
+
+/* The following return a pointer on success, or NULL with errno set on error.
+ * If it returned NULL, but errno is not set, then there was no error, but
+ * there is nothing to return. */
+char **ls_dir(const char *pathname, int hidden);
+char *get_cnf_entry(const char *pathname, const char *entry);
+
+/* Below three functions (file_map, file_unmap and buf_get_line) are from
+ * udev-050 (udev_utils.c). Please see misc.c for copyright info.
+ * (Some are slightly modified, please check udev for originals.) */
+int file_map(const char *filename, char **buf, size_t *bufsize);
+void file_unmap(char *buf, size_t bufsize);
+size_t buf_get_line(char *buf, size_t buflen, size_t cur);
+
+#endif /* _MISC_H */
+
diff --git a/src/core/librcscripts/parse.c b/src/core/librcscripts/parse.c
new file mode 100644
index 0000000..1131c73
--- /dev/null
+++ b/src/core/librcscripts/parse.c
@@ -0,0 +1,982 @@
+/*
+ * parse.c
+ *
+ * Parser for Gentoo style rc-scripts.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "rcscripts.h"
+#include "debug.h"
+#include "depend.h"
+#include "list.h"
+#include "misc.h"
+#include "parse.h"
+#include "simple-regex.h"
+
+#define READ_PIPE 0
+#define WRITE_PIPE 1
+
+/* _pipe[0] is used to send data to the parent (thus the parent only use the
+ * read pipe, and the child uses the write pipe)
+ * _pipe[1] is used to send data to the child (thus the child only use the read
+ * pipe, and the parent uses the write pipe)
+ */
+#define PARENT_READ_PIPE(_pipe) (_pipe[0][READ_PIPE])
+#define PARENT_WRITE_PIPE(_pipe) (_pipe[1][WRITE_PIPE])
+#define CHILD_READ_PIPE(_pipe) (_pipe[1][READ_PIPE])
+#define CHILD_WRITE_PIPE(_pipe) (_pipe[0][WRITE_PIPE])
+
+#define PARSE_BUFFER_SIZE 256
+
+#define OUTPUT_MAX_LINE_LENGHT 256
+#define OUTPUT_BUFFER_SIZE (60 * 2048)
+
+/* void PRINT_TO_BUFFER(char **_buf, int _count, label _error, format) */
+#define PRINT_TO_BUFFER(_buf, _count, _error, _output...) \
+ do { \
+ int _i = 0; \
+ /* FIXME: Might do something more dynamic here */ \
+ if (OUTPUT_BUFFER_SIZE < (_count + OUTPUT_MAX_LINE_LENGHT)) { \
+ errno = ENOMEM; \
+ DBG_MSG("Output buffer size too small!\n"); \
+ goto _error; \
+ } \
+ _i = sprintf(&((*_buf)[_count]), _output); \
+ if (0 < _i) \
+ _count += _i + 1; \
+ } while (0)
+
+LIST_HEAD(rcscript_list);
+
+size_t parse_rcscript(char *scriptname, char **data, size_t index);
+
+size_t parse_print_start(char **data, size_t index);
+size_t parse_print_header(char *scriptname, char **data, size_t index);
+size_t parse_print_body(char *scriptname, char **data, size_t index);
+
+int get_rcscripts(void)
+{
+ rcscript_info_t *info;
+ char **file_list = NULL;
+ char *rcscript;
+ char *confd_file = NULL;
+ int count;
+
+ file_list = ls_dir(RCSCRIPTS_INITDDIR, 0);
+ if (NULL == file_list) {
+ DBG_MSG("'%s' is empty!\n", RCSCRIPTS_INITDDIR);
+ return -1;
+ }
+
+ STRING_LIST_FOR_EACH(file_list, rcscript, count) {
+ /* Is it a file? */
+ if (!(is_file(rcscript, 1))
+ /* Do not process scripts, source or backup files. */
+ || (CHECK_FILE_EXTENSION(rcscript, ".c"))
+ || (CHECK_FILE_EXTENSION(rcscript, ".bak"))
+ || (CHECK_FILE_EXTENSION(rcscript, "~"))) {
+ DBG_MSG("'%s' is not a valid rc-script!\n",
+ gbasename(rcscript));
+ } else {
+ DBG_MSG("Adding rc-script '%s' to list.\n",
+ gbasename(rcscript));
+
+ info = malloc(sizeof(rcscript_info_t));
+ if (NULL == info) {
+ DBG_MSG("Failed to allocate rcscript_info_t!\n");
+ goto error;
+ }
+
+ /* Copy the name */
+ info->filename = strndup(rcscript, strlen(rcscript));
+ if (NULL == info->filename) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ goto loop_error;
+ }
+
+ /* Get the modification time */
+ info->mtime = get_mtime(rcscript, 1);
+ if (0 == info->mtime) {
+ DBG_MSG("Failed to get modification time for '%s'!\n",
+ rcscript);
+ /* We do not care if it fails - we will pick up
+ * later if there is a problem with the file */
+ }
+
+ /* File name for the conf.d config file (if any) */
+ confd_file = strcatpaths(RCSCRIPTS_CONFDDIR, gbasename(rcscript));
+ if (NULL == confd_file) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto loop_error;
+ }
+
+ /* Get the modification time of the conf.d file
+ * (if any exists) */
+ info->confd_mtime = get_mtime(confd_file, 1);
+ if (0 == info->confd_mtime) {
+ DBG_MSG("Failed to get modification time for '%s'!\n",
+ confd_file);
+ /* We do not care that it fails, as not all
+ * rc-scripts will have conf.d config files */
+ }
+
+ free(confd_file);
+
+ list_add_tail(&info->node, &rcscript_list);
+
+ continue;
+
+loop_error:
+ if (NULL != info)
+ free(info->filename);
+ free(info);
+
+ goto error;
+ }
+ }
+
+ /* Final check if we have some entries */
+ if ((NULL == file_list) || (NULL == file_list[0])) {
+ DBG_MSG("No rc-scripts to parse!\n");
+ errno = ENOENT;
+ goto error;
+ }
+
+ STRING_LIST_FREE(file_list);
+
+ return 0;
+
+error:
+ STRING_LIST_FREE(file_list);
+
+ return -1;
+}
+
+/* Returns 0 if we do not need to regen the cache file, else -1 with
+ * errno set if something went wrong */
+int check_rcscripts_mtime(char *cachefile)
+{
+ rcscript_info_t *info;
+ time_t cache_mtime;
+ time_t rc_conf_mtime;
+ time_t rc_confd_mtime;
+
+ if ((NULL == cachefile) || (0 == strlen(cachefile))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ cache_mtime = get_mtime(cachefile, 1);
+ if (0 == cache_mtime) {
+ DBG_MSG("Could not get modification time for cache file '%s'!\n",
+ cachefile);
+ return -1;
+ }
+
+ /* Get and compare mtime for RC_CONF_FILE_NAME with that of cachefile */
+ rc_conf_mtime = get_mtime(RC_CONF_FILE_NAME, 1);
+ if (rc_conf_mtime > cache_mtime) {
+ DBG_MSG("'%s' have a later modification time than '%s'.\n",
+ RC_CONF_FILE_NAME, cachefile);
+ return -1;
+ }
+ /* Get and compare mtime for RC_CONFD_FILE_NAME with that of cachefile */
+ rc_confd_mtime = get_mtime(RC_CONFD_FILE_NAME, 1);
+ if (rc_confd_mtime > cache_mtime) {
+ DBG_MSG("'%s' have a later modification time than '%s'.\n",
+ RC_CONFD_FILE_NAME, cachefile);
+ return -1;
+ }
+
+ /* Get and compare mtime for each rc-script and its conf.d config file
+ * with that of cachefile */
+ list_for_each_entry(info, &rcscript_list, node) {
+ if ((info->mtime > cache_mtime)
+ || (info->confd_mtime > cache_mtime)) {
+ DBG_MSG("'%s' have a later modification time than '%s'.\n",
+ info->filename, cachefile);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Return count on success, -1 on error. If it was critical, errno will be set. */
+size_t parse_rcscript(char *scriptname, char **data, size_t index)
+{
+ regex_data_t tmp_data;
+ char *buf = NULL;
+ char *tmp_buf = NULL;
+ size_t write_count = index;
+ size_t lenght;
+ int count;
+ int current = 0;
+
+ if ((NULL == scriptname) || (0 == strlen(scriptname))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (-1 == file_map(scriptname, &buf, &lenght)) {
+ DBG_MSG("Could not open '%s' for reading!\n",
+ gbasename(scriptname));
+ return -1;
+ }
+
+ while (current < lenght) {
+ count = buf_get_line(buf, lenght, current);
+
+ tmp_buf = strndup(&(buf[current]), count);
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+
+ if (0 == current) {
+ /* Check if it starts with '#!/sbin/runscript' */
+ DO_REGEX(tmp_data, tmp_buf,
+ "[ \t]*#![ \t]*/sbin/runscript[ \t]*.*", error);
+ if (REGEX_FULL_MATCH != tmp_data.match) {
+ DBG_MSG("'%s' is not a valid rc-script!\n",
+ gbasename(scriptname));
+ goto error;
+ }
+
+ /* We do not want rc-scripts ending in '.sh' */
+ if (CHECK_FILE_EXTENSION(scriptname, ".sh")) {
+ EWARN("'%s' is invalid (should not end with '.sh')!\n",
+ gbasename(scriptname));
+ goto error;
+ }
+ DBG_MSG("Parsing '%s'.\n", gbasename(scriptname));
+
+ write_count = parse_print_header(gbasename(scriptname),
+ data, write_count);
+ if (-1 == write_count) {
+ DBG_MSG("Failed to call parse_print_header()!\n");
+ goto error;
+ }
+
+ goto _continue;
+ }
+
+ /* Check for lines with comments, and skip them */
+ DO_REGEX(tmp_data, tmp_buf, "^[ \t]*#", error);
+ if (REGEX_MATCH(tmp_data))
+ goto _continue;
+
+ /* If the line contains 'depend()', set 'got_depend' */
+ DO_REGEX(tmp_data, tmp_buf, "depend[ \t]*()[ \t]*{?", error);
+ if (REGEX_MATCH(tmp_data)) {
+ DBG_MSG("Got 'depend()' function.\n");
+
+ write_count = parse_print_body(gbasename(scriptname),
+ data, write_count);
+ if (-1 == write_count) {
+ DBG_MSG("Failed to call parse_print_body()!\n");
+ goto error;
+ }
+
+ /* Make sure this is the last loop */
+ current += lenght;
+ goto _continue;
+ }
+
+_continue:
+ current += count + 1;
+ free(tmp_buf);
+ }
+
+ file_unmap(buf, lenght);
+
+ return write_count;
+
+error:
+ free(tmp_buf);
+ if (NULL != buf) {
+ int old_errno = errno;
+ file_unmap(buf, lenght);
+ /* file_unmap() might have changed it */
+ errno = old_errno;
+ }
+
+ return -1;
+}
+
+
+size_t generate_stage1(char **data)
+{
+ rcscript_info_t *info;
+ size_t write_count = 0;
+ size_t tmp_count;
+
+ write_count = parse_print_start(data, write_count);
+ if (-1 == write_count) {
+ DBG_MSG("Failed to call parse_print_start()!\n");
+ return -1;
+ }
+
+ list_for_each_entry(info, &rcscript_list, node) {
+ tmp_count = parse_rcscript(info->filename, data, write_count);
+ if (-1 == tmp_count) {
+ DBG_MSG("Failed to parse '%s'!\n",
+ gbasename(info->filename));
+
+ /* If 'errno' is set, it is critical (hopefully) */
+ if (0 != errno)
+ return -1;
+ } else {
+ write_count = tmp_count;
+ }
+ }
+
+ return write_count;
+}
+
+/* Empty signal handler for SIGPIPE */
+static void sig_handler(int signum)
+{
+ return;
+}
+
+/* Returns data's lenght on success, else -1 on error. */
+size_t generate_stage2(char **data)
+{
+ int pipe_fds[2][2] = { { 0, 0 }, { 0, 0 } };
+ pid_t child_pid;
+ size_t write_count = 0;
+ int old_errno = 0;
+
+ /* Pipe to send data to parent */
+ if (-1 == pipe(pipe_fds[0])) {
+ DBG_MSG("Failed to open pipe!\n");
+ goto error;
+ }
+ /* Pipe to send data to child */
+ if (-1 == pipe(pipe_fds[1])) {
+ DBG_MSG("Failed to open pipe!\n");
+ /* Close parent_pfds */
+ goto error;
+ }
+
+ /* Zero data */
+ *data = NULL;
+
+ child_pid = fork();
+ if (-1 == child_pid) {
+ DBG_MSG("Failed to fork()!\n");
+ /* Close all pipes */
+ goto error;
+ }
+ if (0 == child_pid) {
+ /***
+ *** In child
+ ***/
+
+ char *const argv[] = {
+ "bash",
+ "--noprofile",
+ "--norc",
+ "--",
+ NULL
+ };
+
+ /* Close the sides of the pipes we do not use */
+ close(PARENT_WRITE_PIPE(pipe_fds));
+ close(PARENT_READ_PIPE(pipe_fds));
+
+ /* dup2 child side read pipe to STDIN */
+ dup2(CHILD_READ_PIPE(pipe_fds), STDIN_FILENO);
+ /* dup2 child side write pipe to STDOUT */
+ dup2(CHILD_WRITE_PIPE(pipe_fds), STDOUT_FILENO);
+
+ /* We need to be in RCSCRIPTS_INITDDIR for 'before'/'after' '*' to work */
+ if (-1 == chdir(RCSCRIPTS_INITDDIR)) {
+ DBG_MSG("Failed to chdir to '%s'!\n", RCSCRIPTS_INITDDIR);
+ exit(1);
+ }
+
+ if (-1 == execv(SHELL_PARSER, argv)) {
+ DBG_MSG("Failed to execv %s!\n", SHELL_PARSER);
+ exit(1);
+ }
+ } else {
+ /***
+ *** In parent
+ ***/
+
+ struct sigaction act_new;
+ struct sigaction act_old;
+ struct pollfd poll_fds[2];
+ char buf[PARSE_BUFFER_SIZE+1];
+ char *stage1_data = NULL;
+ size_t stage1_write_count = 0;
+ size_t stage1_written = 0;
+ int status = 0;
+
+ DBG_MSG("Child pid = %i\n", child_pid);
+
+ /* Set signal handler for SIGPIPE to empty in case bash errors
+ * out. It will then close the write pipe, and instead of us
+ * getting SIGPIPE, we can handle the write error like normal.
+ */
+ memset(&act_new, 0x00, sizeof(act_new));
+ act_new.sa_handler = (void (*) (int))sig_handler;
+ sigemptyset (&act_new.sa_mask);
+ act_new.sa_flags = 0;
+ sigaction(SIGPIPE, &act_new, &act_old);
+
+ /* Close the sides of the pipes we do not use */
+ close(CHILD_WRITE_PIPE(pipe_fds));
+ CHILD_WRITE_PIPE(pipe_fds) = 0;
+ close(CHILD_READ_PIPE(pipe_fds));
+ CHILD_READ_PIPE(pipe_fds) = 0;
+
+ stage1_data = malloc(OUTPUT_BUFFER_SIZE + 1);
+ if (NULL == stage1_data) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ goto error;
+ }
+
+ /* Pipe parse_rcscripts() to bash */
+ stage1_write_count = generate_stage1(&stage1_data);
+ if (-1 == stage1_write_count) {
+ DBG_MSG("Failed to generate stage1!\n");
+ goto error;
+ }
+
+#if 0
+ int tmp_fd = open("bar", O_CREAT | O_TRUNC | O_RDWR, 0600);
+ write(tmp_fd, stage1_data, stage1_write_count);
+ close(tmp_fd);
+#endif
+
+ do {
+ int tmp_count = 0;
+ int do_write = 0;
+ int do_read = 0;
+
+ /* Check if we can write or read */
+ poll_fds[WRITE_PIPE].fd = PARENT_WRITE_PIPE(pipe_fds);
+ poll_fds[WRITE_PIPE].events = POLLOUT;
+ poll_fds[READ_PIPE].fd = PARENT_READ_PIPE(pipe_fds);
+ poll_fds[READ_PIPE].events = POLLIN | POLLPRI;
+ if (stage1_written < stage1_write_count) {
+ poll(poll_fds, 2, -1);
+ if (poll_fds[WRITE_PIPE].revents & POLLOUT)
+ do_write = 1;
+ } else {
+ poll(&(poll_fds[READ_PIPE]), 1, -1);
+ }
+ if ((poll_fds[READ_PIPE].revents & POLLIN)
+ || (poll_fds[READ_PIPE].revents & POLLPRI))
+ do_read = 1;
+
+ do {
+ /* If we can write, or there is nothing to
+ * read, keep feeding the write pipe */
+ if ((stage1_written >= stage1_write_count)
+ || (1 == do_read)
+ || (1 != do_write))
+ break;
+
+ tmp_count = write(PARENT_WRITE_PIPE(pipe_fds),
+ &stage1_data[stage1_written],
+ strlen(&stage1_data[stage1_written]));
+ if ((-1 == tmp_count) && (EINTR != errno)) {
+ DBG_MSG("Error writing to PARENT_WRITE_PIPE!\n");
+ goto failed;
+ }
+ /* We were interrupted, try to write again */
+ if (-1 == tmp_count) {
+ errno = 0;
+ /* Make sure we retry */
+ tmp_count = 1;
+ continue;
+ }
+ /* What was written before, plus what
+ * we wrote now as well as the ending
+ * '\0' of the line */
+ stage1_written += tmp_count + 1;
+
+ /* Close the write pipe if we done
+ * writing to get a EOF signaled to
+ * bash */
+ if (stage1_written >= stage1_write_count) {
+ close(PARENT_WRITE_PIPE(pipe_fds));
+ PARENT_WRITE_PIPE(pipe_fds) = 0;
+ }
+ } while ((tmp_count > 0) && (stage1_written < stage1_write_count));
+
+ /* Reset tmp_count for below read loop */
+ tmp_count = 0;
+
+ do {
+ char *tmp_p;
+
+ if (1 != do_read)
+ continue;
+
+ tmp_count = read(PARENT_READ_PIPE(pipe_fds), buf,
+ PARSE_BUFFER_SIZE);
+ if ((-1 == tmp_count) && (EINTR != errno)) {
+ DBG_MSG("Error reading PARENT_READ_PIPE!\n");
+ goto failed;
+ }
+ /* We were interrupted, try to read again */
+ if ((-1 == tmp_count) || (0 == tmp_count)) {
+ errno = 0;
+ continue;
+ }
+
+ tmp_p = realloc(*data, write_count + tmp_count);
+ if (NULL == tmp_p) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ goto failed;
+ }
+
+ memcpy(&tmp_p[write_count], buf, tmp_count);
+
+ *data = tmp_p;
+ write_count += tmp_count;
+ } while (tmp_count > 0);
+ } while (!(poll_fds[READ_PIPE].revents & POLLHUP));
+
+failed:
+ /* Set old_errno to disable child exit code checking below */
+ if (0 != errno)
+ old_errno = errno;
+
+ free(stage1_data);
+
+ if (0 != PARENT_WRITE_PIPE(pipe_fds))
+ close(PARENT_WRITE_PIPE(pipe_fds));
+ close(PARENT_READ_PIPE(pipe_fds));
+
+ /* Restore the old signal handler for SIGPIPE */
+ sigaction(SIGPIPE, &act_old, NULL);
+
+ /* Wait for bash to finish */
+ waitpid(child_pid, &status, 0);
+ /* If old_errno is set, we had an error in the read loop, so do
+ * not worry about the child's exit code */
+ if (0 == old_errno) {
+ if ((!WIFEXITED(status)) || (0 != WEXITSTATUS(status))) {
+ DBG_MSG("Bash failed with status 0x%x!\n", status);
+ return -1;
+ }
+ } else {
+ /* Right, we had an error, so set errno, and exit */
+ errno = old_errno;
+ return -1;
+ }
+ }
+
+ return write_count;
+
+ /* Close parent side pipes */
+error:
+ /* Close all pipes */
+ old_errno = errno;
+ if (0 != CHILD_READ_PIPE(pipe_fds))
+ close(CHILD_READ_PIPE(pipe_fds));
+ if (0 != CHILD_WRITE_PIPE(pipe_fds))
+ close(CHILD_WRITE_PIPE(pipe_fds));
+ if (0 != PARENT_READ_PIPE(pipe_fds))
+ close(PARENT_READ_PIPE(pipe_fds));
+ if (0 != PARENT_WRITE_PIPE(pipe_fds))
+ close(PARENT_WRITE_PIPE(pipe_fds));
+ /* close() might have changed it */
+ errno = old_errno;
+
+ return -1;
+}
+
+int write_legacy_stage3(FILE *output)
+{
+ service_info_t *info;
+ char *service;
+ int count;
+ int index = 0;
+ int dep_count;
+ int i;
+
+ if (-1 == fileno(output)) {
+ DBG_MSG("Bad output stream!\n");
+ return -1;
+ }
+
+ fprintf(output, "rc_type_ineed=2\n");
+ fprintf(output, "rc_type_needsme=3\n");
+ fprintf(output, "rc_type_iuse=4\n");
+ fprintf(output, "rc_type_usesme=5\n");
+ fprintf(output, "rc_type_ibefore=6\n");
+ fprintf(output, "rc_type_iafter=7\n");
+ fprintf(output, "rc_type_broken=8\n");
+ fprintf(output, "rc_type_mtime=9\n");
+ fprintf(output, "rc_index_scale=10\n\n");
+ fprintf(output, "declare -a RC_DEPEND_TREE\n\n");
+
+ list_for_each_entry(info, &service_info_list, node) {
+ index++;
+ }
+ if (0 == index) {
+ EERROR("No services to generate dependency tree for!\n");
+ return -1;
+ }
+
+ fprintf(output, "RC_DEPEND_TREE[0]=%i\n\n", index);
+
+ index = 1;
+
+ list_for_each_entry(info, &service_info_list, node) {
+ fprintf(output, "RC_DEPEND_TREE[%i]=\"%s\"\n",
+ index * 10, info->name);
+
+ for (i = 0;i <= BROKEN;i++) {
+ dep_count = 0;
+
+ fprintf(output, "RC_DEPEND_TREE[%i+%i]=",
+ (index * 10), (i + 2));
+
+ STRING_LIST_FOR_EACH(info->depend_info[i], service, count) {
+ if (0 == dep_count)
+ fprintf(output, "\"%s", service);
+ else
+ fprintf(output, " %s", service);
+
+ dep_count++;
+ }
+
+ if (dep_count > 0)
+ fprintf(output, "\"\n");
+ else
+ fprintf(output, "\n");
+ }
+
+ fprintf(output, "RC_DEPEND_TREE[%i+9]=\"%li\"\n\n",
+ index * 10, info->mtime);
+ index++;
+ }
+
+ fprintf(output, "RC_GOT_DEPTREE_INFO=\"yes\"\n");
+
+ info = service_get_virtual("logger");
+ if (NULL == info) {
+ DBG_MSG("No service provides the 'logger' logger virtual!\n");
+ fprintf(output, "\nLOGGER_SERVICE=\n");
+ } else {
+ fprintf(output, "\nLOGGER_SERVICE=\"%s\"\n", info->name);
+ }
+
+
+ return 0;
+}
+
+int parse_cache(const char *data, size_t lenght)
+{
+ service_info_t *info;
+ service_type_t type = ALL_SERVICE_TYPE_T;
+ rcscript_info_t *rs_info;
+ char *tmp_buf = NULL;
+ char *rc_name = NULL;
+ char *tmp_p;
+ char *token;
+ char *field;
+ int count;
+ int current = 0;
+ int retval;
+
+ if ((NULL == data) || (lenght <= 0)) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ while (current < lenght) {
+ count = buf_get_line((char *)data, lenght, current);
+
+ tmp_buf = strndup(&(data[current]), count);
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ tmp_p = tmp_buf;
+
+ /* Strip leading spaces/tabs */
+ while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
+ tmp_p++;
+
+ /* Get FIELD name and FIELD value */
+ token = strsep(&tmp_p, " ");
+
+ /* FIELD name empty/bogus? */
+ if ((NULL == token)
+ || (0 == strlen(token))
+ /* We got an empty FIELD value */
+ || (NULL == tmp_p)
+ || (0 == strlen(tmp_p))) {
+ DBG_MSG("Parsing stopped due to short read!\n");
+ errno = EMSGSIZE;
+ goto error;
+ }
+
+ if (0 == strcmp(token, FIELD_RCSCRIPT)) {
+ DBG_MSG("Field = '%s', value = '%s'\n", token, tmp_p);
+
+ /* Add the service to the list, and initialize all data */
+ retval = service_add(tmp_p);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add %s to service list!\n", tmp_p);
+ goto error;
+ }
+
+ info = service_get_info(tmp_p);
+ if (NULL == info) {
+ DBG_MSG("Failed to get info for '%s'!\n", tmp_p);
+ goto error;
+ }
+ /* Save the rc-script name for next passes of loop */
+ rc_name = info->name;
+
+ goto _continue;
+ }
+
+ if (NULL == rc_name) {
+ DBG_MSG("Other fields should come after '%s'!\n", FIELD_RCSCRIPT);
+ goto error;
+ }
+
+ if (0 == strcmp(token, FIELD_NEED))
+ type = NEED;
+ else if (0 == strcmp(token, FIELD_USE))
+ type = USE;
+ else if (0 == strcmp(token, FIELD_BEFORE))
+ type = BEFORE;
+ else if (0 == strcmp(token, FIELD_AFTER))
+ type = AFTER;
+ else if (0 == strcmp(token, FIELD_PROVIDE))
+ type = PROVIDE;
+ else if (0 == strcmp(token, FIELD_FAILED)) {
+ type = BROKEN;
+
+ /* FIXME: Need to think about what to do syntax BROKEN
+ * services */
+ EWARN("'%s' has syntax errors, please correct!\n", rc_name);
+ }
+
+ if (type < ALL_SERVICE_TYPE_T) {
+ /* Get the first value *
+ * As the values are passed to a bash function, and we
+ * then use 'echo $*' to parse them, they should only
+ * have one space between each value ... */
+ token = strsep(&tmp_p, " ");
+
+ /* Get the correct type name */
+ field = service_type_names[type];
+
+ while (NULL != token) {
+ DBG_MSG("Field = '%s', service = '%s', value = '%s'\n",
+ field, rc_name, token);
+
+ retval = service_add_dependency(rc_name, token, type);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency '%s' to service '%s', type '%s'!\n",
+ token, rc_name, field);
+ goto error;
+ }
+
+ /* Get the next value (if any) */
+ token = strsep(&tmp_p, " ");
+ }
+
+ goto _continue;
+ }
+
+ /* Fall through */
+ DBG_MSG("Unknown FIELD in data!\n");
+
+_continue:
+ type = ALL_SERVICE_TYPE_T;
+ current += count + 1;
+ free(tmp_buf);
+ /* Do not free 'rc_name', as it should be consistant
+ * across loops */
+ }
+
+ /* Set the mtimes
+ * FIXME: Can drop this when we no longer need write_legacy_stage3() */
+ list_for_each_entry(rs_info, &rcscript_list, node) {
+ rc_name = gbasename(rs_info->filename);
+ if (NULL == service_get_info(rc_name))
+ continue;
+
+ retval = service_set_mtime(rc_name, rs_info->mtime);
+ if (-1 == retval) {
+ DBG_MSG("Failed to set mtime for service '%s'!\n", rc_name);
+ return -1;
+ }
+ }
+
+ return 0;
+
+error:
+ free(tmp_buf);
+
+ return -1;
+}
+
+size_t parse_print_start(char **data, size_t index)
+{
+ size_t write_count = index;
+
+ PRINT_TO_BUFFER(data, write_count, error,
+ ". /sbin/functions.sh\n"
+ "[ -e /etc/rc.conf ] && . /etc/rc.conf\n"
+ "\n"
+ /* "set -e\n" */
+ "\n");
+
+ return write_count;
+
+error:
+ return -1;
+}
+
+size_t parse_print_header(char *scriptname, char **data, size_t index)
+{
+ size_t write_count = index;
+
+ PRINT_TO_BUFFER(data, write_count, error,
+ "#*** %s ***\n"
+ "\n"
+ "myservice=\"%s\"\n"
+ "echo \"RCSCRIPT ${myservice}\"\n"
+ "\n",
+ scriptname, scriptname);
+
+ return write_count;
+
+error:
+ return -1;
+}
+
+size_t parse_print_body(char *scriptname, char **data, size_t index)
+{
+ size_t write_count = index;
+ char *tmp_buf = NULL;
+ char *tmp_ptr;
+ char *base;
+ char *ext;
+
+ tmp_buf = strndup(scriptname, strlen(scriptname));
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+
+ /*
+ * Rather do the next block in C than bash, in case we want to
+ * use ash or another shell in the place of bash
+ */
+
+ /* bash: base="${myservice%%.*}" */
+ base = tmp_buf;
+ tmp_ptr = strchr(tmp_buf, '.');
+ if (NULL != tmp_ptr) {
+ tmp_ptr[0] = '\0';
+ tmp_ptr++;
+ } else {
+ tmp_ptr = tmp_buf;
+ }
+ /* bash: ext="${myservice##*.}" */
+ ext = strrchr(tmp_ptr, '.');
+ if (NULL == ext)
+ ext = tmp_ptr;
+
+ PRINT_TO_BUFFER(data, write_count, error,
+ "\n"
+ "(\n"
+ " # Get settings for rc-script ...\n"
+ " [ -e \"/etc/conf.d/${myservice}\" ] && \\\n"
+ " . \"/etc/conf.d/${myservice}\"\n"
+ " [ -e /etc/conf.d/net ] && \\\n"
+ " [ \"%s\" = \"net\" ] && \\\n"
+ " [ \"%s\" != \"${myservice}\" ] && \\\n"
+ " . /etc/conf.d/net\n"
+ " depend() {\n"
+ " return 0\n"
+ " }\n"
+ " \n"
+ " # Actual depend() function ...\n"
+ " (\n"
+ " set -e\n"
+ " . \"/etc/init.d/%s\" >/dev/null 2>&1\n"
+ " set +e\n"
+ " \n"
+ " need() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"NEED $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " use() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"USE $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " before() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"BEFORE $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " after() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"AFTER $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " provide() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"PROVIDE $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " depend\n"
+ " ) || echo \"FAILED ${myservice}\"\n"
+ ")\n"
+ "\n\n",
+ base, ext, scriptname);
+
+ return write_count;
+
+error:
+ return -1;
+}
+
diff --git a/src/core/librcscripts/parse.h b/src/core/librcscripts/parse.h
new file mode 100644
index 0000000..58e7540
--- /dev/null
+++ b/src/core/librcscripts/parse.h
@@ -0,0 +1,100 @@
+/*
+ * parse.h
+ *
+ * Parser for Gentoo style rc-scripts.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#ifndef _PARSE_H
+#define _PARSE_H
+
+#include <sys/types.h>
+#include "list.h"
+
+#define LEGACY_CACHE_FILE_NAME "deptree"
+
+#define FIELD_RCSCRIPT "RCSCRIPT"
+#define FIELD_NEED "NEED"
+#define FIELD_USE "USE"
+#define FIELD_BEFORE "BEFORE"
+#define FIELD_AFTER "AFTER"
+#define FIELD_PROVIDE "PROVIDE"
+#define FIELD_FAILED "FAILED"
+
+typedef struct {
+ struct list_head node;
+
+ char *filename;
+ time_t mtime;
+ time_t confd_mtime;
+} rcscript_info_t;
+
+struct list_head rcscript_list;
+
+int get_rcscripts(void);
+int check_rcscripts_mtime(char *cachefile);
+size_t generate_stage1(char **data);
+size_t generate_stage2(char **data);
+size_t read_stage2(char **data);
+int write_stage2(FILE *outfile);
+size_t generate_stage3(char **data);
+size_t read_stage3(char **data);
+int write_stage3(FILE *outfile);
+int write_legacy_stage3(FILE *output);
+int parse_cache(const char *data, size_t lenght);
+
+/*
+ * get_rcscripts()
+ * |
+ * V
+ * check_rcscripts_mtime() ------------------------------> read_stage3()
+ * | |
+ * | |
+ * V V
+ * generate_stage1() (Called by generate_stage2()) parse_cache()
+ * | |
+ * | |
+ * V |
+ * generate_stage2() ----> write_stage2() (Debugging) |
+ * | |
+ * | |
+ * | === parse_cache() |
+ * V | | |
+ * generate_stage3() ==| | |
+ * | | | |
+ * | | V |
+ * | === service_resolve_dependencies() |
+ * | |
+ * | |
+ * |-------> write_legacy_stage3() (Proof of Concept |
+ * | or Debugging) |
+ * | |
+ * V |
+ * write_stage3() |
+ * | |
+ * | V
+ * |<-------------------------------------------------------
+ * |
+ * V
+ *
+ */
+
+#endif /* _PARSE_H */
+
diff --git a/src/core/librcscripts/rcscripts.h b/src/core/librcscripts/rcscripts.h
new file mode 100644
index 0000000..ea1c9c9
--- /dev/null
+++ b/src/core/librcscripts/rcscripts.h
@@ -0,0 +1,54 @@
+/*
+ * rcscripts.h
+ *
+ * Core defines.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#ifndef _RCSCRIPTS_H
+#define _RCSCRIPTS_H
+
+#define RCSCRIPTS_CONFDDIR ETCDIR "/conf.d"
+#define RCSCRIPTS_INITDDIR ETCDIR "/init.d"
+#define RCSCRIPTS_LIBDIR LIBDIR "/rcscripts"
+
+#define SBIN_RC SBINDIR "/rc"
+#define PROFILE_ENV ETCDIR "/profile.env"
+
+#define RC_CONF_FILE_NAME ETCDIR "/rc.conf"
+#define RC_CONFD_FILE_NAME ETCDIR "/conf.d/rc"
+
+#define RCSCRIPT_HELP RCSCRIPTS_LIBDIR "/sh/rc-help.sh"
+
+#define SVCDIR_CONFIG_ENTRY "svcdir"
+
+#define SHELL_PARSER BINDIR "/bash"
+
+#define DEFAULT_PATH "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin"
+
+#define SELINUX_LIB RCSCRIPTS_LIBDIR "/runscript_selinux.so"
+
+#define SYS_WHITELIST RCSCRIPTS_LIBDIR "/conf.d/env_whitelist"
+#define USR_WHITELIST RCSCRIPTS_CONFDDIR "/env_whitelist"
+
+#define SOFTLEVEL "SOFTLEVEL"
+
+#endif /* _RCSCRIPTS_H */
+
diff --git a/src/core/librcscripts/simple-regex.c b/src/core/librcscripts/simple-regex.c
new file mode 100644
index 0000000..a5a9234
--- /dev/null
+++ b/src/core/librcscripts/simple-regex.c
@@ -0,0 +1,827 @@
+/*
+ * simple_regex.c
+ *
+ * Simle regex library.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+/*
+ * Some notes:
+ *
+ * - This is a very simple regex library (read: return a match if some string
+ * matches some regex). It is probably not POSIX (if there are a POSIX or
+ * other standard) compatible.
+ *
+ * - I primarily wrote it to _not_ use glibc type regex functions, in case we
+ * might want to use it in code that have to be linked agaist klibc, etc.
+ *
+ * - It really is not optimized in any way yet.
+ *
+ * - Supported operators are:
+ *
+ * '.', '?', '*', '+' - So called 'wildcards'
+ * '[a-z]', '[^a-z]' - Basic 'lists'. Note that 'a-z' just specify that
+ * it supports basic lists as well as sequences ..
+ * The '^' is for an inverted list of course.
+ * '^', '$' - The 'from start' and 'to end' operators. If these
+ * are not used at the start ('^') or end ('$') of the
+ * regex, they will be treated as normal characters
+ * (this of course exclude the use of '^' in a 'list').
+ *
+ * - If an invalid argument was passed, the functions returns 0 with
+ * 'regex_data-match == 0' (no error with no match) rather than -1. It may
+ * not be consistant with other practices, but I personally do not feel it is
+ * a critical error for these types of functions, and there are debugging you
+ * can enable to verify that there are no such issues.
+ *
+ * - __somefunction() is usually a helper function for somefunction(). I guess
+ * recursion might be an alternative, but I try to avoid it.
+ *
+ * - In general if we are matching a 'wildcard' ('*', '+' or '?'), a 'word'
+ * (read: some part of the regex that do not contain a 'wildcard' or 'list')
+ * will have a greater 'weight' than the 'wildcard'. This means that we
+ * will only continue to evaluate the 'wildcard' until the following 'word'
+ * (if any) matches. Currently this do not hold true for a 'list' not
+ * followed by a 'wildcard' - I might fix this in future.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "misc.h"
+#include "simple-regex.h"
+
+/* Macro to check if a regex_data_t pointer is valid */
+#define CHECK_REGEX_DATA_P(_regex_data, _on_error) \
+ do { \
+ if ((NULL == _regex_data) \
+ || (NULL == _regex_data->data) \
+ /* We do not check for this, as it might still \
+ * provide a match ('*' or '?' wildcard) */ \
+ /* || (0 == strlen(_regex_data->data)) */ \
+ || (NULL == _regex_data->regex) \
+ || (0 == strlen(_regex_data->regex))) {\
+ DBG_MSG("Invalid argument passed!\n"); \
+ goto _on_error; \
+ } \
+ } while (0)
+
+size_t get_word(const char *regex, char **r_word);
+int match_word(regex_data_t *regex_data);
+size_t get_list_size(const char *regex);
+size_t get_list(const char *regex, char **r_list);
+int __match_list(regex_data_t *regex_data);
+int match_list(regex_data_t *regex_data);
+size_t get_wildcard(const char *regex, char *r_wildcard);
+int __match_wildcard(regex_data_t *regex_data,
+int (*match_func)(regex_data_t *regex_data), const char *regex);
+int match_wildcard(regex_data_t *regex_data);
+int __match(regex_data_t *regex_data);
+
+/*
+ * Return values for match_* functions
+ *
+ * 0 - There was no error. If there was a match, regex_data->match
+ * - will be > 0 (this is the definitive check - if not true, the
+ * - other values of the struct may be bogus), regex_data->count
+ * - will be the amount of data that was matched (might be 0 for
+ * - some wildcards), and regex_data->r_count will be > 0.
+ *
+ * -1 - An error occured. Check errno for more info.
+ *
+ */
+
+size_t get_word(const char *regex, char **r_word)
+{
+ char *r_list;
+ char *tmp_p;
+ size_t count = 0;
+ size_t tmp_count;
+
+ /* NULL string means we do not have a word */
+ if ((NULL == regex) || (0 == strlen(regex))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ *r_word = malloc(strlen(regex) + 1);
+ if (NULL == r_word) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return 0;
+ }
+ tmp_p = *r_word;
+
+ while (strlen(regex) > 0) {
+ switch (regex[0]) {
+ case '*':
+ case '+':
+ case '?':
+ /* If its a wildcard, backup one step */
+ *(--tmp_p) = '\0';
+ count--;
+ return count;
+ case '[':
+ tmp_count = get_list(regex, &r_list);
+ free(r_list);
+ /* In theory should not happen, but you never know
+ * what may happen in future ... */
+ if (-1 == tmp_count)
+ goto error;
+
+ /* Bail if we have a list */
+ if (tmp_count > 0) {
+ tmp_p[0] = '\0';
+ return count;
+ }
+ default:
+ *tmp_p++ = *regex++;
+ count++;
+ break;
+ }
+ }
+
+ tmp_p[0] = '\0';
+
+ return count;
+
+error:
+ free(*r_word);
+
+ return -1;
+}
+
+int match_word(regex_data_t *regex_data)
+{
+ char *data_p = regex_data->data;
+ char *r_word = NULL, *r_word_p;
+ size_t count = 0;
+
+ CHECK_REGEX_DATA_P(regex_data, exit);
+
+ count = get_word(regex_data->regex, &r_word);
+ if (-1 == count)
+ goto error;
+ if (0 == count)
+ goto exit;
+ r_word_p = r_word;
+
+ while ((strlen(data_p) > 0) && (strlen(r_word_p) > 0 )) {
+ /* If 'r_word' is not 100% part of 'string', we do not have
+ * a match. If its a '.', it matches no matter what. */
+ if ((data_p[0] != r_word_p[0]) && ('.' != r_word_p[0])) {
+ count = 0;
+ goto exit;
+ }
+
+ data_p++;
+ r_word_p++;
+ }
+
+ /* If 'string' is shorter than 'r_word', we do not have a match */
+ if ((0 == strlen(data_p)) && (0 < strlen(r_word_p))) {
+ count = 0;
+ goto exit;
+ }
+
+exit:
+ /* Fill in our structure */
+ if (0 == count)
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen(regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = count;
+
+ free(r_word);
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ free(r_word);
+ return -1;
+}
+
+size_t get_list_size(const char *regex)
+{
+ size_t count = 0;
+
+ /* NULL string means we do not have a list */
+ if ((NULL == regex)
+ || (0 == strlen(regex))
+ || ('[' != regex[0])) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ regex++;
+
+ while ((strlen(regex) > 0) && (']' != regex[0])) {
+ /* We have a sequence (x-y) */
+ if (('-' == regex[0])
+ && (']' != regex[1])
+ && (strlen(regex) >= 2)
+ && (regex[-1] < regex[1]))
+ {
+ /* Add current + diff in sequence */
+ count += regex[1] - regex[-1];
+ /* Take care of '-' and next char */
+ regex += 2;
+ } else {
+ regex++;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+size_t get_list(const char *regex, char **r_list)
+{
+ char *tmp_buf = NULL;
+ size_t count = 0;
+ size_t size;
+
+ /* NULL string means we do not have a list */
+ if ((NULL == regex) || (0 == strlen(regex))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ /* Bail if we do not have a list. Do not add debugging, as
+ * it is very noisy (used a lot when we call match_list() in
+ * __match() and match() to test for list matching) */
+ if ('[' != regex[0])
+ return 0;
+
+ size = get_list_size(regex);
+ if (0 == size) {
+ /* Should not be an issue, but just in case */
+ DBG_MSG("0 returned by get_list_size.\n");
+ return 0;
+ }
+
+ *r_list = malloc(size + 1);
+ if (NULL == *r_list) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return -1;
+ }
+ tmp_buf = *r_list;
+
+ /* Take care of '[' */
+ regex++;
+ count++;
+
+ while ((strlen(regex) > 0) && (']' != regex[0])) {
+ /* We have a sequence (x-y) */
+ if (('-' == regex[0])
+ && (']' != regex[1])
+ && (strlen(regex) >= 2)
+ && (regex[-1] < regex[1])) {
+ /* Fill in missing chars in sequence */
+ while (tmp_buf[-1] < regex[1]) {
+ tmp_buf[0] = (char)(tmp_buf[-1] + 1);
+ tmp_buf++;
+ /* We do not increase count */
+ }
+ /* Take care of '-' and next char */
+ count += 2;
+ regex += 2;
+ } else {
+ *tmp_buf++ = *regex++;
+ count++;
+ }
+ }
+
+ tmp_buf[0] = '\0';
+ /* Take care of ']' */
+ count++;
+
+ /* We do not have a list as it does not end in ']' */
+ if (']' != regex[0]) {
+ count = 0;
+ free(*r_list);
+ }
+
+ return count;
+}
+
+/* If the first is the '^' character, everything but the list is matched
+ * NOTE: We only evaluate _ONE_ data character at a time!! */
+int __match_list(regex_data_t *regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *list_p = regex_data->regex;
+ char test_regex[2] = { '\0', '\0' };
+ int invert = 0;
+ int match;
+ int retval;
+
+ CHECK_REGEX_DATA_P(regex_data, failed);
+
+ if ('^' == list_p[0]) {
+ /* We need to invert the match */
+ invert = 1;
+ /* Make sure '^' is not part of our list */
+ list_p++;
+ }
+
+ if (invert)
+ /* All should be a match if not in the list */
+ match = 1;
+ else
+ /* We only have a match if in the list */
+ match = 0;
+
+ while (strlen(list_p) > 0) {
+ test_regex[0] = list_p[0];
+
+ FILL_REGEX_DATA(tmp_data, data_p, test_regex);
+ retval = match_word(&tmp_data);
+ if (-1 == retval)
+ goto error;
+
+ if (REGEX_MATCH(tmp_data)) {
+ if (invert)
+ /* If we exclude the list from
+ * characters we try to match, we
+ * have a match until one of the
+ * list is found. */
+ match = 0;
+ else
+ /* If not, we have to keep looking
+ * until one from the list match
+ * before we have a match */
+ match = 1;
+ break;
+ }
+ list_p++;
+ }
+
+ /* Fill in our structure */
+ if (match) {
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ regex_data->where = regex_data->data;
+ regex_data->count = 1;
+ /* This one is more cosmetic, as match_list() will
+ * do the right thing */
+ regex_data->r_count = 0; /* strlen(regex_data->regex); */
+ } else {
+failed:
+ regex_data->match = REGEX_NO_MATCH;
+ regex_data->where = NULL;
+ regex_data->count = 0;
+ regex_data->r_count = 0;
+ }
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int match_list(regex_data_t *regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *list_p = regex_data->regex;
+ char *r_list = NULL;
+ size_t r_count = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P(regex_data, failed);
+
+ r_count = get_list(list_p, &r_list);
+ if (-1 == r_count)
+ goto error;
+ if (0 == r_count)
+ goto failed;
+
+ FILL_REGEX_DATA(tmp_data, data_p, &list_p[r_count-1]);
+ retval = __match_wildcard(&tmp_data, __match_list, r_list);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH(tmp_data)) {
+ /* This should be 2 ('word' + 'wildcard'), so just remove
+ * the wildcard */
+ tmp_data.r_count--;
+ goto exit;
+ }
+
+ FILL_REGEX_DATA(tmp_data, data_p, r_list);
+ retval = __match_list(&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH(tmp_data))
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ tmp_data.match = REGEX_NO_MATCH;
+ tmp_data.where = NULL;
+ tmp_data.count = 0;
+ tmp_data.r_count = 0;
+
+exit:
+ /* Fill in our structure */
+ regex_data->match = tmp_data.match;
+ regex_data->where = tmp_data.where;
+ regex_data->count = tmp_data.count;
+ if (regex_data->match != REGEX_NO_MATCH)
+ /* tmp_data.r_count for __match_wildcard will take care of the
+ * wildcard, and tmp_data.r_count for __match_list will be 0 */
+ regex_data->r_count = r_count + tmp_data.r_count;
+ else
+ regex_data->r_count = 0;
+
+ free(r_list);
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ free(r_list);
+ return -1;
+}
+
+size_t get_wildcard(const char *regex, char *r_wildcard)
+{
+ /* NULL regex means we do not have a wildcard */
+ if ((NULL == regex) || (0 == strlen(regex))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return 0;
+ }
+
+ r_wildcard[0] = regex[0];
+ r_wildcard[2] = '\0';
+
+ switch (regex[1]) {
+ case '*':
+ case '+':
+ case '?':
+ r_wildcard[1] = regex[1];
+ break;
+ default:
+ r_wildcard[0] = '\0';
+ return 0;
+ }
+
+ return strlen(r_wildcard);
+}
+
+int __match_wildcard(regex_data_t *regex_data, int (*match_func)(regex_data_t *regex_data), const char *regex)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *wildcard_p = regex_data->regex;
+ char r_wildcard[3];
+ size_t count = 0;
+ size_t r_count = 0;
+ int is_match = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P(regex_data, exit);
+
+ if (NULL == match_func) {
+ DBG_MSG("NULL match_func was passed!\n");
+ goto exit;
+ }
+
+ r_count = get_wildcard(wildcard_p, r_wildcard);
+ if (0 == r_count)
+ goto exit;
+
+ FILL_REGEX_DATA(tmp_data, data_p, (char *)regex);
+ retval = match_func(&tmp_data);
+ if (-1 == retval)
+ goto error;
+
+ switch (r_wildcard[1]) {
+ case '*':
+ case '?':
+ /* '*' and '?' always matches */
+ is_match = 1;
+ case '+':
+ /* We need to match all of them */
+ do {
+ /* If we have at least one match for '+', or none
+ * for '*' or '?', check if we have a word or list match.
+ * We do this because a word weights more than a wildcard */
+ if ((strlen(wildcard_p) > 2)
+ && ((count > 0)
+ || ('*' == r_wildcard[1])
+ || ('?' == r_wildcard[1]))) {
+ regex_data_t tmp_data2;
+#if 0
+ printf("data_p = %s, wildcard_p = %s\n", data_p, wildcard_p);
+#endif
+
+ FILL_REGEX_DATA(tmp_data2, data_p, &wildcard_p[2]);
+ retval = match(&tmp_data2);
+ if (-1 == retval)
+ goto error;
+
+ if (/* '.' might be a special case ... */
+ /* ('.' != wildcard_p[2]) && */
+ ((REGEX_MATCH(tmp_data2))
+ && (REGEX_FULL_MATCH == tmp_data2.match))) {
+ goto exit;
+ }
+ }
+
+ if (REGEX_MATCH(tmp_data)) {
+ data_p += tmp_data.count;
+ count += tmp_data.count;
+ is_match = 1;
+
+ FILL_REGEX_DATA(tmp_data, data_p, (char *)regex);
+ retval = match_func(&tmp_data);
+ if (-1 == retval)
+ goto error;
+ }
+ /* Only once for '?' */
+ } while ((REGEX_MATCH(tmp_data)) && ('?' != r_wildcard[1]));
+
+ break;
+ default:
+ /* No wildcard */
+ break;
+ }
+
+exit:
+ /* Fill in our structure */
+ /* We can still have a match ('*' and '?'), although count == 0 */
+ if ((0 == count) && (0 == is_match))
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen(regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int match_wildcard(regex_data_t *regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *wildcard_p = regex_data->regex;
+ char r_wildcard[3];
+ size_t r_count;
+ int retval;
+
+ CHECK_REGEX_DATA_P(regex_data, failed);
+
+ /* Invalid wildcard - we need a character + a regex operator */
+ if (strlen(wildcard_p) < 2)
+ goto failed;
+
+ r_count = get_wildcard(wildcard_p, r_wildcard);
+ if (0 == r_count)
+ goto failed;
+
+ /* Needed so that match_word() will not bail if it sees the wildcard */
+ r_wildcard[1] = '\0';
+
+ FILL_REGEX_DATA(tmp_data, data_p, wildcard_p);
+ retval = __match_wildcard(&tmp_data, match_word, r_wildcard);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH(tmp_data))
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ tmp_data.match = REGEX_NO_MATCH;
+ tmp_data.where = NULL;
+ tmp_data.count = 0;
+ tmp_data.r_count = 0;
+
+exit:
+ /* Fill in our structure */
+ regex_data->match = tmp_data.match;
+ regex_data->where = tmp_data.where;
+ regex_data->count = tmp_data.count;
+ regex_data->r_count = tmp_data.r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int __match(regex_data_t *regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *regex_p = regex_data->regex;
+ size_t count = 0;
+ size_t r_count = 0;
+ int match = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P(regex_data, failed);
+
+ while (strlen(regex_p) > 0) {
+#if 0
+ printf("data_p = '%s', regex_p = '%s'\n", data_p, regex_p);
+#endif
+
+ FILL_REGEX_DATA(tmp_data, data_p, regex_p);
+ retval = match_list(&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH(tmp_data))
+ goto match;
+
+ FILL_REGEX_DATA(tmp_data, data_p, regex_p);
+ retval = match_wildcard(&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH(tmp_data))
+ goto match;
+
+ FILL_REGEX_DATA(tmp_data, data_p, regex_p);
+ retval = match_word(&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH(tmp_data))
+ goto match;
+
+ break;
+
+match:
+ data_p += tmp_data.count;
+ count += tmp_data.count;
+ regex_p += tmp_data.r_count;
+ r_count += tmp_data.r_count;
+ match = 1;
+
+ /* Check that we do not go out of bounds */
+ if (((data_p - regex_data->data) > strlen(regex_data->data))
+ || ((regex_p - regex_data->regex) > strlen(regex_data->regex)))
+ goto failed;
+ }
+
+ /* We could not match the whole regex (data too short?) */
+ if (0 != strlen(regex_p))
+ goto failed;
+
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ count = 0;
+ r_count = 0;
+ match = 0;
+
+exit:
+ /* Fill in our structure */
+ /* We can still have a match ('*' and '?'), although count == 0 */
+ if ((0 == count) && (0 == match))
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen(regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int match(regex_data_t *regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *regex_p;
+ char *tmp_buf = NULL;
+ int from_start = 0;
+ int to_end = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P(regex_data, failed);
+
+ /* We might be modifying regex_p, so make a copy */
+ tmp_buf = strndup(regex_data->regex, strlen(regex_data->regex));
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ regex_p = tmp_buf;
+
+ /* Should we only match from the start? */
+ if ('^' == regex_p[0]) {
+ regex_p++;
+ from_start = 1;
+ }
+
+ /* Should we match up to the end? */
+ if ('$' == regex_p[strlen(regex_p) - 1]) {
+ regex_p[strlen(regex_p) - 1] = '\0';
+ to_end = 1;
+ }
+
+ do {
+ FILL_REGEX_DATA(tmp_data, data_p, regex_p);
+ retval = __match(&tmp_data);
+ if (-1 == retval)
+ goto error;
+ } while ((strlen(data_p++) > 0)
+ && (!REGEX_MATCH(tmp_data))
+ && (0 == from_start));
+
+ /* Compensate for above extra inc */
+ data_p--;
+
+ /* Fill in our structure */
+ if (REGEX_MATCH(tmp_data)) {
+ /* Check if we had an '$' at the end of the regex, and
+ * verify that we still have a match */
+ if ((1 == to_end) && (tmp_data.count != strlen(data_p))) {
+ goto failed;
+ }
+
+ if ((data_p == regex_data->data)
+ && (tmp_data.match == REGEX_FULL_MATCH))
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ regex_data->where = data_p;
+ regex_data->count = tmp_data.count;
+ regex_data->r_count = tmp_data.r_count;
+ if (1 == from_start)
+ regex_data->r_count++;
+ if (1 == to_end)
+ regex_data->r_count++;
+ } else {
+failed:
+ regex_data->match = REGEX_NO_MATCH;
+ regex_data->where = NULL;
+ regex_data->count = 0;
+ regex_data->r_count = 0;
+ }
+
+ free(tmp_buf);
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+ free(tmp_buf);
+
+ return -1;
+}
+
diff --git a/src/core/librcscripts/simple-regex.h b/src/core/librcscripts/simple-regex.h
new file mode 100644
index 0000000..ad91a58
--- /dev/null
+++ b/src/core/librcscripts/simple-regex.h
@@ -0,0 +1,86 @@
+/*
+ * simple_regex.h
+ *
+ * Simle regex library.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#ifndef _SIMPLE_REGEX_H
+#define _SIMPLE_REGEX_H
+
+#define REGEX_NO_MATCH 0 /* We have no match */
+#define REGEX_PARTIAL_MATCH 1 /* Some of the string matches the regex */
+#define REGEX_FULL_MATCH 2 /* The whole string matches the regex */
+
+/* Macro to fill in .data and .regex */
+#define FILL_REGEX_DATA(_regex_data, _string, _regex) \
+ do { \
+ _regex_data.data = _string; \
+ _regex_data.regex = _regex; \
+ } while (0)
+
+/* Fill in _regex_data with _data and _regex, on failure goto _error */
+#define DO_REGEX(_regex_data, _data, _regex, _error) \
+ do { \
+ FILL_REGEX_DATA(_regex_data, _data, _regex); \
+ if (-1 == match(&_regex_data)) { \
+ DBG_MSG("Could not do regex match!\n"); \
+ goto _error; \
+ } \
+ } while (0)
+
+/* Evaluate to true if we have some kind of match */
+#define REGEX_MATCH(_regex_data) \
+ ((REGEX_FULL_MATCH == _regex_data.match) \
+ || (REGEX_PARTIAL_MATCH == _regex_data.match))
+
+/* Same as above, but for use when _regex_data is a pointer */
+#define REGEX_MATCH_P(_regex_data) \
+ ((REGEX_FULL_MATCH == _regex_data->match) \
+ || (REGEX_PARTIAL_MATCH == _regex_data->match))
+
+typedef struct {
+ char *data; /* String to perform regex operation on */
+ char *regex; /* String containing regex to use */
+ int match; /* Will be set if there was a match. Check
+ * REGEX_*_MATCH above for possible values */
+ char *where; /* Pointer to where match starts in data */
+ size_t count; /* Count characters from data matched by regex */
+ size_t r_count; /* Count characters of regex used for match. This
+ * should normally be the lenght of regex, but might
+ * not be for some internal functions ... */
+} regex_data_t;
+
+/*
+ * Return:
+ *
+ * 0 - There was no error. If there was a match, regex_data->match
+ * - will be > 0 (this is the definitive check - if not true, the
+ * - other values of the struct may be bogus), regex_data->count
+ * - will be the amount of data that was matched (might be 0 for
+ * - some wildcards), and regex_data->r_count will be > 0.
+ *
+ * -1 - An error occured. Check errno for more info.
+ *
+ */
+int match(regex_data_t *regex_data);
+
+#endif /* _SIMPLE_REGEX_H */
+
diff --git a/src/core/list.h b/src/core/list.h
index 5c478bc..e69de29 100644
--- a/src/core/list.h
+++ b/src/core/list.h
@@ -1,446 +0,0 @@
-/*
- * Copied from the Linux kernel source tree, version 2.6.0-test1.
- *
- * Licensed under the GPL v2 as per the whole kernel source tree.
- *
- * Ripped out the rcu stuff, as it's not needed.
- *
- * $Header$
- */
-
-#ifndef _LINUX_LIST_H
-#define _LINUX_LIST_H
-
-//#include <linux/stddef.h>
-/**
- * container_of - cast a member of a structure out to the containing structure
- *
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-
-//#include <linux/prefetch.h>
-static inline void prefetch(const void *x) {;}
-
-//#include <asm/system.h>
-
-/*
- * These are non-NULL pointers that will result in page faults
- * under normal circumstances, used to verify that nobody uses
- * non-initialized list entries.
- */
-#define LIST_POISON1 ((void *) 0x00100100)
-#define LIST_POISON2 ((void *) 0x00200200)
-
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-struct list_head {
- struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
-#define INIT_LIST_HEAD(ptr) do { \
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- */
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head->prev, head);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
- */
-static inline void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- entry->next = LIST_POISON1;
- entry->prev = LIST_POISON2;
-}
-
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.
- */
-static inline void list_del_init(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- INIT_LIST_HEAD(entry);
-}
-
-/**
- * list_move - delete from one list and add as another's head
- * @list: the entry to move
- * @head: the head that will precede our entry
- */
-static inline void list_move(struct list_head *list, struct list_head *head)
-{
- __list_del(list->prev, list->next);
- list_add(list, head);
-}
-
-/**
- * list_move_tail - delete from one list and add as another's tail
- * @list: the entry to move
- * @head: the head that will follow our entry
- */
-static inline void list_move_tail(struct list_head *list,
- struct list_head *head)
-{
- __list_del(list->prev, list->next);
- list_add_tail(list, head);
-}
-
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static inline int list_empty(struct list_head *head)
-{
- return head->next == head;
-}
-
-static inline void __list_splice(struct list_head *list,
- struct list_head *head)
-{
- struct list_head *first = list->next;
- struct list_head *last = list->prev;
- struct list_head *at = head->next;
-
- first->prev = head;
- head->next = first;
-
- last->next = at;
- at->prev = last;
-}
-
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static inline void list_splice(struct list_head *list, struct list_head *head)
-{
- if (!list_empty(list))
- __list_splice(list, head);
-}
-
-/**
- * list_splice_init - join two lists and reinitialise the emptied list.
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- *
- * The list at @list is reinitialised
- */
-static inline void list_splice_init(struct list_head *list,
- struct list_head *head)
-{
- if (!list_empty(list)) {
- __list_splice(list, head);
- INIT_LIST_HEAD(list);
- }
-}
-
-/**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
-
-/**
- * list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- */
-#define list_for_each(pos, head) \
- for (pos = (head)->next, prefetch(pos->next); pos != (head); \
- pos = pos->next, prefetch(pos->next))
-
-/**
- * __list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- *
- * This variant differs from list_for_each() in that it's the
- * simplest possible list iteration code, no prefetching is done.
- * Use this for code that knows the list to be very short (empty
- * or 1 entry) most of the time.
- */
-#define __list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_prev - iterate over a list backwards
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- */
-#define list_for_each_prev(pos, head) \
- for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
- pos = pos->prev, prefetch(pos->prev))
-
-/**
- * list_for_each_safe - iterate over a list safe against removal of list entry
- * @pos: the &struct list_head to use as a loop counter.
- * @n: another &struct list_head to use as temporary storage
- * @head: the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
-
-/**
- * list_for_each_entry - iterate over list of given type
- * @pos: the type * to use as a loop counter.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry(pos, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member), \
- prefetch(pos->member.next); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member), \
- prefetch(pos->member.next))
-
-/**
- * list_for_each_entry_reverse - iterate backwards over list of given type.
- * @pos: the type * to use as a loop counter.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry_reverse(pos, head, member) \
- for (pos = list_entry((head)->prev, typeof(*pos), member), \
- prefetch(pos->member.prev); \
- &pos->member != (head); \
- pos = list_entry(pos->member.prev, typeof(*pos), member), \
- prefetch(pos->member.prev))
-
-
-/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos: the type * to use as a loop counter.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe(pos, n, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member), \
- n = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/*
- * Double linked lists with a single pointer list head.
- * Mostly useful for hash tables where the two pointer list head is
- * too wasteful.
- * You lose the ability to access the tail in O(1).
- */
-
-struct hlist_head {
- struct hlist_node *first;
-};
-
-struct hlist_node {
- struct hlist_node *next, **pprev;
-};
-
-#define HLIST_HEAD_INIT { .first = NULL }
-#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
-#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
-#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
-
-static __inline__ int hlist_unhashed(struct hlist_node *h)
-{
- return !h->pprev;
-}
-
-static __inline__ int hlist_empty(struct hlist_head *h)
-{
- return !h->first;
-}
-
-static __inline__ void __hlist_del(struct hlist_node *n)
-{
- struct hlist_node *next = n->next;
- struct hlist_node **pprev = n->pprev;
- *pprev = next;
- if (next)
- next->pprev = pprev;
-}
-
-static __inline__ void hlist_del(struct hlist_node *n)
-{
- __hlist_del(n);
- n->next = LIST_POISON1;
- n->pprev = LIST_POISON2;
-}
-
-static __inline__ void hlist_del_init(struct hlist_node *n)
-{
- if (n->pprev) {
- __hlist_del(n);
- INIT_HLIST_NODE(n);
- }
-}
-
-static __inline__ void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
-{
- struct hlist_node *first = h->first;
- n->next = first;
- if (first)
- first->pprev = &n->next;
- h->first = n;
- n->pprev = &h->first;
-}
-
-/* next must be != NULL */
-static __inline__ void hlist_add_before(struct hlist_node *n, struct hlist_node *next)
-{
- n->pprev = next->pprev;
- n->next = next;
- next->pprev = &n->next;
- *(n->pprev) = n;
-}
-
-static __inline__ void hlist_add_after(struct hlist_node *n,
- struct hlist_node *next)
-{
- next->next = n->next;
- *(next->pprev) = n;
- n->next = next;
-}
-
-#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
-
-/* Cannot easily do prefetch unfortunately */
-#define hlist_for_each(pos, head) \
- for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
- pos = pos->next)
-
-#define hlist_for_each_safe(pos, n, head) \
- for (pos = (head)->first; n = pos ? pos->next : 0, pos; \
- pos = n)
-
-/**
- * hlist_for_each_entry - iterate over list of given type
- * @tpos: the type * to use as a loop counter.
- * @pos: the &struct hlist_node to use as a loop counter.
- * @head: the head for your list.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry(tpos, pos, head, member) \
- for (pos = (head)->first; \
- pos && ({ prefetch(pos->next); 1;}) && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = pos->next)
-
-/**
- * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
- * @tpos: the type * to use as a loop counter.
- * @pos: the &struct hlist_node to use as a loop counter.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_continue(tpos, pos, member) \
- for (pos = (pos)->next; \
- pos && ({ prefetch(pos->next); 1;}) && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = pos->next)
-
-/**
- * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
- * @tpos: the type * to use as a loop counter.
- * @pos: the &struct hlist_node to use as a loop counter.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_from(tpos, pos, member) \
- for (; pos && ({ prefetch(pos->next); 1;}) && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = pos->next)
-
-/**
- * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @tpos: the type * to use as a loop counter.
- * @pos: the &struct hlist_node to use as a loop counter.
- * @n: another &struct hlist_node to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
- for (pos = (head)->first; \
- pos && ({ n = pos->next; 1; }) && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = n)
-
-#endif
diff --git a/src/core/misc.c b/src/core/misc.c
index c589143..e69de29 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -1,659 +0,0 @@
-/*
- * misc.c
- *
- * Miscellaneous macro's and functions.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/param.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "debug.h"
-#include "misc.h"
-
-char *memrepchr(char **str, char old, char new, size_t size)
-{
- char *str_p;
-
- if ((NULL == str)
- || (NULL == *str)
- || (0 == strlen(*str))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return NULL;
- }
-
- str_p = memchr(*str, old, size);
-
- while (NULL != str_p) {
- str_p[0] = new;
- str_p = memchr(&str_p[1], old, size - (str_p - *str) - 1);
- }
-
- return *str;
-}
-
-char *strcatpaths(const char *pathname1, const char *pathname2)
-{
- char *new_path = NULL;
- int lenght;
-
- if ((NULL == pathname1)
- || (0 == strlen(pathname1))
- || (NULL == pathname2)
- || (0 == strlen(pathname2))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return NULL;
- }
-
- /* Lenght of pathname1 + lenght of pathname2 + '/' if needed */
- lenght = strlen(pathname1) + strlen(pathname2) + 1;
- /* lenght + '\0' */
- new_path = malloc(lenght + 1);
- if (NULL == new_path) {
- DBG_MSG("Failed to allocate buffer!\n");
- return NULL;
- }
-
- strncpy(new_path, pathname1, lenght);
- /* Should we add a '/' ? */
- if (new_path[strlen(new_path)-1] != '/')
- strncat(new_path, "/", lenght - strlen(new_path));
- strncat(new_path, pathname2, lenght - strlen(new_path));
-
- return new_path;
-}
-
-char *strndup(const char *str, size_t size)
-{
- char *new_str = NULL;
- size_t len;
-
- if (NULL == str) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return NULL;
- }
-
- /* Check lenght of str without breaching the size limit */
- for (len = 0; (len < size) && ('\0' != str[len]); len++);
-
- new_str = malloc(len + 1);
- if (NULL == new_str) {
- DBG_MSG("Failed to allocate buffer!\n");
- return NULL;
- }
-
- /* Make sure our string is NULL terminated */
- new_str[len] = '\0';
-
- return (char *)memcpy(new_str, str, len);
-}
-
-char *gbasename(const char *path)
-{
- char *new_path = NULL;
-
- if ((NULL == path) || (0 == strlen(path))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return NULL;
- }
-
- /* Copied from glibc */
- new_path = strrchr (path, '/');
- return new_path ? new_path + 1 : (char *)path;
-}
-
-
-int exists(const char *pathname)
-{
- struct stat buf;
- int retval;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- retval = lstat(pathname, &buf);
- if (-1 != retval)
- return 1;
-
- /* Clear errno, as we do not want debugging to trigger */
- errno = 0;
-
- return 0;
-}
-
-int is_file(const char *pathname, int follow_link)
-{
- struct stat buf;
- int retval;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
- if ((-1 != retval) && (S_ISREG(buf.st_mode)))
- return 1;
-
- /* Clear errno, as we do not want debugging to trigger */
- errno = 0;
-
- return 0;
-}
-
-int is_link(const char *pathname)
-{
- struct stat buf;
- int retval;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- retval = lstat(pathname, &buf);
- if ((-1 != retval) && (S_ISLNK(buf.st_mode)))
- return 1;
-
- /* Clear errno, as we do not want debugging to trigger */
- errno = 0;
-
- return 0;
-}
-
-int is_dir(const char *pathname, int follow_link)
-{
- struct stat buf;
- int retval;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
- if ((-1 != retval) && (S_ISDIR(buf.st_mode)))
- return 1;
-
- /* Clear errno, as we do not want debugging to trigger */
- errno = 0;
-
- return 0;
-}
-
-time_t get_mtime(const char *pathname, int follow_link)
-{
- struct stat buf;
- int retval;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
- if (-1 != retval)
- return buf.st_mtime;
-
- /* Clear errno, as we do not want debugging to trigger */
- errno = 0;
-
- return 0;
-}
-
-#ifdef __KLIBC__
-int remove(const char *pathname)
-{
- int retval;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- if (1 == is_dir(pathname, 0))
- retval = rmdir(pathname);
- else
- retval = unlink(pathname);
-
- return retval;
-}
-#endif
-
-int mktree(const char *pathname, mode_t mode)
-{
- char *temp_name = NULL;
- char *temp_token = NULL;
- char *token_p;
- char *token;
- int retval;
- int lenght;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- /* Lenght of 'pathname' + extra for "./" if needed */
- lenght = strlen(pathname) + 2;
- /* lenght + '\0' */
- temp_name = malloc(lenght + 1);
- if (NULL == temp_name) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- return -1;
- }
-
- temp_token = strndup(pathname, strlen(pathname));
- if (NULL == temp_token) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto error;
- }
- token_p = temp_token;
-
- if (pathname[0] == '/')
- temp_name[0] = '\0';
- else
- /* If not an absolute path, make it local */
- strncpy(temp_name, ".", lenght);
-
- token = strsep(&token_p, "/");
- /* First token might be "", but that is OK as it will be when the
- * pathname starts with '/' */
- while (NULL != token) {
- strncat(temp_name, "/", lenght - strlen(temp_name));
- strncat(temp_name, token, lenght - strlen(temp_name));
-
- /* If it does not exist, create the dir. If it does exit,
- * but is not a directory, we will catch it below. */
- if (1 != exists(temp_name)) {
- retval = mkdir(temp_name, mode);
- if (-1 == retval) {
- DBG_MSG("Failed to create directory!\n");
- goto error;
- }
- /* Not a directory or symlink pointing to a directory */
- } else if (1 != is_dir(temp_name, 1)) {
- DBG_MSG("Component in pathname is not a directory!\n");
- errno = ENOTDIR;
- goto error;
- }
-
- do {
- token = strsep(&token_p, "/");
- /* The first "" was Ok, but rather skip double '/' after that */
- } while ((NULL != token) && (0 == strlen(token)));
- }
-
- free(temp_name);
- free(temp_token);
-
- return 0;
-
-error:
- free(temp_name);
- free(temp_token);
-
- return -1;
-}
-
-int rmtree(const char *pathname)
-{
- char **dirlist = NULL;
- int i = 0;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- if (1 != exists(pathname)) {
- DBG_MSG("'%s' does not exists!\n", pathname);
- errno = ENOENT;
- return -1;
- }
-
- dirlist = ls_dir(pathname, 1);
- if ((NULL == dirlist) && (0 != errno)) {
- DBG_MSG("Could not get listing for '%s'!\n", pathname);
- return -1;
- }
-
- while ((NULL != dirlist) && (NULL != dirlist[i])) {
- /* If it is a directory, call rmtree() again with
- * it as argument */
- if (1 == is_dir(dirlist[i], 0)) {
- if (-1 == rmtree(dirlist[i])) {
- DBG_MSG("Failed to delete sub directory!\n");
- goto error;
- }
- }
-
- /* Now actually remove it. Note that if it was a directory,
- * it should already be removed by above rmtree() call */
- if ((1 == exists(dirlist[i]) && (-1 == remove(dirlist[i])))) {
- DBG_MSG("Failed to remove '%s'!\n", dirlist[i]);
- goto error;
- }
- i++;
- }
-
- STRING_LIST_FREE(dirlist);
-
- /* Now remove the parent */
- if (-1 == remove(pathname)) {
- DBG_MSG("Failed to remove '%s'!\n", pathname);
- goto error;
- }
-
- return 0;
-error:
- STRING_LIST_FREE(dirlist);
-
- return -1;
-}
-
-char **ls_dir(const char *pathname, int hidden)
-{
- DIR *dirfd;
- struct dirent *dir_entry;
- char **dirlist = NULL;
-
- if ((NULL == pathname) || (0 == strlen(pathname))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return NULL;
- }
-
- dirfd = opendir(pathname);
- if (NULL == dirfd) {
- DBG_MSG("Failed to call opendir()!\n");
- /* errno will be set by opendir() */
- goto error;
- }
-
- do {
- /* Clear errno to distinguish between EOF and error */
- errno = 0;
- dir_entry = readdir(dirfd);
- /* Only an error if 'errno' != 0, else EOF */
- if ((NULL == dir_entry) && (0 != errno)) {
- DBG_MSG("Failed to call readdir()!\n");
- goto error;
- }
- if ((NULL != dir_entry) &&
- /* Should we display hidden files? */
- (hidden ? 1 : dir_entry->d_name[0] != '.'))
- {
- char *d_name = dir_entry->d_name;
- char *tmp_p;
-
- /* Do not list current or parent entries */
- if ((0 == strcmp(d_name, ".")) ||
- (0 == strcmp(d_name, "..")))
- continue;
-
- tmp_p = strcatpaths(pathname, d_name);
- if (NULL == tmp_p) {
- DBG_MSG("Failed to allocate buffer!\n");
- goto error;
- }
-
- STRING_LIST_ADD(dirlist, tmp_p, error);
- }
- } while (NULL != dir_entry);
-
- if ((NULL == dirlist) || (NULL == dirlist[0]))
- DBG_MSG("Directory is empty.\n");
-
- closedir(dirfd);
-
- return dirlist;
-
-error:
- /* Free dirlist on error */
- STRING_LIST_FREE(dirlist);
-
- if (NULL != dirfd) {
- int old_errno = errno;
- closedir(dirfd);
- /* closedir() might have changed it */
- errno = old_errno;
- }
-
- return NULL;
-}
-
-/* This handles simple 'entry="bar"' type variables. If it is more complex
- * ('entry="$(pwd)"' or such), it will obviously not work, but current behaviour
- * should be fine for the type of variables we want. */
-char *get_cnf_entry(const char *pathname, const char *entry)
-{
- char *buf = NULL;
- char *tmp_buf = NULL;
- char *tmp_p;
- char *value = NULL;
- char *token;
- size_t lenght;
- int count;
- int current = 0;
-
-
- if ((NULL == pathname)
- || (0 == strlen(pathname))
- || (NULL == entry)
- || (0 == strlen(entry))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return NULL;
- }
-
- /* If it is not a file or symlink pointing to a file, bail */
- if (1 != is_file(pathname, 1)) {
- DBG_MSG("Given pathname is not a file or do not exist!\n");
- /* FIXME: Might need to set this to something better? */
- errno = ENOENT;
- return NULL;
- }
-
- if (-1 == file_map(pathname, &buf, &lenght)) {
- DBG_MSG("Could not open config file for reading!\n");
- return NULL;
- }
-
- while (current < lenght) {
- count = buf_get_line(buf, lenght, current);
-
- tmp_buf = strndup(&buf[current], count);
- if (NULL == tmp_buf) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto error;
- }
- tmp_p = tmp_buf;
-
- /* Strip leading spaces/tabs */
- while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
- tmp_p++;
-
- /* Get entry and value */
- token = strsep(&tmp_p, "=");
- /* Bogus entry or value */
- if (NULL == token)
- goto _continue;
-
- /* Make sure we have a string that is larger than 'entry', and
- * the first part equals 'entry' */
- if ((strlen(token) > 0) && (0 == strcmp(entry, token)))
- {
- do {
- /* Bash variables are usually quoted */
- token = strsep(&tmp_p, "\"\'");
- /* If quoted, the first match will be "" */
- } while ((NULL != token) && (0 == strlen(token)));
-
- /* We have a 'entry='. We respect bash rules, so NULL
- * value for now (if not already) */
- if (NULL == token) {
- /* We might have 'entry=' and later 'entry="bar"',
- * so just continue for now ... we will handle
- * it below when 'value == NULL' */
- if (NULL != value) {
- free(value);
- value = NULL;
- }
- goto _continue;
- }
-
- /* If we have already allocated 'value', free it */
- if (NULL != value)
- free(value);
-
- value = strndup(token, strlen(token));
- if (NULL == value) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto error;
- }
-
- /* We do not break, as there might be more than one entry
- * defined, and as bash uses the last, so should we */
- /* break; */
- }
-
-_continue:
- current += count + 1;
- free(tmp_buf);
- /* Set to NULL in case we error out above and have
- * to free below */
- tmp_buf = NULL;
- }
-
-
- if (NULL == value)
- DBG_MSG("Failed to get value for config entry '%s'!\n", entry);
-
- file_unmap(buf, lenght);
-
- return value;
-
-error:
- free(tmp_buf);
- free(value);
-
- if (NULL != buf) {
- int old_errno = errno;
- file_unmap(buf, lenght);
- /* unmmap() might have changed it */
- errno = old_errno;
- }
-
- return NULL;
-}
-
-
-/*
- * Below three functions (file_map, file_unmap and buf_get_line) are
- * from udev-050 (udev_utils.c).
- * (Some are slightly modified, please check udev for originals.)
- *
- * Copyright (C) 2004 Kay Sievers <kay@vrfy.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-int file_map(const char *filename, char **buf, size_t *bufsize)
-{
- struct stat stats;
- int fd;
- int old_errno;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
- DBG_MSG("Failed to open file!\n");
- return -1;
- }
-
- if (fstat(fd, &stats) < 0) {
- DBG_MSG("Failed to stat file!\n");
- old_errno = errno;
- close(fd);
- /* close() might have changed it */
- errno = old_errno;
- return -1;
- }
-
- *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
- if (*buf == MAP_FAILED) {
- DBG_MSG("Failed to mmap file!\n");
- old_errno = errno;
- close(fd);
- /* close() might have changed it */
- errno = old_errno;
- return -1;
- }
- *bufsize = stats.st_size;
-
- close(fd);
-
- return 0;
-}
-
-void file_unmap(char *buf, size_t bufsize)
-{
- munmap(buf, bufsize);
-}
-
-size_t buf_get_line(char *buf, size_t buflen, size_t cur)
-{
- size_t count = 0;
-
- for (count = cur; count < buflen && buf[count] != '\n'; count++);
-
- return count - cur;
-}
-
diff --git a/src/core/misc.h b/src/core/misc.h
index 400e580..e69de29 100644
--- a/src/core/misc.h
+++ b/src/core/misc.h
@@ -1,282 +0,0 @@
-/*
- * misc.h
- *
- * Miscellaneous macro's and functions.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#ifndef _MISC_H
-#define _MISC_H
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-/* Gentoo style e* printing macro's */
-#define EINFO(_args...) \
- do { \
- int old_errno = errno; \
- printf(" \033[32;01m*\033[0m " _args); \
- errno = old_errno; \
- } while (0)
-
-#define EWARN(_args...) \
- do { \
- int old_errno = errno; \
- printf(" \033[33;01m*\033[0m " _args); \
- errno = old_errno; \
- } while (0)
-
-#define EERROR(_args...) \
- do { \
- int old_errno = errno; \
- fprintf(stderr, " \033[31;01m*\033[0m " _args); \
- errno = old_errno; \
- } while (0)
-
-/* Min/Max macro's */
-#ifdef MAX
-# undef MAX
-#endif
-#define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b))
-#ifdef MIN
-# undef MIN
-#endif
-#define MIN(_a, _b) ((_a) > (_b) ? (_b) : (_a))
-
-/* Return true if filename '_name' ends in '_ext' */
-#define CHECK_FILE_EXTENSION(_name, _ext) \
- ((strlen(_name) > strlen(_ext)) \
- && (0 == strncmp(&(_name[strlen(_name) - strlen(_ext)]), \
- _ext, strlen(_ext))))
-
-/* Add a new item to a string list. If the pointer to the list is NULL,
- * allocate enough memory for the amount of entries needed. Ditto for
- * when it already exists, but we add one more entry than it can
- * contain. The list is NULL terminated.
- * NOTE: _only_ memory for the list are allocated, and not for the items - that
- * should be done by relevant code (unlike STRING_LIST_DEL that will
- * free the memory) */
-#define STRING_LIST_ADD(_string_list, _item, _error) \
- do { \
- char **_tmp_p; \
- int _i = 0; \
- if ((NULL == _item) || (0 == strlen(_item))) { \
- DBG_MSG("Invalid argument passed!\n"); \
- errno = EINVAL; \
- goto _error; \
- } \
- while ((NULL != _string_list) && (NULL != _string_list[_i])) { \
- _i++; \
- } \
- /* Amount of entries + new + terminator */ \
- _tmp_p = realloc(_string_list, sizeof(char *) * (_i + 2)); \
- if (NULL == _tmp_p) { \
- DBG_MSG("Failed to reallocate list!\n"); \
- goto _error; \
- } \
- _string_list = _tmp_p; \
- _string_list[_i] = _item; \
- /* Terminator */ \
- _string_list[_i+1] = NULL; \
- } while (0)
-
-/* Add a new item to a string list (foundamental the same as above), but make
- * sure we have all the items alphabetically sorted. */
-#define STRING_LIST_ADD_SORT(_string_list, _item, _error) \
- do { \
- char **_tmp_p; \
- char *_str_p1; \
- char *_str_p2; \
- int _i = 0; \
- if ((NULL == _item) || (0 == strlen(_item))) { \
- DBG_MSG("Invalid argument passed!\n"); \
- errno = EINVAL; \
- goto _error; \
- } \
- while ((NULL != _string_list) && (NULL != _string_list[_i])) \
- _i++; \
- /* Amount of entries + new + terminator */ \
- _tmp_p = realloc(_string_list, sizeof(char *) * (_i + 2)); \
- if (NULL == _tmp_p) { \
- DBG_MSG("Failed to reallocate list!\n"); \
- goto _error; \
- } \
- _string_list = _tmp_p; \
- if (0 == _i) \
- /* Needed so that the end NULL will propagate
- * (iow, make sure our 'NULL != _str_p1' test below
- * do not fail) */ \
- _string_list[_i] = NULL; \
- /* Actual terminator that needs adding */ \
- _string_list[_i+1] = NULL; \
- _i = 0; \
- /* See where we should insert the new item to have it all \
- * alphabetically sorted */ \
- while (NULL != _string_list[_i]) { \
- if (strcmp(_string_list[_i], _item) > 0) { \
- break; \
- } \
- _i++; \
- } \
- /* Now just insert the new item, and shift the rest one over.
- * '_str_p2' is temporary storage to swap the indexes in a loop,
- * and 'str_p1' is used to store the old value across the loop */ \
- _str_p1 = _string_list[_i]; \
- _string_list[_i] = _item; \
- do { \
- _i++;\
- _str_p2 = _string_list[_i]; \
- _string_list[_i] = _str_p1; \
- _str_p1 = _str_p2; \
- } while (NULL != _str_p1); \
- } while (0)
-
-/* Delete one entry from the string list, and shift the rest down if the entry
- * was not at the end. For now we do not resize the amount of entries the
- * string list can contain, and free the memory for the matching item */
-#define STRING_LIST_DEL(_string_list, _item, _error) \
- do { \
- int _i = 0; \
- if ((NULL == _item) \
- || (0 == strlen(_item)) \
- || (NULL == _string_list)) { \
- DBG_MSG("Invalid argument passed!\n"); \
- errno = EINVAL; \
- goto _error; \
- } \
- while (NULL != _string_list[_i]) { \
- if (0 == strcmp(_item, _string_list[_i])) \
- break; \
- else \
- _i++; \
- } \
- if (NULL == _string_list[_i]) { \
- DBG_MSG("Invalid argument passed!\n"); \
- errno = EINVAL; \
- goto _error; \
- } \
- free(_string_list[_i]); \
- /* Shift all the following items one forward */ \
- do { \
- _string_list[_i] = _string_list[_i+1]; \
- /* This stupidity is to shutup gcc */ \
- _i++; \
- } while (NULL != _string_list[_i]); \
- } while (0)
-
-/* Step through each entry in the string list, setting '_pos' to the
- * beginning of the entry. '_counter' is used by the macro as index,
- * but should not be used by code as index (or if really needed, then
- * it should usually by +1 from what you expect, and should only be
- * used in the scope of the macro) */
-#define STRING_LIST_FOR_EACH(_string_list, _pos, _counter) \
- if ((NULL != _string_list) && (0 == (_counter = 0))) \
- while (NULL != (_pos = _string_list[_counter++]))
-
-/* Same as above (with the same warning about '_counter'). Now we just
- * have '_next' that are also used for indexing. Once again rather refrain
- * from using it if not absolutely needed. The major difference to above,
- * is that it should be safe from having the item removed from under you. */
-#define STRING_LIST_FOR_EACH_SAFE(_string_list, _pos, _next, _counter) \
- if ((NULL != _string_list) \
- && (0 == (_counter = 0))) \
- /* First part of the while checks if this is the
- * first loop, and if so setup _pos and _next
- * and increment _counter */ \
- while ((((0 == _counter) \
- && (NULL != (_pos = _string_list[_counter])) \
- && (_pos != (_next = _string_list[++_counter]))) \
- /* Second part is when it is not the first loop
- * and _pos was not removed from under us. We
- * just increment _counter, and setup _pos and
- * _next */ \
- || ((0 != _counter) \
- && (_pos == _string_list[_counter-1]) \
- && (_next == _string_list[_counter]) \
- && (NULL != (_pos = _string_list[_counter])) \
- && (_pos != (_next = _string_list[++_counter]))) \
- /* Last part is when _pos was removed from under
- * us. We basically just setup _pos and _next,
- * but leave _counter alone */ \
- || ((0 != _counter) \
- && (_pos != _string_list[_counter-1]) \
- && (_next == _string_list[_counter-1]) \
- && (NULL != (_pos = _string_list[_counter-1])) \
- && (_pos != (_next = _string_list[_counter])))))
-
-/* Just free the whole string list */
-#define STRING_LIST_FREE(_string_list) \
- do { \
- if (NULL != _string_list) { \
- int _i = 0; \
- while (NULL != _string_list[_i]) \
- free(_string_list[_i++]); \
- free(_string_list); \
- _string_list = NULL; \
- } \
- } while (0)
-
-/* String functions. Return a string on success, or NULL on error
- * or no action taken. On error errno will be set.*/
-char *memrepchr(char **str, char old, char _new, size_t size);
-/* Concat two paths adding '/' if needed. Memory will be allocated
- * with the malloc() call. */
-char *strcatpaths(const char *pathname1, const char *pathname2);
-
-/* Compat functions for GNU extensions */
-char *strndup(const char *str, size_t size);
-/* Same as basename(3), but do not modify path */
-char *gbasename(const char *path);
-
-/* The following functions do not care about errors - they only return
- * 1 if 'pathname' exist, and is the type requested, or else 0.
- * They also might clear errno */
-int exists(const char *pathname);
-int is_file(const char *pathname, int follow_link);
-int is_link(const char *pathname);
-int is_dir(const char *pathname, int follow_link);
-
-/* The following function do not care about errors - it only returns
- * the mtime of 'pathname' if it exists, and is the type requested,
- * or else 0. It also might clear errno */
-time_t get_mtime(const char *pathname, int follow_link);
-
-/* The following functions return 0 on success, or -1 with errno set on error. */
-#ifdef __KLIBC__
-int remove(const char *pathname);
-#endif
-int mktree(const char *pathname, mode_t mode);
-int rmtree(const char *pathname);
-
-/* The following return a pointer on success, or NULL with errno set on error.
- * If it returned NULL, but errno is not set, then there was no error, but
- * there is nothing to return. */
-char **ls_dir(const char *pathname, int hidden);
-char *get_cnf_entry(const char *pathname, const char *entry);
-
-/* Below three functions (file_map, file_unmap and buf_get_line) are from
- * udev-050 (udev_utils.c). Please see misc.c for copyright info.
- * (Some are slightly modified, please check udev for originals.) */
-int file_map(const char *filename, char **buf, size_t *bufsize);
-void file_unmap(char *buf, size_t bufsize);
-size_t buf_get_line(char *buf, size_t buflen, size_t cur);
-
-#endif /* _MISC_H */
-
diff --git a/src/core/parse.c b/src/core/parse.c
index f75d8b1..e69de29 100644
--- a/src/core/parse.c
+++ b/src/core/parse.c
@@ -1,981 +0,0 @@
-/*
- * parse.c
- *
- * Parser for Gentoo style rc-scripts.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/poll.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#include "debug.h"
-#include "depend.h"
-#include "list.h"
-#include "misc.h"
-#include "parse.h"
-#include "simple-regex.h"
-
-#define READ_PIPE 0
-#define WRITE_PIPE 1
-
-/* _pipe[0] is used to send data to the parent (thus the parent only use the
- * read pipe, and the child uses the write pipe)
- * _pipe[1] is used to send data to the child (thus the child only use the read
- * pipe, and the parent uses the write pipe)
- */
-#define PARENT_READ_PIPE(_pipe) (_pipe[0][READ_PIPE])
-#define PARENT_WRITE_PIPE(_pipe) (_pipe[1][WRITE_PIPE])
-#define CHILD_READ_PIPE(_pipe) (_pipe[1][READ_PIPE])
-#define CHILD_WRITE_PIPE(_pipe) (_pipe[0][WRITE_PIPE])
-
-#define PARSE_BUFFER_SIZE 256
-
-#define OUTPUT_MAX_LINE_LENGHT 256
-#define OUTPUT_BUFFER_SIZE (60 * 2048)
-
-/* void PRINT_TO_BUFFER(char **_buf, int _count, label _error, format) */
-#define PRINT_TO_BUFFER(_buf, _count, _error, _output...) \
- do { \
- int _i = 0; \
- /* FIXME: Might do something more dynamic here */ \
- if (OUTPUT_BUFFER_SIZE < (_count + OUTPUT_MAX_LINE_LENGHT)) { \
- errno = ENOMEM; \
- DBG_MSG("Output buffer size too small!\n"); \
- goto _error; \
- } \
- _i = sprintf(&((*_buf)[_count]), _output); \
- if (0 < _i) \
- _count += _i + 1; \
- } while (0)
-
-LIST_HEAD(rcscript_list);
-
-size_t parse_rcscript(char *scriptname, char **data, size_t index);
-
-size_t parse_print_start(char **data, size_t index);
-size_t parse_print_header(char *scriptname, char **data, size_t index);
-size_t parse_print_body(char *scriptname, char **data, size_t index);
-
-int get_rcscripts(void)
-{
- rcscript_info_t *info;
- char **file_list = NULL;
- char *rcscript;
- char *confd_file = NULL;
- int count;
-
- file_list = ls_dir(INITD_DIR_NAME, 0);
- if (NULL == file_list) {
- DBG_MSG("'%s' is empty!\n", INITD_DIR_NAME);
- return -1;
- }
-
- STRING_LIST_FOR_EACH(file_list, rcscript, count) {
- /* Is it a file? */
- if (!(is_file(rcscript, 1))
- /* Do not process scripts, source or backup files. */
- || (CHECK_FILE_EXTENSION(rcscript, ".c"))
- || (CHECK_FILE_EXTENSION(rcscript, ".bak"))
- || (CHECK_FILE_EXTENSION(rcscript, "~"))) {
- DBG_MSG("'%s' is not a valid rc-script!\n",
- gbasename(rcscript));
- } else {
- DBG_MSG("Adding rc-script '%s' to list.\n",
- gbasename(rcscript));
-
- info = malloc(sizeof(rcscript_info_t));
- if (NULL == info) {
- DBG_MSG("Failed to allocate rcscript_info_t!\n");
- goto error;
- }
-
- /* Copy the name */
- info->filename = strndup(rcscript, strlen(rcscript));
- if (NULL == info->filename) {
- DBG_MSG("Failed to allocate buffer!\n");
- goto loop_error;
- }
-
- /* Get the modification time */
- info->mtime = get_mtime(rcscript, 1);
- if (0 == info->mtime) {
- DBG_MSG("Failed to get modification time for '%s'!\n",
- rcscript);
- /* We do not care if it fails - we will pick up
- * later if there is a problem with the file */
- }
-
- /* File name for the conf.d config file (if any) */
- confd_file = strcatpaths(CONFD_DIR_NAME, gbasename(rcscript));
- if (NULL == confd_file) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto loop_error;
- }
-
- /* Get the modification time of the conf.d file
- * (if any exists) */
- info->confd_mtime = get_mtime(confd_file, 1);
- if (0 == info->confd_mtime) {
- DBG_MSG("Failed to get modification time for '%s'!\n",
- confd_file);
- /* We do not care that it fails, as not all
- * rc-scripts will have conf.d config files */
- }
-
- free(confd_file);
-
- list_add_tail(&info->node, &rcscript_list);
-
- continue;
-
-loop_error:
- if (NULL != info)
- free(info->filename);
- free(info);
-
- goto error;
- }
- }
-
- /* Final check if we have some entries */
- if ((NULL == file_list) || (NULL == file_list[0])) {
- DBG_MSG("No rc-scripts to parse!\n");
- errno = ENOENT;
- goto error;
- }
-
- STRING_LIST_FREE(file_list);
-
- return 0;
-
-error:
- STRING_LIST_FREE(file_list);
-
- return -1;
-}
-
-/* Returns 0 if we do not need to regen the cache file, else -1 with
- * errno set if something went wrong */
-int check_rcscripts_mtime(char *cachefile)
-{
- rcscript_info_t *info;
- time_t cache_mtime;
- time_t rc_conf_mtime;
- time_t rc_confd_mtime;
-
- if ((NULL == cachefile) || (0 == strlen(cachefile))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- cache_mtime = get_mtime(cachefile, 1);
- if (0 == cache_mtime) {
- DBG_MSG("Could not get modification time for cache file '%s'!\n",
- cachefile);
- return -1;
- }
-
- /* Get and compare mtime for RC_CONF_FILE_NAME with that of cachefile */
- rc_conf_mtime = get_mtime(RC_CONF_FILE_NAME, 1);
- if (rc_conf_mtime > cache_mtime) {
- DBG_MSG("'%s' have a later modification time than '%s'.\n",
- RC_CONF_FILE_NAME, cachefile);
- return -1;
- }
- /* Get and compare mtime for RC_CONFD_FILE_NAME with that of cachefile */
- rc_confd_mtime = get_mtime(RC_CONFD_FILE_NAME, 1);
- if (rc_confd_mtime > cache_mtime) {
- DBG_MSG("'%s' have a later modification time than '%s'.\n",
- RC_CONFD_FILE_NAME, cachefile);
- return -1;
- }
-
- /* Get and compare mtime for each rc-script and its conf.d config file
- * with that of cachefile */
- list_for_each_entry(info, &rcscript_list, node) {
- if ((info->mtime > cache_mtime)
- || (info->confd_mtime > cache_mtime)) {
- DBG_MSG("'%s' have a later modification time than '%s'.\n",
- info->filename, cachefile);
- return -1;
- }
- }
-
- return 0;
-}
-
-/* Return count on success, -1 on error. If it was critical, errno will be set. */
-size_t parse_rcscript(char *scriptname, char **data, size_t index)
-{
- regex_data_t tmp_data;
- char *buf = NULL;
- char *tmp_buf = NULL;
- size_t write_count = index;
- size_t lenght;
- int count;
- int current = 0;
-
- if ((NULL == scriptname) || (0 == strlen(scriptname))) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- return -1;
- }
-
- if (-1 == file_map(scriptname, &buf, &lenght)) {
- DBG_MSG("Could not open '%s' for reading!\n",
- gbasename(scriptname));
- return -1;
- }
-
- while (current < lenght) {
- count = buf_get_line(buf, lenght, current);
-
- tmp_buf = strndup(&(buf[current]), count);
- if (NULL == tmp_buf) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto error;
- }
-
- if (0 == current) {
- /* Check if it starts with '#!/sbin/runscript' */
- DO_REGEX(tmp_data, tmp_buf,
- "[ \t]*#![ \t]*/sbin/runscript[ \t]*.*", error);
- if (REGEX_FULL_MATCH != tmp_data.match) {
- DBG_MSG("'%s' is not a valid rc-script!\n",
- gbasename(scriptname));
- goto error;
- }
-
- /* We do not want rc-scripts ending in '.sh' */
- if (CHECK_FILE_EXTENSION(scriptname, ".sh")) {
- EWARN("'%s' is invalid (should not end with '.sh')!\n",
- gbasename(scriptname));
- goto error;
- }
- DBG_MSG("Parsing '%s'.\n", gbasename(scriptname));
-
- write_count = parse_print_header(gbasename(scriptname),
- data, write_count);
- if (-1 == write_count) {
- DBG_MSG("Failed to call parse_print_header()!\n");
- goto error;
- }
-
- goto _continue;
- }
-
- /* Check for lines with comments, and skip them */
- DO_REGEX(tmp_data, tmp_buf, "^[ \t]*#", error);
- if (REGEX_MATCH(tmp_data))
- goto _continue;
-
- /* If the line contains 'depend()', set 'got_depend' */
- DO_REGEX(tmp_data, tmp_buf, "depend[ \t]*()[ \t]*{?", error);
- if (REGEX_MATCH(tmp_data)) {
- DBG_MSG("Got 'depend()' function.\n");
-
- write_count = parse_print_body(gbasename(scriptname),
- data, write_count);
- if (-1 == write_count) {
- DBG_MSG("Failed to call parse_print_body()!\n");
- goto error;
- }
-
- /* Make sure this is the last loop */
- current += lenght;
- goto _continue;
- }
-
-_continue:
- current += count + 1;
- free(tmp_buf);
- }
-
- file_unmap(buf, lenght);
-
- return write_count;
-
-error:
- free(tmp_buf);
- if (NULL != buf) {
- int old_errno = errno;
- file_unmap(buf, lenght);
- /* file_unmap() might have changed it */
- errno = old_errno;
- }
-
- return -1;
-}
-
-
-size_t generate_stage1(char **data)
-{
- rcscript_info_t *info;
- size_t write_count = 0;
- size_t tmp_count;
-
- write_count = parse_print_start(data, write_count);
- if (-1 == write_count) {
- DBG_MSG("Failed to call parse_print_start()!\n");
- return -1;
- }
-
- list_for_each_entry(info, &rcscript_list, node) {
- tmp_count = parse_rcscript(info->filename, data, write_count);
- if (-1 == tmp_count) {
- DBG_MSG("Failed to parse '%s'!\n",
- gbasename(info->filename));
-
- /* If 'errno' is set, it is critical (hopefully) */
- if (0 != errno)
- return -1;
- } else {
- write_count = tmp_count;
- }
- }
-
- return write_count;
-}
-
-/* Empty signal handler for SIGPIPE */
-static void sig_handler(int signum)
-{
- return;
-}
-
-/* Returns data's lenght on success, else -1 on error. */
-size_t generate_stage2(char **data)
-{
- int pipe_fds[2][2] = { { 0, 0 }, { 0, 0 } };
- pid_t child_pid;
- size_t write_count = 0;
- int old_errno = 0;
-
- /* Pipe to send data to parent */
- if (-1 == pipe(pipe_fds[0])) {
- DBG_MSG("Failed to open pipe!\n");
- goto error;
- }
- /* Pipe to send data to child */
- if (-1 == pipe(pipe_fds[1])) {
- DBG_MSG("Failed to open pipe!\n");
- /* Close parent_pfds */
- goto error;
- }
-
- /* Zero data */
- *data = NULL;
-
- child_pid = fork();
- if (-1 == child_pid) {
- DBG_MSG("Failed to fork()!\n");
- /* Close all pipes */
- goto error;
- }
- if (0 == child_pid) {
- /***
- *** In child
- ***/
-
- char *const argv[] = {
- "bash",
- "--noprofile",
- "--norc",
- "--",
- NULL
- };
-
- /* Close the sides of the pipes we do not use */
- close(PARENT_WRITE_PIPE(pipe_fds));
- close(PARENT_READ_PIPE(pipe_fds));
-
- /* dup2 child side read pipe to STDIN */
- dup2(CHILD_READ_PIPE(pipe_fds), STDIN_FILENO);
- /* dup2 child side write pipe to STDOUT */
- dup2(CHILD_WRITE_PIPE(pipe_fds), STDOUT_FILENO);
-
- /* We need to be in INITD_DIR_NAME for 'before'/'after' '*' to work */
- if (-1 == chdir(INITD_DIR_NAME)) {
- DBG_MSG("Failed to chdir to '%s'!\n", INITD_DIR_NAME);
- exit(1);
- }
-
- if (-1 == execv(SHELL_PARSER, argv)) {
- DBG_MSG("Failed to execv %s!\n", SHELL_PARSER);
- exit(1);
- }
- } else {
- /***
- *** In parent
- ***/
-
- struct sigaction act_new;
- struct sigaction act_old;
- struct pollfd poll_fds[2];
- char buf[PARSE_BUFFER_SIZE+1];
- char *stage1_data = NULL;
- size_t stage1_write_count = 0;
- size_t stage1_written = 0;
- int status = 0;
-
- DBG_MSG("Child pid = %i\n", child_pid);
-
- /* Set signal handler for SIGPIPE to empty in case bash errors
- * out. It will then close the write pipe, and instead of us
- * getting SIGPIPE, we can handle the write error like normal.
- */
- memset(&act_new, 0x00, sizeof(act_new));
- act_new.sa_handler = (void (*) (int))sig_handler;
- sigemptyset (&act_new.sa_mask);
- act_new.sa_flags = 0;
- sigaction(SIGPIPE, &act_new, &act_old);
-
- /* Close the sides of the pipes we do not use */
- close(CHILD_WRITE_PIPE(pipe_fds));
- CHILD_WRITE_PIPE(pipe_fds) = 0;
- close(CHILD_READ_PIPE(pipe_fds));
- CHILD_READ_PIPE(pipe_fds) = 0;
-
- stage1_data = malloc(OUTPUT_BUFFER_SIZE + 1);
- if (NULL == stage1_data) {
- DBG_MSG("Failed to allocate buffer!\n");
- goto error;
- }
-
- /* Pipe parse_rcscripts() to bash */
- stage1_write_count = generate_stage1(&stage1_data);
- if (-1 == stage1_write_count) {
- DBG_MSG("Failed to generate stage1!\n");
- goto error;
- }
-
-#if 0
- int tmp_fd = open("bar", O_CREAT | O_TRUNC | O_RDWR, 0600);
- write(tmp_fd, stage1_data, stage1_write_count);
- close(tmp_fd);
-#endif
-
- do {
- int tmp_count = 0;
- int do_write = 0;
- int do_read = 0;
-
- /* Check if we can write or read */
- poll_fds[WRITE_PIPE].fd = PARENT_WRITE_PIPE(pipe_fds);
- poll_fds[WRITE_PIPE].events = POLLOUT;
- poll_fds[READ_PIPE].fd = PARENT_READ_PIPE(pipe_fds);
- poll_fds[READ_PIPE].events = POLLIN | POLLPRI;
- if (stage1_written < stage1_write_count) {
- poll(poll_fds, 2, -1);
- if (poll_fds[WRITE_PIPE].revents & POLLOUT)
- do_write = 1;
- } else {
- poll(&(poll_fds[READ_PIPE]), 1, -1);
- }
- if ((poll_fds[READ_PIPE].revents & POLLIN)
- || (poll_fds[READ_PIPE].revents & POLLPRI))
- do_read = 1;
-
- do {
- /* If we can write, or there is nothing to
- * read, keep feeding the write pipe */
- if ((stage1_written >= stage1_write_count)
- || (1 == do_read)
- || (1 != do_write))
- break;
-
- tmp_count = write(PARENT_WRITE_PIPE(pipe_fds),
- &stage1_data[stage1_written],
- strlen(&stage1_data[stage1_written]));
- if ((-1 == tmp_count) && (EINTR != errno)) {
- DBG_MSG("Error writing to PARENT_WRITE_PIPE!\n");
- goto failed;
- }
- /* We were interrupted, try to write again */
- if (-1 == tmp_count) {
- errno = 0;
- /* Make sure we retry */
- tmp_count = 1;
- continue;
- }
- /* What was written before, plus what
- * we wrote now as well as the ending
- * '\0' of the line */
- stage1_written += tmp_count + 1;
-
- /* Close the write pipe if we done
- * writing to get a EOF signaled to
- * bash */
- if (stage1_written >= stage1_write_count) {
- close(PARENT_WRITE_PIPE(pipe_fds));
- PARENT_WRITE_PIPE(pipe_fds) = 0;
- }
- } while ((tmp_count > 0) && (stage1_written < stage1_write_count));
-
- /* Reset tmp_count for below read loop */
- tmp_count = 0;
-
- do {
- char *tmp_p;
-
- if (1 != do_read)
- continue;
-
- tmp_count = read(PARENT_READ_PIPE(pipe_fds), buf,
- PARSE_BUFFER_SIZE);
- if ((-1 == tmp_count) && (EINTR != errno)) {
- DBG_MSG("Error reading PARENT_READ_PIPE!\n");
- goto failed;
- }
- /* We were interrupted, try to read again */
- if ((-1 == tmp_count) || (0 == tmp_count)) {
- errno = 0;
- continue;
- }
-
- tmp_p = realloc(*data, write_count + tmp_count);
- if (NULL == tmp_p) {
- DBG_MSG("Failed to allocate buffer!\n");
- goto failed;
- }
-
- memcpy(&tmp_p[write_count], buf, tmp_count);
-
- *data = tmp_p;
- write_count += tmp_count;
- } while (tmp_count > 0);
- } while (!(poll_fds[READ_PIPE].revents & POLLHUP));
-
-failed:
- /* Set old_errno to disable child exit code checking below */
- if (0 != errno)
- old_errno = errno;
-
- free(stage1_data);
-
- if (0 != PARENT_WRITE_PIPE(pipe_fds))
- close(PARENT_WRITE_PIPE(pipe_fds));
- close(PARENT_READ_PIPE(pipe_fds));
-
- /* Restore the old signal handler for SIGPIPE */
- sigaction(SIGPIPE, &act_old, NULL);
-
- /* Wait for bash to finish */
- waitpid(child_pid, &status, 0);
- /* If old_errno is set, we had an error in the read loop, so do
- * not worry about the child's exit code */
- if (0 == old_errno) {
- if ((!WIFEXITED(status)) || (0 != WEXITSTATUS(status))) {
- DBG_MSG("Bash failed with status 0x%x!\n", status);
- return -1;
- }
- } else {
- /* Right, we had an error, so set errno, and exit */
- errno = old_errno;
- return -1;
- }
- }
-
- return write_count;
-
- /* Close parent side pipes */
-error:
- /* Close all pipes */
- old_errno = errno;
- if (0 != CHILD_READ_PIPE(pipe_fds))
- close(CHILD_READ_PIPE(pipe_fds));
- if (0 != CHILD_WRITE_PIPE(pipe_fds))
- close(CHILD_WRITE_PIPE(pipe_fds));
- if (0 != PARENT_READ_PIPE(pipe_fds))
- close(PARENT_READ_PIPE(pipe_fds));
- if (0 != PARENT_WRITE_PIPE(pipe_fds))
- close(PARENT_WRITE_PIPE(pipe_fds));
- /* close() might have changed it */
- errno = old_errno;
-
- return -1;
-}
-
-int write_legacy_stage3(FILE *output)
-{
- service_info_t *info;
- char *service;
- int count;
- int index = 0;
- int dep_count;
- int i;
-
- if (-1 == fileno(output)) {
- DBG_MSG("Bad output stream!\n");
- return -1;
- }
-
- fprintf(output, "rc_type_ineed=2\n");
- fprintf(output, "rc_type_needsme=3\n");
- fprintf(output, "rc_type_iuse=4\n");
- fprintf(output, "rc_type_usesme=5\n");
- fprintf(output, "rc_type_ibefore=6\n");
- fprintf(output, "rc_type_iafter=7\n");
- fprintf(output, "rc_type_broken=8\n");
- fprintf(output, "rc_type_mtime=9\n");
- fprintf(output, "rc_index_scale=10\n\n");
- fprintf(output, "declare -a RC_DEPEND_TREE\n\n");
-
- list_for_each_entry(info, &service_info_list, node) {
- index++;
- }
- if (0 == index) {
- EERROR("No services to generate dependency tree for!\n");
- return -1;
- }
-
- fprintf(output, "RC_DEPEND_TREE[0]=%i\n\n", index);
-
- index = 1;
-
- list_for_each_entry(info, &service_info_list, node) {
- fprintf(output, "RC_DEPEND_TREE[%i]=\"%s\"\n",
- index * 10, info->name);
-
- for (i = 0;i <= BROKEN;i++) {
- dep_count = 0;
-
- fprintf(output, "RC_DEPEND_TREE[%i+%i]=",
- (index * 10), (i + 2));
-
- STRING_LIST_FOR_EACH(info->depend_info[i], service, count) {
- if (0 == dep_count)
- fprintf(output, "\"%s", service);
- else
- fprintf(output, " %s", service);
-
- dep_count++;
- }
-
- if (dep_count > 0)
- fprintf(output, "\"\n");
- else
- fprintf(output, "\n");
- }
-
- fprintf(output, "RC_DEPEND_TREE[%i+9]=\"%li\"\n\n",
- index * 10, info->mtime);
- index++;
- }
-
- fprintf(output, "RC_GOT_DEPTREE_INFO=\"yes\"\n");
-
- info = service_get_virtual("logger");
- if (NULL == info) {
- DBG_MSG("No service provides the 'logger' logger virtual!\n");
- fprintf(output, "\nLOGGER_SERVICE=\n");
- } else {
- fprintf(output, "\nLOGGER_SERVICE=\"%s\"\n", info->name);
- }
-
-
- return 0;
-}
-
-int parse_cache(const char *data, size_t lenght)
-{
- service_info_t *info;
- service_type_t type = ALL_SERVICE_TYPE_T;
- rcscript_info_t *rs_info;
- char *tmp_buf = NULL;
- char *rc_name = NULL;
- char *tmp_p;
- char *token;
- char *field;
- int count;
- int current = 0;
- int retval;
-
- if ((NULL == data) || (lenght <= 0)) {
- DBG_MSG("Invalid argument passed!\n");
- errno = EINVAL;
- goto error;
- }
-
- while (current < lenght) {
- count = buf_get_line((char *)data, lenght, current);
-
- tmp_buf = strndup(&(data[current]), count);
- if (NULL == tmp_buf) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto error;
- }
- tmp_p = tmp_buf;
-
- /* Strip leading spaces/tabs */
- while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
- tmp_p++;
-
- /* Get FIELD name and FIELD value */
- token = strsep(&tmp_p, " ");
-
- /* FIELD name empty/bogus? */
- if ((NULL == token)
- || (0 == strlen(token))
- /* We got an empty FIELD value */
- || (NULL == tmp_p)
- || (0 == strlen(tmp_p))) {
- DBG_MSG("Parsing stopped due to short read!\n");
- errno = EMSGSIZE;
- goto error;
- }
-
- if (0 == strcmp(token, FIELD_RCSCRIPT)) {
- DBG_MSG("Field = '%s', value = '%s'\n", token, tmp_p);
-
- /* Add the service to the list, and initialize all data */
- retval = service_add(tmp_p);
- if (-1 == retval) {
- DBG_MSG("Failed to add %s to service list!\n", tmp_p);
- goto error;
- }
-
- info = service_get_info(tmp_p);
- if (NULL == info) {
- DBG_MSG("Failed to get info for '%s'!\n", tmp_p);
- goto error;
- }
- /* Save the rc-script name for next passes of loop */
- rc_name = info->name;
-
- goto _continue;
- }
-
- if (NULL == rc_name) {
- DBG_MSG("Other fields should come after '%s'!\n", FIELD_RCSCRIPT);
- goto error;
- }
-
- if (0 == strcmp(token, FIELD_NEED))
- type = NEED;
- else if (0 == strcmp(token, FIELD_USE))
- type = USE;
- else if (0 == strcmp(token, FIELD_BEFORE))
- type = BEFORE;
- else if (0 == strcmp(token, FIELD_AFTER))
- type = AFTER;
- else if (0 == strcmp(token, FIELD_PROVIDE))
- type = PROVIDE;
- else if (0 == strcmp(token, FIELD_FAILED)) {
- type = BROKEN;
-
- /* FIXME: Need to think about what to do syntax BROKEN
- * services */
- EWARN("'%s' has syntax errors, please correct!\n", rc_name);
- }
-
- if (type < ALL_SERVICE_TYPE_T) {
- /* Get the first value *
- * As the values are passed to a bash function, and we
- * then use 'echo $*' to parse them, they should only
- * have one space between each value ... */
- token = strsep(&tmp_p, " ");
-
- /* Get the correct type name */
- field = service_type_names[type];
-
- while (NULL != token) {
- DBG_MSG("Field = '%s', service = '%s', value = '%s'\n",
- field, rc_name, token);
-
- retval = service_add_dependency(rc_name, token, type);
- if (-1 == retval) {
- DBG_MSG("Failed to add dependency '%s' to service '%s', type '%s'!\n",
- token, rc_name, field);
- goto error;
- }
-
- /* Get the next value (if any) */
- token = strsep(&tmp_p, " ");
- }
-
- goto _continue;
- }
-
- /* Fall through */
- DBG_MSG("Unknown FIELD in data!\n");
-
-_continue:
- type = ALL_SERVICE_TYPE_T;
- current += count + 1;
- free(tmp_buf);
- /* Do not free 'rc_name', as it should be consistant
- * across loops */
- }
-
- /* Set the mtimes
- * FIXME: Can drop this when we no longer need write_legacy_stage3() */
- list_for_each_entry(rs_info, &rcscript_list, node) {
- rc_name = gbasename(rs_info->filename);
- if (NULL == service_get_info(rc_name))
- continue;
-
- retval = service_set_mtime(rc_name, rs_info->mtime);
- if (-1 == retval) {
- DBG_MSG("Failed to set mtime for service '%s'!\n", rc_name);
- return -1;
- }
- }
-
- return 0;
-
-error:
- free(tmp_buf);
-
- return -1;
-}
-
-size_t parse_print_start(char **data, size_t index)
-{
- size_t write_count = index;
-
- PRINT_TO_BUFFER(data, write_count, error,
- ". /sbin/functions.sh\n"
- "[ -e /etc/rc.conf ] && . /etc/rc.conf\n"
- "\n"
- /* "set -e\n" */
- "\n");
-
- return write_count;
-
-error:
- return -1;
-}
-
-size_t parse_print_header(char *scriptname, char **data, size_t index)
-{
- size_t write_count = index;
-
- PRINT_TO_BUFFER(data, write_count, error,
- "#*** %s ***\n"
- "\n"
- "myservice=\"%s\"\n"
- "echo \"RCSCRIPT ${myservice}\"\n"
- "\n",
- scriptname, scriptname);
-
- return write_count;
-
-error:
- return -1;
-}
-
-size_t parse_print_body(char *scriptname, char **data, size_t index)
-{
- size_t write_count = index;
- char *tmp_buf = NULL;
- char *tmp_ptr;
- char *base;
- char *ext;
-
- tmp_buf = strndup(scriptname, strlen(scriptname));
- if (NULL == tmp_buf) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto error;
- }
-
- /*
- * Rather do the next block in C than bash, in case we want to
- * use ash or another shell in the place of bash
- */
-
- /* bash: base="${myservice%%.*}" */
- base = tmp_buf;
- tmp_ptr = strchr(tmp_buf, '.');
- if (NULL != tmp_ptr) {
- tmp_ptr[0] = '\0';
- tmp_ptr++;
- } else {
- tmp_ptr = tmp_buf;
- }
- /* bash: ext="${myservice##*.}" */
- ext = strrchr(tmp_ptr, '.');
- if (NULL == ext)
- ext = tmp_ptr;
-
- PRINT_TO_BUFFER(data, write_count, error,
- "\n"
- "(\n"
- " # Get settings for rc-script ...\n"
- " [ -e \"/etc/conf.d/${myservice}\" ] && \\\n"
- " . \"/etc/conf.d/${myservice}\"\n"
- " [ -e /etc/conf.d/net ] && \\\n"
- " [ \"%s\" = \"net\" ] && \\\n"
- " [ \"%s\" != \"${myservice}\" ] && \\\n"
- " . /etc/conf.d/net\n"
- " depend() {\n"
- " return 0\n"
- " }\n"
- " \n"
- " # Actual depend() function ...\n"
- " (\n"
- " set -e\n"
- " . \"/etc/init.d/%s\" >/dev/null 2>&1\n"
- " set +e\n"
- " \n"
- " need() {\n"
- " [ \"$#\" -gt 0 ] && echo \"NEED $*\"; return 0\n"
- " }\n"
- " \n"
- " use() {\n"
- " [ \"$#\" -gt 0 ] && echo \"USE $*\"; return 0\n"
- " }\n"
- " \n"
- " before() {\n"
- " [ \"$#\" -gt 0 ] && echo \"BEFORE $*\"; return 0\n"
- " }\n"
- " \n"
- " after() {\n"
- " [ \"$#\" -gt 0 ] && echo \"AFTER $*\"; return 0\n"
- " }\n"
- " \n"
- " provide() {\n"
- " [ \"$#\" -gt 0 ] && echo \"PROVIDE $*\"; return 0\n"
- " }\n"
- " \n"
- " depend\n"
- " ) || echo \"FAILED ${myservice}\"\n"
- ")\n"
- "\n\n",
- base, ext, scriptname);
-
- return write_count;
-
-error:
- return -1;
-}
-
diff --git a/src/core/parse.h b/src/core/parse.h
index f00d835..e69de29 100644
--- a/src/core/parse.h
+++ b/src/core/parse.h
@@ -1,109 +0,0 @@
-/*
- * parse.h
- *
- * Parser for Gentoo style rc-scripts.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#ifndef _PARSE_H
-#define _PARSE_H
-
-#include <sys/types.h>
-#include "list.h"
-
-#define RC_CONF_FILE_NAME "/etc/rc.conf"
-#define RC_CONFD_FILE_NAME "/etc/conf.d/rc"
-#define INITD_DIR_NAME "/etc/init.d/"
-#define CONFD_DIR_NAME "/etc/conf.d/"
-
-#define SVCDIR_CONFIG_ENTRY "svcdir"
-
-#define SHELL_PARSER "/bin/bash"
-
-#define LEGACY_CACHE_FILE_NAME "deptree"
-
-#define FIELD_RCSCRIPT "RCSCRIPT"
-#define FIELD_NEED "NEED"
-#define FIELD_USE "USE"
-#define FIELD_BEFORE "BEFORE"
-#define FIELD_AFTER "AFTER"
-#define FIELD_PROVIDE "PROVIDE"
-#define FIELD_FAILED "FAILED"
-
-typedef struct {
- struct list_head node;
-
- char *filename;
- time_t mtime;
- time_t confd_mtime;
-} rcscript_info_t;
-
-struct list_head rcscript_list;
-
-int get_rcscripts(void);
-int check_rcscripts_mtime(char *cachefile);
-size_t generate_stage1(char **data);
-size_t generate_stage2(char **data);
-size_t read_stage2(char **data);
-int write_stage2(FILE *outfile);
-size_t generate_stage3(char **data);
-size_t read_stage3(char **data);
-int write_stage3(FILE *outfile);
-int write_legacy_stage3(FILE *output);
-int parse_cache(const char *data, size_t lenght);
-
-/*
- * get_rcscripts()
- * |
- * V
- * check_rcscripts_mtime() ------------------------------> read_stage3()
- * | |
- * | |
- * V V
- * generate_stage1() (Called by generate_stage2()) parse_cache()
- * | |
- * | |
- * V |
- * generate_stage2() ----> write_stage2() (Debugging) |
- * | |
- * | |
- * | === parse_cache() |
- * V | | |
- * generate_stage3() ==| | |
- * | | | |
- * | | V |
- * | === service_resolve_dependencies() |
- * | |
- * | |
- * |-------> write_legacy_stage3() (Proof of Concept |
- * | or Debugging) |
- * | |
- * V |
- * write_stage3() |
- * | |
- * | V
- * |<-------------------------------------------------------
- * |
- * V
- *
- */
-
-#endif /* _PARSE_H */
-
diff --git a/src/core/scripts/Makefile.am b/src/core/scripts/Makefile.am
new file mode 100644
index 0000000..522c755
--- /dev/null
+++ b/src/core/scripts/Makefile.am
@@ -0,0 +1,3 @@
+EXTRA_DIST = \
+ svn2cl.sh \
+ svn2cl.xsl
diff --git a/src/core/scripts/clean.sh b/src/core/scripts/clean.sh
new file mode 100644
index 0000000..47262ee
--- /dev/null
+++ b/src/core/scripts/clean.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+if [[ -f Makefile ]] ; then
+ make distclean
+fi
+
+for f in \
+ `find . -name Makefile.in -o -name Makefile` \
+ `find . -name .libs -o -name .deps -type d` \
+ `find . -name '*.o' -o -name '*.la' -o -name '*.lo' -o -name '*.loT'` \
+ aclocal.m4* autom4te.cache \
+ configure config.* \
+ depcomp install-sh ltmain.sh missing mkinstalldirs libtool \
+ compile stamp-* ;
+do
+ rm -rf $f
+done
diff --git a/src/core/scripts/svn2cl.sh b/src/core/scripts/svn2cl.sh
new file mode 100644
index 0000000..bf89bb0
--- /dev/null
+++ b/src/core/scripts/svn2cl.sh
@@ -0,0 +1,130 @@
+#!/bin/sh
+
+# svn2cl.sh - front end shell script for svn2cl.xsl, calls xsltproc
+# with the correct parameters
+#
+# Copyright (C) 2005 Arthur de Jong.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# exit on any failures
+set -e
+
+# svn2cl version
+VERSION="0.3"
+
+# set default parameters
+STRIPPREFIX=`basename $(pwd)`
+LINELEN=75
+GROUPBYDAY="no"
+INCLUDEREV="no"
+CHANGELOG="ChangeLog"
+
+# do command line checking
+prog=`basename $0`
+while [ -n "$1" ]
+do
+ case "$1" in
+ --strip-prefix)
+ STRIPPREFIX="$2"
+ shift 2
+ ;;
+ --linelen)
+ LINELEN="$2";
+ shift 2
+ ;;
+ --group-by-day)
+ GROUPBYDAY="yes";
+ shift
+ ;;
+ -r|--include-rev)
+ INCLUDEREV="yes";
+ shift
+ ;;
+ -o|--output)
+ CHANGELOG="$2"
+ shift 2
+ ;;
+ --stdout)
+ CHANGELOG="-"
+ shift
+ ;;
+ -V|--version)
+ echo "$prog $VERSION";
+ echo "Written by Arthur de Jong."
+ echo ""
+ echo "Copyright (C) 2005 Arthur de Jong."
+ echo "This is free software; see the source for copying conditions. There is NO"
+ echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+ exit 0
+ ;;
+ -h|--help)
+ echo "Usage: $prog [OPTION]..."
+ echo "Generate a ChangeLog from a checked out subversion repository."
+ echo ""
+ echo " --strip-prefix NAME prefix to strip from all entries, defaults"
+ echo " to the name of the current directory"
+ echo " --linelen NUM maximum length of an output line"
+ echo " --group-by-day group changelog entries by day"
+ echo " -r, --include-rev include revision numbers"
+ echo " -o, --output FILE output to FILE instead of ChangeLog"
+ echo " -f, --file FILE alias for -o, --output"
+ echo " --stdout output to stdout instead of ChangeLog"
+ echo " -h, --help display this help and exit"
+ echo " -V, --version output version information and exit"
+ exit 0
+ ;;
+ *)
+ echo "$prog: invalid option -- $1"
+ echo "Try \`$prog --help' for more information."
+ exit 1
+ ;;
+ esac
+done
+
+# find the directory that this script resides in
+prog="$0"
+while [ -h "$prog" ]
+do
+ prog=`ls -ld "$prog" | sed "s/^.*-> \(.*\)/\1/;/^[^/]/s,^,$(dirname "$prog")/,"`
+done
+dir=`dirname $prog`
+dir=`cd $dir && pwd`
+XSL="$dir/svn2cl.xsl"
+
+# redirect stdout to the changelog file if needed
+if [ "x$CHANGELOG" != "x-" ]
+then
+ exec > "$CHANGELOG"
+fi
+
+# actually run the command we need
+svn --verbose --xml log | \
+ xsltproc --stringparam strip-prefix "$STRIPPREFIX" \
+ --stringparam linelen $LINELEN \
+ --stringparam groupbyday $GROUPBYDAY \
+ --stringparam include-rev $INCLUDEREV \
+ "$XSL" -
diff --git a/src/core/scripts/svn2cl.xsl b/src/core/scripts/svn2cl.xsl
new file mode 100644
index 0000000..3672035
--- /dev/null
+++ b/src/core/scripts/svn2cl.xsl
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+ svn2cl.xsl - xslt stylesheet for converting svn log to a normal
+ changelog
+
+ Usage (replace ++ with two minus signs):
+ svn ++verbose ++xml log | \
+ xsltproc ++stringparam strip-prefix `basename $(pwd)` \
+ ++stringparam linelen 75 \
+ ++stringparam groupbyday yes \
+ ++stringparam include-rev yes \
+ svn2cl.xsl - > ChangeLog
+
+ This file is based on several implementations of this conversion
+ that I was not completely happy with and some other common
+ xslt constructs found on the web.
+
+ Copyright (C) 2004, 2005 Arthur de Jong.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-->
+
+<!DOCTYPE page [
+ <!ENTITY tab "&#9;">
+ <!ENTITY newl "&#13;">
+ <!ENTITY space "&#32;">
+]>
+
+<!--
+ TODO
+ - make external lookups of author names possible
+ - find a place for revision numbers
+ - mark deleted files as such
+ - combine paths
+ - make path formatting nicer
+-->
+
+<xsl:stylesheet
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns="http://www.w3.org/1999/xhtml">
+
+ <xsl:output
+ method="text"
+ encoding="iso-8859-15"
+ media-type="text/plain"
+ omit-xml-declaration="yes"
+ standalone="yes"
+ indent="no" />
+
+ <xsl:strip-space elements="*" />
+
+ <!-- the prefix of pathnames to strip -->
+ <xsl:param name="strip-prefix" select="'/'" />
+
+ <!-- the length of a line to wrap messages at -->
+ <xsl:param name="linelen" select="75" />
+
+ <!-- whether entries should be grouped by day -->
+ <xsl:param name="groupbyday" select="'no'" />
+
+ <!-- whether entries should be grouped by day -->
+ <xsl:param name="include-rev" select="'no'" />
+
+ <!-- add newlines at the end of the changelog -->
+ <xsl:template match="log">
+ <xsl:apply-templates/>
+ <xsl:text>&newl;</xsl:text>
+ </xsl:template>
+
+ <!-- format one entry from the log -->
+ <xsl:template match="logentry">
+ <!-- save log entry number -->
+ <xsl:variable name="pos" select="position()"/>
+ <!-- fetch previous entry's date -->
+ <xsl:variable name="prevdate">
+ <xsl:apply-templates select="../logentry[position()=(($pos)-1)]/date"/>
+ </xsl:variable>
+ <!-- fetch previous entry's author -->
+ <xsl:variable name="prevauthor">
+ <xsl:apply-templates select="../logentry[position()=(($pos)-1)]/author"/>
+ </xsl:variable>
+ <!-- fetch this entry's date -->
+ <xsl:variable name="date">
+ <xsl:apply-templates select="date" />
+ </xsl:variable>
+ <!-- fetch this entry's author -->
+ <xsl:variable name="author">
+ <xsl:apply-templates select="author" />
+ </xsl:variable>
+ <!-- check if header is changed -->
+ <xsl:if test="($prevdate!=$date) or ($prevauthor!=$author)">
+ <!-- add newline -->
+ <xsl:if test="not(position()=1)">
+ <xsl:text>&newl;</xsl:text>
+ </xsl:if>
+ <!-- date -->
+ <xsl:apply-templates select="date" />
+ <!-- two spaces -->
+ <xsl:text>&space;&space;</xsl:text>
+ <!-- author's name -->
+ <xsl:apply-templates select="author" />
+ <!-- two newlines -->
+ <xsl:text>&newl;&newl;</xsl:text>
+ </xsl:if>
+ <!-- get paths string -->
+ <xsl:variable name="paths">
+ <xsl:apply-templates select="paths" />
+ </xsl:variable>
+ <!-- get revision number -->
+ <xsl:variable name="rev">
+ <xsl:if test="$include-rev='yes'">
+ <xsl:text>[r</xsl:text>
+ <xsl:value-of select="@revision"/>
+ <xsl:text>]&space;</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <!-- first line is indented (other indents are done in wrap template) -->
+ <xsl:text>&tab;*&space;</xsl:text>
+ <!-- print the paths and message nicely wrapped -->
+ <xsl:call-template name="wrap">
+ <xsl:with-param name="txt" select="concat($rev,$paths,normalize-space(msg))" />
+ </xsl:call-template>
+ </xsl:template>
+
+ <!-- format date -->
+ <xsl:template match="date">
+ <xsl:variable name="date" select="normalize-space(.)" />
+ <!-- output date part -->
+ <xsl:value-of select="substring($date,1,10)" />
+ <!-- output time part -->
+ <xsl:if test="$groupbyday!='yes'">
+ <xsl:text>&space;</xsl:text>
+ <xsl:value-of select="substring($date,12,5)" />
+ </xsl:if>
+ </xsl:template>
+
+ <!-- format author -->
+ <xsl:template match="author">
+ <xsl:value-of select="normalize-space(.)" />
+ </xsl:template>
+
+ <!-- present a list of paths names -->
+ <xsl:template match="paths">
+ <xsl:for-each select="path">
+ <xsl:sort select="normalize-space(.)" data-type="text" />
+ <!-- unless we are the first entry, add a comma -->
+ <xsl:if test="not(position()=1)">
+ <xsl:text>,&space;</xsl:text>
+ </xsl:if>
+ <!-- print the path name -->
+ <xsl:apply-templates select="."/>
+ </xsl:for-each>
+ <!-- end the list with a colon -->
+ <xsl:text>:&space;</xsl:text>
+ </xsl:template>
+
+ <!-- transform path to something printable -->
+ <xsl:template match="path">
+ <!-- fetch the pathname -->
+ <xsl:variable name="p1" select="normalize-space(.)" />
+ <!-- strip leading slash -->
+ <xsl:variable name="p2">
+ <xsl:choose>
+ <xsl:when test="starts-with($p1,'/')">
+ <xsl:value-of select="substring($p1,2)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$p1" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- strip trailing slash from strip-prefix -->
+ <xsl:variable name="sp">
+ <xsl:choose>
+ <xsl:when test="substring($strip-prefix,string-length($strip-prefix),1)='/'">
+ <xsl:value-of select="substring($strip-prefix,1,string-length($strip-prefix)-1)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$strip-prefix" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- strip strip-prefix -->
+ <xsl:variable name="p3">
+ <xsl:choose>
+ <xsl:when test="starts-with($p2,$sp)">
+ <xsl:value-of select="substring($p2,1+string-length($sp))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- TODO: do not print strings that do not begin with strip-prefix -->
+ <xsl:value-of select="$p2" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- strip another slash -->
+ <xsl:variable name="p4">
+ <xsl:choose>
+ <xsl:when test="starts-with($p3,'/')">
+ <xsl:value-of select="substring($p3,2)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$p3" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- translate empty string to dot -->
+ <xsl:choose>
+ <xsl:when test="$p4 = ''">
+ <xsl:text>.</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$p4" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- string-wrapping template -->
+ <xsl:template name="wrap">
+ <xsl:param name="txt" />
+ <xsl:choose>
+ <xsl:when test="(string-length($txt) &lt; (($linelen)-9)) or not(contains($txt,' '))">
+ <!-- this is easy, nothing to do -->
+ <xsl:value-of select="$txt" />
+ <!-- add newline -->
+ <xsl:text>&newl;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- find the first line -->
+ <xsl:variable name="tmp" select="substring($txt,1,(($linelen)-10))" />
+ <xsl:variable name="line">
+ <xsl:choose>
+ <xsl:when test="contains($tmp,' ')">
+ <xsl:call-template name="find-line">
+ <xsl:with-param name="txt" select="$tmp" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="substring-before($txt,' ')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- print newline and tab -->
+ <xsl:value-of select="$line" />
+ <xsl:text>&newl;&tab;&space;&space;</xsl:text>
+ <!-- wrap the rest of the text -->
+ <xsl:call-template name="wrap">
+ <xsl:with-param name="txt" select="normalize-space(substring($txt,string-length($line)+1))" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- template to trim line to contain space as last char -->
+ <xsl:template name="find-line">
+ <xsl:param name="txt" />
+ <xsl:choose>
+ <xsl:when test="substring($txt,string-length($txt),1) = ' '">
+ <xsl:value-of select="normalize-space($txt)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="find-line">
+ <xsl:with-param name="txt" select="substring($txt,1,string-length($txt)-1)" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/core/simple-regex.c b/src/core/simple-regex.c
index a5a9234..e69de29 100644
--- a/src/core/simple-regex.c
+++ b/src/core/simple-regex.c
@@ -1,827 +0,0 @@
-/*
- * simple_regex.c
- *
- * Simle regex library.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-/*
- * Some notes:
- *
- * - This is a very simple regex library (read: return a match if some string
- * matches some regex). It is probably not POSIX (if there are a POSIX or
- * other standard) compatible.
- *
- * - I primarily wrote it to _not_ use glibc type regex functions, in case we
- * might want to use it in code that have to be linked agaist klibc, etc.
- *
- * - It really is not optimized in any way yet.
- *
- * - Supported operators are:
- *
- * '.', '?', '*', '+' - So called 'wildcards'
- * '[a-z]', '[^a-z]' - Basic 'lists'. Note that 'a-z' just specify that
- * it supports basic lists as well as sequences ..
- * The '^' is for an inverted list of course.
- * '^', '$' - The 'from start' and 'to end' operators. If these
- * are not used at the start ('^') or end ('$') of the
- * regex, they will be treated as normal characters
- * (this of course exclude the use of '^' in a 'list').
- *
- * - If an invalid argument was passed, the functions returns 0 with
- * 'regex_data-match == 0' (no error with no match) rather than -1. It may
- * not be consistant with other practices, but I personally do not feel it is
- * a critical error for these types of functions, and there are debugging you
- * can enable to verify that there are no such issues.
- *
- * - __somefunction() is usually a helper function for somefunction(). I guess
- * recursion might be an alternative, but I try to avoid it.
- *
- * - In general if we are matching a 'wildcard' ('*', '+' or '?'), a 'word'
- * (read: some part of the regex that do not contain a 'wildcard' or 'list')
- * will have a greater 'weight' than the 'wildcard'. This means that we
- * will only continue to evaluate the 'wildcard' until the following 'word'
- * (if any) matches. Currently this do not hold true for a 'list' not
- * followed by a 'wildcard' - I might fix this in future.
- *
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "debug.h"
-#include "misc.h"
-#include "simple-regex.h"
-
-/* Macro to check if a regex_data_t pointer is valid */
-#define CHECK_REGEX_DATA_P(_regex_data, _on_error) \
- do { \
- if ((NULL == _regex_data) \
- || (NULL == _regex_data->data) \
- /* We do not check for this, as it might still \
- * provide a match ('*' or '?' wildcard) */ \
- /* || (0 == strlen(_regex_data->data)) */ \
- || (NULL == _regex_data->regex) \
- || (0 == strlen(_regex_data->regex))) {\
- DBG_MSG("Invalid argument passed!\n"); \
- goto _on_error; \
- } \
- } while (0)
-
-size_t get_word(const char *regex, char **r_word);
-int match_word(regex_data_t *regex_data);
-size_t get_list_size(const char *regex);
-size_t get_list(const char *regex, char **r_list);
-int __match_list(regex_data_t *regex_data);
-int match_list(regex_data_t *regex_data);
-size_t get_wildcard(const char *regex, char *r_wildcard);
-int __match_wildcard(regex_data_t *regex_data,
-int (*match_func)(regex_data_t *regex_data), const char *regex);
-int match_wildcard(regex_data_t *regex_data);
-int __match(regex_data_t *regex_data);
-
-/*
- * Return values for match_* functions
- *
- * 0 - There was no error. If there was a match, regex_data->match
- * - will be > 0 (this is the definitive check - if not true, the
- * - other values of the struct may be bogus), regex_data->count
- * - will be the amount of data that was matched (might be 0 for
- * - some wildcards), and regex_data->r_count will be > 0.
- *
- * -1 - An error occured. Check errno for more info.
- *
- */
-
-size_t get_word(const char *regex, char **r_word)
-{
- char *r_list;
- char *tmp_p;
- size_t count = 0;
- size_t tmp_count;
-
- /* NULL string means we do not have a word */
- if ((NULL == regex) || (0 == strlen(regex))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- *r_word = malloc(strlen(regex) + 1);
- if (NULL == r_word) {
- DBG_MSG("Failed to allocate buffer!\n");
- return 0;
- }
- tmp_p = *r_word;
-
- while (strlen(regex) > 0) {
- switch (regex[0]) {
- case '*':
- case '+':
- case '?':
- /* If its a wildcard, backup one step */
- *(--tmp_p) = '\0';
- count--;
- return count;
- case '[':
- tmp_count = get_list(regex, &r_list);
- free(r_list);
- /* In theory should not happen, but you never know
- * what may happen in future ... */
- if (-1 == tmp_count)
- goto error;
-
- /* Bail if we have a list */
- if (tmp_count > 0) {
- tmp_p[0] = '\0';
- return count;
- }
- default:
- *tmp_p++ = *regex++;
- count++;
- break;
- }
- }
-
- tmp_p[0] = '\0';
-
- return count;
-
-error:
- free(*r_word);
-
- return -1;
-}
-
-int match_word(regex_data_t *regex_data)
-{
- char *data_p = regex_data->data;
- char *r_word = NULL, *r_word_p;
- size_t count = 0;
-
- CHECK_REGEX_DATA_P(regex_data, exit);
-
- count = get_word(regex_data->regex, &r_word);
- if (-1 == count)
- goto error;
- if (0 == count)
- goto exit;
- r_word_p = r_word;
-
- while ((strlen(data_p) > 0) && (strlen(r_word_p) > 0 )) {
- /* If 'r_word' is not 100% part of 'string', we do not have
- * a match. If its a '.', it matches no matter what. */
- if ((data_p[0] != r_word_p[0]) && ('.' != r_word_p[0])) {
- count = 0;
- goto exit;
- }
-
- data_p++;
- r_word_p++;
- }
-
- /* If 'string' is shorter than 'r_word', we do not have a match */
- if ((0 == strlen(data_p)) && (0 < strlen(r_word_p))) {
- count = 0;
- goto exit;
- }
-
-exit:
- /* Fill in our structure */
- if (0 == count)
- regex_data->match = REGEX_NO_MATCH;
- else if (strlen(regex_data->data) == count)
- regex_data->match = REGEX_FULL_MATCH;
- else
- regex_data->match = REGEX_PARTIAL_MATCH;
- if (regex_data->match != REGEX_NO_MATCH)
- regex_data->where = regex_data->data;
- else
- regex_data->where = NULL;
- regex_data->count = count;
- regex_data->r_count = count;
-
- free(r_word);
- return 0;
-
-error:
- regex_data->match = REGEX_NO_MATCH;
-
- free(r_word);
- return -1;
-}
-
-size_t get_list_size(const char *regex)
-{
- size_t count = 0;
-
- /* NULL string means we do not have a list */
- if ((NULL == regex)
- || (0 == strlen(regex))
- || ('[' != regex[0])) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- regex++;
-
- while ((strlen(regex) > 0) && (']' != regex[0])) {
- /* We have a sequence (x-y) */
- if (('-' == regex[0])
- && (']' != regex[1])
- && (strlen(regex) >= 2)
- && (regex[-1] < regex[1]))
- {
- /* Add current + diff in sequence */
- count += regex[1] - regex[-1];
- /* Take care of '-' and next char */
- regex += 2;
- } else {
- regex++;
- count++;
- }
- }
-
- return count;
-}
-
-size_t get_list(const char *regex, char **r_list)
-{
- char *tmp_buf = NULL;
- size_t count = 0;
- size_t size;
-
- /* NULL string means we do not have a list */
- if ((NULL == regex) || (0 == strlen(regex))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- /* Bail if we do not have a list. Do not add debugging, as
- * it is very noisy (used a lot when we call match_list() in
- * __match() and match() to test for list matching) */
- if ('[' != regex[0])
- return 0;
-
- size = get_list_size(regex);
- if (0 == size) {
- /* Should not be an issue, but just in case */
- DBG_MSG("0 returned by get_list_size.\n");
- return 0;
- }
-
- *r_list = malloc(size + 1);
- if (NULL == *r_list) {
- DBG_MSG("Failed to allocate buffer!\n");
- return -1;
- }
- tmp_buf = *r_list;
-
- /* Take care of '[' */
- regex++;
- count++;
-
- while ((strlen(regex) > 0) && (']' != regex[0])) {
- /* We have a sequence (x-y) */
- if (('-' == regex[0])
- && (']' != regex[1])
- && (strlen(regex) >= 2)
- && (regex[-1] < regex[1])) {
- /* Fill in missing chars in sequence */
- while (tmp_buf[-1] < regex[1]) {
- tmp_buf[0] = (char)(tmp_buf[-1] + 1);
- tmp_buf++;
- /* We do not increase count */
- }
- /* Take care of '-' and next char */
- count += 2;
- regex += 2;
- } else {
- *tmp_buf++ = *regex++;
- count++;
- }
- }
-
- tmp_buf[0] = '\0';
- /* Take care of ']' */
- count++;
-
- /* We do not have a list as it does not end in ']' */
- if (']' != regex[0]) {
- count = 0;
- free(*r_list);
- }
-
- return count;
-}
-
-/* If the first is the '^' character, everything but the list is matched
- * NOTE: We only evaluate _ONE_ data character at a time!! */
-int __match_list(regex_data_t *regex_data)
-{
- regex_data_t tmp_data;
- char *data_p = regex_data->data;
- char *list_p = regex_data->regex;
- char test_regex[2] = { '\0', '\0' };
- int invert = 0;
- int match;
- int retval;
-
- CHECK_REGEX_DATA_P(regex_data, failed);
-
- if ('^' == list_p[0]) {
- /* We need to invert the match */
- invert = 1;
- /* Make sure '^' is not part of our list */
- list_p++;
- }
-
- if (invert)
- /* All should be a match if not in the list */
- match = 1;
- else
- /* We only have a match if in the list */
- match = 0;
-
- while (strlen(list_p) > 0) {
- test_regex[0] = list_p[0];
-
- FILL_REGEX_DATA(tmp_data, data_p, test_regex);
- retval = match_word(&tmp_data);
- if (-1 == retval)
- goto error;
-
- if (REGEX_MATCH(tmp_data)) {
- if (invert)
- /* If we exclude the list from
- * characters we try to match, we
- * have a match until one of the
- * list is found. */
- match = 0;
- else
- /* If not, we have to keep looking
- * until one from the list match
- * before we have a match */
- match = 1;
- break;
- }
- list_p++;
- }
-
- /* Fill in our structure */
- if (match) {
- regex_data->match = REGEX_PARTIAL_MATCH;
- regex_data->where = regex_data->data;
- regex_data->count = 1;
- /* This one is more cosmetic, as match_list() will
- * do the right thing */
- regex_data->r_count = 0; /* strlen(regex_data->regex); */
- } else {
-failed:
- regex_data->match = REGEX_NO_MATCH;
- regex_data->where = NULL;
- regex_data->count = 0;
- regex_data->r_count = 0;
- }
-
- return 0;
-
-error:
- regex_data->match = REGEX_NO_MATCH;
-
- return -1;
-}
-
-int match_list(regex_data_t *regex_data)
-{
- regex_data_t tmp_data;
- char *data_p = regex_data->data;
- char *list_p = regex_data->regex;
- char *r_list = NULL;
- size_t r_count = 0;
- int retval;
-
- CHECK_REGEX_DATA_P(regex_data, failed);
-
- r_count = get_list(list_p, &r_list);
- if (-1 == r_count)
- goto error;
- if (0 == r_count)
- goto failed;
-
- FILL_REGEX_DATA(tmp_data, data_p, &list_p[r_count-1]);
- retval = __match_wildcard(&tmp_data, __match_list, r_list);
- if (-1 == retval)
- goto error;
- if (REGEX_MATCH(tmp_data)) {
- /* This should be 2 ('word' + 'wildcard'), so just remove
- * the wildcard */
- tmp_data.r_count--;
- goto exit;
- }
-
- FILL_REGEX_DATA(tmp_data, data_p, r_list);
- retval = __match_list(&tmp_data);
- if (-1 == retval)
- goto error;
- if (REGEX_MATCH(tmp_data))
- goto exit;
-
-failed:
- /* We will fill in regex_data below */
- tmp_data.match = REGEX_NO_MATCH;
- tmp_data.where = NULL;
- tmp_data.count = 0;
- tmp_data.r_count = 0;
-
-exit:
- /* Fill in our structure */
- regex_data->match = tmp_data.match;
- regex_data->where = tmp_data.where;
- regex_data->count = tmp_data.count;
- if (regex_data->match != REGEX_NO_MATCH)
- /* tmp_data.r_count for __match_wildcard will take care of the
- * wildcard, and tmp_data.r_count for __match_list will be 0 */
- regex_data->r_count = r_count + tmp_data.r_count;
- else
- regex_data->r_count = 0;
-
- free(r_list);
- return 0;
-
-error:
- regex_data->match = REGEX_NO_MATCH;
-
- free(r_list);
- return -1;
-}
-
-size_t get_wildcard(const char *regex, char *r_wildcard)
-{
- /* NULL regex means we do not have a wildcard */
- if ((NULL == regex) || (0 == strlen(regex))) {
- DBG_MSG("Invalid argument passed!\n");
- return 0;
- }
-
- r_wildcard[0] = regex[0];
- r_wildcard[2] = '\0';
-
- switch (regex[1]) {
- case '*':
- case '+':
- case '?':
- r_wildcard[1] = regex[1];
- break;
- default:
- r_wildcard[0] = '\0';
- return 0;
- }
-
- return strlen(r_wildcard);
-}
-
-int __match_wildcard(regex_data_t *regex_data, int (*match_func)(regex_data_t *regex_data), const char *regex)
-{
- regex_data_t tmp_data;
- char *data_p = regex_data->data;
- char *wildcard_p = regex_data->regex;
- char r_wildcard[3];
- size_t count = 0;
- size_t r_count = 0;
- int is_match = 0;
- int retval;
-
- CHECK_REGEX_DATA_P(regex_data, exit);
-
- if (NULL == match_func) {
- DBG_MSG("NULL match_func was passed!\n");
- goto exit;
- }
-
- r_count = get_wildcard(wildcard_p, r_wildcard);
- if (0 == r_count)
- goto exit;
-
- FILL_REGEX_DATA(tmp_data, data_p, (char *)regex);
- retval = match_func(&tmp_data);
- if (-1 == retval)
- goto error;
-
- switch (r_wildcard[1]) {
- case '*':
- case '?':
- /* '*' and '?' always matches */
- is_match = 1;
- case '+':
- /* We need to match all of them */
- do {
- /* If we have at least one match for '+', or none
- * for '*' or '?', check if we have a word or list match.
- * We do this because a word weights more than a wildcard */
- if ((strlen(wildcard_p) > 2)
- && ((count > 0)
- || ('*' == r_wildcard[1])
- || ('?' == r_wildcard[1]))) {
- regex_data_t tmp_data2;
-#if 0
- printf("data_p = %s, wildcard_p = %s\n", data_p, wildcard_p);
-#endif
-
- FILL_REGEX_DATA(tmp_data2, data_p, &wildcard_p[2]);
- retval = match(&tmp_data2);
- if (-1 == retval)
- goto error;
-
- if (/* '.' might be a special case ... */
- /* ('.' != wildcard_p[2]) && */
- ((REGEX_MATCH(tmp_data2))
- && (REGEX_FULL_MATCH == tmp_data2.match))) {
- goto exit;
- }
- }
-
- if (REGEX_MATCH(tmp_data)) {
- data_p += tmp_data.count;
- count += tmp_data.count;
- is_match = 1;
-
- FILL_REGEX_DATA(tmp_data, data_p, (char *)regex);
- retval = match_func(&tmp_data);
- if (-1 == retval)
- goto error;
- }
- /* Only once for '?' */
- } while ((REGEX_MATCH(tmp_data)) && ('?' != r_wildcard[1]));
-
- break;
- default:
- /* No wildcard */
- break;
- }
-
-exit:
- /* Fill in our structure */
- /* We can still have a match ('*' and '?'), although count == 0 */
- if ((0 == count) && (0 == is_match))
- regex_data->match = REGEX_NO_MATCH;
- else if (strlen(regex_data->data) == count)
- regex_data->match = REGEX_FULL_MATCH;
- else
- regex_data->match = REGEX_PARTIAL_MATCH;
- if (regex_data->match != REGEX_NO_MATCH)
- regex_data->where = regex_data->data;
- else
- regex_data->where = NULL;
- regex_data->count = count;
- regex_data->r_count = r_count;
-
- return 0;
-
-error:
- regex_data->match = REGEX_NO_MATCH;
-
- return -1;
-}
-
-int match_wildcard(regex_data_t *regex_data)
-{
- regex_data_t tmp_data;
- char *data_p = regex_data->data;
- char *wildcard_p = regex_data->regex;
- char r_wildcard[3];
- size_t r_count;
- int retval;
-
- CHECK_REGEX_DATA_P(regex_data, failed);
-
- /* Invalid wildcard - we need a character + a regex operator */
- if (strlen(wildcard_p) < 2)
- goto failed;
-
- r_count = get_wildcard(wildcard_p, r_wildcard);
- if (0 == r_count)
- goto failed;
-
- /* Needed so that match_word() will not bail if it sees the wildcard */
- r_wildcard[1] = '\0';
-
- FILL_REGEX_DATA(tmp_data, data_p, wildcard_p);
- retval = __match_wildcard(&tmp_data, match_word, r_wildcard);
- if (-1 == retval)
- goto error;
- if (REGEX_MATCH(tmp_data))
- goto exit;
-
-failed:
- /* We will fill in regex_data below */
- tmp_data.match = REGEX_NO_MATCH;
- tmp_data.where = NULL;
- tmp_data.count = 0;
- tmp_data.r_count = 0;
-
-exit:
- /* Fill in our structure */
- regex_data->match = tmp_data.match;
- regex_data->where = tmp_data.where;
- regex_data->count = tmp_data.count;
- regex_data->r_count = tmp_data.r_count;
-
- return 0;
-
-error:
- regex_data->match = REGEX_NO_MATCH;
-
- return -1;
-}
-
-int __match(regex_data_t *regex_data)
-{
- regex_data_t tmp_data;
- char *data_p = regex_data->data;
- char *regex_p = regex_data->regex;
- size_t count = 0;
- size_t r_count = 0;
- int match = 0;
- int retval;
-
- CHECK_REGEX_DATA_P(regex_data, failed);
-
- while (strlen(regex_p) > 0) {
-#if 0
- printf("data_p = '%s', regex_p = '%s'\n", data_p, regex_p);
-#endif
-
- FILL_REGEX_DATA(tmp_data, data_p, regex_p);
- retval = match_list(&tmp_data);
- if (-1 == retval)
- goto error;
- if (REGEX_MATCH(tmp_data))
- goto match;
-
- FILL_REGEX_DATA(tmp_data, data_p, regex_p);
- retval = match_wildcard(&tmp_data);
- if (-1 == retval)
- goto error;
- if (REGEX_MATCH(tmp_data))
- goto match;
-
- FILL_REGEX_DATA(tmp_data, data_p, regex_p);
- retval = match_word(&tmp_data);
- if (-1 == retval)
- goto error;
- if (REGEX_MATCH(tmp_data))
- goto match;
-
- break;
-
-match:
- data_p += tmp_data.count;
- count += tmp_data.count;
- regex_p += tmp_data.r_count;
- r_count += tmp_data.r_count;
- match = 1;
-
- /* Check that we do not go out of bounds */
- if (((data_p - regex_data->data) > strlen(regex_data->data))
- || ((regex_p - regex_data->regex) > strlen(regex_data->regex)))
- goto failed;
- }
-
- /* We could not match the whole regex (data too short?) */
- if (0 != strlen(regex_p))
- goto failed;
-
- goto exit;
-
-failed:
- /* We will fill in regex_data below */
- count = 0;
- r_count = 0;
- match = 0;
-
-exit:
- /* Fill in our structure */
- /* We can still have a match ('*' and '?'), although count == 0 */
- if ((0 == count) && (0 == match))
- regex_data->match = REGEX_NO_MATCH;
- else if (strlen(regex_data->data) == count)
- regex_data->match = REGEX_FULL_MATCH;
- else
- regex_data->match = REGEX_PARTIAL_MATCH;
- if (regex_data->match != REGEX_NO_MATCH)
- regex_data->where = regex_data->data;
- else
- regex_data->where = NULL;
- regex_data->count = count;
- regex_data->r_count = r_count;
-
- return 0;
-
-error:
- regex_data->match = REGEX_NO_MATCH;
-
- return -1;
-}
-
-int match(regex_data_t *regex_data)
-{
- regex_data_t tmp_data;
- char *data_p = regex_data->data;
- char *regex_p;
- char *tmp_buf = NULL;
- int from_start = 0;
- int to_end = 0;
- int retval;
-
- CHECK_REGEX_DATA_P(regex_data, failed);
-
- /* We might be modifying regex_p, so make a copy */
- tmp_buf = strndup(regex_data->regex, strlen(regex_data->regex));
- if (NULL == tmp_buf) {
- DBG_MSG("Failed to allocate temporary buffer!\n");
- goto error;
- }
- regex_p = tmp_buf;
-
- /* Should we only match from the start? */
- if ('^' == regex_p[0]) {
- regex_p++;
- from_start = 1;
- }
-
- /* Should we match up to the end? */
- if ('$' == regex_p[strlen(regex_p) - 1]) {
- regex_p[strlen(regex_p) - 1] = '\0';
- to_end = 1;
- }
-
- do {
- FILL_REGEX_DATA(tmp_data, data_p, regex_p);
- retval = __match(&tmp_data);
- if (-1 == retval)
- goto error;
- } while ((strlen(data_p++) > 0)
- && (!REGEX_MATCH(tmp_data))
- && (0 == from_start));
-
- /* Compensate for above extra inc */
- data_p--;
-
- /* Fill in our structure */
- if (REGEX_MATCH(tmp_data)) {
- /* Check if we had an '$' at the end of the regex, and
- * verify that we still have a match */
- if ((1 == to_end) && (tmp_data.count != strlen(data_p))) {
- goto failed;
- }
-
- if ((data_p == regex_data->data)
- && (tmp_data.match == REGEX_FULL_MATCH))
- regex_data->match = REGEX_FULL_MATCH;
- else
- regex_data->match = REGEX_PARTIAL_MATCH;
- regex_data->where = data_p;
- regex_data->count = tmp_data.count;
- regex_data->r_count = tmp_data.r_count;
- if (1 == from_start)
- regex_data->r_count++;
- if (1 == to_end)
- regex_data->r_count++;
- } else {
-failed:
- regex_data->match = REGEX_NO_MATCH;
- regex_data->where = NULL;
- regex_data->count = 0;
- regex_data->r_count = 0;
- }
-
- free(tmp_buf);
-
- return 0;
-
-error:
- regex_data->match = REGEX_NO_MATCH;
- free(tmp_buf);
-
- return -1;
-}
-
diff --git a/src/core/simple-regex.h b/src/core/simple-regex.h
index ad91a58..e69de29 100644
--- a/src/core/simple-regex.h
+++ b/src/core/simple-regex.h
@@ -1,86 +0,0 @@
-/*
- * simple_regex.h
- *
- * Simle regex library.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#ifndef _SIMPLE_REGEX_H
-#define _SIMPLE_REGEX_H
-
-#define REGEX_NO_MATCH 0 /* We have no match */
-#define REGEX_PARTIAL_MATCH 1 /* Some of the string matches the regex */
-#define REGEX_FULL_MATCH 2 /* The whole string matches the regex */
-
-/* Macro to fill in .data and .regex */
-#define FILL_REGEX_DATA(_regex_data, _string, _regex) \
- do { \
- _regex_data.data = _string; \
- _regex_data.regex = _regex; \
- } while (0)
-
-/* Fill in _regex_data with _data and _regex, on failure goto _error */
-#define DO_REGEX(_regex_data, _data, _regex, _error) \
- do { \
- FILL_REGEX_DATA(_regex_data, _data, _regex); \
- if (-1 == match(&_regex_data)) { \
- DBG_MSG("Could not do regex match!\n"); \
- goto _error; \
- } \
- } while (0)
-
-/* Evaluate to true if we have some kind of match */
-#define REGEX_MATCH(_regex_data) \
- ((REGEX_FULL_MATCH == _regex_data.match) \
- || (REGEX_PARTIAL_MATCH == _regex_data.match))
-
-/* Same as above, but for use when _regex_data is a pointer */
-#define REGEX_MATCH_P(_regex_data) \
- ((REGEX_FULL_MATCH == _regex_data->match) \
- || (REGEX_PARTIAL_MATCH == _regex_data->match))
-
-typedef struct {
- char *data; /* String to perform regex operation on */
- char *regex; /* String containing regex to use */
- int match; /* Will be set if there was a match. Check
- * REGEX_*_MATCH above for possible values */
- char *where; /* Pointer to where match starts in data */
- size_t count; /* Count characters from data matched by regex */
- size_t r_count; /* Count characters of regex used for match. This
- * should normally be the lenght of regex, but might
- * not be for some internal functions ... */
-} regex_data_t;
-
-/*
- * Return:
- *
- * 0 - There was no error. If there was a match, regex_data->match
- * - will be > 0 (this is the definitive check - if not true, the
- * - other values of the struct may be bogus), regex_data->count
- * - will be the amount of data that was matched (might be 0 for
- * - some wildcards), and regex_data->r_count will be > 0.
- *
- * -1 - An error occured. Check errno for more info.
- *
- */
-int match(regex_data_t *regex_data);
-
-#endif /* _SIMPLE_REGEX_H */
-
diff --git a/src/core/src/Makefile.am b/src/core/src/Makefile.am
new file mode 100644
index 0000000..27d52d9
--- /dev/null
+++ b/src/core/src/Makefile.am
@@ -0,0 +1,17 @@
+sbin_PROGRAMS = \
+ depscan \
+ runscript
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(RCSCRIPTS_DEFINES)
+
+LIBRCSCRIPTS = $(top_builddir)/librcscripts/librcscripts.la
+
+depscan_CFLAGS = -DLEGACY_DEPSCAN
+depscan_LDADD = $(LIBRCSCRIPTS)
+depscan_SOURCES = depscan.c
+
+runscript_LDADD = $(LIBRCSCRIPTS) -ldl
+runscript_SOURCES = runscript.c
+
diff --git a/src/core/src/depscan.c b/src/core/src/depscan.c
new file mode 100644
index 0000000..8cc9da8
--- /dev/null
+++ b/src/core/src/depscan.c
@@ -0,0 +1,298 @@
+/*
+ * depscan.c
+ *
+ * Basic frontend for updating the dependency cache.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#ifndef __KLIBC__
+# include <locale.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "librcscripts/rcscripts.h"
+#include "librcscripts/debug.h"
+#include "librcscripts/depend.h"
+#include "librcscripts/misc.h"
+#include "librcscripts/parse.h"
+
+char* svcdir_subdirs[] = {
+ "softscripts",
+ "snapshot",
+ "options",
+ "started",
+ "starting",
+ "inactive",
+ "stopping",
+ NULL
+};
+
+char *svcdir_volatile_subdirs[] = {
+ "snapshot",
+ "broken",
+ NULL
+};
+
+int create_directory(const char *name);
+int create_var_dirs(const char *svcdir);
+int delete_var_dirs(const char *svcdir);
+
+int create_directory(const char *name) {
+ if ((NULL == name) || (0 == strlen(name))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Check if directory exist, and is not a symlink */
+ if (!is_dir(name, 0)) {
+ if (exists(name)) {
+ /* Remove it if not a directory */
+ if (-1 == unlink(name)) {
+ DBG_MSG("Failed to remove '%s'!\n", name);
+ return -1;
+ }
+ }
+ /* Now try to create the directory */
+ if (-1 == mktree(name, 0755)) {
+ DBG_MSG("Failed to create '%s'!\n", name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int create_var_dirs(const char *svcdir) {
+ char *tmp_path = NULL;
+ int i = 0;
+
+ if ((NULL == svcdir) || (0 == strlen(svcdir))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Check and create svcdir if needed */
+ if (-1 == create_directory(svcdir)) {
+ DBG_MSG("Failed to create '%s'!\n", svcdir);
+ return -1;
+ }
+
+ while (NULL != svcdir_subdirs[i]) {
+ tmp_path = strcatpaths(svcdir, svcdir_subdirs[i]);
+ if (NULL == tmp_path) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return -1;
+ }
+
+ /* Check and create all the subdirs if needed */
+ if (-1 == create_directory(tmp_path)) {
+ DBG_MSG("Failed to create '%s'!\n", tmp_path);
+ free(tmp_path);
+ return -1;
+ }
+
+ free(tmp_path);
+ i++;
+ }
+
+ return 0;
+}
+
+int delete_var_dirs(const char *svcdir) {
+ char *tmp_path = NULL;
+ int i = 0;
+
+ if ((NULL == svcdir) || (0 == strlen(svcdir))) {
+ DBG_MSG("Invalid argument passed!\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Just quit if svcdir do not exist */
+ if (!exists(svcdir)) {
+ DBG_MSG("'%s' does not exist!\n", svcdir);
+ return 0;
+ }
+
+ while (NULL != svcdir_volatile_subdirs[i]) {
+ tmp_path = strcatpaths(svcdir, svcdir_volatile_subdirs[i]);
+ if (NULL == tmp_path) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return -1;
+ }
+
+ /* Skip the directory if it does not exist */
+ if (!exists(tmp_path))
+ goto _continue;
+
+ /* Check and delete all files and sub directories if needed */
+ if (-1 == rmtree(tmp_path)) {
+ DBG_MSG("Failed to delete '%s'!\n", tmp_path);
+ free(tmp_path);
+ return -1;
+ }
+
+_continue:
+ free(tmp_path);
+ i++;
+ }
+
+ return 0;
+}
+
+#if defined(LEGACY_DEPSCAN)
+
+int main() {
+ FILE *cachefile_fd = NULL;
+ char *data = NULL;
+ char *svcdir = NULL;
+ char *cachefile = NULL;
+ char *tmp_cachefile = NULL;
+ int tmp_cachefile_fd = 0;
+ int datasize = 0;
+
+ /* Make sure we do not run into locale issues */
+#ifndef __KLIBC__
+ setlocale (LC_ALL, "C");
+#endif
+
+ if (0 != getuid()) {
+ EERROR("Must be root!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ svcdir = get_cnf_entry(RC_CONFD_FILE_NAME, SVCDIR_CONFIG_ENTRY);
+ if (NULL == svcdir) {
+ EERROR("Failed to get config entry '%s'!\n",
+ SVCDIR_CONFIG_ENTRY);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Delete (if needed) volatile directories in svcdir */
+ if (-1 == delete_var_dirs(svcdir)) {
+ /* XXX: Not 100% accurate below message ... */
+ EERROR("Failed to delete '%s', %s", svcdir,
+ "or one of its sub directories!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create all needed directories in svcdir */
+ if (-1 == create_var_dirs(svcdir)) {
+ EERROR("Failed to create '%s', %s", svcdir,
+ "or one of its sub directories!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ cachefile = strcatpaths(svcdir, LEGACY_CACHE_FILE_NAME);
+ if (NULL == cachefile) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tmp_cachefile = strcatpaths(cachefile, "XXXXXX");
+ if (NULL == tmp_cachefile) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ exit(EXIT_FAILURE);
+ }
+ /* Replace the "/XXXXXX" with ".XXXXXX"
+ * Yes, I am lazy. */
+ tmp_cachefile[strlen(tmp_cachefile) - strlen(".XXXXXX")] = '.';
+
+ if (-1 == get_rcscripts()) {
+ EERROR("Failed to get rc-scripts list!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (-1 == check_rcscripts_mtime(cachefile)) {
+ EINFO("Caching service dependencies ...\n");
+ DBG_MSG("Regenerating cache file '%s'.\n", cachefile);
+
+ datasize = generate_stage2(&data);
+ if (-1 == datasize) {
+ EERROR("Failed to generate stage2!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (-1 == parse_cache(data, datasize)) {
+ EERROR("Failed to parse stage2 output!\n");
+ free(data);
+ exit(EXIT_FAILURE);
+ }
+
+#if 0
+ tmp_cachefile_fd = open("foo", O_CREAT | O_TRUNC | O_RDWR, 0600);
+ write(tmp_cachefile_fd, data, datasize);
+ close(tmp_cachefile_fd);
+#endif
+
+ free(data);
+
+ if (-1 == service_resolve_dependencies()) {
+ EERROR("Failed to resolve dependencies!\n");
+ exit(EXIT_FAILURE);
+ }
+
+#ifndef __KLIBC__
+ tmp_cachefile_fd = mkstemp(tmp_cachefile);
+#else
+ /* FIXME: Need to add a mkstemp implementation for klibc */
+ tmp_cachefile_fd = open(tmp_cachefile, O_CREAT | O_TRUNC | O_RDWR, 0600);
+#endif
+ if (-1 == tmp_cachefile_fd) {
+ EERROR("Could not open temporary file for writing!\n");
+ exit(EXIT_FAILURE);
+ }
+ cachefile_fd = fdopen(tmp_cachefile_fd, "w");
+ if (NULL == cachefile_fd) {
+ EERROR("Could not open temporary file for writing!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ write_legacy_stage3(cachefile_fd);
+ fclose(cachefile_fd);
+
+ if ((-1 == unlink(cachefile)) && (exists(cachefile))) {
+ EERROR("Could not remove '%s'!\n", cachefile);
+ unlink(tmp_cachefile);
+ exit(EXIT_FAILURE);
+ }
+
+ if (-1 == rename(tmp_cachefile, cachefile)) {
+ EERROR("Could not move temporary file to '%s'!\n",
+ cachefile);
+ unlink(tmp_cachefile);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+#endif
+
diff --git a/src/core/src/runscript.c b/src/core/src/runscript.c
new file mode 100644
index 0000000..dc3ba51
--- /dev/null
+++ b/src/core/src/runscript.c
@@ -0,0 +1,237 @@
+/*
+ * runscript.c
+ * Handle launching of Gentoo init scripts.
+ *
+ * Copyright 1999-2004 Gentoo Foundation
+ * Distributed under the terms of the GNU General Public License v2
+ * $Header$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dlfcn.h>
+
+#include "librcscripts/rcscripts.h"
+#include "librcscripts/debug.h"
+#include "librcscripts/misc.h"
+
+#define IS_SBIN_RC() ((caller) && (0 == strcmp(caller, SBIN_RC)))
+
+static void (*selinux_run_init_old) (void);
+static void (*selinux_run_init_new) (int argc, char **argv);
+
+extern char **environ;
+
+void setup_selinux(int argc, char **argv) {
+ void *lib_handle = NULL;
+
+ lib_handle = dlopen(SELINUX_LIB, RTLD_NOW | RTLD_GLOBAL);
+ if (NULL != lib_handle) {
+ selinux_run_init_old = dlsym(lib_handle, "selinux_runscript");
+ selinux_run_init_new = dlsym(lib_handle, "selinux_runscript2");
+
+ /* Use new run_init if it exists, else fall back to old */
+ if (NULL != selinux_run_init_new)
+ selinux_run_init_new(argc, argv);
+ else if (NULL != selinux_run_init_old)
+ selinux_run_init_old();
+ else {
+ /* This shouldnt happen... probably corrupt lib */
+ fprintf(stderr, "Run_init is missing from runscript_selinux.so!\n");
+ exit(127);
+ }
+ }
+}
+
+char **get_whitelist(char **whitelist, char *filename) {
+ char *buf = NULL;
+ char *tmp_buf = NULL;
+ char *tmp_p = NULL;
+ char *token = NULL;
+ size_t lenght = 0;
+ int count = 0;
+ int current = 0;
+
+ if (-1 == file_map(filename, &buf, &lenght))
+ return NULL;
+
+ while (current < lenght) {
+ count = buf_get_line(buf, lenght, current);
+
+ tmp_buf = strndup(&buf[current], count);
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ tmp_p = tmp_buf;
+
+ /* Strip leading spaces/tabs */
+ while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
+ tmp_p++;
+
+ /* Get entry - we do not want comments, and only the first word
+ * on a line is valid */
+ token = strsep(&tmp_p, "# \t");
+ if (NULL != token && '\0' != token[0]) {
+ tmp_p = strndup(token, strlen(token));
+ STRING_LIST_ADD(whitelist, tmp_p, error);
+ }
+
+ current += count + 1;
+ free(tmp_buf);
+ /* Set to NULL in case we error out above and have
+ * to free below */
+ tmp_buf = NULL;
+ }
+
+
+ file_unmap(buf, lenght);
+
+ return whitelist;
+
+error:
+ if (NULL != tmp_buf)
+ free(tmp_buf);
+ file_unmap(buf, lenght);
+ STRING_LIST_FREE(whitelist);
+
+ return NULL;
+}
+
+char **filter_environ(char *caller) {
+ char **myenv = NULL;
+ char **whitelist = NULL;
+ char *env_name = NULL;
+ int check_profile = 1;
+ int count = 0;
+
+ if (NULL != getenv(SOFTLEVEL) && !IS_SBIN_RC())
+ /* Called from /sbin/rc, but not /sbin/rc itself, so current
+ * environment should be fine */
+ return environ;
+
+ if (1 == is_file(SYS_WHITELIST, 1))
+ whitelist = get_whitelist(whitelist, SYS_WHITELIST);
+ else
+ EWARN("System environment whitelist missing!\n");
+
+ if (1 == is_file(USR_WHITELIST, 1))
+ whitelist = get_whitelist(whitelist, USR_WHITELIST);
+
+ if (NULL == whitelist)
+ /* If no whitelist is present, revert to old behaviour */
+ return environ;
+
+ if (1 != is_file(PROFILE_ENV, 1))
+ /* XXX: Maybe warn here? */
+ check_profile = 0;
+
+ STRING_LIST_FOR_EACH(whitelist, env_name, count) {
+ char *env_var = NULL;
+ char *tmp_p = NULL;
+ int env_len = 0;
+
+ env_var = getenv(env_name);
+ if (NULL != env_var)
+ goto add_entry;
+
+ if (1 == check_profile) {
+ char *tmp_env_name = NULL;
+ int tmp_len = 0;
+
+ /* The entries in PROFILE_ENV is of the form:
+ * export VAR_NAME=value */
+ tmp_len = strlen(env_name) + strlen("export ") + 1;
+ tmp_env_name = calloc(tmp_len, sizeof(char *));
+ if (NULL == tmp_env_name) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ snprintf(tmp_env_name, tmp_len, "export %s", env_name);
+
+ /* Clear errno so that subsequent calls do not trigger
+ * DBG_MSG */
+ errno = 0;
+ env_var = get_cnf_entry(PROFILE_ENV, tmp_env_name);
+ free(tmp_env_name);
+ if (NULL == env_var && ENOMSG != errno)
+ goto error;
+ else if (NULL != env_var)
+ goto add_entry;
+ }
+
+ continue;
+
+add_entry:
+ env_len = strlen(env_name) + strlen(env_var) + 2;
+ tmp_p = calloc(env_len, sizeof(char *));
+ if (NULL == tmp_p) {
+ DBG_MSG("Failed to allocate temporary buffer!\n");
+ goto error;
+ }
+ snprintf(tmp_p, env_len, "%s=%s", env_name, env_var);
+ STRING_LIST_ADD(myenv, tmp_p, error);
+ }
+
+ STRING_LIST_FREE(whitelist);
+
+ if (NULL == myenv)
+ /* If all else fails, just add a default PATH */
+ STRING_LIST_ADD(myenv, strdup(DEFAULT_PATH), error);
+
+ return myenv;
+
+error:
+ STRING_LIST_FREE(myenv);
+ STRING_LIST_FREE(whitelist);
+
+ return NULL;
+}
+
+int main(int argc, char *argv[]) {
+ char *myargs[32];
+ char **myenv = NULL;
+ char *caller = argv[1];
+ int new = 1;
+
+ /* Need to be /bin/bash, else BASH is invalid */
+ myargs[0] = "/bin/bash";
+ while (argv[new] != 0) {
+ myargs[new] = argv[new];
+ new++;
+ }
+ myargs[new] = NULL;
+
+ /* Do not do help for /sbin/rc */
+ if (argc < 3 && !IS_SBIN_RC()) {
+ execv(RCSCRIPT_HELP, myargs);
+ exit(1);
+ }
+
+ /* Setup a filtered environment according to the whitelist */
+ myenv = filter_environ(caller);
+ if (NULL == myenv) {
+ EWARN("%s: Failed to filter the environment!\n", caller);
+ /* XXX: Might think to bail here, but it could mean the system
+ * is rendered unbootable, so rather not */
+ myenv = environ;
+ }
+
+ /* Ok, we are ready to go, so setup selinux if applicable */
+ setup_selinux(argc, argv);
+
+ if (!IS_SBIN_RC()) {
+ if (execve("/sbin/runscript.sh", myargs, myenv) < 0)
+ exit(1);
+ } else {
+ if (execve("/bin/bash", myargs, myenv) < 0)
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/src/core/test-regex.c b/src/core/test-regex.c
index 610a490..e69de29 100644
--- a/src/core/test-regex.c
+++ b/src/core/test-regex.c
@@ -1,80 +0,0 @@
-/*
- * test-regex.c
- *
- * Test for the simple-regex module.
- *
- * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Header$
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "debug.h"
-#include "simple-regex.h"
-
-char *test_data[] = {
- /* string, pattern, match (1 = yes, 0 = no) */
- "ab", "a?[ab]b", "1",
- "abb", "a?[ab]b", "1",
- "aab", "a?[ab]b", "1",
- "a", "a?a?a?a", "1",
- "aa", "a?a?a?a", "1",
- "aa", "a?a?a?aa", "1",
- "aaa", "a?a?a?aa", "1",
- "ab", "[ab]*", "1",
- "abc", "[ab]*.", "1",
- "ab", "[ab]*b+", "1",
- "ab", "a?[ab]*b+", "1",
- "aaaaaaaaaaaaaaaaaaaaaaa", "a*b", "0",
- "aaaaaaaaabaaabbaaaaaa", "a*b+a*b*ba+", "1",
- "ababababab", "a.*", "1",
- "baaaaaaaab", "a*", "0",
- NULL
-};
-
-int main() {
- regex_data_t tmp_data;
- char buf[256], string[100], regex[100];
- int i;
-
- for (i = 0; NULL != test_data[i]; i += 3) {
- snprintf(string, 99, "'%s'", test_data[i]);
- snprintf(regex, 99, "'%s'", test_data[i + 1]);
- snprintf(buf, 255, "string = %s, pattern = %s", string, regex);
- printf("%-60s", buf);
- DO_REGEX(tmp_data, test_data[i], test_data[i + 1], error);
- if (REGEX_MATCH(tmp_data) && (REGEX_FULL_MATCH == tmp_data.match)) {
- if (0 != strncmp(test_data[i + 2], "1", 1))
- goto error;
- } else {
- if (0 != strncmp(test_data[i + 2], "0", 1))
- goto error;
- }
-
- printf("%s\n", "[ \033[32;01mOK\033[0m ]");
- }
-
- return 0;
-error:
- printf("%s\n", "[ \033[31;01m!!\033[0m ]");
-
- return 1;
-}
diff --git a/src/core/tests/Makefile.am b/src/core/tests/Makefile.am
new file mode 100644
index 0000000..e0bfc4d
--- /dev/null
+++ b/src/core/tests/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = test-regex.c
diff --git a/src/core/tests/test-regex.c b/src/core/tests/test-regex.c
new file mode 100644
index 0000000..610a490
--- /dev/null
+++ b/src/core/tests/test-regex.c
@@ -0,0 +1,80 @@
+/*
+ * test-regex.c
+ *
+ * Test for the simple-regex module.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "simple-regex.h"
+
+char *test_data[] = {
+ /* string, pattern, match (1 = yes, 0 = no) */
+ "ab", "a?[ab]b", "1",
+ "abb", "a?[ab]b", "1",
+ "aab", "a?[ab]b", "1",
+ "a", "a?a?a?a", "1",
+ "aa", "a?a?a?a", "1",
+ "aa", "a?a?a?aa", "1",
+ "aaa", "a?a?a?aa", "1",
+ "ab", "[ab]*", "1",
+ "abc", "[ab]*.", "1",
+ "ab", "[ab]*b+", "1",
+ "ab", "a?[ab]*b+", "1",
+ "aaaaaaaaaaaaaaaaaaaaaaa", "a*b", "0",
+ "aaaaaaaaabaaabbaaaaaa", "a*b+a*b*ba+", "1",
+ "ababababab", "a.*", "1",
+ "baaaaaaaab", "a*", "0",
+ NULL
+};
+
+int main() {
+ regex_data_t tmp_data;
+ char buf[256], string[100], regex[100];
+ int i;
+
+ for (i = 0; NULL != test_data[i]; i += 3) {
+ snprintf(string, 99, "'%s'", test_data[i]);
+ snprintf(regex, 99, "'%s'", test_data[i + 1]);
+ snprintf(buf, 255, "string = %s, pattern = %s", string, regex);
+ printf("%-60s", buf);
+ DO_REGEX(tmp_data, test_data[i], test_data[i + 1], error);
+ if (REGEX_MATCH(tmp_data) && (REGEX_FULL_MATCH == tmp_data.match)) {
+ if (0 != strncmp(test_data[i + 2], "1", 1))
+ goto error;
+ } else {
+ if (0 != strncmp(test_data[i + 2], "0", 1))
+ goto error;
+ }
+
+ printf("%s\n", "[ \033[32;01mOK\033[0m ]");
+ }
+
+ return 0;
+error:
+ printf("%s\n", "[ \033[31;01m!!\033[0m ]");
+
+ return 1;
+}