diff options
author | 2005-12-02 10:56:06 +0000 | |
---|---|---|
committer | 2005-12-02 10:56:06 +0000 | |
commit | a14ee4ab9b6b622475c9520fb3d08e6f78b08881 (patch) | |
tree | ff48cc3f5de2918ab569e3cb391db1ec883a35eb | |
parent | Import the latest baselayout changes. Merging revision 1648. (diff) | |
download | baselayout-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
45 files changed, 6253 insertions, 4813 deletions
diff --git a/ChangeLog.vserver b/ChangeLog.vserver index 7bed750..8a43657 100644 --- a/ChangeLog.vserver +++ b/ChangeLog.vserver @@ -1,6 +1,56 @@ # ChangeLog for Gentoo System Intialization ("rc") scripts # Copyright 1999-2005 Gentoo Foundation; Distributed under the GPLv2 + 02 Dec 2005; Christian Heim <phreak@gentoo.org>: + Import the latest baselayout changes. Merging revision 1658. + + ChangeLog.vserver | 49 + sbin/rc-services.sh | 30 + src/core/COPYING | 340 ++++++ + src/core/ChangeLog | 234 ---- + src/core/ChangeLog.0 | 234 ++++ + src/core/INSTALL | 236 ++++ + src/core/Makefile | 83 - + src/core/Makefile.am | 10 + src/core/autogen.sh | 16 + src/core/configure.ac | 66 + + src/core/data/Makefile.am | 4 + src/core/data/env_whitelist | 30 + src/core/debug.h | 85 - + src/core/depend.c | 557 ---------- + src/core/depend.h | 75 - + src/core/depscan.c | 297 ----- + src/core/librcscripts/Makefile.am | 18 + src/core/librcscripts/debug.h | 85 + + src/core/librcscripts/depend.c | 557 ++++++++++ + src/core/librcscripts/depend.h | 75 + + src/core/librcscripts/list.h | 446 ++++++++ + src/core/librcscripts/misc.c | 659 ++++++++++++ + src/core/librcscripts/misc.h | 282 +++++ + src/core/librcscripts/parse.c | 982 ++++++++++++++++++ + src/core/librcscripts/parse.h | 100 + + src/core/librcscripts/rcscripts.h | 54 + src/core/librcscripts/simple-regex.c | 827 +++++++++++++++ + src/core/librcscripts/simple-regex.h | 86 + + src/core/list.h | 446 -------- + src/core/misc.c | 659 ------------ + src/core/misc.h | 282 ----- + src/core/parse.c | 981 ----------------- + src/core/parse.h | 109 - + src/core/scripts/Makefile.am | 3 + src/core/scripts/clean.sh | 17 + src/core/scripts/svn2cl.sh | 130 ++ + src/core/scripts/svn2cl.xsl | 295 +++++ + src/core/simple-regex.c | 827 --------------- + src/core/simple-regex.h | 86 - + src/core/src/Makefile.am | 17 + src/core/src/depscan.c | 298 +++++ + src/core/src/runscript.c | 237 ++++ + src/core/test-regex.c | 80 - + src/core/tests/Makefile.am | 1 + src/core/tests/test-regex.c | 80 + + 45 files changed, 6253 insertions(+), 4813 deletions(-) + 30 Nov 2005; Christian Heim <phreak@gentoo.org>: Import the latest baselayout changes. Merging revision 1648. diff --git a/sbin/rc-services.sh b/sbin/rc-services.sh index a592a7e..795a78c 100755 --- a/sbin/rc-services.sh +++ b/sbin/rc-services.sh @@ -709,11 +709,11 @@ valid_iafter() { # Get and sort the dependencies of given service[s]. # trace_dependencies() { - local -a services=( "$@" ) - local i j net_services + local -a services=( "$@" ) net_deps + local i j net_services x if [[ $1 == -* ]]; then - deptype="${1/-}" + deptype="${1/-/}" if net_service "${myservice}" ; then services=( "net" "${myservice}" ) else @@ -743,31 +743,37 @@ trace_dependencies() { [[ -n ${x} ]] && net_services="${x}" fi + # Cache the generic "net" depends + net_deps=( $( ineed net ) $( valid_iuse net ) ) + if is_runlevel_start || is_runlevel_stop ; then + net_deps=( "${net_deps[@]}" $( valid_iafter net ) ) + fi + # OK, this is a topological sort # The bonus about doing it in bash is that we can calculate our sort # order as we calculate our dependencies local -a visited sorted visit_service() { - local service="$1" dep + local service="$1" dep x local -a deps [[ " ${visited[@]} " == *" ${service} "* ]] && return visited=( "${visited[@]}" "${service}" ) if [[ -n ${deptype} ]] ; then - deps=( "${deps[@]}" $( "${deptype}" "${service}" ) ) + deps=( $( "${deptype}" "${service}" ) ) else - deps=( - $( ineed "${service}" ) - $( valid_iuse "${service}" ) - ) - + deps=( $( ineed "${service}" ) $( valid_iuse "${service}" ) ) if is_runlevel_start || is_runlevel_stop ; then deps=( "${deps[@]}" $( valid_iafter "${service}" ) ) fi - local x=" ${deps[@]} " - deps=( ${x// net / ${net_services} } ) + # If we're a net service, we have to get deps for ourself + # and the net service as we're both + net_service "${service}" && deps=( "${deps[@]}" "${net_deps[@]}" ) + + x=" ${deps[@]} " + deps=( "${deps[@]}" ${x// net / ${net_services} } ) fi services=( "${services[@]}" "${deps[@]}" ) 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 "	"> + <!ENTITY newl " "> + <!ENTITY space " "> +]> + +<!-- + 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) < (($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; +} |