pax_global_header00006660000000000000000000000064114052274130014511gustar00rootroot0000000000000052 comment=8e05e08866872ba32370a24a9810d029d20ab25a daemon-0.6.4/000077500000000000000000000000001140522741300127635ustar00rootroot00000000000000daemon-0.6.4/LICENSE000066400000000000000000000431311140522741300137720ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 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. Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. , 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. daemon-0.6.4/Makefile000066400000000000000000000134761140522741300144360ustar00rootroot00000000000000# # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # 20100612 raf CC := gcc # CC := cc AR := ar RANLIB := ranlib POD2MAN := pod2man POD2HTML := pod2html GZIP := gzip -f -9 DESTDIR := PREFIX := $(DESTDIR)/usr/local APP_INSDIR := $(PREFIX)/bin LIB_INSDIR := $(PREFIX)/lib MAN_SYSDIR := $(PREFIX)/share/man MAN_LOCDIR := $(PREFIX)/share/man ifeq ($(PREFIX),/usr) MAN_INSDIR := $(MAN_SYSDIR) else MAN_INSDIR := $(MAN_LOCDIR) endif HDR_INSDIR := $(PREFIX)/include DATA_INSDIR := $(PREFIX)/share CONF_INSDIR := /etc APP_MANSECT := 1 LIB_MANSECT := 3 FMT_MANSECT := 5 APP_MANDIR := $(MAN_INSDIR)/man$(APP_MANSECT) LIB_MANDIR := $(MAN_INSDIR)/man$(LIB_MANSECT) FMT_MANDIR := $(MAN_INSDIR)/man$(FMT_MANSECT) APP_MANSECTNAME := User Commands LIB_MANSECTNAME := C Library Functions - libslack FMT_MANSECTNAME := File Formats MAN_GZIP := 1 CCFLAGS += -O3 CCFLAGS += -Wall -pedantic # CCFLAGS += -xO4 CLEAN_FILES += tags core Makefile.bak .makefile.bak pod2htm* DAEMON_SRCDIR := . DAEMON_INCDIRS := libslack DAEMON_LIBDIRS := libslack include $(DAEMON_SRCDIR)/macros.mk .PHONY: all ready test check man html install uninstall dist rpm deb sol obsd fbsd nbsd osx all: ready $(ALL_TARGETS) ready: $(READY_TARGETS) check test: all $(TEST_TARGETS) man: $(MAN_TARGETS) html: $(HTML_TARGETS) install: all $(INSTALL_TARGETS) uninstall: $(UNINSTALL_TARGETS) dist: $(DIST_TARGETS) rpm: $(RPM_TARGETS) deb: $(DEB_TARGETS) sol: $(SOL_TARGETS) obsd: $(OBSD_TARGETS) fbsd: $(FBSD_TARGETS) nbsd: $(NBSD_TARGETS) osx: $(OSX_TARGETS) .PHONY: help help-macros depend clean clobber distclean help:: @echo "This makefile provides the following targets."; \ echo; \ echo " help -- shows this list of targets"; \ echo " help-macros -- shows the values of all make macros"; \ echo " all -- makes $(DAEMON_TARGET) and $(DAEMON_SUBTARGETS) (default)"; \ echo " ready -- prepares the source directory for make"; \ echo " test -- makes and runs library unit tests"; \ echo " check -- same as test"; \ echo " man -- generates all manpages"; \ echo " html -- generates all manpages in html"; \ echo " install -- installs everything under $(PREFIX)"; \ echo " uninstall -- uninstalls everything from $(PREFIX)"; \ echo " depend -- generates source dependencies using makedepend"; \ echo " tags -- generates a tags file using ctags"; \ echo " clean -- removes object files, tags, core and Makefile.bak"; \ echo " clobber -- clean + rm $(DAEMON_TARGET), $(DAEMON_SUBTARGETS) and tests"; \ echo " distclean -- clobber + rm source dependencies"; \ echo " dist -- makes the distribution: ../$(DAEMON_DIST)"; \ echo " rpm -- makes source and binary rpm packages"; \ echo " deb -- makes binary debian package"; \ echo " sol -- makes binary solaris package"; \ echo " obsd -- makes binary openbsd package"; \ echo " fbsd -- makes binary freebsd package"; \ echo " nbsd -- makes binary netbsd package"; \ echo " osx -- makes binary macosx package"; \ echo help-macros:: @echo "CC = $(CC)"; \ echo "PREFIX = $(PREFIX)"; \ echo "APP_INSDIR = $(APP_INSDIR)"; \ echo "LIB_INSDIR = $(LIB_INSDIR)"; \ echo "MAN_INSDIR = $(MAN_INSDIR)"; \ echo "HDR_INSDIR = $(HDR_INSDIR)"; \ echo "DATA_INSDIR = $(DATA_INSDIR)"; \ echo "CONF_INSDIR = $(CONF_INSDIR)"; \ echo "APP_MANSECT = $(APP_MANSECT)"; \ echo "LIB_MANSECT = $(LIB_MANSECT)"; \ echo "FMT_MANSECT = $(FMT_MANSECT)"; \ echo "APP_MANDIR = $(APP_MANDIR)"; \ echo "LIB_MANDIR = $(LIB_MANDIR)"; \ echo "FMT_MANDIR = $(FMT_MANDIR)"; \ echo "TAG_FILES = $(TAG_FILES)"; \ echo "DEPEND_CFILES = $(DEPEND_CFILES)"; \ echo "DEPEND_HFILES = $(DEPEND_HFILES)"; \ echo "CCFLAGS = $(CCFLAGS)"; \ echo "READY_TARGETS = $(READY_TARGETS)"; \ echo "ALL_TARGETS = $(ALL_TARGETS)"; \ echo "TEST_TARGETS = $(TEST_TARGETS)"; \ echo "MAN_TARGETS = $(MAN_TARGETS)"; \ echo "HTML_TARGETS = $(HTML_TARGETS)"; \ echo "INSTALL_TARGETS = $(INSTALL_TARGETS)"; \ echo "UNINSTALL_TARGETS = $(UNINSTALL_TARGETS)"; \ echo "CLEAN_FILES = $(CLEAN_FILES)"; \ echo "CLOBBER_FILES = $(CLOBBER_FILES)"; \ echo "DEBIAN_CLOBBER_FILES = $(DEBIAN_CLOBBER_FILES)"; \ echo "DIST_TARGETS = $(DIST_TARGETS)"; \ echo "RPM_TARGETS = $(RPM_TARGETS)"; \ echo "DEB_TARGETS = $(DEB_TARGETS)"; \ echo "SOL_TARGETS = $(SOL_TARGETS)"; \ echo "OBSD_TARGETS = $(OBSD_TARGETS)"; \ echo "FBSD_TARGETS = $(FBSD_TARGETS)"; \ echo "NBSD_TARGETS = $(NBSD_TARGETS)"; \ echo "OSX_TARGETS = $(OSX_TARGETS)"; \ echo tags: $(TAG_FILES) @ctags $(TAG_FILES) depend: ready $(DEPEND_CFILES) $(DEPEND_HFILES) @makedepend $(DAEMON_CPPFLAGS) $(DEPEND_CFILES) clean:: @rm -rf $(CLEAN_FILES) clobber:: @rm -rf $(CLEAN_FILES) $(CLOBBER_FILES) distclean:: clobber @perl -pi -e 'last if /[D]O NOT DELETE/;' $(patsubst %, %/Makefile, $(DAEMON_SRCDIR) $(DAEMON_SUBDIRS)) include $(DAEMON_SRCDIR)/rules.mk daemon-0.6.4/README000066400000000000000000001130341140522741300136450ustar00rootroot00000000000000README ~~~~~~ daemon - turns other processes into daemons DESCRIPTION ~~~~~~~~~~~ Daemon turns other processes into daemons. There are many tasks that need to be performed to correctly set up a daemon process. This can be tedious. Daemon performs these tasks for other processes. This is useful for writing daemons in languages other than C, C++ or Perl (e.g. /bin/sh, Java). If you want to write daemons in languages that can link against C functions (e.g. C, C++), see libslack which contains the core functionality of daemon. DETAILS ~~~~~~~ Daemon turns other processes into daemons. There are many tasks that need to be performed to correctly set up a daemon process. This can be tedious. Daemon performs these tasks for other processes. The preparatory tasks that daemon performs for other processes are: First revoke any setuid or setgid privileges that daemon may have been installed with (by system administrators who laugh in the face of danger). Process command line options. Change the root directory if the --chroot option was supplied. Change the process uid and gid if the --user option was supplied. Only root can use this option. Note that the uid of daemon itself is changed, rather than just changing the uid of the client process. Read the configuration file (/etc/daemon.conf by default, or specified by the --config option). Note: The root directory and the user must be set before access to the configuration file can be attempted so neither --chroot nor --user options may appear in the configuration file. Disable core file generation to prevent security holes in daemons run by root (unless the --core option is supplied). Become a daemon process: o If daemon was not invoked by init(8) or inetd(8): o Background the process to lose process group leadership. o Start a new process session. o Under SVR4, background the process again to lose process session leadership. This prevents the process from ever gaining a controlling terminal. This only happens when SVR4 is defined and NO_EXTRA_SVR4_FORK is not defined when libslack is compiled. Before doing this, ignore SIGHUP because when the session leader terminates, all processes in the foreground process group are sent a SIGHUP signal. Note that this code may not execute (e.g. when started by init(8) or inetd(8) or when either SVR4 was not defined or NO_EXTRA_SVR4_FORK was defined when libslack was compiled). This means that the client can't make any assumptions about the SIGHUP handler when daemon_init() returns. o Change directory to the root directory so as not to hamper umounts. o Clear the umask to enable explicit file creation modes. o Close all open file descriptors. If daemon was invoked by inetd(8) stdin, stdout and stderr are left open since they are open to a socket. o Open stdin, stdout and stderr to /dev/null in case something requires them to be open. Of course, this is not done if daemon was invoked by inetd(8). o If the --name option is supplied, create and lock a file containing the process id of the daemon process. The presence of this locked file prevents two instances of a daemon with the same name from running at the same time. The default location of the pidfile is /var/run for root or /tmp for ordinary users. If the --umask option was supplied, set the umask to its argument. Otherwise, set the umask to 022 to prevent accidentally creating group or world writable files. Set the current directory if the --chdir option was supplied. Daemon then spawns the client command specified on its command line and waits for it to terminate. If the --syslog, --outlog and/or --errlog option were supplied, the client's standard output and/or standard error are captured by daemon and sent to the respective syslog destinations. When the client terminates, daemon respawns it if the --respawn option is supplied and the client terminated successfully after at least 600 seconds. Otherwise daemon terminates. If daemon receives a SIGTERM signal, it propagates the signal to the client and then terminates. Note: For security reasons, never install daemon with setuid or setgid privileges. It is unnecessary. If you do, daemon will revert to the real user and group for safety before doing anything else. INSTALL ~~~~~~~ This version is only known to work on the following systems: Linux 2.6 (i386, x86_64, debian-5.0.4, ubuntu-10.04, fedora-13) Solaris 10 10/09 (i386, amd64) OpenSolaris 2009/06 (i386, amd64) OpenBSD 4.7 (i386, amd64) FreeBSD 8.0 (i386, amd64) NetBSD 5.0.2 (i386, amd64) MacOSX 10.{4,5,6} (i386, x86_64, ppc) kFreeBSD 20090729 (i386) For these systems, just run the "config" script in the source directory. It will run the appropriate script in the "conf" directory for the current host. Note: There isn't a real configure script so you will no doubt encounter problems on other systems. An ISO C and POSIX/XPG4 environment will help greatly. If your system doesn't have snprintf(3), GNU getopt_long(3), vsscanf(3), strcasecmp(3), strncasecmp(3), strlcpy(3) or strlcat(3), uncomment the relevant lines in the libslack/config.h file to include them in libslack. If your system doesn't have POSIX 1003.2 compliant regex functions, or they are buggy, either: install the GNU implementation, ftp://ftp.gnu.org/gnu/regex/regex-0.12.tar.gz [290K] (doesn't support internationalisation); or install Henry Spencer's implementation, ftp://ftp.zoo.toronto.edu/pub/regex.shar [157K]. If you really, really, really don't want the regular expression functions, uncomment HAVE_REGEX_H in libslack/config.h to enable the rest of the str module to be compiled. If you have a linux-2.2.x system, you must have LinuxThreads-0.8 or LinuxThreads-0.9 and the latest corresponding version of glibc. If you have a linux-2.4.x system, you must have at least glibc-2.2.1 and glibc-linuxthreads-2.2.1. They are available from http://ftp.gnu.org/pub/gnu/glibc/. Older versions can be used but they have some very nasty bugs. First, uninstall any previous version: cd /usr/local/src/daemon-0.6.3 make uninstall To build and test: tar xzf daemon-0.6.4.tar.gz cd daemon-0.6.4 ./config # iff linux, solaris, openbsd, freebsd, netbsd or macosx make # must be gnu make make test # only tests libslack. to test daemon, see test/README To install daemon and its manpage (in /usr/local by default): make install To install into somewhere other than /usr/local: make PREFIX=/opt/daemon install To install an empty /etc/daemon.conf file: make install-daemon-conf To uninstall daemon: make uninstall To install libslack and its manpages (into /usr/local by default): make install-slack To uninstall libslack: make uninstall-slack For more details: make help The manpage for daemon is daemon(1). There is one manpage for each module in libslack (as well as a symlink for each function). The module manpages are agent(3), coproc(3), daemon(3), err(3), fio(3), hsort(3), lim(3), link(3), list(3), locker(3), map(3), mem(3), msg(3), net(3), prog(3), prop(3), pseudo(3), sig(3) and str(3). If necessary, the manpages getopt(3), snprintf(3) and vsscanf(3) are created as well. BINARY PACKAGES ~~~~~~~~~~~~~~~ Some binary packages are available on daemon's website: To install from the Fedora RPM binary package (into /usr by default): rpm -i daemon-0.6.4-1.x86_64.rpm # or rpm -i daemon-0.6.4-1.i686.rpm To install from the OpenBSD binary package (into /usr/local by default): mv daemon-0.6.4-openbsd-amd64.tgz daemon-0.6.4.tgz # or mv daemon-0.6.4-openbsd-i386.tgz daemon-0.6.4.tgz # then pkg_add daemon-0.6.4.tar.gz To install from the FreeBSD binary package (into /usr/local by default): pkg_add daemon-0.6.4-freebsd-amd64.tbz # or pkg_add daemon-0.6.4-freebsd-i386.tbz To install from the NetBSD binary package (into /usr/local by default): pkg_add daemon-0.6.4-netbsd-amd64.tgz # or pkg_add daemon-0.6.4-netbsd-i386.tgz To install from the Mac OS X binary package (into /usr/local by default): cd /usr/local tar xzf /usr/local/src/daemon-0.6.4-macosx-universal.tar.gz # or tar xzf /usr/local/src/daemon-0.6.4-macosx-x86_64.tar.gz # or tar xzf /usr/local/src/daemon-0.6.4-macosx-i386.tar.gz # or tar xzf /usr/local/src/daemon-0.6.4-macosx-powerpc.tar.gz To install from the Solaris10 binary package (into /usr/local by default): gunzip daemon-0.6.4-solaris-amd64.pkg.gz pkgadd -d daemon-0.6.4-solaris-amd64.pkg or gunzip daemon-0.6.4-solaris-i386.pkg.gz pkgadd -d daemon-0.6.4-solaris-i386.pkg On debian/ubuntu systems you should be able to: apt-get install daemon REQUIREMENTS ~~~~~~~~~~~~ Requires GNU make to compile. Requires perl to run the scripts in the conf directory. Requires perl to install per-function manpage links. Requires an ISO C compiler like gcc to compile the source. Requires pod2man (comes with perl) to make the manpages. Requires pod2html (comes with perl) to make the html manpages. Requires POSIX 1003.2 compliant regex functions. See INSTALL. Requires libpthread. See INSTALL. PLATFORM NOTES ~~~~~~~~~~~~~~ These platform notes are quite old and probably mostly irrelevant. Linux ~~~~~ Linux 2.2 always returns 0.0.0.0 on getsockopt(IP_MULTICAST_IF) so net_multicast_get_interface() always returns 0 under Linux 2.2. Linux 2.4 does not have this bug. Make sure you have a recent glibc (at least 2.1.3) and libpthread (at least 0.8) (See INSTALL). Linux 2.2 and 2.4 have a bug-like feature in poll(2). It always times out 10ms later than specified. Libslack corrects for this as best as it can (if > 10ms -= 10ms) but it's not good enough when you need timers with a granularity of 10ms. In this case, you have to use agent_create_with_select() instead of agent_create() under Linux because select() doesn't have this bug. However, scalable I/O is impossible with select(). So, if you need timers with a granularity of 10ms *and* scalable I/O, you need an agent that uses select() in one thread for the timers and separate agents that use poll() in other threads for the I/O. Solaris ~~~~~~~ Solaris (at least 2.6 and 2.7) doesn't return the hardware address or index of network interfaces from ioctl() with a SIOCGIFINDEX command argument. Libslack fills in the index in net_interfaces(). UNIX domain datagram sockets aren't supported very nicely. An actual filesystem entry is needed for the client and it must be unlinked after use. It's also possible for a malicious local user to deny a client access to the server. The solution is to always use UNIX domain stream sockets. Solaris has an inadequate snprintf() function so libslack provides it's own implementation. When configured for Solaris, this snprintf() function will format exactly like the system's sprintf() function, even though it has incorrect behaviour with respect to the ISO C standard. I thought this was better than having thousands of module tests apparently "fail". OpenBSD ~~~~~~~ Has the same UNIX domain datagram socket problem as Solaris. FreeBSD ~~~~~~~ Has the same UNIX domain datagram socket problem as Solaris. Can't lock fifos so fifo_open() can't guarantee a unique reader. Mac OS X ~~~~~~~~ Probably has the same UNIX domain datagram problem as Solaris. COPYING ~~~~~~~ daemon - turns other processes into daemons Copyright (C) 1999-2010 raf 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit http://www.gnu.org/copyleft/gpl.html libslack - A UNIX/C library of general utilities for programmers with Slack Copyright (C) 1999-2010 raf This library 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 library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit http://www.gnu.org/copyleft/gpl.html $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ $OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $ Copyright (c) 1998 Todd C. Miller All rights reserved. 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 ``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. MAILING LISTS ~~~~~~~~~~~~~ If you'd like to be kept up to date with daemon releases or have questions or suggestions, you can join one or more of the following (extremely low volume) mailing lists (@libslack.org). daemon-announce (Announcements) daemon-users (User forum) daemon-dev (Development forum) To subscribe to any of these mailing lists, send a mail message to -request@libslack.org with "subscribe" as the message body. E.g. $ echo subscribe | mail daemon-announce-request@libslack.org $ echo subscribe | mail daemon-users-request@libslack.org $ echo subscribe | mail daemon-dev-request@libslack.org Or you can send a mail message to majordomo@libslack.org with subscribe listname in the message body. This way, you can include multiple subscribe commands to subscribe to multiple lists at the same time. E.g. $ mail majordomo@libslack.org subscribe daemon-announce subscribe daemon-users subscribe daemon-dev . A digest version of each mailing list is also available. Subscribe to digests as above but append -digest to the listname. E.g. $ echo subscribe | mail daemon-announce-digest-request@libslack.org $ echo subscribe | mail daemon-users-digest-request@libslack.org $ echo subscribe | mail daemon-dev-digest-request@libslack.org Or $ mail majordomo@libslack.org subscribe daemon-announce-digest subscribe daemon-users-digest subscribe daemon-dev-digest Note that these are all extremely low volume mailing lists so there's not much use for the digest lists. REFERENCES ~~~~~~~~~~ Advanced Programming in the UNIX Environment W. Richard Stevens Addison-Wesley Professional Computing Series, 1992 UNIX Network Programming W. Richard Stevens Prentice Hall Software Series, 1990 UNIX System V Network Programming Stephen A. Rago Addison-Wesley Professional Computing Series, 1993 TCP/IP Illustrated Volume 1, The Protocols W. Richard Stevens Addison-Wesley Professional Computing Series, 1994 UNIX Network Programming Volume 1, Networking APIs: Sockets and XTI W. Richard Stevens Prentice Hall Software Series, 1998 The Practice of Programming Brian W. Kernighan and Rob Pike, Addison-Wesley Professional Computing Series, 1999 Multithreaded Programming with Pthreads Bil Lewis and Daniel J. Berg Sun Microsystem Press/Prentice Hall, 1998 Effective TCP/IP Programming Jon C. Snader Addison-Wesley, 2000 Design Patterns - Elements of Reusable Object-Oriented Software Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides Addison-Wesley Professional Computing Series, 1995 The Standard C Library P. J. Plauger Prentice Hall, 1992 The Discipline and Method Architecture for Reusable Libraries Kiem-Phong Vo Software - Practice & Experience, v.30, pp.107-128, 2000 http://www.research.att.com/sw/tools/sfio/dm-spe.ps strlcpy and strlcat--Consistent, Safe, String Copy and Concatenation Todd C. Miller and Theo De Raadt Proceedings of the FREENIX Track: 1999 USENIX Annual Technical Conference http://www.usenix.org/events/usenix99/millert.html The Perl manpages Larry Wall http://www.perl.com/ Practical UNIX and Internet Security Simson Garfinkel and Gene Spafford O'Reilly, 1996 Interconnections - Bridges, Routers, Switches and Internetworking Protocols Radia Perlman Addison-Wesley Professional Computing Series, 2000 MT-Disciplined raf http://raf.org/papers/mt-disciplined.html, 2001 I/O Event Handling Under Linux Richard Gooch http://www.atnf.csiro.au/~rgooch/linux/docs/io-events.html, 1999 Scalable kernel performance for Internet servers under realistic loads Gaurav Banga and Jeffrey C. Mogul Proceeding of the Refereed Papers Track: 1998 USENIX Annual Technical Conference http://www.usenix.org/publications/library/proceedings/usenix98/banga.html TCP Buffering and Performance Over An ATM Network Purdue Technical Report CSD-TR 94-026 (Revision) Journal of Internetworking: Research and Experience, Vol. 6 (1), Pages 1-13 Douglas E. Comer and John C. Lin ftp://gwen.cs.purdue.edu/pub/lin/TCP.atm.ps.Z, 1994 Experimental and Simulation Performance Results of TCP/IP over High-Speed ATM over ACTS Charalambos, C.; Lazarou, G.; Frost, V.; Evans, J.; Jonkman, R. Information and Telecommunication Technology Center, Department of Electrical Engineering & Computer Science, The University of Kansas http://acts.lerc.nasa.gov/library/docs/gsn/charalambous.pdf Revelation X: The "Bob" Apocryphon Translated by The Subgenius Foundation, Edited by Reverend Ivan Stang, Simon & Schuster, 1994 HISTORY ~~~~~~~ 0.1 (19991020) - Initial version 0.2 (19991223) - Decoupled core file prevention from daemon_init() into its own function, daemon_prevent_core() - Decoupled signal handling from daemon_init() - Cached daemon_started_by_init() and daemon_started_by_inetd() results - Fixed bug when formatting --help message - Added some modules to libprog: conf, list, hsort, map, prop - Added timestamps to msg_out_file() - Included source to GNU getopt_long_only() (if necessary) - Added hdr.h to allow non-ANSI compilers to parse libprog's headers - Moved libprog to a subdirectory using a "Whole Project" Makefile - Converted "Whole Project" Makefile into "Scalable" Makefiles - Added verbosity functions to libprog(prog) - Added -core option to allow core file generation - Added -respawn option to allow client respawn if death not too sudden - Added -syslog option to redirect client stdout and stderr to syslog - Added -log option to specify where daemon stdout and stderr are sent - Fixed bug when constructing data for GNU getopt_long_only() - Fixed bugs in the options table for libprog(prog) - Changed help message format: separated option chunks by a blank line - Fixed bug when obtaining names associated with syslog constants - Added -Config option and /etc/daemon.conf handling - Added pathetic conf/linux and conf/solaris scripts - Revert to real uid/gid if not same as effective uid/gid for safety - Added manpages 0.3 (20000902) - Started using GNU getopt_long() instead of getopt_long_only() - Started ignoring /etc/daemon.conf if group or world writable - Added -DSVR4 in conf/solaris (doh!) - Added conditional compilation of debug functions - Added assert macro that calls dump() - Completed the daemon(1) manpage (common options weren't documented) - Fixed bug: SIG_IGN, SIG_DFL and nasty signals weren't treated specially - Made lists grow/shrink exponentially rather than linearly - Made maps grow as needed and use arbitrary hash functions and key types - Added multi-dimensional array allocator to the mem module - Added net module: clients/servers, expect/send, pack/unpack, mail - Added internal iterators and some more functions to list and map - Added examples sections to some libprog manpages - Added --user option - Added str module: decent strings + tr, regex, regsub, fmt, trim, lc, uc ... - Added vsscanf(3) implementation for systems that don't have it (e.g. solaris) - Renamed libprog to libslack (thanks, fred!) - Added socks.h - Added daemon_revoke_privileges(), daemon_file_is_safe() to daemon module - Moved revocation of setuid/setgid privileges to start for greater safety - Fixed bug: wasn't unlinking pidfile when client died of natural causes - Fixed bug: wasn't processing config file correctly - Included daemon(1) tests in distribution (not automatic, though) 0.4 (20010215) - Fixed memory usage bugs - Added daemon demo directory (previously, the incomplete "test" directory) - Changed net server/client functions to use service name else port number - Fixed security bug: daemon_file_is_safe() wasn't following symlinks (doh!) - Changed -d and -v options to take optional arguments (both default to 1) - Renamed daemon_file_is_safe() to daemon_path_is_safe() - Added daemon_absolute_path() to daemon module - Added memory pool functions to mem module - Fixed bug: optional option arguments were handled in dodgy C - Made supplied snprintf() POSIX compliant (was using getpagesize()) - Removed conf module (Added daemon_parse_config() to daemon module) - Removed net_chat(), rfc822_localtime(), rfc822_gmtime() (not useful enough) - Added secure memory/pool functions to mem module - Fixed Makefile bug: $(CCFLAGS) for daemon and libslack weren't separate - Added fgetline() to fifo module (reads a line with any line ending) - Added str_fgetline() to str module (like fgetline but handles any length) - Renamed fifo module to fio - Fixed Makefile bug: wasn't uninstalling everything properly - Changed net_send() to take a timeout argument like next_expect() does - Added strlcpy(), strlcat(), strcasecmp() and strncasecmp() to str module - Changed debug levels used by daemon(1) from 8 and 9 to 1 and 2 - Renamed conf/solaris to solaris-gcc and added solaris-cc for sun workshop - Fixed bug: -n and -u options together failed to unlink pidfiles - Fixed security bug: wasn't clearing supplementary groups with -u option - Added support for root and user pidfiles in separate directories - Added make rpm rpm-daemon rpm-slack (with Edward Avis ed at membled.com) - Added installation of manpages for each function (link to module manpage) - Added libslack(3) overview manpage - Added list_break(), map_break() - Renamed assert() to check() for obvious reasons - Renamed re funcs in str module: s/regex/regexpr/g - Added thread module and made everything MT-Safe or MT-Disciplined - Added relative index/range semantics to string functions - Added make deb deb-daemon deb-slack (debian binary packages) - Added make pkg pkg-daemon pkg-slack (solaris binary packages) - Added setsockopt(SO_REUSEADDR) for net servers - Changed net server/client API to allow optional setsockopt(RCVBUF/SNDBUF) - Added REFERENCES section to README file - Stopped net_expect/net_send from interfering with SIGALRM/alarm/setitimer - Added read_timeout(), write_timeout() and rw_timeout() to fio module - Changed net_read() and net_write() to take a timeout argument - Added '?' field size specifier to unpack() (for packet length fields) - Added libslack-config administration utility - Renamed sockaddr typedef to sockaddr_t - Changed net_client to take a timeout parameter - Fixed fd leak in error handling code in net server/client functions - Fixed bug: str_regsub() didn't handle empty string matches at all - Changed str_regexpr_split() to take cflags and eflags arguments - Changed all *_destroy macros to functions that take address of pointer - Changed mem_resize macro so client must explicitly provide address of mem - Changed all *_destroy_t typedefs to *_release_t - Added make [un]install-{daemon|slack}-html (not part of make [un]install) - Temporarily removed snprintf module (not MT-Safe, not essential) - Merged lognames module into msg module - Merged opt module into prog module - Renamed _* functions in err module to *f (not ANSI compliant) - Removed MANIFEST 0.5 (20011109) - Fixed Makefile bug: decoupled libslack optlevel from module test optlevel - Fixed API bug: str_length_unlocked() was static - Fixed API bug: prop_locker() prototype wasn't in prop.h - Added prop_clear() to prop module - Added octal/hex and error handling to command line integer option handling - Fixed bug: check() just called dump() without testing the condition first - Added variants of six standard C string functions with more useful APIs - Added thread module tests (and locker timing tests) - Changed locker function return values (now same as pthread return values) - Improved speed of lockers (now overhead = 1 test + 1 deref + 1 offset) - Changed item/length/size, bin/hex/oct functions to return -1 on error - Changed error returns to consistently set errno (str, list, map) - Added set_errnull() to err module - Added relative index/range semantics to list functions (same as with str) - Changed fifo_open() to take writefd return arg to prevent fd leaks - Simplified mem_resize() assuming ISO C compliant realloc() - Added internal ISO C compliant realloc() for systems that don't have it - Fixed bug: optval/optlen for getsockopt(SO_ERROR) not initialised - Fixed inode leak: fifo_open() didn't unlink fifo created on error - Removed thread_init(), thread_setcancel() and barriers from thread module - Renamed thread module to locker (decoupled thread safety) - Stopped daemon_revoke_privileges() from clearing supplementary group list - Added initgroups(3) when handling --user=user (with no specific ".group") - Added --chroot, --chdir and --umask options - Changed default umask to 022 for safety - Added support for "debug sections" to debug messaging - Added daemon_become_user() to daemon module - Added "_unlocked" versions of functions in str, list and map modules - Renamed "_locked" functions to "_with_locker" to avoid confusion - Fixed bug: removed highly dubious synchronisation from internal iterators - Added read locked iterators - Added alert functions to prog, err and msg modules - Systematically corrected function prototype typos in manpages - Added net_options() to net module (sets multiple socket options) - Added soundex() to str module - Removed caching and mutex locks in daemon_started_by_init/inetd() - Added ignoring SIGHUP before losing session leadership in daemon_init() - Improved IPv6 support in net module (i.e. can now bind to IPv6 wildcard) - Added support for UNIX domain sockets to net module - Increased socket listen queue length from 5 to 1024 - Fixed bug: net service port selection only used numeric port arg - Added handling of name lookup returning multiple addresses for net clients - Added default host to net client functions (i.e. null host == loopback) - Changed net_create_server() and net_create_client() to take sockopt list - Added support for net clients binding to a specific local port - Added net_gethostbyname() and net_getservbyname() to net module - Made net server and client functions threadsafe - Added socket option notes to net module manpage (from W. Richard Stevens) - Added protocol design notes to net module manpage (from Radia Perlman) - Added sendfd() and recvfd() to net module (send/recv open file descriptor) - Replaced ioctl() with fcntl() in nonblock functions - Added --outlog and --errlog options - Fixed/simplified sigchld handling (was overcomplicated and flaky) - Added support for net client/server service argument being numeric - Added link module (singly/doubly linked lists and "growable" freelists) - Added agent module (poll/select plus hierarchical timing wheel scheduler) - Added net_interfaces() to net module (retrieve list of network interfaces) - Added support for IPv4 and IPv6 multicasting to net module - Added Reliability over UDP functions to net module (net_rudp_transact()) - Added Type of Service (TOS) functions to net module - Changed net_pack/net_unpack functions to take a timeout arg - Added nap() function to fio module (subsecond sleep) - Added threadsafe snprintf() function for systems that don't have it - Added asprintf() to str module for systems that don't have it - Added config.h to simplify compile commands (prelude to autoconf) - Removed thread_attr_{init,set}() (not useful enough or portable enough) - Ported to OpenBSD 2.9 - Added pseudo module (pseudo terminals by Tatu Ylonen from openssh) - Added coproc module (coprocesses with or without a pseudo terminal) - Changed syslog functions to take a priority parameter - Added --force option to daemon (respawn even when client crashes) - Added make obsd obsd-daemon obsd-slack (openbsd binary packages) - Added libslack-config --uninstall - Proofread all of the doco (just once) - Added setting appropriate TOS values in mail() - Fixed validation of relative index/range values in str/list modules - Added relative index semantics to list_item() - Added read locked versions of map_apply() and list_apply() - Added make dist-html-daemon and dist-html-slack (manpages as html.tar.gz) 0.6 (20020916) - Added HEADER FILES section to libslack(3) manpage - Added #include to example sections of module manpages - Added conf/freebsd (mmoraes at teleias.com) - Added tools/prefix utility (add prefix to all libslack identifiers) - Added manpage for tools/analyse-debug-locker utility - Separated stdout and stderr in coproc_open() and coproc_close() - Changed coproc open functions to take action() and data arguments - Started using libslack's coproc functions to run client - Added chdir to chroot directory (mmoraes at teleias.com) - Added --env option (mmoraes at teleias.com) - Changed restart behaviour to be like init(8) - Added --acceptable option - Added --attempts option (mmoraes at teleias.com) - Added --delay option - Added --limit option - Removed --force option (no longer meaningful) - Added --foreground option (suggested by mh+daemon at zugschlus.de) - Added --pty option - Exposed daemon_pidfile() - Added daemon_is_running() - Added daemon_stop() - Added --running option - Added --stop option - Added --unsafe option - Added --safe option - Fixed bug: config file parsing could produce garbage options - Replaced demo directory with test directory - Added support for sending client stdout/stderr directly to a file - Renamed --syslog to --output (-s to -o) - Renamed --outlog to --stdout (-o to -O) - Renamed --errlog to --stderr (-e to -E) - Renamed --log to --errlog - Added --dbglog option - Renamed sighandler_t to signal_handler_t (glibc clash - gdr at gno.org) - Ported to Solaris 8 - Split Solaris configuration scripts into conf/solaris[68]-[g]cc - Added support for installing gzipped manpages - Added --inherit option - Added make daemon.conf (just an empty default) - Stopped installing libslack as well by default - Added daemon.conf(5) manpage (just a symlink to daemon(1)) - Added make [un]install-daemon-conf (not by default) - Added Debian source package generation - Stopped generating debian package (until it's a shared library) - Added make fbsd fbsd-daemon fbsd-slack (freebsd binary packages) - Added --command option (suggested by mh+daemon at zugschlus.de) - Added support for absolute path in daemon_pidfile() - Added --pidfiles, --pidfile options (suggested by mh+daemon at zugschlus.de) - Added daemon_getpid() - Added --restart option (suggested by mh+daemon at zugschlus.de) - Added support for ~/.daemonrc - Added --noconfig option - Added check for dubious characters in daemon names - Added libslack-config --upgrade option - Added lame ./config script that just calls existing lame conf/* scripts 0.6.1 (20030901) - Fixed bug: --noconfig option broken (spotted by paolo at telmap.com) - Fixed bug: --command option broken (spotted by paolo at telmap.com) - Fixed bug: exit too quickly on sigterm (spotted by stodden at cs.tum.edu) - Set prog name to --name argument, if any (for syslog message prefix) 0.6.2 (20040102) - Fixed bug: --stop during spawn delay wasn't acted upon - Fixed API bug: didn't include - Fixed bug: -lm wasn't in `libslack-config --libs` on Solaris - Changed --restart: reset spawn burst counters (for stodden at peppermind.de) - Changed daemon_path_is_safe() to give an explanation when unsafe - Added explanations when rejecting unsafe config files and executables - Fixed bug: reset agent state to IDLE when interrupted (bte at kamash.com) - Trim unquoted leading spaces from property values (with bte at kamash.com) - Also trim only unquoted trailing spaces from property names - Added tools/migrate-properties utility (preserves old propfile behaviour) - Fixed bug: ownership partially lost in map_resize (with bte at kamash.com) - Fixed bug: mem_resize was broken since libslack-0.4 (bte at kamash.com) - Fixed DOC bug: stated the importance of including first - Cleaned up error messages (now that prog name is set to --name argument) - Added sections to libslack(3) features list (coproc+pty, low level api) - Fixed bug: if select() failed (unlikely), coproc wasn't closed properly - Fixed bug: when tty_raw() failed (unlikely), it returned the wrong value - Added many examples to the manpages (there are now 91 runnable examples) - Added tools/check-examples to verify that all examples work - Fixed bug: str_fgetline() returned empty string rather than null on eof - Fixed bug: cstrstr() didn't always work due to a typo - Added sections to libslack(3) features list (documentation, testing) - Added intel solaris8 binary package - Ported to Mac OS X (Darwin) 10.3.2 - Added make osx osx-daemon osx-slack (macosx binary packages) - Added pid to --running output (requested by rubinson at email.arizona.edu) - Dropped support for K&R clients (suggested by skaller at ozemail.com.au) - Added hsort_closure(3) (suggested by skaller at maxtal.com.au) - Added make slack.swig (generate a SWIG input file for libslack) 0.6.3 (20040806) - Fixed bug: --acceptable was broken (chris at edesix.com) 0.6.4 (20100612) - Added more debug statements - Fixed typographical errors in documentation - Fixed bug: need double-fork on Linux (spotted by Joey Hess joeyh at debian.org) - Updated makefiles to work with recent versions of GNU make - Updated to avoid new warnings in recent versions of gcc - Fixed pidfile race condition (with Hilko Bengen bengen at hilluzination.de) - Fixed bug in message for --verbose --running when --pidfile[s] also used - Don't strip if $DEB_BUILD_OPTIONS contains nostrip (for Julien Danjou acid at debian.org) - Made --running work when readonly (spotted by Thomas Koch thomas at koch.ro) - Stopped modifying --user argument so ps output looks right (for Hilko Bengen) - Made --user use ":" (but still accept ".") (for Lars Wirzenius lars at catalyst.net.nz) - Ported to GNU/kFreeBSD (with Cyril Brulebois cyril.brulebois at enst-bretagne.fr) - Added --idiot option (for Andras Korn korn-debbugs at chardonnay.math.bme.hu) - Added example/daemon.initd script (inspired by Javier Fernández-Sanguino Peña jfs at computer.org) - Ported to NetBSD - Added make nbsd nbsd-daemon nbsd-slack (netbsd binary packages) - Ported to Solaris10 and OpenSolaris - Tested on ubuntu-10.04, debian-5.0.4, fedora-13 (x86_64, i386) - Tested on solaris-10-200910, opensolaris-200906 (amd64, i386) - Tested on openbsd-4.7, freebsd-8.0, netbsd-5.0.2 (amd64, i386) - Tested on macosx-10.{4,5,6} (x86_64, i386, ppc) - Tested on kfreebsd-20090729 (i386) - Updated make rpm sol obsd fbsd osx to continue working ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ URL: http://libslack.org/daemon/ Date: 20100612 Author: raf daemon-0.6.4/conf/000077500000000000000000000000001140522741300137105ustar00rootroot00000000000000daemon-0.6.4/conf/freebsd000077500000000000000000000153611140522741300152560ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify Makefile, macros.mk and config.h to compile on FreeBSD # # 20100612 raf perl -pi \ -e 's/^(\S+ \+= xnet)$/# $1/;' \ -e 's/^(\S+ \+= socket)$/# $1/;' \ -e 's/^(\S+ \+= nsl)$/# $1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^(SNPRINTF := snprintf)$/# $1/;' \ -e 's/^(VSSCANF := vsscanf)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_VSSCANF=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-overlength-strings)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^(\w*LIBS \+= pthread)$/# $1/;' \ -e 's/^# (\w*LIBS \+= util)$/$1/;' \ -e 's/^# (\w*LDFLAGS \+= -pthread)$/$1/;' \ -e 's/^MAN_GZIP := 0$/MAN_GZIP := 1/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/\* #undef $1 \*\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_VSSCANF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRNCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCPY) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCAT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_ASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_MTU) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SOCKADDR_SA_LEN) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^#define (MLOCK_REQUIRES_PAGE_BOUNDARY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_ALTERNATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_SIGNED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NIL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_STR_FMT_NULL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NOALT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SVR4) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_OPENPTY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_LIBUTIL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/kfreebsd000077500000000000000000000153721140522741300154330ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify Makefile, macros.mk and config.h to compile on Linux # # 20100612 raf perl -pi \ -e 's/^(\S+ \+= xnet)$/# $1/;' \ -e 's/^(\S+ \+= socket)$/# $1/;' \ -e 's/^(\S+ \+= nsl)$/# $1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^(GETOPT := getopt)$/# $1/;' \ -e 's/^(SNPRINTF := snprintf)$/# $1/;' \ -e 's/^(VSSCANF := vsscanf)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_GETOPT_LONG=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_VSSCANF=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-overlength-strings)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^# (\w*LIBS \+= util)$/$1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 0$/MAN_GZIP := 1/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_GETOPT_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSSCANF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRNCASECMP) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_ISOC_REALLOC) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LINUX_POLL_BUG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_6) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_6) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_MTU) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^#define (MLOCK_REQUIRES_PAGE_BOUNDARY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_ALTERNATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_SIGNED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NIL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_STR_FMT_NULL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NOALT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_UNIX_DOMAIN_WILDCARD) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_OPENPTY) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTY_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_TTYNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/linux000077500000000000000000000153761140522741300150110ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify Makefile, macros.mk and config.h to compile on Linux # # 20100612 raf perl -pi \ -e 's/^(\S+ \+= xnet)$/# $1/;' \ -e 's/^(\S+ \+= socket)$/# $1/;' \ -e 's/^(\S+ \+= nsl)$/# $1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^(GETOPT := getopt)$/# $1/;' \ -e 's/^(SNPRINTF := snprintf)$/# $1/;' \ -e 's/^(VSSCANF := vsscanf)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_GETOPT_LONG=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_VSSCANF=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-overlength-strings)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^# (\w*LIBS \+= util)$/$1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 0$/MAN_GZIP := 1/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_GETOPT_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSSCANF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRNCASECMP) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_ISOC_REALLOC) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LINUX_POLL_BUG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_6) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_6) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_IFINDEX) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_MTU) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^#define (MLOCK_REQUIRES_PAGE_BOUNDARY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_ALTERNATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_SIGNED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NIL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_STR_FMT_NULL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NOALT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_UNIX_DOMAIN_WILDCARD) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_OPENPTY) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTY_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VHANGUP) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_TTYNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/macosx000077500000000000000000000153761140522741300151440ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify Makefile, macros.mk and config.h to compile on FreeBSD # # 20100612 raf perl -pi \ -e 's/^(\S+ \+= xnet)$/# $1/;' \ -e 's/^(\S+ \+= socket)$/# $1/;' \ -e 's/^(\S+ \+= nsl)$/# $1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^(SNPRINTF := snprintf)$/# $1/;' \ -e 's/^(VSSCANF := vsscanf)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_VSSCANF=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^# (\S+ \+= -DNO_POSIX_C_SOURCE=1)$/$1/;' \ -e 's/^# (\S+ \+= -DNO_POSIX_SOURCE=1)$/$1/;' \ -e 's/^# (\S+ \+= -DNO_XOPEN_SOURCE=1)$/$1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -O3)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^(\w*LIBS \+= util)$/# $1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 0$/MAN_GZIP := 1/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^\/\* #undef (NO_POSIX_SOURCE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (NO_XOPEN_SOURCE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STDINT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_VSSCANF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRNCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCPY) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCAT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_ASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_IFINDEX) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_MTU) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SOCKADDR_SA_LEN) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^#define (MLOCK_REQUIRES_PAGE_BOUNDARY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_ALTERNATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_SIGNED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NIL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_STR_FMT_NULL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NOALT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SVR4) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_OPENPTY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_UTIL_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) \*\/$/#define $1 1/;' \ `find . -name config.h` daemon-0.6.4/conf/netbsd000077500000000000000000000153621140522741300151240ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify Makefile, macros.mk and config.h to compile on NetBSD # # 20100612 raf perl -pi \ -e 's/^(\S+ \+= xnet)$/# $1/;' \ -e 's/^(\S+ \+= socket)$/# $1/;' \ -e 's/^(\S+ \+= nsl)$/# $1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^(SNPRINTF := snprintf)$/# $1/;' \ -e 's/^(VSSCANF := vsscanf)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_VSSCANF=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DNO_POSIX_SOURCE=1)$/$1/;' \ -e 's/^# (\S+ \+= -DNO_XOPEN_SOURCE=1)$/$1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^(\w*LIBS \+= pthread)$/# $1/;' \ -e 's/^# (\w*LIBS \+= util)$/$1/;' \ -e 's/^# (\w*LDFLAGS \+= -pthread)$/$1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^\/\* #undef (NO_POSIX_SOURCE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (NO_XOPEN_SOURCE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_VSSCANF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRNCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCPY) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCAT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_ASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_IFINDEX) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_MTU) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SOCKADDR_SA_LEN) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^#define (MLOCK_REQUIRES_PAGE_BOUNDARY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_ALTERNATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_SIGNED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NIL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_STR_FMT_NULL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NOALT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SVR4) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_OPENPTY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_UTIL_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/openbsd000077500000000000000000000153671140522741300153040ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify Makefile, macros.mk and config.h to compile on OpenBSD # # 20100612 raf perl -pi \ -e 's/^(\S+ \+= xnet)$/# $1/;' \ -e 's/^(\S+ \+= socket)$/# $1/;' \ -e 's/^(\S+ \+= nsl)$/# $1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^(SNPRINTF := snprintf)$/# $1/;' \ -e 's/^(VSSCANF := vsscanf)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_VSSCANF=1)$/$1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DNO_POSIX_SOURCE=1)$/$1/;' \ -e 's/^# (\S+ \+= -DNO_XOPEN_SOURCE=1)$/$1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^(\w*LIBS \+= pthread)$/# $1/;' \ -e 's/^# (\w*LIBS \+= util)$/$1/;' \ -e 's/^# (\w*LDFLAGS \+= -pthread)$/$1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^\/\* #undef (NO_POSIX_SOURCE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (NO_XOPEN_SOURCE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VSNPRINTF) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_VSSCANF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRNCASECMP) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCPY) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_STRLCAT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_ASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_VASPRINTF) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_5) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_IFINDEX) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IFREQ_IFR_MTU) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SOCKADDR_SA_LEN) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^#define (MLOCK_REQUIRES_PAGE_BOUNDARY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_ALTERNATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_SIGNED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NIL) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_STR_FMT_NULL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NOALT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SVR4) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_OPENPTY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_UTIL_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_TTYNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME_R) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/solaris10-cc000077500000000000000000000160401140522741300160370ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify the macros.mk make include files to compile on Solaris 10 with cc # # 20100612 raf perl -pi \ -e 's/^# (\S+ \+= xnet)$/$1/;' \ -e 's/^# (\S+ \+= socket)$/$1/;' \ -e 's/^# (\S+ \+= nsl)$/$1/;' \ -e 's/^# (\S+ \+= m)$/$1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^# (SNPRINTF := snprintf)$/$1/;' \ -e 's/^# (VSSCANF := vsscanf)$/$1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_VSSCANF=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^# (CC := cc)$/$1/;' \ -e 's/^(CC := gcc)$/# $1/;' \ -e 's/^(\S+ \+= -Wno-long-long)$/# $1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -O3)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -Wall -pedantic)$/# $1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -xO4)$/$1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^(\w*LIBS \+= util)$/# $1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` # Solaris10 has /usr/ucb/install but OpenSolaris 200906 doesn't # so we use own version rather than trying to get their native # (weird) install program to work the way the normal one does. if [ ! -f /usr/ucb/install ] then perl -pi \ -e 's/install -m/.\/myinstall -m/' \ `find . -name rules.mk` else perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` fi perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^#define (HAVE_POLL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSSCANF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRNCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_MTU) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (MLOCK_REQUIRES_PAGE_BOUNDARY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_ALTERNATE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_SIGNED) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NIL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_STR_FMT_NULL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NOALT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_OPENPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTSNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/solaris10-gcc000077500000000000000000000166611140522741300162170ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify the macros.mk make include files to compile on Solaris 10 with gcc # # 20100612 raf perl -pi \ -e 's/^# (\S+ \+= xnet)$/$1/;' \ -e 's/^# (\S+ \+= socket)$/$1/;' \ -e 's/^# (\S+ \+= nsl)$/$1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^# (SNPRINTF := snprintf)$/$1/;' \ -e 's/^# (VSSCANF := vsscanf)$/$1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_VSSCANF=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DROOT_PID_DIR)=.*$/# $1=\\"\/var\/run\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^(\w*LIBS \+= util)$/# $1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` # Solaris10 has /usr/ucb/install but OpenSolaris 200906 doesn't # so we use own version rather than trying to get their native # (weird) install program to work the way the normal one does. if [ ! -f /usr/ucb/install ] then perl -pi \ -e 's/install -m/.\/myinstall -m/' \ `find . -name rules.mk` else perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` fi # On 64-bit Solaris/OpenSolaris gcc generates 32-bit binaries by # default so we have to ask explicitly for 64-bit. if [ "`isainfo -k`" = "amd64" ] then perl -pi \ -e 's/^# (\w*(?:CC|LD)FLAGS \+= -m64)$/$1/;' \ `find . -name Makefile` \ `find . -name macros.mk` else perl -pi \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ `find . -name Makefile` \ `find . -name macros.mk` #fi perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSSCANF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRNCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_MTU) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_IF_INDEXTONAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_IF_NAMETOINDEX) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (MLOCK_REQUIRES_PAGE_BOUNDARY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_ALTERNATE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_SIGNED) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NIL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_STR_FMT_NULL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NOALT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_OPENPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTSNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) \*\/$/#define $1 1/;' \ `find . -name config.h` daemon-0.6.4/conf/solaris6-cc000077500000000000000000000153241140522741300157700ustar00rootroot00000000000000#!/bin/sh # # libslack - http://libslack.org/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify the macros.mk make include files to compile on Solaris 6 with cc # # 20100612 raf perl -pi \ -e 's/^# (\S+ \+= xnet)$/$1/;' \ -e 's/^# (\S+ \+= socket)$/$1/;' \ -e 's/^# (\S+ \+= nsl)$/$1/;' \ -e 's/^# (\S+ \+= m)$/$1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^# (SNPRINTF := snprintf)$/$1/;' \ -e 's/^# (VSSCANF := vsscanf)$/$1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_VSSCANF=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DROOT_PID_DIR)=.*$/$1=\\"\/etc\\"/;' \ -e 's/^# (CC := cc)$/$1/;' \ -e 's/^(CC := gcc)$/# $1/;' \ -e 's/^(\S+ \+= -Wno-long-long)$/# $1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -O3)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -Wall -pedantic)$/# $1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -xO4)$/$1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^(\w*LIBS \+= util)$/# $1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^#define (HAVE_POLL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSSCANF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRNCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PTHREAD_RWLOCK) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_MSGHDR_MSG_CONTROL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_MTU) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_INDEXTONAME) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_NAMETOINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (MLOCK_REQUIRES_PAGE_BOUNDARY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_ALTERNATE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_SIGNED) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NIL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_STR_FMT_NULL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NOALT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_OPENPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTSNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/solaris6-gcc000077500000000000000000000153341140522741300161400ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify the macros.mk make include files to compile on Solaris 6 with gcc # # 20100612 raf perl -pi \ -e 's/^# (\S+ \+= xnet)$/$1/;' \ -e 's/^# (\S+ \+= socket)$/$1/;' \ -e 's/^# (\S+ \+= nsl)$/$1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^# (SNPRINTF := snprintf)$/$1/;' \ -e 's/^# (VSSCANF := vsscanf)$/$1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_VSSCANF=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DROOT_PID_DIR)=.*$/$1=\\"\/etc\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^(\w*LIBS \+= util)$/# $1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSSCANF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRNCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PTHREAD_RWLOCK) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_MSGHDR_MSG_CONTROL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_MTU) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_INDEXTONAME) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_NAMETOINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (MLOCK_REQUIRES_PAGE_BOUNDARY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_ALTERNATE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_SIGNED) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NIL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_STR_FMT_NULL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NOALT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_OPENPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTSNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/solaris8-cc000077500000000000000000000153371140522741300157760ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify the macros.mk make include files to compile on Solaris 8 with cc # # 20100612 raf perl -pi \ -e 's/^# (\S+ \+= xnet)$/$1/;' \ -e 's/^# (\S+ \+= socket)$/$1/;' \ -e 's/^# (\S+ \+= nsl)$/$1/;' \ -e 's/^# (\S+ \+= m)$/$1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^# (SNPRINTF := snprintf)$/$1/;' \ -e 's/^# (VSSCANF := vsscanf)$/$1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_VSSCANF=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DROOT_PID_DIR)=.*$/$1=\\"\/etc\\"/;' \ -e 's/^# (CC := cc)$/$1/;' \ -e 's/^(CC := gcc)$/# $1/;' \ -e 's/^(\S+ \+= -Wno-long-long)$/# $1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -O3)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -Wall -pedantic)$/# $1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -xO4)$/$1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^(\w*LIBS \+= util)$/# $1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^#define (HAVE_POLL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSSCANF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRNCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_MTU) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_INDEXTONAME) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_NAMETOINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (MLOCK_REQUIRES_PAGE_BOUNDARY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_ALTERNATE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_SIGNED) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NIL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_STR_FMT_NULL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NOALT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_OPENPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTSNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/conf/solaris8-gcc000077500000000000000000000153441140522741300161430ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify the macros.mk make include files to compile on Solaris 8 with gcc # # 20100612 raf perl -pi \ -e 's/^# (\S+ \+= xnet)$/$1/;' \ -e 's/^# (\S+ \+= socket)$/$1/;' \ -e 's/^# (\S+ \+= nsl)$/$1/;' \ -e 's/^(\S+ \+= m)$/# $1/;' \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^# (SNPRINTF := snprintf)$/$1/;' \ -e 's/^# (VSSCANF := vsscanf)$/$1/;' \ -e 's/^(\S+ \+= -DHAVE_GETOPT_LONG=1)$/# $1/;' \ -e 's/^(\S+ \+= -DHAVE_VSSCANF=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DHAVE_PTHREAD_RWLOCK=1)$/$1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_C_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_POSIX_SOURCE=1)$/# $1/;' \ -e 's/^(\S+ \+= -DNO_XOPEN_SOURCE=1)$/# $1/;' \ -e 's/^# (\S+ \+= -DROOT_PID_DIR)=.*$/$1=\\"\/etc\\"/;' \ -e 's/^(CC := cc)$/# $1/;' \ -e 's/^# (CC := gcc)$/$1/;' \ -e 's/^# (\S+ \+= -Wno-long-long)$/$1/;' \ -e 's/^(\S+ \+= -Wno-overlength-strings)$/# $1/;' \ -e 's/^# (\w*CCFLAGS \+= -O3)$/$1/;' \ -e 's/^# (\w*CCFLAGS \+= -Wall -pedantic)$/$1/;' \ -e 's/^(\w*(?:CC|LD)FLAGS \+= -m64)$/# $1/;' \ -e 's/^(\w*CCFLAGS \+= -xO4)$/# $1/;' \ -e 's/^# (\w*LIBS \+= pthread)$/$1/;' \ -e 's/^(\w*LIBS \+= util)$/# $1/;' \ -e 's/^(\w*LDFLAGS \+= -pthread)$/# $1/;' \ -e 's/^MAN_GZIP := 1$/MAN_GZIP := 0/;' \ -e 's/^(MAN_LOCDIR\s*:=).*$/$1 \$(PREFIX)\/share\/man/;' \ `find . -name Makefile` \ `find . -name macros.mk` perl -pi \ -e 's/\.\/myinstall -m/install -m/' \ `find . -name rules.mk` perl -pi \ -e 's/^#define (NO_POSIX_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (NO_XOPEN_SOURCE) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_STDARG_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_STDINT_H) 1$/\/\* #undef $1 \*\//;' \ -e 's/^\/\* #undef (HAVE_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_POLL_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_SYS_SELECT_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_REGEX_H) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_DOUBLE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_LONG_LONG) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_SNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSSCANF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRNCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FLOCKFILE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_FCNTL_THAT_CAN_LOCK_FIFOS) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_ISOC_REALLOC) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_POLL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_LINUX_POLL_BUG) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_MLOCK) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETHOSTBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETHOSTBYNAME_R_3) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_6) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_FUNC_GETSERVBYNAME_R_5) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_FUNC_GETSERVBYNAME_R_4) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_PRIVATE) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_PROCESS_SHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_INIT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_CONDATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_MUTEXATTR_SETPSHARED) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PTHREAD_RWLOCK) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_MSGHDR_MSG_CONTROL) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_IFREQ_IFR_IFINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IFREQ_IFR_MTU) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_SOCKADDR_SA_LEN) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_INDEXTONAME) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_IF_NAMETOINDEX) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (MLOCK_REQUIRES_PAGE_BOUNDARY) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_ALTERNATE) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_SIGNED) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_PTR_FMT_NIL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_STR_FMT_NULL) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PRINTF_FLT_FMT_G_STD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PRINTF_PTR_FMT_NOALT) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS_NEGATIVE_WIDTH_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_PRINTF_WITH_SOLARIS8_ZERO_PRECISION_ALT_OCTAL_BEHAVIOUR) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_UNIX_DOMAIN_WILDCARD) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (SVR4) \*\/$/#define $1 1/;' \ -e 's/^#define (SOCKS) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_CYGWIN) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_DEV_PTMX) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_DEV_PTS_AND_PTC) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_OPENPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTY_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_UTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_LIBUTIL_H) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_SYS_STROPTS_H) \*\/$/#define $1 1/;' \ -e 's/^#define (HAVE_VHANGUP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE__GETPTY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_TTYNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_PTSNAME_R) 1$/\/* #undef $1 *\//;' \ -e 's/^\/\* #undef (HAVE_PTSNAME) \*\/$/#define $1 1/;' \ -e 's/^\/\* #undef (HAVE_POLL_THAT_ABORTS_WHEN_POLLFDS_IS_NULL) \*\/$/#define $1 1/;' \ `find . -name config.h` daemon-0.6.4/conf/test000077500000000000000000000033041140522741300146150ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # Modify the macros.mk make include files to exercise optional code # # 20100612 raf perl -pi \ -e 's/^# (GETOPT := getopt)$/$1/;' \ -e 's/^# (VSSCANF := vsscanf)$/$1/;' \ -e 's/^# (SNPRINTF := snprintf)$/$1/;' \ `find . -name macros.mk` perl -pi \ -e 's/^#define (HAVE_GETOPT_LONG) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSSCANF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRNCASECMP) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCPY) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_STRLCAT) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_SNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VSNPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_ASPRINTF) 1$/\/* #undef $1 *\//;' \ -e 's/^#define (HAVE_VASPRINTF) 1$/\/* #undef $1 *\//;' \ `find . -name config.h` daemon-0.6.4/config000077500000000000000000000035721140522741300141650ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # 20100612 raf die() { echo "$0: $@" >&2; exit 1; } case "`uname -a`" in Linux*) echo "Configuring for linux" conf/linux ;; GNU/kFreeBSD*) echo "Configuring for kfreebsd" conf/kfreebsd ;; FreeBSD*) echo "Configuring for freebsd" conf/freebsd ;; NetBSD*) echo "Configuring for netbsd" conf/netbsd ;; OpenBSD*) echo "Configuring for openbsd" conf/openbsd ;; SunOS*) cc="" solaris="solaris10" [ "x`uname -r`" = "x5.8" ] && solaris="solaris8" [ "x`uname -r`" = "x5.6" ] && solaris="solaris6" if [ "`uname -m`" = sun4u ] then [ "x`which gcc 2>&-`" != "x" ] && cc="gcc" [ "x`which cc 2>&-`" != "x" ] && cc="cc" else [ "x`which cc 2>&-`" != "x" ] && cc="cc" [ "x`which gcc 2>&-`" != "x" ] && cc="gcc" fi [ "$cc" = "" ] && die "Failed to find a compiler" echo "Configuring for $solaris (with $cc)" conf/$solaris-$cc ;; Darwin*) echo "Configuring for macosx" conf/macosx ;; *) die "Unknown platform: please configure manually" ;; esac exit 0 # vi:set ts=4 sw=4: daemon-0.6.4/configure000077500000000000000000000035721140522741300147010ustar00rootroot00000000000000#!/bin/sh # # daemon - http://libslack.org/daemon/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # 20100612 raf die() { echo "$0: $@" >&2; exit 1; } case "`uname -a`" in Linux*) echo "Configuring for linux" conf/linux ;; GNU/kFreeBSD*) echo "Configuring for kfreebsd" conf/kfreebsd ;; FreeBSD*) echo "Configuring for freebsd" conf/freebsd ;; NetBSD*) echo "Configuring for netbsd" conf/netbsd ;; OpenBSD*) echo "Configuring for openbsd" conf/openbsd ;; SunOS*) cc="" solaris="solaris10" [ "x`uname -r`" = "x5.8" ] && solaris="solaris8" [ "x`uname -r`" = "x5.6" ] && solaris="solaris6" if [ "`uname -m`" = sun4u ] then [ "x`which gcc 2>&-`" != "x" ] && cc="gcc" [ "x`which cc 2>&-`" != "x" ] && cc="cc" else [ "x`which cc 2>&-`" != "x" ] && cc="cc" [ "x`which gcc 2>&-`" != "x" ] && cc="gcc" fi [ "$cc" = "" ] && die "Failed to find a compiler" echo "Configuring for $solaris (with $cc)" conf/$solaris-$cc ;; Darwin*) echo "Configuring for macosx" conf/macosx ;; *) die "Unknown platform: please configure manually" ;; esac exit 0 # vi:set ts=4 sw=4: daemon-0.6.4/daemon.c000066400000000000000000002632421140522741300144030ustar00rootroot00000000000000/* * daemon - http://libslack.org/daemon/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - turns other processes into daemons =head1 SYNOPSIS usage: daemon [options] [--] [cmd arg...] options: -h, --help - Print a help message then exit -V, --version - Print a version message then exit -v, --verbose[=level] - Set the verbosity level -d, --debug[=level] - Set the debugging level -C, --config=path - Specify the system configuration file -N, --noconfig - Bypass the system configuration file -n, --name=name - Guarantee a single named instance -X, --command=cmd - Specify the client command as an option -P, --pidfiles=/dir - Override standard pidfile location -F, --pidfile=/path - Override standard pidfile name and location -u, --user=user[:[group]] - Run the client as user[:group] -R, --chroot=path - Run the client with path as root -D, --chdir=path - Run the client in directory path -m, --umask=umask - Run the client with the given umask -e, --env="var=val" - Set a client environment variable -i, --inherit - Inherit environment variables -U, --unsafe - Allow execution of unsafe executable -S, --safe - Deny execution of unsafe executable -c, --core - Allow core file generation -r, --respawn - Respawn the client when it terminates -a, --acceptable=# - Minimum acceptable client duration (seconds) -A, --attempts=# - Respawn # times on error before delay -L, --delay=# - Delay between spawn attempt bursts (seconds) -M, --limit=# - Maximum number of spawn attempt bursts --idiot - Idiot mode (trust root with the above) -f, --foreground - Run the client in the foreground -p, --pty[=noecho] - Allocate a pseudo terminal for the client -l, --errlog=spec - Send daemon's error output to syslog or file -b, --dbglog=spec - Send daemon's debug output to syslog or file -o, --output=spec - Send client's output to syslog or file -O, --stdout=spec - Send client's stdout to syslog or file -E, --stderr=spec - Send client's stderr to syslog or file --running - Check if a named daemon is running --restart - Restart a named daemon client --stop - Terminate a named daemon process =head1 DESCRIPTION I turns other processes into daemons. There are many tasks that need to be performed to correctly set up a daemon process. This can be tedious. I performs these tasks for other processes. The preparatory tasks that I performs for other processes are: =over 4 =item * First revoke any setuid or setgid privileges that I may have been installed with (by system administrators who laugh in the face of danger). =item * Process command line options. =item * Change the root directory if the C<--chroot> option was supplied. =item * Change the process uid and gid if the C<--user> option was supplied. Only I can use this option. Note that the uid of I itself is changed, rather than just changing the uid of the client process. =item * Read the system configuration file (C by default, or specified by the C<--config> option) unless the C<--noconfig> option was supplied. Then read the user's configuration file (C<~/.daemonrc>), if any. Generic options are processed first, then options specific to the daemon with the given name. B nor C<--user> options may appear in the configuration file.> =item * Disable core file generation to prevent leaking sensitive information in daemons run by I (unless the C<--core> option was supplied). =item * Become a daemon process: =over 4 =item * If I was not invoked by I or I: =over 4 =item * Background the process to lose process group leadership. =item * Start a new process session. =item * Under I, background the process again to lose process session leadership. This prevents the process from ever gaining a controlling terminal. This only happens when C is defined and C is not defined when I is compiled. Before doing this, ignore C because when the session leader terminates, all processes in the foreground process group are sent a C signal (apparently). Note that this code may not execute (e.g. when started by I or I or when either C was not defined or C was defined when I was compiled). This means that the client can't make any assumptions about the C handler. =back =item * Change directory to the root directory so as not to hamper umounts. =item * Clear the umask to enable explicit file creation modes. =item * Close all open file descriptors. If I was invoked by I, C, C and C are left open since they are open to a socket. =item * Open C, C and C to C in case something requires them to be open. Of course, this is not done if I was invoked by I. =item * If the C<--name> option was supplied, create and lock a file containing the process id of the I process. The presence of this locked file prevents two instances of a daemon with the same name from running at the same time. The standard location of the pidfile is C for I or C for ordinary users. If the C<--pidfiles> option was supplied, its argument specifies the directory in which the pidfile will be placed. If the C<--pidfile> option was supplied, its argument specifies the name of the pidfile and the directory in which it will be placed. =back =item * If the C<--umask> option was supplied, set the umask to its argument. Otherwise, set the umask to C<022> to prevent clients from accidentally creating group or world writable files. =item * Set the current directory if the C<--chdir> option was supplied. =item * Spawn the client command and wait for it to terminate. The client command may be specified as command line arguments or as the argument of the C<--command> option. If both the C<--command> option and command line arguments are present, the client command is the result of appending the command line arguments to the argument of the C<--command> option. =item * If the C<--syslog>, C<--outlog> and/or C<--errlog> options were supplied, the client's standard output and/or standard error are captured by I and sent to the respective I destinations. =item * When the client terminates, I respawns it if the C<--respawn> option was supplied. If the client ran for less than 300 seconds (or the value of the C<--acceptable> option), then I sees this as an error. It will attempt to restart the client up to five times (or the value of the C<--attempts> option) before waiting for 300 seconds (or the value of the C<--delay> option). This gives the administrator the chance to correct whatever is preventing the client from running without overloading system resources. If the C<--limit> option was supplied, I terminates after the specified number of spawn attempt bursts. The default is zero which means never give up, never surrender. When the client terminates and the C<--respawn> option wasn't supplied, I terminates. =item * If I receives a C signal, it propagates the signal to the client and then terminates. =item * If I receives a C signal (from another invocation of I supplied with the C<--restart> option), it sends a C signal to the client. If started with the C<--respawn> option, the client process will be restarted after it is killed by the C signal. =item * If the C<--foreground> option was supplied, the client process is run as a foreground process and is not turned into a daemon. If I is connected to a terminal, so will the client process. If I is not connected to a terminal but the client needs to be connected to a terminal, use the C<--pty> option. =back =head1 OPTIONS =over 4 =item C<-h>, C<--help> Display a help message and exit. =item C<-V>, C<--version> Display a version message and exit. =item C<-v>I<[level]>, C<--verbose>I<[=level]> Set the message verbosity level to I (or 1 if I is not supplied). I does not have any verbose messages so this has no effect unless the C<--running> option is supplied. =item C<-d>I<[level]>, C<--debug>I<[=level]> Set the debug message level to I (or 1 if I is not supplied). Level 1 traces high level function calls. Level 2 traces lower level function calls and shows configuration information. Level 3 adds environment variables. Level 9 adds every return value from I to the output. Debug messages are sent to the destination specified by the C<--dbglog> option (by default, the I facility, C). =item C<-C> I, C<--config=>I Specify the configuration file to use. By default, C is the configuration file if it exists and is not group or world writable and does not exist in a group or world writable directory. The configuration file lets you predefine options that apply to all clients and to specifically named clients. =item C<-N>, C<--noconfig> Bypass the system configuration file, C. Only the user's C<~/.daemonrc> configuration file will be read (if it exists). =item C<-n> I, C<--name=>I Create and lock a pid file (CIC<.pid>), ensuring that only one daemon with the given I is active at the same time. =item C<-X> I, C<--command=>I Specify the client command as an option. If a command is specified along with its name in the configuration file, then daemons can be started merely by mentioning their name: daemon --name ftumpch B Specifying the client command in the configuration file means that no shell features are available (i.e. no meta characters). =item C<-P> I, C<--pidfiles=>I Override the standard pidfile location. The standard pidfile location is user dependent: I's pidfiles live in C. Normal users' pidfiles live in C. This option can only be used with the C<--name> option. Use this option if these locations are unacceptable but make sure you don't forget where you put your pidfiles. This option is best used in configuration files or in shell scripts, not on the command line. =item C<-F> I, C<--pidfile=>I Override the standard pidfile name and location. The standard pidfile location is described immediately above. The standard pidfile name is the argument of the C<--name> option followed by C<.pid>. Use this option if the standard pidfile name and location are unacceptable but make sure you don't forget where you put your pidfile. This option should only be used in configuration files or in shell scripts, not on the command line. =item C<-u> I, C<--user=>I Run the client as a different user (and group). This only works for I. If the argument includes a I<:group> specifier, I will assume the specified group and no other. Otherwise, I will assume all groups that the specified user is in. For backwards compatibility, C<"."> may be used instead of C<":"> to separate the user and group but since C<"."> may appear in user and group names, ambiguities can arise such as using C<--user=>I with users I and I and group I. With such an ambiguity, I will assume the user I and group I. Use C<--user=>I instead for the other interpretation. =item C<-R> I, C<--chroot=>I Change the root directory to I before running the client. On some systems, only I can do this. Note that the path to the client program and to the configuration file (if any) must be relative to the new root path. =item C<-D> I, C<--chdir=>I Change the directory to I before running the client. =item C<-m> I, C<--umask=>I Change the umask to I before running the client. I must be a valid octal mode. The default umask is C<022>. =item C<-e> I, C<--env=>I Set an environment variable for the client process. This option can be used any number of times. If it is used, only the supplied environment variables are passed to the client process. Otherwise, the client process inherits the current set of environment variables. =item C<-i>, C<--inherit> Explicitly inherit environment variables. This is only needed when the C<--env> option is used. When this option is used, the C<--env> option adds to the inherited environment, rather than replacing it. =item C<-U>, C<--unsafe> Allow reading an unsafe configuration file and execution of an unsafe executable. A configuration file or executable is unsafe if it is group or world writable or is in a directory that is group or world writable (following symbolic links). If an executable is a script interpreted by another executable, then it is considered unsafe if the interpreter is unsafe. If the interpreter is C (with an argument that is a command name to be searched for in C<$PATH>), then that command must be safe. By default, I will refuse to read an unsafe configuration file or to execute an unsafe executable when run by I. This option overrides that behaviour and hence should never be used. =item C<-S>, C<--safe> Deny reading an unsafe configuration file and execution of an unsafe executable. By default, I will allow reading an unsafe configuration file and execution of an unsafe executable when run by ordinary users. This option overrides that behaviour. =item C<-c>, C<--core> Allow the client to create a core file. This should only be used for debugging as it could lead to security holes in daemons run by I. =item C<-r>, C<--respawn> Respawn the client when it terminates. =item C<-a> I<#>, C<--acceptable=>I<#> Specify the minimum acceptable duration in seconds of a client process. The default value is 300 seconds. It cannot be set to less than 10 seconds except by I when used in conjunction with the C<--idiot> option. This option can only be used with the C<--respawn> option. less than this, it is considered to have failed. =item C<-A> I<#>, C<--attempts=>I<#> Number of attempts to spawn before delaying. The default value is 5. It cannot be set to more than 100 attempts except by I when used in conjunction with the C<--idiot> option. This option can only be used with the C<--respawn> option. =item C<-L> I<#>, C<--delay=>I<#> Delay in seconds between each burst of spawn attempts. The default value is 300 seconds. It cannot be set to less than 10 seconds except by I when used in conjunction with the C<--idiot> option. This option can only be used with the C<--respawn> option. =item C<-M> I<#>, -C<--limit=>I<#> Limit the number of spawn attempt bursts. The default value is zero which means no limit. This option can only be used with the C<--respawn> option. =item C<--idiot> Turn on idiot mode in which I will not enforce the minimum or maximum values normally imposed on the C<--acceptable>, C<--attempts> and C<--delay> option arguments. The C<--idiot> option must appear before any of these options. Only the I user may use this option because it can turn a slight misconfiguration into a lot of wasted CPU effort and log messages. =item C<-f>, C<--foreground> Run the client in the foreground. The client is not turned into a daemon. =item C<-p>I<[noecho]>, C<--pty>I<[=noecho]> Connect the client to a pseudo terminal. This option can only be used with the C<--foreground> option. This is the default when the C<--foreground> option is supplied and I's standard input is connected to a terminal. This option is only necessary when the client process must be connected to a controlling terminal but I itself has been run without a controlling terminal (e.g. from I or a pipeline). If the C argument is supplied with this option, the client's side of the pseudo terminal will be set to noecho mode. Use this only if there really is a terminal involved and input is being echoed twice. =item C<-l> I, C<--errlog=>I Send I's standard output and error to the syslog destination or file specified by I. If I is of the form C<"facility.priority">, then output is sent to I. Otherwise, output is appended to the file whose path is given in I. By default, output is sent to C. =item C<-b> I, C<--dbglog=>I Send I's debug output to the syslog destination or file specified by I. If I is of the form C<"facility.priority">, then output is sent to I. Otherwise, output is appended to the file whose path is given in I. By default, output is sent to C. =item C<-o> I, C<--output=>I Capture the client's standard output and error and send it to the syslog destination or file specified by I. If I is of the form C<"facility.priority">, then output is sent to I. Otherwise, output is appended to the file whose path is given in I. By default, output is discarded unless the C<--foreground> option is present. In this case, the client's stdout and stderr are propagated to I's stdout and stderr respectively. =item C<-O> I, C<--stdout=>I Capture the client's standard output and send it to the syslog destination or file specified by I. If I is of the form C<"facility.priority">, then output is sent to I. Otherwise, stdout is appended to the file whose path is given in I. By default, stdout is discarded unless the C<--foreground> option is present, in which case, the client's stdout is propagated to I's stdout. =item C<-E> I, C<--stderr=>I Capture the client's standard error and send it to the syslog destination specified by I. If I is of the form C<"facility.priority">, then stderr is sent to I. Otherwise, stderr is appended to the file whose path is given in I. By default, stderr is discarded unless the C<--foreground> option is present, in this case, the client's stderr is propagated to I's stderr. =item C<--running> Check whether or not a named daemon is running, then I with C if the named daemon is running or C if it isn't. If the C<--verbose> option is supplied, print a message before exiting. This option can only be used with the C<--name> option. Note that the C<--chroot>, C<--user>, C<--name>, C<--pidfiles> and C<--pidfile> (and possibly C<--config>) options must be the same as for the target daemon. Note that the C<--running> option must appear before any C<--pidfile> or C<--pidfiles> option when checking if another user's daemon is running otherwise you might get an error about the pidfile directory not being writable. =item C<--restart> Instruct a named daemon to terminate and restart its client process. This option can only be used with the C<--name> option. Note that the C<--chroot>, C<--user>, C<--name>, C<--pidfiles> and C<--pidfile> (and possibly C<--config>) options must be the same as for the target daemon. =item C<--stop> Stop a named daemon then I. This option can only be used with the C<--name> option. Note that the C<--chroot>, C<--user>, C<--name>, C<--pidfiles> and C<--pidfile> (and possibly C<--config>) options must be the same as for the target daemon. =back As with all other programs, a C<--> argument signifies the end of options. Any options that appear on the command line after C<--> are part of the client command. =head1 FILES C, C<~/.daemonrc> - define default options Each line of the configuration file consists of a client name or C<'*'>, followed by whitespace, followed by a comma separated list of options. Blank lines and comments (C<'#'> to end of the line) are ignored. Lines may be continued with a C<'\'> character at the end of the line. For example: * errlog=daemon.err,output=local0.err,core test1 syslog=local0.debug,debug=9,verbose=9,respawn test2 syslog=local0.debug,debug=9,verbose=9,respawn The command line options are processed first to look for a C<--config> option. If no C<--config> option was supplied, the default file, C, is used. If the user has their own configuration file (C<~/.daemonrc>) it is also used. If the configuration files contain any generic (C<'*'>) entries, their options are applied in order of appearance. If the C<--name> option was supplied and the configuration files contain any entries with the given name, their options are then applied in order of appearance. Finally, the command line options are applied again. This ensures that any generic options apply to all clients by default. Client specific options override generic options. User options override system wide options. Command line options override everything else. Note that the configuration files are not opened and read until after any C<--chroot> and/or C<--user> command line options are processed. This means that the configuration file paths and the client's file path must be relative to the C<--chroot> argument. It also means that the configuration files and the client executable must be readable/executable by the user specified by the C<--user> argument. It also means that the C<--chroot> and C<--user> options must not appear in the configuration file. Also note that the C<--name> must not appear in the configuration file either. =head1 BUGS If you specify (in a configuration file) that all clients allow core file generation, there is no way to countermand that for any client (without using an alternative configuration file). So don't do that. The same applies to respawning and foreground. It is possible for the client process to obtain a controlling terminal under I. If anything calls I on a terminal device without the C flag, the process doing so will obtain a controlling terminal and then be susceptible to unintended termination by a C. Clients run in the foreground with a pseudo terminal don't respond to job control (i.e. suspending with Control-Z doesn't work). This is because the client belongs to an orphaned process group (it starts in its own process session) so the kernel won't send it C signals. However, if the client is a shell that supports job control, it's subprocesses can be suspended. Clients can only be restarted if they were started with the C<--respawn> option. Using C<--restart> on a non-respawning daemon client is equivalent to using C<--stop>. =head1 MAILING LISTS The following mailing lists exist for daemon related discussion: daemon-announce@libslack.org - Announcements daemon-users@libslack.org - User forum daemon-dev@libslack.org - Development forum To subscribe to any of these mailing lists, send a mail message to IC<-request@libslack.org> with C as the message body. e.g. $ echo subscribe | mail daemon-announce-request@libslack.org $ echo subscribe | mail daemon-users-request@libslack.org $ echo subscribe | mail daemon-dev-request@libslack.org Or you can send a mail message to C with C I in the message body. This way, you can subscribe to multiple lists at the same time. e.g. $ mail majordomo@libslack.org subscribe daemon-announce subscribe daemon-users subscribe daemon-dev . A digest version of each mailing list is also available. Subscribe to digests as above but append C<-digest> to the listname. =head1 SEE ALSO I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #ifndef _BSD_SOURCE #define _BSD_SOURCE /* For SIGWINCH and CEOF on OpenBSD-4.7 */ #endif #ifndef __BSD_VISIBLE #define __BSD_VISIBLE 1 /* For SIGWINCH on FreeBSD-8.0 */ #endif #ifndef _NETBSD_SOURCE #define _NETBSD_SOURCE /* For CEOF, chroot() on NetBSD-5.0.2 */ #endif #include #include #include #include #include #ifdef _POSIX_SOURCE #undef _POSIX_SOURCE /* For CEOF on FreeBSD-8.0 */ #define _RESTORE_POSIX_SOURCE #endif #include #ifdef _RESTORE_POSIX_SOURCE #define _POSIX_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include /* Configuration file entries */ typedef struct Config Config; struct Config { char *name; List *options; }; #ifndef RESPAWN_ACCEPTABLE #define RESPAWN_ACCEPTABLE 300 #endif #ifndef RESPAWN_ACCEPTABLE_MIN #define RESPAWN_ACCEPTABLE_MIN 10 #endif #ifndef RESPAWN_ATTEMPTS #define RESPAWN_ATTEMPTS 5 #endif #ifndef RESPAWN_ATTEMPTS_MIN #define RESPAWN_ATTEMPTS_MIN 0 #endif #ifndef RESPAWN_ATTEMPTS_MAX #define RESPAWN_ATTEMPTS_MAX 100 #endif #ifndef RESPAWN_DELAY #define RESPAWN_DELAY 300 #endif #ifndef RESPAWN_DELAY_MIN #define RESPAWN_DELAY_MIN 10 #endif #ifndef RESPAWN_LIMIT #define RESPAWN_LIMIT 0 #endif #ifndef RESPAWN_LIMIT_MIN #define RESPAWN_LIMIT_MIN 0 #endif #ifndef SLAVENAMESIZE #define SLAVENAMESIZE 64 #endif #ifndef CONFIG_PATH #define CONFIG_PATH "/etc/daemon.conf" #endif #ifndef CONFIG_PATH_USER #define CONFIG_PATH_USER ".daemonrc" #endif /* Global variables */ extern char **environ; static struct { int ac; /* number of command line arguments */ char **av; /* the command line arguments */ char **cmd; /* command vector to execute */ char *name; /* the daemon's name to use for the locked pid file */ char *pidfiles; /* location of the pidfile */ char *pidfile; /* absolute path for the pidfile */ char *user; /* name of user to run as */ char *group; /* name of group to run as */ char userbuf[BUFSIZ]; /* buffer to store the user name */ char groupbuf[BUFSIZ]; /* buffer to store the group name */ char *chroot; /* name of root directory to run under */ char *chdir; /* name of directory to change to */ char *command; /* the client command as a string */ mode_t umask; /* set umask to this */ int init_groups; /* initgroups(3) if group not specified */ uid_t initial_uid; /* the uid when the program started */ uid_t uid; /* run the client as this user */ gid_t gid; /* run the client as this group */ List *env; /* client environment variables */ char **environ; /* client environment */ int inherit; /* inherit environment variables? */ int respawn; /* respawn the client process when it terminates? */ int acceptable; /* minimum acceptable client duration in seconds */ int attempts; /* number of times to attempt respawning before delay */ int delay; /* delay in seconds between respawn attempt bursts */ int limit; /* number of spawn attempt bursts */ int idiot; /* idiot mode */ int attempt; /* spawn attempt counter */ int burst; /* spawn attempt burst counter */ int foreground; /* run the client in the foreground? */ int pty; /* allocate a pseudo terminal for the client? */ int noecho; /* set client pty to noecho mode? */ int core; /* do we allow core file generation? */ int unsafe; /* executable unsafe executables as root? */ int safe; /* do not execute unsafe executables? */ char *client_out; /* syslog/file spec for client stdout */ char *client_err; /* syslog/file spec for client stderr */ char *daemon_err; /* syslog/file spec for daemon output */ char *daemon_dbg; /* syslog/file spec for daemon debug output */ int client_outlog; /* syslog facility for client stdout */ int client_errlog; /* syslog facility for client stderr */ int daemon_errlog; /* syslog facility for daemon output */ int daemon_dbglog; /* syslog facility for daemon debug output */ int client_outfd; /* file descriptor for client stdout */ int client_errfd; /* file descriptor for client stderr */ char *config; /* name of the config file to use - /etc/daemon.conf */ int noconfig; /* bypass the system configuration file? */ pid_t pid; /* the pid of the client process to run as a daemon */ int in; /* file descriptor for client stdin */ int out; /* file descriptor for client stdout */ int err; /* file descriptor for client stderr */ int masterfd; /* master side of the pseudo terminal */ char slavename[SLAVENAMESIZE]; /* pty device name */ size_t slavenamesize; /* size of g.slavename */ int stop; /* stop a named daemon? */ int running; /* check whether or not a named daemon is running? */ int restart; /* restart a named daemon? */ time_t spawn_time; /* when did we last spawn the client? */ int done_name; /* have we already set the name? */ int done_chroot; /* have we already set the root directory? */ int done_user; /* have we already set the user id? */ int done_config; /* have we already processed the configuration file? */ struct termios stdin_termios; /* stdin's terminal attributes */ struct winsize stdin_winsize; /* stdin's terminal window size */ int stdin_isatty; /* is stdin a terminal? */ int stdin_eof; /* has stdin received eof? */ int terminated; /* have we received a term signal? */ } g = { 0, /* ac */ null, /* av */ null, /* cmd */ null, /* name */ null, /* pidfiles */ null, /* pidfile */ null, /* user */ null, /* group */ { 0 }, /* userbuf */ { 0 }, /* groupbuf */ null, /* chroot */ null, /* chdir */ null, /* command */ S_IWGRP | S_IWOTH, /* umask */ 0, /* init_groups */ 0, /* initial_uid */ 0, /* uid */ 0, /* gid */ null, /* env */ null, /* environ */ 0, /* inherit */ 0, /* respawn */ RESPAWN_ACCEPTABLE, /* acceptable */ RESPAWN_ATTEMPTS, /* attempts */ RESPAWN_DELAY, /* delay */ RESPAWN_LIMIT, /* limit */ 0, /* idiot */ 0, /* attempt */ 0, /* burst */ 0, /* foreground */ 0, /* pty */ 0, /* noecho */ 0, /* core */ 0, /* unsafe */ 0, /* safe */ null, /* client_out */ null, /* client_err */ null, /* daemon_err */ null, /* daemon_dbg */ 0, /* client_outlog */ 0, /* client_errlog */ LOG_DAEMON | LOG_ERR, /* daemon_errlog */ LOG_DAEMON | LOG_DEBUG, /* daemon_dbglog */ -1, /* client_outfd */ -1, /* client_errfd */ null, /* config */ 0, /* noconfig */ (pid_t)0, /* pid */ -1, /* in */ -1, /* out */ -1, /* err */ -1, /* masterfd */ { 0 }, /* slavename */ SLAVENAMESIZE, /* slavenamesize */ 0, /* stop */ 0, /* running */ 0, /* restart */ (time_t)0, /* spawn_time */ 0, /* done_name */ 0, /* done_chroot */ 0, /* done_user */ 0, /* done_config */ { 0 }, /* stdin_termios */ { 0 }, /* stdin_winsize */ 0, /* stdin_isatty */ 0, /* stdin_eof */ 0 /* terminated */ }; /* C Store the C<--name> option argument, C. */ #ifndef ACCEPT_NAME #define ACCEPT_NAME "-._abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" #endif #ifndef ACCEPT_PATH #define ACCEPT_PATH ACCEPT_NAME "/" #endif static void handle_name_option(const char *spec) { debug((1, "handle_name_option(spec = %s)", spec)) if (g.done_config) return; if (g.done_name) prog_usage_msg("Misplaced option: --name=%s in config file (must be on command line)", spec); if (strspn(spec, ACCEPT_NAME) != strlen(spec)) prog_usage_msg("Invalid --name argument: '%s' (Must consist entirely of [-._a-zA-Z0-9])", spec); g.name = (char *)spec; } /* C Store the C<--pidfiles> option argument, C. */ static void handle_pidfiles_option(const char *spec) { struct stat status[1]; debug((1, "handle_pidfiles_option(spec = %s)", spec)) if (strspn(spec, ACCEPT_PATH) != strlen(spec)) prog_usage_msg("Invalid --pidfiles argument: '%s' (Must consist entirely of [-._a-zA-Z0-9/])", spec); if (*spec != PATH_SEP) prog_usage_msg("Invalid --pidfiles argument: '%s' (Must be an absolute directory path)", spec); if (stat(spec, status) == -1 || !S_ISDIR(status->st_mode)) prog_usage_msg("Invalid --pidfiles argument: '%s' (Directory does not exist)", spec); if (!g.running && access(spec, W_OK) == -1) prog_usage_msg("Invalid --pidfiles argument: '%s' (Directory is not writable)", spec); g.pidfiles = (char *)spec; } /* C Store the C<--pidfile> option argument, C. */ static void handle_pidfile_option(const char *spec) { struct stat status[1]; char *buf, *end; size_t size; debug((1, "handle_pidfile_option(spec = %s)", spec)) if (strspn(spec, ACCEPT_PATH) != strlen(spec)) prog_usage_msg("Invalid --pidfile argument: '%s' (Must consist entirely of [-._a-zA-Z0-9/])", spec); if (*spec != PATH_SEP || (stat(spec, status) == 0 && S_ISDIR(status->st_mode))) prog_usage_msg("Invalid --pidfile argument: '%s' (Must be an absolute file path)", spec); if ((size = (end = strrchr(spec, PATH_SEP)) - spec + 1) == 1) ++size; if (!(buf = mem_create(size, char))) fatalsys("out of memory"); snprintf(buf, size, "%.*s", (int)size - 1, spec); if (stat(buf, status) == -1 || !S_ISDIR(status->st_mode)) prog_usage_msg("Invalid --pidfile argument: '%s' (Parent directory does not exist)", spec); if (!g.running && access(buf, W_OK) == -1) prog_usage_msg("Invalid --pidfile argument: '%s' (Parent directory is not writable)", spec); g.pidfile = (char *)spec; } /* C Parse and store the client C<--user[.group]> option argument, C. */ static void handle_user_option(char *spec) { struct passwd *pwd; struct group *grp; char **member; char *pos; debug((1, "handle_user_option(spec = %s)", spec)) if (g.done_config) return; if (g.done_user) prog_usage_msg("Misplaced option: --user=%s in config file (must be on command line)", spec); if (getuid() || geteuid()) prog_usage_msg("Invalid option: --user (only works for root)"); if ((pos = strchr(spec, ':')) || (pos = strchr(spec, '.'))) { if (pos > spec) snprintf(g.user = g.userbuf, BUFSIZ, "%*.*s", (int)(pos - spec), (int)(pos - spec), spec); if (*++pos) snprintf(g.group = g.groupbuf, BUFSIZ, "%s", pos); } else { snprintf(g.user = g.userbuf, BUFSIZ, "%s", spec); } g.init_groups = (g.group == null); if (!g.user) prog_usage_msg("Invalid --user argument: '%s' (no user name)", spec); if (!(pwd = getpwnam(g.user))) prog_usage_msg("Invalid --user argument: '%s' (unknown user %s)", spec, g.user); g.uid = pwd->pw_uid; g.gid = pwd->pw_gid; if (g.group) { if (!(grp = getgrnam(g.group))) prog_usage_msg("Invalid --user argument: '%s' (unknown group %s)", spec, g.group); if (grp->gr_gid != pwd->pw_gid) { for (member = grp->gr_mem; *member; ++member) if (!strcmp(*member, g.user)) break; if (!*member) prog_usage_msg("Invalid --user argument: '%s' (user %s is not in group %s)", spec, g.user, g.group); } g.gid = grp->gr_gid; } } /* C Store the C<--chroot> option argument, C. */ static void handle_chroot_option(const char *spec) { debug((1, "handle_chroot_option(spec = %s)", spec)) if (g.done_config) return; if (g.done_chroot) prog_usage_msg("Misplaced option: --chroot=%s in config file (must be on command line)", spec); g.chroot = (char *)spec; } /* C Parse and store the C<--umask> option argument, C. */ static void handle_umask_option(const char *spec) { char *end; long val; debug((1, "handle_umask_option(spec = %s)", spec)) val = strtol(spec, &end, 8); if (end == spec || *end || val < 0 || val > 0777) prog_usage_msg("Invalid --umask argument: '%s' (must be a valid octal mode)", spec); g.umask = val; } /* C Store the C<--env> option argument, C. */ static void handle_env_option(const char *var) { debug((1, "handle_env_option(spec = %s)", var)) if (g.env == null && !(g.env = list_create(null))) fatalsys("failed to create environment list"); if (!list_append(g.env, (void *)var)) fatalsys("failed to add '%s' to environment list", var); } /* C Process the C<--inherit> option. Add the contents of C to the list of environment variables to be used by the client. */ static void handle_inherit_option(void) { char **env; debug((1, "handle_inherit_option()")) if (g.env == null && !(g.env = list_create(null))) fatalsys("failed to create environment list"); for (env = environ; *env; ++env) if (!list_append(g.env, *env)) fatalsys("failed to add '%s' to environment list", env); g.inherit = 1; } /* C Store the C<--acceptable> option argument, C. */ static void handle_acceptable_option(int acceptable) { debug((1, "handle_acceptable_option(acceptable = %d)", acceptable)) if (!g.idiot && acceptable < RESPAWN_ACCEPTABLE_MIN) prog_usage_msg("Invalid --acceptable argument: %d (less than %d)\n", acceptable, RESPAWN_ACCEPTABLE_MIN); g.acceptable = acceptable; } /* C Store the positive C<--attempts> option argument, C. */ static void handle_attempts_option(int attempts) { debug((1, "handle_attempts_option(attempts = %d)", attempts)) if (!g.idiot && (attempts < RESPAWN_ATTEMPTS_MIN || attempts > RESPAWN_ATTEMPTS_MAX)) prog_usage_msg("Invalid --attempts argument: %d (not between %d and %d)", attempts, RESPAWN_ATTEMPTS_MIN, RESPAWN_ATTEMPTS_MAX); g.attempts = attempts; } /* C Store the C<--delay> option argument, C. */ static void handle_delay_option(int delay) { debug((1, "handle_delay_option(delay = %d)", delay)) if (!g.idiot && delay < RESPAWN_DELAY_MIN) prog_usage_msg("Invalid --delay argument: %d (less than %d)\n", delay, RESPAWN_DELAY_MIN); g.delay = delay; } /* C Store the C<--limit> option argument, C. */ static void handle_limit_option(int limit) { debug((1, "handle_limit_option(limit = %d)", limit)) if (limit < RESPAWN_LIMIT_MIN) prog_usage_msg("Invalid --limit argument: %d (less than %d)\n", limit, RESPAWN_LIMIT_MIN); g.limit = limit; } /* C Store the C<--idiot> option argument if allowed. */ static void handle_idiot_option(void) { debug((1, "handle_idiot_option()")) if (g.initial_uid) prog_usage_msg("Invalid option: --idiot (is only for root)"); g.idiot = 1; } /* C Store the C<--pty> option argument, C. */ static void handle_pty_option(char *arg) { debug((1, "handle_pty_option(arg = %s)", arg)) g.pty = 1; if (arg) { if (strcmp(arg, "noecho")) prog_usage_msg("Invalid --pty argument: '%s' (Only 'noecho' is supported)", arg); g.noecho = 1; } } /* C Parse the syslog target, C. Store C in C<*str> and store the parsed facility and priority in C<*var>. */ static void store_syslog(const char *option, const char *spec, char **str, int *var) { int facility; int priority; debug((1, "store_syslog(spec = %s)", spec)) if (syslog_parse(spec, &facility, &priority) == -1) { *str = (char *)spec; /* Must be a file */ *var = 0; /* Erase default syslog */ return; } *str = (char *)spec; *var = facility | priority; } /* C Parse and store the C<--errlog> option argument, C. */ static void handle_errlog_option(const char *spec) { debug((1, "handle_errlog_option(spec = %s)", spec)) store_syslog("errlog", spec, &g.daemon_err, &g.daemon_errlog); } /* C Parse and store the C<--dbglog> option argument, C. */ static void handle_dbglog_option(const char *spec) { debug((1, "handle_dbglog_option(spec = %s)", spec)) store_syslog("dbglog", spec, &g.daemon_dbg, &g.daemon_dbglog); } /* C Parse and store the C<--output> option argument, C. */ static void handle_output_option(const char *spec) { debug((1, "handle_output_option(spec = %s)", spec)) store_syslog("output", spec, &g.client_out, &g.client_outlog); store_syslog("output", spec, &g.client_err, &g.client_errlog); } /* C Parse and store the C<--stdout> option argument, C. */ static void handle_stdout_option(const char *spec) { debug((1, "handle_stdout_option(spec = %s)", spec)) store_syslog("stdout", spec, &g.client_out, &g.client_outlog); } /* C Parse and store the C<--stderr> option argument, C. */ static void handle_stderr_option(const char *spec) { debug((1, "handle_stderr_option(spec = %s)", spec)) store_syslog("stderr", spec, &g.client_err, &g.client_errlog); } /* C for I or C for ordinary users. =back On success, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int daemon_init(const char *name) { pid_t pid; long nopen; int fd; /* ** Don't setup a daemon-friendly process context ** if started by init(8) or inetd(8). */ if (!(daemon_started_by_init() || daemon_started_by_inetd())) { /* ** Background the process. ** Lose process session/group leadership. */ if ((pid = fork()) == -1) return -1; if (pid) exit(EXIT_SUCCESS); /* Become a process session leader. */ /* This can only fail when we're already a session leader. */ setsid(); #ifndef NO_EXTRA_SVR4_FORK #ifdef SVR4 /* ** Ignore SIGHUP because when the session leader terminates (which is ** about to happen), all processes in the foreground process group ** are sent the SIGHUP signal (apparently). It is expected that ** clients will set their own SIGHUP handler after the call to ** daemon_init() if necessary. */ { struct sigaction act[1]; act->sa_handler = SIG_IGN; sigemptyset(&act->sa_mask); act->sa_flags = 0; if (sigaction(SIGHUP, act, NULL) == -1) return -1; } /* ** Lose process session leadership ** to prevent gaining a controlling ** terminal in SVR4. */ if ((pid = fork()) == -1) return -1; if (pid) exit(EXIT_SUCCESS); #endif #endif } /* Enter the root directory to prevent hampering umounts. */ if (chdir(ROOT_DIR) == -1) return -1; /* Clear umask to enable explicit file modes. */ umask(0); /* ** We need to close all open file descriptors. Check how ** many file descriptors we have (If indefinite, a usable ** number (1024) will be returned). ** ** Flaw: If many files were opened and then this limit ** was reduced to below the highest file descriptor, ** we may not close all file descriptors. */ if ((nopen = limit_open()) == -1) return -1; /* ** Close all open file descriptors. If started by inetd, ** we don't close stdin, stdout and stderr. ** Don't forget to open any future tty devices with O_NOCTTY ** so as to prevent gaining a controlling terminal ** (not necessary with SVR4). */ if (daemon_started_by_inetd()) { for (fd = 0; fd < nopen; ++fd) { switch (fd) { case STDIN_FILENO: case STDOUT_FILENO: case STDERR_FILENO: break; default: close(fd); } } } else { for (fd = 0; fd < nopen; ++fd) close(fd); /* ** Open stdin, stdout and stderr to /dev/null just in case some ** code buried in a library somewhere expects them to be open. */ if ((fd = open("/dev/null", O_RDWR)) == -1) return -1; /* ** This is only needed for very strange (hypothetical) ** POSIX implementations where STDIN_FILENO != 0 or ** STDOUT_FILE != 1 or STDERR_FILENO != 2 (yeah, right). */ if (fd != STDIN_FILENO) { if (dup2(fd, STDIN_FILENO) == -1) return -1; close(fd); } if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) return -1; if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) return -1; } /* Place our process id in the file system and lock it. */ if (name) return daemon_pidfile(name); return 0; } /* =item C Unlinks the locked pid file, if any. Returns 0. =cut */ int daemon_close(void) { ptry(pthread_mutex_lock(&g.lock)) if (g.pidfile) { unlink(g.pidfile); mem_destroy(&g.pidfile); } ptry(pthread_mutex_unlock(&g.lock)) return 0; } /* =item C Return the process id of the daemon with the given C. If the daemon in question is owned by I, then this function must be invoked by I. Similarly, if the daemon in question is owned by an ordinary user, then this function must be invoked by an ordinary user. If C is the absolute path to the pidfile (rather than just the daemon name), then any user may call this function. On success, returns the process id of the daemon. On error, returns C<-1> with C set appropriately. =cut */ pid_t daemon_getpid(const char *name) { char *pidfile = NULL; char buf[BUFSIZ]; ssize_t bytes; int pid_fd; int pid = 0; /* Check argument */ if (!name) return set_errno(EINVAL); /* Build the pidfile path */ if (daemon_construct_pidfile(name, &pidfile) == -1) return -1; /* Open the pidfile */ pid_fd = open(pidfile, O_RDONLY); mem_release(pidfile); if (pid_fd == -1) return -1; /* Read it */ bytes = read(pid_fd, buf, BUFSIZ); close(pid_fd); if (bytes == -1) return -1; if (sscanf(buf, "%d", &pid) != 1) return -1; return (pid_t)pid; } /* =item C Checks whether or not a daemon with the given C is running. If the daemon in question is owned by I, then this function must be invoked by I. Similarly, if the daemon in question is owned by an ordinary user, then this function must be invoked by an ordinary user. However, if C is the absolute path to the pidfile (rather than just the daemon name), then any user may call this function. On success, returns C<1> if the daemon is running or C<0> if it is not. On error, returns C<-1> with C set appropriately. =cut */ int daemon_is_running(const char *name) { char *pidfile = NULL; int pid_fd; /* Check argument */ if (!name) return set_errno(EINVAL); /* Build the pidfile path */ if (daemon_construct_pidfile(name, &pidfile) == -1) return -1; /* Open the pidfile to see if it exists */ if ((pid_fd = open(pidfile, O_RDONLY)) == -1) { mem_release(pidfile); if (errno != ENOENT) return -1; /* The pidfile doesn't exist, so the daemon probably isn't running */ return 0; } /* Is the pidfile write-locked? If so, the following will fail */ if (fcntl_lock(pid_fd, F_SETLK, F_RDLCK, SEEK_SET, 0, 0) == -1) { mem_release(pidfile); close(pid_fd); if (errno != EACCES && errno != EAGAIN) return -1; return 1; } mem_release(pidfile); close(pid_fd); /* Not write-locked - daemon is not running */ return 0; } /* =item C Stop a daemon process with the given C by sending it a C signal. If the daemon in question is owned by I, then this function must be invoked by I. Similarly, if the daemon in question is owned by an ordinary user, then this function must be invoked by that user. Note that I can't use this function to stop a daemon started by another user just by passing the name of the daemon (because the pidfiles for I daemons and user daemons are stored in different directories). In order for I to stop an ordinary user's daemon process, C has to be the absolute path to the daemon's pidfile. On success, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int daemon_stop(const char *name) { char *pidfile = NULL; char pidbuf[32]; ssize_t bytes; int pid_fd; int pid = -1; /* Check argument */ if (!name) return set_errno(EINVAL); /* Build the pidfile path */ if (daemon_construct_pidfile(name, &pidfile) == -1) return -1; /* Open it and lock it (if possible) */ if ((pid_fd = daemon_lock_pidfile(pidfile)) == -1) { /* Already locked - daemon is running */ if (errno == EACCES || errno == EAGAIN) { /* Read the process id */ if ((pid_fd = open(pidfile, O_RDONLY)) == -1) { mem_release(pidfile); return -1; } mem_release(pidfile); if ((bytes = read(pid_fd, pidbuf, 32)) <= 0) { close(pid_fd); return -1; } close(pid_fd); if (sscanf(pidbuf, "%d", &pid) != 1 || pid <= 0) return set_errno(EINVAL); /* Stop the daemon */ return kill((pid_t)pid, SIGTERM); } mem_release(pidfile); return -1; } /* Not locked - daemon is not running */ close(pid_fd); unlink(pidfile); mem_release(pidfile); return set_errno(ESRCH); } /* =back =head1 ERRORS Additional errors may be generated and returned from the underlying system calls. See their manual pages. =over 4 =item C An argument was invalid (e.g. C). =item C The C passed to I or I resulted in a path name that is too long for the intended filesystem. =item C I recursed too deeply (16 levels). =item C I found that there was no daemon running with the given name. =back =head1 MT-Level MT-Safe =head1 EXAMPLE This example reads and prints C with I, becomes a daemon and then sends a I message and then terminates. #include const char * const config_fname = "/etc/fstab"; List *config = NULL; void fstab_parser(void *obj, const char *path, char *line, size_t lineno) { char device[64], mount[64], fstype[64], opts[64]; int freq, passno; if (sscanf(line, "%s %s %s %s %d %d", device, mount, fstype, opts, &freq, &passno) != 6) fprintf(stderr, "Syntax Error in %s (line %d): %s\n", path, lineno, line); else { char *copy; printf("%s %s %s %s %d %d\n", device, mount, fstype, opts, freq, passno); if (!(copy = mem_strdup(line))) fprintf(stderr, "out of memory\n"); else if (!list_append(config, copy)) fprintf(stderr, "failed to add line %d to config\n", lineno); } } void hup(int signo) { list_remove_range(config, 0, -1); daemon_parse_config(config_fname, config, fstab_parser); } void term(int signo) { daemon_close(); exit(EXIT_SUCCESS); } void do_stuff() { // do stuff... syslog(LOG_DAEMON | LOG_DEBUG, "Here we are"); kill(getpid(), SIGTERM); signal_handle_all(); } int main(int ac, char **av) { if (daemon_revoke_privileges() == -1 || daemon_prevent_core() == -1 || daemon_path_is_safe(config_fname, NULL, 0) != 1 || (config = list_create(free)) == NULL || daemon_parse_config(config_fname, config, fstab_parser) == NULL || daemon_init(prog_basename(*av)) == -1 || signal_set_handler(SIGHUP, 0, hup) == -1 || signal_set_handler(SIGTERM, 0, term) == -1) return EXIT_FAILURE; do_stuff(); return EXIT_SUCCESS; // unreached } =head1 BUGS It is possible to obtain a controlling terminal under I (and even under I if I was not defined or C was defined when I is compiled). If anything calls I on a terminal device without the C flag, the process doing so will obtain a controlling terminal. Because I's pidfiles are created in a different directory (C) to those of ordinary users (C), it is possible for I and another user to use the same name for a daemon client. This shouldn't be a problem. It's probably desirable. But if it is a problem, recompile I and relink I so that all pidfiles are created in C by defining C and C to both be C. The exclusive creation and locking of the pidfile doesn't work correctly over NFS on Linux so pidfiles must reside locally. I ignores ACLs (so does I). It should probably treat a path as unsafe if there are any ACLs (allowing extra access) along the path. The functions I, I, I, I, I and I should probably all have the I prefix removed from their names. Their use is more general than just in daemons. =head1 SEE ALSO I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST #include #include #include "msg.h" #include "prog.h" #include "sig.h" typedef struct Pair1 Pair1; typedef struct Data1 Data1; typedef struct Data2 Data2; struct Data1 { int test; int i; Pair1 *pair; }; struct Pair1 { const char *service; const char *port; }; static Pair1 pairs[] = { { "echo", "7/tcp" }, { "echo", "7/udp" }, { "ftp", "21/tcp" }, { "ssh", "22/tcp" }, { "smtp", "25/tcp" }, { NULL, NULL } }; static const int final_pair = 5; static Data1 data1[1] = {{ 0, 0, pairs }}; struct Data2 { int test; int i; int j; const char *text; const char *results[3][8]; }; static Data2 data2[1] = { { 0, 0, 0, "\n" "# This is a comment\n" "\n" "line1 = word1 word2\n" "line2 = word3 \\\n" "\tword4 word5 \\ # a comment in a funny place\n" "\tword6 word7\n" "\n" "line3 = \\\n" "\tword8\n" "\n", { { "line1", "=", "word1", "word2", NULL, NULL, NULL, NULL }, { "line2", "=", "word3", "word4", "word5", "word6", "word7", NULL }, { "line3", "=", "word8", NULL, NULL, NULL, NULL, NULL } } } }; static const int final_line = 3; static const int final_word = 3; static int errors = 0; static int config_test1(int test, const char *name) { FILE *out = fopen(name, "w"); int i; if (!out) { ++errors, printf("Test%d: failed to create file: '%s'\n", test, name); return 0; } for (i = 0; data1->pair[i].service; ++i) fprintf(out, "%s %s\n", data1->pair[i].service, data1->pair[i].port); fclose(out); return 1; } static void parse_test1(void *obj, const char *path, char *line, size_t lineno) { Data1 *data1 = (Data1 *)obj; char service[BUFSIZ]; char port[BUFSIZ]; if (sscanf(line, "%s %s", service, port) != 2) ++errors, printf("Test%d: syntax error: '%s' (file %s line %d)\n", data1->test, line, path, (int)lineno); else if (strcmp(service, data1->pair[data1->i].service)) ++errors, printf("Test%d: expected service '%s', received '%s' (file %s line %d)\n", data1->test, data1->pair[data1->i].service, service, path, (int)lineno); else if (strcmp(port, data1->pair[data1->i].port)) ++errors, printf("Test%d: expected port '%s', received '%s' (file %s line %d)\n", data1->test, data1->pair[data1->i].port, port, path, (int)lineno); ++data1->i; } static int config_test2(int test, const char *name) { FILE *out = fopen(name, "w"); if (!out) { ++errors, printf("Test%d: failed to create file: '%s'\n", test, name); return 0; } fprintf(out, "%s", data2->text); fclose(out); return 1; } static void parse_test2(void *obj, const char *path, char *line, size_t lineno) { Data2 *data2 = (Data2 *)obj; char word[8][BUFSIZ]; int words; words = sscanf(line, "%s %s %s %s %s %s %s %s", word[0], word[1], word[2], word[3], word[4], word[5], word[6], word[7]); for (data2->j = 0; data2->j < words; ++data2->j) { if (!data2->results[data2->i][data2->j]) { ++errors, printf("Test%d: too many words: '%s' (file %s line %d)\n", data2->test, line, path, (int)lineno); break; } if (strcmp(word[data2->j], data2->results[data2->i][data2->j])) { ++errors; printf("Test%d: expected '%s', received '%s' (file %s line %d)\n", data2->test, data2->results[data2->i][data2->j], word[data2->j - 1], path, (int)lineno); break; } } ++data2->i; } void term(int signo) { daemon_close(); exit(EXIT_SUCCESS); } int main(int ac, char **av) { const char *config_name; char *cwd; char *core = "core"; char *core2 = "daemon.core"; /* OpenBSD, FreeBSD */ int facility = LOG_DAEMON | LOG_ERR; pid_t pid; int rc; uid_t uid, euid; gid_t gid, egid; int no_privileges = 0; int not_safe = 0; int not_root = 0; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "daemon"); /* Test (a bit) daemon_started_by_init() and daemon_started_by_inetd() */ if ((rc = daemon_started_by_init()) != 0) ++errors, printf("Test1: daemon_started_by_init() failed (ret %d, not %d)\n", rc, 0); if ((rc = daemon_started_by_inetd()) != 0) ++errors, printf("Test2: daemon_started_by_inetd() failed (ret %d, not %d)\n", rc, 0); /* Test daemon_prevent_core() */ unlink(core); unlink(core2); switch (pid = fork()) { case -1: { ++errors, printf("Test3: Failed to run test: fork: %s\n", strerror(errno)); break; } case 0: { if (daemon_prevent_core() == -1) { printf("Test3: daemon_prevent_core() failed: %s\n", strerror(errno)); return 1; } dump(""); } default: { struct stat statbuf[1]; int status; if (waitpid(pid, &status, 0) == -1) { printf("Test3: Failed to evaluate test: waitpid: %s\n", strerror(errno)); break; } #ifndef WCOREDUMP #define WCOREDUMP(status) 0 #endif if (WIFEXITED(status) && WEXITSTATUS(status)) ++errors; else if (WCOREDUMP(status) && (stat(core, statbuf) == 0 || stat(core2, statbuf)) == 0) ++errors, printf("Test3: child dumped core\n"); unlink(core); unlink(core2); } } /* Test daemon_revoke_privileges() if possible */ uid = getuid(); gid = getgid(); euid = geteuid(); egid = getegid(); if (euid == uid && egid == gid) no_privileges = 1; else if (daemon_revoke_privileges() == -1 || geteuid() != getuid() || getegid() != getgid()) ++errors, printf("Test4: daemon_revoke_privileges() failed: %s\n", strerror(errno)); /* Test daemon_become_user() if possible */ if (uid) not_root = 1; else { switch (pid = fork()) { case -1: { ++errors, printf("Test5: Failed to run test: fork: %s\n", strerror(errno)); break; } case 0: { gid_t gids[10]; errno = 0; if (daemon_become_user(1, 1, NULL) == -1) { printf("Test5: daemon_become_user(1, 1, NULL) failed: %s\n", strerror(errno)); return 1; } if (geteuid() != 1 || getuid() != 1 || getegid() != 1 || getgid() != 1) { printf("Test5: daemon_become_user(1, 1, NULL) failed: euid/egid = %d/%d, uid/gid = %d/%d\n", (int)geteuid(), (int)getegid(), (int)getuid(), (int)getgid()); return 1; } if (getgroups(0, NULL) != 0 && (getgroups(10, gids) != 1 || gids[0] != getuid())) { printf("Test5: daemon_become_user(1, 1, NULL) failed: getgroups() = %d (not 0 or 1)\n", getgroups(0, NULL)); return 1; } return 0; } default: { int status; if (waitpid(pid, &status, 0) == -1) { printf("Test5: Failed to evaluate test: waitpid: %s\n", strerror(errno)); break; } if (WIFEXITED(status) && WEXITSTATUS(status)) ++errors; } } } /* Test daemon_absolute_path() */ #define TEST_ABSOLUTE_PATH(i, path, abs_path) \ { \ char *result = daemon_absolute_path(path); \ if (!result) \ ++errors, printf("Test%d: absolute_path(%s) failed (%s)\n", (i), (path), strerror(errno)); \ else if (strcmp(result, (abs_path))) \ { \ struct stat result_status[1], abs_status[1]; \ ++errors, printf("Test%d: absolute_path(%s) failed (was %s, not %s)\n", (i), (path), result, (abs_path)); \ printf("\n"); \ if (stat(result, result_status) != -1 && stat(abs_path, abs_status) != -1 && result_status->st_ino == abs_status->st_ino) \ printf(" But they have the same inode (%d)\n", (int)abs_status->st_ino); \ printf(" Does your pwd return canonical paths?\n\n"); \ free(result); \ } \ } /* We must be in a safe, writable directory to test relative paths */ if (!(cwd = mem_create(limit_path(), char))) ++errors, printf("Test6: Failed to run test: mem_create: %s\n", strerror(errno)); else if (!getcwd(cwd, limit_path())) ++errors, printf("Test6: Failed to run test: getcwd: %s\n", strerror(errno)); else if (chdir("/etc") == -1) ++errors, printf("Test6: Failed to run test: chdir: %s\n", strerror(errno)); else { TEST_ABSOLUTE_PATH(6, ".", "/etc") TEST_ABSOLUTE_PATH(7, "..", "/") TEST_ABSOLUTE_PATH(8, "/", "/") TEST_ABSOLUTE_PATH(9, "/etc/passwd", "/etc/passwd") TEST_ABSOLUTE_PATH(10, "/.", "/") TEST_ABSOLUTE_PATH(11, "/..", "/") TEST_ABSOLUTE_PATH(12, "/./etc", "/etc") TEST_ABSOLUTE_PATH(13, "/../etc", "/etc") TEST_ABSOLUTE_PATH(14, "/etc/.././.././../usr", "/usr") TEST_ABSOLUTE_PATH(15, "../../../../../etc/././../etc/./.././etc", "/etc") TEST_ABSOLUTE_PATH(16, "././../../../../../etc/././.", "/etc") TEST_ABSOLUTE_PATH(17, "/etc/./sysconfig/./network-scripts/../blog/..", "/etc/sysconfig") TEST_ABSOLUTE_PATH(18, "/etc/./sysconfig/./network-scripts/../blog/../..", "/etc") TEST_ABSOLUTE_PATH(19, "passwd", "/etc/passwd") TEST_ABSOLUTE_PATH(20, "passwd/", "/etc/passwd") TEST_ABSOLUTE_PATH(21, "passwd////", "/etc/passwd") TEST_ABSOLUTE_PATH(22, "///////////////", "/") TEST_ABSOLUTE_PATH(23, "///////etc////////", "/etc") TEST_ABSOLUTE_PATH(24, "//////./.././..////..//", "/") chdir(cwd); } /* Test daemon_path_is_safe() */ #define TEST_PATH_IS_SAFE(i, path, safe, err, explanation) \ { \ int rc; \ char buf[128]; \ errno = 0; \ if ((rc = daemon_path_is_safe(path, buf, 128)) != (safe)) \ { \ struct stat status[1]; \ ++errors, printf("Test%d: daemon_path_is_safe(%s) failed (ret %d, not %d) %s\n", (i), (path), rc, (safe), errno ? strerror(errno) : ""); \ if (stat(ROOT_DIR, status) != -1 && status->st_mode & (S_IWGRP | S_IWOTH)) \ printf("\n No Wonder! Your %s directory is %s writable!!!\n\n", ROOT_DIR, (status->st_mode & S_IWOTH) ? "world" : "group"); \ } \ else if (rc == -1 && errno != (err)) \ ++errors, printf("Test%d: daemon_path_is_safe(%s) failed (errno was %d, not %d)\n", (i), (path), errno, (err)); \ else if (rc == 0 && strcmp(buf + strlen(buf) - strlen(explanation), explanation)) \ ++errors, printf("Test%d: daemon_path_is_safe(%s) failed (explanation was \"%s\", not \"%s\")\n", (i), (path), buf, (explanation)); \ } TEST_PATH_IS_SAFE(25, "/etc/passwd", 1, 0, "") TEST_PATH_IS_SAFE(26, "/tmp", 0, 0, "/tmp is group and world writable") TEST_PATH_IS_SAFE(27, "/nonexistent-path", -1, ENOENT, "") if (daemon_path_is_safe(".", NULL, 0) != 1) not_safe = 1; else { const char *sym_link = "daemon_path_is_safe.test"; const char *sym_linked = "/tmp/daemon_path_is_safe.test"; mode_t mask; int fd; /* Test absolute link from safe directory to unsafe directory */ if ((fd = open(sym_linked, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) ++errors, printf("Test28: Failed to run test: open(%s) failed %s\n", sym_linked, strerror(errno)); else { close(fd); TEST_PATH_IS_SAFE(28, sym_linked, 0, 0, "/tmp is group and world writable") if (symlink(sym_linked, sym_link) == -1) ++errors, printf("Test28: Failed to run test: symlink(%s, %s) failed %s\n", sym_linked, sym_link, strerror(errno)); else { TEST_PATH_IS_SAFE(28, sym_link, 0, 0, "/tmp is group and world writable") if (unlink(sym_link) == -1) ++errors, printf("Test28: Failed to unlink(%s): %s\n", sym_link, strerror(errno)); } if (unlink(sym_linked) == -1) ++errors, printf("Test28: Failed to unlink(%s): %s\n", sym_linked, strerror(errno)); } /* Test relative symbolic link from safe directory to safe directory */ if (mkdir("safedir", S_IRUSR | S_IWUSR | S_IXUSR) == -1) ++errors, printf("Test29: Failed to run test: mkdir(%s) failed: %s\n", "safedir", strerror(errno)); else { if (symlink("..", "safedir/safelink") == -1) ++errors, printf("Test29: symlink(.., safedir/safelink) failed: %s\n", strerror(errno)); else { TEST_PATH_IS_SAFE(29, "safedir/safelink", 1, 0, "") if (unlink("safedir/safelink") == -1) ++errors, printf("Test29: Failed to unlink(safedir/safelink): %s\n", strerror(errno)); } if (rmdir("safedir") == -1) ++errors, printf("Test29: Failed to rmdir(safedir): %s\n", strerror(errno)); } /* Test relative symbolic link from safe directory to unsafe directory */ if (mkdir("safedir", S_IRUSR | S_IWUSR | S_IXUSR) == -1) ++errors, printf("Test30: Failed to run test: mkdir(safedir) failed: %s\n", strerror(errno)); else { mask = umask((mode_t)0); if (mkdir("unsafedir", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) == -1) ++errors, printf("Test30: Failed to run test: mkdir(unsafedir) failed: %s\n", strerror(errno)); else { if (symlink("../unsafedir", "safedir/unsafelink") == -1) ++errors, printf("Test30: symlink(../unsafedir, safedir/unsafelink) failed: %s\n", strerror(errno)); else { TEST_PATH_IS_SAFE(30, "safedir/unsafelink", 0, 0, "/unsafedir is group writable") if (unlink("safedir/unsafelink") == -1) ++errors, printf("Test30: Failed to unlink(safedir/unsafelink): %s\n", strerror(errno)); } if (rmdir("unsafedir") == -1) ++errors, printf("Test30: Failed to rmdir(unsafedir): %s\n", strerror(errno)); } if (rmdir("safedir") == -1) ++errors, printf("Test30: Failed to rmdir(safedir): %s\n", strerror(errno)); umask(mask); } /* Test relative symbolic link from unsafe directory to safe directory */ mask = umask((mode_t)0); if (mkdir("unsafedir", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) == -1) ++errors, printf("Test31: Failed to run test: mkdir(unsafedir) failed: %s\n", strerror(errno)); else { if (symlink("unsafedir", "unsafelink") == -1) ++errors, printf("Test31: symlink(../unsafedir, unsafelink) failed: %s\n", strerror(errno)); else { if (mkdir("unsafelink/unsafedir", S_IRUSR | S_IWUSR | S_IXUSR) == -1) ++errors, printf("Test31: Failed to run test: mkdir(unsafelink/unsafedir) failed: %s\n", strerror(errno)); else { TEST_PATH_IS_SAFE(31, "unsafelink/unsafedir", 0, 0, "/unsafedir is group writable") if (rmdir("unsafelink/unsafedir") == -1) ++errors, printf("Test31: Failed to rmdir(unsafelink/unsafedir): %s\n", strerror(errno)); } if (unlink("unsafelink") == -1) ++errors, printf("Test31: Failed to unlink(unsafelink): %s\n", strerror(errno)); } if (rmdir("unsafedir") == -1) ++errors, printf("Test31: Failed to rmdir(unsafedir): %s\n", strerror(errno)); } umask(mask); } /* Test daemon_parse_config() */ config_name = "daemon_parse_config.testfile"; if (config_test1(32, config_name)) { int errors_save = errors; data1->test = 32; daemon_parse_config(config_name, data1, parse_test1); if (errors == errors_save && data1->i != final_pair) ++errors, printf("Test32: failed to parse entire config file\n"); unlink(config_name); } if (config_test2(33, config_name)) { int errors_save = errors; data2->test = 33; daemon_parse_config(config_name, data2, parse_test2); if (errors == errors_save && (data2->i != final_line || data2->j != final_word)) ++errors, printf("Test33: failed to parse entire config file\n"); unlink(config_name); } /* Test daemon_init() and daemon_close() */ switch (pid = fork()) { case -1: { ++errors, printf("Test34: Failed to run test: fork: %s\n", strerror(errno)); break; } case 0: { if (signal_set_handler(SIGTERM, 0, term) == -1) { syslog(facility, "%s: Test34: signal_set_handler(SIGTERM) failed: %s", *av, strerror(errno)); _exit(EXIT_FAILURE); } if (daemon_init(prog_basename(*av)) == -1) { syslog(facility, "%s: Test34: daemon_init(\"%s\") failed: %s", *av, prog_basename(*av), strerror(errno)); _exit(EXIT_FAILURE); } syslog(facility, "%s succeeded", *av); if (kill(getpid(), SIGTERM) == -1) syslog(facility, "%s: Test34: kill(%d, SIGTERM) failed: %s", *av, pid, strerror(errno)); signal_handle_all(); /* ** We can only get here if the signal hasn't arrived yet. ** If so, exit anyway. */ syslog(facility, "%s: Test34: signal_handle_all() failed", *av); _exit(EXIT_SUCCESS); } default: { int status; if (waitpid(pid, &status, 0) == -1) { printf("Test34: Failed to evaluate test: waitpid: %s\n", strerror(errno)); break; } if (WIFEXITED(status) && WEXITSTATUS(status)) ++errors, printf("Test34: Failed to run test: signal_set_handler() failed\n"); } } if (errors) printf("%d/34 tests failed\n", errors); else printf("All tests passed\n"); printf("\n"); printf(" Note: Can't verify syslog daemon.err output (don't know where it goes).\n"); printf(" Look for \"%s succeeded\" (not \"%s failed\").\n", *av, *av); if (no_privileges) { printf("\n"); printf(" Note: Can't test daemon_revoke_privileges().\n"); printf(" Rerun test suid and/or sgid as someone other than the tester.\n"); } if (not_safe) { printf("\n"); printf(" Note: Can't perform all tests on daemon_path_is_safe().\n"); printf(" Rerun test from a safe directory (writable by the tester).\n"); } if (not_root) { printf("\n"); printf(" Note: Can't test daemon_become_user().\n"); printf(" Audit the code and rerun the test as root.\n"); } return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/daemon.h000066400000000000000000000040521140522741300161640ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_DAEMON_H #define LIBSLACK_DAEMON_H #include #ifndef ROOT_PID_DIR #define ROOT_PID_DIR "/var/run" #endif #ifndef USER_PID_DIR #define USER_PID_DIR "/tmp" #endif #ifndef ROOT_DIR #define ROOT_DIR "/" #endif #ifndef ETC_DIR #define ETC_DIR "/etc" #endif #ifndef PATH_SEP #define PATH_SEP '/' #endif #ifndef PATH_SEP_STR #define PATH_SEP_STR "/" #endif #ifndef PATH_LIST_SEP #define PATH_LIST_SEP ':' #endif typedef void daemon_config_parser_t(void *obj, const char *path, char *line, size_t lineno); _begin_decls int daemon_started_by_init(void); int daemon_started_by_inetd(void); int daemon_prevent_core(void); int daemon_revoke_privileges(void); int daemon_become_user(uid_t uid, gid_t gid, char *user); char *daemon_absolute_path(const char *path); int daemon_path_is_safe(const char *path, char *explanation, size_t explanation_size); void *daemon_parse_config(const char *path, void *obj, daemon_config_parser_t *parser); int daemon_pidfile(const char *name); int daemon_init(const char *name); int daemon_close(void); pid_t daemon_getpid(const char *name); int daemon_is_running(const char *name); int daemon_stop(const char *name); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/err.c000066400000000000000000000754431140522741300155200ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - message/error/debug/verbosity/alert messaging module =head1 SYNOPSIS #include #include void msg(const char *format, ...); void vmsg(const char *format, va_list args); void verbose(size_t level, const char *format, ...); void vverbose(size_t level, const char *format, va_list args); void debugf(size_t level, const char *format, ...); void vdebugf(size_t level, const char *format, va_list args); int error(const char *format, ...); int verror(const char *format, va_list args); void fatal(const char *format, ...); void vfatal(const char *format, va_list args); void dump(const char *format, ...); void vdump(const char *format, va_list args); void alert(int priority, const char *format, ...); void valert(int priority, const char *format, va_list args); void debugsysf(size_t level, const char *format, ...); void vdebugsysf(size_t level, const char *format, va_list args); int errorsys(const char *format, ...); int verrorsys(const char *format, va_list args); void fatalsys(const char *format, ...); void vfatalsys(const char *format, va_list args); void dumpsys(const char *format, ...); void vdumpsys(const char *format, va_list args); void alertsys(int priority, const char *format, ...); void valertsys(int priority, const char *format, va_list args); int set_errno(int errnum); void *set_errnull(int errnum); void (*(set_errnullf)(int errnum))(); #define debug(args) #define vdebug(args) #define debugsys(args) #define vdebugsys(args) #define check(test, mesg) =head1 DESCRIPTION This module works with the I and I modules to provide functions for emitting various types of message with simple call syntax and flexible behaviour. The message types catered for are: normal, verbose, debug, error, fatal error, dump and alert messages. All messages are created and sent with I-like syntax. The destinations for these messages are configurable by the client. Calling I causes normal and verbose messages to be sent to standard output; debug, error, fatal error, dump and alert messages to be sent to standard error. Calls to I, I, I, I, I and I cause normal and verbose messages to be sent to the specified destination. Calls to I, I, I, I, I and I cause error, fatal error and dump messages to be sent to the specified destination. Calls to I, I, I, I, I, I, I cause debug messages to be sent to the specified destination. Calls to I, I, I, I, I, I, I cause alert messages to be sent to the specified destination. Calls to the generic functions I, I, I and I cause their respective message types to be sent to the specified destination or destinations (multiplexing messages is only possible via these functions). See I for more details. =over 4 =cut */ #ifndef _BSD_SOURCE #define _BSD_SOURCE /* For snprintf() on OpenBSD-4.7 */ #endif #include "config.h" #include "std.h" #include "msg.h" #include "prog.h" #include "err.h" #ifndef HAVE_SNPRINTF #include "snprintf.h" #endif #ifndef TEST /* =item C Outputs a message to the program's normal message destination. C is a I-like format string and processes any remaining arguments in the same way as I. B msg(buf); // EVIL msg("%s", buf); // GOOD =cut */ void msg(const char *format, ...) { va_list args; va_start(args, format); vmsg(format, args); va_end(args); } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vmsg(const char *format, va_list args) { vmsg_out(prog_out(), format, args); } /* =item C Outputs a verbose message to the program's normal message destination if C is less than or equal to the program's current verbosity level. If the program's name has been supplied using I, the message will be preceded by the name, a colon and a space. The message is also preceded by as many spaces as the message level. This indents messages according to their verbosity. C is a I-like format string and processes any remaining arguments in the same way as I. The message is followed by a newline. =cut */ void verbose(size_t level, const char *format, ...) { if (prog_verbosity_level() >= level) { va_list args; va_start(args, format); vverbose(level, format, args); va_end(args); } } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vverbose(size_t level, const char *format, va_list args) { if (prog_verbosity_level() >= level) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); if (prog_name()) msg_out(prog_out(), "%s: %*s%s\n", prog_name(), level, "", mesg); else msg_out(prog_out(), "%*s%s\n", level, "", mesg); } } /* =item C Outputs a debug message to the program's debug message destination if C satisfies the program's current debug level. The debug level is broken into two components. The low byte specifies the level. The next three bytes specify a section within which the level applies. Debug messages with a section value whose bits overlap those of the program's current debug section and with a level that is less than or equal to the program's current debug level are emitted. As a convenience, if the program's current debug section is zero, debug messages with a sufficiently small level are emitted regardless of the message section. See I for examples. If the program's name has been supplied using I, the message will be preceded by the name, a colon and a space. The message is also preceded by as many spaces as the debug level. This indents debug messages according to their debug level. C is a I-like format string and processes any remaining arguments in the same way as I. The message is followed by a newline. =cut */ static int debug_level_match(size_t level) { size_t debug_level, debug_section, section; debug_level = prog_debug_level(); debug_section = debug_level & 0xffffff00; debug_level &= 0x000000ff; section = level & 0xffffff00; level &= 0x000000ff; return (!debug_section || debug_section & section) && debug_level >= level; } void debugf(size_t level, const char *format, ...) { if (debug_level_match(level)) { va_list args; va_start(args, format); vdebugf(level, format, args); va_end(args); } } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdebugf(size_t level, const char *format, va_list args) { if (debug_level_match(level)) { char mesg[MSG_SIZE], prefix[32] = ""; vsnprintf(mesg, MSG_SIZE, format, args); if (level & 0xffffff00) snprintf(prefix, 32, " [%d]", (int)((level & 0xffffff00) >> 8)); if (prog_name()) msg_out(prog_dbg(), "%s: debug:%s%*s%s\n", prog_name(), prefix, level & 0xff, "", mesg); else msg_out(prog_dbg(), "debug:%s%*s%s\n", prefix, level & 0xff, "", mesg); } } /* =item C Outputs an error message to the program's error message destination. If the program's name has been supplied using I, the message will be preceded by the name, a colon and a space. C is a I-like format string and processes any remaining arguments in the same way as I. The message is followed by a newline. Returns -1. =cut */ int error(const char *format, ...) { va_list args; va_start(args, format); verror(format, args); va_end(args); return -1; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ int verror(const char *format, va_list args) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); if (prog_name()) msg_out(prog_err(), "%s: %s\n", prog_name(), mesg); else msg_out(prog_err(), "%s\n", mesg); return -1; } /* =item C Outputs an error message to the program's error message destination and then calls I with a return code of C. If the program's name was supplied using I, the message will be preceded by the name, a colon and a space. This is followed by the string C<"fatal: ">. C is a I-like format string and processes any remaining arguments in the same way as I. The message is followed by a newline. B Never use this in a library. Only an application can decide which errors are fatal. =cut */ void fatal(const char *format, ...) { va_list args; va_start(args, format); vfatal(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vfatal(const char *format, va_list args) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); error("fatal: %s", mesg); exit(EXIT_FAILURE); } /* =item C Outputs an error message to the program's error message destination and then calls I. If the program's name was supplied using I, the message will be preceded by the name, a colon and a space. This is followed by the string C<"dump: ">. C is a I-like format string and processes any remaining arguments in the same way as I. The message is followed by a newline. B Never use this in a library. Only an application can decide which errors are fatal. =cut */ void dump(const char *format, ...) { va_list args; va_start(args, format); vdump(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdump(const char *format, va_list args) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); error("dump: %s", mesg); abort(); } /* =item C Outputs an alert message of the given C to the program's alert message destination. If the program's name has been supplied using I, the message will be preceded by the name, a colon and a space. C is a I-like format string and processes any remaining arguments in the same way as I. The message is followed by a newline. Note that this only works when the program's alert message destination is a simple syslog destination. If the alert message destination is anything else (including a multiplexing message destination containing syslog destinations), C is ignored. =cut */ void alert(int priority, const char *format, ...) { va_list args; va_start(args, format); valert(priority, format, args); va_end(args); } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void valert(int priority, const char *format, va_list args) { Msg *alert; char mesg[MSG_SIZE]; int err; vsnprintf(mesg, MSG_SIZE, format, args); alert = prog_alert(); if ((err = msg_wrlock(alert))) { set_errno(err); return; } msg_syslog_set_priority_unlocked(alert, priority); if (prog_name()) msg_out_unlocked(alert, "%s: %s\n", prog_name(), mesg); else msg_out_unlocked(alert, "%s\n", mesg); if ((err = msg_unlock(alert))) set_errno(err); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C and a newline (rather than just a newline). =cut */ void debugsysf(size_t level, const char *format, ...) { if (debug_level_match(level)) { va_list args; va_start(args, format); vdebugsysf(level, format, args); va_end(args); } } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdebugsysf(size_t level, const char *format, va_list args) { if (debug_level_match(level)) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); debugf(level, "%s: %s", mesg, strerror(errno_saved)); } } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C and a newline (rather than just a newline). =cut */ int errorsys(const char *format, ...) { va_list args; va_start(args, format); verrorsys(format, args); va_end(args); return -1; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ int verrorsys(const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); return error("%s: %s", mesg, strerror(errno_saved)); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C and a newline (rather than just a newline). =cut */ void fatalsys(const char *format, ...) { va_list args; va_start(args, format); vfatalsys(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vfatalsys(const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); fatal("%s: %s", mesg, strerror(errno_saved)); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C and a newline (rather than just a newline). =cut */ void dumpsys(const char *format, ...) { va_list args; va_start(args, format); vdumpsys(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdumpsys(const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); dump("%s: %s", mesg, strerror(errno_saved)); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C and a newline (rather than just a newline). =cut */ void alertsys(int priority, const char *format, ...) { va_list args; va_start(args, format); valertsys(priority, format, args); va_end(args); } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void valertsys(int priority, const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); alert(priority, "%s: %s", mesg, strerror(errno_saved)); } /* =item C Sets C to C and returns -1. =cut */ int (set_errno)(int errnum) { errno = errnum; return -1; } /* =item C Sets C to C and returns C. =cut */ void *(set_errnull)(int errnum) { errno = errnum; return NULL; } /* =item C Sets C to C and returns C as a function pointer. This is useful because ISO C doesn't like casting normal pointers into function pointers. =cut */ void (*(set_errnullf)(int errnum))() { errno = errnum; return NULL; } /* =item C< #define debug(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. debug((1, "rc=%d", rc)) =item C< #define vdebug(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. vdebug((1, format, args)) =item C< #define debugsys(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. debugsys((1, "fd=%d", fd)) =item C< #define vdebugsys(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. vdebugsys((1, format, args)) =item C< #define check(cond, mesg)> Like I but includes a string argument, C, for including an explanation of the condition tested and then calls I to terminate the program. This means the message will be sent to the right place(s) rather than just to C. B Like I, this function is largely useless. It should never be left in production code (because it's rude) so you need to write code to handle error conditions properly anyway. You might as well not bother using I or I in the first place. =back =head1 MT-Level MT-Safe =head1 EXAMPLES Send a range of messages to default locations: #include #include #include int main(int ac, char **av) { prog_init(); prog_set_debug_level(1); prog_set_verbosity_level(1); msg("This is a %s\n", "message"); verbose(0, "This is a %s message (level %d)", "verbose", 0); verbose(1, "This is a %s message (level %d)", "verbose", 1); verbose(2, "This is a %s message (level %d)", "verbose", 2); debug((0, "This is a %s message (level %d)", "debug", 0)) debug((1, "This is a %s message (level %d)", "debug", 1)) debug((2, "This is a %s message (level %d)", "debug", 2)) alert(LOG_ERR, "This is an %s message", "alert"); error("This is an %s message", "error"); fatal("This is a %s message", "fatal error"); return EXIT_SUCCESS; } =head1 SEE ALSO I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST #include #include #include #include int verify(int test, const char *name, const char *result) { char buf[BUFSIZ]; int fd; ssize_t bytes; if ((fd = open(name, O_RDONLY)) == -1) { printf("Test%d: failed to create err file: %s (%s)\n", test, name, strerror(errno)); return 1; } memset(buf, 0, BUFSIZ); bytes = read(fd, buf, BUFSIZ); close(fd); unlink(name); if (bytes == -1) { printf("Test%d: failed to read err file: %s (%s)\n", test, name, strerror(errno)); return 1; } if (!strstr(buf, result)) { printf("Test%d: err file produced incorrect input:\nshould contain:\n%s\nwas:\n%s\n", test, result, buf); return 1; } return 0; } int verifysys(int test, const char *name, const char *result, int err) { char buf[BUFSIZ]; snprintf(buf, BUFSIZ, result, strerror(err)); return verify(test, name, buf); } int main(int ac, char **av) { const char * const out = "err.out"; const char * const err = "err.err"; const char * const dbg = "err.dbg"; const char * const alertfile = "err.alert"; const char * const core = "core"; const char * const core2 = "err.core"; /* OpenBSD */ char buf[BUFSIZ]; int rci; void *rcp; void (*rcfp)(); const char *results[12] = { "msg\n", "verbosemsg\n", "debug: debugmsg\n", "errormsg\n", "fatal: fatalmsg\n", "dump: dumpmsg\n", "debug: debugsysmsg: %s\n", "errorsysmsg: %s\n", "fatal: fatalsysmsg: %s\n", "dump: dumpsysmsg: %s\n", "debug: [1] lexer debugmsg\n" "debug: [2] parser debugmsg\n" "debug: [1] lexer debugmsg\n" "debug: [2] parser debugmsg\n" "debug: [4] interp debugmsg\n" "debug: global debugmsg\n", "alertmsg\n" "alertsysmsg: " /* followed by "Success" or "Error 0" */ }; pid_t pid; int errors = 0; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "err"); /* Test debug, verbose and error */ prog_set_debug_level(1); prog_set_verbosity_level(1); prog_out_file(out); msg("msg\n"); errors += verify(1, out, results[0]); prog_out_file(out); verbose(1, "verbosemsg"); errors += verify(2, out, results[1]); prog_dbg_file(dbg); debugf(1, "debugmsg"); errors += verify(3, dbg, results[2]); prog_err_file(err); error("errormsg"); errors += verify(4, err, results[3]); /* Test fatal */ switch (pid = fork()) { case 0: { prog_err_file(err); fatal("fatalmsg"); } case -1: { ++errors; printf("Test5: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors, printf("Test5: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_FAILURE) ++errors, printf("Test5: failed: %s %d\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status)); } } errors += verify(5, err, results[4]); /* Test dump */ switch (pid = fork()) { case 0: { prog_err_file(err); unlink(core); unlink(core2); dump("dumpmsg"); } case -1: { ++errors; printf("Test6: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { struct stat statbuf[1]; int status; if (waitpid(pid, &status, 0) == -1) { ++errors, printf("Test6: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) ++errors, printf("Test5: failed: %s %d\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status)); if (stat(core, statbuf) == -1 && errno == ENOENT && stat(core2, statbuf) == -1 && errno == ENOENT) ++errors, printf("Test6: failed: no core file produced (ulimit?)\n"); else { unlink(core); unlink(core2); } } } errors += verify(6, err, results[5]); /* Test debugsys, errorsys */ prog_dbg_file(dbg); set_errno(EPERM); debugsysf(1, "debugsysmsg"); errors += verifysys(7, dbg, results[6], EPERM); prog_err_file(err); set_errno(ENOENT); errorsys("errorsysmsg"); errors += verifysys(8, err, results[7], ENOENT); /* Test fatalsys */ switch (pid = fork()) { case 0: { prog_err_file(err); set_errno(EPERM); fatalsys("fatalsysmsg"); } case -1: { ++errors; printf("Test9: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test9: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_FAILURE) ++errors, printf("Test5: failed: %s %d\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status)); } } errors += verifysys(9, err, results[8], EPERM); /* Test dumpsys */ switch (pid = fork()) { case 0: { prog_err_file(err); unlink(core); unlink(core2); set_errno(ENOENT); dumpsys("dumpsysmsg"); } case -1: { ++errors; printf("Test10: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { struct stat statbuf[1]; int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test10: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) ++errors, printf("Test5: failed: %s %d\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status)); if (stat(core, statbuf) == -1 && errno == ENOENT && stat(core2, statbuf) == -1 && errno == ENOENT) ++errors, printf("Test10: failed: no core file produced (ulimit?)\n"); else { unlink(core); unlink(core2); } } } errors += verifysys(10, err, results[9], ENOENT); /* Test check true */ switch (pid = fork()) { case 0: { int i = 1; prog_err_file(err); unlink(core); unlink(core2); set_errno(ENOENT); check(i == 1, "checkmsg"); _exit(EXIT_SUCCESS); } case -1: { ++errors; printf("Test11: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test11: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) ++errors, printf("Test5: failed: %s %d\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status)); } } /* Test check false */ switch (pid = fork()) { case 0: { int i = 1; prog_err_file(err); unlink(core); unlink(core2); set_errno(ENOENT); check(i == 0, "checkmsg"); _exit(EXIT_SUCCESS); } case -1: { ++errors; printf("Test12: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { struct stat statbuf[1]; int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test12: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) ++errors, printf("Test5: failed: %s %d\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status)); if (stat(core, statbuf) == -1 && errno == ENOENT && stat(core2, statbuf) == -1 && errno == ENOENT) ++errors, printf("Test12: failed: no core file produced (ulimit?)\n"); else { unlink(core); unlink(core2); } } } snprintf(buf, BUFSIZ, "dump: Internal Error: %s: %s [", "i == 0", "checkmsg"); errors += verify(12, err, buf); /* Test debug sections */ #define LEXER_SECTION (1 << 8) #define PARSER_SECTION (2 << 8) #define INTERP_SECTION (4 << 8) prog_set_debug_level(LEXER_SECTION | PARSER_SECTION | 1); prog_dbg_file(dbg); msg_set_timestamp_format(""); debugf(LEXER_SECTION | 1, "lexer debugmsg"); /* yes */ debugf(LEXER_SECTION | 4, "lexer debugmsg"); /* no (level too high) */ debugf(PARSER_SECTION | 1, "parser debugmsg"); /* yes */ debugf(INTERP_SECTION | 1, "interp debugmsg"); /* no (wrong section) */ debugf(1, "global debugmsg"); /* no (no section to match) */ prog_set_debug_level(1); debugf(LEXER_SECTION | 1, "lexer debugmsg"); /* yes */ debugf(LEXER_SECTION | 4, "lexer debugmsg"); /* no (level too high) */ debugf(PARSER_SECTION | 1, "parser debugmsg"); /* yes */ debugf(INTERP_SECTION | 1, "interp debugmsg"); /* yes */ debugf(1, "global debugmsg"); /* yes */ debugf(4, "global debugmsg"); /* no (level too high) */ errors += verify(13, dbg, results[10]); #undef LEXER_SECTION #undef PARSER_SECTION #undef INTERP_SECTION /* Test alert() and alertsys() */ prog_alert_file(alertfile); alert(LOG_INFO, "alertmsg"); errno = 0; alertsys(LOG_INFO, "alertsysmsg"); errors += verify(14, alertfile, results[11]); prog_out_none(); prog_err_none(); prog_dbg_none(); prog_alert_none(); /* Test set_errno() and set_errnull() and set_errnullf() */ errno = 0; if ((rci = set_errno(EINVAL)) != -1) ++errors, printf("Test15: set_errno(EINVAL) failed (returned %d, not %d)\n", rci, -1); else if (errno != EINVAL) ++errors, printf("Test15: set_errno(EINVAL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); errno = 0; if ((rcp = set_errnull(EINVAL)) != NULL) ++errors, printf("Test16: set_errnull(EINVAL) failed (returned %p, not %p)\n", rcp, (void *)NULL); else if (errno != EINVAL) ++errors, printf("Test16: set_errnull(EINVAL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); errno = 0; if ((rcfp = set_errnullf(EINVAL)) != NULL) /* ++errors, printf("Test17: set_errnullf(EINVAL) failed (returned %p, not %p)\n", rcfp, (void *)NULL); */ ++errors, printf("Test17: set_errnullf(EINVAL) failed (returned something other than null)\n"); else if (errno != EINVAL) ++errors, printf("Test17: set_errnullf(EINVAL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (errors) printf("%d/17 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/err.h000066400000000000000000000061071140522741300155140ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_ERR_H #define LIBSLACK_ERR_H #include #include #include #if __cplusplus #define void_cast static_cast #else #define void_cast (void) #endif #undef debug #undef vdebug #undef debugsys #undef vdebugsys #undef check #ifdef NDEBUG #define debug(args) #define vdebug(args) #define debugsys(args) #define vdebugsys(args) #define check(cond, mesg) (void_cast(0)) #else #define debug(args) debugf args; #define vdebug(args) vdebugf args; #define debugsys(args) debugsysf args; #define vdebugsys(args) vdebugsysf args; #define check(cond, mesg) ((cond) ? void_cast(0) : (dump("Internal Error: %s: %s [%s:%d]", (#cond), (mesg), __FILE__, __LINE__))) #endif _begin_decls void msg(const char *format, ...); void vmsg(const char *format, va_list args); void verbose(size_t level, const char *format, ...); void vverbose(size_t level, const char *format, va_list args); void debugf(size_t level, const char *format, ...); void vdebugf(size_t level, const char *format, va_list args); int error(const char *format, ...); int verror(const char *format, va_list args); void fatal(const char *format, ...); void vfatal(const char *format, va_list args); void dump(const char *format, ...); void vdump(const char *format, va_list args); void alert(int priority, const char *format, ...); void valert(int priority, const char *format, va_list args); void debugsysf(size_t level, const char *format, ...); void vdebugsysf(size_t level, const char *format, va_list args); int errorsys(const char *format, ...); int verrorsys(const char *format, va_list args); void fatalsys(const char *format, ...); void vfatalsys(const char *format, va_list args); void dumpsys(const char *format, ...); void vdumpsys(const char *format, va_list args); void alertsys(int priority, const char *format, ...); void valertsys(int priority, const char *format, va_list args); int set_errno(int errnum); void *set_errnull(int errnum); void (*set_errnullf(int errnum))(); _end_decls /* Don't look below here - optimisations only */ /* And they produce lots of warnings with gcc-4.1 */ /* #define set_errno(errnum) (errno = (errnum), -1) */ /* #define set_errnull(errnum) ((void *)((void *)(errno = (errnum)), NULL)) */ #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/fio.c000066400000000000000000000641121140522741300154740ustar00rootroot00000000000000/* # libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - fifo and file control module and some I/O =head1 SYNOPSIS #include #include char *fgetline(char *line, size_t size, FILE *stream); char *fgetline_unlocked(char *line, size_t size, FILE *stream); int read_timeout(int fd, long sec, long usec); int write_timeout(int fd, long sec, long usec); int rw_timeout(int fd, long sec, long usec); int nap(long sec, long usec); int fcntl_set_flag(int fd, int flag); int fcntl_clear_flag(int fd, int flag); int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len); int nonblock_set(int fd, int arg); int nonblock_on(int fd); int nonblock_off(int fd); int fifo_exists(const char *path, int prepare); int fifo_has_reader(const char *path, int prepare); int fifo_open(const char *path, mode_t mode, int lock, int *writefd); =head1 DESCRIPTION This module provides various I/O related functions: reading a line of text no matter what line endings are used; timeouts for read/write operations without signals; exclusively opening a fifo for reading; and some random shorthand functions for manipulating file flags and locks. =over 4 =cut */ #include "config.h" #ifndef NO_POSIX_SOURCE #define NO_POSIX_SOURCE /* For ETIMEDOUT, EADDRINUSE, EOPNOTSUPP on FreeBSD-8.0 */ #endif #include "std.h" #include #include #if HAVE_SYS_SELECT_H #include #endif #include #include #include "err.h" #include "fio.h" #ifndef TEST void (flockfile)(FILE *stream); /* Missing from old glibc headers */ void (funlockfile)(FILE *stream); #ifndef HAVE_FLOCKFILE #define flockfile(stream) #define funlockfile(stream) #define getc_unlocked(stream) getc(stream) #endif /* =item C Similar to I except that it recognises UNIX (C<"\n">), DOS (C<"\r\n">) and Macintosh (C<"\r">) line endings (even different line endings in the same file). Reads characters from C and stores them in the buffer pointed to by C. Reading stops after an C or the end of the line is reached or when C characters have been stored. If the end of the line was reached, it is stored as a C<"\n"> character. A C is stored after the last character in the buffer. On success, returns C. On error, or when the end of file occurs while no characters have been read, returns C. Note that even when C is returned, C is modified and will always be C-terminated. So it is safe to examine C even when this function returns C. Calls to this function can be mixed with calls to other input functions from the I library for the same input stream. This is a drop-in replacement for I. char line[BUFSIZ]; while (fgetline(line, BUFSIZ, stdin)) printf("%s", line); =cut */ char *fgetline(char *line, size_t size, FILE *stream) { char *ret; flockfile(stream); ret = fgetline_unlocked(line, size, stream); funlockfile(stream); return ret; } /* =item C Equivalent to I except that C is not locked. =cut */ char *fgetline_unlocked(char *line, size_t size, FILE *stream) { char *s = line; char *end = line + size - 1; int c = '\0', c2; if (!s) return NULL; while (s < end && (c = getc_unlocked(stream)) != EOF) { if (c == '\n') { *s++ = c; break; } else if (c == '\r') { *s++ = '\n'; if ((c2 = getc_unlocked(stream)) == '\n') break; ungetc(c2, stream); break; } else *s++ = c; } *s = '\0'; if (c == EOF && (s == line || ferror(stream))) return NULL; return line; } /* =item C Performs a I on a single file descriptor, C, for reading and exceptions (i.e. arrival of urgent data), that times out after C seconds and C microseconds. This is just a shorthand function to provide a simple timed I (or I or I or I or I or I without resorting to I and C signals (best avoided). On success, returns C<0>. On error, returns C<-1> with C set appropriately (C if it timed out, otherwise set by I). Usage: if (read_timeout(fd, 5, 0) == -1 || (bytes = read(fd, buf, count)) == -1) return -1; =cut */ int read_timeout(int fd, long sec, long usec) { fd_set readfds[1]; fd_set exceptfds[1]; struct timeval timeout[1]; if (fd < 0 || sec < 0 || usec < 0) return set_errno(EINVAL); FD_ZERO(readfds); FD_SET(fd, readfds); *exceptfds = *readfds; timeout->tv_sec = sec; timeout->tv_usec = usec; switch (select(fd + 1, readfds, NULL, exceptfds, timeout)) { case -1: return -1; case 0: return set_errno(ETIMEDOUT); } return 0; } /* =item C Performs a I on a single file descriptor, C, for writing, that times out after C seconds and C microseconds. This is just a shorthand function to provide a simple timed I (or I or I or I or I) without resorting to I and C signals (best avoided). On success, returns C<0>. On error, returns C<-1> with C set appropriately (C if it timed out, otherwise set by I). Usage: if (write_timeout(fd, 5, 0) == -1 || (bytes = write(fd, buf, count)) == -1) return -1; =cut */ int write_timeout(int fd, long sec, long usec) { fd_set writefds[1]; struct timeval timeout[1]; if (fd < 0 || sec < 0 || usec < 0) return set_errno(EINVAL); FD_ZERO(writefds); FD_SET(fd, writefds); timeout->tv_sec = sec; timeout->tv_usec = usec; switch (select(fd + 1, NULL, writefds, NULL, timeout)) { case -1: return -1; case 0: return set_errno(ETIMEDOUT); } return 0; } /* =item C Performs a I on a single file descriptor, C, for reading, writing and exceptions (i.e. arrival of urgent data), that times out after C seconds and C microseconds. This is just a shorthand function to provide a simple timed I or I without resorting to I and C signals (best avoided). On success, returns a bit mask indicating whether C is readable (C), writable (C) and/or has urgent data available (C). On error, returns C<-1> with C set appropriately (C if it timed out, otherwise set by I). if ((mask = rw_timeout(fd, 5, 0)) == -1) return -1; if ((mask & W_OK) && (bytes = write(fd, buf, count)) == -1) return -1; if ((mask & R_OK) && (bytes = read(fd, buf, count)) == -1) return -1; =cut */ int rw_timeout(int fd, long sec, long usec) { fd_set readfds[1]; fd_set writefds[1]; fd_set exceptfds[1]; struct timeval timeout[1]; int rc = 0; if (fd < 0 || sec < 0 || usec < 0) return set_errno(EINVAL); FD_ZERO(readfds); FD_SET(fd, readfds); *writefds = *readfds; *exceptfds = *readfds; timeout->tv_sec = sec; timeout->tv_usec = usec; switch (select(fd + 1, readfds, writefds, exceptfds, timeout)) { case -1: return -1; case 0: return set_errno(ETIMEDOUT); } if (FD_ISSET(fd, readfds)) rc |= R_OK; if (FD_ISSET(fd, writefds)) rc |= W_OK; if (FD_ISSET(fd, exceptfds)) rc |= X_OK; return rc; } /* =item C Puts the process to sleep for C seconds and C microseconds. Note, however, that many systems' timers only have 10ms resolution. On success, returns C<0>. On error, returns C<-1> with C set appropriately. nap(1, 500000); // Sleep for 1.5 seconds nap(0, 100000); // Sleep for 0.1 seconds =cut */ int nap(long sec, long usec) { struct timeval tv[1]; if (sec < 0 || usec < 0) return set_errno(EINVAL); tv->tv_sec = sec; tv->tv_usec = usec; return select(0, NULL, NULL, NULL, tv); } /* =item C Shorthand for setting the file flag, C, on the file descriptor, C, using I. All other flags are unaffected. On success, returns C<0>. On error, returns C<-1> with C set by I with C or C as the command. =cut */ int fcntl_set_flag(int fd, int flag) { int flags; if ((flags = fcntl(fd, F_GETFL, 0)) == -1) return -1; return fcntl(fd, F_SETFL, flags | flag); } /* =item C Shorthand for clearing the file flag, C, from the file descriptor, C, using I. All other flags are unaffected. On success, returns C<0>. On error, returns C<-1> with C set by I with C or C as the command. =cut */ int fcntl_clear_flag(int fd, int flag) { int flags; if ((flags = fcntl(fd, F_GETFL, 0)) == -1) return -1; return fcntl(fd, F_SETFL, flags & ~flag); } /* =item C Shorthand for performing discretionary file locking operations on the file descriptor, C. C is the locking command and is passed to I. C, C, C and C are used to fill a I structure which is passed to I. Returns the same as I with C as the command. if (fcntl_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1) return -1; =cut */ int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len) { struct flock lock[1]; lock->l_type = type; lock->l_whence = whence; lock->l_start = start; lock->l_len = len; return fcntl(fd, cmd, lock); } /* =item C Sets non-blocking mode for the file descriptor, C, if C is non-zero. Sets blocking mode if C is zero. On success, returns C<0>. On error, returns C<-1> with C set by I with C or C as the command. =cut */ int nonblock_set(int fd, int arg) { return (arg) ? nonblock_on(fd) : nonblock_off(fd); } /* =item C Sets non-blocking mode for the file descriptor, C. On success, returns C<0>. On error, returns C<-1> with C set by I with C or C as the command. =cut */ int nonblock_on(int fd) { return fcntl_set_flag(fd, O_NONBLOCK); } /* =item C Sets blocking mode for the file descriptor, C. On success, returns C<0>. On error, returns C<-1> with C set by I with C or C as the command. =cut */ int nonblock_off(int fd) { return fcntl_clear_flag(fd, O_NONBLOCK); } /* =item C Determines whether or not C refers to a fifo. Returns C<0> if C doesn't exist or doesn't refer to a fifo. If C refers to a fifo, returns 1. If C is non-zero, and C refers to a non-fifo, it will be unlinked. On error, returns C<-1> with C set by I. =cut */ int fifo_exists(const char *path, int prepare) { struct stat status[1]; if (stat(path, status) == -1) return (errno == ENOENT) ? 0 : -1; if (S_ISFIFO(status->st_mode) == 0) { if (prepare) unlink(path); return 0; } return 1; } /* =item C Determines whether or not C refers to a fifo that is being read by another process. If C does not exist or does not refer to a fifo or if the fifo can't be opened for non-blocking I, returns C<0>. If C is non-zero, and path refers to a non-fifo, it will be unlinked. On error, returns C<-1> with C set by I or I. =cut */ int fifo_has_reader(const char *path, int prepare) { int fd; /* ** Check that fifo exists and is a fifo. ** If not, there can be no reader process. */ switch (fifo_exists(path, prepare)) { case 0: return 0; case -1: return -1; } /* ** Open the fifo for non-blocking write. ** If there is no reader process, open() ** will fail with errno == ENXIO. */ if ((fd = open(path, O_WRONLY | O_NONBLOCK)) == -1) return (errno == ENXIO) ? 0 : -1; if (close(fd) == -1) return -1; return 1; } /* =item C Creates a fifo named C with creation mode C for reading. If C already exists, is a fifo and has a reader process, returns C<-1> with C set to C. If the fifo is created (or an existing one can be reused), two file descriptors are opened to the fifo. A read descriptor and a write descriptor. On success, returns the read descriptor. The write descriptor only exists to ensure that there is always at least one writer process for the fifo. This allows a I on the read descriptor to block until another process writes to the fifo rather than returning an C condition. This is done in a POSIX compliant way. If C is non-zero, the fifo is exclusively locked. If C is not C, the write descriptor is stored there. On error, returns C<-1> with C set by I, I, I, I or I. char *fifopath = "/tmp/fifo"; int fd, wfd; if ((fd = fifo_open(fifopath, S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH, 1, &wfd)) == -1) return -1; // Read from fd... close(fd); close(wfd); unlink(fifopath); =cut */ int fifo_open(const char *path, mode_t mode, int lock, int *writefd) { struct stat status[1]; int rfd, wfd, mine = 0; /* Don't open the fifo for reading twice. */ switch (fifo_has_reader(path, 1)) { case 1: return set_errno(EADDRINUSE); case -1: return -1; } /* Create the fifo. */ if (mkfifo(path, mode) != -1) mine = 1; else if (errno != EEXIST) return -1; /* ** Open the fifo for non-blocking read only. ** This prevents blocking while waiting for a ** writer process. We are about to supply our ** own writer. */ if ((rfd = open(path, O_RDONLY | O_NONBLOCK)) == -1) { if (mine) unlink(path); return -1; } /* ** A sanity check to make sure that what we have just ** opened is really a fifo. Someone may have just replaced ** the fifo with a file between fifo_has_reader and here. */ if (fstat(rfd, status) == -1 || S_ISFIFO(status->st_mode) == 0) { if (mine) unlink(path); close(rfd); return -1; } /* ** Open the fifo for write only and leave this fd open. ** This guarantees that there is always at least one ** writer process. This prevents EOF indications being ** returned from read() when there are no other writer ** processes. ** ** Just opening the fifo "rw" should work but it's undefined ** by POSIX. */ if ((wfd = open(path, O_WRONLY)) == -1) { if (mine) unlink(path); close(rfd); return -1; } /* ** Exclusively lock the fifo to prevent two invocations ** deciding that there's no reader and opening this fifo ** at the same time. ** ** Note: some systems (e.g. FreeBSD, Mac OS X) can't lock fifos :( */ /* On MacOSX-10.6 these are different numbers */ #ifndef ENOTSUP #define ENOTSUP EOPNOTSUPP #endif if (lock && fcntl_lock(wfd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1 && errno != EOPNOTSUPP && errno != ENOTSUP && errno != EBADF) { if (mine) unlink(path); close(rfd); close(wfd); return (errno == EACCES) ? set_errno(EADDRINUSE) : -1; } /* A sanity test on the write descriptor we have just opened and locked. */ if (fstat(wfd, status) == -1 || S_ISFIFO(status->st_mode) == 0) { if (mine) unlink(path); close(rfd); close(wfd); return -1; } /* Now put the reader into blocking mode. */ if (nonblock_off(rfd) == -1) { if (mine) unlink(path); close(rfd); close(wfd); return -1; } /* ** Flaw: If someone unceremoniously unlinks our fifo, we won't know ** about it and nothing will stop another invocation from creating a new ** fifo and handling it. This process would sleep forever in select(). */ if (writefd) *writefd = wfd; return rfd; } /* =back =head1 ERRORS These functions set C to the following values. C may also be set by the underlying system calls. See their manpages for details. =over 4 =item C The I, I and I functions set this when a timeout occurs. =item C I sets this when the path refers to a fifo that already has another process reading from it. =back =head1 MT-Level MT-Safe Mac OS X doesn't have I, I or I so I is not MT-Safe on such platforms. You must guard all stdio calls in multi threaded programs with explicit synchronisation variables. =head1 EXAMPLES A paranoid I example: #include #include int main() { char line[BUFSIZ]; while (fgetline(line, BUFSIZ, stdin)) printf("%s", line); if (ferror(stdin)) { if (!*line) printf("%s\n", line); return EXIT_FAILURE; } return EXIT_SUCCESS; } Read from stdin but give up after 5 seconds: #include #include int main() { char buf[BUFSIZ]; ssize_t bytes; if (read_timeout(STDIN_FILENO, 5, 0) == -1 || (bytes = read(STDIN_FILENO, buf, BUFSIZ)) == -1 || write(STDOUT_FILENO, buf, bytes) != bytes) return EXIT_FAILURE; return EXIT_SUCCESS; } A command line sub-second sleep command: #include #include int main(int ac, char **av) { if (ac != 3) return EXIT_FAILURE; nap(atoi(av[1]), atoi(av[2])); return EXIT_SUCCESS; } Setting file flags: #include #include int main() { if (fcntl_set_flag(STDIN_FILENO, O_NONBLOCK | O_ASYNC) == -1) return EXIT_FAILURE; if (fcntl_set_flag(STDOUT_FILENO, O_APPEND) == -1) return EXIT_FAILURE; if (nonblock_on(STDOUT_FILENO) == -1) return EXIT_FAILURE; if (fcntl_clear_flag(STDIN_FILENO, O_NONBLOCK | O_ASYNC) == -1) return EXIT_FAILURE; if (fcntl_clear_flag(STDOUT_FILENO, O_APPEND) == -1) return EXIT_FAILURE; if (nonblock_off(STDOUT_FILENO) == -1) return EXIT_FAILURE; return EXIT_SUCCESS; } File locking: #include #include int main(int ac, char **av) { int fd; if ((fd = open(av[1], O_RDWR)) == -1) return EXIT_FAILURE; if (fcntl_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1) return close(fd), EXIT_FAILURE; // Write to the file... if (fcntl_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 0, 0) == -1) return close(fd), EXIT_FAILURE; close(fd); return EXIT_SUCCESS; } Turn a logfile into a fifo that sends log messages to syslog instead: #include #include #include int main() { char *fifopath = "/tmp/log2syslog"; char buf[BUFSIZ]; ssize_t bytes; int fd, wfd; if ((fd = fifo_open(fifopath, S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH, 1, &wfd)) == -1) return EXIT_FAILURE; while ((bytes = read(fd, buf, BUFSIZ)) > 0) { buf[bytes] = '\0'; syslog(LOG_DAEMON | LOG_ERR, "%s", buf); } close(fd); close(wfd); unlink(fifopath); } =head1 BUGS Some systems, such as Mac OS X, can't lock fifos. On these systems, I ignores the locking failure and returns successfully. This means that there is no guarantee of a unique reader process on these systems. You will need to lock an ordinary file yourself to provide this guarantee. =head1 SEE ALSO I, I, I, I, I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST #include int main(int ac, char **av) { const char * const fifoname = "./fio.fifo"; const char * const filename = "./fio.file"; const mode_t mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; FILE *file; char line[BUFSIZ]; const int lock = 1; int errors = 0; int fd, wfd; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "fio"); umask(0); if ((fd = fifo_open(fifoname, mode, lock, &wfd)) == -1) { ++errors, printf("Test1: fifo_open(\"%s\", %d, %d) failed (%s)\n", fifoname, (int)mode, lock, strerror(errno)); #ifndef HAVE_FCNTL_THAT_CAN_LOCK_FIFOS printf("\n Can your system lock fifos?\n\n"); #endif } else { if ((fcntl_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0)) != -1) ++errors, printf("Test2: fcntl_lock(wrlock) failed\n"); /* Should really test that the following non-blocking changes do occur */ if (nonblock_on(fd) == -1) ++errors, printf("Test3: nonblock_on() failed (%s)\n", strerror(errno)); if (nonblock_off(fd) == -1) ++errors, printf("Test4: nonblock_off() failed (%s)\n", strerror(errno)); if (fcntl_set_flag(fd, O_NONBLOCK) == -1) ++errors, printf("Test5: fcntl_set_flag() failed (%s)\n", strerror(errno)); if (fcntl_clear_flag(fd, O_NONBLOCK) == -1) ++errors, printf("Test6: fcntl_clear_flag() failed (%s)\n", strerror(errno)); close(fd); close(wfd); unlink(fifoname); } #define CHECK_FGETLINE(i, size, expected) \ if ((expected) && !fgetline(line, (size), file)) \ ++errors, printf("Test%d: fgetline() failed\n", (i)); \ else if ((expected) && strcmp(line, ((expected) ? (expected) : ""))) \ ++errors, printf("Test%d: fgetline() read \"%s\", not \"%s\"\n", (i), line, (expected ? expected : "(null)")); #define TEST_FGETLINE(i, buf, size, contents, line1, line2, line3) \ if (!(file = fopen(filename, "wb"))) \ ++errors, printf("Test%d: failed to run test: failed to create test file\n", (i)); \ else \ { \ if (fwrite((contents), 1, strlen(contents), file) != strlen(contents)) \ ++errors, printf("Test%d: failed to run test: failed to write to test file\n", (i)); \ else \ { \ fclose(file); \ if (!(file = fopen(filename, "r"))) \ ++errors, printf("Test%d: failed to run test: failed to open test file for reading\n", (i)); \ else \ { \ CHECK_FGETLINE((i), (size), (line1)) \ CHECK_FGETLINE((i), (size), (line2)) \ CHECK_FGETLINE((i), (size), (line3)) \ if (fgetline(buf, BUFSIZ, file)) \ ++errors, printf("Test%d: fgetline() failed to return NULL at end of file\n", (i)); \ } \ } \ fclose(file); \ unlink(filename); \ } TEST_FGETLINE(7, line, BUFSIZ, "abc\ndef\r\nghi\r", "abc\n", "def\n", "ghi\n") TEST_FGETLINE(8, line, BUFSIZ, "abc\rdef\nghi\r\n", "abc\n", "def\n", "ghi\n") TEST_FGETLINE(9, line, BUFSIZ, "abc\r\ndef\rghi\n", "abc\n", "def\n", "ghi\n") TEST_FGETLINE(10, line, BUFSIZ, "abc\ndef\rghi", "abc\n", "def\n", "ghi") TEST_FGETLINE(11, line, BUFSIZ, "", (char *)NULL, (char *)NULL, (char *)NULL) TEST_FGETLINE(12, line, 5, "abc", "abc", (char *)NULL, (char *)NULL) TEST_FGETLINE(13, line, 5, "abc\n", "abc\n", (char *)NULL, (char *)NULL) TEST_FGETLINE(14, line, 5, "abc\r\n", "abc\n", (char *)NULL, (char *)NULL) TEST_FGETLINE(15, line, 5, "abc\r", "abc\n", (char *)NULL, (char *)NULL) TEST_FGETLINE(16, line, 3, "abc\r", "ab", "c\n", (char *)NULL) TEST_FGETLINE(17, NULL, 0, "abc\r", (char *)NULL, (char *)NULL, (char *)NULL) /* Test read_timeout() and write_timeout() */ if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, S_IRUSR | S_IWUSR)) == -1) ++errors, printf("Test19: failed to create %s (%s)\n", filename, strerror(errno)); else { char buf[12] = "0123456789\n"; if (write_timeout(fd, 1, 0) == -1) ++errors, printf("Test18: write_timeout(fd, 1, 0) failed (%s)\n", strerror(errno)); else if (write(fd, buf, 11) != 11) ++errors, printf("Test18: write(fd, \"0123456789\\n\", 11) failed (%s)\n", strerror(errno)); else { close(fd); if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) == -1) ++errors, printf("Test19: failed to open %s for reading (%s)\n", filename, strerror(errno)); else if (read_timeout(fd, 1, 0) == -1) ++errors, printf("Test19: read_timeout(fd, 1, 0) failed (%s)\n", strerror(errno)); else if (read(fd, buf, 11) != 11) ++errors, printf("Test19: read(fd) failed (%s)\n", strerror(errno)); } close(fd); } unlink(filename); /* Test error handling */ #define TEST_ERR(i, func) \ if ((func) != -1) \ ++errors, printf("Test%d: %s failed to return -1\n", (i), (#func)); \ else if (errno != EINVAL) \ ++errors, printf("Test%d: %s failed (errno = %s, not %s)\n", (i), (#func), strerror(errno), strerror(EINVAL)); TEST_ERR(20, read_timeout(-1, 0, 0)) TEST_ERR(21, read_timeout(0, -1, 0)) TEST_ERR(22, read_timeout(0, 0, -1)) TEST_ERR(23, write_timeout(-1, 0, 0)) TEST_ERR(24, write_timeout(0, -1, 0)) TEST_ERR(25, write_timeout(0, 0, -1)) TEST_ERR(26, rw_timeout(-1, 0, 0)) TEST_ERR(27, rw_timeout(0, -1, 0)) TEST_ERR(28, rw_timeout(0, 0, -1)) TEST_ERR(29, nap(-1, 0)) TEST_ERR(30, nap(0, -1)) if (errors) printf("%d/30 tests failed\n", errors); else printf("All tests passed\n"); #ifndef HAVE_FCNTL_THAT_CAN_LOCK_FIFOS printf("\n"); printf(" Note: Some systems (e.g. FreeBSD) can't lock fifos so fifo_open()\n"); printf(" can't guarantee a unique reader.\n"); #endif return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/fio.h000066400000000000000000000033111140522741300154730ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_FIO_H #define LIBSLACK_FIO_H #include #include #include _begin_decls char *fgetline(char *line, size_t size, FILE *stream); char *fgetline_unlocked(char *line, size_t size, FILE *stream); int read_timeout(int fd, long sec, long usec); int write_timeout(int fd, long sec, long usec); int rw_timeout(int fd, long sec, long usec); int nap(long sec, long usec); int fcntl_set_flag(int fd, int flag); int fcntl_clear_flag(int fd, int flag); int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len); int nonblock_set(int fd, int arg); int nonblock_on(int fd); int nonblock_off(int fd); int fifo_exists(const char *path, int prepare); int fifo_has_reader(const char *path, int prepare); int fifo_open(const char *path, mode_t mode, int lock, int *writefd); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/getopt.c000066400000000000000000001350541140522741300162250ustar00rootroot00000000000000/* Perform additional initialization for getopt functions in GNU libc. Copyright (C) 1997, 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 1997. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Attention: this file is *not* necessary when the GNU getopt functions are used outside the GNU libc. Some additional functionality of the getopt functions in GNU libc require this additional work. */ /* (c) 1993 by Thomas Koenig (ig25@rz.uni-karlsruhe.de) Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one Since the Linux kernel and libraries are constantly changing, this manual page may be incorrect or out-of-date. The author(s) assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein. The author(s) may not have taken the same level of care in the production of this manual, which is licensed free of charge, as they might when working professionally. Formatted or processed versions of this manual, if unaccompanied by the source, must acknowledge the copyright and authors of this work. License. Modified Sat Jul 24 19:27:50 1993 by Rik Faith (faith@cs.unc.edu) Modified Mon Aug 30 22:02:34 1995 by Jim Van Zandt longindex is a pointer, has_arg can take 3 values, using consistent names for optstring and longindex, \n in formats fixed. Documenting opterr and getopt_long_only. Clarified explanations (borrowing heavily from the source code). Modified 8 May 1998 by Joseph S. Myers (jsm28@cam.ac.uk) =head1 NAME I - Parse command line options =head1 SYNOPSIS #include int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg; extern int optind, opterr, optopt; #include int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); =head1 DESCRIPTION The I function parses the command line arguments. Its arguments C and C are the argument count and array as passed to the I function on program invocation. An element of C that starts with `-' (and is not exactly "-" or "--") is an option element. The characters of this element (aside from the initial `-') are option characters. If I is called repeatedly, it returns successively each of the option characters from each of the option elements. If I finds another option character, it returns that character, updating the external variable C and a static variable C so that the next call to I can resume the scan with the following option character or C-element. If there are no more option characters, I returns C. Then C is the index in C of the first C-element that is not an option. C is a string containing the legitimate option characters. If such a character is followed by a colon, the option requires an argument, so I places a pointer to the following text in the same C-element, or the text of the following C-element, in C. Two colons mean an option takes an optional arg; if there is text in the current C-element, it is returned in C, otherwise C is set to zero. This is a GNU extension. If C contains C followed by a semicolon, then C<-W foo> is treated as the long option C<--foo>. (The C<-W> option is reserved by POSIX.2 for implementation extensions.) This behaviour is a GNU extension, not available with libraries before GNU libc 2. By default, I permutes the contents of C as it scans, so that eventually all the non-options are at the end. Two other modes are also implemented. If the first character of C is `+' or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a non-option argument is encountered. If the first character of C is `-', then each non-option C-element is handled as if it were the argument of an option with character code 1. (This is used by programs that were written to expect options and other C-elements in any order and that care about the ordering of the two.) The special argument `--' forces an end of option-scanning regardless of the scanning mode. If I does not recognize an option character, it prints an error message to stderr, stores the character in C, and returns `?'. The calling program may prevent the error message by setting C to 0. The I function works like I except that it also accepts long options, started out by two dashes. Long option names may be abbreviated if the abbreviation is unique or is an exact match for some defined option. A long option may take a parameter, of the form C<--arg=param> or C<--arg param>. C is a pointer to the first element of an array of C declared in C as struct option { const char *name; int has_arg; int *flag; int val; }; The meanings of the different fields are: =over 4 =item C is the name of the long option. =item C is: C (or 0) if the option does not take an argument, C (or 1) if the option requires an argument, or C (or 2) if the option takes an optional argument. =item C specifies how results are returned for a long option. If C is C, then I returns C. (For example, the calling program may set C to the equivalent short option character.) Otherwise, I returns 0, and C points to a variable which is set to C if the option is found, but left unchanged if the option is not found. =item C is the value to return, or to load into the variable pointed to by C. =back The last element of the array has to be filled with zeroes. If C is not C, it points to a variable which is set to the index of the long option relative to C. I is like I, but `-' as well as `--' can indicate a long option. If an option that starts with `-' (not `--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. =head1 RETURN VALUE The I function returns the option character if the option was found successfully, `:' if there was a missing parameter for one of the options, `?' for an unknown option character, or C for the end of the option list. I and I also return the option character when a short option is recognized. For a long option, they return C if C is C, and 0 otherwise. Error and C returns are the same as for I, plus `?' for an ambiguous match or an extraneous parameter. =head1 ENVIRONMENT VARIABLES =over 4 =item C If this is set, then option processing stops as soon as a non-option argument is encountered. =item C<_>IC<_GNU_nonoption_argv_flags_> This variable was used by I 2.0 to communicate to GNU libc which arguments are the results of wildcard expansion and so should not be considered as options. This behaviour was removed in I version 2.01, but the support remains in GNU libc. =back =head1 EXAMPLE The following example program, from the source code, illustrates the use of I with most of its features. #include #ifndef HAVE_GETOPT_LONG #include #else #include #endif int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 1, 0, 'c'}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:012", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } =head1 BUGS This manpage is confusing. The POSIX.2 specification of I has a technical error described in POSIX.2 Interpretation 150. The GNU implementation (and probably all other implementations) implements the correct behaviour rather than that speci- fied. =head1 CONFORMING TO =over 4 =item I: POSIX.2, provided the environment variable POSIXLY_CORRECT is set. Otherwise, the elements of C aren't really const, because we permute them. We pretend they're const in the prototype to be compatible with other systems. =back =cut */ #include "getopt.h" #include #include #if 0 #include #endif pid_t getpid(); /* Variable to synchronize work. */ char *__getopt_nonoption_flags; /* Lower-case digits. */ const char _itoa_lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; /* Upper-case digits. */ const char _itoa_upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* Convert VALUE into ASCII in base BASE (2..36). Write backwards starting the character just before BUFLIM. Return the address of the first (left-to-right) character in the number. Use upper case letters iff UPPER_CASE is nonzero. */ extern char *_itoa (unsigned long int value, char *buflim, unsigned int base, int upper_case); static char * _itoa_word (unsigned long value, char *buflim, unsigned int base, int upper_case) { extern const char _itoa_upper_digits[], _itoa_lower_digits[]; const char *digits = upper_case ? _itoa_upper_digits : _itoa_lower_digits; char *bp = buflim; switch (base) { #define SPECIAL(Base) \ case Base: \ do \ *--bp = digits[value % Base]; \ while ((value /= Base) != 0); \ break SPECIAL (10); SPECIAL (16); SPECIAL (8); default: do *--bp = digits[value % base]; while ((value /= base) != 0); } return bp; } /* Remove the environment variable "__GNU_nonoption_argv_flags_" if it is still available. If the getopt functions are also used in the application it does not exist anymore since it was saved for the use in getopt. */ void __getopt_clean_environment (char **env) { /* Bash 2.0 puts a special variable in the environment for each command it runs, specifying which ARGV elements are the results of file name wildcard expansion and therefore should not be considered as options. */ static const char envvar_tail[] = "_GNU_nonoption_argv_flags_="; char var[100]; char *cp, **ep; size_t len; /* Construct the "__GNU_nonoption_argv_flags_=" string. We must not use `sprintf'. */ cp = memcpy (&var[sizeof (var) - sizeof (envvar_tail)], envvar_tail, sizeof (envvar_tail)); cp = _itoa_word (getpid (), cp, 10, 0); *--cp = '_'; len = (var + sizeof (var) - 1) - cp; for (ep = env; *ep != NULL; ++ep) if (!strncmp (*ep, cp, len)) { /* Found it. Store this pointer and move later ones back. */ char **dp = ep; __getopt_nonoption_flags = &(*ep)[len]; do dp[0] = dp[1]; while (*dp++); /* Continue the loop in case the name appears again. */ } } /* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to drepper@gnu.org before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO # define _NO_PROTO #endif #ifdef HAVE_CONFIG_H # include #endif #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ # ifndef const # define const # endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 # include # if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION # define ELIDE_CODE # endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ # include # include #endif /* GNU C library. */ #ifdef VMS # include # if HAVE_STRING_H - 0 # include # endif #endif #ifndef _ /* This is for other GNU distributions with internationalized messages. When compiling libc, the _ macro is predefined. */ # ifdef HAVE_LIBINTL_H # include # define _(msgid) gettext (msgid) # else # define _(msgid) (msgid) # endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a NULL string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ # include # define my_index strchr #else # if HAVE_STRING_H # include # else # include # endif /* Avoid depending on library functions or files whose names are inconsistent. */ #ifndef getenv extern char *getenv (); #endif static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ # if (!defined __STDC__ || !__STDC__) && !defined strlen /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen (const char *); # endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ /* Defined in getopt_init.c */ extern char *__getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; static int original_argc; static char *const *original_argv; /* Make sure the environment variable bash 2.0 puts in the environment is valid for the getopt call we must make sure that the ARGV passed to getopt is that one passed to the process. */ static void __attribute__ ((unused)) store_args_and_env (int argc, char *const *argv) { /* XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ original_argc = argc; original_argv = argv; } # ifdef text_set_element text_set_element (__libc_subinit, store_args_and_env); # endif /* text_set_element */ # define SWAP_FLAGS(ch1, ch2) \ if (nonoption_flags_len > 0) \ { \ char __tmp = __getopt_nonoption_flags[ch1]; \ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ __getopt_nonoption_flags[ch2] = __tmp; \ } #else /* !_LIBC */ # define SWAP_FLAGS(ch1, ch2) #endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ #if defined __STDC__ && __STDC__ static void exchange (char **); #endif static void exchange (argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ #ifdef _LIBC /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and presents new arguments. */ char *new_str = malloc (top + 1); if (new_str == NULL) nonoption_flags_len = nonoption_flags_max_len = 0; else { memset (__mempcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS (bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ #if defined __STDC__ && __STDC__ static const char *_getopt_initialize (int, char *const *, const char *); #endif static const char * _getopt_initialize (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #ifdef _LIBC if (posixly_correct == NULL && argc == original_argc && argv == original_argv) { if (nonoption_flags_max_len == 0) { if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') nonoption_flags_max_len = -1; else { const char *orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen (orig_str); if (nonoption_flags_max_len < argc) nonoption_flags_max_len = argc; __getopt_nonoption_flags = (char *) malloc (nonoption_flags_max_len); if (__getopt_nonoption_flags == NULL) nonoption_flags_max_len = -1; else memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); } } nonoption_flags_len = nonoption_flags_max_len; } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { optarg = NULL; if (optind == 0 || !__getopt_initialized) { if (optind == 0) optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize (argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #ifdef _LIBC # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && __getopt_nonoption_flags[optind] == '1')) #else # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a NULL option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, _("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, _("%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen (nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); else /* +option or -option */ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); else fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) fprintf (stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #endif /* Not ELIDE_CODE. */ /* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "getopt.h" #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #endif #ifndef NULL #define NULL 0 #endif int getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } #endif /* Not ELIDE_CODE. */ #ifdef TEST #include #include int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ /* vi:set ts=8 sw=2 sts=2: */ daemon-0.6.4/libslack/getopt.h000066400000000000000000000134031140522741300162230ustar00rootroot00000000000000/* Declarations for getopt. Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _GETOPT_H #ifndef __need_getopt # define _GETOPT_H 1 #endif #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; #ifndef __need_getopt /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { # if defined __STDC__ && __STDC__ const char *name; # else char *name; # endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ # define no_argument 0 # define required_argument 1 # define optional_argument 2 #endif /* need getopt */ /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, `optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in `optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU `getopt'. The argument `--' causes premature termination of argument scanning, explicitly telling `getopt' that there are no more options. If OPTS begins with `--', then non-option arguments are treated as arguments to the option '\0'. This behavior is specific to the GNU `getopt'. */ #if defined __STDC__ && __STDC__ # ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int __argc, char *const *__argv, const char *__shortopts); # else /* not __GNU_LIBRARY__ */ extern int getopt (); # endif /* __GNU_LIBRARY__ */ # ifndef __need_getopt extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); extern int getopt_long_only (int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only); # endif #else /* not __STDC__ */ extern int getopt (); # ifndef __need_getopt extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); # endif #endif /* __STDC__ */ #ifdef __cplusplus } #endif /* Make sure we later can get all the definitions and declarations. */ #undef __need_getopt #endif /* getopt.h */ /* vi:set ts=8 sw=2 sts=2: */ daemon-0.6.4/libslack/hdr.h000066400000000000000000000021321140522741300154730ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_HDR_H #define LIBSLACK_HDR_H #undef _begin_decls #undef _end_decls #ifdef __cplusplus #define _begin_decls extern "C" { #define _end_decls } #else #define _begin_decls #define _end_decls #endif #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/hsort.c000066400000000000000000000301511140522741300160520ustar00rootroot00000000000000/* # libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* * Generic Heapsort. * * Synopsis: * hsort(void *base, size_t n, size_t size, int (*fn)(void *, void *)) * Description: * Hsort sorts the array of `n' items which starts at address `base'. * The size of each item is as given. Items are compared by the function * `fn', which is passed pointers to two items as arguments. The function * should return < 0 if item1 < item2, == 0 if item1 == item2, and > 0 * if item1 > item2. * Version: * 1988 April 28 * Author: * Stephen Russell, Department of Computer Science, * University of Sydney, 2006 * Australia. */ /* =head1 NAME I - generic heap sort =head1 SYNOPSIS #include #include typedef int hsort_cmp_t(const void *a, const void *b); typedef int hsort_closure_cmp_t(const void *a, const void *b, const void *data); void hsort(void *base, size_t n, size_t size, hsort_cmp_t *cmp); void hsort_closure(void *base, size_t n, size_t size, hsort_closure_cmp_t *cmp, const void *data); =head1 DESCRIPTION I is an implementation of the heap sort algorithm. It sorts a table of data in place. C points to the element at the base of the table. C is the number of elements in the table. C is the size of the elements in bytes. C is the comparison function, which is called with two arguments that point to the elements being compared. As the function must return an integer less than, equal to, or greater than zero, so must the first argument to be considered be less than, equal to, or greater than the second. This is a drop-in replacement for I. I is the same but it supports passing arbitrary data to the comparison function. =head1 NOTES The comparison function need not compare every byte, so arbitrary data may be contained in the elements in addition to the values being compared. The order in the output of two items which compare as equal is unpredictable. =head1 MT-Level MT-Safe Note that the array being sorted will still have to be write locked during I if it is accessed by other threads. =head1 EXAMPLE This examples sorts and prints an array of strings. #include #include int cmp(const char **p1, const char **p2) { return strcmp(*p1, *p2); } int main(int ac, char **av) { char *string[4] = { "jkl", "ghi", "def", "abc" }; int i; hsort(string, 4, sizeof string[0], (hsort_cmp_t *)cmp); for (i = 0; i < 4; ++i) printf("%s\n", string[i]); return EXIT_SUCCESS; } =head1 SEE ALSO I =head1 AUTHOR Stephen Russell, Department of Computer Science, University of Sydney, 2006. Australia 1988 April 28 Header file, test code and manpage by raf =cut */ #include "std.h" #include "hsort.h" #ifndef TEST #ifdef INLINE #define swap(p1, p2, n) \ {\ register char *_p1, *_p2;\ register size_t _n;\ register char _tmp;\ \ for (_p1 = p1, _p2 = p2, _n = n; _n-- > 0; )\ {\ _tmp = *_p1; *_p1++ = *_p2; *_p2++ = _tmp;\ }\ }\ #else /* * Support routine for swapping elements of the array. */ static void swap ( register char *p1, register char *p2, register size_t n ) { register char ctmp; /* * On machines with no alignment restrictions for ints, * the following loop may improve performance if moving lots * of data. It has been commented out for portability. register int itmp; for ( ; n > sizeof(int); n -= sizeof(int)) { itmp = *(int *)p1; *(int *)p1 = *(int *)p2; p1 += sizeof(int); *(int *)p2 = itmp; p2 += sizeof(int); } */ while (n-- != 0) { ctmp = *p1; *p1++ = *p2; *p2++ = ctmp; } } #endif /* * To avoid function calls in the inner loops, the code responsible for * constructing a heap from (part of) the array has been expanded inline. * It is possible to convert this common code to a function. The three * parameters base0, cmp and size are invariant - only the size of the * gap and the high bound of the array change. In phase 1, gap decreases * while hi is fixed. In phase 2, gap == size, and hi decreases. The * variables p, q, and g are only used in this common code. */ void hsort(void *base, size_t n, size_t size, hsort_cmp_t *cmp) { register char *p, *q, *base0, *hi; register unsigned int gap, g; if (n < 2) return; base0 = (char *)base - size; /* set up address of h[0] */ /* * The gap is the distance, in bytes, between h[0] and h[i], * for some i. It is also the distance between h[i] and h[2*i]; * that is, the distance between a node and its left child. * The initial node of interest is h[n/2] (the rightmost * interior node), so gap is set accordingly. The following is * the only multiplication needed. */ gap = (n >> 1) * size; /* initial gap is n/2*size */ hi = base0 + gap + gap; /* calculate address of h[n] */ if (n & 1) hi += size; /* watch out for odd arrays */ /* * Phase 1: Construct heap from random data. * * For i = n/2 downto 2, ensure h[i] is greater than its * children h[2*i] and h[2*i+1]. By decreasing 'gap' at each * iteration, we move down the heap towards h[2]. The final step * of making h[1] the maximum value is done in the next phase. */ for ( ; gap != size; gap -= size) { /* fixheap(base0, size, cmp, gap, hi) */ for (p = base0 + (g = gap); (q = p + g) <= hi; p = q) { g += g; /* double gap for next level */ /* * Find greater of left and right children. */ if (q != hi && (*cmp)(q + size, q) > 0) { q += size; /* choose right child */ g += size; /* follow right subtree */ } /* * Compare with parent. */ if ((*cmp)(p, q) >= 0) break; /* order is correct */ swap(p, q, size); /* swap parent and child */ } } /* * Phase 2: Each iteration makes the first item in the * array the maximum, then swaps it with the last item, which * is its correct position. The size of the heap is decreased * each iteration. The gap is always "size", as we are interested * in the heap starting at h[1]. */ for ( ; hi != base; hi -= size) { /* fixheap(base0, size, cmp, gap (== size), hi) */ p = (char *)base; /* == base0 + size */ for (g = size; (q = p + g) <= hi; p = q) { g += g; if (q != hi && (*cmp)(q + size, q) > 0) { q += size; g += size; } if ((*cmp)(p, q) >= 0) break; swap(p, q, size); } swap((char *)base, hi, size); /* move largest item to end */ } } void hsort_closure(void *base, size_t n, size_t size, hsort_closure_cmp_t *cmp, const void *data) { register char *p, *q, *base0, *hi; register unsigned int gap, g; if (n < 2) return; base0 = (char *)base - size; /* set up address of h[0] */ /* * The gap is the distance, in bytes, between h[0] and h[i], * for some i. It is also the distance between h[i] and h[2*i]; * that is, the distance between a node and its left child. * The initial node of interest is h[n/2] (the rightmost * interior node), so gap is set accordingly. The following is * the only multiplication needed. */ gap = (n >> 1) * size; /* initial gap is n/2*size */ hi = base0 + gap + gap; /* calculate address of h[n] */ if (n & 1) hi += size; /* watch out for odd arrays */ /* * Phase 1: Construct heap from random data. * * For i = n/2 downto 2, ensure h[i] is greater than its * children h[2*i] and h[2*i+1]. By decreasing 'gap' at each * iteration, we move down the heap towards h[2]. The final step * of making h[1] the maximum value is done in the next phase. */ for ( ; gap != size; gap -= size) { /* fixheap(base0, size, cmp, gap, hi) */ for (p = base0 + (g = gap); (q = p + g) <= hi; p = q) { g += g; /* double gap for next level */ /* * Find greater of left and right children. */ if (q != hi && (*cmp)(q + size, q, data) > 0) { q += size; /* choose right child */ g += size; /* follow right subtree */ } /* * Compare with parent. */ if ((*cmp)(p, q, data) >= 0) break; /* order is correct */ swap(p, q, size); /* swap parent and child */ } } /* * Phase 2: Each iteration makes the first item in the * array the maximum, then swaps it with the last item, which * is its correct position. The size of the heap is decreased * each iteration. The gap is always "size", as we are interested * in the heap starting at h[1]. */ for ( ; hi != base; hi -= size) { /* fixheap(base0, size, cmp, gap (== size), hi) */ p = (char *)base; /* == base0 + size */ for (g = size; (q = p + g) <= hi; p = q) { g += g; if (q != hi && (*cmp)(q + size, q, data) > 0) { q += size; g += size; } if ((*cmp)(p, q, data) >= 0) break; swap(p, q, size); } swap((char *)base, hi, size); /* move largest item to end */ } } #endif #ifdef TEST int errors = 0; char *string[4]; char *data = "arbitrary"; int testno = 0; static int cmp(const char **a, const char **b) { return strcmp(*a, *b); } static int cmp_closure(const char **a, const char **b, const char *data) { if (!data || strcmp(data, "arbitrary")) /* may crash here if buggy */ ++errors, printf("Test%d: hsort_closure() failed: data not supplied\n", testno); return strcmp(*a, *b); } static void verify(int test) { if (strcmp(string[0], "abc")) ++errors, printf("Test%d: 1st item is '%s' (not 'abc')\n", test, string[0]); else if (strcmp(string[1], "def")) ++errors, printf("Test%d: 2nd item is '%s' (not 'def')\n", test, string[1]); else if (strcmp(string[2], "ghi")) ++errors, printf("Test%d: 3rd item is '%s' (not 'ghi')\n", test, string[2]); else if (strcmp(string[3], "jkl")) ++errors, printf("Test%d: 4th item is '%s' (not 'jkl')\n", test, string[3]); } int main(int ac, char **av) { if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "hsort"); string[0] = "abc"; string[1] = "def"; string[2] = "ghi"; string[3] = "jkl"; hsort(string, 4, sizeof string[0], (hsort_cmp_t *)cmp); verify(1); string[0] = "jkl"; string[1] = "ghi"; string[2] = "def"; string[3] = "abc"; hsort(string, 4, sizeof string[0], (hsort_cmp_t *)cmp); verify(2); string[0] = "def"; string[1] = "abc"; string[2] = "jkl"; string[3] = "ghi"; hsort(string, 4, sizeof string[0], (hsort_cmp_t *)cmp); verify(3); string[0] = "abc"; string[1] = "def"; string[2] = "ghi"; string[3] = "jkl"; testno = 5; hsort_closure(string, 4, sizeof string[0], (hsort_closure_cmp_t *)cmp_closure, data); verify(testno); string[0] = "jkl"; string[1] = "ghi"; string[2] = "def"; string[3] = "abc"; testno = 6; hsort_closure(string, 4, sizeof string[0], (hsort_closure_cmp_t *)cmp_closure, data); verify(testno); string[0] = "def"; string[1] = "abc"; string[2] = "jkl"; string[3] = "ghi"; testno = 7; hsort_closure(string, 4, sizeof string[0], (hsort_closure_cmp_t *)cmp_closure, data); verify(testno); if (errors) printf("%d/6 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/hsort.h000066400000000000000000000024521140522741300160620ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_HSORT_H #define LIBSLACK_HSORT_H #include #include typedef int hsort_cmp_t(const void *a, const void *b); typedef int hsort_closure_cmp_t(const void *a, const void *b, const void *data); _begin_decls void hsort(void *base, size_t n, size_t size, hsort_cmp_t *cmp); void hsort_closure(void *base, size_t n, size_t size, hsort_closure_cmp_t *cmp, const void *data); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/lib.h000066400000000000000000000030061140522741300154650ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_LIB_H #define LIBSLACK_LIB_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_SNPRINTF #include #endif #ifndef HAVE_VSSCANF #include #endif #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/libslack-config.pod000066400000000000000000000072051140522741300203060ustar00rootroot00000000000000# # libslack - http://libslack.org/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # 20100612 raf =head1 NAME I - administrative tool for libslack =head1 SYNOPSIS libslack-config [options] options: -h, --help - Print this help and exit -v, --version - Print the version of the currently installed libslack -L, --latest - Print the latest version of libslack (uses wget) -D, --download - Download the latest version of libslack (uses wget) -U, --upgrade - Upgrade to the latest version of libslack (uses wget) -p, --prefix - Print the prefix directory of the libslack installation -c, --cflags - Print CFLAGS needed to compile clients of libslack -l, --libs - Print LDFLAGS needed to link against libslack -l, --ldflags - Identical to --libs -u, --uninstall - Uninstall libslack Note: The dashes are optional for long option names Command line example: gcc -o app app.c `libslack-config --cflags --libs` Makefile example: APP_CFLAGS += $(shell libslack-config --cflags) APP_LDFLAGS += $(shell libslack-config --libs) =head1 DESCRIPTION I is an administrative tool for I. It can print the compiler and linker flags that are needed to compile and link programs that use I. It can also print the version of the currently installed I or print the latest version available at C. It can also download the latest version. It can also upgrade libslack. It can also uninstall libslack. =head1 OPTIONS =over 4 =item C<-h>, C<--help> Print a help message and exit. =item C<-v>, C<--version> Print the version of the currently installed I. =item C<-L>, C<--latest> Print the latest version of I available at C. =item C<-D>, C<--download> Download the latest version of I from C. =item C<-U>, C<--upgrade> Upgrade to the latest version of I from C. This downloads the latest version, configures it, compiles it, uninstalls the currently installed version and then installs the new version wherever the current version was installed. =item C<-p>, C<--prefix> Print the prefix directory under which I was installed. =item C<-c>, C<--cflags> Print the compiler flags needed to compile code that uses I. =item C<-l>, C<--libs>, C<--ldflags> Print the linker flags needed to link code against I. =item C<-u>, C<--uninstall> Uninstall I. =back =head1 SEE ALSO C, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I =head1 AUTHOR 20100612 raf =cut daemon-0.6.4/libslack/libslack-config.t000066400000000000000000000102751140522741300177700ustar00rootroot00000000000000#!/bin/bash # # libslack - http://libslack.org/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # 20100612 raf var() { eval $1='$2'; export $1; } die() { echo "$@" >&2; exit 1; } var url "@@URL@@" var name "@@NAME@@" var version "@@VERSION@@" var prefix "@@PREFIX@@" var cflags "@@CFLAGS@@" var libs "@@LIBS@@" if test "$prefix" != "/usr" -a "$prefix" != "/usr/local" then cflags="-I$prefix/include $cflags" libs="-L$prefix/lib $libs" fi usage() { cat <) { $version{$1} = 1 if /[Hh][Rr][Ee][Ff]=".*$ENV{name}-([\d.]+)\.tar\.gz"/; } sub version_sort { my @anum = split /\./, $a; my @bnum = split /\./, $b; while ($#anum != -1 && $#bnum != -1) { return $x if $x = $bnum[0] - $anum[0]; shift @anum; shift @bnum; } return -1 if $#anum != -1; return 1 if $#bnum != -1; return 0; } @version = sort { version_sort } keys %version; die "No versions found at $ENV{url}download/\n" if $#version == -1; print "$ENV{url}download/$ENV{name}-$version[0].tar.gz\n"; exit 0; ' } download() { latest="`latest 2>&1`" test "$latest" = "No versions found at ${url}download" && die "$latest" file="`echo $latest | sed 's/^.*\///'`" test -f "$file" && die "The file $file already exists" wget "$latest" } upgrade() { latest="`latest 2>&1`" test "$latest" = "No versions found at ${url}/download" && die "$latest" file="`echo $latest | sed 's/^.*\///'`" dir="`echo $file | sed 's/\.tar\.gz$//'`" test -f "$file" || wget "$latest" test -s "$file" || die "Failed to download $latest" tar xzf "$file" || die "Failed to untar $file" cd "$dir" || die "Failed to cd $dir" ./configure || die "Failed to configure $dir" make || die "Failed to make $dir" uninstall || die "Failed to uninstall current version" make PREFIX="$prefix" install || die "Failed to install $dir into $prefix" cd .. && rm -rf "$dir" } uninstall() { @@UNINSTALL@@ } test $# -eq 0 && usage 1 1>&2 while test $# -gt 0 do case "$1" in -h|--help|help) usage 0;; -v|--version|version) echo "$version";; -L|--latest|latest) latest;; -D|--download|download) download;; -U|--upgrade|upgrade) upgrade;; -p|--prefix|prefix) echo "$prefix";; -c|--cflags|cflags) echo "$cflags";; -l|--libs|libs) echo "$libs";; -l|--ldflags|ldflags) echo "$libs";; -u|--uninstall|uninstall) uninstall;; *) usage 1 >&2;; esac shift done exit 0 # vim:set ts=4 sw=4: daemon-0.6.4/libslack/libslack.pod000066400000000000000000000360341140522741300170450ustar00rootroot00000000000000# # libslack - http://libslack.org/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # 20100612 raf =head1 NAME I - A UNIX/C library of general utilities for programmers with Slack Slack(n.): The state in which you need nothing, because you already have it. -- Dobbs, 1956 =head1 SYNOPSIS Compiling: gcc -o app app.c `libslack-config --cflags --libs` Headers: /* Include this before all other header files */ #include or /* Include this before all other header files */ #include /* Then select what you want from the rest */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_SNPRINTF #include #endif #ifndef HAVE_VSSCANF #include #endif =head1 DESCRIPTION Libslack is a library of general utilities designed to make UNIX/C programming a bit easier on the eye. It is a seemingly random collection of modules and functions that I find commonly useful. It's a small library with lots of functionality, accurately documented and thoroughly tested. Good library naming conventions are not rigorously observed on the principle that common operations should always be easy to write and code should always be easy to read. =head1 FEATURES =over 4 =item Program Framework The "framework" parts of I are bound to be the most objectionable. Nobody likes frameworks. Don't worry, there isn't much to it. If you don't like it, pretend it's not there. I provides the ability for programs to identify themselves, perform command line option processing in a single line of code and produce "standard" GNU style C<--help>, C<--version> and usage messages. Debug and verbose messaging is also provided and is enabled by the use of "standard" C<--debug> and C<--verbose> command line options. Messages (including error, debug and verbose messages) are provided with clean call syntax and flexible semantics. Messages can be directed to log files, file descriptors, syslog or multiplexed to any of the above (and to anywhere else (e.g. dialog boxes) if you implement your own message handlers) without complicating the call syntax. ISO C imposes extreme restrictions on signal handlers. POSIX imposes less extreme but still annoying restrictions. I contains functions that provide a level of abstraction between the signal handlers that you write and the real (ISO C compliant) signal handlers. This allows you to write unrestricted signal handlers. Coarse grained persistence of simple configuration information is provided by the use of Java style properties files in "well-known" locations. =item Daemon Services I provides functions that make writing daemons trivial. It includes functions to become a daemon and to optionally create a locked pid file to ensure that only a single instance of a named daemon is ever active at the same time. The daemon function behaves appropriately if the client process is started by I or I. There are also functions to streamline the parsing of simple configuration files (like those commonly found in the C directory). There are also functions that help you write more secure daemons (i.e. revoke setuid/setgid privileges, prevent core file generation, change user and group, test the safety of a given file path). =item Network Services I provides functions to simplify the implementation of network servers and clients (TCP, UDP, unicast and multicast) and the (text or binary) application protocols that they use. Network servers and clients can be setup in a single line of code. There is transparent support for IPv4, IPv6 and UNIX domain socket addresses. There is support for reliability over UDP. Text protocols are implemented by sequences of expect and send functions. Binary protocol packets can be packed and unpacked (using functions similar to I and I in I but network/storage friendly). There's also a function to send mail. =item Agent Oriented Programming I provides a generic agent oriented programming model in the form of the I data type. Like objects, agents can react to external stimuli. Unlike objects, agents can can also take independent actions. This is performed by multiplexing I/O events on arbitrary file descriptors using I or I and also multiplexing timers for scheduling actions. Connecting and disconnecting file descriptors is done in constant time. Scheduling and cancelling actions is done in constant time. Timer maintenance is performed in constant average time. This means that agents are scalable with respect to the number of outstanding timers and can support thousands of scheduled actions. A single agent may be used like a simple event loop, or multiple agents can be connected to each other in arbitrary networks across multiple threads, processes and/or hosts. A single agent isn't scalable with respect to the number of connected descriptors but multiple agents can be combined to achieve much higher scalability. They're useful for networked applications, distributed systems and simulations. =item Data Types I provides a generic growable pointer array data type called I, a generic growable hash table data type called I and a decent I data type that comes with heaps of functions (many lifted from I). There are also abstract singly and doubly linked list data types with optional, "growable" freelists. =item Decoupled Thread Safety I provides the I data type which decouples thread synchronisation strategies from client code. This allows code to be written that delegates the choice of synchronisation strategy to the client. This enables run time selection of locking strategies which means it's even possible to let the end user specify which locking strategy to use. It supports mutual exclusion locks, readers/writer locks and no locking. There are also debug versions that print messages to standard output to help clients locate deadlocks. See C. =item Coprocesses and Pseudo Terminals I provides functions for creating pseudo terminals portably and for creating coprocesses that use either pipes or a pseudo terminal for communication with the client process. =item Miscellaneous I contains convenience/shorthand functions for random things such as reading a line of text with any line ending (UNIX, DOS or Macintosh), fifo and file control, retrieving POSIX.1 limits, parsing syslog facility/priority pairs, dynamic allocation of multi-dimensional arrays, memory pools, secure memory, secure memory pools, There's also a heap sort function. And there are also implementations of GNU I, I, I, I, I, I, I and I for systems that don't have them. =item Low Level API Although there is a lot of functionality in I, the API itself is as low-level as possible. There are no gratuitous data structures that wrap around perfectly acceptable data structures that are provided by the system. For instance, the networking functions do not return some home grown I object. They return file descriptors just like underlying system calls. The coprocess API is similar in spirit to I and I. The I function interoperates perfectly with standard I/O. Errors are returned via C just like system calls and some standard library functions. You may not like C if you write threaded programs but I figured that until C goes away, it's best to accept it and find a way to benefit from it. Unavoidably, the string module does wrap an object around perfectly good char pointers but the underlying char pointer is always accessible and many of the string functions have versions that work on ordinary C strings. The purpose of this design is Laziness. It means I don't have to wrap every net related system call or standard string function in an extra function and you don't have to learn a load of new identifiers for functionality that you already know how to access. The same goes for error codes. I don't want to re-invent them and you don't need to re-learn them. It also means that I functions can be more easily incorporated into existing programs to enhance their functionality without huge code rewrites and without the need for a steep learning curve. =item Accurate Documentation Every module has a manpage that explains every function in detail. The function signatures in the manpages are systematically checked against the source code to make sure that they never get out of sync. There are plenty of examples in the manpages. Most of them are systematically compiled and executed to make sure that they never get out of sync with the source code. The actual text of the manpages can't be systematically checked but it does get proofread regularly. If you find any errors of any kind in the documentation, please let me know. All of the documentation takes the form of manpages because manpages are faster and more accessible than anything else. =item Thorough Testing I is thoroughly tested. This doesn't mean that there are no bugs but it does mean that there is a large list of bugs that it doesn't have. Excluding the snprintf module, there are nearly 2,800 tests. Including the snprintf module, there are nearly 150,000 tests. These tests also serve as extra examples. =back =head1 MODULES Libslack contains the following modules: agent - agent oriented programming coproc - coprocesses using pipes or pseudo terminals daemon - becoming a daemon err - message/error/debug/verbosity/alert messaging fio - fifo and file control and some I/O getopt - GNU getopt_long() for systems that don't have it hsort - generic heap sort lim - POSIX.1 limits convenience functions link - abstract linked lists with optional growable free lists list - list (growable pointer array) data type locker - abstract locking and reader/writer lock implementation map - map (hash table) data type mem - memory helper functions, secure memory, memory pools msg - message handling and syslog helper functions net - network functions (clients/servers, expect/send, pack/unpack, mail) prog - program framework and flexible command line option handling prop - program properties files pseudo - pseudo terminals sig - ISO C compliant signal handling snprintf - safe sprintf() for systems that don't have it str - string data type (tr, regexpr, regsub, fmt, trim, lc, uc, ...) vsscanf - sscanf() with va_list argument for systems that don't have it Each module, as well as each function, has its own section 3 manpage. =head1 HEADER FILES Each module has its own header file (see SYNOPSIS). If you want to include these header files individually, you must first include the header file. Alternatively, you can just include the header file and get everything. B Make sure that or is included before any system header files. Otherwise, the system headers won't be set up properly. If I was installed on your system with a prefix added to each identifier, you may also want to include which allows you to continue to use the original I identifiers and have them automatically translated into the prefixed identifiers. Alternatively, if I was installed on your system with the original names and you wish to compile client code that does use a prefix, you can include I<"remove_prefix.h"> which allows you to continue to use the prefixed identifiers and have them automatically translated into the original identifiers. Note that you will probably have to generate this header file yourself (see the prefix program in the tools directory). If your client code uses one prefix and the I installed on your current system uses a different prefix, you will need to include both of these header files. First include I<"remove_prefix.h">. Then include . =head1 EXAMPLES There are examples in all of the module manpages. If you can't find what you're looking for there, look at the test code which can be found at the bottom of each module's source file. The test code executes every function and thus serves as a treasure trove of examples. If you think of an example that really needs to be in the manpages, don't hesitate to let me know. =head1 MAILING LISTS The following mailing lists exist for libslack related discussion: slack-announce.libslack.org - Announcements slack-users@libslack.org - User forum slack-dev@libslack.org - Development forum To subscribe to any of these mailing lists, send a mail message to IC<-request@libslack.org> with C as the message body. e.g. $ mail slack-announce-request@libslack.org subscribe . $ mail slack-users-request@libslack.org subscribe . $ mail slack-dev-request@libslack.org subscribe . Or you can send a mail message to C with C I in the message body. This way, you can subscribe to multiple lists at the same time. e.g. $ mail majordomo@libslack.org subscribe slack-announce subscribe slack-users subscribe slack-dev . A digest version of each mailing list is also available. Subscribe to digests as above but append C<-digest> to the listname. =head1 SEE ALSO C, C, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I =head1 AUTHOR 20100612 raf =cut daemon-0.6.4/libslack/lim.c000066400000000000000000000453731140522741300155100ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - POSIX.1 limits module =head1 SYNOPSIS #include #include long limit_arg(void); long limit_child(void); long limit_tick(void); long limit_group(void); long limit_open(void); long limit_stream(void); long limit_tzname(void); long limit_job(void); long limit_save_ids(void); long limit_version(void); long limit_pcanon(const char *path); long limit_fcanon(int fd); long limit_canon(void); long limit_pinput(const char *path); long limit_finput(int fd); long limit_input(void); long limit_pvdisable(const char *path); long limit_fvdisable(int fd); long limit_vdisable(void); long limit_plink(const char *path); long limit_flink(int fd); long limit_link(void); long limit_pname(const char *path); long limit_fname(int fd); long limit_name(void); long limit_ppath(const char *path); long limit_fpath(int fd); long limit_path(void); long limit_ppipe(const char *path); long limit_fpipe(int fd); long limit_pnotrunc(const char *path); long limit_fnotrunc(int fd); long limit_notrunc(void); long limit_pchown(const char *path); long limit_fchown(int fd); long limit_chown(void); =head1 DESCRIPTION This module provides functions for simply and reliably obtaining a POSIX.1 limit for the current system or a usable default when a particular facility is unlimited on the current system. These functions always return a usable value. =over 4 =cut */ #include "config.h" #include "std.h" #include #include #include "lim.h" #include "err.h" enum name_t { LIMIT_ARG, LIMIT_CHILD, LIMIT_TICK, LIMIT_GROUP, LIMIT_OPEN, LIMIT_STREAM, LIMIT_TZNAME, LIMIT_JOB, LIMIT_SAVE_IDS, LIMIT_VERSION, LIMIT_CANON, LIMIT_INPUT, LIMIT_VDISABLE, LIMIT_LINK, LIMIT_NAME, LIMIT_PATH, LIMIT_PIPE, LIMIT_NOTRUNC, LIMIT_CHOWN, LIMIT_COUNT }; typedef struct conf_t conf_t; struct conf_t { const int name; /* name argument for sysconf, [f]pathconf */ const long value; /* limit to use when indeterminate */ const off_t offset; /* offset to apply to value when not indeterminate */ }; #ifndef TEST static const struct { conf_t conf[LIMIT_COUNT]; } g = { { { _SC_ARG_MAX, 131072, 0 }, { _SC_CHILD_MAX, 1024, 0 }, { _SC_CLK_TCK, -1, 0 }, { _SC_NGROUPS_MAX, 32, 0 }, { _SC_OPEN_MAX, 1024, 0 }, { _SC_STREAM_MAX, 1024, 0 }, { _SC_TZNAME_MAX, 3, 0 }, { _SC_JOB_CONTROL, 0, 0 }, { _SC_SAVED_IDS, 0, 0 }, { _SC_VERSION, 0, 0 }, { _PC_MAX_CANON, 255, 0 }, { _PC_MAX_INPUT, 255, 0 }, { _PC_VDISABLE, 0, 0 }, { _PC_LINK_MAX, 32768, 0 }, { _PC_NAME_MAX, 1024, 0 }, { _PC_PATH_MAX, 4096, 2 }, { _PC_PIPE_BUF, 4096, 0 }, { _PC_NO_TRUNC, 0, 0 }, { _PC_CHOWN_RESTRICTED, 0, 0 } } }; /* C Returns system limits using I. If the limit is indeterminate, a predetermined default value is returned. Whatever happens, a usable value is returned. */ static long limit_sysconf(int limit) { long value; if ((value = sysconf(g.conf[limit].name)) == -1) return g.conf[limit].value; return value; } /* C Returns system limits using I. If the limit is indeterminate, a predetermined default value is returned. If the limit is determinate, a predetermined amount may be added to its value. This is only needed for C<_PC_PATH_MAX> which is the maximum length of a relative path. To be more useful, 2 is added to this limit to account for the C<'/'> and C<'\0'> that will be needed to form an absolute path. Whatever happens, a usable value is returned. */ static long limit_pathconf(int limit, const char *path) { long value; if ((value = pathconf(path, g.conf[limit].name)) == -1) return g.conf[limit].value; return value + g.conf[limit].offset; } /* C Returns system limits using I. If the limit is indeterminate, a predetermined default value is returned. If the limit is determinate, a predetermined amount may be added to its value. This is only needed for C<_PC_PATH_MAX> which is the maximum length of a relative path. To be more useful, 2 is added to this limit to account for the C<'/'> and C<'\0'> that will be needed to form an absolute path. Whatever happens, a usable value is returned. */ static long limit_fpathconf(int limit, int fd) { long value; if ((value = fpathconf(fd, g.conf[limit].name)) == -1) return g.conf[limit].value; return value + g.conf[limit].offset; } /* =item C Returns the maximum length of arguments to the I family of functions. If indeterminate, a usable value (131072) is returned. =cut */ long limit_arg(void) { return limit_sysconf(LIMIT_ARG); } /* =item C Returns the maximum number of simultaneous processes per user id. If indeterminate, a usable value (1024) is returned. =cut */ long limit_child(void) { return limit_sysconf(LIMIT_CHILD); } /* =item C Returns the number of clock ticks per second. If indeterminate (which makes no sense), -1 is returned. This should never happen. =cut */ long limit_tick(void) { return limit_sysconf(LIMIT_TICK); } /* =item C Returns the maximum number of groups that a user may belong to. If indeterminate, a usable value (32) is returned. =cut */ long limit_group(void) { return limit_sysconf(LIMIT_GROUP); } /* =item C Returns the maximum number of files that a process can have open at any time. If indeterminate, a usable value (1024) is returned. =cut */ long limit_open(void) { return limit_sysconf(LIMIT_OPEN); } /* =item C Returns the maximum number of streams that a process can have open at any time. If indeterminate, a usable value (1024) is returned. =cut */ long limit_stream(void) { return limit_sysconf(LIMIT_STREAM); } /* =item C Returns the maximum number of bytes in a timezone name. If indeterminate, a usable value (3) is returned. =cut */ long limit_tzname(void) { return limit_sysconf(LIMIT_TZNAME); } /* =item C Returns whether or not job control is supported. =cut */ long limit_job(void) { return limit_sysconf(LIMIT_JOB); } /* =item C Returns whether or not a process has a saved set-user-id and a saved set-group-id. =cut */ long limit_save_ids(void) { return limit_sysconf(LIMIT_SAVE_IDS); } /* =item C Returns the year and month the POSIX.1 standard was approved in the format YYYYMML. =cut */ long limit_version(void) { return limit_sysconf(LIMIT_VERSION); } /* =item C Returns the maximum length of a formatted input line for the terminal referred to by C. If indeterminate, a usable value (255) is returned. =cut */ long limit_pcanon(const char *path) { return limit_pathconf(LIMIT_CANON, path); } /* =item C Returns the maximum length of a formatted input line for the terminal referred to by C. If indeterminate, a usable value (255) is returned. =cut */ long limit_fcanon(int fd) { return limit_fpathconf(LIMIT_CANON, fd); } /* =item C Returns the maximum length of a formatted input line for the controlling terminal (C). If indeterminate, a usable value (255) is returned. =cut */ long limit_canon(void) { return limit_pcanon("/dev/tty"); } /* =item C Returns the maximum length of an input line for the terminal referred to by C. If indeterminate, a usable value (255) is returned. =cut */ long limit_pinput(const char *path) { return limit_pathconf(LIMIT_INPUT, path); } /* =item C Returns the maximum length of an input line for the terminal referred to by C. If indeterminate, a usable value (255) is returned. =cut */ long limit_finput(int fd) { return limit_fpathconf(LIMIT_INPUT, fd); } /* =item C Returns the maximum length of an input line for the controlling terminal (C). If indeterminate, a usable value (255) is returned. =cut */ long limit_input(void) { return limit_pinput("/dev/tty"); } /* =item C Returns whether or not special character processing can be disabled for the terminal referred to by C. =cut */ long limit_pvdisable(const char *path) { return limit_pathconf(LIMIT_VDISABLE, path); } /* =item C Returns whether or not special character processing can be disabled for the terminal referred to by C. =cut */ long limit_fvdisable(int fd) { return limit_fpathconf(LIMIT_VDISABLE, fd); } /* =item C Returns whether or not special character processing can be disabled for the controlling terminal (C). =cut */ long limit_vdisable(void) { return limit_pvdisable("/dev/tty"); } /* =item C Returns the maximum number of links to the file represented by C. If indeterminate, a usable value (32768) is returned. =cut */ long limit_plink(const char *path) { return limit_pathconf(LIMIT_LINK, path); } /* =item C Returns the maximum number of links to the file represented by C. If indeterminate, a usable value (32768) is returned. =cut */ long limit_flink(int fd) { return limit_fpathconf(LIMIT_LINK, fd); } /* =item C Returns the maximum number of links to the root directory (C). If indeterminate, a usable value (32768) is returned. =cut */ long limit_link(void) { return limit_plink("/"); } /* =item C Returns the maximum length of a filename in the directory referred to by C that the process can create. If indeterminate, a usable value (1024) is returned. =cut */ long limit_pname(const char *path) { return limit_pathconf(LIMIT_NAME, path); } /* =item C Returns the maximum length of a filename in the directory referred to by C that the process can create. If indeterminate, a usable value (1024) is returned. =cut */ long limit_fname(int fd) { return limit_fpathconf(LIMIT_NAME, fd); } /* =item C Returns the maximum length of a filename in the root directory (C) that the process can create. If indeterminate, a usable value (1024) is returned. =cut */ long limit_name(void) { return limit_pname("/"); } /* =item C Returns the maximum length of an absolute pathname (including the C character) when C is the current directory. If indeterminate, a usable value (4096) is returned. =cut */ long limit_ppath(const char *path) { return limit_pathconf(LIMIT_PATH, path); } /* =item C Returns the maximum length of an absolute pathname (including the C character) when C refers to the current directory. If indeterminate, a usable value (4096) is returned. =cut */ long limit_fpath(int fd) { return limit_fpathconf(LIMIT_PATH, fd); } /* =item C Returns the maximum length of an absolute pathname (including the C character). If indeterminate, a usable value (4096) is returned. =cut */ long limit_path(void) { return limit_ppath("/"); } /* =item C Returns the size of the pipe buffer for the fifo referred to by C. If indeterminate, a usable value (4096) is returned. =cut */ long limit_ppipe(const char *path) { return limit_pathconf(LIMIT_PIPE, path); } /* =item C Returns the size of the pipe buffer for the pipe or fifo referred to by C. If indeterminate, a usable value (4096) is returned. =cut */ long limit_fpipe(int fd) { return limit_fpathconf(LIMIT_PIPE, fd); } /* =item C Returns whether or not an error is generated when accessing filenames longer than the maximum filename length for the filesystem referred to by C. =cut */ long limit_pnotrunc(const char *path) { return limit_pathconf(LIMIT_NOTRUNC, path); } /* =item C Returns whether or not an error is generated when accessing filenames longer than the maximum filename length for the filesystem referred to by C. =cut */ long limit_fnotrunc(int fd) { return limit_fpathconf(LIMIT_NOTRUNC, fd); } /* =item C Returns whether or not an error is generated when accessing filenames longer than the maximum filename length for the root filesystem. =cut */ long limit_notrunc(void) { return limit_pnotrunc("/"); } /* =item C Returns whether or not I may be called on the file referred to by C or the files contained in the directory referred to by C. =cut */ long limit_pchown(const char *path) { return limit_pathconf(LIMIT_CHOWN, path); } /* =item C Returns whether or not I may be called on the file referred to by C or the files contained in the directory referred to by C. =cut */ long limit_fchown(int fd) { return limit_fpathconf(LIMIT_CHOWN, fd); } /* =item C Returns whether or not I may be called on the files contained in the root filesystem. =cut */ long limit_chown(void) { return limit_pchown("/"); } /* =back =head1 RETURNS The functions that return a condition return 1 when the condition is true or C<0> when it is false. All of the others either return the system limit indicated or a predetermined, usable value when the indicated limit is indeterminate. These functions never return C<-1>. =head1 MT-Level MT-Safe =head1 EXAMPLES Store the current directory into allocated memory: #include #include int main() { long path_size = limit_path(); char *buf = malloc(path_size * sizeof(char)); if (!buf) return EXIT_FAILURE; printf("%s\n", getcwd(buf, path_size)); return EXIT_SUCCESS; } Close all file descriptors: #include #include int main() { int fd_limit = limit_open(); int fd; for (fd = 0; fd < fd_limit; ++fd) close(fd); return EXIT_SUCCESS; } =head1 SEE ALSO I, I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST int main(int ac, char **av) { int fds[2]; long limit; int errors = 0; int verbose = (ac >= 2 && !strcmp(av[1], "-v")); if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s [-v]\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "lim"); if ((limit = limit_arg()) == -1) ++errors, printf("Test1: limit_arg() failed\n"); else if (verbose) printf("arg = %ld\n", limit); if ((limit = limit_child()) == -1) ++errors, printf("Test2: limit_child() failed\n"); else if (verbose) printf("child = %ld\n", limit); if ((limit = limit_tick()) == -1) ++errors, printf("Test3: limit_tick() failed\n"); else if (verbose) printf("tick = %ld\n", limit); if ((limit = limit_group()) == -1) ++errors, printf("Test4: limit_group() failed\n"); else if (verbose) printf("group = %ld\n", limit); if ((limit = limit_open()) == -1) ++errors, printf("Test5: limit_open() failed\n"); else if (verbose) printf("open = %ld\n", limit); if ((limit = limit_stream()) == -1) ++errors, printf("Test6: limit_stream() failed\n"); else if (verbose) printf("stream = %ld\n", limit); if ((limit = limit_tzname()) == -1) ++errors, printf("Test7: limit_tzname() failed\n"); else if (verbose) printf("tzname = %ld\n", limit); if ((limit = limit_job()) == -1) ++errors, printf("Test8: limit_job() failed\n"); else if (verbose) printf("job = %ld\n", limit); if ((limit = limit_save_ids()) == -1) ++errors, printf("Test9: limit_save_ids() failed\n"); else if (verbose) printf("save_ids = %ld\n", limit); if ((limit = limit_version()) == -1) ++errors, printf("Test10: limit_version() failed\n"); else if (verbose) printf("version = %ld\n", limit); if ((limit = limit_canon()) == -1) ++errors, printf("Test11: limit_canon() failed\n"); else if (verbose) printf("canon = %ld\n", limit); if ((limit = limit_input()) == -1) ++errors, printf("Test12: limit_input() failed\n"); else if (verbose) printf("input = %ld\n", limit); if ((limit = limit_vdisable()) == -1) ++errors, printf("Test13: limit_vdisable() failed\n"); else if (verbose) printf("vdisable = %ld\n", limit); if ((limit = limit_link()) == -1) ++errors, printf("Test14: limit_link() failed\n"); else if (verbose) printf("link = %ld\n", limit); if ((limit = limit_name()) == -1) ++errors, printf("Test15: limit_name() failed\n"); else if (verbose) printf("name = %ld\n", limit); if ((limit = limit_path()) == -1) ++errors, printf("Test16: limit_path() failed\n"); else if (verbose) printf("path = %ld\n", limit); if (pipe(fds) == -1) { ++errors, printf("Test17: failed to test limit_fpipe() - pipe() failed (%s)\n", strerror(errno)); } else { if ((limit = limit_fpipe(fds[0])) == -1) ++errors, printf("Test17: limit_fpipe() failed\n"); else if (verbose) printf("pipe = %ld\n", limit); close(fds[0]); close(fds[1]); } if ((limit = limit_notrunc()) == -1) ++errors, printf("Test18: limit_notrunc() failed\n"); else if (verbose) printf("notrunc = %ld\n", limit); if ((limit = limit_chown()) == -1) ++errors, printf("Test19: limit_chown() failed\n"); else if (verbose) printf("chown = %ld\n", limit); if (errors) printf("%d/19 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/lim.h000066400000000000000000000037311140522741300155050ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_LIM_H #define LIBSLACK_LIM_H #include _begin_decls long limit_arg(void); long limit_child(void); long limit_tick(void); long limit_group(void); long limit_open(void); long limit_stream(void); long limit_tzname(void); long limit_job(void); long limit_save_ids(void); long limit_version(void); long limit_pcanon(const char *path); long limit_fcanon(int fd); long limit_canon(void); long limit_pinput(const char *path); long limit_finput(int fd); long limit_input(void); long limit_pvdisable(const char *path); long limit_fvdisable(int fd); long limit_vdisable(void); long limit_plink(const char *path); long limit_flink(int fd); long limit_link(void); long limit_pname(const char *path); long limit_fname(int fd); long limit_name(void); long limit_ppath(const char *path); long limit_fpath(int fd); long limit_path(void); long limit_ppipe(const char *path); long limit_fpipe(int fd); long limit_pnotrunc(const char *path); long limit_fnotrunc(int fd); long limit_notrunc(void); long limit_pchown(const char *path); long limit_fchown(int fd); long limit_chown(void); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/link.c000066400000000000000000001057201140522741300156550ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - linked list module =head1 SYNOPSIS #include #include typedef struct slink_t slink_t; typedef struct dlink_t dlink_t; struct slink_t { void *next; }; struct dlink_t { void *next; void *prev; }; int slink_has_next(void *link); void *slink_next(void *link); int dlink_has_next(void *link); void *dlink_next(void *link); int dlink_has_prev(void *link); void *dlink_prev(void *link); void *slink_insert(void *link, void *item); void *dlink_insert(void *link, void *item); void *slink_remove(void *link); void *dlink_remove(void *link); void *slink_freelist_init(void *freelist, size_t nelem, size_t size); void *dlink_freelist_init(void *freelist, size_t nelem, size_t size); void *slink_freelist_attach(void *freelist1, void *freelist2); void *dlink_freelist_attach(void *freelist1, void *freelist2); void *slink_alloc(void **freelist); void *dlink_alloc(void **freelist); void *slink_free(void **freelist, void *item); void *dlink_free(void **freelist, void *item); =head1 DESCRIPTION This module provides functions for manipulating singly and doubly linked lists. Two abstract types are defined: I, containing a pointer to the next item, and I, containing pointers to the next and previous items. These functions work with any struct whose first element is an I or a I struct. There is support for optional growable free lists so items may be dynamically allocated individually or allocated from a free list. Free lists can be arrays of structs or dynamically allocated. When a free list is exhausted, further memory may be attached to the free list to extend it. =over 4 =cut */ #include "config.h" #include "std.h" #include "link.h" #include "err.h" #ifndef TEST /* =item C Returns C<1> if C's C pointer is not C. Otherwise, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int slink_has_next(void *link) { if (!link) return set_errno(EINVAL); return (((slink_t *)link)->next != NULL); } /* =item C Returns C's C pointer. On error, returns C with C set appropriately. =cut */ void *slink_next(void *link) { if (!link) return set_errnull(EINVAL); return ((slink_t *)link)->next; } /* =item C Returns C<1> if C's C pointer is not C. Otherwise, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int dlink_has_next(void *link) { if (!link) return set_errno(EINVAL); return (((dlink_t *)link)->next != NULL); } /* =item C Returns C's C pointer. On error, returns C with C set appropriately. =cut */ void *dlink_next(void *link) { if (!link) return set_errnull(EINVAL); return ((dlink_t *)link)->next; } /* =item C Returns C<1> if C's C pointer is not C. Otherwise, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int dlink_has_prev(void *link) { if (!link) return set_errno(EINVAL); return (((dlink_t *)link)->prev != NULL); } /* =item C Returns C's C pointer. On error, returns C with C set appropriately. =cut */ void *dlink_prev(void *link) { if (!link) return set_errnull(EINVAL); return ((dlink_t *)link)->prev; } /* =item C Inserts C before C. Returns C. On error, returns C with C set appropriately. Items may only be inserted at the beginning of a singly linked list. =cut */ void *slink_insert(void *link, void *item) { slink_t *insert; if (!item) return set_errnull(EINVAL); insert = item; insert->next = link; return insert; } /* =item C Inserts C before C. Returns C. On error, returns C with C set appropriately. Items may be inserted anywhere in a doubly linked list. =cut */ void *dlink_insert(void *link, void *item) { dlink_t *insert, *next, *prev; if (!item) return set_errnull(EINVAL); insert = item; next = link; prev = (next) ? next->prev : NULL; insert->next = next; insert->prev = prev; if (next) next->prev = insert; if (prev) prev->next = insert; return insert; } /* =item C Removes the first item from the list beginning with C. On success, returns C's C pointer. On error, returns C with C set appropriately. =cut */ void *slink_remove(void *link) { slink_t *remove; if (!link) return set_errnull(EINVAL); remove = link; return remove->next; } /* =item C Removes C from the list of which it is part. On success, returns C's C pointer. On error, returns C with C set appropriately. =cut */ void *dlink_remove(void *link) { dlink_t *remove; if (!link) return set_errnull(EINVAL); remove = link; if (remove->next) ((dlink_t *)remove->next)->prev = remove->prev; if (remove->prev) ((dlink_t *)remove->prev)->next = remove->next; return remove->next; } /* =item C Initialises an array of C elements each C bytes for use as a singly linked free list. On success, returns C. On error, returns C with C set appropriately. =cut */ void *slink_freelist_init(void *freelist, size_t nelem, size_t size) { char *link; if (!freelist || !nelem || !size) return set_errnull(EINVAL); for (link = freelist; --nelem; link += size) { ((slink_t *)link)->next = link + size; } ((slink_t *)link)->next = NULL; return freelist; } /* =item C Initialises an array of C elements each C bytes for use as a doubly linked free list. On success, returns C. On error, returns C with C set appropriately. =cut */ void *dlink_freelist_init(void *freelist, size_t nelem, size_t size) { char *link, *prev; if (!freelist || !nelem || !size) return set_errnull(EINVAL); for (prev = NULL, link = freelist; --nelem; prev = link, link += size) { ((dlink_t *)link)->next = link + size; ((dlink_t *)link)->prev = prev; } ((dlink_t *)link)->next = NULL; ((dlink_t *)link)->prev = prev; return freelist; } /* =item C Attaches C to the end of C. Both free lists must have already been initialised with I. Note that it will not be possible to separate these free lists. On success, returns a pointer to the beginning of the combined freelist. On error, returns C with C set appropriately. =cut */ void *slink_freelist_attach(void *freelist1, void *freelist2) { char *freelist; if (!freelist2) return set_errnull(EINVAL); if (!(freelist = freelist1)) return freelist2; while (slink_has_next(freelist) == 1) freelist = slink_next(freelist); ((slink_t *)freelist)->next = freelist2; return freelist1; } /* =item C Attaches C to the end of C. Both free lists must have already been initialised with I. Note that it will not be possible to separate these free lists. On success, returns a pointer to the beginning of the combined freelist. On error, returns C with C set appropriately. =cut */ void *dlink_freelist_attach(void *freelist1, void *freelist2) { char *freelist; if (!freelist2) return set_errnull(EINVAL); if (!(freelist = freelist1)) return freelist2; while (dlink_has_next(freelist)) freelist = dlink_next(freelist); ((dlink_t *)freelist)->next = freelist2; ((dlink_t *)freelist2)->prev = freelist; return freelist1; } /* =item C Allocates an item from C<*freelist> and updates C<*freelist> to point to the next free item. C<*freelist> must be a singly linked freelist initialised with I. On success, returns the allocated item. On error, returns C with C set appropriately. =cut */ void *slink_alloc(void **freelist) { void *alloc; if (!freelist) return set_errnull(EINVAL); if (!*freelist) return set_errnull(ENOSPC); alloc = *freelist; *freelist = slink_remove(*freelist); return alloc; } /* =item C Allocates an item from C<*freelist> and updates C<*freelist> to point to the next free item. C<*freelist> must be a doubly linked freelist initialised with I. On success, returns the allocated item. On error, returns C with C set appropriately. =cut */ void *dlink_alloc(void **freelist) { void *alloc; if (!freelist) return set_errnull(EINVAL); if (!*freelist) return set_errnull(ENOSPC); alloc = *freelist; *freelist = dlink_remove(*freelist); return alloc; } /* =item C Inserts C into C<*freelist> and updates C<*freelist> to point to I. C<*freelist> must be a singly linked freelist initialised with I. On success, returns the resulting free list. On error, returns C with C set appropriately. =cut */ void *slink_free(void **freelist, void *item) { if (!freelist || !item) return set_errnull(EINVAL); return *freelist = slink_insert(*freelist, item); } /* =item C Inserts C into C<*freelist> and updates C<*freelist> to point to C. C<*freelist> must be a doubly linked freelist initialised with I. On success, returns the resulting free list. On error, returns C with C set appropriately. =cut */ void *dlink_free(void **freelist, void *item) { if (!freelist || !item) return set_errnull(EINVAL); return *freelist = dlink_insert(*freelist, item); } /* =back =head1 ERRORS The following errors are returned by these functions. =over 4 =item C When C pointers are incorrectly passed as arguments to most functions. =item C When I or I is called and the free list is exhausted. =back =head1 MT-Level Unsafe This module declares abstract types. They must be used as part of larger data structures. It is assumed that the surrounding data structure and its functions will provide any locking that is required. =head1 EXAMPLES A singly linked example that reads pairs of numbers from stdin (attaching more space to the list as necessary), iterates over the items and then deletes them: #include #include typedef struct spoint_t spoint_t; struct spoint_t { slink_t link; int x; int y; }; #define SLIST_SIZE 10 #define MAX_ADDITIONS 100 spoint_t sfreespace[SLIST_SIZE]; spoint_t *sfreelist = sfreespace; spoint_t *spoints = NULL; spoint_t *additional[MAX_ADDITIONS]; int added = 0; int main(int ac, char **av) { spoint_t *item, *morespace; int x, y, i; // Initialize the singly-linked list of points if (slink_freelist_init(sfreespace, SLIST_SIZE, sizeof(spoint_t)) != sfreespace) return EXIT_FAILURE; // Read coordinates from stdin and populate the list while (scanf("%d %d", &x, &y) == 2) { // Add more space to the list when it runs out if (!(item = slink_alloc((void **)&sfreelist))) { if (added == MAX_ADDITIONS) return EXIT_FAILURE; // or extend additional if (!(morespace = malloc(SLIST_SIZE * sizeof(spoint_t)))) return EXIT_FAILURE; additional[added++] = morespace; // remember to free this if (slink_freelist_init(morespace, SLIST_SIZE, sizeof(spoint_t)) != morespace) return EXIT_FAILURE; if (!(sfreelist = slink_freelist_attach(sfreelist, morespace))) return EXIT_FAILURE; if (!(item = slink_alloc((void **)&sfreelist))) return EXIT_FAILURE; } // Initialize the item item->x = x; item->y = y; // Insert it into the list if (!(spoints = slink_insert(spoints, item))) return EXIT_FAILURE; } // Iterate over the list with slink_next() for (item = spoints; item; item = slink_next(item)) printf("%d %d\n", item->x, item->y); // Iterate over the list with slink_has_next() for (item = spoints; slink_has_next(item) == 1; item = slink_next(item)) { spoint_t *next = slink_next(item); printf("%d %d -> %d %d\n", item->x, item->y, next->x, next->y); } if (item) printf("%d %d !\n", item->x, item->y); // Remove the items (printing them out) while (spoints) { spoints = slink_remove(item = spoints); printf("%d %d\n", item->x, item->y); slink_free((void **)&sfreelist, item); } // Deallocate any attached freelists for (i = 0; i < added; ++i) free(additional[i]); return EXIT_SUCCESS; } A doubly linked example that reads pairs of numbers from stdin (attaching more space to the list as necessary), iterates over the items and then deletes them: #include #include typedef struct dpoint_t dpoint_t; struct dpoint_t { dlink_t link; int x; int y; }; #define DLIST_SIZE 10 #define MAX_ADDITIONS 100 dpoint_t dfreespace[DLIST_SIZE]; dpoint_t *dfreelist = dfreespace; dpoint_t *dpoints = NULL; dpoint_t *additional[MAX_ADDITIONS]; int added = 0; int main(int ac, char **av) { dpoint_t *item, *morespace; dpoint_t *last; int x, y, i; // Initialize the doubly-linked list of points if (dlink_freelist_init(dfreespace, DLIST_SIZE, sizeof(dpoint_t)) != dfreespace) return EXIT_FAILURE; // Read coordinates from stdin and populate the list while (scanf("%d %d", &x, &y) == 2) { // Add more space to the list when it runs out if (!(item = dlink_alloc((void **)&dfreelist))) { if (added == MAX_ADDITIONS) return EXIT_FAILURE; // or extend additional if (!(morespace = malloc(DLIST_SIZE * sizeof(dpoint_t)))) return EXIT_FAILURE; additional[added++] = morespace; // remember to free this if (dlink_freelist_init(morespace, DLIST_SIZE, sizeof(dpoint_t)) != morespace) return EXIT_FAILURE; if (!(dfreelist = dlink_freelist_attach(dfreelist, morespace))) return EXIT_FAILURE; if (!(item = dlink_alloc((void **)&dfreelist))) return EXIT_FAILURE; } // Initialize the item item->x = x; item->y = y; // Insert it into the list if (!(dpoints = dlink_insert(dpoints, item))) return EXIT_FAILURE; } // Iterate over the list with dlink_next() for (item = dpoints; item; item = dlink_next(item)) { dpoint_t *prev = dlink_prev(item); dpoint_t *next = dlink_next(item); if (prev && next) printf("%d %d -> %d %d -> %d %d\n", prev->x, prev->y, item->x, item->y, next->x, next->y); else if (prev) printf("%d %d -> %d %d -> end\n", prev->x, prev->y, item->x, item->y); else if (next) printf("start -> %d %d -> %d %d\n", item->x, item->y, next->x, next->y); } // Iterate backwards with dlink_has_next() and dlink_prev() for (item = dpoints; dlink_has_next(item) == 1; item = dlink_next(item)) {} for (; item; item = dlink_prev(item)) { dpoint_t *prev = dlink_prev(item); dpoint_t *next = dlink_next(item); if (prev && next) printf("%d %d -> %d %d -> %d %d\n", prev->x, prev->y, item->x, item->y, next->x, next->y); else if (prev) printf("%d %d -> %d %d -> end\n", prev->x, prev->y, item->x, item->y); else if (next) printf("start -> %d %d -> %d %d\n", item->x, item->y, next->x, next->y); } // Remove the items (printing them out) while (dpoints) { dpoints = dlink_remove(item = dpoints); printf("%d %d\n", item->x, item->y); dlink_free((void **)&dfreelist, item); } // Deallocate any attached freelists for (i = 0; i < added; ++i) free(additional[i]); return EXIT_SUCCESS; } =head1 BUGS These functions only work on structs where the C and C pointers at the first elements. To fix this would require adding an C parameter to each function to tell it where the C and C pointers were within the item. It's probably not worth it. Attached free lists can't be detached. To change this would require more code and more metadata. Again, it's probably not worth it. =head1 SEE ALSO I, I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST typedef struct spoint_t spoint_t; typedef struct dpoint_t dpoint_t; struct spoint_t { slink_t link; int x; int y; }; struct dpoint_t { dlink_t link; int x; int y; }; #define SLIST_SIZE 10 #define DLIST_SIZE 10 spoint_t sfreespace1[SLIST_SIZE]; spoint_t sfreespace2[SLIST_SIZE]; dpoint_t dfreespace1[DLIST_SIZE]; dpoint_t dfreespace2[DLIST_SIZE]; spoint_t *sfreelist = sfreespace1; dpoint_t *dfreelist = dfreespace1; spoint_t *spoints = NULL; dpoint_t *dpoints = NULL; int main(int ac, char **av) { int errors = 0; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "link"); /* Test singly linked lists */ /* Test slink_freelist_init() */ if (slink_freelist_init(sfreespace1, SLIST_SIZE, sizeof(spoint_t)) != sfreespace1) ++errors, printf("Test1: slink_freelist_init() failed (%s)\n", strerror(errno)); else { spoint_t *item; size_t i; /* Test slink_alloc(), slink_insert() */ for (i = 0; i < SLIST_SIZE; ++i) { if (!(item = slink_alloc((void **)&sfreelist))) ++errors, printf("Test2: slink_alloc() failed (%s)\n", strerror(errno)); else if (!(spoints = slink_insert(spoints, item))) ++errors, printf("Test3: slink_insert() failed (%s)\n", strerror(errno)); else { item->x = i; item->y = i + 1; } } if ((item = slink_alloc((void **)&sfreelist))) ++errors, printf("Test4: slink_alloc() with no space failed (item = %p, not null)\n", (void *)item); else if (errno != ENOSPC) ++errors, printf("Test5: slink_alloc() with no space failed (errno = %s, not %s)\n", strerror(errno), strerror(ENOSPC)); /* Test slink_next() */ for (i = SLIST_SIZE - 1, item = spoints; item; --i, item = slink_next(item)) { if (item->x != i) ++errors, printf("Test6: slink_next() failed (item%d->x == %d (not %d)\n", (int)i, item->x, (int)i); if (item->y != i + 1) ++errors, printf("Test7: slink_next() failed (item%d->y == %d (not %d)\n", (int)i, item->y, (int)i + 1); } if (i != -1) ++errors, printf("Test8: slink_next() failed (only %d items, not %d)\n", (int)i + SLIST_SIZE + 1, SLIST_SIZE); /* Test slink_remove(), slink_free() */ for (i = 0; i < SLIST_SIZE; ++i) { spoints = slink_remove(item = spoints); if (!spoints && i < SLIST_SIZE - 1) ++errors, printf("Test9: slink_remove() failed (i = %d)\n", (int)i); if (!slink_free((void **)&sfreelist, item)) ++errors, printf("Test10: slink_free() failed (i = %d)\n", (int)i); } if (spoints) ++errors, printf("Test11: slink_remove() failed\n"); /* Test slink_freelist_attach() */ if (slink_freelist_init(sfreespace2, SLIST_SIZE, sizeof(spoint_t)) != sfreespace2) ++errors, printf("Test12: slink_freelist_init() failed (%s)\n", strerror(errno)); else if (!(sfreelist = slink_freelist_attach(sfreelist, sfreespace2))) ++errors, printf("Test13: slink_freelist_attach() failed (%s)\n", strerror(errno)); { spoint_t *item; size_t i; /* Test slink_alloc(), slink_insert() */ for (i = 0; i < SLIST_SIZE * 2; ++i) { if (!(item = slink_alloc((void **)&sfreelist))) ++errors, printf("Test14: slink_alloc() failed (%s)\n", strerror(errno)); else if (!(spoints = slink_insert(spoints, item))) ++errors, printf("Test15: slink_insert() failed (%s)\n", strerror(errno)); else { item->x = i; item->y = i + 1; } } if ((item = slink_alloc((void **)&sfreelist))) ++errors, printf("Test16: slink_alloc() with no space failed (item = %p, not null)\n", (void *)item); else if (errno != ENOSPC) ++errors, printf("Test17: slink_alloc() with no space failed (errno = %s, not %s)\n", strerror(errno), strerror(ENOSPC)); /* Test slink_next() */ for (i = SLIST_SIZE * 2 - 1, item = spoints; item; --i, item = slink_next(item)) { if (item->x != i) ++errors, printf("Test18: slink_next() failed (item%d->x == %d (not %d)\n", (int)i, item->x, (int)i); if (item->y != i + 1) ++errors, printf("Test19: slink_next() failed (item%d->y == %d (not %d)\n", (int)i, item->y, (int)i + 1); } if (i != -1) ++errors, printf("Test20: slink_next() failed (only %d items, not %d)\n", (int)i + SLIST_SIZE * 2 + 1, SLIST_SIZE * 2); /* Test slink_remove(), slink_free() */ for (i = SLIST_SIZE * 2 - 1; spoints; --i) { spoints = slink_remove(item = spoints); if (!spoints && i >= -1) ++errors, printf("Test21: slink_remove() failed (i = %d)\n", (int)i); if (!slink_free((void **)&sfreelist, item)) ++errors, printf("Test22: slink_free() failed (i = %d)\n", (int)i); } if (i != -1) ++errors, printf("Test23: slink_remove() failed (i = %d, not -1)\n", (int)i); if (spoints) ++errors, printf("Test24: slink_remove() failed (spoints = %p, not null)\n", (void *)spoints); } } /* Test doubly linked lists */ /* Test dlink_freelist_init() */ if (dlink_freelist_init(dfreespace1, DLIST_SIZE, sizeof(dpoint_t)) != dfreespace1) ++errors, printf("Test25: dlink_freelist_init() failed (%s)\n", strerror(errno)); else { dpoint_t *item, *last; size_t i; /* Test dlink_alloc(), dlink_insert() */ for (i = 0; i < DLIST_SIZE; ++i) { if (!(item = dlink_alloc((void **)&dfreelist))) ++errors, printf("Test26: dlink_alloc() failed (%s)\n", strerror(errno)); else if (!(dpoints = dlink_insert(dpoints, item))) ++errors, printf("Test27: dlink_insert() failed (%s)\n", strerror(errno)); else { item->x = i; item->y = i + 1; } } if ((item = dlink_alloc((void **)&dfreelist))) ++errors, printf("Test28: dlink_alloc() with no space failed (item = %p, not null)\n", (void *)item); else if (errno != ENOSPC) ++errors, printf("Test29: dlink_alloc() with no space failed (errno = %s, not %s)\n", strerror(errno), strerror(ENOSPC)); /* Test dlink_next */ for (i = DLIST_SIZE - 1, item = dpoints; item; --i, item = dlink_next(item)) { if (item->x != i) ++errors, printf("Test30: dlink_next() failed (item%d->x == %d (not %d)\n", (int)i, item->x, (int)i); if (item->y != i + 1) ++errors, printf("Test31: dlink_next() failed (item%d->y == %d (not %d)\n", (int)i, item->y, (int)i + 1); last = item; } if (i != -1) ++errors, printf("Test32: dlink_next() failed (only %d items, not %d)\n", (int)i + DLIST_SIZE + 1, DLIST_SIZE); /* Test dlink_prev() */ for (item = last, ++i; item; ++i, item = dlink_prev(item)) { if (item->x != i) ++errors, printf("Test33: dlink_prev() failed (item%d->x == %d (not %d)\n", (int)i, item->x, (int)i); if (item->y != i + 1) ++errors, printf("Test34: dlink_prev() failed (item%d->y == %d (not %d)\n", (int)i, item->y, (int)i + 1); } if (i != DLIST_SIZE) ++errors, printf("Test35: dlink_prev() failed (only %d items, not %d)\n", (int)i, DLIST_SIZE); /* Test dlink_remove(), dlink_free() */ for (i = 0; i < DLIST_SIZE; ++i) { dpoints = dlink_remove(item = dpoints); if (!dpoints && i < DLIST_SIZE - 1) ++errors, printf("Test36: dlink_remove() failed (i = %d)\n", (int)i); if (!dlink_free((void **)&dfreelist, item)) ++errors, printf("Test37: dlink_free() failed (i = %d)\n", (int)i); } if (dpoints) ++errors, printf("Test38: dlink_remove() failed\n"); /* Test dlink_freelist_attach() */ if (dlink_freelist_init(dfreespace2, DLIST_SIZE, sizeof(dpoint_t)) != dfreespace2) ++errors, printf("Test39: dlink_freelist_init() failed (%s)\n", strerror(errno)); else if (!(dfreelist = dlink_freelist_attach(dfreelist, dfreespace2))) ++errors, printf("Test40: dlink_freelist_attach() failed (%s)\n", strerror(errno)); { dpoint_t *item; size_t i; /* Test dlink_alloc(), dlink_insert() */ for (i = 0; i < DLIST_SIZE * 2; ++i) { if (!(item = dlink_alloc((void **)&dfreelist))) ++errors, printf("Test41: dlink_alloc() failed (%s)\n", strerror(errno)); else if (!(dpoints = dlink_insert(dpoints, item))) ++errors, printf("Test42: dlink_insert() failed (%s)\n", strerror(errno)); else { item->x = i; item->y = i + 1; } } if ((item = dlink_alloc((void **)&dfreelist))) ++errors, printf("Test43: dlink_alloc() with no space failed (item = %p, not null)\n", (void *)item); else if (errno != ENOSPC) ++errors, printf("Test44: dlink_alloc() with no space failed (errno = %s, not %s)\n", strerror(errno), strerror(ENOSPC)); /* Test dlink_next() */ for (i = 0, item = dpoints; item; ++i, item = dlink_next(item)) { if (item->x != DLIST_SIZE * 2 - 1 - i) ++errors, printf("Test45: dlink_next() failed (item%d->x == %d (not %d)\n", (int)i, item->x, DLIST_SIZE * 2 - (int)i - 1); if (item->y != DLIST_SIZE * 2 - 1 - i + 1) ++errors, printf("Test46: dlink_next() failed (item%d->y == %d (not %d)\n", (int)i, item->y, DLIST_SIZE * 2 - (int)i); } if (i != DLIST_SIZE * 2) ++errors, printf("Test47: dlink_next() failed (only %d items, not %d)\n", (int)i, DLIST_SIZE * 2); /* Test dlink_prev() */ for (item = last; item; --i, item = dlink_prev(item)) { if (item->x != DLIST_SIZE * 2 - i) ++errors, printf("Test48: dlink_prev() failed (item%d->x == %d (not %d)\n", (int)i, item->x, DLIST_SIZE * 2 - (int)i); if (item->y != DLIST_SIZE * 2 - i + 1) ++errors, printf("Test49: dlink_prev() failed (item%d->y == %d (not %d)\n", (int)i, item->y, DLIST_SIZE * 2 - (int)i + 1); last = item; } if (i != 0) ++errors, printf("Test50: dlink_prev() failed (only %d items, not %d)\n", DLIST_SIZE - (int)i, DLIST_SIZE); /* Test dlink_remove(), dlink_free() */ for (i = 0; i < DLIST_SIZE * 2; ++i) { dpoints = dlink_remove(item = dpoints); if (!dpoints && i < DLIST_SIZE * 2 - 1) ++errors, printf("Test51: dlink_remove() failed (i = %d)\n", (int)i); if (!dlink_free((void **)&dfreelist, item)) ++errors, printf("Test52: dlink_free() failed (i = %d)\n", (int)i); } if (dpoints) ++errors, printf("Test53: dlink_remove() failed\n"); } } /* Test errors */ if (slink_has_next(NULL) != -1) ++errors, printf("Test54: slink_has_next(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test55: slink_has_next(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_next(NULL)) ++errors, printf("Test56: slink_next(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test57: slink_next(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_has_next(NULL) != -1) ++errors, printf("Test58: dlink_has_next(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test59: dlink_has_next(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_next(NULL)) ++errors, printf("Test60: dlink_next(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test61: dlink_next(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_has_prev(NULL) != -1) ++errors, printf("Test62: dlink_has_prev(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test63: dlink_has_prev(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_prev(NULL)) ++errors, printf("Test64: dlink_prev(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test65: dlink_prev(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_insert(NULL, NULL)) ++errors, printf("Test66: slink_insert(NULL, NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test67: slink_insert(NULL, NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_insert(NULL, NULL)) ++errors, printf("Test68: dlink_insert(NULL, NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test69: dlink_insert(NULL, NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_remove(NULL)) ++errors, printf("Test70: slink_remove(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test71: slink_remove(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_remove(NULL)) ++errors, printf("Test72: dlink_remove(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test73: dlink_remove(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_freelist_init(NULL, 1, 1)) ++errors, printf("Test74: slink_freelist_init(NULL, 1, 1) failed\n"); else if (errno != EINVAL) ++errors, printf("Test75: slink_freelist_init(NULL, 1, 1) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_freelist_init(sfreespace1, 0, 1)) ++errors, printf("Test76: slink_freelist_init(sfreespace1, 0, 1) failed\n"); else if (errno != EINVAL) ++errors, printf("Test77: slink_freelist_init(sfreespace1, 0, 1) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_freelist_init(sfreespace1, 1, 0)) ++errors, printf("Test78: slink_freelist_init(sfreespace1, 1, 0) failed\n"); else if (errno != EINVAL) ++errors, printf("Test79: slink_freelist_init(sfreespace1, 1, 0) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_freelist_init(NULL, 1, 1)) ++errors, printf("Test80: dlink_freelist_init(NULL, 1, 1) failed\n"); else if (errno != EINVAL) ++errors, printf("Test81: dlink_freelist_init(NULL, 1, 1) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_freelist_init(dfreespace1, 0, 1)) ++errors, printf("Test82: dlink_freelist_init(dfreespace1, 0, 1) failed\n"); else if (errno != EINVAL) ++errors, printf("Test83: dlink_freelist_init(dfreespace1, 0, 1) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_freelist_init(dfreespace1, 1, 0)) ++errors, printf("Test84: dlink_freelist_init(dfreespace1, 1, 0) failed\n"); else if (errno != EINVAL) ++errors, printf("Test85: dlink_freelist_init(dfreespace1, 1, 0) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_freelist_attach(sfreelist, NULL)) ++errors, printf("Test86: slink_freelist_attach(sfreelist, NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test87: slink_freelist_attach(sfreelist, NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_freelist_attach(dfreelist, NULL)) ++errors, printf("Test88: dlink_freelist_attach(dfreelist, NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test89: dlink_freelist_attach(dfreelist, NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_alloc(NULL)) ++errors, printf("Test90: slink_alloc(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test91: slink_alloc(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); sfreelist = NULL; if (slink_alloc((void **)&sfreelist)) ++errors, printf("Test92: slink_alloc(&NULL) failed\n"); else if (errno != ENOSPC) ++errors, printf("Test93: slink_alloc(&NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_alloc(NULL)) ++errors, printf("Test94: dlink_alloc(NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test95: dlink_alloc(NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); dfreelist = NULL; if (dlink_alloc((void **)&dfreelist)) ++errors, printf("Test96: dlink_alloc(&NULL) failed\n"); else if (errno != ENOSPC) ++errors, printf("Test97: dlink_alloc(&NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_free(NULL, (void *)1)) ++errors, printf("Test98: slink_free(NULL, ...) failed\n"); else if (errno != EINVAL) ++errors, printf("Test99: slink_free(NULL, ...) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (slink_free((void **)&sfreelist, NULL)) ++errors, printf("Test100: slink_free(&sfreelist, NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test101: slink_free(&sfreelist, NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_free(NULL, (void *)1)) ++errors, printf("Test102: dlink_free(NULL, ...) failed\n"); else if (errno != EINVAL) ++errors, printf("Test103: dlink_free(NULL, ...) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (dlink_free((void **)&dfreelist, NULL)) ++errors, printf("Test104: dlink_free(&sfreelist, NULL) failed\n"); else if (errno != EINVAL) ++errors, printf("Test105: dlink_free(&sfreelist, NULL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (errors) printf("%d/105 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/link.h000066400000000000000000000035721140522741300156640ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_LINK_H #define LIBSLACK_LINK_H #include typedef struct slink_t slink_t; typedef struct dlink_t dlink_t; struct slink_t { void *next; }; struct dlink_t { void *next; void *prev; }; _begin_decls int slink_has_next(void *link); void *slink_next(void *link); int dlink_has_next(void *link); void *dlink_next(void *link); int dlink_has_prev(void *link); void *dlink_prev(void *link); void *slink_insert(void *link, void *item); void *dlink_insert(void *link, void *item); void *slink_remove(void *link); void *dlink_remove(void *link); void *slink_freelist_init(void *freelist, size_t nelem, size_t size); void *dlink_freelist_init(void *freelist, size_t nelem, size_t size); void *slink_freelist_attach(void *freelist1, void *freelist2); void *dlink_freelist_attach(void *freelist1, void *freelist2); void *slink_alloc(void **freelist); void *dlink_alloc(void **freelist); void *slink_free(void **freelist, void *item); void *dlink_free(void **freelist, void *item); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/list.c000066400000000000000000003302421140522741300156720ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - list module =head1 SYNOPSIS #include #include typedef struct List List; typedef struct Lister Lister; typedef void list_release_t(void *item); typedef void *list_copy_t(const void *item); typedef int list_cmp_t(const void *a, const void *b); typedef void list_action_t(void *item, size_t *index, void *data); typedef void *list_map_t(void *item, size_t *index, void *data); typedef int list_query_t(void *item, size_t *index, void *data); List *list_create(list_release_t *destroy); List *list_make(list_release_t *destroy, ...); List *list_vmake(list_release_t *destroy, va_list args); List *list_copy(const List *src, list_copy_t *copy); List *list_create_with_locker(Locker *locker, list_release_t *destroy); List *list_make_with_locker(Locker *locker, list_release_t *destroy, ...); List *list_vmake_with_locker(Locker *locker, list_release_t *destroy, va_list args); List *list_copy_with_locker(Locker *locker, const List *src, list_copy_t *copy); int list_rdlock(const List *list); int list_wrlock(const List *list); int list_unlock(const List *list); void list_release(List *list); void *list_destroy(List **list); int list_own(List *list, list_release_t *destroy); int list_own_unlocked(List *list, list_release_t *destroy); list_release_t *list_disown(List *list); list_release_t *list_disown_unlocked(List *list); void *list_item(const List *list, ssize_t index); void *list_item_unlocked(const List *list, ssize_t index); int list_item_int(const List *list, ssize_t index); int list_item_int_unlocked(const List *list, ssize_t index); int list_empty(const List *list); int list_empty_unlocked(const List *list); ssize_t list_length(const List *list); ssize_t list_length_unlocked(const List *list); ssize_t list_last(const List *list); ssize_t list_last_unlocked(const List *list); List *list_remove(List *list, ssize_t index); List *list_remove_unlocked(List *list, ssize_t index); List *list_remove_range(List *list, ssize_t index, ssize_t range); List *list_remove_range_unlocked(List *list, ssize_t index, ssize_t range); List *list_insert(List *list, ssize_t index, void *item); List *list_insert_unlocked(List *list, ssize_t index, void *item); List *list_insert_int(List *list, ssize_t index, int item); List *list_insert_int_unlocked(List *list, ssize_t index, int item); List *list_insert_list(List *list, ssize_t index, const List *src, list_copy_t *copy); List *list_insert_list_unlocked(List *list, ssize_t index, const List *src, list_copy_t *copy); List *list_append(List *list, void *item); List *list_append_unlocked(List *list, void *item); List *list_append_int(List *list, int item); List *list_append_int_unlocked(List *list, int item); List *list_append_list(List *list, const List *src, list_copy_t *copy); List *list_append_list_unlocked(List *list, const List *src, list_copy_t *copy); List *list_prepend(List *list, void *item); List *list_prepend_unlocked(List *list, void *item); List *list_prepend_int(List *list, int item); List *list_prepend_int_unlocked(List *list, int item); List *list_prepend_list(List *list, const List *src, list_copy_t *copy); List *list_prepend_list_unlocked(List *list, const List *src, list_copy_t *copy); List *list_replace(List *list, ssize_t index, ssize_t range, void *item); List *list_replace_unlocked(List *list, ssize_t index, ssize_t range, void *item); List *list_replace_int(List *list, ssize_t index, ssize_t range, int item); List *list_replace_int_unlocked(List *list, ssize_t index, ssize_t range, int item); List *list_replace_list(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy); List *list_replace_list_unlocked(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy); List *list_extract(const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_unlocked(const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_with_locker(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_with_locker_unlocked(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_push(List *list, void *item); List *list_push_unlocked(List *list, void *item); List *list_push_int(List *list, int item); List *list_push_int_unlocked(List *list, int item); void *list_pop(List *list); void *list_pop_unlocked(List *list); int list_pop_int(List *list); int list_pop_int_unlocked(List *list); void *list_shift(List *list); void *list_shift_unlocked(List *list); int list_shift_int(List *list); int list_shift_int_unlocked(List *list); List *list_unshift(List *list, void *item); List *list_unshift_unlocked(List *list, void *item); List *list_unshift_int(List *list, int item); List *list_unshift_int_unlocked(List *list, int item); List *list_splice(List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_unlocked(List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_with_locker(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_with_locker_unlocked(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_sort(List *list, list_cmp_t *cmp); List *list_sort_unlocked(List *list, list_cmp_t *cmp); void list_apply(List *list, list_action_t *action, void *data); void list_apply_rdlocked(List *list, list_action_t *action, void *data); void list_apply_wrlocked(List *list, list_action_t *action, void *data); void list_apply_unlocked(List *list, list_action_t *action, void *data); List *list_map(List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_unlocked(List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_with_locker(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_with_locker_unlocked(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_grep(List *list, list_query_t *grep, void *data); List *list_grep_unlocked(List *list, list_query_t *grep, void *data); List *list_grep_with_locker(Locker *locker, List *list, list_query_t *grep, void *data); List *list_grep_with_locker_unlocked(Locker *locker, List *list, list_query_t *grep, void *data); ssize_t list_query(List *list, ssize_t *index, list_query_t *query, void *data); ssize_t list_query_unlocked(List *list, ssize_t *index, list_query_t *query, void *data); Lister *lister_create(List *list); Lister *lister_create_rdlocked(List *list); Lister *lister_create_wrlocked(List *list); Lister *lister_create_unlocked(const List *list); void lister_release(Lister *lister); void lister_release_unlocked(Lister *lister); void *lister_destroy(Lister **lister); void *lister_destroy_unlocked(Lister **lister); int lister_has_next(Lister *lister); void *lister_next(Lister *lister); int lister_next_int(Lister *lister); void lister_remove(Lister *lister); int list_has_next(List *list); void list_break(List *list); void *list_next(List *list); int list_next_int(List *list); void list_remove_current(List *list); =head1 DESCRIPTION This module provides functions for manipulating and iterating over lists of homogeneous data (or heterogeneous data if it's polymorphic). Is may own their items. Is created with a non-C destroy function use that function to destroy an item when it is removed from the list and to destroy each item when the list itself is destroyed. Be careful not to insert items owned by one list into a list that doesn't own its own items unless you know that the source list (and all of the shared items) will outlive the destination list. =over 4 =cut */ #include "config.h" #include "std.h" #include "list.h" #include "mem.h" #include "err.h" #include "hsort.h" #include "locker.h" #define xor(a, b) (!(a) ^ !(b)) #define iff(a, b) !xor(a, b) #define implies(a, b) (!(a) || (b)) struct List { size_t size; /* number of item slots allocated */ size_t length; /* number of items used */ void **list; /* vector of items (void *) */ list_release_t *destroy; /* item destructor, if any */ Lister *lister; /* built-in iterator */ Locker *locker; /* locking strategy for this object */ }; struct Lister { List *list; /* the list being iterated over */ ssize_t index; /* the index of the current item */ }; #ifndef TEST /* Minimum list length: must be a power of 2 */ static const size_t MIN_LIST_SIZE = 4; /* C Allocates enough memory to add C extra items to C if necessary. On success, returns C<0>. On error, returns C<-1>. */ static int grow(List *list, size_t items) { int grown = 0; while (list->length + items > list->size) { if (list->size) list->size <<= 1; else list->size = MIN_LIST_SIZE; grown = 1; } if (grown) return mem_resize(&list->list, list->size) ? 0 : -1; return 0; } /* C Allocates less memory for removing C items from C if necessary. On success, returns C<0>. On error, returns C<-1>. */ static int shrink(List *list, size_t items) { int shrunk = 0; while (list->length - items < list->size >> 1) { if (list->size == MIN_LIST_SIZE) break; list->size >>= 1; shrunk = 1; } if (shrunk) return mem_resize(&list->list, list->size) ? 0 : -1; return 0; } /* C Slides C's items, starting at C, C slots to the right to make room for more. On success, returns C<0>. On error, returns C<-1>. */ static int expand(List *list, ssize_t index, size_t range) { if (grow(list, range) == -1) return -1; memmove(list->list + index + range, list->list + index, (list->length - index) * sizeof(*list->list)); list->length += range; return 0; } /* C Slides C's items, starting at C + C, C positions to the left to close a gap starting at C. On success, returns C<0>. On error, returns C<-1>. */ static int contract(List *list, ssize_t index, size_t range) { memmove(list->list + index, list->list + index + range, (list->length - index - range) * sizeof(*list->list)); if (shrink(list, range) == -1) return -1; list->length -= range; return 0; } /* C Expands or contracts C as required so that C occupies C. On success, returns C<0>. On error, returns C<-1>. */ static int adjust(List *list, ssize_t index, size_t range, size_t length) { if (range < length) return expand(list, index + range, length - range); if (range > length) return contract(list, index + length, range - length); return 0; } /* C Destroys the items in C ranging from C to C. */ static void killitems(List *list, size_t index, size_t range) { while (range--) { if (list->destroy) list->destroy(list->list[index]); list->list[index++] = NULL; } } /* =item C Creates a I with C as its item destructor. It is the caller's responsibility to deallocate the new list with I or I. On success, returns the new list. On error, returns C with C set appropriately. =cut */ List *list_create(list_release_t *destroy) { return list_create_with_locker(NULL, destroy); } /* =item C Creates a I with C as its item destructor and the remaining arguments as its initial items. It is the caller's responsibility to deallocate the new list with I or I. On success, returns the new list. On error, return C with C set appropriately. =cut */ List *list_make(list_release_t *destroy, ...) { List *list; va_list args; va_start(args, destroy); list = list_vmake_with_locker(NULL, destroy, args); va_end(args); return list; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ List *list_vmake(list_release_t *destroy, va_list args) { return list_vmake_with_locker(NULL, destroy, args); } /* =item C Creates a copy of C using C as the copy constructor (if not C). It is the caller's responsibility to deallocate the new list with I or I. On success, returns the new copy. On error, returns C with C set appropriately. =cut */ List *list_copy(const List *src, list_copy_t *copy) { return list_copy_with_locker(NULL, src, copy); } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_create_with_locker(Locker *locker, list_release_t *destroy) { List *list; if (!(list = mem_new(List))) /* XXX decouple */ return NULL; list->size = list->length = 0; list->list = NULL; list->destroy = destroy; list->lister = NULL; list->locker = locker; return list; } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_make_with_locker(Locker *locker, list_release_t *destroy, ...) { List *list; va_list args; va_start(args, destroy); list = list_vmake_with_locker(locker, destroy, args); va_end(args); return list; } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_vmake_with_locker(Locker *locker, list_release_t *destroy, va_list args) { List *list; void *item; if (!(list = list_create_with_locker(locker, destroy))) return NULL; while ((item = va_arg(args, void *)) != NULL) { if (!list_append(list, item)) { list_release(list); return NULL; } } return list; } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_copy_with_locker(Locker *locker, const List *src, list_copy_t *copy) { List *list; if (!src) return set_errnull(EINVAL); if (!(list = list_extract(src, 0, src->length, copy))) return NULL; list->locker = locker; return list; } /* =item C Claims a read lock on C (if C was created with a I). This is needed when multiple read only I module functions need to be called atomically. It is the client's responsibility to call I after the atomic operation. The only functions that may be called on C between calls to I and I are any read only I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ #define list_rdlock(list) ((list) ? locker_rdlock((list)->locker) : EINVAL) #define list_wrlock(list) ((list) ? locker_wrlock((list)->locker) : EINVAL) #define list_unlock(list) ((list) ? locker_unlock((list)->locker) : EINVAL) int (list_rdlock)(const List *list) { return list_rdlock(list); } /* =item C Claims a write lock on C (if C was created with a I). This is needed when multiple read/write I module functions need to be called atomically. It is the client's responsibility to call I after the atomic operation. The only functions that may be called on C between calls to I and I are any I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ int (list_wrlock)(const List *list) { return list_wrlock(list); } /* =item C Unlocks a read or write lock on C obtained with I or I (if C was created with a C). On success, returns C<0>. On error, returns an error code. =cut */ int (list_unlock)(const List *list) { return list_unlock(list); } /* =item C Releases (deallocates) C, destroying its items if necessary. On error, sets C appropriately. =cut */ void list_release(List *list) { if (!list) return; if (list->list) { killitems(list, 0, list->length); mem_release(list->list); } mem_release(list); } /* =item C Destroys (deallocates and sets to C) C<*list>. Returns C. B lists shared by multiple threads must not be destroyed until after all threads have finished with it. =cut */ void *list_destroy(List **list) { if (list && *list) { list_release(*list); *list = NULL; } return NULL; } /* =item C Causes C to take ownership of its items. The items will be destroyed using C when they are removed or when C is destroyed. On success, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int list_own(List *list, list_release_t *destroy) { int err; if (!list || !destroy) return set_errno(EINVAL); if ((err = list_wrlock(list))) return set_errno(err); list->destroy = destroy; if ((err = list_unlock(list))) return set_errno(err); return 0; } /* =item C Equivalent to I except that C is not write locked. =cut */ int list_own_unlocked(List *list, list_release_t *destroy) { if (!list || !destroy) return set_errno(EINVAL); list->destroy = destroy; return 0; } /* =item C Causes C to relinquish ownership of its items. The items will not be destroyed when they are removed from C or when C is destroyed. On success, returns the previous destroy function, if any. On error, returns C with C set appropriately. =cut */ list_release_t *list_disown(List *list) { list_release_t *destroy; int err; if (!list) return (list_release_t *)set_errnullf(EINVAL); if ((err = list_wrlock(list))) return (list_release_t *)set_errnullf(err); destroy = list_disown_unlocked(list); if ((err = list_unlock(list))) return (list_release_t *)set_errnullf(err); return destroy; } /* =item C Equivalent to I except that C is not write locked. =cut */ list_release_t *list_disown_unlocked(List *list) { list_release_t *destroy; if (!list) return (list_release_t *)set_errnullf(EINVAL); destroy = list->destroy; list->destroy = NULL; return destroy; } /* =item C Returns the C'th item in C. If C is negative, it refers to an item position relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On error, returns C with C set appropriately. =cut */ void *list_item(const List *list, ssize_t index) { void *item; int err; if (!list) return set_errnull(EINVAL); if ((err = list_rdlock(list))) return set_errnull(err); item = list_item_unlocked(list, index); if ((err = list_unlock(list))) return set_errnull(err); return item; } /* =item C Equivalent to I except that C is not read locked. =cut */ void *list_item_unlocked(const List *list, ssize_t index) { if (!list) return set_errnull(EINVAL); if (index < 0) index = list->length + 1 + index; if (index < 0) return set_errnull(EINVAL); if (index >= list->length) return set_errnull(EINVAL); return list->list[index]; } /* =item C Equivalent to I except that the item is cast to an integer type. =cut */ int list_item_int(const List *list, ssize_t index) { return (int)(long)list_item(list, index); } /* =item C Equivalent to I except that C is not read locked. =cut */ int list_item_int_unlocked(const List *list, ssize_t index) { return (int)(long)list_item_unlocked(list, index); } /* =item C Returns whether or not C is empty. On error, returns C<-1> with C set appropriately. =cut */ int list_empty(const List *list) { int empty; int err; if (!list) return set_errno(EINVAL); if ((err = list_rdlock(list))) return set_errno(err); empty = (list->length == 0); if ((err = list_unlock(list))) return set_errno(err); return empty; } /* =item C Equivalent to I except that C is not read locked. =cut */ int list_empty_unlocked(const List *list) { if (!list) return set_errno(EINVAL); return (list->length == 0); } /* =item C Returns the length of C. On error, returns C<-1> with C set appropriately. =cut */ ssize_t list_length(const List *list) { size_t length; int err; if (!list) return set_errno(EINVAL); if ((err = list_rdlock(list))) return set_errno(err); length = list->length; if ((err = list_unlock(list))) return set_errno(err); return length; } /* =item C Equivalent to I except that C is not read locked. =cut */ ssize_t list_length_unlocked(const List *list) { if (!list) return set_errno(EINVAL); return list->length; } /* =item C Returns the index of the last item in C, or C<-1> if there are no items. On error, returns C<-1> with C set appropriately. =cut */ ssize_t list_last(const List *list) { ssize_t last; int err; if (!list) return set_errno(EINVAL); if ((err = list_rdlock(list))) return set_errno(err); last = list->length - 1; if ((err = list_unlock(list))) return set_errno(err); return last; } /* =item C Equivalent to I except that C is not read locked. =cut */ ssize_t list_last_unlocked(const List *list) { if (!list) return set_errno(EINVAL); return list->length - 1; } /* =item C Removes the C'th item from C. If C is negative, it refers to an item position relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_remove(List *list, ssize_t index) { return list_remove_range(list, index, 1); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_remove_unlocked(List *list, ssize_t index) { return list_remove_range_unlocked(list, index, 1); } /* =item C Removes C items from C starting at C. If C or C are negative, they refer to item positions relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_remove_range(List *list, ssize_t index, ssize_t range) { List *ret; int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); ret = list_remove_range_unlocked(list, index, range); if ((err = list_unlock(list))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_remove_range_unlocked(List *list, ssize_t index, ssize_t range) { if (!list) return set_errnull(EINVAL); if (index < 0) index = list->length + 1 + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = list->length + 1 + range - index; if (range < 0) return set_errnull(EINVAL); if (list->length < index + range) return set_errnull(EINVAL); killitems(list, index, range); if (contract(list, index, range) == -1) return NULL; return list; } /* =item C Adds C to C at position C. If C is negative, it refers to an item position relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_insert(List *list, ssize_t index, void *item) { List *ret; int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); ret = list_insert_unlocked(list, index, item); if ((err = list_unlock(list))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_insert_unlocked(List *list, ssize_t index, void *item) { if (!list) return set_errnull(EINVAL); if (index < 0) index = list->length + 1 + index; if (index < 0) return set_errnull(EINVAL); if (list->length < index) return set_errnull(EINVAL); if (expand(list, index, 1) == -1) return NULL; list->list[index] = item; return list; } /* =item C Equivalent to I except that C is an integer type. =cut */ List *list_insert_int(List *list, ssize_t index, int item) { return list_insert(list, index, (void *)(long)item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_insert_int_unlocked(List *list, ssize_t index, int item) { return list_insert_unlocked(list, index, (void *)(long)item); } /* =item C Inserts the items from C into C, starting at position C using C as the copy constructor (if not C). If C is negative, it refers to an item position relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_insert_list(List *list, ssize_t index, const List *src, list_copy_t *copy) { List *ret; int err; if (!src || !list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); if ((err = list_rdlock(src))) { list_unlock(list); return set_errnull(err); } ret = list_insert_list_unlocked(list, index, src, copy); if ((err = list_unlock(src))) { list_unlock(list); return set_errnull(err); } if ((err = list_unlock(list))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write locked and C is not read locked. Note: If C needs to be read locked, it is the caller's responsibility to lock and unlock it explicitly with I and I. =cut */ #define enlist(item, copy) ((copy) ? (copy)(item) : (item)) List *list_insert_list_unlocked(List *list, ssize_t index, const List *src, list_copy_t *copy) { size_t i; if (!src || !list) return set_errnull(EINVAL); if (index < 0) index = list->length + 1 + index; if (index < 0) return set_errnull(EINVAL); if (list->length < index || xor(list->destroy, copy)) return set_errnull(EINVAL); if (expand(list, index, src->length) == -1) return NULL; for (i = 0; i < src->length; ++i) list->list[index + i] = enlist(src->list[i], copy); return list; } /* =item C Appends C to C. On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_append(List *list, void *item) { return list_insert(list, -1, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_append_unlocked(List *list, void *item) { return list_insert_unlocked(list, -1, item); } /* =item C Equivalent to I except that C is an integer. =cut */ List *list_append_int(List *list, int item) { return list_insert_int(list, -1, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_append_int_unlocked(List *list, int item) { return list_insert_int_unlocked(list, -1, item); } /* =item C Appends the items in C to C using C as the copy constructor (if not C). On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_append_list(List *list, const List *src, list_copy_t *copy) { return list_insert_list(list, -1, src, copy); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_append_list_unlocked(List *list, const List *src, list_copy_t *copy) { return list_insert_list_unlocked(list, -1, src, copy); } /* =item C Prepends C to C. On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_prepend(List *list, void *item) { return list_insert(list, 0, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_prepend_unlocked(List *list, void *item) { return list_insert_unlocked(list, 0, item); } /* =item C Equivalent to I except that C is an integer. =cut */ List *list_prepend_int(List *list, int item) { return list_insert_int(list, 0, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_prepend_int_unlocked(List *list, int item) { return list_insert_int_unlocked(list, 0, item); } /* =item C Prepends the items in C to C using C as the copy constructor (if not C). On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_prepend_list(List *list, const List *src, list_copy_t *copy) { return list_insert_list(list, 0, src, copy); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_prepend_list_unlocked(List *list, const List *src, list_copy_t *copy) { return list_insert_list_unlocked(list, 0, src, copy); } /* =item C Replaces C items in C, starting at C, with C. If C or C are negative, they refer to item positions relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_replace(List *list, ssize_t index, ssize_t range, void *item) { List *ret; int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); ret = list_replace_unlocked(list, index, range, item); if ((err = list_unlock(list))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_replace_unlocked(List *list, ssize_t index, ssize_t range, void *item) { if (!list) return set_errnull(EINVAL); if (index < 0) index = list->length + 1 + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = list->length + 1 + range - index; if (range < 0) return set_errnull(EINVAL); if (list->length < index + range) return set_errnull(EINVAL); killitems(list, index, range); if (adjust(list, index, range, 1) == -1) return NULL; list->list[index] = item; return list; } /* =item C Equivalent to I except that C is an integer type. =cut */ List *list_replace_int(List *list, ssize_t index, ssize_t range, int item) { return list_replace(list, index, range, (void *)(long)item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_replace_int_unlocked(List *list, ssize_t index, ssize_t range, int item) { return list_replace_unlocked(list, index, range, (void *)(long)item); } /* =item C Replaces C items in C, starting at C, with the items in C using C as the copy constructor (if not C). If C or C are negative, they refer to item positions relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On success, return C. On error, returns C with C set appropriately. =cut */ List *list_replace_list(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy) { List *ret; int err; if (!src || !list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); if ((err = list_rdlock(src))) { list_unlock(list); return set_errnull(err); } ret = list_replace_list_unlocked(list, index, range, src, copy); if ((err = list_unlock(src))) { list_unlock(list); return set_errnull(err); } if ((err = list_unlock(list))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write locked and C is not read locked. Note: If C needs to be read locked, it is the caller's responsibility to lock and unlock it explicitly with I and I. =cut */ List *list_replace_list_unlocked(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy) { ssize_t length; if (!src || !list) return set_errnull(EINVAL); if (index < 0) index = list->length + 1 + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = list->length + 1 + range - index; if (range < 0) return set_errnull(EINVAL); if (list->length < index + range || xor(list->destroy, copy)) return set_errnull(EINVAL); killitems(list, index, range); length = src->length; if (adjust(list, index, range, length) == -1) return NULL; while (length--) list->list[index + length] = enlist(src->list[length], copy); return list; } /* =item C Creates a new list consisting of C items from C, starting at C, using C as the copy constructor (if not C). If C or C are negative, they refer to item positions relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). It is the caller's responsibility to deallocate the new list with I or I. On success, returns the new list. On error, returns C with C set appropriately. =cut */ List *list_extract(const List *list, ssize_t index, ssize_t range, list_copy_t *copy) { return list_extract_with_locker(NULL, list, index, range, copy); } /* =item C Equivalent to I except that C is not read locked. =cut */ List *list_extract_unlocked(const List *list, ssize_t index, ssize_t range, list_copy_t *copy) { return list_extract_with_locker_unlocked(NULL, list, index, range, copy); } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_extract_with_locker(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy) { List *ret; int err; if (!list) return set_errnull(EINVAL); if ((err = list_rdlock(list))) return set_errnull(err); ret = list_extract_with_locker_unlocked(locker, list, index, range, copy); if ((err = list_unlock(list))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not read locked. =cut */ List *list_extract_with_locker_unlocked(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy) { List *ret; if (!list) return set_errnull(EINVAL); if (index < 0) index = list->length + 1 + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = list->length + 1 + range - index; if (range < 0) return set_errnull(EINVAL); if (list->length < index + range || xor(list->destroy, copy)) return set_errnull(EINVAL); if (!(ret = list_create_with_locker(locker, copy ? list->destroy : NULL))) return NULL; while (range--) { if (!list_append(ret, enlist(list->list[index++], copy))) { list_release(ret); return NULL; } } return ret; } #undef enlist /* =item C Pushes C onto the end of C. On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_push(List *list, void *item) { return list_append(list, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_push_unlocked(List *list, void *item) { return list_append_unlocked(list, item); } /* =item C Equivalent to I except that C is an integer. =cut */ List *list_push_int(List *list, int item) { return list_append_int(list, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_push_int_unlocked(List *list, int item) { return list_append_int_unlocked(list, item); } /* =item C Pops the last item off C. On success, returns the item popped. On error, returns C with C set appropriately. =cut */ void *list_pop(List *list) { void *item; int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); item = list_pop_unlocked(list); if ((err = list_unlock(list))) return set_errnull(err); return item; } /* =item C Equivalent to I except that C is not write locked. =cut */ void *list_pop_unlocked(List *list) { void *item; if (!list) return set_errnull(EINVAL); if (!list->length) return set_errnull(EINVAL); item = list->list[list->length - 1]; list->list[list->length - 1] = NULL; if (!list_remove_unlocked(list, list->length - 1)) { list->list[list->length - 1] = item; return NULL; } return item; } /* =item C Equivalent to I except that the item popped has an integer type. =cut */ int list_pop_int(List *list) { return (int)(long)list_pop(list); } /* =item C Equivalent to I except that C is not write locked. =cut */ int list_pop_int_unlocked(List *list) { return (int)(long)list_pop_unlocked(list); } /* =item C Removes and returns the first item in C. On success, returns the item shifted. On error, returns C with C set appropriately. =cut */ void *list_shift(List *list) { void *item; int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); item = list_shift_unlocked(list); if ((err = list_unlock(list))) return set_errnull(err); return item; } /* =item C Equivalent to I except that C is not write locked. =cut */ void *list_shift_unlocked(List *list) { void *item; if (!list) return set_errnull(EINVAL); if (!list->length) return set_errnull(EINVAL); item = list->list[0]; list->list[0] = NULL; if (!list_remove_unlocked(list, 0)) { list->list[0] = item; return NULL; } return item; } /* =item C Equivalent to I except that the item shifted has an integer type. =cut */ int list_shift_int(List *list) { return (int)(long)list_shift(list); } /* =item C Equivalent to I except that C is not write locked. =cut */ int list_shift_int_unlocked(List *list) { return (int)(long)list_shift_unlocked(list); } /* =item C Inserts C at the start of C. On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_unshift(List *list, void *item) { return list_prepend(list, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_unshift_unlocked(List *list, void *item) { return list_prepend_unlocked(list, item); } /* =item C Equivalent to I except that C is an integer. =cut */ List *list_unshift_int(List *list, int item) { return list_prepend_int(list, item); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_unshift_int_unlocked(List *list, int item) { return list_prepend_int_unlocked(list, item); } /* =item C Removes a sublist from C starting at C with length C after copying the items with C (if not C). If C or C are negative, they refer to item positions relative to the end of the list (C<-1> is the position after the last item, C<-2> is the position of the last item and so on). On success, returns the sublist. It is the caller's responsibility to deallocate the new list with I or I. On error, returns C with C set appropriately. =cut */ List *list_splice(List *list, ssize_t index, ssize_t range, list_copy_t *copy) { return list_splice_with_locker(NULL, list, index, range, copy); } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_splice_unlocked(List *list, ssize_t index, ssize_t range, list_copy_t *copy) { return list_splice_with_locker_unlocked(NULL, list, index, range, copy); } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_splice_with_locker(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy) { List *ret; int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); ret = list_splice_with_locker_unlocked(locker, list, index, range, copy); if ((err = list_unlock(list))) { list_release(ret); return set_errnull(err); } return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_splice_with_locker_unlocked(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy) { List *ret; if (!list) return set_errnull(EINVAL); if (!(ret = list_extract_with_locker_unlocked(locker, list, index, range, copy))) return NULL; if (!list_remove_range_unlocked(list, index, range)) { list_release(ret); return NULL; } return ret; } /* =item C Sorts the items in C using the item comparison function C and I for lists of fewer than 10000 items and I for larger lists. On success, returns C. On error, returns C with C set appropriately. =cut */ List *list_sort(List *list, list_cmp_t *cmp) { List *ret; int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); ret = list_sort_unlocked(list, cmp); if ((err = list_unlock(list))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ List *list_sort_unlocked(List *list, list_cmp_t *cmp) { if (!list) return set_errnull(EINVAL); if (!list->list || !list->length) return set_errnull(EINVAL); ((list->length >= 10000) ? hsort : qsort)(list->list, list->length, sizeof list->list[0], cmp); return list; } /* =item C Invokes C for each of C's items. The arguments passed to C are the item, a pointer to the loop variable containing the item's position within C and C. On error, sets C appropriately. =cut */ void list_apply(List *list, list_action_t *action, void *data) { list_apply_wrlocked(list, action, data); } /* =item C Equivalent to I except that C is read locked rather than write locked. Use this in preference to I when C and its items will not be modified during the iteration. =cut */ void list_apply_rdlocked(List *list, list_action_t *action, void *data) { int err; if (!list || !action) { set_errno(EINVAL); return; } if ((err = list_rdlock(list))) { set_errno(err); return; } list_apply_unlocked(list, action, data); if ((err = list_unlock(list))) set_errno(err); } /* =item C Equivalent to I except that this function name makes the fact that C is write locked explicit. =cut */ void list_apply_wrlocked(List *list, list_action_t *action, void *data) { int err; if (!list || !action) { set_errno(EINVAL); return; } if ((err = list_wrlock(list))) { set_errno(err); return; } list_apply_unlocked(list, action, data); if ((err = list_unlock(list))) set_errno(err); } /* =item C Equivalent to I except that C is not write locked. =cut */ void list_apply_unlocked(List *list, list_action_t *action, void *data) { size_t i; if (!list || !action) { set_errno(EINVAL); return; } for (i = 0; i < list->length; ++i) action(list->list[i], &i, data); } /* =item C Creates and returns a new list containing the return values of C, invoked once for each item in C. The arguments passed to C are the item, a pointer to the loop variable containing the item's position within C and C. C will be the item destructor for the new list. It is the caller's responsibility to deallocate the new list with I or I. On success, returns the new list. On error, returns C with C set appropriately. =cut */ List *list_map(List *list, list_release_t *destroy, list_map_t *map, void *data) { return list_map_with_locker(NULL, list, destroy, map, data); } /* =item C Equivalent to I except that C is not read locked. =cut */ List *list_map_unlocked(List *list, list_release_t *destroy, list_map_t *map, void *data) { return list_map_with_locker_unlocked(NULL, list, destroy, map, data); } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_map_with_locker(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data) { List *mapping; int err; if (!list || !map) return set_errnull(EINVAL); if ((err = list_rdlock(list))) return set_errnull(err); mapping = list_map_with_locker_unlocked(locker, list, destroy, map, data); if ((err = list_unlock(list))) { list_release(mapping); return set_errnull(err); } return mapping; } /* =item C Equivalent to I except that C is not read locked. =cut */ List *list_map_with_locker_unlocked(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data) { List *mapping; size_t i; if (!list || !map) return set_errnull(EINVAL); if (!(mapping = list_create_with_locker(locker, destroy))) return NULL; for (i = 0; i < list->length; ++i) { if (!list_append(mapping, map(list->list[i], &i, data))) { list_release(mapping); return NULL; } } return mapping; } /* =item C Creates and returns a new list by invoking C for each item in C, and appending the items for which C returned a non-zero value. The arguments passed to C are the item, a pointer to the loop variable containing the item's position within C and C. It is the caller's responsibility to deallocate the new list with I or I. Note that the new list does not own its items since it does not copy them. On success, returns the new list. On error, returns C with C set appropriately. =cut */ List *list_grep(List *list, list_query_t *grep, void *data) { return list_grep_with_locker(NULL, list, grep, data); } /* =item C Equivalent to I except that C is not read locked. =cut */ List *list_grep_unlocked(List *list, list_query_t *grep, void *data) { return list_grep_with_locker_unlocked(NULL, list, grep, data); } /* =item C Equivalent to I except that multiple threads accessing the new list will be synchronised by C. =cut */ List *list_grep_with_locker(Locker *locker, List *list, list_query_t *grep, void *data) { List *grepping; int err; if (!list || !list) return set_errnull(EINVAL); if ((err = list_rdlock(list))) return set_errnull(err); grepping = list_grep_with_locker_unlocked(locker, list, grep, data); if ((err = list_unlock(list))) { list_release(grepping); return set_errnull(err); } return grepping; } /* =item C Equivalent to I except that C is not read locked. =cut */ List *list_grep_with_locker_unlocked(Locker *locker, List *list, list_query_t *grep, void *data) { List *grepping; size_t i; if (!list || !list) return set_errnull(EINVAL); if (!(grepping = list_create(NULL))) return NULL; for (i = 0; i < list->length; ++i) { if (grep(list->list[i], &i, data)) { if (!list_append(grepping, list->list[i])) { list_release(grepping); return NULL; } } } return grepping; } /* =item C Invokes C on each item in C, starting at C<*index> until C returns a non-zero value. The arguments passed to C are the item, C and C. Returns the index of the item that satisfied C, or C<-1> when C is not satisfied by any remaining items. The value pointed to by C is set to the return value. On error, sets C appropriately. =cut */ ssize_t list_query(List *list, ssize_t *index, list_query_t *query, void *data) { ssize_t ret; int err; if (!list || !index || !query) return set_errno(EINVAL); if ((err = list_rdlock(list))) return set_errno(err); ret = list_query_unlocked(list, index, query, data); if ((err = list_unlock(list))) return set_errno(err); return ret; } /* =item C Equivalent to I except that C is not read locked. =cut */ ssize_t list_query_unlocked(List *list, ssize_t *index, list_query_t *query, void *data) { size_t i; if (!list || !index || !query) return set_errno(EINVAL); if (*index >= list->length) return set_errno(EINVAL); for (i = *index; i < list->length; ++i) { if (query(list->list[i], (size_t *)index, data)) return *index = i; } return *index = -1; } /* =item C Creates an iterator for C. It is the caller's responsibility to deallocate the iterator with I or I. The iterator keeps C write locked until it is released with I or I. Note that the iterator itself is not locked so it must not be shared between threads. On success, returns the iterator. On error, returns C with C set appropriately. =cut */ Lister *lister_create(List *list) { return lister_create_wrlocked(list); } /* =item C Equivalent to I except that C is read locked rather than write locked. Use this in preference to I when no calls to I will be made during the iteration. =cut */ Lister *lister_create_rdlocked(List *list) { int err; if (!list) return set_errnull(EINVAL); if ((err = list_rdlock(list))) return set_errnull(err); return lister_create_unlocked(list); } /* =item C Equivalent to I except that this function name makes the fact that C is write locked explicit. =cut */ Lister *lister_create_wrlocked(List *list) { int err; if (!list) return set_errnull(EINVAL); if ((err = list_wrlock(list))) return set_errnull(err); return lister_create_unlocked(list); } /* =item C Equivalent to I except that C is not write locked. =cut */ Lister *lister_create_unlocked(const List *list) { Lister *lister; if (!list) return set_errnull(EINVAL); if (!(lister = mem_new(Lister))) /* XXX decouple */ return NULL; lister->list = (List *)list; lister->index = -1; return lister; } /* =item C Releases (deallocates) C and unlocks the associated list. =cut */ void lister_release(Lister *lister) { int err; if (!lister) return; if ((err = list_unlock(lister->list))) { set_errno(err); return; } mem_release(lister); } /* =item C Equivalent to I except that the associated list is not unlocked. =cut */ void lister_release_unlocked(Lister *lister) { if (!lister) return; mem_release(lister); } /* =item C Destroys (deallocates and sets to C) C<*lister> and unlocks the associated list. Returns C. On error, sets C appropriately. =cut */ void *lister_destroy(Lister **lister) { if (lister && *lister) { lister_release(*lister); *lister = NULL; } return NULL; } /* =item C Equivalent to I except that the associated list is not unlocked. =cut */ void *lister_destroy_unlocked(Lister **lister) { if (lister && *lister) { lister_release_unlocked(*lister); *lister = NULL; } return NULL; } /* =item C Returns whether or not there is another item in the list over which C is iterating. On error, returns C<-1> with C set appropriately. =cut */ int lister_has_next(Lister *lister) { if (!lister) return set_errno(EINVAL); return lister->index + 1 < lister->list->length; } /* =item C Returns the next item in the iteration, C. On error, returns C with C set appropriately. =cut */ void *lister_next(Lister *lister) { if (!lister) return set_errnull(EINVAL); return list_item_unlocked(lister->list, (size_t)++lister->index); } /* =item C Equivalent to I except that the item returned is an integer. =cut */ int lister_next_int(Lister *lister) { if (!lister) return set_errno(EINVAL); return list_item_int_unlocked(lister->list, (size_t)++lister->index); } /* =item C Removes the current item in the iteration, C. The next item in the iteration is the item following the removed item, if any. This must be called after I or I. On error, sets C appropriately. =cut */ void lister_remove(Lister *lister) { if (!lister) { set_errno(EINVAL); return; } if (lister->index == -1) { set_errno(EINVAL); return; } list_remove_unlocked(lister->list, (size_t)lister->index--); } /* =item C Returns whether or not there is another item in C using an internal iterator. The first time this is called, a new internal I will be created (Note: There can be only one). When there are no more items, returns C<0> and destroys the internal iterator. When it returns C<1>, use I or I to retrieve the next item. On error, returns C<-1> with C set appropriately. Note: If an iteration using an internal iterator terminates before the end of the list, it is the caller's responsibility to call I. Failure to do so will cause the internal iterator to leak. It will also break the next call to I which will continue where the current iteration stopped rather than starting at the beginning again. I assumes that there is no internal iterator so it is the caller's responsibility to complete the iteration or call I before releasing C with I or I. Note: The internal I does not lock C so this function is not threadsafe. It can only be used with lists created in the current function (to guarantee that no other thread can access it). This practice should be observed even in single threaded applications to avoid breaking iterator semantics (possible with nested function calls). If C is a parameter or a variable declared outside the function, it is best to create an explicit I instead. If this function is used on such lists instead, it is the caller's responsibility to explicitly lock C first with I and explicitly unlock it with I. Do this even if you are writing single threaded code in case your function may one day be used in a multi threaded application. =cut */ int list_has_next(List *list) { int has; if (!list) return set_errno(EINVAL); if (!list->lister && !(list->lister = lister_create_unlocked(list))) return -1; if ((has = lister_has_next(list->lister)) != 1) list_break(list); return has; } /* =item C Unlocks C and destroys its internal iterator. Must be used only when an iteration using an internal iterator has terminated before reaching the end of C. On error, returns C with C set appropriately. =cut */ void list_break(List *list) { if (!list) { set_errno(EINVAL); return; } lister_destroy_unlocked(&list->lister); } /* =item C Returns the next item in C using it's internal iterator. On error, returns C with C set appropriately. =cut */ void *list_next(List *list) { if (!list || !list->lister) return set_errnull(EINVAL); return lister_next(list->lister); } /* =item C Equivalent to I except that the item returned is an integer. =cut */ int list_next_int(List *list) { if (!list || !list->lister) return set_errno(EINVAL); return lister_next_int(list->lister); } /* =item C Removes the current item in C using it's internal iterator. The next item in the iteration is the item following the removed item, if any. This must be called after I. On error, sets C appropriately. =cut */ void list_remove_current(List *list) { if (!list || !list->lister) { set_errno(EINVAL); return; } lister_remove(list->lister); } /* =back =head1 ERRORS On error, C is set either by an underlying function, or as follows: =over 4 =item C When arguments are C or out of range. =back =head1 MT-Level MT-Disciplined By default, Is are not MT-Safe because most programs are single threaded and synchronisation doesn't come for free. Even in multi threaded programs, not all Is are necessarily shared between multiple threads. When a I is shared between multiple threads which need to be synchronised, the method of synchronisation must be carefully selected by the client code. There are tradeoffs between concurrency and overhead. The greater the concurrency, the greater the overhead. More locks give greater concurrency but have greater overhead. Readers/Writer locks can give greater concurrency than mutex locks but have greater overhead. One lock for each I may be required, or one lock for all (or a set of) Is may be more appropriate. Generally, the best synchronisation strategy for a given application can only be determined by testing/benchmarking the written application. It is important to be able to experiment with the synchronisation strategy at this stage of development without pain. To facilitate this, Is can be created with I which takes a I argument. The I specifies a lock and a set of functions for manipulating the lock. Each I can have it's own lock by creating a separate I for each I. Multiple Is can share the same lock by sharing the same I. Only the application developer can determine what is appropriate for each application on a case by case basis. I means that the application developer has a mechanism for specifying the synchronisation requirements to be applied to library code. =head1 EXAMPLES Create a list that doesn't own its items, populate it and then iterate over its values with the internal iterator to print the values: #include #include int main() { List *list; if (!(list = list_create(NULL))) return EXIT_FAILURE; list_append(list, "123"); list_append(list, "456"); list_append(list, "789"); while (list_has_next(list) == 1) printf("%s\n", (char *)list_next(list)); list_destroy(&list); return EXIT_SUCCESS; } The same but create the list and populate it at the same time: #include #include int main() { List *list; if (!(list = list_make(NULL, "123", "456", "789", NULL))) return EXIT_FAILURE; while (list_has_next(list) == 1) printf("%s\n", (char *)list_next(list)); list_destroy(&list); return EXIT_SUCCESS; } Create a map that does own its items, populate it and then iterator over it with an external iterator to print its items. #include #include int main() { List *list; Lister *lister; if (!(list = list_create(NULL))) return EXIT_FAILURE; list_append(list, "123"); list_append(list, "456"); list_append(list, "789"); if (!(lister = lister_create(list))) { list_destroy(&list); return EXIT_FAILURE; } while (lister_has_next(lister) == 1) printf("%s\n", (char *)lister_next(lister)); lister_destroy(&lister); list_destroy(&list); return EXIT_SUCCESS; } The same but with an apply function: #include #include void action(void *item, size_t *index, void *data) { printf("%s\n", (char *)item); } int main() { List *list; if (!(list = list_create(free))) return EXIT_FAILURE; list_append(list, strdup("123")); list_append(list, strdup("456")); list_append(list, strdup("789")); list_apply(list, action, NULL); list_destroy(&list); return EXIT_SUCCESS; } The same but with a list of integers: #include #include int main() { List *list; if (!(list = list_create(NULL))) return EXIT_FAILURE; list_append(list, (void *)123); list_append(list, (void *)456); list_append(list, (void *)789); while (list_has_next(list) == 1) printf("%d\n", list_next_int(list)); list_destroy(&list); return EXIT_SUCCESS; } Create a copy of a list: #include #include int main() { List *orig; List *copy; if (!(orig = list_make(free, strdup("123"), strdup("456"), strdup("789"), NULL))) return EXIT_FAILURE; if (!(copy = list_copy(orig, (list_copy_t *)strdup))) { list_destroy(&orig); return EXIT_FAILURE; } list_destroy(&orig); while (list_has_next(copy) == 1) printf("%s\n", (char *)list_next(copy)); list_destroy(©); return EXIT_SUCCESS; } Transfer ownership from one list to another: #include #include int main() { List *donor; List *recipient; if (!(donor = list_make(free, strdup("123"), strdup("456"), strdup("789"), NULL))) return EXIT_FAILURE; if (!(recipient = list_create(NULL))) { list_destroy(&donor); return EXIT_FAILURE; } while (list_has_next(donor) == 1) list_append(recipient, list_next(donor)); list_own(recipient, list_disown(donor)); list_destroy(&donor); while (list_has_next(recipient) == 1) printf("%s\n", (char *)list_next(recipient)); list_destroy(&recipient); return EXIT_SUCCESS; } Manipulate a list, examine it, use apply, map, grep and query, remove items while iterating: #include #include int cmp(const void *a, const void *b) { return strcmp(*(char **)a, *(char **)b); } void action(void *item, size_t *index, void *data) { printf("%s\n", (char *)item); } void *upper(void *item, size_t *index, void *data) { char *uc = strdup(item); if (uc) *uc = toupper(*uc); return uc; } int even(void *item, size_t *index, void *data) { return item && (*(char *)item & 1) == 0; } int main() { Lister *lister; List *list; void *item; List *res; ssize_t i; if (!(list = list_create(NULL))) return EXIT_FAILURE; // Manipulate a list printf("length %d empty %d\n", list_length(list), list_empty(list)); list_append(list, "a"); list_append(list, "b"); list_append(list, "c"); list_remove(list, 0); list_insert(list, 1, "d"); list_prepend(list, "e"); list_replace(list, 1, 2, "f"); list_push(list, "g"); list_push(list, "h"); list_push(list, "i"); item = list_pop(list); list_unshift(list, list_shift(list)); list_release(list_splice(list, 0, 1, NULL)); list_sort(list, cmp); printf("last %s\n", (char *)list_item(list, list_last(list))); // Apply an action to a list list_apply(list, action, NULL); // Map a list into another list res = list_map(list, free, upper, NULL); list_apply(res, action, NULL); list_destroy(&res); // Grep a list for items that match some criteria res = list_grep(list, even, NULL); list_apply(res, action, NULL); list_destroy(&res); // Locate a list's items that match some criteria for (i = 0; list_query(list, &i, even, NULL) != -1; ++i) printf("%d %s even\n", i, (char *)list_item(list, i)); // Remove elements via the internal iterator and break out of loop while (list_has_next(list) == 1) { item = list_next(list); list_remove_current(list); if (!strcmp(item, "f")) { list_break(list); break; } } // Remove elements via an external iterator for (lister = lister_create(list); lister_has_next(lister) == 1; ) { item = lister_next(lister); lister_remove(lister); } lister_destroy(&lister); list_destroy(&list); return EXIT_SUCCESS; } Manipulate a list of integers: #include #include int main() { Lister *lister; List *list; int item; int i; if (!(list = list_create(NULL))) return EXIT_FAILURE; // Manipulate a list list_append_int(list, 1); list_append_int(list, 2); list_append_int(list, 3); list_remove(list, 0); list_insert_int(list, 1, 4); list_prepend_int(list, 5); list_replace_int(list, 1, 2, 6); list_push_int(list, 7); list_push_int(list, 8); list_push_int(list, 9); item = list_pop_int(list); list_unshift_int(list, list_shift_int(list)); list_release(list_splice(list, 0, 1, NULL)); // Get items as integers for (i = 0; i < list_length(list); ++i) printf("%d\n", list_item_int(list, i)); // Remove elements via the internal iterator while (list_has_next(list) == 1) { item = list_next_int(list); list_remove_current(list); } // Remove elements via an external iterator for (lister = lister_create(list); lister_has_next(lister) == 1; ) { item = lister_next_int(lister); lister_remove(lister); } list_destroy(&list); return EXIT_SUCCESS; } Append, insert, prepend and replace using another list, not just one item: #include #include int main() { List *list; List *src; int i; if (!(list = list_create(NULL))) return EXIT_FAILURE; if (!(src = list_make(NULL, "a", "b", "c", NULL))) { list_destroy(&list); return EXIT_FAILURE; } list_append_list(list, src, NULL); list_insert_list(list, 1, src, NULL); list_prepend_list(list, src, NULL); list_replace_list(list, 1, 2, src, NULL); for (i = 0; i < list_length(list); ++i) printf("%s\n", (char *)list_item(list, i)); list_destroy(&list); return EXIT_SUCCESS; } The same as the previous example but with a list that owns its items: #include #include int main() { List *list; List *src; int i; if (!(list = list_create(free))) return EXIT_FAILURE; if (!(src = list_make(NULL, "a", "b", "c", NULL))) { list_destroy(&list); return EXIT_FAILURE; } list_append_list(list, src, (list_copy_t *)strdup); list_insert_list(list, 1, src, (list_copy_t *)strdup); list_prepend_list(list, src, (list_copy_t *)strdup); list_replace_list(list, 1, 2, src, (list_copy_t *)strdup); for (i = 0; i < list_length(list); ++i) printf("%s\n", (char *)list_item(list, i)); list_destroy(&list); return EXIT_SUCCESS; } =head1 CAVEAT Little attempt is made to protect the client from sharing items between lists with differing ownership policies and getting it wrong. When copying items from any list to an owning list, a copy function must be supplied. When adding a single item to an owning list, it is assumed that the list may take over ownership of the item. When an owning list is destroyed, all of its items are destroyed. If any of these items had been shared with a non-owning list that outlived the owning list, then the non-owning list will contain items that point to deallocated memory. If you use an internal iterator in a loop that terminates before the end of the list, and fail to call I, the internal iterator will leak. =head1 BUGS Uses I. The type of memory used and the allocation strategy need to be decoupled from this code. =head1 SEE ALSO I, I, I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST #include char action_data[1024]; void action(void *item, size_t *index) { strlcat(action_data, item, 1024); } int query_data[] = { 2, 6, 8 , -1 }; int query(void *item, size_t *index) { return !strcmp((const char *)item, "def"); } int sort_cmp(const char **a, const char **b) { return strcmp(*a, *b); } int mapf(int item, size_t *index, int *sum) { return (sum) ? *sum += item : item; } int grepf(int item, size_t *index, int *data) { return !(item & 1); } #define RD 0 #define WR 1 List *mtlist = NULL; Locker *locker = NULL; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; #ifdef PTHREAD_RWLOCK_INITIALIZER pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; #else pthread_rwlock_t rwlock; #endif int barrier[2]; int length[2]; const int lim = 1000; int debug = 0; int errors = 0; void *produce(void *arg) { int i; int test = *(int *)arg; for (i = 0; i <= lim; ++i) { if (debug) printf("p: prepend %d\n", i); if (!list_prepend_int(mtlist, i)) ++errors, printf("Test%d: list_prepend_int(mtlist, %d), failed\n", test, i); write(length[WR], "", 1); } write(barrier[WR], "", 1); return NULL; } void *consume(void *arg) { int i, v; int test = *(int *)arg; char ack; for (i = 0; i < lim * 2; ++i) { if (debug) printf("c: pop\n"); while (read(length[RD], &ack, 1) == -1 && errno == EINTR) {} v = list_pop_int(mtlist); if (debug) printf("c: pop %d\n", v); if (v == lim) break; } if (i != lim) ++errors, printf("Test%d: consumer read %d items, not %d\n", test, i, lim); write(barrier[WR], "", 1); return NULL; } void *iterate_builtin(void *arg) { int i; int t = *(int *)arg; int broken = 0; if (debug) printf("i%d: loop\n", t); for (i = 0; i < lim / 10; ++i) { list_wrlock(mtlist); while (list_has_next(mtlist) == 1) { int val = list_next_int(mtlist); if (debug) printf("i%d: loop %d/%d val %d\n", t, i, lim / 10, val); if (!broken) { list_break(mtlist); broken = 1; break; } } list_unlock(mtlist); } write(barrier[WR], "", 1); return NULL; } void *iterate_rdlocked(void *arg) { int i; int t = *(int *)arg; if (debug) printf("j%d: loop\n", t); for (i = 0; i < lim / 10; ++i) { Lister *lister = lister_create_rdlocked(mtlist); while (lister_has_next(lister) == 1) { int val = (int)(long)lister_next(lister); if (debug) printf("j%d: loop %d/%d val %d\n", t, i, lim / 10, val); } lister_release(lister); } write(barrier[WR], "", 1); return NULL; } void *iterate_wrlocked(void *arg) { int i; int t = *(int *)arg; if (debug) printf("k%d: loop\n", t); for (i = 0; i < lim / 10; ++i) { Lister *lister = lister_create_wrlocked(mtlist); while (lister_has_next(lister) == 1) { int val = (int)(long)lister_next(lister); if (debug) printf("k%d: loop %d/%d val %d\n", t, i, lim / 10, val); } lister_release(lister); } write(barrier[WR], "", 1); return NULL; } void mt_test(int test, Locker *locker) { if (!(mtlist = list_create_with_locker(locker, NULL))) ++errors, printf("Test%d: list_create_with_locker(NULL) failed\n", test); else { static int iid[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; pthread_attr_t attr; pthread_t id; int i; char ack; if (pipe(length) == -1 || pipe(barrier) == -1) { ++errors, printf("Test%d: failed to perform test: pipe() failed\n", test); return; } pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&id, &attr, produce, &test); pthread_create(&id, &attr, consume, &test); pthread_create(&id, &attr, iterate_builtin, iid + 0); pthread_create(&id, &attr, iterate_builtin, iid + 1); pthread_create(&id, &attr, iterate_builtin, iid + 2); pthread_create(&id, &attr, iterate_builtin, iid + 3); pthread_create(&id, &attr, iterate_rdlocked, iid + 4); pthread_create(&id, &attr, iterate_rdlocked, iid + 5); pthread_create(&id, &attr, iterate_rdlocked, iid + 6); pthread_create(&id, &attr, iterate_wrlocked, iid + 7); pthread_create(&id, &attr, iterate_wrlocked, iid + 8); pthread_create(&id, &attr, iterate_wrlocked, iid + 9); pthread_attr_destroy(&attr); for (i = 0; i < 12; ++i) while (read(barrier[RD], &ack, 1) == -1 && errno == EINTR) {} list_destroy(&mtlist); if (mtlist) ++errors, printf("Test%d: list_destroy(&mtlist) failed\n", test); close(length[RD]); close(length[WR]); close(barrier[RD]); close(barrier[WR]); } } #define TEST_ACT(i, action) \ if (!(action)) \ ++errors, printf("Test%d: %s failed\n", (i), (#action)); #define TEST_EQ(i, action, value) \ if ((val = (action)) != (value)) \ ++errors, printf("Test%d: %s failed (returned %d, not %d)\n", (i), (#action), val, (value)); #define CHECK_LENGTH(i, action, list, length) \ if (list_length(list) != (length)) \ ++errors, printf("Test%d: %s failed: list_length(%s) = %d, not %d\n", (i), (#action), (#list), (int)list_length(list), (length)); #define CHECK_ITEM(i, action, list, item, value) \ if (!list_item((list), (item)) || strcmp(list_item((list), (item)), (value))) \ ++errors, printf("Test%d: %s failed (item %d is \"%s\", not \"%s\")\n", (i), (#action), (item), (char *)list_item(list, (item)), (value)); #define CHECK_INT_ITEM(i, action, list, item, value) \ if (list_item_int((list), (item)) != (value)) \ ++errors, printf("Test%d: %s failed (item %d is %d, not %d)\n", (i), (#action), (item), list_item_int(list, (item)), (value)); int main(int ac, char **av) { int errors = 0; List *a, *b, *c, *d; Lister *lister; ssize_t index = 0; int i, val; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s [debug]\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "list"); /* Test list_make, list_length, list_item */ TEST_ACT(1, a = list_make(NULL, "abc", "def", "ghi", "jkl", NULL)) else CHECK_LENGTH(1, list_make(), a, 4) else CHECK_ITEM(1, list_make(), a, 0, "abc") else CHECK_ITEM(1, list_make(), a, 1, "def") else CHECK_ITEM(1, list_make(), a, 2, "ghi") else CHECK_ITEM(1, list_make(), a, 3, "jkl") /* Test list_create(NULL), list_empty, list_append, list_prepend, list_insert, list_last */ TEST_ACT(2, b = list_create(NULL)) TEST_ACT(3, list_empty(b)) TEST_ACT(4, list_append(b, "abc")) else CHECK_LENGTH(4, list_append(b, "abc"), b, 1) else CHECK_ITEM(4, list_append(b, "abc"), b, 0, "abc") TEST_ACT(5, !list_empty(b)) TEST_ACT(6, list_prepend(b, "def")) else CHECK_LENGTH(6, list_prepend(b, "def"), b, 2) else CHECK_ITEM(6, list_prepend(b, "def"), b, 0, "def") else CHECK_ITEM(6, list_prepend(b, "def"), b, 1, "abc") TEST_ACT(7, list_insert(b, 1, "ghi")) else CHECK_LENGTH(7, list_insert(b, "ghi"), b, 3) else CHECK_ITEM(7, list_insert(b, "ghi"), b, 0, "def") else CHECK_ITEM(7, list_insert(b, "ghi"), b, 1, "ghi") else CHECK_ITEM(7, list_insert(b, "ghi"), b, 2, "abc") TEST_EQ(8, list_last(b), 2) TEST_EQ(9, list_length(NULL), -1) else if (errno != EINVAL) ++errors, printf("Test9: list_length(NULL) failed (errno = %d, not EINVAL)\n", errno); TEST_EQ(10, list_empty(NULL), -1) else if (errno != EINVAL) ++errors, printf("Test10: list_empty(NULL) failed (errno = %d, not EINVAL)\n", errno); /* Test list_copy, list_destroy */ if ((c = list_copy(a, (list_copy_t *)free))) ++errors, printf("Test11: list_copy() with copy() but no destroy() failed\n"); if (!(c = list_copy(a, NULL))) ++errors, printf("Test12: list_copy() without copy() or destroy() failed\n"); else CHECK_LENGTH(12, list_copy(a, NULL), c, 4) else CHECK_ITEM(12, list_copy(a, NULL), c, 0, "abc") else CHECK_ITEM(12, list_copy(a, NULL), c, 1, "def") else CHECK_ITEM(12, list_copy(a, NULL), c, 2, "ghi") else CHECK_ITEM(12, list_copy(a, NULL), c, 3, "jkl") list_destroy(&c); if (c) ++errors, printf("Test13: list_destroy(&c) failed\n"); /* Test list_create(free), list_append, list_copy */ if (!(c = list_create((list_release_t *)free))) ++errors, printf("Test14: list_create(free) failed\n"); else { TEST_ACT(15, list_append(c, mem_strdup("abc"))) TEST_ACT(16, list_append(c, mem_strdup("def"))) TEST_ACT(17, list_append(c, mem_strdup("ghi"))) TEST_ACT(18, list_append(c, mem_strdup("jkl"))) if ((d = list_copy(c, NULL))) ++errors, printf("Test19: list_copy() with destroy() but no copy() failed\n"); if (!(d = list_copy(c, (list_copy_t *)mem_strdup))) ++errors, printf("Test20: list_copy() with copy() and destroy() failed\n"); } /* Test list_remove, list_replace */ TEST_ACT(21, list_remove(a, 3)) else CHECK_LENGTH(21, list_remove(a, 3), a, 3) else CHECK_ITEM(21, list_remove(a, 3), a, 0, "abc") else CHECK_ITEM(21, list_remove(a, 3), a, 1, "def") else CHECK_ITEM(21, list_remove(a, 3), a, 2, "ghi") TEST_ACT(22, list_remove(a, 0)) else CHECK_LENGTH(22, list_remove(a, 0), a, 2) else CHECK_ITEM(22, list_remove(a, 0), a, 0, "def") else CHECK_ITEM(22, list_remove(a, 0), a, 1, "ghi") TEST_ACT(23, list_replace(a, 1, 1, "123")) else CHECK_LENGTH(23, list_replace(a, 1, 1, "123"), a, 2) else CHECK_ITEM(23, list_replace(a, 1, 1, "123"), a, 0, "def") else CHECK_ITEM(23, list_replace(a, 1, 1, "123"), a, 1, "123") /* Test list_append_list, list_prepend_list, list_insert_list */ TEST_ACT(24, list_append_list(a, b, NULL)) else CHECK_LENGTH(24, list_append_list(a, b, NULL), a, 5) else CHECK_ITEM(24, list_append_list(a, b, NULL), a, 0, "def") else CHECK_ITEM(24, list_append_list(a, b, NULL), a, 1, "123") else CHECK_ITEM(24, list_append_list(a, b, NULL), a, 2, "def") else CHECK_ITEM(24, list_append_list(a, b, NULL), a, 3, "ghi") else CHECK_ITEM(24, list_append_list(a, b, NULL), a, 4, "abc") TEST_ACT(25, list_prepend_list(a, c, NULL)) else CHECK_LENGTH(25, list_prepend_list(a, c, NULL), a, 9) else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 0, "abc") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 1, "def") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 2, "ghi") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 3, "jkl") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 4, "def") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 5, "123") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 6, "def") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 7, "ghi") else CHECK_ITEM(25, list_prepend_list(a, c, NULL), a, 8, "abc") TEST_ACT(26, list_insert_list(b, 1, c, NULL)) else CHECK_LENGTH(26, list_insert_list(b, 1, c, NULL), b, 7) else CHECK_ITEM(26, list_insert_list(b, 1, c, NULL), b, 0, "def") else CHECK_ITEM(26, list_insert_list(b, 1, c, NULL), b, 1, "abc") else CHECK_ITEM(26, list_insert_list(b, 1, c, NULL), b, 2, "def") else CHECK_ITEM(26, list_insert_list(b, 1, c, NULL), b, 3, "ghi") else CHECK_ITEM(26, list_insert_list(b, 1, c, NULL), b, 4, "jkl") else CHECK_ITEM(26, list_insert_list(b, 1, c, NULL), b, 5, "ghi") else CHECK_ITEM(26, list_insert_list(b, 1, c, NULL), b, 6, "abc") /* Test list_replace_list, list_remove_range */ if (list_replace_list(c, 1, 2, d, NULL)) ++errors, printf("Test27: list_replace_list() with destroy() but not copy() failed\n"); if (list_replace_list(a, 1, 2, d, (list_copy_t *)mem_strdup)) ++errors, printf("Test28: list_replace_list() with copy() but not destroy() failed\n"); TEST_ACT(29, list_replace_list(a, 1, 2, d, NULL)) else CHECK_LENGTH(29, list_replace_list(a, 1, 2, d, NULL), a, 11) else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 0, "abc") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 1, "abc") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 2, "def") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 3, "ghi") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 4, "jkl") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 5, "jkl") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 6, "def") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 7, "123") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 8, "def") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 9, "ghi") else CHECK_ITEM(29, list_replace_list(a, 1, 2, d, NULL), a, 10, "abc") TEST_ACT(30, list_remove_range(b, 1, 3)) else CHECK_LENGTH(30, list_remove_range(b, 1, 3), b, 4) else CHECK_ITEM(30, list_remove_range(b, 1, 3), b, 0, "def") else CHECK_ITEM(30, list_remove_range(b, 1, 3), b, 1, "jkl") else CHECK_ITEM(30, list_remove_range(b, 1, 3), b, 2, "ghi") else CHECK_ITEM(30, list_remove_range(b, 1, 3), b, 3, "abc") /* Test list_apply, list_query, list_sort */ list_apply(a, (list_action_t *)action, NULL); if (strcmp(action_data, "abcabcdefghijkljkldef123defghiabc")) ++errors, printf("Test31: list_apply() failed\n"); for (i = 0; list_query(a, &index, (list_query_t *)query, NULL) != -1; ++i, ++index) { if (index != query_data[i]) { ++errors, printf("Test32: list_query returned %d (not %d)\n", (int)index, query_data[i]); break; } } TEST_ACT(33, list_sort(a, (list_cmp_t *)sort_cmp)) else CHECK_LENGTH(33, list_sort(a, sort_cmp), a, 11) else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 0, "123") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 1, "abc") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 2, "abc") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 3, "abc") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 4, "def") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 5, "def") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 6, "def") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 7, "ghi") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 8, "ghi") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 9, "jkl") else CHECK_ITEM(33, list_sort(a, sort_cmp), a, 10, "jkl") /* Test lister_create, lister_has_next, lister_next, lister_destroy */ TEST_ACT(34, lister = lister_create(a)) for (i = 0; lister_has_next(lister) == 1; ++i) { void *item = lister_next(lister); if (item != a->list[i]) /* white box */ { ++errors, printf("Test35: iteration %d is '%s' not '%s'\n", i, (char *)item, (char *)list_item(a, i)); break; } } lister_destroy(&lister); if (lister) ++errors, printf("Test36: lister_destroy(&lister) failed, lister is %p not NULL\n", (void *)lister); /* Test list_has_next, list_next, list_break */ for (i = 0; list_has_next(a) == 1; ++i) { void *item = list_next(a); if (item != a->list[i]) /* white box */ { ++errors, printf("Test37: internal iteration %d is '%s' not '%s'\n", i, (char *)item, (char *)list_item(a, i)); list_break(a); break; } } for (i = 0; list_has_next(a) == 1; ++i) { void *item = list_next(a); if (item != a->list[i]) /* white box */ { ++errors, printf("Test38: internal iteration %d is '%s' not '%s'\n", i, (char *)item, (char *)list_item(a, i)); list_break(a); break; } if (i == 2) { list_break(a); break; } } if (a->lister) ++errors, printf("Test38: list_break() failed\n"); /* Test lister_remove */ if (!(lister = lister_create(a))) ++errors, printf("Test39: lister_create() failed\n"); for (i = 0; lister_has_next(lister) == 1; ++i) { lister_next(lister); if (i == 4) lister_remove(lister); } lister_destroy(&lister); if (lister) ++errors, printf("Test40: lister_destroy(&lister) failed, lister is %p not NULL\n", (void *)lister); CHECK_LENGTH(41, list_remove(lister), a, 10) else CHECK_ITEM(41, lister_remove(lister), a, 0, "123") else CHECK_ITEM(41, lister_remove(lister), a, 1, "abc") else CHECK_ITEM(41, lister_remove(lister), a, 2, "abc") else CHECK_ITEM(41, lister_remove(lister), a, 3, "abc") else CHECK_ITEM(41, lister_remove(lister), a, 4, "def") else CHECK_ITEM(41, lister_remove(lister), a, 5, "def") else CHECK_ITEM(41, lister_remove(lister), a, 6, "ghi") else CHECK_ITEM(41, lister_remove(lister), a, 7, "ghi") else CHECK_ITEM(41, lister_remove(lister), a, 8, "jkl") else CHECK_ITEM(41, lister_remove(lister), a, 9, "jkl") list_destroy(&a); if (a) ++errors, printf("Test42: list_destroy(&a) failed, a is %p not NULL\n", (void *)a); list_destroy(&b); if (b) ++errors, printf("Test43: list_destroy(&b) failed, b is %p not NULL\n", (void *)b); list_destroy(&c); if (c) ++errors, printf("Test44: list_destroy(&c) failed, c is %p not NULL\n", (void *)c); list_destroy(&d); if (d) ++errors, printf("Test45: list_destroy(&d) failed, d is %p not NULL\n", (void *)d); /* Test relative index/range */ TEST_ACT(46, a = list_make(NULL, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL)) else { void *item; TEST_ACT(47, list_item(a, -1) == NULL) TEST_ACT(47, item = list_item(a, -2)) TEST_ACT(47, !strcmp(item, "9")) TEST_ACT(47, item = list_item(a, -3)) TEST_ACT(47, !strcmp(item, "8")) TEST_ACT(47, item = list_item(a, -4)) TEST_ACT(47, !strcmp(item, "7")) TEST_ACT(47, item = list_item(a, -5)) TEST_ACT(47, !strcmp(item, "6")) TEST_ACT(47, item = list_item(a, -6)) TEST_ACT(47, !strcmp(item, "5")) TEST_ACT(47, item = list_item(a, -7)) TEST_ACT(47, !strcmp(item, "4")) TEST_ACT(47, item = list_item(a, -8)) TEST_ACT(47, !strcmp(item, "3")) TEST_ACT(47, item = list_item(a, -9)) TEST_ACT(47, !strcmp(item, "2")) TEST_ACT(47, item = list_item(a, -10)) TEST_ACT(47, !strcmp(item, "1")) TEST_ACT(47, item = list_item(a, -11)) TEST_ACT(47, !strcmp(item, "0")) TEST_ACT(47, list_item(a, -12) == NULL) TEST_ACT(47, list_item(a, -1000) == NULL) TEST_ACT(48, list_remove_range(a, -5, -1)) else CHECK_LENGTH(48, list_remove_range(a, -5, -1), a, 6) else CHECK_ITEM(48, list_remove_range(a, -5, -1), a, 0, "0") else CHECK_ITEM(48, list_remove_range(a, -5, -1), a, 1, "1") else CHECK_ITEM(48, list_remove_range(a, -5, -1), a, 2, "2") else CHECK_ITEM(48, list_remove_range(a, -5, -1), a, 3, "3") else CHECK_ITEM(48, list_remove_range(a, -5, -1), a, 4, "4") else CHECK_ITEM(48, list_remove_range(a, -5, -1), a, 5, "5") TEST_ACT(49, list_remove_range(a, -1, -1)) else CHECK_LENGTH(49, list_remove_range(a, -1, -1), a, 6) else CHECK_ITEM(49, list_remove_range(a, -1, -1), a, 0, "0") else CHECK_ITEM(49, list_remove_range(a, -1, -1), a, 1, "1") else CHECK_ITEM(49, list_remove_range(a, -1, -1), a, 2, "2") else CHECK_ITEM(49, list_remove_range(a, -1, -1), a, 3, "3") else CHECK_ITEM(49, list_remove_range(a, -1, -1), a, 4, "4") else CHECK_ITEM(49, list_remove_range(a, -1, -1), a, 5, "5") TEST_ACT(50, list_remove_range(a, -3, -2)) else CHECK_LENGTH(50, list_remove_range(a, -3, -2), a, 5) else CHECK_ITEM(50, list_remove_range(a, -3, -2), a, 0, "0") else CHECK_ITEM(50, list_remove_range(a, -3, -2), a, 1, "1") else CHECK_ITEM(50, list_remove_range(a, -3, -2), a, 2, "2") else CHECK_ITEM(50, list_remove_range(a, -3, -2), a, 3, "3") else CHECK_ITEM(50, list_remove_range(a, -3, -2), a, 4, "5") TEST_ACT(51, list_insert(a, -1, "X")) else CHECK_LENGTH(51, list_insert(a, -1, "X"), a, 6) else CHECK_ITEM(51, list_insert(a, -1, "X"), a, 0, "0") else CHECK_ITEM(51, list_insert(a, -1, "X"), a, 1, "1") else CHECK_ITEM(51, list_insert(a, -1, "X"), a, 2, "2") else CHECK_ITEM(51, list_insert(a, -1, "X"), a, 3, "3") else CHECK_ITEM(51, list_insert(a, -1, "X"), a, 4, "5") else CHECK_ITEM(51, list_insert(a, -1, "X"), a, 5, "X") TEST_ACT(52, b = list_make(NULL, "a", "b", "c", NULL)) else TEST_ACT(52, list_insert_list(a, -1, b, NULL)) else CHECK_LENGTH(52, list_insert_list(a, -1, b, NULL), a, 9) else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 0, "0") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 1, "1") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 2, "2") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 3, "3") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 4, "5") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 5, "X") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 6, "a") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 7, "b") else CHECK_ITEM(52, list_insert_list(a, -1, b, NULL), a, 8, "c") list_destroy(&b); TEST_ACT(53, b = list_make(NULL, "x", "y", "z", NULL)) else TEST_ACT(53, list_insert_list(a, -10, b, NULL)) else CHECK_LENGTH(53, list_insert_list(a, -10, b, NULL), a, 12) else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 0, "x") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 1, "y") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 2, "z") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 3, "0") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 4, "1") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 5, "2") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 6, "3") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 7, "5") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 8, "X") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 9, "a") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 10, "b") else CHECK_ITEM(53, list_insert_list(a, -10, b, NULL), a, 11, "c") list_destroy(&b); TEST_ACT(54, b = list_make(NULL, "0", NULL)) else TEST_ACT(54, list_replace_list(a, -13, -9, b, NULL)) else CHECK_LENGTH(54, list_replace_list(a, -13, -9, b, NULL), a, 9) else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 0, "0") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 1, "1") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 2, "2") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 3, "3") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 4, "5") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 5, "X") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 6, "a") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 7, "b") else CHECK_ITEM(54, list_replace_list(a, -13, -9, b, NULL), a, 8, "c") list_destroy(&b); TEST_ACT(55, b = list_make(NULL, "X", NULL)) else TEST_ACT(55, list_replace_list(a, -5, -1, b, NULL)) else CHECK_LENGTH(55, list_replace_list(a, -5, -1, b, NULL), a, 6) else CHECK_ITEM(55, list_replace_list(a, -5, -1, b, NULL), a, 0, "0") else CHECK_ITEM(55, list_replace_list(a, -5, -1, b, NULL), a, 1, "1") else CHECK_ITEM(55, list_replace_list(a, -5, -1, b, NULL), a, 2, "2") else CHECK_ITEM(55, list_replace_list(a, -5, -1, b, NULL), a, 3, "3") else CHECK_ITEM(55, list_replace_list(a, -5, -1, b, NULL), a, 4, "5") else CHECK_ITEM(55, list_replace_list(a, -5, -1, b, NULL), a, 5, "X") list_destroy(&b); TEST_ACT(56, list_replace(a, -5, -2, "Y")) else CHECK_LENGTH(56, list_replace(a, -5, -2, "Y"), a, 4) else CHECK_ITEM(56, list_replace(a, -5, -2, "Y"), a, 0, "0") else CHECK_ITEM(56, list_replace(a, -5, -2, "Y"), a, 1, "1") else CHECK_ITEM(56, list_replace(a, -5, -2, "Y"), a, 2, "Y") else CHECK_ITEM(56, list_replace(a, -5, -2, "Y"), a, 3, "X") TEST_ACT(57, b = list_extract(a, -4, -2, NULL)) else CHECK_LENGTH(57, b = list_extract(a, -4, -2, NULL), b, 2) else CHECK_ITEM(57, b = list_extract(a, -4, -2, NULL), b, 0, "1") else CHECK_ITEM(57, b = list_extract(a, -4, -2, NULL), b, 1, "Y") list_destroy(&b); /* Test relative error checking */ TEST_ACT(58, list_remove_range(a, -2, -1000) == NULL) TEST_ACT(59, list_remove_range(a, -1000, -2) == NULL) TEST_ACT(60, list_remove_range(a, -1000, -1000) == NULL) TEST_ACT(61, list_insert(a, -1000, "?") == NULL) TEST_ACT(62, b = list_make(NULL, "?", NULL)) else { TEST_ACT(63, list_insert_list(a, -1000, b, NULL) == NULL) TEST_ACT(64, list_replace_list(a, -2, -1000, b, NULL) == NULL) TEST_ACT(65, list_replace_list(a, -1000, -2, b, NULL) == NULL) TEST_ACT(66, list_replace_list(a, -1000, -1000, b, NULL) == NULL) list_destroy(&b); } TEST_ACT(67, list_replace(a, -2, -1000, "?") == NULL) TEST_ACT(68, list_replace(a, -1000, -2, "?") == NULL) TEST_ACT(69, list_replace(a, -1000, -1000, "?") == NULL) TEST_ACT(70, list_extract(a, -2, -1000, NULL) == NULL) TEST_ACT(71, list_extract(a, -1000, -2, NULL) == NULL) TEST_ACT(72, list_extract(a, -1000, -1000, NULL) == NULL) list_destroy(&a); } /* Test lists with int items, list_map, list_grep */ TEST_ACT(73, a = list_create(NULL)) else { TEST_ACT(74, list_append_int(a, 2)) else CHECK_LENGTH(74, list_append_int(a, 2), a, 1) else CHECK_INT_ITEM(74, list_append_int(a, 2), a, 0, 2) TEST_ACT(75, list_prepend_int(a, 0)) else CHECK_LENGTH(75, list_prepend_int(a, 0), a, 2) else CHECK_INT_ITEM(75, list_prepend_int(a, 0), a, 0, 0) else CHECK_INT_ITEM(75, list_prepend_int(a, 0), a, 1, 2) TEST_ACT(76, list_insert_int(a, 1, 1)) else CHECK_LENGTH(76, list_insert_int(a, 1, 1), a, 3) else CHECK_INT_ITEM(76, list_insert_int(a, 1, 1), a, 0, 0) else CHECK_INT_ITEM(76, list_insert_int(a, 1, 1), a, 1, 1) else CHECK_INT_ITEM(76, list_insert_int(a, 1, 1), a, 2, 2) for (i = 0; list_has_next(a) == 1; ++i) { int item = list_next_int(a); if (item != (int)(long)a->list[i]) /* white box */ ++errors, printf("Test77: int list test failed (item %d = %d, not %d)\n", i, item, list_item_int(a, i)); } if (i != 3) ++errors, printf("Test78: list_has_next() failed (only %d items, not %d)\n", i, 3); if (!(lister = lister_create(a))) ++errors, printf("Test79: lister_create(a) failed\n"); else { for (i = 0; lister_has_next(lister) == 1; ++i) { int item = lister_next_int(lister); if (item != (int)(long)a->list[i]) /* white box */ ++errors, printf("Test80: int list test failed (item %d = %d, not %d)\n", i, item, i); } if (i != 3) ++errors, printf("Test81: lister_has_next() failed (only %d items, not %d)\n", i, 3); lister_destroy(&lister); } TEST_ACT(82, list_replace_int(a, 2, 1, 4)) else CHECK_LENGTH(82, list_replace_int(a, 2, 1, 4), a, 3) else CHECK_INT_ITEM(82, list_replace_int(a, 2, 1, 4), a, 0, 0) else CHECK_INT_ITEM(82, list_replace_int(a, 2, 1, 4), a, 1, 1) else CHECK_INT_ITEM(82, list_replace_int(a, 2, 1, 4), a, 2, 4) i = 0; TEST_ACT(83, b = list_map(a, NULL, (list_map_t *)mapf, &i)) else { CHECK_LENGTH(83, b = list_map(), b, 3) CHECK_INT_ITEM(83, b = list_map(), b, 0, 0) CHECK_INT_ITEM(83, b = list_map(), b, 1, 1) CHECK_INT_ITEM(83, b = list_map(), b, 2, 5) list_destroy(&b); } TEST_ACT(84, b = list_grep(a, (list_query_t *)grepf, NULL)) else { CHECK_LENGTH(84, b = list_grep(), b, 2) CHECK_INT_ITEM(84, b = list_grep(), b, 0, 0) CHECK_INT_ITEM(84, b = list_grep(), b, 1, 4) list_destroy(&b); } list_destroy(&a); } /* Test list_push_int, list_pop_int, list_unshift_int, list_shift_int */ TEST_ACT(85, a = list_create(NULL)) else { TEST_ACT(86, list_push_int(a, 1)) TEST_ACT(87, list_push_int(a, 2)) TEST_ACT(88, list_push_int(a, 3)) TEST_ACT(89, list_push_int(a, 0)) TEST_ACT(90, list_push_int(a, 5)) TEST_ACT(91, list_push_int(a, 6)) TEST_ACT(92, list_push_int(a, 7)) TEST_EQ(93, list_pop_int(a), 7) TEST_EQ(94, list_pop_int(a), 6) TEST_EQ(95, list_pop_int(a), 5) TEST_EQ(96, list_pop_int(a), 0) TEST_EQ(97, list_pop_int(a), 3) TEST_EQ(98, list_pop_int(a), 2) TEST_EQ(99, list_pop_int(a), 1) TEST_EQ(100, list_pop_int(a), 0) TEST_ACT(101, list_unshift_int(a, 1)) TEST_ACT(102, list_unshift_int(a, 2)) TEST_ACT(103, list_unshift_int(a, 3)) TEST_ACT(104, list_unshift_int(a, 0)) TEST_ACT(105, list_unshift_int(a, 5)) TEST_ACT(106, list_unshift_int(a, 6)) TEST_ACT(107, list_unshift_int(a, 7)) TEST_EQ(108, list_shift_int(a), 7) TEST_EQ(109, list_shift_int(a), 6) TEST_EQ(110, list_shift_int(a), 5) TEST_EQ(111, list_shift_int(a), 0) TEST_EQ(112, list_shift_int(a), 3) TEST_EQ(113, list_shift_int(a), 2) TEST_EQ(114, list_shift_int(a), 1) TEST_EQ(115, list_shift_int(a), 0) list_destroy(&a); } /* Test list_push, list_pop, list_unshift, list_shift */ TEST_ACT(116, a = list_create(free)) else { char *item; TEST_ACT(117, list_push(a, mem_strdup("1"))) TEST_ACT(118, list_push(a, mem_strdup("2"))) TEST_ACT(119, list_push(a, mem_strdup("3"))) TEST_ACT(120, list_push(a, mem_strdup("4"))) TEST_ACT(121, list_push(a, mem_strdup("5"))) TEST_ACT(122, list_push(a, mem_strdup("6"))) TEST_ACT(123, list_push(a, mem_strdup("7"))) #define CHECK_POP(i, action, value) \ if (!(item = (action)) || strcmp(item, (value))) \ ++errors, printf("Test%d: %s failed (\"%s\", not \"%s\")\n", (i), (#action), (item) ? item : "NULL", (value) ? (value) : NULL); \ free(item); CHECK_POP(124, list_pop(a), "7") CHECK_POP(125, list_pop(a), "6") CHECK_POP(126, list_pop(a), "5") CHECK_POP(127, list_pop(a), "4") CHECK_POP(128, list_pop(a), "3") CHECK_POP(129, list_pop(a), "2") CHECK_POP(130, list_pop(a), "1") TEST_ACT(131, !list_pop(a)) TEST_ACT(132, list_unshift(a, mem_strdup("1"))) TEST_ACT(133, list_unshift(a, mem_strdup("2"))) TEST_ACT(134, list_unshift(a, mem_strdup("3"))) TEST_ACT(135, list_unshift(a, mem_strdup("4"))) TEST_ACT(136, list_unshift(a, mem_strdup("5"))) TEST_ACT(137, list_unshift(a, mem_strdup("6"))) TEST_ACT(138, list_unshift(a, mem_strdup("7"))) CHECK_POP(139, list_shift(a), "7") CHECK_POP(140, list_shift(a), "6") CHECK_POP(141, list_shift(a), "5") CHECK_POP(142, list_shift(a), "4") CHECK_POP(143, list_shift(a), "3") CHECK_POP(144, list_shift(a), "2") CHECK_POP(145, list_shift(a), "1") TEST_ACT(146, !list_shift(a)) list_destroy(&a); } /* Test list_make, list_splice */ TEST_ACT(147, a = list_make(NULL, "a", "b", "c", "d", "e", "f", NULL)) else { List *splice; TEST_ACT(148, splice = list_splice(a, 0, 1, NULL)) else { CHECK_LENGTH(149, splice = list_splice(a, 0, 1, NULL), splice, 1) CHECK_ITEM(150, splice = list_splice(a, 0, 1, NULL), splice, 0, "a") CHECK_LENGTH(151, splice = list_splice(a, 0, 1, NULL), a, 5) CHECK_ITEM(152, splice = list_splice(a, 0, 1, NULL), a, 0, "b") CHECK_ITEM(153, splice = list_splice(a, 0, 1, NULL), a, 1, "c") CHECK_ITEM(154, splice = list_splice(a, 0, 1, NULL), a, 2, "d") CHECK_ITEM(155, splice = list_splice(a, 0, 1, NULL), a, 3, "e") CHECK_ITEM(156, splice = list_splice(a, 0, 1, NULL), a, 4, "f") list_destroy(&splice); } TEST_ACT(157, splice = list_splice(a, 4, 1, NULL)) else { CHECK_LENGTH(158, splice = list_splice(a, 4, 1, NULL), splice, 1) CHECK_ITEM(159, splice = list_splice(a, 4, 1, NULL), splice, 0, "f") CHECK_LENGTH(160, splice = list_splice(a, 4, 1, NULL), a, 4) CHECK_ITEM(161, splice = list_splice(a, 4, 1, NULL), a, 0, "b") CHECK_ITEM(162, splice = list_splice(a, 4, 1, NULL), a, 1, "c") CHECK_ITEM(163, splice = list_splice(a, 4, 1, NULL), a, 2, "d") CHECK_ITEM(164, splice = list_splice(a, 4, 1, NULL), a, 3, "e") list_destroy(&splice); } TEST_ACT(165, splice = list_splice(a, 1, 2, NULL)) else { CHECK_LENGTH(166, splice = list_splice(a, 1, 2, NULL), splice, 2) CHECK_ITEM(167, splice = list_splice(a, 1, 2, NULL), splice, 0, "c") CHECK_ITEM(168, splice = list_splice(a, 1, 2, NULL), splice, 1, "d") CHECK_LENGTH(169, splice = list_splice(a, 1, 2, NULL), a, 2) CHECK_ITEM(170, splice = list_splice(a, 1, 2, NULL), a, 0, "b") CHECK_ITEM(171, splice = list_splice(a, 1, 2, NULL), a, 1, "e") list_destroy(&splice); } list_destroy(&a); } /* Test MT Safety */ debug = av[1] && !strcmp(av[1], "debug"); if (debug) setbuf(stdout, NULL); #ifndef PTHREAD_RWLOCK_INITIALIZER pthread_rwlock_init(&rwlock, NULL); #endif if (debug) locker = locker_create_debug_rwlock(&rwlock); else locker = locker_create_rwlock(&rwlock); if (!locker) ++errors, printf("Test172: locker_create_rwlock() failed\n"); else { mt_test(173, locker); locker_destroy(&locker); } if (debug) locker = locker_create_debug_mutex(&mutex); else locker = locker_create_mutex(&mutex); if (!locker) ++errors, printf("Test174: locker_create_mutex() failed\n"); else { mt_test(175, locker); locker_destroy(&locker); } /* Test assumption: sizeof(int) <= sizeof(void *) */ if (sizeof(int) > sizeof(void *)) ++errors, printf("Test176: assumption failed: sizeof(int) > sizeof(void *): int lists are limited to %d bytes\n", (int)sizeof(void *)); if (errors) printf("%d/176 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/list.h000066400000000000000000000177001140522741300157000ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_LIST_H #define LIBSLACK_LIST_H #include #include #include #include typedef struct List List; typedef struct Lister Lister; typedef void list_release_t(void *item); typedef void *list_copy_t(const void *item); typedef int list_cmp_t(const void *a, const void *b); typedef void list_action_t(void *item, size_t *index, void *data); typedef void *list_map_t(void *item, size_t *index, void *data); typedef int list_query_t(void *item, size_t *index, void *data); _begin_decls List *list_create(list_release_t *destroy); List *list_make(list_release_t *destroy, ...); List *list_vmake(list_release_t *destroy, va_list args); List *list_copy(const List *src, list_copy_t *copy); List *list_create_with_locker(Locker *locker, list_release_t *destroy); List *list_make_with_locker(Locker *locker, list_release_t *destroy, ...); List *list_vmake_with_locker(Locker *locker, list_release_t *destroy, va_list args); List *list_copy_with_locker(Locker *locker, const List *src, list_copy_t *copy); int list_rdlock(const List *list); int list_wrlock(const List *list); int list_unlock(const List *list); void list_release(List *list); void *list_destroy(List **list); int list_own(List *list, list_release_t *destroy); int list_own_unlocked(List *list, list_release_t *destroy); list_release_t *list_disown(List *list); list_release_t *list_disown_unlocked(List *list); void *list_item(const List *list, ssize_t index); void *list_item_unlocked(const List *list, ssize_t index); int list_item_int(const List *list, ssize_t index); int list_item_int_unlocked(const List *list, ssize_t index); int list_empty(const List *list); int list_empty_unlocked(const List *list); ssize_t list_length(const List *list); ssize_t list_length_unlocked(const List *list); ssize_t list_last(const List *list); ssize_t list_last_unlocked(const List *list); List *list_remove(List *list, ssize_t index); List *list_remove_unlocked(List *list, ssize_t index); List *list_remove_range(List *list, ssize_t index, ssize_t range); List *list_remove_range_unlocked(List *list, ssize_t index, ssize_t range); List *list_insert(List *list, ssize_t index, void *item); List *list_insert_unlocked(List *list, ssize_t index, void *item); List *list_insert_int(List *list, ssize_t index, int item); List *list_insert_int_unlocked(List *list, ssize_t index, int item); List *list_insert_list(List *list, ssize_t index, const List *src, list_copy_t *copy); List *list_insert_list_unlocked(List *list, ssize_t index, const List *src, list_copy_t *copy); List *list_append(List *list, void *item); List *list_append_unlocked(List *list, void *item); List *list_append_int(List *list, int item); List *list_append_int_unlocked(List *list, int item); List *list_append_list(List *list, const List *src, list_copy_t *copy); List *list_append_list_unlocked(List *list, const List *src, list_copy_t *copy); List *list_prepend(List *list, void *item); List *list_prepend_unlocked(List *list, void *item); List *list_prepend_int(List *list, int item); List *list_prepend_int_unlocked(List *list, int item); List *list_prepend_list(List *list, const List *src, list_copy_t *copy); List *list_prepend_list_unlocked(List *list, const List *src, list_copy_t *copy); List *list_replace(List *list, ssize_t index, ssize_t range, void *item); List *list_replace_unlocked(List *list, ssize_t index, ssize_t range, void *item); List *list_replace_int(List *list, ssize_t index, ssize_t range, int item); List *list_replace_int_unlocked(List *list, ssize_t index, ssize_t range, int item); List *list_replace_list(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy); List *list_replace_list_unlocked(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy); List *list_extract(const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_unlocked(const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_with_locker(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_with_locker_unlocked(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_push(List *list, void *item); List *list_push_unlocked(List *list, void *item); List *list_push_int(List *list, int item); List *list_push_int_unlocked(List *list, int item); void *list_pop(List *list); void *list_pop_unlocked(List *list); int list_pop_int(List *list); int list_pop_int_unlocked(List *list); void *list_shift(List *list); void *list_shift_unlocked(List *list); int list_shift_int(List *list); int list_shift_int_unlocked(List *list); List *list_unshift(List *list, void *item); List *list_unshift_unlocked(List *list, void *item); List *list_unshift_int(List *list, int item); List *list_unshift_int_unlocked(List *list, int item); List *list_splice(List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_unlocked(List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_with_locker(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_with_locker_unlocked(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_sort(List *list, list_cmp_t *cmp); List *list_sort_unlocked(List *list, list_cmp_t *cmp); void list_apply(List *list, list_action_t *action, void *data); void list_apply_rdlocked(List *list, list_action_t *action, void *data); void list_apply_wrlocked(List *list, list_action_t *action, void *data); void list_apply_unlocked(List *list, list_action_t *action, void *data); List *list_map(List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_unlocked(List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_with_locker(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_with_locker_unlocked(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_grep(List *list, list_query_t *grep, void *data); List *list_grep_unlocked(List *list, list_query_t *grep, void *data); List *list_grep_with_locker(Locker *locker, List *list, list_query_t *grep, void *data); List *list_grep_with_locker_unlocked(Locker *locker, List *list, list_query_t *grep, void *data); ssize_t list_query(List *list, ssize_t *index, list_query_t *query, void *data); ssize_t list_query_unlocked(List *list, ssize_t *index, list_query_t *query, void *data); Lister *lister_create(List *list); Lister *lister_create_rdlocked(List *list); Lister *lister_create_wrlocked(List *list); Lister *lister_create_unlocked(const List *list); void lister_release(Lister *lister); void lister_release_unlocked(Lister *lister); void *lister_destroy(Lister **lister); void *lister_destroy_unlocked(Lister **lister); int lister_has_next(Lister *lister); void *lister_next(Lister *lister); int lister_next_int(Lister *lister); void lister_remove(Lister *lister); int list_has_next(List *list); void list_break(List *list); void *list_next(List *list); int list_next_int(List *list); void list_remove_current(List *list); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/locker.c000066400000000000000000001132121140522741300161720ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - abstract locking, rwlocks =head1 SYNOPSIS #include #include typedef struct Locker Locker; typedef int lockerf_t(void *lock); Locker *locker_create_mutex(pthread_mutex_t *mutex); Locker *locker_create_rwlock(pthread_rwlock_t *rwlock); Locker *locker_create_debug_mutex(pthread_mutex_t *mutex); Locker *locker_create_debug_rwlock(pthread_rwlock_t *rwlock); Locker *locker_create(void *lock, lockerf_t *tryrdlock, lockerf_t *rdlock, lockerf_t *trywrlock, lockerf_t *wrlock, lockerf_t *unlock); void locker_release(Locker *locker); void *locker_destroy(Locker **locker); int locker_tryrdlock(Locker *locker); int locker_rdlock(Locker *locker); int locker_trywrlock(Locker *locker); int locker_wrlock(Locker *locker); int locker_unlock(Locker *locker); int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared); int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared); =head1 DESCRIPTION This module provides an abstraction of thread synchronisation that facilitates the implementation of I libraries. I'll explain what this means. Libraries need to be I when used in a multi threaded program. However, most programs are single threaded and synchronisation doesn't come for free so libraries should be I when used in a single threaded program. Even in multi threaded programs, some functions or objects may only be accessed by a single thread and so they should not incur the expense of synchronisation. When an object is shared between multiple threads which need to be synchronised, the method of synchronisation must be carefully selected by the client code. There are tradeoffs between concurrency and overhead. The greater the concurrency, the greater the overhead. More locks give greater concurrency but have greater overhead. Readers/Writer locks can give greater concurrency than Mutex locks but have greater overhead. One lock for each object may be required, or one lock for all (or a set of) objects may be more appropriate. Generally, the best synchronisation strategy for a given application can only be determined by testing/benchmarking the written application. It is important to be able to experiment with the synchronisation strategy at this stage of development without pain. The solution, of course, is to decouple the synchronisation strategy from the library code. To facilitate this, the I type and associated functions can be incorporated into library code to provide the necessary flexibility. The I type specifies a lock and a set of functions for manipulating the lock. Arbitrary objects can include a pointer to a I object to use for thread synchronisation. Such objects may each have their own lock by having separate I objects or they may share the same lock by sharing the same I object. Only the application developer can determine what is appropriate for each application on a case by case basis. I means that the application developer has a mechanism for specifying the synchronisation requirements to be applied to library code. For more details, see I (C). This module also provides an implementation of readers/writer locks which are defined in recent standards but may not be on your system yet. The readers/writer lock implementation originally came from code by Bil Lewis and was then completed and made robust. =over 4 =cut */ #include "config.h" #include "std.h" #include "locker.h" #include "mem.h" #include "err.h" #ifndef HAVE_PTHREAD_PROCESS_PRIVATE #define PTHREAD_PROCESS_PRIVATE 0 #endif #ifndef HAVE_PTHREAD_PROCESS_SHARED #define PTHREAD_PROCESS_SHARED 0 #endif #ifndef HAVE_PTHREAD_CONDATTR_INIT #define pthread_condattr_init(condattr) 0 #endif #ifndef HAVE_PTHREAD_CONDATTR_SETPSHARED #define pthread_condattr_setpshared(condattr, pshared) 0 #endif #ifndef HAVE_PTHREAD_MUTEXATTR_SETPSHARED #define pthread_mutexattr_setpshared(mutexattr, pshared) 0 #endif #if 0 struct Locker { void *lock; lockerf_t *tryrdlock; lockerf_t *rdlock; lockerf_t *trywrlock; lockerf_t *wrlock; lockerf_t *unlock; }; #endif #ifndef TEST #define try(action) { int rc = (action); if (rc != 0) return rc; } #define try_catch(action, catch) { int rc = (action); if (rc != 0) return (catch), rc; } /* =item C Creates a I object that will operate on the mutex lock, C. I and I will call I. I and I will call I. I will call I. It is the caller's responsibility to initialise C if necessary before use and to destroy C if necessary after use. On success, returns the new I object. On error, returns C with C set appropriately. =cut */ Locker *locker_create_mutex(pthread_mutex_t *mutex) { return locker_create ( mutex, (lockerf_t *)pthread_mutex_trylock, (lockerf_t *)pthread_mutex_lock, (lockerf_t *)pthread_mutex_trylock, (lockerf_t *)pthread_mutex_lock, (lockerf_t *)pthread_mutex_unlock ); } /* =item C Creates a I object that will operate on the readers/writer lock, C. I will call I. I will call I. I will call I. I will call I. I will call I. It is the caller's responsibility to initialise C if necessary before use and to destroy C if necessary after use. On success, returns the new I object. On error, returns C with C set appropriately. =cut */ Locker *locker_create_rwlock(pthread_rwlock_t *rwlock) { return locker_create ( rwlock, (lockerf_t *)pthread_rwlock_tryrdlock, (lockerf_t *)pthread_rwlock_rdlock, (lockerf_t *)pthread_rwlock_trywrlock, (lockerf_t *)pthread_rwlock_wrlock, (lockerf_t *)pthread_rwlock_unlock ); } #ifndef NO_DEBUG_LOCKERS /* =item C Equivalent to I except that debug messages are printed to standard output before and after each locking function is called to help locate deadlocks. The debug messages look like: [thread id] funcname(mutex address) ... [thread id] funcname(mutex address) done On success, returns the new I. On error, returns C with C set appropriately. =cut */ static int debug_invoke(const char *name, lockerf_t *action, void *lock) { int err; printf("[%lu] %s(%p) ...\n", (unsigned long)pthread_self(), name, lock); err = action(lock); if (err) printf("[%lu] %s(%p) done (%s)\n", (unsigned long)pthread_self(), name, lock, strerror(err)); else printf("[%lu] %s(%p) done\n", (unsigned long)pthread_self(), name, lock); return err; } static int debug_pthread_mutex_trylock(pthread_mutex_t *mutex) { return debug_invoke("pthread_mutex_trylock", (lockerf_t *)pthread_mutex_trylock, mutex); } static int debug_pthread_mutex_lock(pthread_mutex_t *mutex) { return debug_invoke("pthread_mutex_lock", (lockerf_t *)pthread_mutex_lock, mutex); } static int debug_pthread_mutex_unlock(pthread_mutex_t *mutex) { return debug_invoke("pthread_mutex_unlock", (lockerf_t *)pthread_mutex_unlock, mutex); } Locker *locker_create_debug_mutex(pthread_mutex_t *mutex) { return locker_create ( mutex, (lockerf_t *)debug_pthread_mutex_trylock, (lockerf_t *)debug_pthread_mutex_lock, (lockerf_t *)debug_pthread_mutex_trylock, (lockerf_t *)debug_pthread_mutex_lock, (lockerf_t *)debug_pthread_mutex_unlock ); } /* =item C Equivalent to I except that debug messages are printed to standard output before and after each locking function is called to help locate deadlocks. The debug messages look like: [thread id] funcname(rwlock address) ... [thread id] funcname(rwlock address) done On success, returns the new I. On error, returns C with C set appropriately. =cut */ static int debug_pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { return debug_invoke("pthread_rwlock_tryrdlock", (lockerf_t *)pthread_rwlock_tryrdlock, rwlock); } static int debug_pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { return debug_invoke("pthread_rwlock_rdlock", (lockerf_t *)pthread_rwlock_rdlock, rwlock); } static int debug_pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { return debug_invoke("pthread_rwlock_trywrlock", (lockerf_t *)pthread_rwlock_trywrlock, rwlock); } static int debug_pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { return debug_invoke("pthread_rwlock_wrlock", (lockerf_t *)pthread_rwlock_wrlock, rwlock); } static int debug_pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { return debug_invoke("pthread_rwlock_unlock", (lockerf_t *)pthread_rwlock_unlock, rwlock); } Locker *locker_create_debug_rwlock(pthread_rwlock_t *rwlock) { return locker_create ( rwlock, (lockerf_t *)debug_pthread_rwlock_tryrdlock, (lockerf_t *)debug_pthread_rwlock_rdlock, (lockerf_t *)debug_pthread_rwlock_trywrlock, (lockerf_t *)debug_pthread_rwlock_wrlock, (lockerf_t *)debug_pthread_rwlock_unlock ); } #endif /* =item C Creates a I object that will operate on the synchronisation variable, C. I will call C. I will call C. I will call C. I will call C. I will call C. It is the caller's responsibility to initialise C if necessary before use and to destroy C if necessary after use. None of the arguments may be C. On success, returns the new I object. On error, returns C with C set appropriately. =cut */ Locker *locker_create(void *lock, lockerf_t *tryrdlock, lockerf_t *rdlock, lockerf_t *trywrlock, lockerf_t *wrlock, lockerf_t *unlock) { Locker *locker; if (!lock || !tryrdlock || !rdlock || !trywrlock || !wrlock || !unlock) return set_errnull(EINVAL); if (!(locker = mem_new(Locker))) /* XXX decouple */ return NULL; locker->lock = lock; locker->tryrdlock = tryrdlock; locker->rdlock = rdlock; locker->trywrlock = trywrlock; locker->wrlock = wrlock; locker->unlock = unlock; return locker; } /* =item C Releases (deallocates) C. It is the caller's responsibility to destroy the synchronisation variable used by C if necessary. =cut */ void locker_release(Locker *locker) { if (!locker) return; mem_release(locker); } /* =item C Destroys (deallocates and sets to C) C<*locker>. Returns C. It is the caller's responsibility to destroy the synchronisation variable used by C if necessary. =cut */ void *locker_destroy(Locker **locker) { if (locker && *locker) { locker_release(*locker); *locker = NULL; } return NULL; } /* =item C Tries to claim a read lock on the synchronisation variable managed by C. See I and I for details. On success, returns C<0>. On error, returns the error code from the underlying I library function. =cut */ int (locker_tryrdlock)(Locker *locker) { return locker ? locker->tryrdlock(locker->lock) : 0; } /* =item C Claims a read lock on the synchronisation variable managed by C. See I and I for details. On success, returns C<0>. On error, returns the error code from the underlying I library function. =cut */ int (locker_rdlock)(Locker *locker) { return locker ? locker->rdlock(locker->lock) : 0; } /* =item C Tries to claim a write lock on the synchronisation variable managed by C. See I and I for details. On success, returns C<0>. On error, returns the error code from the underlying I library function. =cut */ int (locker_trywrlock)(Locker *locker) { return locker ? locker->trywrlock(locker->lock) : 0; } /* =item C Claims a write lock on the synchronisation variable managed by C. See I and I for details. On success, returns C<0>. On error, returns the error code from the underlying I library function. =cut */ int (locker_wrlock)(Locker *locker) { return locker ? locker->wrlock(locker->lock) : 0; } /* =item C Unlocks the synchronisation variable managed by C. See I and I for details. On success, returns C<0>. On error, returns the error code from the underlying I library function. =cut */ int (locker_unlock)(Locker *locker) { return locker ? locker->unlock(locker->lock) : 0; } #ifndef HAVE_PTHREAD_RWLOCK /* =item I Initialises the readers/writer lock, C, with the attributes in C. If C is C, C is initialised as a process private lock. Note that this is the only option under Linux. On success, returns C<0>. On error, returns an error code. =cut */ int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { #define DMA pthread_mutexattr_destroy(&mutexattr) #define DCA pthread_condattr_destroy(&condattr) #define DL pthread_mutex_destroy(&rwlock->lock) #define DR pthread_cond_destroy(&rwlock->readers) #define DW pthread_cond_destroy(&rwlock->writers) pthread_mutexattr_t mutexattr; pthread_condattr_t condattr; int pshared; if (!rwlock) return EINVAL; try(pthread_mutexattr_init(&mutexattr)) try_catch(pthread_condattr_init(&condattr), DMA) if (attr) try_catch(pthread_rwlockattr_getpshared(attr, &pshared), (DCA, DMA)) else pshared = PTHREAD_PROCESS_PRIVATE; try_catch(pthread_mutexattr_setpshared(&mutexattr, pshared), (DCA, DMA)) try_catch(pthread_condattr_setpshared(&condattr, pshared), (DCA, DMA)) try_catch(pthread_mutex_init(&rwlock->lock, &mutexattr), (DCA, DMA)) try_catch(pthread_cond_init(&rwlock->readers, &condattr), (DL, DCA, DMA)) try_catch(pthread_cond_init(&rwlock->writers, &condattr), (DR, DL, DCA, DMA)) rwlock->waiters = 0; rwlock->state = 0; return 0; #undef DMA #undef DCA #undef DL #undef DR #undef DW } /* =item I Destroys the readers/writer lock, C, that was initialised by I. It is the caller's responsibility to deallocate the memory pointed to by C if necessary. On success, returns C<0>. On error, returns an error code. =cut */ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { if (!rwlock) return EINVAL; pthread_mutex_destroy(&rwlock->lock); pthread_cond_destroy(&rwlock->readers); pthread_cond_destroy(&rwlock->writers); return 0; } /* =item I Claims a read lock on C. Multiple threads may hold a read lock at the same time. On success, returns C<0>. On error, returns an error code. =cut */ static void rdlock_cleanup(void *arg) { pthread_rwlock_t *rwlock = (pthread_rwlock_t *)arg; pthread_mutex_unlock(&rwlock->lock); } int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { if (!rwlock) return EINVAL; try(pthread_mutex_lock(&rwlock->lock)) pthread_cleanup_push(rdlock_cleanup, rwlock); /* Wait until there are no active or queued writers */ while (rwlock->state == -1 || rwlock->waiters) try(pthread_cond_wait(&rwlock->readers, &rwlock->lock)) ++rwlock->state; pthread_cleanup_pop(1); return 0; } /* =item I Attempts to claim a read lock on C. On success, returns C<0>. On error, returns an error code. If C is already locked, returns C. =cut */ int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { if (!rwlock) return EINVAL; /* Are there no active or waiting writers? */ try(pthread_mutex_lock(&rwlock->lock)) if (rwlock->state != -1 && !rwlock->waiters) { ++rwlock->state; try(pthread_mutex_unlock(&rwlock->lock)) return 0; } try(pthread_mutex_unlock(&rwlock->lock)) return EBUSY; } /* =item I Claims a write lock on C. Only a single thread may hold a write lock at any point in time. On success, returns C<0>. On error, returns an error code. =cut */ static void wrlock_cleanup(void *arg) { pthread_rwlock_t *rwlock = (pthread_rwlock_t *)arg; /* ** Was the only queued writer and lock is available for readers. ** Called through cancellation clean-up so lock is held at entry. */ if (--rwlock->waiters == 0 && rwlock->state != -1) pthread_cond_broadcast(&rwlock->readers); pthread_mutex_unlock(&rwlock->lock); } int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { if (!rwlock) return EINVAL; try(pthread_mutex_lock(&rwlock->lock)) /* Queue this writer */ ++rwlock->waiters; pthread_cleanup_push(wrlock_cleanup, rwlock); /* Wait until readers have finished */ while (rwlock->state != 0) try(pthread_cond_wait(&rwlock->writers, &rwlock->lock)) rwlock->state = -1; pthread_cleanup_pop(1); return 0; } /* =item I Attempts to claim a write lock on C. On success, returns C<0>. On error, returns an error code. If C is already locked, returns C. =cut */ int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { if (!rwlock) return EINVAL; try(pthread_mutex_lock(&rwlock->lock)) /* Are there no readers and no active or queued writers? */ if (rwlock->state == 0 && rwlock->waiters == 0) { rwlock->state = -1; try(pthread_mutex_unlock(&rwlock->lock)) return 0; } try(pthread_mutex_unlock(&rwlock->lock)) return EBUSY; } /* =item I Unlocks C. On success, returns C<0>. On error, returns an error code. =cut */ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { if (!rwlock) return EINVAL; /* Writer releasing lock */ if (rwlock->state == -1) { /* Mark as available */ rwlock->state = 0; /* Signal queued writers if any, or broadcast to readers */ if (rwlock->waiters) try(pthread_cond_signal(&rwlock->writers)) else try(pthread_cond_broadcast(&rwlock->readers)) } else { /* Reader releasing lock */ if (--rwlock->state == 0) try(pthread_cond_signal(&rwlock->writers)) } try(pthread_mutex_unlock(&rwlock->lock)) return 0; } /* =item I Initialises the readers/writer lock attribute object, C. On success, returns C<0>. On error, returns an error code. =cut */ int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { if (!attr) return EINVAL; attr->pshared = PTHREAD_PROCESS_PRIVATE; return 0; } /* =item I Destroys the readers/writer lock attribute object, C, that was initialised by I. Note that the memory pointed to by C may also need to be deallocated separately. On success, returns C<0>. On error, returns an error code. =cut */ int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) { if (!attr) return EINVAL; return 0; } /* =item I Stores the C attribute of C into C<*pshared>. On success, returns C<0>. On error, returns an error code. =cut */ int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared) { if (!attr || !pshared) return EINVAL; *pshared = attr->pshared; return 0; } /* =item I Sets the C attribute of C to C. On success, returns C<0>. On error, returns an error code. Note that under Linux, C must be C or I will subsequently fail. =cut */ int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) { if (!attr) return EINVAL; if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) return EINVAL; attr->pshared = pshared; return 0; } #endif /* =back =head1 ERRORS On error, C is set either by an underlying function, or as follows: =over 4 =item C Arguments are C or invalid. =back =head1 MT-Level MT-Safe =head1 EXAMPLES A mutex example: #include #include int main(int ac, char **av) { pthread_mutex_t mutex[1]; Locker *locker; errno = pthread_mutex_init(mutex, NULL); locker = locker_create_mutex(mutex); errno = locker_rdlock(locker); errno = locker_unlock(locker); errno = locker_wrlock(locker); errno = locker_unlock(locker); locker_destroy(&locker); pthread_mutex_destroy(mutex); return EXIT_SUCCESS; } A rwlock example: #include #include int main(int ac, char **av) { pthread_rwlock_t rwlock[1]; Locker *locker; errno = pthread_rwlock_init(rwlock, NULL); locker = locker_create_rwlock(rwlock); errno = locker_rdlock(locker); errno = locker_unlock(locker); errno = locker_wrlock(locker); errno = locker_unlock(locker); locker_destroy(&locker); pthread_rwlock_destroy(rwlock); return EXIT_SUCCESS; } A debug mutex example: #include #include int main(int ac, char **av) { pthread_mutex_t mutex[1]; Locker *locker; errno = pthread_mutex_init(mutex, NULL); locker = locker_create_debug_mutex(mutex); errno = locker_rdlock(locker); errno = locker_unlock(locker); errno = locker_wrlock(locker); errno = locker_unlock(locker); locker_destroy(&locker); pthread_mutex_destroy(mutex); return EXIT_SUCCESS; } A debug rwlock example: #include #include int main(int ac, char **av) { pthread_rwlock_t rwlock[1]; Locker *locker; errno = pthread_rwlock_init(rwlock, NULL); locker = locker_create_debug_rwlock(rwlock); errno = locker_rdlock(locker); errno = locker_unlock(locker); errno = locker_wrlock(locker); errno = locker_unlock(locker); locker_destroy(&locker); pthread_rwlock_destroy(rwlock); return EXIT_SUCCESS; } A non-locking example: #include #include int main(int ac, char **av) { Locker *locker = NULL; errno = locker_rdlock(locker); errno = locker_unlock(locker); errno = locker_wrlock(locker); errno = locker_unlock(locker); return EXIT_SUCCESS; } =head1 SEE ALSO I, C =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST #include #include struct List /* identical to list.c */ { size_t size; /* number of item slots allocated */ size_t length; /* number of items used */ void **list; /* vector of items (void *) */ list_release_t *destroy; /* item destructor, if any */ Lister *lister; /* built-in iterator */ Locker *locker; /* locking strategy for this object */ }; /* Unsafe */ #define list_length_test_nolock_macro(list) ((list) ? (list)->length : -1) ssize_t list_length_test_nolock_func(const List *list) { if (!list) return -1; return list->length; } /* MT-Safe */ ssize_t list_length_test_direct_mutex(const List *list, pthread_mutex_t *mutex) { size_t length; if (!list) return -1; if (pthread_mutex_lock(mutex)) return -1; length = list->length; if (pthread_mutex_unlock(mutex)) return -1; return length; } ssize_t list_length_test_direct_rwlock(const List *list, pthread_rwlock_t *rwlock) { size_t length; if (!list) return -1; if (pthread_rwlock_rdlock(rwlock)) return -1; length = list->length; if (pthread_rwlock_unlock(rwlock)) return -1; return length; } /* MT-Disciplined */ ssize_t list_length_test_lock_funcptr(const List *list, lockerf_t *lockf, lockerf_t *unlockf, void *lock) { size_t length; if (!list) return -1; if (lock && lockf(lock)) return -1; length = list->length; if (lock && unlockf(lock)) return -1; return length; } ssize_t list_length_test_lock_funcptr_1test(const List *list, lockerf_t *lockf, lockerf_t *unlockf, void *lock) { size_t length; if (!list) return -1; if (!lock) return list->length; if (lockf(lock)) return -1; length = list->length; if (unlockf(lock)) return -1; return length; } ssize_t list_length_test_separate_locker(const List *list, Locker *locker) { size_t length; if (!list) return -1; if (locker_rdlock(locker)) return -1; length = list->length; if (locker_unlock(locker)) return -1; return length; } ssize_t list_length_test_separate_locker_1test(const List *list, Locker *locker) { size_t length; if (!list) return -1; if (!locker) return list->length; if (locker->rdlock(locker->lock)) return -1; length = list->length; if (locker->unlock(locker->lock)) return -1; return length; } ssize_t list_length_test_builtin_locker(const List *list) { size_t length; if (!list) return -1; if (locker_rdlock(list->locker)) return -1; length = list->length; if (locker_unlock(list->locker)) return -1; return length; } ssize_t list_length_test_builtin_locker_cacheaddr(const List *list) { size_t length; Locker *locker; if (!list) return -1; locker = list->locker; if (locker_rdlock(locker)) return -1; length = list->length; if (locker_unlock(locker)) return -1; return length; } ssize_t list_length_test_builtin_locker_1test(const List *list) { size_t length; if (!list) return -1; if (!list->locker) return list->length; if (list->locker->rdlock(list->locker->lock)) return -1; length = list->length; if (list->locker->unlock(list->locker->lock)) return -1; return length; } ssize_t list_length_test_builtin_locker_1test_cacheaddr(const List *list) { size_t length; Locker *locker; lockerf_t *rdlock; lockerf_t *unlock; void *lock; if (!list) return -1; if (!list->locker) return list->length; locker = list->locker; rdlock = locker->rdlock; unlock = locker->unlock; lock = locker->lock; if (rdlock(lock)) return -1; length = list->length; if (unlock(lock)) return -1; return length; } int main(int ac, char **av) { int errors = 0; int debug; Locker *locker; pthread_mutex_t mutex[1]; pthread_rwlock_t rwlock[1]; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s [debug]\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "locker"); /* ** Note: Debug lockers are not tested by default. Invoke the test with ** av[1] == "debug" to use debug lockers instead of ordinary lockers. ** Local implementation of rwlocks are not tested unless your system ** requires them. */ debug = (av[1] && !strcmp(av[1], "debug")); if (debug) setbuf(stdout, NULL); /* Test mutex lockers */ if ((errno = pthread_mutex_init(mutex, NULL))) ++errors, printf("Test1: failed to perform test: pthread_mutex_init() failed: err = %d\n", errno); else if (!(locker = (debug ? locker_create_debug_mutex : locker_create_mutex)(mutex))) ++errors, printf("Test1: %s() failed (%s)\n", debug ? "locker_create_debug_mutex" : "locker_create_mutex", strerror(errno)); else { if ((errno = locker_tryrdlock(locker))) ++errors, printf("Test2: locker_tryrdlock(mutex_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test3: locker_unlock(mutex_locker) failed (%s)\n", strerror(errno)); if ((errno = locker_rdlock(locker))) ++errors, printf("Test4: locker_rdlock(mutex_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test5: locker_unlock(mutex_locker) failed (%s)\n", strerror(errno)); if ((errno = locker_trywrlock(locker))) ++errors, printf("Test6: locker_trywrlock(mutex_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test7: locker_unlock(mutex_locker) failed (%s)\n", strerror(errno)); if ((errno = locker_wrlock(locker))) ++errors, printf("Test8: locker_wrlock(mutex_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test9: locker_unlock(mutex_locker) failed (%s)\n", strerror(errno)); locker_destroy(&locker); if (locker) ++errors, printf("Test10: locker_destroy(mutex_locker) failed (%s)\n", strerror(errno)); } pthread_mutex_destroy(mutex); /* Test rwlock lockers */ if ((errno = pthread_rwlock_init(rwlock, NULL))) ++errors, printf("Test11: failed to perform test: pthread_rwlock_init() failed: err = %d\n", errno); else if (!(locker = (debug ? locker_create_debug_rwlock : locker_create_rwlock)(rwlock))) ++errors, printf("Test11: %s() failed (%s)\n", debug ? "locker_create_debug_rwlock" : "locker_create_rwlock", strerror(errno)); else { if ((errno = locker_tryrdlock(locker))) ++errors, printf("Test12: locker_tryrdlock(rwlock_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test13: locker_unlock(rwlock_locker) failed (%s)\n", strerror(errno)); if ((errno = locker_rdlock(locker))) ++errors, printf("Test14: locker_rdlock(rwlock_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test15: locker_unlock(rwlock_locker) failed (%s)\n", strerror(errno)); if ((errno = locker_trywrlock(locker))) ++errors, printf("Test16: locker_trywrlock(rwlock_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test17: locker_unlock(rwlock_locker) failed (%s)\n", strerror(errno)); if ((errno = locker_wrlock(locker))) ++errors, printf("Test18: locker_wrlock(rwlock_locker) failed (%s)\n", strerror(errno)); else if ((errno = locker_unlock(locker))) ++errors, printf("Test19: locker_unlock(rwlock_locker) failed (%s)\n", strerror(errno)); locker_destroy(&locker); if (locker) ++errors, printf("Test20: locker_destroy(rwlock_locker) failed (%s)\n", strerror(errno)); } pthread_rwlock_destroy(rwlock); /* Timing tests */ if (av[1] && !strcmp(av[1], "time")) { List *list, *mutex_list, *rwlock_list; clock_t start, end; size_t i, length; pthread_mutex_t mutex; Locker *mutex_locker; pthread_rwlock_t rwlock; Locker *rwlock_locker; double nm, nf; double dm, dr; double np, mp, rp, np1, mp1, rp1; double nsl, msl, rsl, nsl1, msl1, rsl1; double nbl, mbl, rbl, nblc, mblc, rblc; double nbl1, mbl1, rbl1, nbl1c, mbl1c, rbl1c; printf("Timing: struct attribute accesses with differing MT safety\n"); list = list_make(NULL, "...", NULL); pthread_mutex_init(&mutex, NULL); mutex_locker = locker_create_mutex(&mutex); mutex_list = list_create_with_locker(mutex_locker, NULL); pthread_rwlock_init(&rwlock, NULL); rwlock_locker = locker_create_rwlock(&rwlock); rwlock_list = list_create_with_locker(rwlock_locker, NULL); #define ITERATIONS 10000000 #define TIME_TEST(label, base, basetime, timevar, action) \ start = clock(); \ for (i = 0; i < ITERATIONS; ++i) \ action; \ end = clock(); \ timevar = ((double)(end - start) / (double)CLOCKS_PER_SEC) / ITERATIONS * 1000000000; \ printf(" %-39s %g ns", (label), (timevar)); \ if (!base) \ { \ printf(" (overhead = %g ns", (timevar) - (basetime)); \ if ((basetime) != 0.0) \ printf(" = %g%%", (((timevar) / (basetime)) - 1.0) * 100); \ printf(")"); \ } \ printf("\n"); printf(" Unsafe:\n"); TIME_TEST("nolock/macro", 1, 0.0, nm, length = list_length_test_nolock_macro(list)) TIME_TEST("nolock/func", 1, 0.0, nf, length = list_length_test_nolock_func(list)) printf("\n"); printf(" MT-Safe:\n"); TIME_TEST("direct mutex", 1, 0.0, dm, length = list_length_test_direct_mutex(list, &mutex)) TIME_TEST("direct rwlock", 1, 0.0, dr, length = list_length_test_direct_rwlock(list, &rwlock)) printf("\n"); printf(" MT-Disciplined:\n"); TIME_TEST("null pointers", 0, nf, np, length = list_length_test_lock_funcptr(list, NULL, NULL, NULL)) TIME_TEST("null pointers (1test)", 0, nf, np1, length = list_length_test_lock_funcptr_1test(list, NULL, NULL, NULL)) TIME_TEST("null separate locker", 0, nf, nsl, length = list_length_test_separate_locker(list, NULL)) TIME_TEST("null separate locker (1test)", 0, nf, nsl1, length = list_length_test_separate_locker_1test(list, NULL)) TIME_TEST("null builtin locker", 0, nf, nbl, length = list_length_test_builtin_locker(list)) TIME_TEST("null builtin locker (1test)", 0, nf, nbl1, length = list_length_test_builtin_locker_1test(list)) TIME_TEST("null builtin locker (cacheaddr)", 0, nf, nblc, length = list_length_test_builtin_locker_cacheaddr(list)) TIME_TEST("null builtin locker (1test/cacheaddr)", 0, nf, nbl1c, length = list_length_test_builtin_locker_1test_cacheaddr(list)) printf("\n"); TIME_TEST("mutex pointers", 0, dm, mp, length = list_length_test_lock_funcptr(mutex_list, (lockerf_t *)pthread_mutex_lock, (lockerf_t *)pthread_mutex_unlock, &mutex)) TIME_TEST("mutex pointers (1test)", 0, dm, mp1, length = list_length_test_lock_funcptr_1test(mutex_list, (lockerf_t *)pthread_mutex_lock, (lockerf_t *)pthread_mutex_unlock, &mutex)) TIME_TEST("mutex separate locker", 0, dm, msl, length = list_length_test_separate_locker(mutex_list, mutex_locker)) TIME_TEST("mutex separate locker (1test)", 0, dm, msl1, length = list_length_test_separate_locker_1test(mutex_list, mutex_locker)) TIME_TEST("mutex builtin locker", 0, dm, mbl, length = list_length_test_builtin_locker(mutex_list)) TIME_TEST("mutex builtin locker (1test)", 0, dm, mbl1, length = list_length_test_builtin_locker_1test(mutex_list)) TIME_TEST("mutex builtin locker (cacheaddr)", 0, dm, mblc, length = list_length_test_builtin_locker_cacheaddr(mutex_list)) TIME_TEST("mutex builtin locker (1test/cacheaddr)", 0, dm, mbl1c, length = list_length_test_builtin_locker_1test_cacheaddr(mutex_list)) printf("\n"); TIME_TEST("rwlock pointers", 0, dr, rp, length = list_length_test_lock_funcptr(rwlock_list, (lockerf_t *)pthread_rwlock_rdlock, (lockerf_t *)pthread_rwlock_unlock, &rwlock)) TIME_TEST("rwlock pointers (1test)", 0, dr, rp1, length = list_length_test_lock_funcptr_1test(rwlock_list, (lockerf_t *)pthread_rwlock_rdlock, (lockerf_t *)pthread_rwlock_unlock, &rwlock)) TIME_TEST("rwlock separate locker", 0, dr, rsl, length = list_length_test_separate_locker(rwlock_list, rwlock_locker)) TIME_TEST("rwlock separate locker (1test)", 0, dr, rsl1, length = list_length_test_separate_locker_1test(rwlock_list, rwlock_locker)) TIME_TEST("rwlock builtin locker", 0, dr, rbl, length = list_length_test_builtin_locker(rwlock_list)) TIME_TEST("rwlock builtin locker (1test)", 0, dr, rbl1, length = list_length_test_builtin_locker_1test(rwlock_list)) TIME_TEST("rwlock builtin locker (cacheaddr)", 0, dr, rblc, length = list_length_test_builtin_locker_cacheaddr(rwlock_list)) TIME_TEST("rwlock builtin locker (1test/cacheaddr)", 0, dr, rbl1c, length = list_length_test_builtin_locker_1test_cacheaddr(rwlock_list)) printf("\n"); list_release(list); list_release(mutex_list); locker_release(mutex_locker); pthread_mutex_destroy(&mutex); list_release(rwlock_list); locker_release(rwlock_locker); pthread_rwlock_destroy(&rwlock); } if (errors) printf("%d/20 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/locker.h000066400000000000000000000072651140522741300162110ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_LOCKER_H #define LIBSLACK_LOCKER_H #include #include typedef struct Locker Locker; typedef int lockerf_t(void *lock); #ifndef HAVE_PTHREAD_RWLOCK typedef struct pthread_rwlock_t pthread_rwlock_t; typedef struct pthread_rwlockattr_t pthread_rwlockattr_t; struct pthread_rwlock_t { pthread_mutex_t lock; /* Lock for structure */ pthread_cond_t readers; /* Are there readers waiting? */ pthread_cond_t writers; /* Are there writers waiting? */ int waiters; /* Number of writers waiting */ int state; /* State: -1 -> writer, 0 -> idle, +ve -> readers */ }; struct pthread_rwlockattr_t { int pshared; /* Shared between processes or not */ }; #define PTHREAD_RWLOCK_INITIALIZER \ { \ PTHREAD_MUTEX_INITIALIZER, \ PTHREAD_COND_INITIALIZER, \ PTHREAD_COND_INITIALIZER, \ 0, 0 \ } #endif _begin_decls Locker *locker_create_mutex(pthread_mutex_t *mutex); Locker *locker_create_rwlock(pthread_rwlock_t *rwlock); Locker *locker_create_debug_mutex(pthread_mutex_t *mutex); Locker *locker_create_debug_rwlock(pthread_rwlock_t *rwlock); Locker *locker_create(void *lock, lockerf_t *tryrdlock, lockerf_t *rdlock, lockerf_t *trywrlock, lockerf_t *wrlock, lockerf_t *unlock); void locker_release(Locker *locker); void *locker_destroy(Locker **locker); int locker_tryrdlock(Locker *locker); int locker_rdlock(Locker *locker); int locker_trywrlock(Locker *locker); int locker_wrlock(Locker *locker); int locker_unlock(Locker *locker); #ifndef HAVE_PTHREAD_RWLOCK int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared); int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared); #endif _end_decls /* Don't look below here - optimisations only */ struct Locker { void *lock; lockerf_t *tryrdlock; lockerf_t *rdlock; lockerf_t *trywrlock; lockerf_t *wrlock; lockerf_t *unlock; }; #define locker_tryrdlock(locker) ((locker) ? (locker)->tryrdlock((locker)->lock) : 0) #define locker_rdlock(locker) ((locker) ? (locker)->rdlock((locker)->lock) : 0) #define locker_trywrlock(locker) ((locker) ? (locker)->trywrlock((locker)->lock) : 0) #define locker_wrlock(locker) ((locker) ? (locker)->wrlock((locker)->lock) : 0) #define locker_unlock(locker) ((locker) ? (locker)->unlock((locker)->lock) : 0) #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/macros.mk000066400000000000000000000176021140522741300163720ustar00rootroot00000000000000# # libslack - http://libslack.org/ # # Copyright (C) 1999-2010 raf # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/copyleft/gpl.html # # 20100612 raf # Uncomment these to override the defines in daemon.h and prog.h # # SLACK_DEFINES += -DPATH_SEP=\'/\' # SLACK_DEFINES += -DROOT_DIR=\"/\" # SLACK_DEFINES += -DETC_DIR=\"/etc\" # SLACK_DEFINES += -DROOT_PID_DIR=\"/var/run\" # SLACK_DEFINES += -DUSER_PID_DIR=\"/tmp\" # Uncomment this if your system doesn't have GNU getopt_long() # # GETOPT := getopt # Uncomment this if your system does have getopt_long() # SLACK_CLIENT_CFLAGS += -DHAVE_GETOPT_LONG=1 # Uncomment this if your system doesn't have a good snprintf() # # SNPRINTF := snprintf # Uncomment this if your system does have a good snprintf() # SLACK_CLIENT_CFLAGS += -DHAVE_SNPRINTF=1 # Uncomment this if your system doesn't have vsscanf() # # VSSCANF := vsscanf # Uncomment this if your system does have vsscanf() # SLACK_CLIENT_CFLAGS += -DHAVE_VSSCANF=1 # Uncomment these if your system has POSIX threads reader/writer locks. # SLACK_CLIENT_CFLAGS += -DHAVE_PTHREAD_RWLOCK=1 # Uncomment this to prevent an extra fork on SVR4 which prevents # the process from ever gaining a controlling terminal. If this is # uncommented, the O_NOCTTY flag should be passed to all calls to # open(2) made by the process (as is required on BSD anyway) # # SLACK_DEFINES += -DNO_EXTRA_SVR4_FORK=1 # Uncomment this to override the default value of 8192 bytes # as the size for message buffers # # SLACK_DEFINES += -DMSG_SIZE=8192 # Uncomment this to override the default value of 32 as the maximum # number of dimensions of an allocated space # # SLACK_DEFINES += -DMEM_MAX_DIM=32 # Uncomment these if necessary (Needed on MacOSX 10.4) # # SLACK_DEFINES += -DNO_POSIX_C_SOURCE=1 # SLACK_TEST_DEFINES += -DNO_POSIX_C_SOURCE=1 # Uncomment these on MacOSX to create universal binaries # # SLACK_CCFLAGS += -arch x86_64 -arch i386 -arch ppc # -arch ppc64 # SLACK_TEST_LDFLAGS += -arch x86_64 -arch i386 -arch ppc # -arch ppc64 # Uncomment these on 64-bit OpenSolaris or Solaris to compile for 64-bit # # SLACK_CCFLAGS += -m64 # SLACK_TEST_CCFLAGS += -m64 # SLACK_TEST_LDFLAGS += -m64 # SLACK_CLIENT_LDFLAGS += -m64 # Uncomment these if your system has the "long long int" type. # SLACK_CCFLAGS += -Wno-long-long SLACK_TEST_CCFLAGS += -Wno-long-long # Uncomment this if your gcc supports it # SLACK_TEST_CCFLAGS += -Wno-overlength-strings # Uncomment this to exclude compilation of the debug locker functions. # These functions shamefully assume that pthread_self() can be cast into # an unsigned long. If this is not true on on your system, uncomment this # define or change the code. # # SLACK_DEFINES += -DNO_DEBUG_LOCKERS=1 SLACK_NAME := slack SLACK_VERSION := 0.6 SLACK_URL := http://libslack.org/ SLACK_ID := lib$(SLACK_NAME)-$(SLACK_VERSION) SLACK_DIST := $(SLACK_ID).tar.gz SLACK_HTML_DIST := $(SLACK_ID).html.tar.gz SLACK_TARGET := $(SLACK_SRCDIR)/lib$(SLACK_NAME).a SLACK_INSTALL := $(SLACK_ID).a SLACK_INSTALL_LINK := lib$(SLACK_NAME).a SLACK_CONFIG := $(SLACK_SRCDIR)/lib$(SLACK_NAME)-config SLACK_MODULES := agent coproc daemon err fio $(GETOPT) hsort lim link list locker map mem msg net prog prop pseudo sig $(SNPRINTF) str $(VSSCANF) SLACK_HEADERS := std lib hdr socks SLACK_LIB_PODS := libslack SLACK_APP_PODS := libslack-config SLACK_HTMLDIR := $(DATA_INSDIR)/lib$(SLACK_NAME)/doc SLACK_CFILES := $(patsubst %, $(SLACK_SRCDIR)/%.c, $(SLACK_MODULES)) SLACK_OFILES := $(patsubst %, $(SLACK_SRCDIR)/%.o, $(SLACK_MODULES)) SLACK_HFILES := $(patsubst %, $(SLACK_SRCDIR)/%.h, $(SLACK_MODULES) $(SLACK_HEADERS)) SLACK_LIB_PODNAMES := $(patsubst %.pod, %, $(SLACK_LIB_PODS)) $(SLACK_MODULES) SLACK_LIB_MANFILES := $(patsubst %, $(SLACK_SRCDIR)/%.$(LIB_MANSECT), $(SLACK_LIB_PODNAMES)) SLACK_LIB_HTMLFILES := $(sort $(patsubst %, $(SLACK_SRCDIR)/%.$(LIB_MANSECT).html, $(SLACK_LIB_PODNAMES) getopt snprintf vsscanf)) SLACK_APP_PODNAMES := $(patsubst %.pod, %, $(SLACK_APP_PODS)) SLACK_APP_MANFILES := $(patsubst %, $(SLACK_SRCDIR)/%.$(APP_MANSECT), $(SLACK_APP_PODNAMES)) SLACK_APP_HTMLFILES := $(patsubst %, $(SLACK_SRCDIR)/%.$(APP_MANSECT).html, $(SLACK_APP_PODNAMES)) ifeq ($(MAN_GZIP), 1) SLACK_LIB_MANFILES := $(patsubst %, %.gz, $(SLACK_LIB_MANFILES)) SLACK_APP_MANFILES := $(patsubst %, %.gz, $(SLACK_APP_MANFILES)) MAN_SUFFIX := .gz endif SLACK_SWIGFILE := $(SLACK_SRCDIR)/slack.swig SLACK_TESTDIR := $(SLACK_SRCDIR)/test SLACK_TESTS := $(patsubst %, $(SLACK_TESTDIR)/%, $(SLACK_MODULES)) SLACK_INCLINK := $(SLACK_SRCDIR)/$(SLACK_NAME) ifeq ($(FINAL_PREFIX),) FINAL_PREFIX := $(PREFIX) endif TAG_FILES += $(SLACK_HFILES) $(SLACK_CFILES) DEPEND_HFILES += $(SLACK_HFILES) DEPEND_CFILES += $(SLACK_CFILES) ifeq ($(SLACK_SRCDIR), .) SLACK_MAIN := 1 endif ALL_TARGETS += slack READY_TARGETS += ready-slack TEST_TARGETS += test-slack MAN_TARGETS += man-slack HTML_TARGETS += html-slack ifeq ($(SLACK_MAIN), 1) INSTALL_TARGETS += install-slack UNINSTALL_TARGETS += uninstall-slack endif DIST_TARGETS += dist-slack RPM_TARGETS += rpm-slack DEB_TARGETS += SOL_TARGETS += sol-slack OBSD_TARGETS += obsd-slack FBSD_TARGETS += fbsd-slack NBSD_TARGETS += nbsd-slack OSX_TARGETS += osx-slack CLEAN_FILES += $(SLACK_OFILES) $(SLACK_CONFIG) $(SLACK_LIB_MANFILES) $(SLACK_APP_MANFILES) $(SLACK_LIB_HTMLFILES) $(SLACK_APP_HTMLFILES) $(SLACK_SRCDIR)/pod2html-* $(SLACK_SWIGFILE) CLOBBER_FILES += $(SLACK_TARGET) $(SLACK_SRCDIR)/tags $(SLACK_TESTDIR) $(SLACK_INCLINK) SLACK_RPM_FILES += $(LIB_INSDIR)/$(SLACK_INSTALL_LINK) SLACK_RPM_FILES += $(LIB_INSDIR)/$(SLACK_INSTALL) SLACK_RPM_FILES += $(APP_INSDIR)/$(notdir $(SLACK_CONFIG)) SLACK_RPM_FILES += $(patsubst %, $(HDR_INSDIR)/$(SLACK_NAME)/%, $(notdir $(SLACK_HFILES))) SLACK_RPM_DOCFILES += $(patsubst %, $(APP_MANDIR)/%, $(notdir $(SLACK_APP_MANFILES))) SLACK_RPM_DOCFILES += $(patsubst %, $(LIB_MANDIR)/%, $(notdir $(SLACK_LIB_MANFILES))) SLACK_RPM_DOCFILES += $(foreach MODULE, $(SLACK_MODULES), $(patsubst %, $(LIB_MANDIR)/%.$(LIB_MANSECT)$(MAN_SUFFIX), $(shell perl -n -e 'print $$1, "\n" if /^=item C<(?:const )?\w+[\s*]*(\w+)\(.*\)>$$/ or /^=item C< \#define (\w+)\(.*\)>$$/' "$(SLACK_SRCDIR)/$(MODULE).c"))) SLACK_SOL := RAFOslk SLACK_CPPFLAGS += $(SLACK_DEFINES) $(patsubst %, -I%, $(SLACK_INCDIRS)) SLACK_CCFLAGS += $(CCFLAGS) SLACK_CFLAGS += $(SLACK_CPPFLAGS) $(SLACK_CCFLAGS) SLACK_TEST_CPPFLAGS += $(SLACK_TEST_DEFINES) $(patsubst %, -I%, $(SLACK_INCDIRS)) SLACK_TEST_CCFLAGS += -Wall -pedantic SLACK_TEST_CFLAGS += $(SLACK_TEST_CPPFLAGS) $(SLACK_TEST_CCFLAGS) # SLACK_TEST_LDFLAGS += -pthread SLACK_TEST_LIBS += $(SLACK_NAME) SLACK_TEST_LIBS += pthread SLACK_TEST_LIBS += util # SLACK_CLIENT_LDFLAGS += -pthread SLACK_CLIENT_LIBS += $(SLACK_NAME) SLACK_CLIENT_LIBS += pthread SLACK_CLIENT_LIBS += util # Uncomment these on Solaris for sockets (used by the daemon and net modules) # # SLACK_TEST_LIBS += xnet # SLACK_TEST_LIBS += socket # SLACK_TEST_LIBS += nsl # SLACK_CLIENT_LIBS += xnet # SLACK_CLIENT_LIBS += socket # SLACK_CLIENT_LIBS += nsl # Uncomment these on Solaris if you are using Sun's C compiler # # SLACK_CLIENT_LIBS += m # SLACK_TEST_LIBS += m SLACK_TEST_LDFLAGS += $(patsubst %, -L%, $(SLACK_LIBDIRS)) $(patsubst %, -l%, $(SLACK_TEST_LIBS)) SLACK_CLIENT_LDFLAGS += $(patsubst %, -l%, $(SLACK_CLIENT_LIBS)) SLACK_SUBTARGETS := SLACK_SUBDIRS := daemon-0.6.4/libslack/map.c000066400000000000000000002203701140522741300154740ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - map module =head1 SYNOPSIS #include #include typedef struct Map Map; typedef struct Mapper Mapper; typedef struct Mapping Mapping; typedef list_release_t map_release_t; typedef list_copy_t map_copy_t; typedef list_cmp_t map_cmp_t; typedef size_t map_hash_t(size_t table_size, const void *key); typedef void map_action_t(void *key, void *item, void *data); Map *map_create(map_release_t *destroy); Map *map_create_sized(size_t size, map_release_t *destroy); Map *map_create_with_hash(map_hash_t *hash, map_release_t *destroy); Map *map_create_sized_with_hash(size_t size, map_hash_t *hash, map_release_t *destroy); Map *map_create_with_locker(Locker *locker, map_release_t *destroy); Map *map_create_with_locker_sized(Locker *locker, size_t size, map_release_t *destroy); Map *map_create_with_locker_with_hash(Locker *locker, map_hash_t *hash, map_release_t *destroy); Map *map_create_with_locker_sized_with_hash(Locker *locker, size_t size, map_hash_t *hash, map_release_t *destroy); Map *map_create_generic(map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); Map *map_create_generic_sized(size_t size, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); Map *map_create_generic_with_locker(Locker *locker, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); Map *map_create_generic_with_locker_sized(Locker *locker, size_t size, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); int map_rdlock(const Map *map); int map_wrlock(const Map *map); int map_unlock(const Map *map); void map_release(Map *map); void *map_destroy(Map **map); int map_own(Map *map, map_release_t *destroy); int map_own_unlocked(Map *map, map_release_t *destroy); map_release_t *map_disown(Map *map); map_release_t *map_disown_unlocked(Map *map); int map_add(Map *map, const void *key, void *value); int map_add_unlocked(Map *map, const void *key, void *value); int map_put(Map *map, const void *key, void *value); int map_put_unlocked(Map *map, const void *key, void *value); int map_insert(Map *map, const void *key, void *value, int replace); int map_insert_unlocked(Map *map, const void *key, void *value, int replace); int map_remove(Map *map, const void *key); int map_remove_unlocked(Map *map, const void *key); void *map_get(Map *map, const void *key); void *map_get_unlocked(const Map *map, const void *key); Mapper *mapper_create(Map *map); Mapper *mapper_create_rdlocked(Map *map); Mapper *mapper_create_wrlocked(Map *map); Mapper *mapper_create_unlocked(Map *map); void mapper_release(Mapper *mapper); void mapper_release_unlocked(Mapper *mapper); void *mapper_destroy(Mapper **mapper); void *mapper_destroy_unlocked(Mapper **mapper); int mapper_has_next(Mapper *mapper); void *mapper_next(Mapper *mapper); const Mapping *mapper_next_mapping(Mapper *mapper); void mapper_remove(Mapper *mapper); int map_has_next(Map *map); void map_break(Map *map); void *map_next(Map *map); const Mapping *map_next_mapping(Map *map); void map_remove_current(Map *map); const void *mapping_key(const Mapping *mapping); const void *mapping_value(const Mapping *mapping); List *map_keys(Map *map); List *map_keys_unlocked(Map *map); List *map_keys_with_locker(Locker *locker, Map *map); List *map_keys_with_locker_unlocked(Locker *locker, Map *map); List *map_values(Map *map); List *map_values_unlocked(Map *map); List *map_values_with_locker(Locker *locker, Map *map); List *map_values_with_locker_unlocked(Locker *locker, Map *map); void map_apply(Map *map, map_action_t *action, void *data); void map_apply_rdlocked(Map *map, map_action_t *action, void *data); void map_apply_wrlocked(Map *map, map_action_t *action, void *data); void map_apply_unlocked(Map *map, map_action_t *action, void *data); ssize_t map_size(Map *map); ssize_t map_size_unlocked(const Map *map); =head1 DESCRIPTION This module provides functions for manipulating and iterating over a set of mappings from one object to another object, also known as hashes or associative arrays. Is may own their items. Is created with a non-C destroy function use that function to destroy an item when it is removed from the map and to destroy each item when the map itself it destroyed. Is are hash tables with 11 buckets by default. They grow when necessary, approximately doubling in size each time up to a maximum size of 26,214,401 buckets. =over 4 =cut */ #ifndef _BSD_SOURCE #define _BSD_SOURCE /* For snprintf() on OpenBSD-4.7 */ #endif #include "config.h" #include "std.h" #include "map.h" #include "mem.h" #include "err.h" #include "locker.h" struct Map { size_t size; /* number of buckets */ size_t items; /* number of items */ List **chain; /* array of hash buckets */ map_hash_t *hash; /* hash function */ map_copy_t *copy; /* key copy function */ map_cmp_t *cmp; /* key comparison function */ map_release_t *key_destroy; /* destructor function for keys */ map_release_t *value_destroy; /* destructor function for items */ Mapper *mapper; /* built-in iterator */ Locker *locker; /* locking strategy for this object */ }; struct Mapping { void *key; /* a map key */ void *value; /* a map value */ map_release_t *key_destroy; /* destructor function for key */ map_release_t *value_destroy; /* destructor function for value */ }; struct Mapper { Map *map; /* the map being iterated over */ ssize_t chain_index; /* the index of the chain of the current item */ ssize_t item_index; /* the index of the current item */ ssize_t next_chain_index; /* the index of the chain of the next item */ ssize_t next_item_index; /* the index of the next item */ }; #ifndef TEST /* Increasing sequence of valid (i.e. prime) table sizes to choose from. */ static const size_t table_sizes[] = { 11, 23, 47, 101, 199, 401, 797, 1601, 3203, 6397, 12799, 25601, 51199, 102397, 204803, 409597, 819187, 1638431, 3276799, 6553621, 13107197, 26214401 }; static const size_t num_table_sizes = sizeof(table_sizes) / sizeof(table_sizes[0]); /* Average bucket length threshold that must be reached before a map grows */ static const double table_resize_factor = 2.0; #if 0 /* C JPW hash function. Returns a hash value (in the range 0..size-1) for C. */ static size_t hash(size_t size, const void *key) { unsigned char *k = key; size_t g, h = 0; while (*k) if ((g = (h <<= 4, h += *k++) & 0xf0000000)) h ^= (g >> 24) ^ g; return h % size; } #endif /* C Hash function from The Practice of Programming by Kernighan and Pike (p57). Returns a hash value (in the range 0..size-1) for C. */ static size_t hash(size_t size, const void *key) { const unsigned char *k = key; size_t h = 0; while (*k) h *= 31, h += *k++; return h % size; } /* C Creates a new mapping from C to C. C is the destructor function for C. On success, returns the new mapping. On error, returns C with C set appropriately. */ static Mapping *mapping_create(void *key, void *value, map_release_t *key_destroy, map_release_t *value_destroy) { Mapping *mapping; if (!(mapping = mem_new(Mapping))) /* XXX decouple */ return NULL; mapping->key = key; mapping->value = value; mapping->key_destroy = key_destroy; mapping->value_destroy = value_destroy; return mapping; } /* C Releases (deallocates) C, destroying its value if necessary. */ static void mapping_release(Mapping *mapping) { if (!mapping) return; if (mapping->key_destroy) mapping->key_destroy(mapping->key); if (mapping->value_destroy) mapping->value_destroy(mapping->value); mem_release(mapping); } /* =item C Creates a small I with string keys and C as its item destructor. It is the caller's responsibility to deallocate the new map with I or I. On success, returns the new map. On error, returns C with C set appropriately. =cut */ Map *map_create(map_release_t *destroy) { return map_create_sized_with_hash(table_sizes[0], (map_hash_t *)hash, destroy); } /* =item C Equivalent to I except that the initial number of buckets is approximately C. The actual size will be the first prime greater than or equal to C in a prebuilt sequence of primes between 11 and 26,214,401 that double at each step. =cut */ Map *map_create_sized(size_t size, map_release_t *destroy) { return map_create_sized_with_hash(size, (map_hash_t *)hash, destroy); } /* =item C Equivalent to I except that C is used as the hash function. The arguments to C are a I specifying the number of buckets and a I specifying the key to hash. It must return a I between zero and the table size - 1. =cut */ Map *map_create_with_hash(map_hash_t *hash, map_release_t *destroy) { return map_create_sized_with_hash(table_sizes[0], hash, destroy); } /* =item C Equivalent to I except that C is used as the hash function. The arguments to C are a I specifying the number of buckets and a I specifying the key to hash. It must return a I between zero and the table size - 1. =cut */ Map *map_create_sized_with_hash(size_t size, map_hash_t *hash, map_release_t *destroy) { return map_create_generic_sized(size, (map_copy_t *)mem_strdup, (map_cmp_t *)strcmp, hash, (map_release_t *)free, destroy); } /* =item C Equivalent to I except that multiple threads accessing the new map will be synchronised by C. =cut */ Map *map_create_with_locker(Locker *locker, map_release_t *destroy) { return map_create_with_locker_sized_with_hash(locker, table_sizes[0], (map_hash_t *)hash, destroy); } /* =item C Equivalent to I except that multiple threads accessing the new map will be synchronised by C. =cut */ Map *map_create_with_locker_sized(Locker *locker, size_t size, map_release_t *destroy) { return map_create_with_locker_sized_with_hash(locker, size, (map_hash_t *)hash, destroy); } /* =item C Equivalent to I except that multiple threads accessing the new map will be synchronised by C. =cut */ Map *map_create_with_locker_with_hash(Locker *locker, map_hash_t *hash, map_release_t *destroy) { return map_create_with_locker_sized_with_hash(locker, table_sizes[0], hash, destroy); } /* =item C Equivalent to I except that multiple threads accessing the new map will be synchronised by C. =cut */ Map *map_create_with_locker_sized_with_hash(Locker *locker, size_t size, map_hash_t *hash, map_release_t *destroy) { return map_create_generic_with_locker_sized(locker, size, (map_copy_t *)mem_strdup, (map_cmp_t *)strcmp, hash, (map_release_t *)free, destroy); } /* =item C Equivalent to I except that the mapping keys can be of any type. C is used to copy mapping keys. The argument to C is the key to be copied. It must return a copy of its argument. C is used to compare mapping keys. The arguments to C are two keys to be compared. It must return < 0 if the first compares less than the second, 0 if they compare equal and > 0 if the first compares greater than the second. C is the hash function. The arguments to C are a I specifying the number of buckets and a I specifying the key to hash. It must return a I between zero and the table size - 1. C is the destructor for mapping keys. C is the destructor for mapping values. On success, returns the new map. On error, returns C with C set appropriately. =cut */ Map *map_create_generic(map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy) { return map_create_generic_with_locker_sized(NULL, table_sizes[0], copy, cmp, hash, key_destroy, value_destroy); } /* =item C Equivalent to I except that the initial number of buckets is approximately C. The actual size will be the first prime greater than or equal to C in a prebuilt sequence of primes between 11 and 26,214,401 that double at each step. =cut */ Map *map_create_generic_sized(size_t size, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy) { return map_create_generic_with_locker_sized(NULL, size, copy, cmp, hash, key_destroy, value_destroy); } /* =item C Equivalent to I except that multiple threads accessing the new map will be synchronised by C. =cut */ Map *map_create_generic_with_locker(Locker *locker, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy) { return map_create_generic_with_locker_sized(locker, table_sizes[0], copy, cmp, hash, key_destroy, value_destroy); } /* =item C Equivalent to I except that multiple threads accessing the new map will be synchronised by C. =cut */ Map *map_create_generic_with_locker_sized(Locker *locker, size_t size, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy) { Map *map; size_t i; for (i = 0; i < num_table_sizes; ++i) { if (table_sizes[i] >= size) { size = table_sizes[i]; break; } } if (i == num_table_sizes) return set_errnull(EINVAL); if (!(map = mem_new(Map))) /* XXX decouple */ return NULL; if (!(map->chain = mem_create(size, List *))) { mem_release(map); return NULL; } map->size = size; map->items = 0; memset(map->chain, 0, map->size * sizeof(List *)); map->hash = hash; map->copy = copy; map->cmp = cmp; map->key_destroy = key_destroy; map->value_destroy = value_destroy; map->mapper = NULL; map->locker = locker; return map; } /* =item C Claims a read lock on C (if C was created with a I). This is needed when multiple read only I module functions need to be called atomically. It is the client's responsibility to call I after the atomic operation. The only functions that may be called on C between calls to I and I are any read only I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ #define map_rdlock(map) ((map) ? locker_rdlock((map)->locker) : EINVAL) #define map_wrlock(map) ((map) ? locker_wrlock((map)->locker) : EINVAL) #define map_unlock(map) ((map) ? locker_unlock((map)->locker) : EINVAL) int (map_rdlock)(const Map *map) { return map_rdlock(map); } /* =item C Claims a write lock on C (if C was created with a I). This is needed when multiple read/write I module functions need to be called atomically. It is the client's responsibility to subsequently call I. The only functions that may be called on C between calls to I and I are any I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ int (map_wrlock)(const Map *map) { return map_wrlock(map); } /* =item C Unlocks a read or write lock on C obtained with I or I (if C was created with a C). On success, returns C<0>. On error, returns an error code. =cut */ int (map_unlock)(const Map *map) { return map_unlock(map); } /* =item C Releases (deallocates) C, destroying its items if necessary. On error, sets C appropriately. =cut */ void map_release(Map *map) { size_t i; if (!map) return; for (i = 0; i < map->size; ++i) list_release(map->chain[i]); mem_release(map->chain); mem_release(map); } /* =item C Destroys (deallocates and sets to C) C<*map>. Returns C. B maps shared by multiple threads must not be destroyed until after all threads have finished with it. =cut */ void *map_destroy(Map **map) { if (map && *map) { map_release(*map); *map = NULL; } return NULL; } /* =item C Causes C to take ownership of its items. The items will be destroyed using C when their mappings are removed from C or when C is destroyed. On success, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int map_own(Map *map, map_release_t *destroy) { int ret; int err; if (!map || !destroy) return set_errno(EINVAL); if ((err = map_wrlock(map))) return set_errno(err); ret = map_own_unlocked(map, destroy); if ((err = map_unlock(map))) return set_errno(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ int map_own_unlocked(Map *map, map_release_t *destroy) { ssize_t length; size_t c, i; if (!map || !destroy) return set_errno(EINVAL); if (destroy == map->value_destroy) return 0; map->value_destroy = destroy; for (c = 0; c < map->size; ++c) { List *chain = map->chain[c]; if (!chain) continue; if ((length = list_length_unlocked(chain)) == -1) return -1; for (i = 0; i < length; ++i) { Mapping *mapping = (Mapping *)list_item_unlocked(chain, i); mapping->value_destroy = destroy; } } return 0; } /* =item C Causes C to relinquish ownership of its items. The items will not be destroyed when their mappings are removed from C or when C is destroyed. On success, returns the previous destroy function, if any. On error, returns C with C set appropriately. =cut */ map_release_t *map_disown(Map *map) { map_release_t *ret; int err; if (!map) return (map_release_t *)set_errnullf(EINVAL); if ((err = map_wrlock(map))) return (map_release_t *)set_errnullf(err); ret = map_disown_unlocked(map); if ((err = map_unlock(map))) return (map_release_t *)set_errnullf(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ map_release_t *map_disown_unlocked(Map *map) { ssize_t length; size_t c, i; map_release_t *destroy; if (!map) return (map_release_t *)set_errnullf(EINVAL); if (!map->value_destroy) return NULL; destroy = map->value_destroy; map->value_destroy = NULL; for (c = 0; c < map->size; ++c) { List *chain = map->chain[c]; if (!chain) continue; if ((length = list_length_unlocked(chain)) == -1) return NULL; for (i = 0; i < length; ++i) { Mapping *mapping = (Mapping *)list_item_unlocked(chain, i); mapping->value_destroy = NULL; } } return destroy; } /* C Resizes C to use the next prime in a built in sequence of primes between 11 and 26,214,401 that is greater than the current size. On success, returns C<0>. On error, returns C<-1> with C set appropriately. */ static int map_resize(Map *map) { size_t size = 0; size_t i; Mapper *mapper; Map *new_map; if (!map) return set_errno(EINVAL); for (i = 1; i < num_table_sizes; ++i) { if (table_sizes[i] > map->size) { size = table_sizes[i]; break; } } if (i == num_table_sizes || size == 0) return set_errno(EINVAL); if (!(new_map = map_create_generic_sized(size, map->copy, map->cmp, map->hash, map->key_destroy, map->value_destroy))) return -1; if (!(mapper = mapper_create_unlocked(map))) { map_release(new_map); return -1; } while (mapper_has_next(mapper) == 1) { const Mapping *mapping = mapper_next_mapping(mapper); if (map_add_unlocked(new_map, mapping->key, mapping->value) == -1) { mapper_release_unlocked(mapper); map_release(new_map); return -1; } } mapper_release_unlocked(mapper); errno = 0; if (map_disown_unlocked(map) == NULL && errno) { map_release(new_map); return -1; } for (i = 0; i < map->size; ++i) list_release(map->chain[i]); mem_release(map->chain); map->size = new_map->size; map->items = new_map->items; map->chain = new_map->chain; map->value_destroy = new_map->value_destroy; mem_release(new_map); return 0; } /* =item C Adds the C<(key, value)> mapping to C if C is not already present. Note that C is copied but C is not. On success, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int map_add(Map *map, const void *key, void *value) { return map_insert(map, key, value, 0); } /* =item C Equivalent to I except that C is not write locked. =cut */ int map_add_unlocked(Map *map, const void *key, void *value) { return map_insert_unlocked(map, key, value, 0); } /* =item C Adds the C<(key, value)> mapping to C, replacing any existing C<(key, value)> mapping. Note that C is copied but C is not. On success, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int map_put(Map *map, const void *key, void *value) { return map_insert(map, key, value, 1); } /* =item C Equivalent to I except that C is not write locked. =cut */ int map_put_unlocked(Map *map, const void *key, void *value) { return map_insert_unlocked(map, key, value, 1); } /* =item C Adds the C<(key, value)> mapping to C, replacing any existing C<(key, value)> mapping if C is non-zero. Note that C is copied but C is not. On success, returns C<0>. On error, or if C is already present and C is zero, returns C<-1> with C set appropriately. =cut */ int map_insert(Map *map, const void *key, void *value, int replace) { int ret; int err; if (!map || !key) return set_errno(EINVAL); if ((err = map_wrlock(map))) return set_errno(err); ret = map_insert_unlocked(map, key, value, replace); if ((err = map_unlock(map))) return set_errno(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ int map_insert_unlocked(Map *map, const void *key, void *value, int replace) { Mapping *mapping; List *chain; ssize_t length; size_t h, c; if (!map || !key) return set_errno(EINVAL); if ((double)map->items / (double)map->size >= (double)table_resize_factor) if (map_resize(map) == -1) return -1; if ((h = map->hash(map->size, key)) >= map->size) return set_errno(EINVAL); if (!map->chain[h] && !(map->chain[h] = list_create((map_release_t *)mapping_release))) return -1; chain = map->chain[h]; if ((length = list_length_unlocked(chain)) == -1) return -1; for (c = 0; c < length; ++c) { mapping = (Mapping *)list_item_unlocked(chain, c); if (!map->cmp(mapping->key, key)) { if (replace && list_remove_unlocked(chain, c)) break; return -1; } } if (!(mapping = mapping_create(map->copy(key), value, map->key_destroy, map->value_destroy))) return -1; if (!list_append_unlocked(chain, mapping)) { mapping_release(mapping); return -1; } ++map->items; return 0; } /* =item C Removes C<(key, value)> mapping from C if it is present. If C was created with a destroy function, then the value will be destroyed. On success, returns C<0>. On error, returns C<-1> with C set appropriately. =cut */ int map_remove(Map *map, const void *key) { int ret; int err; if (!map || !key) return set_errno(EINVAL); if ((err = map_wrlock(map))) return set_errno(err); ret = map_remove_unlocked(map, key); if ((err = map_unlock(map))) return set_errno(err); return ret; } /* =item C Equivalent to I except that C is not write locked. =cut */ int map_remove_unlocked(Map *map, const void *key) { List *chain; ssize_t length; size_t h, c; if (!map || !key) return set_errno(EINVAL); if ((h = map->hash(map->size, key)) >= map->size) return set_errno(EINVAL); if (!(chain = map->chain[h])) return set_errno(ENOENT); if ((length = list_length_unlocked(chain)) == -1) return -1; for (c = 0; c < length; ++c) { Mapping *mapping = (Mapping *)list_item_unlocked(chain, c); if (!map->cmp(mapping->key, key)) { if (!list_remove_unlocked(chain, c)) return -1; --map->items; return 0; } } return set_errno(ENOENT); } /* =item C Returns the value associated with C in C, or C if there is none. On error, returns C with C set appropriately. =cut */ void *map_get(Map *map, const void *key) { void *ret; int err; if (!map || !key) return set_errnull(EINVAL); if ((err = map_rdlock(map))) return set_errnull(err); ret = map_get_unlocked(map, key); if ((err = map_unlock(map))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not read locked. =cut */ void *map_get_unlocked(const Map *map, const void *key) { List *chain; ssize_t length; size_t h, c; if (!map || !key) return set_errnull(EINVAL); if ((h = map->hash(map->size, key)) >= map->size) return set_errnull(EINVAL); if (!(chain = map->chain[h])) return set_errnull(ENOENT); if ((length = list_length_unlocked(chain)) == -1) return NULL; for (c = 0; c < length; ++c) { Mapping *mapping = (Mapping *)list_item_unlocked(chain, c); if (!map->cmp(mapping->key, key)) return mapping->value; } return set_errnull(ENOENT); } /* =item C Creates an iterator for C. The iterator keeps C write locked until it is released with I or I. Note that the iterator itself is not locked so it must not be shared between threads. On success, returns the iterator. On error, returns C with C set appropriately. =cut */ Mapper *mapper_create(Map *map) { return mapper_create_wrlocked(map); } /* =item C Equivalent to I except that C is read locked rather than write locked. Use this in preference to I when no calls to I will be made during the iteration. =cut */ Mapper *mapper_create_rdlocked(Map *map) { int err; if (!map) return set_errnull(EINVAL); if ((err = map_rdlock(map))) return set_errnull(err); return mapper_create_unlocked(map); } /* =item C Equivalent to I except that this function name makes the fact that C is write locked explicit. =cut */ Mapper *mapper_create_wrlocked(Map *map) { int err; if (!map) return set_errnull(EINVAL); if ((err = map_wrlock(map))) return set_errnull(err); return mapper_create_unlocked(map); } /* =item C Equivalent to I except that C is not write locked. =cut */ Mapper *mapper_create_unlocked(Map *map) { Mapper *mapper; if (!map) return set_errnull(EINVAL); if (!(mapper = mem_new(Mapper))) /* XXX decouple */ return NULL; mapper->map = map; mapper->chain_index = -1; mapper->item_index = -1; mapper->next_chain_index = -1; mapper->next_item_index = -1; return mapper; } /* =item C Releases (deallocates) C and unlocks the associated map. =cut */ void mapper_release(Mapper *mapper) { int err; if (!mapper) return; if ((err = map_unlock(mapper->map))) { set_errno(err); return; } mem_release(mapper); } /* =item C Equivalent to I except that the associated map is not unlocked. =cut */ void mapper_release_unlocked(Mapper *mapper) { if (!mapper) return; mem_release(mapper); } /* =item C Destroys (deallocates and sets to C) C<*mapper> and unlocks the associated map. Returns C. On error, sets C appropriately. =cut */ void *mapper_destroy(Mapper **mapper) { if (mapper && *mapper) { mapper_release(*mapper); *mapper = NULL; } return NULL; } /* =item C Equivalent to I except that the associated map is not unlocked. =cut */ void *mapper_destroy_unlocked(Mapper **mapper) { if (mapper && *mapper) { mapper_release_unlocked(*mapper); *mapper = NULL; } return NULL; } /* =item C Returns whether or not there is another item in the map over which C is iterating. On error, returns C<-1> with C set appropriately. =cut */ int mapper_has_next(Mapper *mapper) { List *chain; ssize_t length; if (!mapper) return set_errno(EINVAL); /* Find the current/first chain */ mapper->next_chain_index = mapper->chain_index; mapper->next_item_index = mapper->item_index; if (mapper->next_chain_index == -1) ++mapper->next_chain_index; while (mapper->next_chain_index < mapper->map->size && !mapper->map->chain[mapper->next_chain_index]) ++mapper->next_chain_index; if (mapper->next_chain_index == mapper->map->size) return 0; chain = mapper->map->chain[mapper->next_chain_index]; /* Find the next item */ if ((length = list_length_unlocked(chain)) == -1) return -1; if (++mapper->next_item_index < length) return 1; do { ++mapper->next_chain_index; while (mapper->next_chain_index < mapper->map->size && !mapper->map->chain[mapper->next_chain_index]) ++mapper->next_chain_index; if (mapper->next_chain_index == mapper->map->size) return 0; chain = mapper->map->chain[mapper->next_chain_index]; if ((length = list_length_unlocked(chain)) == -1) return -1; } while (length == 0); mapper->next_item_index = 0; return 1; } /* =item C Returns the next item in the map over which C is iterating. On error, returns C with C set appropriately. =cut */ void *mapper_next(Mapper *mapper) { if (!mapper) return set_errnull(EINVAL); return mapper_next_mapping(mapper)->value; } /* =item C Returns the next mapping (key, value pair) in the map over which C is iterating. On error, returns C with C set appropriately. =cut */ const Mapping *mapper_next_mapping(Mapper *mapper) { if (!mapper) return set_errnull(EINVAL); mapper->chain_index = mapper->next_chain_index; mapper->item_index = mapper->next_item_index; return (Mapping *)list_item_unlocked(mapper->map->chain[mapper->chain_index], mapper->item_index); } /* =item C Removes the current item in the iteration C. The next item in the iteration is the item following the removed item, if any. This must be called after I. On error, sets C appropriately. =cut */ void mapper_remove(Mapper *mapper) { if (!mapper) { set_errno(EINVAL); return; } if (mapper->item_index == -1) { set_errno(EINVAL); return; } list_remove_unlocked(mapper->map->chain[mapper->chain_index], (size_t)mapper->item_index--); --mapper->map->items; } /* =item C Returns whether or not there is another item in C using an internal iterator. The first time this is called, a new internal I will be created (Note: There can be only one). When there are no more items, returns C<0> and destroys the internal iterator. When it returns C<1>, use I to retrieve the next item. On error, returns C<-1> with C set appropriately. Note: If an iteration using an internal iterator terminates before the end of the map, it is the caller's responsibility to call I. Failure to do so will cause the internal iterator to leak. It will also break the next call to I which will continue where the current iteration stopped rather than starting at the beginning again. I assumes that there is no internal iterator so it is the caller's responsibility to complete the iteration or call I before releasing C with I or I. Note: The internal I does not lock C so this function is not threadsafe. It can only be used with maps created in the current function (to guarantee that no other thread can access it). This practice should be observed even in single threaded applications to avoid breaking iterator semantics (possible with nested function calls). If C is a parameter or a variable declared outside the function, it is best to create an explicit I instead. If this function is used on such maps instead, it is the caller's responsibility to explicitly lock C first with I and explicitly unlock it with I. Do this even if you are writing single threaded code in case your function may one day be used in a multi threaded application. =cut */ int map_has_next(Map *map) { int has; if (!map) return set_errno(EINVAL); if (!map->mapper && !(map->mapper = mapper_create_unlocked(map))) return -1; if ((has = mapper_has_next(map->mapper)) != 1) map_break(map); return has; } /* =item C Unlocks C and destroys its internal iterator. Must be used only when an iteration using an internal iterator has terminated before reaching the end of C. On error, returns C with C set appropriately. =cut */ void map_break(Map *map) { if (!map) { set_errno(EINVAL); return; } mapper_destroy_unlocked(&map->mapper); } /* =item C Returns the next item in C using it's internal iterator. On error, returns C with C set appropriately. =cut */ void *map_next(Map *map) { if (!map || !map->mapper) return set_errnull(EINVAL); return mapper_next(map->mapper); } /* =item C Returns the next mapping (key, value pair) in C using it's internal iterator. On error, returns C<-1> with C set appropriately. =cut */ const Mapping *map_next_mapping(Map *map) { if (!map || !map->mapper) return set_errnull(EINVAL); return mapper_next_mapping(map->mapper); } /* =item C Removes the current item in C using it's internal iterator. The next item in the iteration is the item following the removed item, if any. This must be called after I. =cut */ void map_remove_current(Map *map) { if (!map || !map->mapper) { set_errno(EINVAL); return; } mapper_remove(map->mapper); } /* =item C Returns the key in C. On error, returns C with C set appropriately. =cut */ const void *mapping_key(const Mapping *mapping) { if (!mapping) return set_errnull(EINVAL); return mapping->key; } /* =item C Returns the value in C. On error, returns C with C set appropriately. =cut */ const void *mapping_value(const Mapping *mapping) { if (!mapping) return set_errnull(EINVAL); return mapping->value; } /* =item C Creates and returns a list of all of the keys contained in C. It is the caller's responsibility to deallocate the new list with I or I. The keys in the new list are owned by C so the list returned must not outlive C. On error, returns C with C set appropriately. =cut */ List *map_keys(Map *map) { return map_keys_with_locker(NULL, map); } /* =item C Equivalent to I except that C is not read locked. =cut */ List *map_keys_unlocked(Map *map) { return map_keys_with_locker_unlocked(NULL, map); } /* =item C Equivalent to I except that multiple threads accessing the list returned will be synchronised by C. =cut */ List *map_keys_with_locker(Locker *locker, Map *map) { List *keys; int err; if (!map) return set_errnull(EINVAL); if ((err = map_rdlock(map))) return set_errnull(err); keys = map_keys_with_locker_unlocked(locker, map); if ((err = map_unlock(map))) return set_errnull(err); return keys; } /* =item C Equivalent to I except that C is not read locked. =cut */ List *map_keys_with_locker_unlocked(Locker *locker, Map *map) { Mapper *mapper; List *keys; if (!map) return set_errnull(EINVAL); if (!(keys = list_create_with_locker(locker, NULL))) return NULL; if (!(mapper = mapper_create_unlocked(map))) { list_release(keys); return NULL; } while (mapper_has_next(mapper) == 1) { const Mapping *mapping = mapper_next_mapping(mapper); if (!list_append(keys, mapping->key)) { list_destroy(&keys); break; } } mapper_release_unlocked(mapper); return keys; } /* =item C Creates and returns a list of all of the values contained in C. It is the caller's responsibility to deallocate the new list with I or I. The values in the list are not owned by the list so it must not outlive the owner of the items in C. On error, returns C with C set appropriately. =cut */ List *map_values(Map *map) { return map_values_with_locker(NULL, map); } /* =item C Equivalent to I except that C is not read locked. =cut */ List *map_values_unlocked(Map *map) { return map_values_with_locker_unlocked(NULL, map); } /* =item C Equivalent to I except that multiple threads accessing the list returned will be synchronised by C. =cut */ List *map_values_with_locker(Locker *locker, Map *map) { List *values; int err; if (!map) return set_errnull(EINVAL); if ((err = map_rdlock(map))) return set_errnull(err); values = map_values_with_locker_unlocked(locker, map); if ((err = map_unlock(map))) return set_errnull(err); return values; } /* =item C Equivalent to I except that C is not read locked. =cut */ List *map_values_with_locker_unlocked(Locker *locker, Map *map) { Mapper *mapper; List *values; if (!map) return set_errnull(EINVAL); if (!(values = list_create_with_locker(locker, NULL))) return NULL; if (!(mapper = mapper_create_unlocked(map))) { list_release(values); return NULL; } while (mapper_has_next(mapper) == 1) { const Mapping *mapping = mapper_next_mapping(mapper); if (!list_append(values, mapping->value)) { list_destroy(&values); break; } } mapper_release_unlocked(mapper); return values; } /* =item C Invokes C for each of C's items. The arguments passed to C are the key, the item and C. On error, sets C appropriately. =cut */ void map_apply(Map *map, map_action_t *action, void *data) { map_apply_wrlocked(map, action, data); } /* =item C Equivalent to I except that C is read locked rather than write locked. Use this in preference to I when C and its items will not be modified during the iteration. =cut */ void map_apply_rdlocked(Map *map, map_action_t *action, void *data) { int err; if (!map || !action) { set_errno(EINVAL); return; } if ((err = map_rdlock(map))) { set_errno(err); return; } map_apply_unlocked(map, action, data); if ((err = map_unlock(map))) { set_errno(err); return; } } /* =item C Equivalent to I except that this function name makes the fact that C is write locked explicit. =cut */ void map_apply_wrlocked(Map *map, map_action_t *action, void *data) { int err; if (!map || !action) { set_errno(EINVAL); return; } if ((err = map_wrlock(map))) { set_errno(err); return; } map_apply_unlocked(map, action, data); if ((err = map_unlock(map))) { set_errno(err); return; } } /* =item C Equivalent to I except that C is not write locked. =cut */ void map_apply_unlocked(Map *map, map_action_t *action, void *data) { Mapper *mapper; if (!map || !action) { set_errno(EINVAL); return; } if (!(mapper = mapper_create_unlocked(map))) return; while (mapper_has_next(mapper) == 1) { const Mapping *mapping = mapper_next_mapping(mapper); action(mapping->key, mapping->value, data); } mapper_release_unlocked(mapper); } /* =item C Returns the number of mappings in C. On error, returns C<-1> with C set appropriately. =cut */ ssize_t map_size(Map *map) { size_t size; int err; if (!map) return set_errno(EINVAL); if ((err = map_rdlock(map))) return set_errno(err); size = map->items; if ((err = map_unlock(map))) return set_errno(err); return size; } /* =item C Equivalent to I except that C is not read locked. =cut */ ssize_t map_size_unlocked(const Map *map) { if (!map) return set_errno(EINVAL); return map->items; } /* =back =head1 ERRORS On error, C is set either by an underlying function, or as follows: =over 4 =item C When arguments are C or out of range. =item C When I tries to get or I tries to remove a non-existent mapping. =back =head1 MT-Level MT-Disciplined By default, Is are not MT-Safe because most programs are single threaded and synchronisation doesn't come for free. Even in multi threaded programs, not all Is are necessarily shared between multiple threads. When a I is shared between multiple threads which need to be synchronised, the method of synchronisation must be carefully selected by the client code. There are tradeoffs between concurrency and overhead. The greater the concurrency, the greater the overhead. More locks give greater concurrency but have greater overhead. Readers/Writer locks can give greater concurrency than Mutex locks but have greater overhead. One lock for each I may be required, or one lock for all (or a set of) Is may be more appropriate. Generally, the best synchronisation strategy for a given application can only be determined by testing/benchmarking the written application. It is important to be able to experiment with the synchronisation strategy at this stage of development without pain. To facilitate this, Is can be created with I which takes a I argument. The I specifies a lock and a set of functions for manipulating the lock. Each I can have it's own lock by creating a separate I for each I. Multiple Is can share the same lock by sharing the same I. Only the application developer can determine what is appropriate for each application on a case by case basis. I means that the application developer has a mechanism for specifying the synchronisation requirements to be applied to library code. =head1 EXAMPLES Create a map that doesn't own its items, populate it and then iterate over its values with the internal iterator to print the values: #include #include int main() { Map *map; if (!(map = map_create(NULL))) return EXIT_FAILURE; map_add(map, "abc", "123"); map_add(map, "def", "456"); map_add(map, "ghi", "789"); while (map_has_next(map) == 1) printf("%s\n", (char *)map_next(map)); map_destroy(&map); return EXIT_SUCCESS; } Create a map that does own its items, populate it and then iterate over its items with an external iterator to print its keys and values: #include #include int main() { Map *map; Mapper *mapper; if (!(map = map_create(free))) return EXIT_FAILURE; map_add(map, "abc", strdup("123")); map_add(map, "def", strdup("456")); map_add(map, "ghi", strdup("789")); if (!(mapper = mapper_create(map))) { map_destroy(&map); return EXIT_FAILURE; } while (mapper_has_next(mapper) == 1) { const Mapping *mapping = mapper_next_mapping(mapper); printf("%s -> %s\n", (char *)mapping_key(mapping), (char *)mapping_value(mapping)); } mapper_destroy(&mapper); map_destroy(&map); return EXIT_SUCCESS; } The same but with an apply function: #include #include void action(void *key, void *item, void *data) { printf("%s -> %s\n", (char *)key, (char *)item); } int main() { Map *map; if (!(map = map_create(free))) return EXIT_FAILURE; map_add(map, "abc", strdup("123")); map_add(map, "def", strdup("456")); map_add(map, "ghi", strdup("789")); map_apply(map, action, NULL); map_destroy(&map); return EXIT_SUCCESS; } The same but with integer keys: #include #include static void *int_copy(const void *key) { return (void *)key; } static int int_cmp(const void *a, const void *b) { return (int)a - (int)b; } static size_t int_hash(size_t size, const void *key) { return (int)key % size; } void action(void *key, void *item, void *data) { printf("%d -> %s\n", (int)key, (char *)item); } int main(int ac, char **av) { Map *map; if (!(map = map_create_generic(int_copy, int_cmp, int_hash, NULL, free))) return EXIT_FAILURE; map_add(map, (void *)123, strdup("abc")); map_add(map, (void *)456, strdup("def")); map_add(map, (void *)789, strdup("ghi")); map_apply(map, action, NULL); map_destroy(&map); return EXIT_SUCCESS; } =head1 CAVEAT A C pointer can't be a key. Neither can zero when using integers as keys. If you use an internal iterator in a loop that terminates before the end of the map, and fail to call I, the internal iterator will leak. =head1 BUGS Uses I. The type of memory used and the allocation strategy need to be decoupled from this code. =head1 SEE ALSO I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST #ifndef HAVE_SNPRINTF #include #endif #if 0 static void map_print(const char *name, Map *map) { size_t i, j; if (!map) { printf("%s = nil\n", name); return; } printf("%s =\n{\n", name); for (i = 0; i < map->size; ++i) { if (map->chain[i]) { List *chain = map->chain[i]; ssize_t length = list_length(chain); if (length == -1) printf(" !!! length == -1 !!!\n"); for (j = 0; j < length; ++j) { Mapping *mapping = (Mapping *)list_item(chain, j); printf(" [%d/%d] \"%s\" -> \"%s\"\n", i, j, (char *)mapping->key, (char *)mapping->value); } } } printf("}\n"); } #endif static void map_histogram(const char *name, Map *map) { size_t i; int *histogram; if (!map) { printf("%s = nil\n", name); return; } if (!(histogram = mem_create(map->items, int))) { printf("Failed to allocate histogram for map %s\n", name); return; } memset(histogram, 0, map->items * sizeof(int)); for (i = 0; i < map->size; ++i) { size_t length = list_length(map->chain[i]); if (length == -1) { printf(" length[%d] = -1\n", (int)i); continue; } ++histogram[length]; } printf("\nhistogram %s =\n{\n", name); for (i = 0; i < map->items; ++i) if (histogram[i]) printf(" %d chain%s of length %d\n", histogram[i], (histogram[i] == 1) ? "" : "s", (int)i); printf("}\n"); } static void test_hash(void) { FILE *words = fopen("/usr/dict/words", "r"); char word[BUFSIZ]; Map *map; size_t c; size_t min = 0xffffffff; size_t max = 0x00000000; int sum = 0; if (!words) { printf("Failed to open /usr/dict/words\n"); exit(EXIT_FAILURE); } if (!(map = map_create(free))) { printf("Failed to create map\n"); exit(EXIT_FAILURE); } while (fgets(word, BUFSIZ, words)) { char *eow = strchr(word, '\n'); if (eow) *eow = nul; map_add(map, word, mem_strdup(word)); } fclose(words); printf("%d entries into %d buckets:\n\n", (int)map->items, (int)map->size); for (c = 0; c < map->size; ++c) { size_t length; if (!map->chain[c]) continue; if ((length = list_length(map->chain[c])) == -1) { printf(" length[%d] == -1\n", (int)c); continue; } if (length > max) max = length; if (length < min) min = length; sum += length; } printf("avg = %g\n", (double)sum / (double)map->size); printf("min = %d\n", (int)min); printf("max = %d\n", (int)max); map_histogram("dict", map); map_release(map); exit(EXIT_SUCCESS); } static int sort_cmp(const char **a, const char **b) { return strcmp(*a, *b); } static void test_action(char *key, char *value, char *cat) { size_t len = strlen(cat); snprintf(cat + len, BUFSIZ, "%s%s=%s", (len) ? ", " : "", key, value); } typedef struct Point Point; struct Point { int x; int y; }; static Point *point_create(int x, int y) { Point *point = mem_create(1, Point); if (!point) return NULL; point->x = x; point->y = y; return point; } static Point *point_copy(Point *point) { return point_create(point->x, point->y); } static int point_cmp(Point *a, Point *b) { if (a->x > b->x) return 1; if (a->y > b->y) return 1; if (a->x == b->x && a->y == b->y) return 0; return -1; } static size_t point_hash(size_t size, Point *point) { return (point->x * 31 + point->y * 37) % size; } static void point_release(Point *point) { mem_release(point); } static int direct_copy(int key) { return key; } static int direct_cmp(int a, int b) { return a - b; } static size_t direct_hash(size_t size, int key) { return key % size; } #define RD 0 #define WR 1 Map *mtmap = NULL; Locker *locker = NULL; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; #ifdef PTHREAD_RWLOCK_INITIALIZER pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; #else pthread_rwlock_t rwlock; #endif int barrier[2]; int size[2]; const int lim = 1000; int debug = 0; int errors = 0; void *produce(void *arg) { int i; int test = *(int *)arg; for (i = 1; i <= lim; ++i) { if (debug) printf("p: add %d\n", i); if (map_add(mtmap, (void *)(long)i, (void *)(long)i) == -1) ++errors, printf("Test%d: map_add(mtmap, %d), failed (%s)\n", test, i, strerror(errno)); write(size[WR], "", 1); } write(barrier[WR], "", 1); return NULL; } void *consume(void *arg) { int i; int test = *(int *)arg; char ack; for (i = 1; i <= lim; ++i) { if (debug) printf("c: pop\n"); while (read(size[RD], &ack, 1) == -1 && errno == EINTR) {} if (map_remove(mtmap, (void *)(long)i) == -1) { ++errors, printf("Test%d: map_remove(mtmap, %d), failed\n", test, i); break; } if (debug) printf("c: remove %d\n", i); } if (i != lim + 1) ++errors, printf("Test%d: consumer read %d items, not %d\n", test, i - 1, lim); write(barrier[WR], "", 1); return NULL; } void *iterate_builtin(void *arg) { int i; int t = *(int *)arg; int broken = 0; if (debug) printf("i%d: loop\n", t); for (i = 0; i < lim / 10; ++i) { map_wrlock(mtmap); while (map_has_next(mtmap) == 1) { int val = (int)(long)map_next(mtmap); if (debug) printf("i%d: loop %d/%d val %d\n", t, i, lim / 10, val); if (!broken) { map_break(mtmap); broken = 1; break; } } map_unlock(mtmap); } write(barrier[WR], "", 1); return NULL; } void *iterate_rdlocked(void *arg) { int i; int t = *(int *)arg; if (debug) printf("j%d: loop\n", t); for (i = 0; i < lim / 10; ++i) { Mapper *mapper = mapper_create_rdlocked(mtmap); while (mapper_has_next(mapper) == 1) { int val = (int)(long)mapper_next(mapper); if (debug) printf("j%d: loop %d/%d val %d\n", t, i, lim / 10, val); } mapper_release(mapper); } write(barrier[WR], "", 1); return NULL; } void *iterate_wrlocked(void *arg) { int i; int t = *(int *)arg; if (debug) printf("k%d: loop\n", t); for (i = 0; i < lim / 10; ++i) { Mapper *mapper = mapper_create_wrlocked(mtmap); while (mapper_has_next(mapper) == 1) { int val = (int)(long)mapper_next(mapper); if (debug) printf("k%d: loop %d/%d val %d\n", t, i, lim / 10, val); } mapper_release(mapper); } write(barrier[WR], "", 1); return NULL; } pthread_mutex_t rand_mutex[1] = { PTHREAD_MUTEX_INITIALIZER }; void *reader(void *arg) { int i; int t = *(int *)arg; if (debug) printf("r%d: loop\n", t); for (i = 0; i < lim / 10; ++i) { int key, value, r; List *keys, *values; pthread_mutex_lock(rand_mutex); r = rand(); pthread_mutex_unlock(rand_mutex); map_rdlock(mtmap); key = 1 + (int)((double)(map_size_unlocked(mtmap) - 1) * r / (RAND_MAX + 1.0)); value = (int)(long)map_get_unlocked(mtmap, (void *)(long)key); map_unlock(mtmap); keys = map_keys(mtmap); values = map_values(mtmap); if (debug) printf("r%d: loop %d/%d key/val %d/%d, #keys %d, #values %d\n", t, i, lim / 10, key, value, (int)list_length(keys), (int)list_length(values)); list_destroy(&keys); list_destroy(&values); } write(barrier[WR], "", 1); return NULL; } void mt_test(int test, Locker *locker) { mtmap = map_create_generic_with_locker(locker, (map_copy_t *)direct_copy, (map_cmp_t *)direct_cmp, (map_hash_t *)direct_hash, NULL, NULL); if (!mtmap) ++errors, printf("Test%d: map_create_generic_with_locker(NULL) failed\n", test); else { static int iid[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; pthread_attr_t attr; pthread_t id; int i; char ack; if (pipe(size) == -1 || pipe(barrier) == -1) { ++errors, printf("Test%d: failed to perform test: pipe() failed\n", test); return; } srand(getpid() ^ time(NULL)); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&id, &attr, produce, &test); pthread_create(&id, &attr, consume, &test); pthread_create(&id, &attr, iterate_builtin, iid + 0); pthread_create(&id, &attr, iterate_builtin, iid + 1); pthread_create(&id, &attr, iterate_builtin, iid + 2); pthread_create(&id, &attr, iterate_builtin, iid + 3); pthread_create(&id, &attr, iterate_rdlocked, iid + 4); pthread_create(&id, &attr, iterate_rdlocked, iid + 5); pthread_create(&id, &attr, iterate_rdlocked, iid + 6); pthread_create(&id, &attr, iterate_wrlocked, iid + 7); pthread_create(&id, &attr, iterate_wrlocked, iid + 8); pthread_create(&id, &attr, iterate_wrlocked, iid + 9); pthread_create(&id, &attr, reader, iid + 10); pthread_create(&id, &attr, reader, iid + 11); pthread_create(&id, &attr, reader, iid + 12); pthread_attr_destroy(&attr); for (i = 0; i < 15; ++i) while (read(barrier[RD], &ack, 1) == -1 && errno == EINTR) {} map_destroy(&mtmap); if (mtmap) ++errors, printf("Test%d: map_destroy(&mtmap) failed\n", test); close(size[RD]); close(size[WR]); close(barrier[RD]); close(barrier[WR]); } } #define TEST_ACT(i, action) \ if (!(action)) \ ++errors, printf("Test%d: %s failed\n", (i), (#action)); #define TEST_INT_ACT(i, action) \ if ((action) == -1) \ ++errors, printf("Test%d: %s failed\n", (i), (#action)); #define TEST_EQ(i, action, value) \ if ((val = (action)) != (value)) \ ++errors, printf("Test%d: %s failed (returned %d, not %d)\n", (i), (#action), val, (value)); #define TEST_NEQ(i, action, value) \ if ((val = (action)) == (value)) \ ++errors, printf("Test%d: %s failed (returned %d)\n", (i), (#action), val); #define CHECK_ITEM(i, action, key, result) \ if (!(value = (char *)(action)) || strcmp(value, (result))) \ ++errors, printf("Test%d: %s failed (mapping \"%s\" is \"%s\", not \"%s\")\n", (i), (#action), (key), value, (result)); #define CHECK_LIST_ITEM(i, action, list, item, value) \ if (!list_item((list), (item)) || strcmp(list_item((list), (item)), (value))) \ ++errors, printf("Test%d: %s failed (item %d is \"%s\", not \"%s\")\n", (i), (#action), (item), (char *)list_item(list, (item)), (value)); #define CHECK_LIST(i, desc, list) \ if (list_length(list) != 4) \ ++errors, printf("Test%d: %s failed (%d items, not 4)\n", (i), (desc), (int)list_length(list)); \ else \ { \ TEST_ACT((i), list_sort((list), (list_cmp_t *)sort_cmp)); \ CHECK_LIST_ITEM((i), list_sort((list), sort_cmp), (list), 0, "abc") \ CHECK_LIST_ITEM((i), list_sort((list), sort_cmp), (list), 1, "def") \ CHECK_LIST_ITEM((i), list_sort((list), sort_cmp), (list), 2, "ghi") \ CHECK_LIST_ITEM((i), list_sort((list), sort_cmp), (list), 3, "jkl") \ } int main(int ac, char **av) { Map *map; Mapper *mapper; List *keys, *values; const void *ckey; const char *cvalue; char *value; char cat[BUFSIZ]; void *ptr; int val; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s [debug|hash]\n", *av); return EXIT_SUCCESS; } if (ac == 2 && !strcmp(av[1], "hash")) test_hash(); printf("Testing: %s\n", "map"); /* Test map_create, map_add, map_get */ TEST_ACT(1, map = map_create(NULL)) else { TEST_INT_ACT(2, map_add(map, "abc", "abc")) TEST_INT_ACT(3, map_add(map, "def", "def")) TEST_INT_ACT(4, map_add(map, "ghi", "ghi")) TEST_INT_ACT(5, map_add(map, "jkl", "jkl")) CHECK_ITEM(6, map_get(map, "abc"), "abc", "abc") CHECK_ITEM(7, map_get(map, "def"), "def", "def") CHECK_ITEM(8, map_get(map, "ghi"), "ghi", "ghi") CHECK_ITEM(9, map_get(map, "jkl"), "jkl", "jkl") if ((value = (char *)map_get(map, "zzz"))) ++errors, printf("Test10: map_get(\"zzz\") failed\n"); /* Test mapper_create, mapper_has_next, mapper_next, map_destroy */ TEST_ACT(11, mapper = mapper_create(map)) else { TEST_ACT(12, keys = list_create(NULL)) while (mapper_has_next(mapper) == 1) { void *item = mapper_next(mapper); TEST_ACT(13, item) else TEST_ACT(13, list_append(keys, item)) } mapper_destroy(&mapper); if (mapper) ++errors, printf("Test14: mapper_destroy(&mapper) failed (%p, not NULL)", (void *)mapper); CHECK_LIST(15, "mapper_has_next/mapper_next", keys) list_destroy(&keys); } /* Test mapper_next_mapping, mapping_key, mapping_value */ TEST_ACT(16, mapper = mapper_create(map)) else { TEST_ACT(16, keys = list_create(NULL)) TEST_ACT(16, values = list_create(NULL)) while (mapper_has_next(mapper) == 1) { const Mapping *mapping = mapper_next_mapping(mapper); if (!mapping) ++errors, printf("Test17: mapper_next_mapping() failed\n"); else { TEST_ACT(17, ckey = mapping_key((void *)mapping)) else TEST_ACT(17, list_append(keys, (void *)ckey)) TEST_ACT(17, cvalue = mapping_value(mapping)) else TEST_ACT(17, list_append(values, (void *)cvalue)) } } mapper_destroy(&mapper); if (mapper) ++errors, printf("Test18: mapper_destroy(&mapper) failed (%p, not NULL)", (void *)mapper); CHECK_LIST(19, "mapper_has_next/mapper_next_mapping", keys) list_destroy(&keys); CHECK_LIST(20, "mapper_has_next/mapper_next_mapping", values) list_destroy(&values); } /* Test map_has_next, map_next */ TEST_ACT(21, keys = list_create(NULL)) while (map_has_next(map) == 1) { void *item; TEST_ACT(22, item = map_next(map)) else TEST_ACT(22, list_append(keys, item)) } CHECK_LIST(23, "map_has_next/map_next", keys) list_destroy(&keys); /* Test map_next_mapping */ TEST_ACT(24, keys = list_create(NULL)) TEST_ACT(24, values = list_create(NULL)) while (map_has_next(map) == 1) { const Mapping *mapping; TEST_ACT(25, mapping = map_next_mapping(map)) else { TEST_ACT(25, ckey = mapping_key(mapping)) else TEST_ACT(25, list_append(keys, (void *)ckey)) TEST_ACT(25, cvalue = mapping_value(mapping)) else TEST_ACT(25, list_append(values, (void *)cvalue)) } } CHECK_LIST(26, "map_has_next/map_next_mapping", keys) list_destroy(&keys); CHECK_LIST(27, "map_has_next/map_next_mapping", values) list_destroy(&values); /* Test map_keys */ TEST_ACT(28, keys = map_keys(map)) else { CHECK_LIST(29, "map_keys", keys) list_destroy(&keys); } /* Test map_values */ TEST_ACT(30, values = map_values(map)) else { CHECK_LIST(31, "map_values", values) list_destroy(&values); } /* Test map_remove */ TEST_ACT(32, map_remove(map, "zzz") == -1) TEST_ACT(33, map_remove(map, "abc") != -1) TEST_ACT(34, map_remove(map, "def") != -1) TEST_ACT(35, map_remove(map, "ghi") != -1) TEST_ACT(36, map_remove(map, "jkl") != -1) map_destroy(&map); if (map) ++errors, printf("Test37: map_destroy(&map) failed (%p, not NULL)\n", (void *)map); } /* Test map_apply */ TEST_ACT(38, map = map_create(NULL)) else { TEST_ACT(39, map_add(map, "1", "7") == 0) TEST_ACT(40, map_add(map, "2", "6") == 0) TEST_ACT(41, map_add(map, "3", "5") == 0) TEST_ACT(42, map_add(map, "4", "4") == 0) TEST_ACT(43, map_add(map, "5", "3") == 0) TEST_ACT(44, map_add(map, "6", "2") == 0) TEST_ACT(45, map_add(map, "7", "1") == 0) CHECK_ITEM(46, map_get(map, "1"), "1", "7") CHECK_ITEM(47, map_get(map, "2"), "2", "6") CHECK_ITEM(48, map_get(map, "3"), "3", "5") CHECK_ITEM(49, map_get(map, "4"), "4", "4") CHECK_ITEM(50, map_get(map, "5"), "5", "3") CHECK_ITEM(51, map_get(map, "6"), "6", "2") CHECK_ITEM(52, map_get(map, "7"), "7", "1") cat[0] = nul; map_apply(map, (map_action_t *)test_action, cat); if (strcmp(cat, "7=1, 1=7, 2=6, 3=5, 4=4, 5=3, 6=2")) ++errors, printf("Test53: map_apply(cat) failed (cat = \"%s\", not \"%s\")\n", cat, "7=1, 1=7, 2=6, 3=5, 4=4, 5=3, 6=2"); map_destroy(&map); if (map) ++errors, printf("Test54: map_destroy(&map) failed (%p, not NULL)\n", (void *)map); } /* Test mapper_remove, map_size */ TEST_ACT(55, map = map_create(NULL)) else { TEST_ACT(56, map_add(map, "1", "7") == 0) TEST_ACT(57, map_add(map, "2", "6") == 0) TEST_ACT(58, map_add(map, "3", "5") == 0) TEST_ACT(59, map_add(map, "4", "4") == 0) TEST_ACT(60, map_add(map, "5", "3") == 0) TEST_ACT(61, map_add(map, "6", "2") == 0) TEST_ACT(62, map_add(map, "7", "1") == 0) TEST_ACT(63, mapper = mapper_create(map)) else { while (mapper_has_next(mapper) == 1) { void *item; TEST_ACT(64, item = mapper_next(mapper)) mapper_remove(mapper); } mapper_destroy(&mapper); if (mapper) ++errors, printf("Test65: mapper_destroy(&mapper) failed (%p, not NULL)\n", (void *)mapper); TEST_EQ(66, map_size(map), 0) } map_destroy(&map); if (map) ++errors, printf("Test67: map_destroy(&map) failed (%p, not NULL)\n", (void *)map); } /* Test map_remove_current */ TEST_ACT(68, map = map_create(NULL)) else { TEST_ACT(69, map_add(map, "1", "1") == 0) TEST_ACT(70, map_add(map, "2", "2") == 0) TEST_ACT(71, map_add(map, "3", "3") == 0) TEST_ACT(72, map_add(map, "4", "4") == 0) while (map_has_next(map) == 1) { void *item; TEST_ACT(73, item = map_next(map)) map_remove_current(map); } TEST_EQ(74, map_size(map), 0) mapper_destroy(&mapper); map_destroy(&map); } /* Test map_create_generic (Point -> char *) */ #define ADD_POINT(i, xpos, ypos, value, rc) \ point->x = (xpos); \ point->y = (ypos); \ TEST_ACT((i), map_add(map, point, (value)) == (rc)) #define GET_POINT(i, xpos, ypos, str) \ point->x = (xpos); \ point->y = (ypos); \ if (!(value = map_get(map, point))) \ ++errors, printf("Test%d: map_get(generic) failed\n", (i)); \ else if (strcmp(value, (str))) \ ++errors, printf("Test%d: map_get(generic) failed (\"%s\", not \"%s\")\n", (i), value, (str)); TEST_ACT(75, map = map_create_generic((map_copy_t *)point_copy, (map_cmp_t *)point_cmp, (map_hash_t *)point_hash, (map_release_t *)point_release, NULL)) else { Point *point = point_create(0, 0); ADD_POINT(76, 0, 0, "(0, 0)", 0) ADD_POINT(77, 1, 0, "(1, 0)", 0) ADD_POINT(78, 0, 1, "(0, 1)", 0) ADD_POINT(79, 1, 1, "(1, 1)", 0) ADD_POINT(80, -1, 0, "(-1, 0)", 0) ADD_POINT(81, 0, -1, "(0, -1)", 0) ADD_POINT(82, -1, -1, "(-1, -1)", 0) ADD_POINT(83, 2, 0, "(2, 0)", 0) ADD_POINT(84, 0, 2, "(0, 2)", 0) ADD_POINT(85, 2, 2, "(2, 2)", 0) ADD_POINT(86, -2, 0, "(-2, 0)", 0) ADD_POINT(87, 0, -2, "(0, -2)", 0) ADD_POINT(88, -2, -2, "(-2, -2)", 0) ADD_POINT(89, 0, 0, "(0, 0)", -1) TEST_EQ(90, map_size(map), 13) GET_POINT(91, 0, 0, "(0, 0)") GET_POINT(92, 1, 0, "(1, 0)") GET_POINT(93, 0, 1, "(0, 1)") GET_POINT(94, 1, 1, "(1, 1)") GET_POINT(95, -1, 0, "(-1, 0)") GET_POINT(96, 0, -1, "(0, -1)") GET_POINT(97, -1, -1, "(-1, -1)") GET_POINT(98, 2, 0, "(2, 0)") GET_POINT(99, 0, 2, "(0, 2)") GET_POINT(100, 2, 2, "(2, 2)") GET_POINT(101, -2, 0, "(-2, 0)") GET_POINT(102, 0, -2, "(0, -2)") GET_POINT(103, -2, -2, "(-2, -2)") point_release(point); map_destroy(&map); } /* Test map_create_generic (int -> int) and map growth */ TEST_ACT(104, map = map_create_generic((map_copy_t *)direct_copy, (map_cmp_t *)direct_cmp, (map_hash_t *)direct_hash, NULL, NULL)) else { TEST_ACT(105, map_add(map, (void *)1, (void *)1) == 0) TEST_ACT(106, map_add(map, (void *)2, (void *)2) == 0) TEST_ACT(107, map_add(map, (void *)3, (void *)3) == 0) TEST_ACT(108, map_add(map, (void *)4, (void *)4) == 0) TEST_ACT(109, map_add(map, (void *)5, (void *)5) == 0) TEST_ACT(110, map_add(map, (void *)6, (void *)6) == 0) TEST_ACT(111, map_add(map, (void *)7, (void *)7) == 0) TEST_ACT(112, map_add(map, (void *)8, (void *)8) == 0) TEST_ACT(113, map_add(map, (void *)9, (void *)9) == 0) TEST_ACT(114, map_add(map, (void *)10, (void *)10) == 0) TEST_ACT(115, map_add(map, (void *)11, (void *)11) == 0) TEST_ACT(116, map_add(map, (void *)12, (void *)12) == 0) TEST_ACT(117, map_add(map, (void *)13, (void *)13) == 0) TEST_ACT(118, map_add(map, (void *)14, (void *)14) == 0) TEST_ACT(119, map_add(map, (void *)15, (void *)15) == 0) TEST_ACT(120, map_add(map, (void *)16, (void *)16) == 0) TEST_ACT(121, map_add(map, (void *)17, (void *)17) == 0) TEST_ACT(122, map_add(map, (void *)18, (void *)18) == 0) TEST_ACT(123, map_add(map, (void *)19, (void *)19) == 0) TEST_ACT(124, map_add(map, (void *)20, (void *)20) == 0) TEST_ACT(125, map_add(map, (void *)21, (void *)21) == 0) TEST_ACT(126, map_add(map, (void *)22, (void *)22) == 0) TEST_ACT(127, map_add(map, (void *)23, (void *)23) == 0) TEST_ACT(128, map_add(map, (void *)24, (void *)24) == 0) TEST_ACT(129, map_add(map, (void *)25, (void *)25) == 0) TEST_ACT(130, map_add(map, (void *)25, (void *)25) != 0) TEST_EQ(130, map_size(map), 25) TEST_EQ(131, (int)(long)map_get(map, (void *)1), 1) TEST_EQ(132, (int)(long)map_get(map, (void *)2), 2) TEST_EQ(133, (int)(long)map_get(map, (void *)3), 3) TEST_EQ(134, (int)(long)map_get(map, (void *)4), 4) TEST_EQ(135, (int)(long)map_get(map, (void *)5), 5) TEST_EQ(136, (int)(long)map_get(map, (void *)6), 6) TEST_EQ(137, (int)(long)map_get(map, (void *)7), 7) TEST_EQ(138, (int)(long)map_get(map, (void *)8), 8) TEST_EQ(139, (int)(long)map_get(map, (void *)9), 9) TEST_EQ(140, (int)(long)map_get(map, (void *)10), 10) TEST_EQ(141, (int)(long)map_get(map, (void *)11), 11) TEST_EQ(142, (int)(long)map_get(map, (void *)12), 12) TEST_EQ(143, (int)(long)map_get(map, (void *)13), 13) TEST_EQ(144, (int)(long)map_get(map, (void *)14), 14) TEST_EQ(145, (int)(long)map_get(map, (void *)15), 15) TEST_EQ(146, (int)(long)map_get(map, (void *)16), 16) TEST_EQ(147, (int)(long)map_get(map, (void *)17), 17) TEST_EQ(148, (int)(long)map_get(map, (void *)18), 18) TEST_EQ(149, (int)(long)map_get(map, (void *)19), 19) TEST_EQ(150, (int)(long)map_get(map, (void *)20), 20) TEST_EQ(151, (int)(long)map_get(map, (void *)21), 21) TEST_EQ(152, (int)(long)map_get(map, (void *)22), 22) TEST_EQ(153, (int)(long)map_get(map, (void *)23), 23) TEST_EQ(154, (int)(long)map_get(map, (void *)24), 24) TEST_EQ(155, (int)(long)map_get(map, (void *)25), 25) map_destroy(&map); } /* Test MT Safety */ debug = ac == 2 && !strcmp(av[1], "debug"); if (debug) setbuf(stdout, NULL); #ifndef PTHREAD_RWLOCK_INITIALIZER pthread_rwlock_init(&rwlock, NULL); #endif if (debug) locker = locker_create_debug_rwlock(&rwlock); else locker = locker_create_rwlock(&rwlock); if (!locker) ++errors, printf("Test220: locker_create_rwlock() failed\n"); else { mt_test(220, locker); locker_destroy(&locker); } if (debug) locker = locker_create_debug_mutex(&mutex); else locker = locker_create_mutex(&mutex); if (!locker) ++errors, printf("Test221: locker_create_mutex() failed\n"); else { mt_test(221, locker); locker_destroy(&locker); } /* Test assumption: sizeof(int) <= sizeof(void *) */ if (sizeof(int) > sizeof(void *)) ++errors, printf("Test 222: assumption failed: sizeof(int) > sizeof(void *): int maps are limited to %d bytes\n", (int)sizeof(void *)); /* Test assumption: memset(&ptr, 0, sizeof(void *)) same as NULL */ memset(&ptr, 0, sizeof(void *)); if (ptr != NULL) ++errors, printf("Test223: assumption failed: memset(&ptr, 0, sizeof(void *)) not same as NULL\n"); if (errors) printf("%d/223 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/map.h000066400000000000000000000115641140522741300155040ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_MAP_H #define LIBSLACK_MAP_H #include #include #include typedef struct Map Map; typedef struct Mapper Mapper; typedef struct Mapping Mapping; typedef list_release_t map_release_t; typedef list_copy_t map_copy_t; typedef list_cmp_t map_cmp_t; typedef size_t map_hash_t(size_t table_size, const void *key); typedef void map_action_t(void *key, void *item, void *data); _begin_decls Map *map_create(map_release_t *destroy); Map *map_create_sized(size_t size, map_release_t *destroy); Map *map_create_with_hash(map_hash_t *hash, map_release_t *destroy); Map *map_create_sized_with_hash(size_t size, map_hash_t *hash, map_release_t *destroy); Map *map_create_with_locker(Locker *locker, map_release_t *destroy); Map *map_create_with_locker_sized(Locker *locker, size_t size, map_release_t *destroy); Map *map_create_with_locker_with_hash(Locker *locker, map_hash_t *hash, map_release_t *destroy); Map *map_create_with_locker_sized_with_hash(Locker *locker, size_t size, map_hash_t *hash, map_release_t *destroy); Map *map_create_generic(map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); Map *map_create_generic_sized(size_t size, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); Map *map_create_generic_with_locker(Locker *locker, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); Map *map_create_generic_with_locker_sized(Locker *locker, size_t size, map_copy_t *copy, map_cmp_t *cmp, map_hash_t *hash, map_release_t *key_destroy, map_release_t *value_destroy); int map_rdlock(const Map *map); int map_wrlock(const Map *map); int map_unlock(const Map *map); void map_release(Map *map); void *map_destroy(Map **map); int map_own(Map *map, map_release_t *destroy); int map_own_unlocked(Map *map, map_release_t *destroy); map_release_t *map_disown(Map *map); map_release_t *map_disown_unlocked(Map *map); int map_add(Map *map, const void *key, void *value); int map_add_unlocked(Map *map, const void *key, void *value); int map_put(Map *map, const void *key, void *value); int map_put_unlocked(Map *map, const void *key, void *value); int map_insert(Map *map, const void *key, void *value, int replace); int map_insert_unlocked(Map *map, const void *key, void *value, int replace); int map_remove(Map *map, const void *key); int map_remove_unlocked(Map *map, const void *key); void *map_get(Map *map, const void *key); void *map_get_unlocked(const Map *map, const void *key); Mapper *mapper_create(Map *map); Mapper *mapper_create_rdlocked(Map *map); Mapper *mapper_create_wrlocked(Map *map); Mapper *mapper_create_unlocked(Map *map); void mapper_release(Mapper *mapper); void mapper_release_unlocked(Mapper *mapper); void *mapper_destroy(Mapper **mapper); void *mapper_destroy_unlocked(Mapper **mapper); int mapper_has_next(Mapper *mapper); void *mapper_next(Mapper *mapper); const Mapping *mapper_next_mapping(Mapper *mapper); void mapper_remove(Mapper *mapper); int map_has_next(Map *map); void map_break(Map *map); void *map_next(Map *map); const Mapping *map_next_mapping(Map *map); void map_remove_current(Map *map); const void *mapping_key(const Mapping *mapping); const void *mapping_value(const Mapping *mapping); List *map_keys(Map *map); List *map_keys_unlocked(Map *map); List *map_keys_with_locker(Locker *locker, Map *map); List *map_keys_with_locker_unlocked(Locker *locker, Map *map); List *map_values(Map *map); List *map_values_unlocked(Map *map); List *map_values_with_locker(Locker *locker, Map *map); List *map_values_with_locker_unlocked(Locker *locker, Map *map); void map_apply(Map *map, map_action_t *action, void *data); void map_apply_rdlocked(Map *map, map_action_t *action, void *data); void map_apply_wrlocked(Map *map, map_action_t *action, void *data); void map_apply_unlocked(Map *map, map_action_t *action, void *data); ssize_t map_size(Map *map); ssize_t map_size_unlocked(const Map *map); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/mem.c000066400000000000000000001224441140522741300155000ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - memory module =head1 SYNOPSIS #include #include typedef struct Pool Pool; #define null NULL #define nul '\0' #define mem_new(type) #define mem_create(size, type) #define mem_resize(mem, size) void *mem_resize_fn(void **mem, size_t size); #define mem_release(mem) void *mem_destroy(void **mem); void *mem_create_secure(size_t size); void mem_release_secure(void *mem); void *mem_destroy_secure(void **mem); char *mem_strdup(const char *str); #define mem_create2d(type, x, y) #define mem_create3d(type, x, y, z) #define mem_create4d(type, x, y, z, a) void *mem_create_space(size_t size, ...); size_t mem_space_start(size_t size, ...); #define mem_release2d(space) #define mem_release3d(space) #define mem_release4d(space) #define mem_release_space(space) #define mem_destroy2d(space) #define mem_destroy3d(space) #define mem_destroy4d(space) #define mem_destroy_space(space) Pool *pool_create(size_t size); Pool *pool_create_with_locker(Locker *locker, size_t size); void pool_release(Pool *pool); void *pool_destroy(Pool **pool); Pool *pool_create_secure(size_t size); Pool *pool_create_secure_with_locker(Locker *locker, size_t size); void pool_release_secure(Pool *pool); void *pool_destroy_secure(Pool **pool); void pool_clear_secure(Pool *pool); #define pool_new(pool, type) #define pool_newsz(pool, size, type) void *pool_alloc(Pool *pool, size_t size); void pool_clear(Pool *pool); =head1 DESCRIPTION This module is mostly just an interface to I, I and I that tries to ensure that pointers that don't point to anything get set to C. It also provides dynamically allocated multi-dimensional arrays, memory pools and secure memory for the more adventurous. =over 4 =cut */ #include "config.h" #include "std.h" #ifdef HAVE_MLOCK #include #endif #include "err.h" #include "mem.h" struct Pool { size_t size; /* number of bytes in the pool */ size_t used; /* number of bytes allocated from the pool */ char *pool; /* address of the pool */ Locker *locker; /* locking strategy for the pool */ }; #ifndef TEST /* =item C< #define null NULL> Easier to type. Easier to read. =item C< #define nul '\0'> A name for the C character. =item C< #define mem_new(type)> Allocates enough memory (with I) to store an object of type C. It is the caller's responsibility to deallocate the allocated memory with I, I or I. On success, returns the address of the allocated memory. On error, returns C. =item C< #define mem_create(size, type)> Allocates enough memory (with I) to store C objects of type C. It is the caller's responsibility to deallocate the allocated memory with I, I or I. On success, returns the address of the allocated memory. On error, returns C. =item C< #define mem_resize(mem, num)> Alters the amount of memory pointed to by C<*mem>. If C<*mem> is C, new memory is allocated and assigned to C<*mem>. If size is zero, C<*mem> is deallocated and C is assigned to C<*mem>. Otherwise, C<*mem> is reallocated and assigned back to C<*mem>. On success, returns C<*mem> (which will be C if C is zero). On error, returns C with C set appropriately and C<*mem> is not altered. =item C An interface to I that also assigns to a pointer variable unless an error occurred. C points to the pointer to be affected. C is the requested size in bytes. If C is zero, C<*mem> is deallocated and set to C. This function is exposed as an implementation side effect. Don't call it directly. Call I instead. On error, returns C with C set appropriately. =cut */ #ifdef HAVE_ISOC_REALLOC #define isoc_realloc realloc #else static void *isoc_realloc(void *ptr, size_t size) { void *p; if (size) { if (!(p = (ptr) ? realloc(ptr, size) : malloc(size))) errno = ENOMEM; /* Not required by ISO C but handy */ } else { free(ptr); p = NULL; } return p; } #endif void *mem_resize_fn(void **mem, size_t size) { void *ptr; if (!mem) return set_errnull(EINVAL); ptr = isoc_realloc(*mem, size); if (size && !ptr) return NULL; return *mem = ptr; } /* =item C< #define mem_release(mem)> Releases (deallocates) C. Same as I. Only to be used in destructor functions. In other cases, use I which also sets C to C. =item C Calls I on the pointer, C<*mem>. Then assigns C to this pointer. Returns C. =cut */ void *(mem_destroy)(void **mem) { if (mem && *mem) { free(*mem); *mem = NULL; } return NULL; } /* =item C Allocates C bytes of memory (with I) and then locks it into RAM with I so that it can't be paged to disk where some nefarious local user with root access might read its contents. It is the caller's responsibility to deallocate the secure memory with I or I which will clear the memory and unlock it before deallocating it. On success, returns the address of the secure allocated memory. On error, returns C with C set appropriately. Note that entire pages are locked by I so don't create many, small pieces of secure memory or many entire pages will be locked. Use a secure memory pool instead. Also note that secure memory requires root privileges. On some systems (e.g. Solaris), memory locks must start on page boundaries. So we need to I enough memory to extend from whatever address I may return to the next page boundary (worst case: C) and then the actual number of bytes requested. We need an additional 8 bytes to store the address returned by I (so we can I it later) and the size passed to I so we can pass it to I later. Unfortunately, we need to store the address and size after the page boundary and not before it because I may return a page boundary or an address less than 8 bytes to the left of a page boundary. It will look like: for free() +-------+ +- size+8 for munlock() v | v +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |* * * *|# # # #| | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ^ ^ ^ . . . size bytes . . . . . . | +- next page | +- malloc() +- address returned If your system doesn't require page boundaries (e.g. Linux), the address returned by I is locked and returned and only the size is stored. =cut */ void *mem_create_secure(size_t size) { #ifdef HAVE_MLOCK char *addr, *lock; #ifdef MLOCK_REQUIRES_PAGE_BOUNDARY long pagesize; if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) return set_errnull(EINVAL); size += sizeof(void *) + sizeof(size_t); addr = malloc(pagesize - sizeof(int) + size); #else size += sizeof(size_t); addr = malloc(size); #endif if (!addr) return NULL; #ifdef MLOCK_REQUIRES_PAGE_BOUNDARY if ((long)addr & (pagesize - 1)) /* addr not on page boundary */ lock = (void *)(((long)addr & ~(pagesize - 1)) + pagesize); else lock = addr; #else lock = addr; #endif if (mlock(lock, size) == -1) { free(addr); return NULL; } #ifdef MLOCK_REQUIRES_PAGE_BOUNDARY *(void **)lock = addr; lock += sizeof(void *); #endif *(size_t *)lock = size; lock += sizeof(size_t); return lock; #else errno = ENOSYS; return NULL; #endif } /* =item C Sets the memory pointed to by C to C bytes, then unlocks and releases (deallocates) C. Only to be used on memory returned by I. Only to be used in destructor functions. In other cases, use I which also sets C to C. =cut */ void mem_release_secure(void *mem) { #ifdef HAVE_MLOCK char *addr, *lock; size_t size; if (!mem) return; lock = mem; lock -= sizeof(size_t); size = *(size_t *)lock; #ifdef MLOCK_REQUIRES_PAGE_BOUNDARY lock -= sizeof(void *); addr = *(void **)lock; #else addr = lock; #endif memset(lock, 0xff, size); memset(lock, 0xaa, size); memset(lock, 0x55, size); memset(lock, 0x00, size); munlock(lock, size); free(addr); #endif } /* =item C Sets the memory pointed to by C<*mem> to C bytes, then unlocks and destroys (deallocates and sets to C) C<*mem>. Only to be used on memory returned by I. Returns C. =cut */ void *(mem_destroy_secure)(void **mem) { if (mem && *mem) { mem_release_secure(*mem); *mem = NULL; } return NULL; } /* =item C Returns a dynamically allocated copy of C. It is the caller's responsibility to deallocate the new string with I, I or I. This function exists because I is not part of the ISO C standard. On error, returns C with C set appropriately. =cut */ char *mem_strdup(const char *str) { size_t size; char *copy; if (!str) return set_errnull(EINVAL); if (!(copy = mem_create(size = strlen(str) + 1, char))) return NULL; return memcpy(copy, str, size); } /* =item C< #define mem_create2d(i, j, type)> Alias for allocating a 2-dimensional array. See I. =item C< #define mem_create3d(i, j, k, type)> Alias for allocating a 3-dimensional array. See I. =item C< #define mem_create4d(i, j, k, l, type)> Alias for allocating a 4-dimensional array. See I. =item C Allocates a multi-dimensional array of elements of size C and sets the memory to C bytes. The remaining arguments specify the sizes of each dimension. The last argument must be zero. There is an arbitrary limit of 32 dimensions. The memory returned is set to zero. The memory returned needs to be cast or assigned into the appropriate pointer type. You can then set and access elements exactly like a real multi-dimensional C array. Finally, it must be deallocated with I or I or I or I or I. Note: You must not use I on all of the returned memory because the start of this memory contains pointers into the remainder. The exact amount of this overhead depends on the number and size of dimensions. The memory is allocated with I to reduce the need to I the elements but if you need to know where the elements begin, use I. The memory returned looks like (e.g.): char ***a = mem_create3d(2, 2, 3, char); +-------------------------+ +-------|-------------------+ | a +-------|-------|-------------+ | | | +-------|-------|-------|-------+ | | | v | | | | V V V V +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | a[0] | a[1] |a[0][0]|a[0][1]|a[1][0]|a[1][1]| | | | | | | | | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | ^ ^ a a a a a a a a a a a a +-------|-------+ | 0 0 0 0 0 0 1 1 1 1 1 1 +-----------------------+ 0 0 0 1 1 1 0 0 0 1 1 1 0 1 2 0 1 2 0 1 2 0 1 2 =cut */ #ifndef MEM_MAX_DIM #define MEM_MAX_DIM 32 #endif void *mem_create_space(size_t size, ...) { size_t dim[MEM_MAX_DIM], d, i, j; size_t lengths[MEM_MAX_DIM]; size_t starts[MEM_MAX_DIM]; size_t sizes[MEM_MAX_DIM]; char *space; size_t arg, length; va_list args; va_start(args, size); for (d = 0; d < MEM_MAX_DIM && (arg = va_arg(args, size_t)); ++d) dim[d] = arg; va_end(args); for (length = i = 0; i < d; ++i) { starts[i] = length; lengths[i] = sizes[i] = (i == d - 1) ? size : sizeof(void *); for (j = 0; j <= i; ++j) lengths[i] *= dim[j]; length += lengths[i]; } if (!(space = calloc(length, 1))) return NULL; for (i = 0; i < d - 1; ++i) { size_t num = dim[i]; for (j = 0; j < i; ++j) num *= dim[j]; for (j = 0; j < num; ++j) *(char **)(space + starts[i] + j * sizes[i]) = space + starts[i + 1] + j * dim[i + 1] * sizes[i + 1]; } return space; } /* =item C Calculates the amount of overhead required for a multi-dimensional array created by a call to I with the same arguments. If you need reset all elements in such an array to zero: int ****space = mem_create_space(sizeof(int), 2, 3, 4, 5, 0); size_t start = mem_space_start(sizeof(int), 2, 3, 4, 5, 0); memset((char *)space + start, '\0', sizeof(int) * 2 * 3 * 4 * 5); =cut */ size_t mem_space_start(size_t size, ...) { size_t dim[MEM_MAX_DIM], d, i, j; size_t lengths[MEM_MAX_DIM]; size_t arg, length; va_list args; va_start(args, size); for (d = 0; d < MEM_MAX_DIM && (arg = va_arg(args, size_t)); ++d) dim[d] = arg; va_end(args); for (length = i = 0; i < d; ++i) { lengths[i] = (i == d - 1) ? size : sizeof(void *); for (j = 0; j <= i; ++j) lengths[i] *= dim[j]; length += lengths[i]; } return length - lengths[d - 1]; } /* =item C< #define mem_release2d(space)> Alias for releasing (deallocating) a 2-dimensional array. See I. =item C< #define mem_release3d(space)> Alias for releasing (deallocating) a 3-dimensional array. See I. =item C< #define mem_release4d(space)> Alias for releasing (deallocating) a 4-dimensional array. See I. =item C< #define mem_release_space(space)> Releases (deallocates) a multi-dimensional array, C, allocated with I. Same as I. Only to be used in destructor functions. In other cases, use I which also sets C to C. =item C< #define mem_destroy2d(space)> Alias for destroying (deallocating and setting to C) a 2-dimensional array. See I. =item C< #define mem_destroy3d(space)> Alias for destroying (deallocating and setting to C) a 3-dimensional array. See I. =item C< #define mem_destroy4d(space)> Alias for destroying (deallocating and setting to C) a 4-dimensional array. See I. =item C< #define mem_destroy_space(mem)> Destroys (deallocates and sets to C) the multi-dimensional array pointed to by C. =cut */ /* =item C Creates a memory pool of size C from which smaller chunks of memory may be subsequently allocated (with I) without resorting to the use of I. Useful when you have many small objects to allocate but I is slowing your program down too much. It is the caller's responsibility to deallocate the new pool with I or I. On success, returns the pool. On error, returns C. The size of a pool can't be changed after it is created and the individual chunks of memory allocated from within a pool can't be separately deallocated. The entire pool can be emptied with I. =cut */ Pool *pool_create(size_t size) { return pool_create_with_locker(NULL, size); } /* =item C Equivalent to I except that multiple threads accessing the new pool will be synchronised by C. =cut */ Pool *pool_create_with_locker(Locker *locker, size_t size) { Pool *pool = mem_create(1, Pool); if (!pool) return NULL; if (!(pool->pool = malloc(size))) { mem_release(pool); return NULL; } pool->size = size; pool->used = 0; pool->locker = locker; return pool; } /* C Claims a write lock on C. On success, returns C<0>. On error, returns an error code. C Unlocks a write lock on C. On success, returns C<0>. On error, returns an error code. */ #define pool_lock(pool) locker_wrlock((pool)->locker) #define pool_unlock(pool) locker_unlock((pool)->locker) /* =item C Releases (deallocates) C. Only to be used in destructor functions. In other cases, use I which also sets C to C. =cut */ void pool_release(Pool *pool) { Locker *locker; int err; if (!pool) return; if ((err = pool_lock(pool))) { set_errno(err); return; } locker = pool->locker; mem_release(pool->pool); mem_release(pool); if ((err = locker_unlock(locker))) set_errno(err); } /* =item C Destroys (deallocates and sets to C) C<*pool>. Returns C. B pools shared by multiple threads must not be destroyed until after all threads have finished with it. =cut */ void *pool_destroy(Pool **pool) { if (pool && *pool) { pool_release(*pool); *pool = NULL; } return NULL; } /* =item C Creates a memory pool of size C just like I except that the memory pool is locked into RAM with I so that it can't be paged to disk where some nefarious local user might read its contents. It is the caller's responsibility to deallocate the new pool with I or I which will clear the memory pool and unlock it before deallocating it. In all other ways, the pool returned is exactly like a pool returned by I. On success, returns the pool. On error, returns C with C set appropriately. Note that secure memory requires root privileges. =cut */ Pool *pool_create_secure(size_t size) { return pool_create_secure_with_locker(NULL, size); } /* =item C Equivalent to I except that multiple threads accessing the new pool will be synchronised by C. =cut */ Pool *pool_create_secure_with_locker(Locker *locker, size_t size) { #ifdef HAVE_MLOCK Pool *pool = mem_create(1, Pool); if (!pool) return NULL; if (!(pool->pool = mem_create_secure(size))) { mem_release(pool); return NULL; } pool->size = size; pool->used = 0; pool->locker = locker; return pool; #else errno = ENOSYS; return NULL; #endif } /* =item C Sets the contents of the memory pool to C bytes, then unlocks and releases (deallocates) C. Only to be used on pools returned by I. Only to be used in destructor functions. In other cases, use I which also sets C to C. =cut */ void pool_release_secure(Pool *pool) { #ifdef HAVE_MLOCK Locker *locker; int err; if (!pool) return; if ((err = pool_lock(pool))) { set_errno(err); return; } locker = pool->locker; mem_release_secure(pool->pool); mem_release(pool); if ((err = locker_unlock(locker))) set_errno(err); #endif } /* =item C Sets the contents of the memory pool to C bytes, then unlocks and destroys (deallocates and sets to C) C<*pool>. Returns C. B secure pools shared by multiple threads must not be destroyed until after all threads have finished with it. =cut */ void *pool_destroy_secure(Pool **pool) { if (pool && *pool) { pool_release_secure(*pool); *pool = NULL; } return NULL; } /* =item C Fills the secure C with C bytes and deallocates all of the chunks of secure memory previously allocated from C so that it can be reused. Does not use I. =cut */ static void pool_clear_unlocked(Pool *pool); void pool_clear_secure(Pool *pool) { int err; if (!pool) return; if ((err = pool_lock(pool))) { set_errno(err); return; } pool_clear_unlocked(pool); memset(pool->pool, 0xff, pool->size); memset(pool->pool, 0xaa, pool->size); memset(pool->pool, 0x55, pool->size); memset(pool->pool, 0x00, pool->size); if ((err = pool_unlock(pool))) set_errno(err); } /* =item C< #define pool_new(pool, type)> Allocates enough memory from C to store an object of type C. On success, returns the address of the allocated memory. On error, returns C with C set appropriately. =item C< #define pool_newsz(pool, size, type)> Allocates enough memory from C to store C objects of type C. On success, returns the address of the allocated memory. On error, returns C with C set appropriately. =item C Allocates a chunk of memory of C bytes from C. Does not use I. The pointer returned must not be passed to I or I. Only the entire pool can be deallocated with I or I. All of the chunks can be deallocated in one go with I without deallocating the pool itself. On success, returns the pointer to the allocated pool memory. On error, returns C with C set appropriately (i.e. C if C is C, C if C does not have enough unused memory to allocate C bytes). It is the caller's responsibility to ensure the correct alignment if necessary by allocating the right numbers of bytes. The easiest way to do ensure is to use separate pools for each specific data type that requires specific alignment. =cut */ void *pool_alloc(Pool *pool, size_t size) { void *addr; int err; if (!pool) return set_errnull(EINVAL); if ((err = pool_lock(pool))) return set_errnull(err); if (pool->used + size > pool->size) { pool_unlock(pool); return set_errnull(ENOSPC); } addr = pool->pool + pool->used; pool->used += size; if ((err = pool_unlock(pool))) return set_errnull(err); return addr; } /* =item C Deallocates all of the chunks of memory previously allocated from C so that it can be reused. Does not use I. =cut */ static void pool_clear_with_locker(Pool *pool, int lock_pool) { int err; if (!pool) return; if (lock_pool && (err = pool_lock(pool))) { set_errno(err); return; } pool->used = 0; if (lock_pool && (err = pool_unlock(pool))) set_errno(err); } static void pool_clear_unlocked(Pool *pool) { pool_clear_with_locker(pool, 0); } void pool_clear(Pool *pool) { pool_clear_with_locker(pool, 1); } /* =back =head1 ERRORS On error, C is set by underlying functions or as follows: =over 4 =item C When arguments are invalid. =item C When there is insufficient available space in a pool for I to satisfy a request. =item C Returned by I and I when I is not supported (e.g. Mac OS X). =back =head1 MT-Level MT-Safe (mem) MT-Disciplined (pool) man I for details. =head1 EXAMPLES 1D array of longs: long *mem = mem_create(100, long); mem_resize(&mem, 200); mem_destroy(&mem); 3D array of ints: int ***space = mem_create3d(10, 20, 30, int); int i, j, k; for (i = 0; i < 10; ++i) for (j = 0; j < 20; ++j) for (k = 0; k < 30; ++k) space[i][j][k] = i + j + j; mem_destroy3d(&space); A pool of a million integers: void pool() { Pool *pool; int i, *p; if (!(pool = pool_create(1024 * 1024 * sizeof(int)))) return; for (i = 0; i < 1024 * 1024; ++i) { if (!(p = pool_new(pool, int))) break; *p = i; } pool_destroy(&pool); } Secure memory: char *secure_passwd = mem_create_secure(32); if (!secure_passwd) exit(EXIT_FAILURE); get_passwd(secure_passwd, 32); use_passwd(secure_passwd); mem_destroy_secure(&secure_passwd); Secure memory pool: Pool *secure_pool; char *secure_passwd; if (!(secure_pool = pool_create_secure(1024 * 1024))) exit(EXIT_FAILURE); secure_passwd = pool_alloc(secure_pool, 32); get_passwd(secure_passwd, 32); use_passwd(secure_passwd); pool_destroy_secure(&secure_pool); =head1 SEE ALSO I, I, I, I, I, I, I, I =head1 AUTHOR 20100612 raf =cut */ #endif #ifdef TEST #include #include #include #include int main(int ac, char **av) { int *mem1 = NULL; char *mem2 = NULL; char *str = NULL; int **space2 = NULL; int ***space3 = NULL; int ****space4 = NULL; int *****space5 = NULL; char **space2d = NULL; double ***space3d = NULL; int i, j, k, l, m; Pool *pool; clock_t start_clock; clock_t end_clock; long malloc_time; long pool_time; int errors = 0; int no_secure_mem; pid_t pid; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s [pool]\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "mem"); /* Test create, resize and destroy */ if (!(mem1 = mem_create(100, int))) ++errors, printf("Test1: mem_create(100, int) failed\n"); if (!(mem_resize(&mem1, 50))) ++errors, printf("Test2: mem_resize(100 -> 50) failed\n"); if (mem_resize(&mem1, 0)) ++errors, printf("Test3: mem_resize(50 -> 0) failed\n"); if (!(mem_resize(&mem1, 50))) ++errors, printf("Test4: mem_resize(0 -> 50) failed\n"); if (mem_destroy(&mem1)) ++errors, printf("Test5: mem_destroy() failed\n"); if (!(mem2 = mem_create(0, char))) ++errors, printf("Test6: mem_create(0, char) failed\n"); if (!(mem_resize(&mem2, 10))) ++errors, printf("Test7: mem_resize(0 -> 10) failed\n"); if (mem_resize(&mem2, 0)) ++errors, printf("Test8: mem_resize(10 -> 0) failed\n"); if (mem_destroy(&mem2)) ++errors, printf("Test9: mem_destroy() failed\n"); /* This used to segfault a broken version of mem_resize() */ switch (pid = fork()) { case -1: { fprintf(stderr, "Failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } case 0: { size_t size = 16; sockopt_t *so = mem_create(size, sockopt_t); sockaddr_any_t *sa = mem_create(size, sockaddr_any_t); mem_resize(&so, size << 1); memset(so + size, 0, size * sizeof(sockopt_t)); mem_resize(&sa, size << 1); memset(sa + size, 0, size * sizeof(sockaddr_any_t)); mem_destroy(&so); mem_destroy(&sa); _exit(EXIT_SUCCESS); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { fprintf(stderr, "Failed to evaluate test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (WIFSIGNALED(status) && WTERMSIG(status) != SIGABRT) ++errors, printf("Test10: mem_resize() failed - killed by signal %d\n", WTERMSIG(status)); else if (WIFEXITED(status) && WEXITSTATUS(status)) ++errors, printf("Test10: mem_resize() failed - exit status %d\n", WEXITSTATUS(status)); break; } } /* Test strdup */ if (!(str = mem_strdup("test"))) ++errors, printf("Test11: mem_strdup() failed (returned NULL)\n"); else { if (strcmp(str, "test")) ++errors, printf("Test12: mem_strdup() failed (equals \"%s\", not \"test\")\n", str); mem_release(str); } /* Test 2D space allocation and deallocation */ if (!(space2 = mem_create_space(sizeof(int), (size_t)1, (size_t)1, (size_t)0))) ++errors, printf("Test13: mem_create_space(1, 1) failed (returned NULL)\n"); else { space2[0][0] = 37; if (space2[0][0] != 37) ++errors, printf("Test14: mem_create_space(1, 1) failed (space2[%d][%d] = %d, not %d)\n", 0, 0, space2[0][0], 37); mem_destroy_space(&space2); if (space2) ++errors, printf("Test15: mem_destroy_space(1, 1) failed\n"); } if (!(space2 = mem_create_space(sizeof(int), (size_t)10, (size_t)10, (size_t)0))) ++errors, printf("Test16: mem_create_space(10, 10) failed (returned NULL)\n"); else { int error = 0; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) space2[i][j] = i + j; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) if (space2[i][j] != i + j) ++error, printf("Test17: mem_create_space(10, 10) failed (space2[%d][%d] = %d, not %d)\n", i, j, space2[i][j], i + j); if (error) ++errors; mem_destroy_space(&space2); if (space2) ++errors, printf("Test18: mem_destroy_space(10, 10) failed\n"); } /* Test 3D space allocation and deallocation */ if (!(space3 = mem_create_space(sizeof(int), (size_t)1, (size_t)1, (size_t)1, (size_t)0))) ++errors, printf("Test19: mem_create_space(1, 1, 1) failed (returned NULL)\n"); else { space3[0][0][0] = 37; if (space3[0][0][0] != 37) ++errors, printf("Test20: mem_create_space(1, 1, 1) failed (space3[%d][%d][%d] = %d, not %d)\n", 0, 0, 0, space3[0][0][0], 37); mem_destroy_space(&space3); if (space3) ++errors, printf("Test21: mem_destroy_space(1, 1, 1) failed\n"); } if (!(space3 = mem_create_space(sizeof(int), (size_t)10, (size_t)10, (size_t)10, (size_t)0))) ++errors, printf("Test22: mem_create_space(10, 10, 10) failed (returned NULL)\n"); else { int error = 0; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) space3[i][j][k] = i + j + k; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) if (space3[i][j][k] != i + j + k) ++error, printf("Test23: mem_create_space(10, 10, 10) failed (space3[%d][%d][%d] = %d, not %d)\n", i, j, k, space3[i][j][k], i + j + k); if (error) ++errors; mem_destroy_space(&space3); if (space3) ++errors, printf("Test24: mem_destroy_space(10, 10, 10) failed\n"); } /* Test 4D space allocation and deallocation */ if (!(space4 = mem_create_space(sizeof(int), (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)0))) ++errors, printf("Test25: mem_create_space(1, 1, 1, 1) failed (returned NULL)\n"); else { space4[0][0][0][0] = 37; if (space4[0][0][0][0] != 37) ++errors, printf("Test26: mem_create_space(1, 1, 1, 1) failed (space4[%d][%d][%d][%d] = %d, not %d)\n", 0, 0, 0, 0, space4[0][0][0][0], 37); mem_destroy_space(&space4); if (space4) ++errors, printf("Test27: mem_destroy_space(1, 1, 1, 1) failed\n"); } if (!(space4 = mem_create_space(sizeof(int), (size_t)10, (size_t)10, (size_t)10, (size_t)10, (size_t)0))) ++errors, printf("Test28: mem_create_space(10, 10, 10, 10) failed (returned NULL)\n"); else { int error = 0; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) for (l = 0; l < 10; ++l) space4[i][j][k][l] = i + j + k + l; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) for (l = 0; l < 10; ++l) if (space4[i][j][k][l] != i + j + k + l) ++error, printf("Test29: mem_create_space(10, 10, 10, 10) failed (space4[%d][%d][%d][%d] = %d, not %d)\n", i, j, k, l, space4[i][j][k][l], i + j + k + l); if (error) ++errors; mem_destroy_space(&space4); if (space4) ++errors, printf("Test30: mem_destroy_space(10, 10, 10, 10) failed\n"); } /* Test 5D space allocation and deallocation */ if (!(space5 = mem_create_space(sizeof(int), (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)1, (size_t)0))) ++errors, printf("Test31: mem_create_space(1, 1, 1, 1, 1) failed (returned NULL)\n"); else { space5[0][0][0][0][0] = 37; if (space5[0][0][0][0][0] != 37) ++errors, printf("Test32: mem_create_space(1, 1, 1, 1, 1) failed (space5[%d][%d][%d][%d][%d] = %d, not %d)\n", 0, 0, 0, 0, 0, space5[0][0][0][0][0], 37); mem_destroy_space(&space5); if (space5) ++errors, printf("Test33: mem_destroy_space(1, 1, 1, 1, 1) failed\n"); } if (!(space5 = mem_create_space(sizeof(int), (size_t)10, (size_t)10, (size_t)10, (size_t)10, (size_t)10, (size_t)0))) ++errors, printf("Test34: mem_create_space(10, 10, 10, 10, 10) failed (returned NULL)\n"); else { int error = 0; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) for (l = 0; l < 10; ++l) for (m = 0; m < 10; ++m) space5[i][j][k][l][m] = i + j + k + l + m; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) for (l = 0; l < 10; ++l) for (m = 0; m < 10; ++m) if (space5[i][j][k][l][m] != i + j + k + l + m) ++error, printf("Test35: mem_create_space(10, 10, 10, 10, 10) failed (space5[%d][%d][%d][%d][%d] = %d, not %d)\n", i, j, k, l, m, space5[i][j][k][l][m], i + j + k + l + m); if (error) ++errors; mem_destroy_space(&space5); if (space5) ++errors, printf("Test36: mem_destroy_space(10, 10, 10, 10, 10) failed\n"); } /* Test element sizes smaller than sizeof(void *) */ if (!(space2d = mem_create_space(sizeof(char), (size_t)1, (size_t)1, (size_t)0))) ++errors, printf("Test37: mem_create_space(char, 1, 1) failed (returned NULL)\n"); else { space2d[0][0] = 'a'; if (space2d[0][0] != 'a') ++errors, printf("Test38: mem_create_space(char, 1, 1) failed (space2d[%d][%d] = '%c', not '%c')\n", 0, 0, space2d[0][0], 'a'); mem_destroy_space(&space2d); if (space2d) ++errors, printf("Test39: mem_destroy_space(char, 1, 1) failed\n"); } if (!(space2d = mem_create_space(sizeof(char), (size_t)10, (size_t)10, (size_t)0))) ++errors, printf("Test40: mem_create_space(char, 10, 10) failed (returned NULL)\n"); else { int error = 0; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) space2d[i][j] = 'a' + (i + j) % 26; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) if (space2d[i][j] != 'a' + (i + j) % 26) ++error, printf("Test41: mem_create_space(char, 10, 10) failed (space2d[%d][%d] = '%c', not '%c')\n", i, j, space2d[i][j], 'a' + (i + j) % 26); if (error) ++errors; mem_destroy_space(&space2d); if (space2d) ++errors, printf("Test42: mem_destroy_space(char, 10, 10) failed\n"); } /* Test element sizes larger than sizeof(void *) */ if (!(space3d = mem_create_space(sizeof(double), (size_t)1, (size_t)1, (size_t)1, (size_t)0))) ++errors, printf("Test43: mem_create_space(double, 1, 1, 1) failed (returned NULL)\n"); else { space3d[0][0][0] = 37.5; if (space3d[0][0][0] != 37.5) ++errors, printf("Test44: mem_create_space(double, 1, 1, 1) failed (space3d[%d][%d][%d] = %g, not %g)\n", 0, 0, 0, space3d[0][0][0], 37.5); mem_destroy_space(&space3d); if (space3d) ++errors, printf("Test45: mem_destroy_space(double, 1, 1, 1) failed\n"); } if (!(space3d = mem_create_space(sizeof(double), (size_t)10, (size_t)10, (size_t)10, (size_t)0))) ++errors, printf("Test46: mem_create_space(double, 10, 10, 10) failed (returned NULL)\n"); else { int error = 0; for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) space3d[i][j][k] = (double)(i + j + k); for (i = 0; i < 10; ++i) for (j = 0; j < 10; ++j) for (k = 0; k < 10; ++k) if (space3d[i][j][k] != (double)(i + j + k)) ++error, printf("Test47: mem_create_space(double, 10, 10, 10) failed (space3[%d][%d][%d] = %g, not %g)\n", i, j, k, space3d[i][j][k], (double)(i + j + k)); if (error) ++errors; mem_destroy_space(&space3d); if (space3d) ++errors, printf("Test48: mem_destroy_space(double, 10, 10, 10) failed\n"); } /* Test mem_space_start() */ if (!(space4 = mem_create_space(sizeof(int), (size_t)2, (size_t)3, (size_t)4, (size_t)5, (size_t)0))) ++errors, printf("Test49: mem_create_space(int, 2, 3, 4, 5) failed (returned NULL)\n"); else { int error = 0; size_t start; for (i = 0; i < 2; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 4; ++k) for (l = 0; l < 5; ++l) space4[i][j][k][l] = i + j + k + l; for (i = 0; i < 2; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 4; ++k) for (l = 0; l < 5; ++l) if (space4[i][j][k][l] != i + j + k + l) ++error, printf("Test50: mem_create_space(int, 2, 3, 4, 5) failed (space4[%d][%d][%d][%d] = %d, not %d)\n", i, j, k, l, space4[i][j][k][l], i + j + k + l); if (error) ++errors; start = mem_space_start(sizeof(int), (size_t)2, (size_t)3, (size_t)4, (size_t)5, (size_t)0); memset((char *)space4 + start, 0, sizeof(int) * 2 * 3 * 4 * 5); for (i = 0; i < 2; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 4; ++k) for (l = 0; l < 4; ++l) if (space4[i][j][k][l] != 0) ++error, printf("Test51: mem_space_start(int, 2, 3, 4, 5) failed (space4[%d][%d][%d][%d] = %d, not %d)\n", i, j, k, l, space4[i][j][k][l], 0); for (i = 0; i < 2; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 4; ++k) for (l = 0; l < 5; ++l) space4[i][j][k][l] = i + j + k + l; for (i = 0; i < 2; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 4; ++k) for (l = 0; l < 5; ++l) if (space4[i][j][k][l] != i + j + k + l) ++error, printf("Test52: mem_space_start(int, 2, 3, 4, 5) failed (space4[%d][%d][%d][%d] = %d, not %d)\n", i, j, k, l, space4[i][j][k][l], i + j + k + l); if (error) ++errors; mem_destroy_space(&space4); if (space4) ++errors, printf("Test53: mem_destroy_space(int, 2, 3, 4, 5) failed\n"); } /* Test pool functions */ start_clock = clock(); if (!(pool = pool_create(1024 * 1024))) ++errors, printf("Test54: pool_create(1024 * 1024) failed: %s\n", strerror(errno)); else { for (i = 0; i < 1024 * 1024; ++i) { if (!pool_new(pool, char)) { ++errors, printf("Test55: pool_alloc() failed: %s\n", strerror(errno)); break; } } errno = 0; if (pool_alloc(pool, 1) != NULL || errno != ENOSPC) ++errors, printf("Test56: pool_alloc(pool, 1) failed (errno %d, not %d)\n", errno, ENOSPC); errno = 0; if (pool_alloc(NULL, 1) != NULL || errno != EINVAL) ++errors, printf("Test57: pool_alloc(NULL, 1) failed (errno %d, not %d)\n", errno, EINVAL); pool_clear(pool); if (!pool_alloc(pool, 1)) ++errors, printf("Test58: pool_clear(), pool_alloc() failed: %s\n", strerror(errno)); pool_destroy(&pool); if (pool) ++errors, printf("Test59: pool_destroy() failed (%p, not NULL)\n", (void *)pool); } end_clock = clock(); pool_time = end_clock - start_clock; if (ac == 2 && !strcmp(av[1], "pool")) { start_clock = clock(); for (i = 0; i < 1024 * 1024; ++i) free(malloc(1)); end_clock = clock(); malloc_time = end_clock - start_clock; printf("malloc %ldus, pool %ldus (pool %g times faster than malloc)\n", malloc_time, pool_time, (double)malloc_time / (double)pool_time); } /* Test secure mem/pool functions */ no_secure_mem = (getuid() != 0); if (!no_secure_mem) { #ifdef MLOCK_REQUIRES_PAGE_BOUNDARY /* Test that page boundary is a power of two */ long pagesize; if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) ++errors, printf("Test60: Failed to perform test: sysconf(_SC_PAGESIZE) failed\n"); else { long size = pagesize; int bits; for (bits = 0; size; size >>= 1) if (size & 1) ++bits; if (bits != 1) ++errors, printf("Test60: pagesize (%ld) is not a power of 2! Secure memory won't work\n", pagesize); } #endif if (!(mem1 = mem_create_secure(1024))) ++errors, printf("Test61: mem_create_secure(1024) failed: %s\n", strerror(errno)); else { mem_destroy_secure(&mem1); if (mem1) ++errors, printf("Test62: mem_destroy_secure(1024) failed: mem == %p, not NULL\n", (void *)mem1); } if (!(pool = pool_create_secure(32))) ++errors, printf("Test63: pool_create_secure(32) failed: %s\n", strerror(errno)); else { void *whitebox = pool->pool; if (!(mem1 = pool_alloc(pool, 32))) ++errors, printf("Test64: pool_alloc(pool, 32) failed: %s\n", strerror(errno)); pool_destroy_secure(&pool); /* Note: This test may be invalid because the memory has already been deallocated */ if (memcmp(whitebox, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32)) ++errors, printf("Test65: pool_destroy_secure(32) failed: memory not cleared (possibly)\n"); if (pool) ++errors, printf("Test66: pool_destroy_secure(32) failed: pool == %p, not NULL\n", (void *)pool); } } /* Test assumption: memory failure results in errno == ENOMEM */ errno = 0; str = NULL; if (!mem_resize(&str, UINT_MAX) && errno != ENOMEM) ++errors, printf("Test67: assumption failed: realloc failed but errno == \"%s\" (not \"%s\")\n", strerror(errno), strerror(ENOMEM)); if (errors) printf("%d/67 tests failed\n", errors); else printf("All tests passed\n"); if (no_secure_mem) { printf("\n"); printf(" Note: Can't perform secure memory tests.\n"); printf(" Audit the code and rerun the test as root.\n"); } return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/mem.h000066400000000000000000000060471140522741300155050ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ #ifndef LIBSLACK_MEM_H #define LIBSLACK_MEM_H #include #include #include #ifndef null #define null NULL #endif #ifndef nul #define nul '\0' #endif typedef struct Pool Pool; _begin_decls #define mem_new(type) malloc(sizeof(type)) #define mem_create(size, type) malloc((size) * sizeof(type)) #define mem_resize(mem, size) mem_resize_fn((void **)(mem), (size) * sizeof(**(mem))) void *mem_resize_fn(void **mem, size_t size); #define mem_release(mem) free(mem) void *mem_destroy(void **mem); #define mem_destroy(mem) (mem_destroy)((void **)(mem)) void *mem_create_secure(size_t size); void mem_release_secure(void *mem); void *mem_destroy_secure(void **mem); #define mem_destroy_secure(mem) (mem_destroy_secure)((void **)(mem)) char *mem_strdup(const char *str); #define mem_create2d(i, j, type) ((type **)mem_create_space(sizeof(type), (i), (j), 0)) #define mem_create3d(i, j, k, type) ((type ***)mem_create_space(sizeof(type), (i), (j), (k), 0)) #define mem_create4d(i, j, k, l, type) ((type ****)mem_create_space(sizeof(type), (i), (j), (k), (l), 0)) void *mem_create_space(size_t size, ...); size_t mem_space_start(size_t size, ...); #define mem_release2d(space) mem_release_space(space) #define mem_release3d(space) mem_release_space(space) #define mem_release4d(space) mem_release_space(space) #define mem_release_space(space) mem_release(space) #define mem_destroy2d(space) mem_destroy_space(space) #define mem_destroy3d(space) mem_destroy_space(space) #define mem_destroy4d(space) mem_destroy_space(space) #define mem_destroy_space(space) mem_destroy(space) Pool *pool_create(size_t size); Pool *pool_create_with_locker(Locker *locker, size_t size); void pool_release(Pool *pool); void *pool_destroy(Pool **pool); Pool *pool_create_secure(size_t size); Pool *pool_create_secure_with_locker(Locker *locker, size_t size); void pool_release_secure(Pool *pool); void *pool_destroy_secure(Pool **pool); void pool_clear_secure(Pool *pool); #define pool_new(pool, type) pool_alloc((pool), sizeof(type)) #define pool_newsz(pool, size, type) pool_alloc((pool), (size) * sizeof(type)) void *pool_alloc(Pool *pool, size_t size); void pool_clear(Pool *pool); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.6.4/libslack/msg.c000066400000000000000000001130201140522741300154760ustar00rootroot00000000000000/* * libslack - http://libslack.org/ * * Copyright (C) 1999-2010 raf * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or visit http://www.gnu.org/copyleft/gpl.html * * 20100612 raf */ /* =head1 NAME I - message module =head1 SYNOPSIS #include #include typedef struct Msg Msg; typedef void msg_out_t(void *data, const void *mesg, size_t mesglen); typedef void msg_release_t(void *data); Msg *msg_create(int type, msg_out_t *out, void *data, msg_release_t *destroy); Msg *msg_create_with_locker(Locker *locker, int type, msg_out_t *out, void *data, msg_release_t *destroy); int msg_rdlock(Msg *mesg); int msg_wrlock(Msg *mesg); int msg_unlock(Msg *mesg); void msg_release(Msg *mesg); void *msg_destroy(Msg **mesg); void msg_out(Msg *dst, const char *format, ...); void msg_out_unlocked(Msg *dst, const char *format, ...); void vmsg_out(Msg *dst, const char *format, va_list args); void vmsg_out_unlocked(Msg *dst, const char *format, va_list args); Msg *msg_create_fd(int fd); Msg *msg_create_fd_with_locker(Locker *locker, int fd); Msg *msg_create_stderr(void); Msg *msg_create_stderr_with_locker(Locker *locker); Msg *msg_create_stdout(void); Msg *msg_create_stdout_with_locker(Locker *locker); Msg *msg_create_file(const char *path); Msg *msg_create_file_with_locker(Locker *locker, const char *path); Msg *msg_create_syslog(const char *ident, int option, int facility, int priority); Msg *msg_create_syslog_with_locker(Locker *locker, const char *ident, int option, int facility, int priority); Msg *msg_syslog_set_facility(Msg *mesg, int facility); Msg *msg_syslog_set_facility_unlocked(Msg *mesg, int facility); Msg *msg_syslog_set_priority(Msg *mesg, int priority); Msg *msg_syslog_set_priority_unlocked(Msg *mesg, int priority); Msg *msg_create_plex(Msg *msg1, Msg *msg2); Msg *msg_create_plex_with_locker(Locker *locker, Msg *msg1, Msg *msg2); int msg_add_plex(Msg *mesg, Msg *item); int msg_add_plex_unlocked(Msg *mesg, Msg *item); const char *msg_set_timestamp_format(const char *format); int msg_set_timestamp_format_locker(Locker *locker); int syslog_lookup_facility(const char *facility); int syslog_lookup_priority(const char *priority); const char *syslog_facility_str(int spec); const char *syslog_priority_str(int spec); int syslog_parse(const char *spec, int *facility, int *priority); =head1 DESCRIPTION This module provides general messaging functions. Message channels can be created that send messages to a file descriptor, a file, I or a client defined message handler or that multiplexes messages to any combination of the above. Messages sent to files are timestamped using (by default) the I format: C<"%Y%m%d %H:%M:%S">. It also provides functions for parsing I targets, converting between I facility names and codes, and converting between I priority names and codes. =over 4 =cut */ #ifndef _BSD_SOURCE #define _BSD_SOURCE /* For snprintf() on OpenBSD-4.7 */ #endif #include "config.h" #include "std.h" #include #include #include #include #include "msg.h" #include "mem.h" #include "err.h" #include "str.h" #ifndef HAVE_SNPRINTF #include "snprintf.h" #endif typedef int MsgFDData; typedef struct MsgFileData MsgFileData; typedef struct MsgSyslogData MsgSyslogData; typedef struct MsgPlexData MsgPlexData; #define MSG_FD 1 #define MSG_FILE 2 #define MSG_SYSLOG 3 #define MSG_PLEX 4 struct Msg { int type; /* subtype */ msg_out_t *out; /* message handling function */ void *data; /* subtype specific data */ msg_release_t *destroy; /* destructor function for data */ Locker *locker; /* locking strategy for this structure */ }; struct MsgFileData { int fd; /* file descriptor (-1 if open failed) */ }; struct MsgSyslogData { int facility; /* syslog(3) priority */ int priority; /* syslog(3) priority */ }; struct MsgPlexData { size_t size; /* elements allocated */ size_t length; /* length of Msg list */ Msg **list; /* list of Msg objects */ }; typedef struct syslog_map_t syslog_map_t; struct syslog_map_t { char *name; int val; }; /* ** The following masks may be wrong on some systems. */ #ifndef LOG_PRIMASK #define LOG_PRIMASK 0x0007 #endif #ifndef LOG_FACMASK #define LOG_FACMASK 0x03f8 #endif static const syslog_map_t syslog_facility_map[] = { { "kern", LOG_KERN }, { "user", LOG_USER }, { "mail", LOG_MAIL }, { "daemon", LOG_DAEMON }, { "auth", LOG_AUTH }, { "syslog", LOG_SYSLOG }, { "lpr", LOG_LPR }, { "news", LOG_NEWS }, { "uucp", LOG_UUCP }, { "cron", LOG_CRON }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { NULL, -1 } }; static const syslog_map_t syslog_priority_map[] = { { "emerg", LOG_EMERG }, { "alert", LOG_ALERT }, { "crit", LOG_CRIT }, { "err", LOG_ERR }, { "warning", LOG_WARNING }, #ifdef LOG_NOTICE { "notice", LOG_NOTICE }, #endif { "info", LOG_INFO }, { "debug", LOG_DEBUG }, { NULL, -1 } }; #ifndef TEST static const char *timestamp_format = "%Y%m%d %H:%M:%S "; static Locker *timestamp_format_locker = NULL; /* =item C Creates a I object initialised with C, C, C and C. Client defined message handlers must specify a C greater than C<4>. It is the caller's responsibility to deallocate the new I with I or I. On success, returns the new I object. On error, returns C. =cut */ Msg *msg_create(int type, msg_out_t *out, void *data, msg_release_t *destroy) { return msg_create_with_locker(NULL, type, out, data, destroy); } /* =item C Equivalent to I except that multiple threads accessing the new I will be synchronised by C. =cut */ Msg *msg_create_with_locker(Locker *locker, int type, msg_out_t *out, void *data, msg_release_t *destroy) { Msg *mesg; if (!(mesg = mem_new(Msg))) return NULL; mesg->type = type; mesg->out = out; mesg->data = data; mesg->destroy = destroy; mesg->locker = locker; return mesg; } /* =item C Claims a read lock on C (if C was created with a I). This is needed when multiple read only I module functions need to be called atomically. It is the caller's responsibility to call I after the atomic operation. The only functions that may be called on C between calls to I and I are any read only I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ #define msg_rdlock(mesg) ((mesg) ? locker_rdlock((mesg)->locker) : EINVAL) #define msg_wrlock(mesg) ((mesg) ? locker_wrlock((mesg)->locker) : EINVAL) #define msg_unlock(mesg) ((mesg) ? locker_unlock((mesg)->locker) : EINVAL) int (msg_rdlock)(Msg *mesg) { return msg_rdlock(mesg); } /* =item C Claims a write lock on C. Claims a write lock on C (if C was created with a I). This is needed when multiple read/write I module functions need to be called atomically. It is the caller's responsibility to call I after the atomic operation. The only functions that may be called on C between calls to I and I are any I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ int (msg_wrlock)(Msg *mesg) { return msg_wrlock(mesg); } /* =item C Unlocks a read or write lock on C obtained with I or I (if C was created with a I). On success, returns C<0>. On error, returns an error code. =cut */ int (msg_unlock)(Msg *mesg) { return msg_unlock(mesg); } /* =item C Releases (deallocates) C and its internal data. =cut */ void msg_release(Msg *mesg) { if (!mesg) return; if (mesg->destroy) mesg->destroy(mesg->data); mem_release(mesg); } /* =item C Destroys (deallocates and sets to C) C<*mesg>. Returns C. =cut */ void *msg_destroy(Msg **mesg) { if (mesg && *mesg) { msg_release(*mesg); *mesg = NULL; } return NULL; } /* =item C Sends a message to C. C is a I-like format string. Any remaining arguments are processed as in I. B msg_out(dst, buf); // EVIL msg_out(dst, "%s", buf); // GOOD =cut */ void msg_out(Msg *dst, const char *format, ...) { va_list args; va_start(args, format); vmsg_out(dst, format, args); va_end(args); } /* =item C Equivalent to I except that C is not read locked. =cut */ void msg_out_unlocked(Msg *dst, const char *format, ...) { va_list args; va_start(args, format); vmsg_out_unlocked(dst, format, args); va_end(args); } /* =item C Sends a message to C. C is a I-like format string. C is processed as in I. =cut */ void vmsg_out(Msg *dst, const char *format, va_list args) { int err; if (!dst) return; if ((err = msg_rdlock(dst))) { set_errno(err); return; } vmsg_out_unlocked(dst, format, args); if ((err = msg_unlock(dst))) set_errno(err); } /* =item C Equivalent to I except that C is not read locked. =cut */ void vmsg_out_unlocked(Msg *dst, const char *format, va_list args) { if (!dst) return; if (dst->out) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); dst->out(dst->data, mesg, strlen(mesg)); } } /* C Creates and initialises the internal data needed by a I object that sends messages to file descriptor C. On success, returns the data. On error, returns C. */ static MsgFDData *msg_fddata_create(int fd) { MsgFDData *data; if (!(data = mem_new(MsgFDData))) return NULL; *data = fd; return data; } /* C Releases (deallocates) the internal data needed by a I object that sends messages to a file descriptor. The file descriptor is not closed. */ static void msg_fddata_release(MsgFDData *data) { mem_release(data); } /* C Sends a message to a file descriptor. C is a pointer to the file descriptor. C is the message. C is it's length. */ static void msg_out_fd(void *data, const void *mesg, size_t mesglen) { if (data && mesg) if (write(*(MsgFDData *)data, mesg, mesglen) == -1) /* Avoid gcc warning */; } /* =item C Creates a I object that sends messages to file descriptor C. It is the caller's responsibility to deallocate the new I with I or I. On success, returns the new I object. On error, returns C. =cut */ Msg *msg_create_fd(int fd) { return msg_create_fd_with_locker(NULL, fd); } /* =item C Equivalent to I except that multiple threads accessing the new I will be synchronised by C. =cut */ Msg *msg_create_fd_with_locker(Locker *locker, int fd) { MsgFDData *data; Msg *mesg; if (!(data = msg_fddata_create(fd))) return NULL; if (!(mesg = msg_create_with_locker(locker, MSG_FD, msg_out_fd, data, (msg_release_t *)msg_fddata_release))) { msg_fddata_release(data); return NULL; } return mesg; } /* =item C Creates a I object that sends messages to standard error. It is the caller's responsibility to deallocate the new I with I or I. On success, returns the new I object. On error, returns C. =cut */ Msg *msg_create_stderr(void) { return msg_create_fd_with_locker(NULL, STDERR_FILENO); } /* =item C Equivalent to I except that multiple threads accessing the new I will be synchronised by C. =cut */ Msg *msg_create_stderr_with_locker(Locker *locker) { return msg_create_fd_with_locker(locker, STDERR_FILENO); } /* =item C Creates a I object that sends messages to standard output. It is the caller's responsibility to deallocate the new I with I or I. On success, returns the new I object. On error, returns C. =cut */ Msg *msg_create_stdout(void) { return msg_create_fd_with_locker(NULL, STDOUT_FILENO); } /* =item C Equivalent to I except that multiple threads accessing the new I will be synchronised by C. =cut */ Msg *msg_create_stdout_with_locker(Locker *locker) { return msg_create_fd_with_locker(locker, STDOUT_FILENO); } /* C Initialises the internal data needed by a I object that sends messages to the file specified by C. This data consists of a copy of C and an open file descriptor to the file. The file descriptor is opened with the C, C and C flags. On success, returns C<0>. On error, returns C<-1> with C set appropriately. */ static int msg_filedata_init(MsgFileData *data, const char *path) { mode_t mode; if (!data || !path) return set_errno(EINVAL); mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; if ((data->fd = open(path, O_WRONLY | O_CREAT | O_APPEND, mode)) == -1) return -1; return 0; } /* C Creates the internal data needed by a I object that sends messages to the file specified by C. On success, returns the data. On error, returns C with C set appropriately. */ static MsgFileData *msg_filedata_create(const char *path) { MsgFileData *data; if (!(data = mem_new(MsgFileData))) return NULL; if (msg_filedata_init(data, path) == -1) { mem_release(data); return NULL; } return data; } /* C Releases (deallocates) the internal data needed by a I object that sends messages to a file. The file descriptor is closed first. */ static void msg_filedata_release(MsgFileData *data) { if (!data) return; if (data->fd != -1) close(data->fd); mem_release(data); } /* C Sends a message to a file. C contains the file descriptor. C is the message. C is it's length. On error, sets C appropriately. */ static void msg_out_file(void *data, const void *mesg, size_t mesglen) { MsgFileData *dst = data; char buf[MSG_SIZE]; size_t buflen; int err; time_t t = time(NULL); if ((err = locker_rdlock(timestamp_format_locker))) { set_errno(err); return; } strftime(buf, MSG_SIZE, timestamp_format, localtime(&t)); if ((err = locker_unlock(timestamp_format_locker))) { set_errno(err); return; } buflen = strlen(buf); if (buflen + mesglen >= MSG_SIZE) mesglen -= MSG_SIZE - buflen; memmove(buf + buflen, mesg, mesglen); if (mesg && dst && dst->fd != -1) if (write(dst->fd, buf, buflen + mesglen) == -1) /* Avoid gcc warning */; } /* =item C Creates a I object that sends messages to the file specified by C. It is the caller's responsibility to deallocate the new I with I or I. On success, returns the new I object. On error, returns C with C set appropriately. =cut */ Msg *msg_create_file(const char *path) { return msg_create_file_with_locker(NULL, path); } /* =item C Equivalent to I except that multiple threads accessing the new I will be synchronised by C. =cut */ Msg *msg_create_file_with_locker(Locker *locker, const char *path) { MsgFileData *data; Msg *mesg; if (!(data = msg_filedata_create(path))) return NULL; if (!(mesg = msg_create_with_locker(locker, MSG_FILE, msg_out_file, data, (msg_release_t *)msg_filedata_release))) { msg_filedata_release(data); return NULL; } return mesg; } /* C Initialises the internal data needed by a I object that sends messages to I. I is called with C and C