aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--COPYING543
-rw-r--r--NEWS0
-rw-r--r--TODO3
-rw-r--r--client/syslog-client.c464
-rw-r--r--daemon/conf.c730
-rw-r--r--daemon/fifo.c105
-rw-r--r--daemon/listener.c335
-rw-r--r--daemon/logrotate.c264
-rw-r--r--daemon/main.c660
-rw-r--r--daemon/names.c63
-rw-r--r--daemon/pathnames.c139
-rw-r--r--daemon/purger.c155
-rw-r--r--daemon/syslogd.c600
-rw-r--r--daemon/syslogd.h204
-rw-r--r--daemon/writer.c498
-rw-r--r--etc/syslog.conf253
-rwxr-xr-xextras/cross-compile-libs214
-rw-r--r--extras/glib-static-win32.patch21
-rw-r--r--test/dropcount.c41
-rw-r--r--test/test.c201
21 files changed, 5494 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..e3bff75
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Alexander Yaworsky <yaworsky@mail.ru>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..fe55f46
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,543 @@
+THIS SOFTWARE IS NOT COPYRIGHTED
+
+This software and its source code is offered for use in the public domain.
+You may use, modify or distribute it freely.
+
+This software is distributed in the hope that it will be useful but
+WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+DISCLAIMED. This includes but is not limited to warranties of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
+However, the following sources
+
+client/logger.c taken from util-linux-2.12
+ ftp://ftp.kernel.org/pub/linux/utils/util-linux
+include/syslog.h taken from slackware http://www.slackware.org
+test/test.c derivative of client/logger.c
+
+contain this copyright notice:
+
+------------------------------------------------------------------------------
+Copyright (c) 1983, 1993
+The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by the University of
+ California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+------------------------------------------------------------------------------
+
+The following libraries are distributed in binary form under the terms of
+GNU Library General Public License:
+
+Glib http://www.gtk.org
+libiconv http://www.gnu.org/software/libiconv
+libintl http://www.gnu.org/software/gettext
+
+------------------------------------------------------------------------------
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..184b9ca
--- /dev/null
+++ b/TODO
@@ -0,0 +1,3 @@
+GUI configuration utility
+
+GUI log viewer
diff --git a/client/syslog-client.c b/client/syslog-client.c
new file mode 100644
index 0000000..c9dc86d
--- /dev/null
+++ b/client/syslog-client.c
@@ -0,0 +1,464 @@
+/*
+ * syslog-client.c - syslog client implementation for windows
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/* define SYSLOG_CONF_DIR where syslog.host should be
+ */
+
+#ifndef SYSLOG_CONF_DIR
+static const char *syslog_conf_dir = ".";
+#else
+static const char *syslog_conf_dir = SYSLOG_CONF_DIR;
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <syslog.h>
+
+#ifdef TEST
+# define SYSLOG_DGRAM_SIZE 80
+#else
+# define SYSLOG_DGRAM_SIZE 1024
+#endif
+
+static BOOL initialized = FALSE;
+static int log_mask = 0xFF;
+static char *syslog_ident;
+static int syslog_facility;
+static char str_pid[ 40 ];
+static SOCKADDR_IN sa_logger;
+static SOCKET sock;
+static char local_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ];
+static char datagramm[ SYSLOG_DGRAM_SIZE ];
+static int datagramm_size;
+
+/******************************************************************************
+ * set_syslog_conf_dir
+ *
+ * maybe this function will be useful...
+ */
+const char* set_syslog_conf_dir( const char* dir )
+{
+ const char *ret = syslog_conf_dir;
+ syslog_conf_dir = dir;
+ return ret;
+}
+
+/******************************************************************************
+ * init_logger_addr
+ *
+ * Read configuration file syslog.host. This file should contain host address
+ * and, optionally, port. Initialize sa_logger. If the configuration file does
+ * not exist, use localhost:514.
+ * Returns: 0 - ok, -1 - error.
+ */
+static void init_logger_addr()
+{
+ char pathname[ FILENAME_MAX ];
+ char *p;
+ FILE *fd;
+ char host[256];
+ struct hostent * phe;
+
+ memset( &sa_logger, 0, sizeof(SOCKADDR_IN) );
+ sa_logger.sin_family = AF_INET;
+
+ if( '\\' == syslog_conf_dir[0] || '/' == syslog_conf_dir[0] || ':' == syslog_conf_dir[1] )
+ {
+ /* absolute path */
+ strcpy( pathname, syslog_conf_dir );
+ }
+ else
+ {
+ /* relative path */
+ char *q;
+
+ strcpy( pathname, __argv[0] );
+ p = strrchr( pathname, '\\' ) + 1;
+ q = strrchr( pathname, '/' ) + 1;
+ if( p < q )
+ *q = 0;
+ else if( p > q )
+ *p = 0;
+ else
+ pathname[0] = 0;
+ strcat( pathname, syslog_conf_dir );
+ }
+ p = &pathname[ strlen( pathname ) - 1 ];
+ if( '\\' != *p && '/' != *p )
+ {
+ p++; *p = '/';
+ }
+ strcpy( ++p, "syslog.host" );
+
+ /* read destination host name */
+ fd = fopen( pathname, "r" );
+ if( !fd )
+ goto use_default;
+
+ if( NULL == fgets( host, sizeof(host), fd ) )
+ host[0] = 0;
+ else
+ {
+ p = strchr( host, '\n' );
+ if( p )
+ *p = 0;
+ p = strchr( host, '\r' );
+ if( p )
+ *p = 0;
+ }
+ fclose( fd );
+
+ p = strchr( host, ':' );
+ if( p )
+ *p++ = 0;
+
+ phe = gethostbyname( host );
+ if( !phe )
+ goto use_default;
+
+ memcpy( &sa_logger.sin_addr.s_addr, phe->h_addr, phe->h_length );
+
+ if( p )
+ sa_logger.sin_port = htons( (unsigned short) strtoul( p, NULL, 0 ) );
+ else
+ sa_logger.sin_port = htons( SYSLOG_PORT );
+ return;
+
+use_default:
+ sa_logger.sin_addr.S_un.S_addr = htonl( 0x7F000001 );
+ sa_logger.sin_port = htons( SYSLOG_PORT );
+}
+
+/******************************************************************************
+ * closelog
+ *
+ * Close desriptor used to write to system logger.
+ */
+void closelog()
+{
+ if( !initialized )
+ return;
+ closesocket( sock );
+ WSACleanup();
+ initialized = FALSE;
+}
+
+/******************************************************************************
+ * openlog
+ *
+ * Open connection to system logger.
+ */
+void openlog( char* ident, int option, int facility )
+{
+ BOOL failed = TRUE, wsa_initialized = FALSE;
+ WSADATA wsd;
+ SOCKADDR_IN sa_local;
+ DWORD n;
+ int size;
+
+ if( initialized )
+ return;
+
+ syslog_facility = facility? facility : LOG_USER;
+
+ /* FIXME: should we reset logmask? */
+
+ if( option & LOG_PID )
+ snprintf( str_pid, sizeof(str_pid), "[%lu]", GetCurrentProcessId() );
+ else
+ str_pid[0] = 0;
+
+ /* FIXME: handle other options */
+
+ n = sizeof(local_hostname);
+ if( !GetComputerName( local_hostname, &n ) )
+ goto done;
+
+ sock = INVALID_SOCKET;
+ if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) )
+ goto done;
+ wsa_initialized = TRUE;
+
+ init_logger_addr();
+
+ for( n = 0;; n++ )
+ {
+ sock = socket( AF_INET, SOCK_DGRAM, 0 );
+ if( INVALID_SOCKET == sock )
+ goto done;
+
+ memset( &sa_local, 0, sizeof(SOCKADDR_IN) );
+ sa_local.sin_family = AF_INET;
+ if( bind( sock, (SOCKADDR*) &sa_local, sizeof(SOCKADDR_IN) ) == 0 )
+ break;
+ closesocket( sock );
+ sock = INVALID_SOCKET;
+ if( n == 100 )
+ goto done;
+ Sleep(0);
+ }
+
+ /* get size of datagramm */
+ size = sizeof(datagramm_size);
+ if( getsockopt( sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*) &datagramm_size, &size ) )
+ goto done;
+ if( datagramm_size - strlen(local_hostname) - (ident? strlen(ident) : 0) < 64 )
+ goto done;
+ if( datagramm_size > sizeof(datagramm) )
+ datagramm_size = sizeof(datagramm);
+
+ if( atexit( closelog ) )
+ goto done;
+
+ syslog_ident = ident;
+ syslog_facility = facility;
+ failed = FALSE;
+
+done:
+ if( failed )
+ {
+ if( sock != INVALID_SOCKET ) closesocket( sock );
+ if( wsa_initialized ) WSACleanup();
+ }
+ initialized = !failed;
+}
+
+/******************************************************************************
+ * setlogmask
+ *
+ * Set the log mask level.
+ */
+int setlogmask( int mask )
+{
+ int ret = log_mask;
+
+ if( mask )
+ log_mask = mask;
+ return ret;
+}
+
+/******************************************************************************
+ * syslog
+ *
+ * Generate a log message using FMT string and option arguments.
+ */
+void syslog( int pri, char* fmt, ... )
+{
+ va_list ap;
+
+ va_start( ap, fmt );
+ vsyslog( pri, fmt, ap );
+ va_end( ap );
+}
+
+/******************************************************************************
+ * vsyslog
+ *
+ * Generate a log message using FMT and using arguments pointed to by AP.
+ */
+void vsyslog( int pri, char* fmt, va_list ap )
+{
+ static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ SYSTEMTIME stm;
+ int len;
+ char *p;
+
+ if( !(LOG_MASK( LOG_PRI( pri )) & log_mask) )
+ return;
+
+ openlog( NULL, 0, pri & LOG_FACMASK );
+ if( !initialized )
+ return;
+
+ if( !(pri & LOG_FACMASK) )
+ pri |= syslog_facility;
+
+ GetLocalTime( &stm );
+ len = sprintf( datagramm, "<%d>%s %2d %02d:%02d:%02d %s %s%s: ",
+ pri,
+ month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond,
+ local_hostname, syslog_ident? syslog_ident : "", str_pid );
+ vsnprintf( datagramm + len, datagramm_size - len, fmt, ap );
+ p = strchr( datagramm, '\n' );
+ if( p )
+ *p = 0;
+ p = strchr( datagramm, '\r' );
+ if( p )
+ *p = 0;
+
+ sendto( sock, datagramm, strlen(datagramm), 0, (SOCKADDR*) &sa_logger, sizeof(SOCKADDR_IN) );
+}
+
+/******************************************************************************
+ * test
+ */
+#ifdef TEST
+
+static HANDLE hRxEvent = NULL, hAckEvent = NULL;
+static SOCKET rxsock = INVALID_SOCKET;
+static char buffer[ sizeof(datagramm)+1 ];
+
+static DWORD WINAPI listener( LPVOID param )
+{
+ for(;;)
+ {
+ int ret;
+
+ WaitForSingleObject( hAckEvent, INFINITE );
+ ret = recv( rxsock, buffer, sizeof(datagramm), 0 );
+ if( ret <= 0 )
+ break;
+ buffer[ ret ] = 0;
+ SetEvent( hRxEvent );
+ }
+ return 0;
+}
+
+static int transact( int pri, char* fmt, ... )
+{
+ va_list ap;
+ DWORD r;
+
+ va_start( ap, fmt );
+ vsyslog( pri, fmt, ap );
+ va_end( ap );
+ r = WaitForSingleObject( hRxEvent, 2000 );
+ if( WAIT_TIMEOUT == r )
+ {
+ fprintf( stderr, "timeout\n" );
+ return -1;
+ }
+ if( WAIT_FAILED == r )
+ {
+ fprintf( stderr, "wait failed, error %lu\n", GetLastError() );
+ return -1;
+ }
+ printf( "*** %s\n", buffer );
+ SetEvent( hAckEvent );
+ return 0;
+}
+
+int main( int argc, char* argv[] )
+{
+ int ret = 1;
+ WSADATA wsd;
+ SOCKADDR_IN sa_local;
+ DWORD tid;
+ HANDLE hThread = NULL;
+
+ if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) )
+ {
+ fprintf( stderr, "WSAStartup() failed, error %d\n", WSAGetLastError() );
+ return 1;
+ }
+
+ hRxEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( !hRxEvent )
+ {
+ fprintf( stderr, "CreateEvent() failed, error %lu\n", GetLastError() );
+ goto done;
+ }
+
+ hAckEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
+ if( !hAckEvent )
+ {
+ fprintf( stderr, "CreateEvent() failed, error %lu\n", GetLastError() );
+ goto done;
+ }
+
+ rxsock = socket( AF_INET, SOCK_DGRAM, 0 );
+ if( INVALID_SOCKET == sock )
+ {
+ fprintf( stderr, "socket() failed, error %d\n", WSAGetLastError() );
+ goto done;
+ }
+
+ memset( &sa_local, 0, sizeof(SOCKADDR_IN) );
+ sa_local.sin_family = AF_INET;
+ sa_local.sin_addr.S_un.S_addr = htonl( 0x7F000001 );
+ sa_local.sin_port = htons( SYSLOG_PORT );
+ if( bind( rxsock, (SOCKADDR*) &sa_local, sizeof(SOCKADDR_IN) ) )
+ {
+ fprintf( stderr, "bind() failed, error %d\n", WSAGetLastError() );
+ goto done;
+ }
+
+ hThread = CreateThread( NULL, 0, listener, NULL, 0, &tid );
+ if( !hThread )
+ {
+ fprintf( stderr, "CreateThread() failed, error %lu\n", GetLastError() );
+ goto done;
+ }
+
+ openlog( "test_ident", 0, LOG_USER );
+ if( !initialized )
+ {
+ fprintf( stderr, "openlog() failed\n" );
+ goto done;
+ }
+
+ if( transact( LOG_DEBUG, "test message %d", 1 ) )
+ goto done;
+
+ setlogmask( LOG_MASK( LOG_EMERG ) );
+
+ if( !transact( LOG_DEBUG, "test message %d", 2 ) )
+ goto done;
+
+ if( transact( LOG_EMERG, "test message %d", 3 ) )
+ goto done;
+
+ closelog();
+ openlog( "test_ident", LOG_PID, LOG_USER );
+ if( !initialized )
+ {
+ fprintf( stderr, "openlog() failed\n" );
+ goto done;
+ }
+
+ setlogmask( LOG_MASK( LOG_DEBUG ) );
+
+ if( transact( LOG_DEBUG, "test message %d with pid", 4 ) )
+ goto done;
+
+ if( transact( LOG_DEBUG, "long test message %d 1234567890 1234567890 1234567890 1234567890 1234567890", 5 ) )
+ goto done;
+
+ ret = 0;
+
+done:
+ if( rxsock != INVALID_SOCKET ) closesocket( rxsock );
+ if( hAckEvent )
+ {
+ if( hThread )
+ {
+ SetEvent( hAckEvent );
+ puts( "waiting for thread shutdown" );
+ WaitForSingleObject( hThread, INFINITE );
+ }
+ CloseHandle( hAckEvent );
+ }
+ if( hThread ) CloseHandle( hThread );
+ if( hRxEvent ) CloseHandle( hRxEvent );
+ WSACleanup();
+ return 0;
+}
+
+#endif /* TEST */
diff --git a/daemon/conf.c b/daemon/conf.c
new file mode 100644
index 0000000..654e46b
--- /dev/null
+++ b/daemon/conf.c
@@ -0,0 +1,730 @@
+/*
+ * conf.c - syslogd implementation for windows, configuration reader
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/* define SYSLOG_CONF_DIR where syslog.host should be
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <winsock2.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+#ifndef SYSLOG_CONF_DIR
+static char syslog_conf_dir[] = ".";
+#else
+static char syslog_conf_dir[] = SYSLOG_CONF_DIR;
+#endif
+
+/* options and their default values */
+gboolean use_dns = TRUE;
+gchar *source_encoding = NULL;
+gchar *destination_encoding = NULL;
+int mark_interval = 0;
+gchar *mark_message = "-- MARK --";
+int hold = 3;
+gchar *logdir = NULL;
+
+/* sources, destinations, filters and logpaths */
+struct logpath_names
+{
+ gchar *source;
+ gchar *filter;
+ gchar *destination;
+};
+
+GList *sources = NULL;
+GList *destinations = NULL;
+GList *filters = NULL;
+GList *logpaths = NULL;
+
+GList *purger_dirs = NULL;
+
+/* Glib markup wrapper data */
+static gchar *encoding = NULL;
+static gboolean prolog_expected = TRUE;
+/* parser data */
+static struct filter *current_filter = NULL;
+
+/******************************************************************************
+ * xml_start_element
+ *
+ * parse configuration elements
+ */
+static void xml_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ const gchar *aname, *aval;
+ int line_number;
+
+ prolog_expected = FALSE;
+
+ g_markup_parse_context_get_position( context, &line_number, NULL );
+
+ /* top-level elements */
+ if( strcmp( element_name, "source" ) == 0 )
+ {
+ struct source *source = g_malloc( sizeof(struct source) );
+
+ source->name = NULL;
+ source->type = ST_UNDEFINED;
+ memset( &source->udp, 0, sizeof(source->udp) );
+ source->udp.sin_family = AF_INET;
+ source->udp.sin_port = htons( SYSLOG_PORT );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "name" ) == 0 )
+ source->name = g_strdup( aval );
+ else if( strcmp( aname, "type" ) == 0 )
+ {
+ if( strcmp( aval, "internal" ) == 0 )
+ source->type = ST_INTERNAL;
+ else if( strcmp( aval, "udp" ) == 0 )
+ source->type = ST_UDP;
+ }
+ else if( strcmp( aname, "interface" ) == 0 )
+ {
+ struct hostent *he = gethostbyname( aval );
+ if( !he )
+ {
+ ERR( "Cannot resolve hostname %s; error %lu\n", aval, WSAGetLastError() );
+ g_free( source );
+ return;
+ }
+ memcpy( &source->udp.sin_addr.s_addr, he->h_addr, he->h_length );
+ }
+ else if( strcmp( aname, "port" ) == 0 )
+ source->udp.sin_port = htons( strtoul( aval, NULL, 0 ) );
+ }
+
+ if( !source->name )
+ ERR( "Undefined source name at line %d\n", line_number );
+ if( ST_UNDEFINED == source->type )
+ ERR( "Undefined source type at line %d\n", line_number );
+ if( (!source->name) || ST_UNDEFINED == source->type )
+ {
+ g_free( source );
+ return;
+ }
+ sources = g_list_append( sources, source );
+ }
+ else if( strcmp( element_name, "destination" ) == 0 )
+ {
+ struct destination *dest = g_malloc0( sizeof(struct destination) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "name" ) == 0 )
+ dest->name = g_strdup( aval );
+ else if( strcmp( aname, "file" ) == 0 )
+ dest->file = normalize_pathname( aval );
+ else if( strcmp( aname, "rotate" ) == 0 )
+ {
+ if( strcmp( aval, "daily" ) == 0 )
+ dest->rotate = RP_DAILY;
+ else if( strcmp( aval, "weekly" ) == 0 )
+ dest->rotate = RP_WEEKLY;
+ else if( strcmp( aval, "monthly" ) == 0 )
+ dest->rotate = RP_MONTHLY;
+ else
+ {
+ ERR( "Invalid rotation period at line %d\n", line_number );
+ dest->rotate = RP_INVALID;
+ }
+ }
+ else if( strcmp( aname, "size" ) == 0 )
+ {
+ char *endptr;
+ dest->size = strtoul( aval, &endptr, 0 );
+ if( 'k' == *endptr )
+ dest->size *= 1024;
+ else if( 'M' == *endptr )
+ dest->size *= 1024 * 1024;
+ }
+ else if( strcmp( aname, "backlogs" ) == 0 )
+ dest->backlogs = strtoul( aval, NULL, 0 );
+ else if( strcmp( aname, "ifempty" ) == 0 )
+ {
+ if( strcmp( aval, "yes" ) == 0 )
+ dest->ifempty = TRUE;
+ else if( strcmp( aval, "no" ) == 0 )
+ dest->ifempty = FALSE;
+ else
+ {
+ dest->ifempty = TRUE;
+ ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d; assumed \"yes\"\n",
+ aval, aname, line_number );
+ }
+ }
+ else if( strcmp( aname, "olddir" ) == 0 )
+ dest->olddir = normalize_pathname( aval );
+ else if( strcmp( aname, "compresscmd" ) == 0 )
+ dest->compresscmd = g_strdup( aval );
+ else if( strcmp( aname, "compressoptions" ) == 0 )
+ dest->compressoptions = g_strdup( aval );
+ }
+ if( !dest->name )
+ ERR( "Undefined destination name at line %d\n", line_number );
+ if( !dest->file )
+ ERR( "Undefined destination file at line %d\n", line_number );
+ if( (!dest->name) || (!dest->file) || RP_INVALID == dest->rotate )
+ {
+ if( dest->name ) g_free( dest->name );
+ if( dest->file ) g_free( dest->file );
+ if( dest->olddir ) g_free( dest->olddir );
+ if( dest->compresscmd ) g_free( dest->compresscmd );
+ if( dest->compressoptions ) g_free( dest->compressoptions );
+ g_free( dest );
+ return;
+ }
+ if( dest->compresscmd && !dest->compressoptions )
+ dest->compressoptions = g_strdup( "$PATHNAME" );
+
+ dest->file_writers = NULL;
+ InitializeCriticalSection( &dest->cs_file_writers );
+
+ destinations = g_list_append( destinations, dest );
+ }
+ else if( strcmp( element_name, "filter" ) == 0 )
+ {
+ current_filter = g_malloc0( sizeof(struct filter) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ if( strcmp( aname, "name" ) == 0 )
+ current_filter->name = g_strdup( *attribute_values );
+ }
+ if( !current_filter->name )
+ {
+ ERR( "Undefined filter name at line %d\n", line_number );
+ g_free( current_filter );
+ current_filter = NULL;
+ return;
+ }
+ }
+ else if( strcmp( element_name, "logpath" ) == 0 )
+ {
+ /* at first, fill logpaths list with logpath_names structures
+ and replace them later with logpath structures after configuration has been read
+ */
+ struct logpath_names *logpath = g_malloc0( sizeof(struct logpath_names) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "source" ) == 0 )
+ logpath->source = g_strdup( aval );
+ else if( strcmp( aname, "filter" ) == 0 )
+ logpath->filter = g_strdup( aval );
+ else if( strcmp( aname, "destination" ) == 0 )
+ logpath->destination = g_strdup( aval );
+ }
+ if( !logpath->source )
+ ERR( "Undefined log path source at line %d\n", line_number );
+ if( !logpath->destination )
+ ERR( "Undefined log path destination at line %d\n", line_number );
+ if( (!logpath->source) || (!logpath->destination) )
+ {
+ if( logpath->source ) g_free( logpath->source );
+ if( logpath->filter ) g_free( logpath->filter );
+ if( logpath->destination ) g_free( logpath->destination );
+ g_free( logpath );
+ return;
+ }
+ logpaths = g_list_append( logpaths, logpath );
+ }
+ else if( strcmp( element_name, "options" ) == 0 )
+ {
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "dns" ) == 0 )
+ {
+ if( strcmp( aval, "yes" ) == 0 )
+ use_dns = TRUE;
+ else if( strcmp( aval, "no" ) == 0 )
+ use_dns = FALSE;
+ else
+ ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n", aval, aname, line_number );
+ }
+ else if( strcmp( aname, "source_encoding" ) == 0 )
+ source_encoding = g_strdup( aval );
+ else if( strcmp( aname, "destination_encoding" ) == 0 )
+ destination_encoding = g_strdup( aval );
+ else if( strcmp( aname, "mark_interval" ) == 0 )
+ mark_interval = strtoul( aval, NULL, 0 );
+ else if( strcmp( aname, "mark_message" ) == 0 )
+ mark_message = g_strdup( aval );
+ else if( strcmp( aname, "hold" ) == 0 )
+ {
+ hold = strtoul( aval, NULL, 0 );
+ if( hold < 1 )
+ hold = 1;
+ }
+ else if( strcmp( aname, "logdir" ) == 0 )
+ logdir = normalize_pathname( aval );
+ }
+ }
+ else if( strcmp( element_name, "purge" ) == 0 )
+ {
+ struct purger_dir *pdir = g_malloc0( sizeof(struct purger_dir) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "directory" ) == 0 )
+ pdir->directory = normalize_pathname( aval );
+ else if( strcmp( aname, "keep_days" ) == 0 )
+ pdir->keep_days = strtoul( aval, NULL, 0 );
+ }
+ if( !pdir->directory )
+ ERR( "Undefined purge directory at line %d\n", line_number );
+ if( !pdir->keep_days )
+ ERR( "Undefined keep_days parameter at line %d\n", line_number );
+ if( (!pdir->directory) || (!pdir->keep_days) )
+ {
+ if( pdir->directory ) g_free( pdir->directory );
+ g_free( pdir );
+ return;
+ }
+ purger_dirs = g_list_append( purger_dirs, pdir );
+ }
+ else if( current_filter )
+ {
+ /* sub-elements of filter */
+ int val = -2;
+
+ if( strcmp( element_name, "facility" ) == 0 )
+ {
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "value" ) == 0 )
+ {
+ val = strtol( aval, NULL, 0 );
+ if( val < 0 || val >= LOG_NFACILITIES )
+ {
+ val = -1;
+ break;
+ }
+ }
+ else if( strcmp( aname, "name" ) == 0 )
+ {
+ CODE *c;
+ for( c = facilitynames; c->c_name; c++ )
+ if( strcmp( aval, c->c_name ) == 0 )
+ {
+ val = LOG_FAC( c->c_val );
+ break;
+ }
+ if( !c->c_name )
+ {
+ val = -1;
+ break;
+ }
+ }
+ }
+ if( -2 == val )
+ ERR( "Undefined facility at line %d\n", line_number );
+ else if( val != -1 )
+ current_filter->facilities[ val ] = TRUE;
+ }
+ else if( strcmp( element_name, "priority" ) == 0 )
+ {
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "value" ) == 0 )
+ {
+ val = strtol( aval, NULL, 0 );
+ if( val < 0 || val >= 8 )
+ {
+ val = -1;
+ break;
+ }
+ }
+ else if( strcmp( aname, "name" ) == 0 )
+ {
+ CODE *c;
+ for( c = prioritynames; c->c_name; c++ )
+ if( strcmp( aval, c->c_name ) == 0 )
+ {
+ val = LOG_PRI( c->c_val );
+ break;
+ }
+ if( !c->c_name )
+ {
+ val = -1;
+ break;
+ }
+ }
+ }
+ if( -2 == val )
+ ERR( "Undefined priority at line %d\n", line_number );
+ else if( val != -1 )
+ current_filter->priorities[ val ] = TRUE;
+ }
+
+ if( -1 == val )
+ ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n", aval, aname, line_number );
+ }
+}
+
+/******************************************************************************
+ * xml_end_element
+ */
+static void xml_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ if( strcmp( element_name, "filter" ) == 0 )
+ {
+ if( current_filter )
+ {
+ /* append filter to the list */
+ filters = g_list_append( filters, current_filter );
+ current_filter = NULL;
+ }
+ }
+}
+
+/******************************************************************************
+ * xml_passthrough
+ *
+ * look for encoding name
+ */
+static void xml_passthrough (GMarkupParseContext *context,
+ const gchar *passthrough_text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ const gchar *startptr, *endptr;
+
+ if( !prolog_expected )
+ return;
+
+ startptr = g_strstr_len( passthrough_text, text_len, "<?xml " );
+ if( !startptr )
+ return;
+
+ startptr += 6;
+ text_len -= startptr - passthrough_text;
+
+ endptr = g_strstr_len( startptr, text_len, "?>" );
+ if( !endptr )
+ goto parsed;
+
+ text_len = endptr - startptr;
+ startptr = g_strstr_len( startptr, text_len, "encoding=\"" );
+ if( !startptr )
+ goto parsed;
+
+ startptr += 10;
+
+ endptr = strchr( startptr, '"' );
+ if( !endptr )
+ goto parsed;
+
+ if( strncmp( startptr, "windows-", 8 ) == 0 )
+ {
+ gchar *p;
+
+ startptr += 8;
+ p = g_strndup( startptr, endptr - startptr );
+ if( !p )
+ goto parsed;
+ encoding = g_strdup_printf( "CP%s", p );
+ g_free( p );
+ }
+ else
+ encoding = g_strndup( startptr, endptr - startptr );
+
+parsed:
+ prolog_expected = FALSE;
+}
+
+/******************************************************************************
+ * resolve_logpaths
+ *
+ * replace logpath_names structures
+ */
+static void resolve_logpaths()
+{
+ GList *paths = NULL;
+ GList *path_item;
+ struct logpath *logpath = NULL;
+
+ for( path_item = logpaths; path_item; path_item = path_item->next )
+ {
+ struct logpath_names *names = path_item->data;
+ GList *item;
+
+ g_free( logpath );
+ logpath = g_malloc( sizeof(struct logpath) );
+
+ /* find source */
+ for( item = sources; item; item = item->next )
+ {
+ struct source *s = item->data;
+ if( strcmp( s->name, names->source ) == 0 )
+ break;
+ }
+ if( !item )
+ {
+ ERR( "Undefined source \"%s\" in log path\n", names->source );
+ continue;
+ }
+ logpath->source = item->data;
+
+ /* find destination */
+ for( item = destinations; item; item = item->next )
+ {
+ struct destination *d = item->data;
+ if( strcmp( d->name, names->destination ) == 0 )
+ break;
+ }
+ if( !item )
+ {
+ ERR( "Undefined destination \"%s\" in log path\n", names->destination );
+ continue;
+ }
+ logpath->destination = item->data;
+
+ /* find filter */
+ if( !names->filter )
+ logpath->filter = NULL;
+ else
+ {
+ for( item = filters; item; item = item->next )
+ {
+ struct filter *f = item->data;
+ if( strcmp( f->name, names->filter ) == 0 )
+ break;
+ }
+ if( item )
+ logpath->filter = item->data;
+ else
+ logpath->filter = NULL;
+ }
+ /* add item to paths */
+ paths = g_list_append( paths, logpath );
+ logpath = NULL;
+ }
+
+ /* free list */
+ for( path_item = logpaths; path_item; path_item = path_item->next )
+ {
+ struct logpath_names *names = path_item->data;
+ g_free( names->source );
+ g_free( names->destination );
+ if( names->filter ) g_free( names->filter );
+ g_free( names );
+ }
+ g_list_free( logpaths );
+
+ /* set new list */
+ logpaths = paths;
+}
+
+/******************************************************************************
+ * dump_configuration
+ */
+static void dump_configuration()
+{
+# ifdef HAVE_DEBUG
+
+ GList *item;
+
+ TRACE( "Sources:\n" );
+ for( item = sources; item; item = item->next )
+ {
+ struct source *s = item->data;
+ TRACE( "\tname=%s\ttype=%s\tinterface=%d:%d:%d:%d\tport=%d\n",
+ s->name,
+ (s->type == ST_INTERNAL)? "internal" : ((s->type == ST_UDP)? "udp" : "undefined"),
+ s->udp.sin_addr.S_un.S_un_b.s_b1, s->udp.sin_addr.S_un.S_un_b.s_b2,
+ s->udp.sin_addr.S_un.S_un_b.s_b3, s->udp.sin_addr.S_un.S_un_b.s_b4,
+ ntohs( s->udp.sin_port ) );
+ }
+ TRACE( "Destinations:\n" );
+ for( item = destinations; item; item = item->next )
+ {
+ struct destination *d = item->data;
+ TRACE( "\tname=%s\tfile=%s\n"
+ "\t\trotate=%s size=%d backlogs=%d ifempty=%s\n"
+ "\t\tolddir=%s compresscmd=%s\n",
+ d->name, d->file,
+ (d->rotate == RP_DAILY)? "daily"
+ : (d->rotate == RP_WEEKLY)? "weekly"
+ : (d->rotate == RP_MONTHLY)? "monthly"
+ : "undefined",
+ d->size, d->backlogs, d->ifempty? "yes" : "no",
+ d->olddir? d->olddir : "NULL",
+ d->compresscmd? d->compresscmd : "NULL" );
+ }
+ TRACE( "Filters:\n" );
+ for( item = filters; item; item = item->next )
+ {
+ struct filter *f = item->data;
+ int i;
+ TRACE( "\tname=%s\n", f->name );
+ TRACE( "\tfacilities:\n" );
+ for( i = 0; i < LOG_NFACILITIES; i++ )
+ if( f->facilities[i] )
+ TRACE( "\t\t%s\n", get_facility_name( i ) );
+ TRACE( "\tpriorities:\n" );
+ for( i = 0; i < 8; i++ )
+ if( f->priorities[i] )
+ TRACE( "\t\t%s\n", get_priority_name( i ) );
+ }
+ TRACE( "Log paths:\n" );
+ for( item = logpaths; item; item = item->next )
+ {
+ struct logpath *p = item->data;
+ TRACE( "\tsource=%s\tfilter=%s\tdestination=%s\n",
+ p->source->name, p->filter? p->filter->name : "NULL", p->destination->name );
+ }
+ TRACE( "Purge directories:\n" );
+ for( item = purger_dirs; item; item = item->next )
+ {
+ struct purger_dir *p = item->data;
+ TRACE( "\tdirectory=%s\tkeep_days=%d\n", p->directory, p->keep_days );
+ }
+ TRACE( "Options:\n" );
+ TRACE( "\tuse_dns=%d\n", (int) use_dns );
+ TRACE( "\tsource_encoding=%s\n", source_encoding? source_encoding : "NULL" );
+ TRACE( "\tdestination_encoding=%s\n", destination_encoding? destination_encoding : "NULL" );
+ TRACE( "\tmark_interval=%d\n", mark_interval );
+ TRACE( "\tmark_message=%s\n", mark_message );
+ TRACE( "\thold=%d\n", hold );
+ TRACE( "\tlogdir=%s\n", logdir? logdir : "NULL" );
+
+# endif /* HAVE_DEBUG */
+}
+
+/******************************************************************************
+ * read_configuration
+ */
+gboolean read_configuration()
+{
+ gboolean ret = FALSE;
+ GMarkupParser parser = {
+ xml_start_element,
+ xml_end_element,
+ NULL,
+ xml_passthrough,
+ NULL
+ };
+ gchar *pathname;
+ FILE *fd = NULL;
+ GMarkupParseContext *ctx = NULL;
+ char buffer[256];
+ GError *error = NULL;
+
+ TRACE_ENTER( "\n" );
+
+ if( '\\' == syslog_conf_dir[0] || '/' == syslog_conf_dir[0] || ':' == syslog_conf_dir[1] )
+ /* absolute path */
+ pathname = g_build_filename( syslog_conf_dir, "syslog.conf", NULL );
+ else
+ /* relative path */
+ pathname = g_build_filename( g_path_get_dirname( __argv[0] ),
+ syslog_conf_dir, "syslog.conf", NULL );
+ fd = fopen( pathname, "r" );
+ if( !fd )
+ {
+ ERR( "Cannot open configuration file %s: %s\n", pathname, strerror(errno) );
+ goto done;
+ }
+
+ ctx = g_markup_parse_context_new( &parser, 0, NULL, NULL );
+ if( !ctx )
+ {
+ ERR( "Failed g_markup_parse_context_new\n" );
+ goto done;
+ }
+
+ while( fgets( buffer, sizeof(buffer), fd ) )
+ {
+ gchar *encoded = NULL;
+ gchar *parser_input;
+ gboolean r;
+
+ /* wrapper for Glib's XML parser:
+ determine encoding in xml_passthrough and convert data fed to the parser to UTF-8 */
+ if( encoding )
+ {
+ encoded = g_convert( buffer, -1, "UTF-8", encoding, NULL, NULL, &error );
+ if( !encoded )
+ goto done;
+
+ parser_input = encoded;
+ }
+ else
+ parser_input = buffer;
+
+ r = g_markup_parse_context_parse( ctx, parser_input, strlen( parser_input ), &error );
+ if( encoded ) g_free( encoded );
+ if( !r )
+ goto done;
+ }
+ if( !feof( fd ) )
+ {
+ ERR( "Cannot read configuration file %s: %s\n", pathname, strerror(errno) );
+ goto done;
+ }
+
+ resolve_logpaths();
+ dump_configuration();
+ if( !logdir )
+ {
+ ERR( "logdir is not defined\n" );
+ goto done;
+ }
+ ret = TRUE;
+
+done:
+ if( error )
+ {
+ gchar *locale_msg = g_locale_from_utf8( error->message, -1, NULL, NULL, NULL );
+ if( locale_msg )
+ {
+ ERR( "%s\n", locale_msg );
+ g_free( locale_msg );
+ }
+ g_error_free( error );
+ }
+ if( ctx ) g_markup_parse_context_free( ctx );
+ if( fd ) fclose( fd );
+ g_free( pathname );
+
+ TRACE_LEAVE( "done; ret=%d\n", (int) ret );
+ return ret;
+}
diff --git a/daemon/fifo.c b/daemon/fifo.c
new file mode 100644
index 0000000..2754885
--- /dev/null
+++ b/daemon/fifo.c
@@ -0,0 +1,105 @@
+/*
+ * fifo.c - syslogd implementation for windows, simple and fast queue
+ *
+ * Created by Alexander Yaworsky
+ *
+ * This replacement eliminates strange page faults which were
+ * with glib's 2.6.3 queues.
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <glib.h>
+#include <syslog.h>
+#include <syslogd.h>
+
+/******************************************************************************
+ * fifo_create
+ *
+ * Allocate and initialize fifo structure. Add an empty item to the fifo.
+ */
+struct fifo* fifo_create()
+{
+ struct fifo *ret = g_malloc( sizeof(struct fifo) );
+ struct fifo_item *guard_item= g_malloc( sizeof(struct fifo_item) );
+ ret->first = guard_item;
+ ret->last = guard_item;
+ guard_item->next = NULL;
+ guard_item->payload = NULL;
+ return ret;
+}
+
+/******************************************************************************
+ * fifo_destroy
+ *
+ * Delete all items and free fifo structure.
+ */
+void fifo_destroy( struct fifo* queue )
+{
+ struct fifo_item *item;
+
+ while( (item = queue->first) != NULL )
+ {
+ queue->first = item->next;
+ g_free( item );
+ }
+ g_free( queue );
+}
+
+/******************************************************************************
+ * fifo_push
+ *
+ * Add item to queue.
+ */
+void fifo_push( struct fifo* queue, void* data )
+{
+ struct fifo_item *item = g_malloc( sizeof(struct fifo_item) );
+ item->next = NULL;
+ item->payload = data;
+ queue->last->next = item;
+ queue->last = item;
+}
+
+/******************************************************************************
+ * fifo_pop
+ *
+ * Extract item from queue.
+ * Consider four possible conditions:
+ * 1) next == NULL, payload == NULL: there is the only one empty item
+ * in the queue; leave it and return NULL
+ * 2) next == NULL, payload != NULL: leave item and return its payload;
+ * set item's payload NULL
+ * 3) next != NULL, payload == NULL: queue was empty when a new item was
+ * added; free this item and go to the next one
+ * 4) next != NULL, payload != NULL: free item and return its payload
+ */
+void* fifo_pop( struct fifo* queue )
+{
+ for(;;)
+ {
+ struct fifo_item *item = queue->first;
+ struct fifo_item *next = InterlockedExchangePointer( &item->next, NULL );
+ void *data = item->payload;
+ item->payload = NULL;
+ if( next )
+ {
+ queue->first = next;
+ g_free( item );
+ }
+ if( data )
+ return data;
+ if( !next )
+ return NULL;
+ }
+}
diff --git a/daemon/listener.c b/daemon/listener.c
new file mode 100644
index 0000000..32c6297
--- /dev/null
+++ b/daemon/listener.c
@@ -0,0 +1,335 @@
+/*
+ * listener.c - syslogd implementation for windows, listener for UDP
+ * and "internal" sources
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <stdio.h>
+#include <winsock2.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+static SOCKET *socket_array = NULL;
+static int socket_count = 0;
+
+static struct source **source_references = NULL;
+
+static HANDLE *event_array = NULL;
+static int event_count = 0;
+
+/* message data */
+static unsigned max_datagram_size = 1024;
+static gchar *datagram_buffer = NULL;
+static struct raw_message message;
+
+static CRITICAL_SECTION cs_internal_message;
+static HANDLE internal_message_accepted = NULL;
+static gchar internal_message_buffer[ 1024 ];
+
+/******************************************************************************
+ * init_listener
+ *
+ * create sockets and synchronization objects including ones for "internal"
+ * source
+ */
+gboolean init_listener()
+{
+ gboolean ret = FALSE;
+ unsigned n;
+ GList *item;
+ int i;
+ struct source *internal_src;
+
+ TRACE_ENTER( "\n" );
+
+ /* create critical section and event for the access serialization to internal message buffer
+ */
+ InitializeCriticalSection( &cs_internal_message );
+ internal_message_accepted = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( !internal_message_accepted )
+ {
+ ERR( "Cannot create event; error %lu\n", GetLastError() );
+ goto done;
+ }
+
+ /* allocate memory for sockets and events;
+ * the number of sockets is not greater than number of sources
+ */
+ n = g_list_length( sources );
+ socket_array = g_malloc( n * sizeof(SOCKET) );
+
+ /* number of source references is greater by one because of inclusion the event
+ * for "internal" source
+ * FIXME: how about multiple internal sources?
+ */
+ source_references = g_malloc( (n + 1) * sizeof(struct source*) );
+
+ /* number of events is greater by two because of inclusion the event
+ * for "internal" source and the service_stop_event
+ */
+ event_array = g_malloc( (n + 2) * sizeof(HANDLE) );
+
+ /* create sockets */
+ for( item = sources; item; item = item->next )
+ {
+ struct source *src = item->data;
+ SOCKET sock;
+ unsigned dgram_size;
+ int size;
+
+ if( src->type == ST_INTERNAL )
+ {
+ internal_src = src;
+ continue;
+ }
+
+ if( src->type != ST_UDP )
+ continue;
+
+ sock = socket( AF_INET, SOCK_DGRAM, 0 );
+ if( INVALID_SOCKET == sock )
+ {
+ ERR( "socket() error %lu\n", WSAGetLastError() );
+ goto done;
+ }
+
+ if( bind( sock, (struct sockaddr*) &src->udp, sizeof(src->udp) ) )
+ {
+ ERR( "bind() error %lu\n", WSAGetLastError() );
+ closesocket( sock );
+ goto done;
+ }
+
+ size = sizeof(dgram_size);
+ if( getsockopt( sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*) &dgram_size, &size ) )
+ {
+ ERR( "getsockopt( SO_MAX_MSG_SIZE ) error %lu\n", WSAGetLastError() );
+ closesocket( sock );
+ goto done;
+ }
+ TRACE( "datagram size for %d.%d.%d.%d:%d is %u\n",
+ src->udp.sin_addr.S_un.S_un_b.s_b1, src->udp.sin_addr.S_un.S_un_b.s_b2,
+ src->udp.sin_addr.S_un.S_un_b.s_b3, src->udp.sin_addr.S_un.S_un_b.s_b4,
+ ntohs( src->udp.sin_port ), dgram_size );
+ if( dgram_size > max_datagram_size )
+ max_datagram_size = dgram_size;
+
+ source_references[ socket_count ] = src;
+ socket_array[ socket_count++ ] = sock;
+ }
+ source_references[ socket_count ] = internal_src;
+
+ /* create events;
+ * service_stop_event is added to the array
+ */
+ while( event_count <= socket_count )
+ {
+ HANDLE evt = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( !evt )
+ {
+ ERR( "Cannot create event; error %lu\n", GetLastError() );
+ goto done;
+ }
+ event_array[ event_count++ ] = evt;
+ }
+ event_array[ event_count++ ] = service_stop_event;
+
+ /* bind events to sockets */
+ for( i = 0; i < socket_count; i++ )
+ {
+ if( WSAEventSelect( socket_array[ i ], event_array[ i ], FD_READ ) )
+ {
+ ERR( "WSAEventSelect() error %lu\n", WSAGetLastError() );
+ goto done;
+ }
+ }
+
+ /* allocate datagram buffer */
+ datagram_buffer = g_malloc( max_datagram_size );
+
+ ret = TRUE;
+
+done:
+ if( !ret )
+ fini_listener();
+
+ TRACE_LEAVE( "done; socket_count=%d, event_count=%d, max_datagram_size=%d, ret=%d\n",
+ socket_count, event_count, max_datagram_size, (int) ret );
+ return ret;
+}
+
+/******************************************************************************
+ * fini_listener
+ */
+void fini_listener()
+{
+ int i;
+
+ TRACE_ENTER( "\n" );
+
+ for( i = 0; i < socket_count; i++ )
+ closesocket( socket_array[ i ] );
+ g_free( socket_array );
+ socket_array = NULL;
+ socket_count = 0;
+
+ g_free( source_references );
+ source_references = NULL;
+
+ /* note that the last event is the service_stop_event
+ * and should not be destroyed
+ */
+ for( i = 0; i < event_count - 1; i++ )
+ CloseHandle( event_array[ i ] );
+ g_free( event_array );
+ event_array = NULL;
+ event_count = 0;
+
+ g_free( datagram_buffer );
+ datagram_buffer = NULL;
+
+ if( internal_message_accepted )
+ {
+ CloseHandle( internal_message_accepted );
+ internal_message_accepted = NULL;
+ }
+ DeleteCriticalSection( &cs_internal_message );
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * listener
+ *
+ * wait for a message; generate mark message;
+ * allocates a new raw_message structure and assigns its pointer to *msg
+ */
+enum listener_status listener( struct raw_message** msg )
+{
+ enum listener_status ret = LSNR_ERROR;
+ DWORD t, w;
+ int r;
+ int addrlen;
+
+ TRACE_ENTER( "\n" );
+
+ for(;;)
+ {
+ if( !mark_interval )
+ t = INFINITE;
+ else
+ t = mark_interval * 1000;
+ w = WaitForMultipleObjects( event_count, event_array, FALSE, t );
+ if( WAIT_TIMEOUT == w )
+ {
+ /* issue mark message */
+ log_internal( LOG_NOTICE, "%s", mark_message );
+ continue;
+ }
+ if( WAIT_FAILED == w )
+ {
+ ERR( "Wait error %lu\n", GetLastError() );
+ goto done;
+ }
+ if( w >= event_count )
+ {
+ ERR( "Unknown wait error\n" );
+ goto done;
+ }
+ if( w == event_count - 1 )
+ {
+ /* shut down */
+ ret = LSNR_SHUTDOWN;
+ goto done;
+ }
+ if( w == event_count - 2 )
+ {
+ /* got "internal" message */
+ message.source = source_references[ socket_count ];
+ if( !message.source )
+ {
+ /* internal source is not defined, cannot handle message */
+ SetEvent( internal_message_accepted );
+ continue;
+ }
+ message.msg = g_strdup( internal_message_buffer );
+ SetEvent( internal_message_accepted );
+ memset( &message.sender_addr, 0, sizeof(message.sender_addr) );
+ goto alloc_msg;
+ }
+ /* got UDP message, read it */
+ addrlen = sizeof(message.sender_addr);
+ r = recvfrom( socket_array[ w ], datagram_buffer, max_datagram_size,
+ 0, (struct sockaddr*) &message.sender_addr, &addrlen );
+ if( r < 0 )
+ {
+ ERR( "recvfrom() error %lu\n", WSAGetLastError() );
+ goto done;
+ }
+ if( !r )
+ continue;
+
+ message.msg = g_strndup( datagram_buffer, r );
+ message.source = source_references[ w ];
+
+ alloc_msg:
+ *msg = g_malloc( sizeof(struct raw_message) );
+ memcpy( *msg, &message, sizeof(struct raw_message) );
+
+ ret = LSNR_GOT_MESSAGE;
+ goto done;
+ }
+
+done:
+ TRACE_LEAVE( "done; ret=%d\n", (int) ret );
+ return ret;
+}
+
+/******************************************************************************
+ * log_internal
+ *
+ * generate internal log message
+ */
+void log_internal( int pri, char* fmt, ... )
+{
+ va_list args;
+ SYSTEMTIME stm;
+ int len;
+ char *p;
+
+ TRACE_ENTER( "\n" );
+ EnterCriticalSection( &cs_internal_message );
+
+ GetLocalTime( &stm );
+ len = sprintf( internal_message_buffer, "<%d>%s %2d %02d:%02d:%02d %s syslog: ",
+ LOG_SYSLOG | pri,
+ str_month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond,
+ local_hostname );
+ va_start( args, fmt );
+ vsnprintf( internal_message_buffer + len, sizeof(internal_message_buffer) - len, fmt, args );
+ va_end( args );
+ p = strchr( internal_message_buffer, '\n' );
+ if( p )
+ *p = 0;
+ p = strchr( internal_message_buffer, '\r' );
+ if( p )
+ *p = 0;
+
+ SetEvent( event_array[ event_count - 2 ] );
+ LeaveCriticalSection( &cs_internal_message );
+ TRACE_LEAVE( "done\n" );
+}
diff --git a/daemon/logrotate.c b/daemon/logrotate.c
new file mode 100644
index 0000000..5b0932a
--- /dev/null
+++ b/daemon/logrotate.c
@@ -0,0 +1,264 @@
+/*
+ * logrotate.c - syslogd implementation for windows, log rotation
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+/******************************************************************************
+ * expand_options
+ *
+ * Substitute $PATHNAME and $FILENAME
+ */
+static gchar* expand_options( const gchar* compressoptions, const gchar* pathname )
+{
+ gchar *filename = g_path_get_basename( pathname );
+ GString *str = g_string_new( compressoptions );
+ gchar *substr_pathname;
+ gchar *substr_filename;
+
+ TRACE_ENTER( "\n" );
+ do {
+ substr_pathname = strstr( str->str, "$PATHNAME" );
+ substr_filename = strstr( str->str, "$FILENAME" );
+
+ if( substr_pathname )
+ {
+ int pos = substr_pathname - str->str;
+ g_string_erase( str, pos, 9 );
+ g_string_insert( str, pos, pathname );
+ }
+
+ if( substr_filename )
+ {
+ int pos = substr_filename - str->str;
+ g_string_erase( str, pos, 9 );
+ g_string_insert( str, pos, filename );
+ }
+ } while( substr_pathname || substr_filename );
+ g_free( filename );
+ TRACE_LEAVE( "return %s\n", str->str );
+ return g_string_free( str, FALSE );
+}
+
+/******************************************************************************
+ * compress_backlog
+ *
+ * Run external compression program.
+ * Return TRUE if all right.
+ */
+static gboolean compress_backlog( const gchar* compresscmd, const gchar* compressoptions,
+ const gchar* backlog )
+{
+ gboolean ret = FALSE;
+ char command_pathname[ MAX_PATH ];
+ LPTSTR command_filename;
+ gchar *options;
+ gchar *command_line;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ DWORD exit_code;
+
+ TRACE_ENTER( "\n" );
+
+ if( !SearchPath( NULL, compresscmd, ".exe",
+ sizeof(command_pathname), command_pathname, &command_filename ) )
+ {
+ ERR( "Command %s not found\n", compresscmd );
+ TRACE_LEAVE( "error\n" );
+ return FALSE;
+ }
+
+ options = expand_options( compressoptions, backlog );
+ command_line = g_strconcat( command_pathname, " ", options, NULL );
+
+ memset( &si, 0, sizeof(si ) );
+ si.cb = sizeof(si);
+ TRACE_2( "command_line=%s\n", command_line );
+ if( !CreateProcess( NULL, command_line, NULL, NULL, FALSE,
+ DETACHED_PROCESS, NULL, NULL, &si, &pi ) )
+ ERR( "Cannot create process %s; error %lu\n", command_line, GetLastError() );
+ else
+ {
+ CloseHandle( pi.hThread );
+ TRACE_2( "waiting for %s\n", command_line );
+ WaitForSingleObject( pi.hProcess, INFINITE );
+ if( !GetExitCodeProcess( pi.hProcess, &exit_code ) )
+ exit_code = 1;
+ CloseHandle( pi.hProcess );
+ ret = 0 == exit_code;
+ }
+
+ g_free( command_line );
+ g_free( options );
+
+ TRACE_LEAVE( "ret=%d\n", ret );
+ return ret;
+}
+
+/******************************************************************************
+ * do_rotate
+ *
+ * All criteria for rotation are met, just do it.
+ */
+static void do_rotate( const gchar* pathname, struct destination* destination )
+{
+ int i;
+ gchar *dest_pathname;
+ gchar *backlog;
+
+ TRACE_ENTER( "\n" );
+
+ /* construct destination pathname for backlogs */
+ if( destination->olddir )
+ {
+ gchar *filename = g_path_get_basename( pathname );
+
+ if( g_path_is_absolute( destination->olddir ) )
+ dest_pathname = g_build_filename( destination->olddir, filename, NULL );
+ else
+ {
+ gchar *prefix = g_path_get_dirname( __argv[0] );
+ dest_pathname = g_build_filename( prefix, destination->olddir, filename, NULL );
+ g_free( prefix );
+ }
+ g_free( filename );
+ }
+ else
+ dest_pathname = g_strdup( pathname );
+
+ /* remove earliest backlog */
+ backlog = g_strdup_printf( "%s.%d", dest_pathname, destination->backlogs );
+ DeleteFile( backlog );
+ g_free( backlog );
+
+ /* rotate backlogs */
+ for( i = destination->backlogs; i > 1; i-- )
+ {
+ gchar *old_backlog = g_strdup_printf( "%s.%d", dest_pathname, i - 1 );
+ gchar *new_backlog = g_strdup_printf( "%s.%d", dest_pathname, i );
+ TRACE_2( "Move %s to %s\n", old_backlog, new_backlog );
+ if( !MoveFile( old_backlog, new_backlog ) )
+ {
+ /* most possible that old backlog file does not exist */
+ TRACE_2( "Can't move %s to %s; error %lu\n", old_backlog, new_backlog, GetLastError() );
+ }
+ g_free( old_backlog );
+ g_free( new_backlog );
+ }
+
+ backlog = g_strconcat( dest_pathname, ".1", NULL );
+
+ /* move current log */
+ TRACE_2( "Move %s to %s\n", pathname, backlog );
+ if( !MoveFile( pathname, backlog ) )
+ ERR( "Can't move %s to %s; error %lu\n", pathname, backlog, GetLastError() );
+
+ /* compress new backlog */
+ if( destination->compresscmd )
+ {
+ if( compress_backlog( destination->compresscmd, destination->compressoptions, backlog ) )
+ /* remove original uncompressed file */
+ DeleteFile( backlog );
+ }
+
+ g_free( backlog );
+ g_free( dest_pathname );
+
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * rotate_logfile
+ */
+void rotate_logfile( const gchar* pathname, struct destination* destination )
+{
+ struct stat fst;
+ gboolean rotated = FALSE;
+ time_t current_time;
+ struct tm *tm;
+
+ TRACE_ENTER( "pathname=%s\n", pathname );
+
+ if( 0 == destination->backlogs
+ || (RP_UNDEFINED == destination->rotate && 0 == destination->size) )
+ {
+ TRACE_LEAVE( "no conditions for rotation\n" );
+ return;
+ }
+
+ if( stat( pathname, &fst ) )
+ {
+ /* most possible that file does not exist */
+ TRACE_2( "stat(%s) error %s\n", pathname, strerror( errno ) );
+ goto done;
+ }
+
+ if( destination->size )
+ {
+ if( fst.st_size > destination->size )
+ {
+ do_rotate( pathname, destination );
+ rotated = TRUE;
+ goto done;
+ }
+ TRACE_2( "checked size: file=%d, max=%d\n", fst.st_size, destination->size );
+ }
+
+ current_time = time(NULL);
+ tm = gmtime( &current_time );
+ TRACE_2( "checking time: creation=%d, modification=%d\n", fst.st_ctime, fst.st_mtime );
+ switch( destination->rotate )
+ {
+ case RP_DAILY:
+ if( fst.st_mtime - (fst.st_ctime - fst.st_ctime % (24 * 3600)) > 24 * 3600 )
+ break;
+ goto done;
+ case RP_WEEKLY:
+ if( 0 == tm->tm_wday && fst.st_mtime - fst.st_ctime > 24 * 3600 )
+ break;
+ goto done;
+ case RP_MONTHLY:
+ if( 1 == tm->tm_mday && fst.st_mtime - fst.st_ctime > 24 * 3600 )
+ break;
+ goto done;
+ default:
+ goto done;
+ }
+
+ if( fst.st_size || destination->ifempty )
+ {
+ do_rotate( pathname, destination );
+ rotated = TRUE;
+ }
+ else
+ {
+ TRACE_2( "do not rotate empty file\n" );
+ }
+
+done:
+ TRACE_LEAVE( "done; %s\n", rotated? "rotated" : "not rotated" );
+}
diff --git a/daemon/main.c b/daemon/main.c
new file mode 100644
index 0000000..eb77bb7
--- /dev/null
+++ b/daemon/main.c
@@ -0,0 +1,660 @@
+/*
+ * main.c - syslogd implementation for windows, main function
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <winsock2.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+HANDLE service_stop_event;
+
+char local_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ];
+
+int verbosity_level = 0;
+
+static char *service_name = "syslogd";
+static char *service_display_name = "syslogd";
+static char *service_stop_event_name = "syslogd-stop";
+
+static OSVERSIONINFO vi;
+
+static SERVICE_STATUS_HANDLE hss;
+static SERVICE_STATUS sstatus;
+
+/******************************************************************************
+ * display message
+ */
+void display_message( FILE* fd, char* file, int line, const char* func, char* fmt, ... )
+{
+ va_list args;
+ char formatstr[512];
+
+ snprintf( formatstr, sizeof(formatstr), "%08lX:%s:%d:%s: %s",
+ GetCurrentThreadId(), file, line, func, fmt );
+ va_start( args, fmt );
+ vfprintf( fd, formatstr, args );
+ va_end( args );
+ fflush( fd );
+}
+
+/******************************************************************************
+ * shutdown_service
+ *
+ * set service stop event
+ * wait until event exists
+ */
+static void shutdown_service( gboolean quiet )
+{
+ HANDLE he;
+ BOOL ret;
+
+ for(;;)
+ {
+ he = OpenEvent( EVENT_MODIFY_STATE, FALSE, service_stop_event_name );
+ if( !he )
+ {
+ if( !quiet )
+ ERR( "cannot open event; error %lu\n", GetLastError() );
+ return;
+ }
+ TRACE( "setting stop event\n" );
+ ret = SetEvent( he );
+ CloseHandle( he );
+ if( !ret )
+ {
+ if( !quiet )
+ {
+ ERR( "cannot set event; error %lu\n", GetLastError() );
+ return;
+ }
+ }
+ quiet = TRUE;
+ Sleep(0);
+ }
+}
+
+/******************************************************************************
+ * service control handler
+ */
+static void WINAPI ServiceControlHandler( DWORD Control )
+{
+ switch( Control )
+ {
+ case SERVICE_CONTROL_STOP:
+ sstatus.dwCurrentState = SERVICE_STOP_PENDING;
+ sstatus.dwCheckPoint = 0;
+ SetEvent( service_stop_event );
+ break;
+ case SERVICE_CONTROL_INTERROGATE: /* return status immediately */
+ break;
+ }
+ SetServiceStatus( hss, &sstatus );
+}
+
+/******************************************************************************
+ * service main
+ */
+static void WINAPI winnt_ServiceMain( DWORD Argc, LPTSTR* Argv )
+{
+ hss = RegisterServiceCtrlHandler( PACKAGE_NAME,
+ (LPHANDLER_FUNCTION)ServiceControlHandler );
+ if( !hss )
+ return;
+
+ service_stop_event = CreateEvent( NULL, TRUE, FALSE, NULL );
+ if( !service_stop_event )
+ return;
+
+ sstatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ sstatus.dwCurrentState = SERVICE_RUNNING;
+ sstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ sstatus.dwWin32ExitCode = NO_ERROR;
+ sstatus.dwCheckPoint = 0;
+ sstatus.dwWaitHint = 1000;
+ SetServiceStatus( hss, &sstatus );
+
+ syslogd_main();
+
+ sstatus.dwWin32ExitCode = 0;
+ sstatus.dwServiceSpecificExitCode = 0;
+ sstatus.dwCurrentState = SERVICE_STOPPED;
+ sstatus.dwCheckPoint = 0;
+ SetServiceStatus( hss, &sstatus );
+
+ CloseHandle( service_stop_event );
+}
+
+static int win9x_ServiceMain()
+{
+ service_stop_event = CreateEvent( NULL, TRUE, FALSE, service_stop_event_name );
+ if( !service_stop_event )
+ {
+ ERR( "Cannot create event %s; error %lu\n", service_stop_event_name, GetLastError() );
+ return 1;
+ }
+ if( ERROR_ALREADY_EXISTS == GetLastError() )
+ {
+ CloseHandle( service_stop_event );
+ ERR( "Service is already running\n" );
+ return 1;
+ }
+
+ syslogd_main();
+
+ CloseHandle( service_stop_event );
+ return 0;
+}
+
+/******************************************************************************
+ * open system Run key
+ */
+static BOOL open_run_key( PHKEY hk )
+{
+ static char runkey[] = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
+
+ TRACE_ENTER( "\n" );
+ if( RegOpenKey( HKEY_LOCAL_MACHINE, runkey, hk ) )
+ {
+ ERR( "Cannot open registry key %s; error %lu\n", runkey, GetLastError() );
+ return FALSE;
+ }
+ TRACE_LEAVE( "done\n" );
+ return TRUE;
+}
+
+/******************************************************************************
+ * service installer
+ */
+static BOOL win9x_InstallService( char* command_line )
+{
+ BOOL ret;
+ HKEY hk;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ TRACE_ENTER( "command_line=%s\n", command_line );
+ ret = open_run_key( &hk );
+ if( !ret )
+ {
+ TRACE_LEAVE( "done\n" );
+ return FALSE;
+ }
+
+ if( RegSetValueEx( hk, service_name, 0, REG_SZ, command_line, strlen( command_line ) + 1 ) )
+ {
+ ERR( "Cannot set registry value; error %lu\n", GetLastError() );
+ ret = FALSE;
+ }
+ RegCloseKey( hk );
+ if( !ret )
+ return FALSE;
+
+ memset( &si, 0, sizeof(si ) );
+ si.cb = sizeof(si);
+ ret = CreateProcess( NULL, command_line, NULL, NULL, FALSE, DETACHED_PROCESS,
+ NULL, NULL, &si, &pi );
+ if( !ret )
+ {
+ ERR( "Cannot create process; error %lu\n", GetLastError() );
+ return FALSE;
+ }
+
+ CloseHandle( pi.hThread );
+ CloseHandle( pi.hProcess );
+ TRACE_LEAVE( "done\n" );
+ return TRUE;
+}
+
+static BOOL winnt_InstallService( char* command_line )
+{
+ SC_HANDLE hscm, hsvc;
+ BOOL ret;
+
+ TRACE_ENTER( "command_line=%s\n", command_line );
+ hscm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+ if( !hscm )
+ {
+ ERR( "Cannot open service control manager; error %lu\n", GetLastError() );
+ return FALSE;
+ }
+ hsvc = CreateService( hscm, service_name, service_display_name,
+ SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
+ command_line, NULL, NULL, NULL, NULL, NULL );
+ if( !hsvc )
+ {
+ ERR( "Cannot create service; error %lu\n", GetLastError() );
+ ret = FALSE;
+ }
+ else
+ {
+ ret = StartService( hsvc, 0, NULL );
+ if( !ret )
+ ERR( "Cannot start service; error %lu\n", GetLastError() );
+ CloseServiceHandle( hsvc );
+ }
+ CloseServiceHandle( hscm );
+ TRACE_LEAVE( "done\n" );
+ return ret;
+}
+
+static BOOL install_service( char* priority )
+{
+ BOOL ret;
+ char command_line[ MAX_PATH + 64 ];
+
+ TRACE_ENTER( "\n" );
+ if( __argv[0][1] == ':' )
+ command_line[0] = 0;
+ else
+ {
+ TRACE( "argv[0] contains no absolute path\n" );
+ GetCurrentDirectory( MAX_PATH, command_line );
+ strcat( command_line, "\\" );
+ }
+ strcat( command_line, __argv[0] );
+ strcat( command_line, " --service" );
+ if( priority )
+ {
+ strcat( command_line, " --priority " );
+ strcat( command_line, priority );
+ }
+
+ if( VER_PLATFORM_WIN32_NT == vi.dwPlatformId )
+ ret = winnt_InstallService( command_line );
+ else
+ ret = win9x_InstallService( command_line );
+
+ TRACE_LEAVE( "done; ret=%d\n", ret );
+ return ret;
+}
+
+/******************************************************************************
+ * service uninstaller
+ */
+static void win9x_RemoveService()
+{
+ HKEY hk;
+
+ TRACE_ENTER( "\n" );
+ if( !open_run_key( &hk ) )
+ return;
+ RegDeleteValue( hk, service_name );
+ RegCloseKey( hk );
+ TRACE_LEAVE( "done\n" );
+}
+
+static void winnt_RemoveService()
+{
+ SC_HANDLE hscm, hsvc;
+ int i;
+
+ TRACE_ENTER( "\n" );
+ hscm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+ if( !hscm )
+ {
+ ERR( "Cannot open service control manager; error %lu\n", GetLastError() );
+ return;
+ }
+
+ hsvc = OpenService( hscm, service_name, SERVICE_ALL_ACCESS );
+ if( !hsvc )
+ {
+ ERR( "Cannot open service %s; error %lu\n", service_name, GetLastError() );
+ CloseServiceHandle( hscm );
+ return;
+ }
+
+ ControlService( hsvc, SERVICE_CONTROL_STOP, &sstatus );
+
+ for( i = 0; i < 10; i++ )
+ {
+ Sleep( 100 );
+ if( !QueryServiceStatus( hsvc, &sstatus ) )
+ {
+ TRACE( "Cannot query service status; error %lu\n", GetLastError() );
+ break;
+ }
+ if( SERVICE_STOPPED == sstatus.dwCurrentState )
+ break;
+ }
+ if( !DeleteService( hsvc ) )
+ TRACE( "Cannot delete service; error %lu\n", GetLastError() );
+
+ CloseServiceHandle( hsvc );
+ CloseServiceHandle( hscm );
+ TRACE_LEAVE( "done\n" );
+}
+
+static void remove_service()
+{
+ TRACE_ENTER( "\n" );
+ if( VER_PLATFORM_WIN32_NT == vi.dwPlatformId )
+ winnt_RemoveService();
+ else
+ win9x_RemoveService();
+
+ /* always try to shutdown because it's possible that daemon
+ is running not as service under winNT */
+ shutdown_service( TRUE );
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * exception_handler
+ */
+LONG WINAPI exception_handler( PEXCEPTION_POINTERS ei )
+{
+ DWORD i;
+ BYTE *addr;
+
+ fprintf( stderr,
+ "*********************************\n"
+ "thread id:\t\t%lX\n"
+ "ExceptionCode:\t\t%lX\n"
+ "ExceptionFlags:\t\t%lX\n"
+ "ExceptionRecord:\t%p\n"
+ "ExceptionAddress:\t%p\n"
+ "NumberParameters:\t%lx\n"
+ "ExceptionInformation:\n",
+ GetCurrentThreadId(),
+ ei->ExceptionRecord->ExceptionCode,
+ ei->ExceptionRecord->ExceptionFlags,
+ ei->ExceptionRecord->ExceptionRecord,
+ ei->ExceptionRecord->ExceptionAddress,
+ ei->ExceptionRecord->NumberParameters );
+
+ for( i = 0; i < ei->ExceptionRecord->NumberParameters; i++ )
+ fprintf( stderr, "\t%lX\n", ei->ExceptionRecord->ExceptionInformation[i] );
+
+ fprintf( stderr,
+ "ContextFlags=%lX\n"
+ "CS=%lX DS=%lX ES=%lX SS=%lX FS=%lX GS=%lX\n"
+ "EAX=%lX EBX=%lX ECX=%lX EDX=%lX ESI=%lX EDI=%lX\n"
+ "EBP=%lX ESP=%lX EIP=%lX EFLAGS=%lX\n"
+ "Stack Dump:\n",
+ ei->ContextRecord->ContextFlags,
+ ei->ContextRecord->SegCs,
+ ei->ContextRecord->SegDs,
+ ei->ContextRecord->SegEs,
+ ei->ContextRecord->SegSs,
+ ei->ContextRecord->SegFs,
+ ei->ContextRecord->SegGs,
+ ei->ContextRecord->Eax,
+ ei->ContextRecord->Ebx,
+ ei->ContextRecord->Ecx,
+ ei->ContextRecord->Edx,
+ ei->ContextRecord->Esi,
+ ei->ContextRecord->Edi,
+ ei->ContextRecord->Ebp,
+ ei->ContextRecord->Esp,
+ ei->ContextRecord->Eip,
+ ei->ContextRecord->EFlags );
+
+ addr = (LPBYTE) (ei->ContextRecord->Esp);
+ while( !IsBadReadPtr( addr, 16 ) )
+ {
+ int skip = ((DWORD) addr) & 15;
+ BYTE *keep_addr = addr;
+
+ fprintf( stderr, "%08lX", ((DWORD) addr) & ~15 );
+ for( i = 0; i < skip; i++ )
+ fprintf( stderr, " " );
+ for( ; i < 8; i++ )
+ fprintf( stderr, " %02X", *addr++ );
+ if( i == 8 )
+ fputc( '-', stderr );
+ for( ; i < 16; i++ )
+ fprintf( stderr, "%02X ", *addr++ );
+ fputc( ' ', stderr );
+ addr = keep_addr;
+ for( i = 0; i < skip; i++ )
+ fputc( ' ', stderr );
+ for( ; i < 16; i++ )
+ {
+ BYTE b = *addr++;
+
+ if( b < 32 ) b = ' ';
+ fputc( b, stderr );
+ }
+ fputc( '\n', stderr );
+ }
+ fprintf( stderr, "*********************************\n" );
+ fflush( stderr );
+ ExitProcess(2);
+}
+
+/******************************************************************************
+ * main
+ *
+ * Parse command line
+ */
+int main( int argc, char* argv[] )
+{
+ static int help_flag = 0;
+ static int version_flag = 0;
+ static int install_flag = 0;
+ static int remove_flag = 0;
+ static int service_flag = 0;
+ static int shutdown_flag = 0;
+ char *instance_name = NULL;
+ char *priority = NULL;
+ int getopt_failure = 0;
+ WSADATA wsd;
+ DWORD size;
+
+ SetUnhandledExceptionFilter( exception_handler );
+
+ if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) )
+ {
+ ERR( "Cannot initialize winsock; error %lu\n", WSAGetLastError() );
+ return 1;
+ }
+
+ /* get local host name */
+ size = sizeof(local_hostname);
+ if( !GetComputerName( local_hostname, &size ) )
+ {
+ ERR( "Cannot get computer name; error %lu\n", GetLastError() );
+ return 1;
+ }
+ TRACE( "local host name=%s\n", local_hostname );
+
+ /* get windows version */
+ vi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
+ if( ! GetVersionEx( &vi ) )
+ {
+ ERR( "Cannot get windows version; error %lu\n", GetLastError() );
+ return 1;
+ }
+
+ for(;;)
+ {
+ static struct option long_options[] =
+ {
+ { "verbose", no_argument, NULL, 'v'},
+ { "help", no_argument, &help_flag, 1 },
+ { "version", no_argument, &version_flag, 1 },
+ { "install", no_argument, &install_flag, 1 },
+ { "remove", no_argument, &remove_flag, 1 },
+ { "service", no_argument, &service_flag, 1 },
+ { "shutdown", no_argument, &shutdown_flag,1 },
+ { "instance", required_argument, NULL, 'I'},
+ { "priority", required_argument, NULL, 'p'},
+ { 0, 0, 0, 0 }
+ };
+ int option_char;
+ int option_index;
+
+ option_char = getopt_long( argc, argv, "vhirsI:p:",
+ long_options, &option_index );
+ if( -1 == option_char )
+ break;
+
+ switch( option_char )
+ {
+ case 0:
+ break;
+
+ case 'v':
+ verbosity_level++;
+ break;
+
+ case 'h':
+ help_flag = 1;
+ break;
+
+ case 'i':
+ install_flag = 1;
+ break;
+
+ case 'r':
+ remove_flag = 1;
+ break;
+
+ case 'I':
+ instance_name = optarg;
+ break;
+
+ case 'p':
+ {
+ DWORD pclass;
+ if( strcmpi( optarg, "normal" ) == 0 )
+ {
+ priority = optarg;
+ pclass = NORMAL_PRIORITY_CLASS;
+ }
+ else if( strcmpi( optarg, "high" ) == 0 )
+ {
+ priority = optarg;
+ pclass = ABOVE_NORMAL_PRIORITY_CLASS;
+ }
+ else if( strcmpi( optarg, "highest" ) == 0 )
+ {
+ priority = optarg;
+ pclass = HIGH_PRIORITY_CLASS;
+ }
+ else
+ {
+ ERR( "Invalid priority %s; must be 'normal', 'high' or 'highest'\n", optarg );
+ break;
+ }
+ SetPriorityClass( GetCurrentProcess(), pclass );
+ break;
+ }
+ case '?':
+ /* getopt_long already printed an error message. */
+ getopt_failure++;
+ break;
+
+ default:
+ abort();
+ }
+ }
+ if( getopt_failure )
+ return 1;
+
+ /* handle flags in order of priority */
+ /* at first, check instance name */
+ if( instance_name )
+ {
+ service_name = g_strconcat( service_name, "_", instance_name, NULL );
+ service_display_name = g_strconcat( service_display_name, "_", instance_name, NULL );
+ service_stop_event_name = g_strconcat( service_stop_event_name, "_", instance_name, NULL );
+ }
+ /* check service flag */
+ if( service_flag )
+ {
+ if( VER_PLATFORM_WIN32_NT == vi.dwPlatformId )
+ {
+ /* run as service under windows NT */
+ static SERVICE_TABLE_ENTRY service_table[] = {
+ { "", (LPSERVICE_MAIN_FUNCTION) winnt_ServiceMain },
+ { NULL, NULL }
+ };
+
+ if( !StartServiceCtrlDispatcher( service_table ) )
+ {
+ ERR( "Cannot start service control dispatcher; error %lu\n", GetLastError() );
+ return 1;
+ }
+ return 0;
+ }
+ else
+ {
+ /* run as service under windows 9x */
+ FreeConsole();
+ return win9x_ServiceMain();
+ }
+ }
+
+ /* print version */
+ if( version_flag )
+ {
+ printf( "%s %s\n", PACKAGE_NAME, PACKAGE_VERSION );
+ return 0;
+ }
+
+ /* print help */
+ if( help_flag )
+ {
+ printf( "Usage: syslogd [OPTION]\n" );
+ printf( "This is syslog daemon for windows.\n" );
+ printf( "\n" );
+ printf( "-v, --verbose\tbe verbose; each occurence of this parameter\n"
+ "\t\tincreases verbosity\n" );
+ printf( "-i, --install\tinstall and start service\n" );
+ printf( "-r, --remove\tstop and remove service\n" );
+ printf( "-s, --shutdown\tsend shutdown signal to the daemon\n" );
+ printf( "-I, --instance\tset instance name in the case of multiple daemons\n" );
+ printf( "-p, --priority\tset priority class; value may be 'normal' (default),\n"
+ "\t\t\t'high', or 'highest'\n" );
+ printf( "-h, --help\tdisplay this message\n" );
+ printf( "--version\tdisplay version information\n" );
+ return 0;
+ }
+
+ /* install/remove/shutdown */
+ if( remove_flag )
+ {
+ if( install_flag || shutdown_flag )
+ ERR( "Remove option has priority over install/shutdown\n" );
+ remove_service();
+ return 0;
+ }
+ if( install_flag )
+ {
+ if( shutdown_flag )
+ ERR( "Install option has priority over shutdown\n" );
+ install_service( priority );
+ return 0;
+ }
+ if( shutdown_flag )
+ {
+ shutdown_service( FALSE );
+ return 0;
+ }
+
+ /* run as ordinary console application */
+ return win9x_ServiceMain();
+}
diff --git a/daemon/names.c b/daemon/names.c
new file mode 100644
index 0000000..7c31d21
--- /dev/null
+++ b/daemon/names.c
@@ -0,0 +1,63 @@
+/*
+ * names.c - syslogd implementation for windows, syslog priority
+ * and facility names
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <stdio.h>
+#define SYSLOG_NAMES
+#include <syslog.h>
+
+char* get_priority_name( int pri )
+{
+ static char *names[] = {
+ "emerg",
+ "alert",
+ "crit",
+ "error",
+ "warning",
+ "notice",
+ "info",
+ "debug"
+ };
+ return names[ pri ];
+}
+
+char* get_facility_name( int fac )
+{
+ static char *names[] = {
+ "kern",
+ "user",
+ "mail",
+ "daemon",
+ "auth",
+ "syslog",
+ "lpr",
+ "news",
+ "uucp",
+ "cron",
+ "authpriv",
+ "ftp",
+ "local0",
+ "local1",
+ "local2",
+ "local3",
+ "local4",
+ "local5",
+ "local6",
+ "local7"
+ };
+ return names[ fac ];
+}
diff --git a/daemon/pathnames.c b/daemon/pathnames.c
new file mode 100644
index 0000000..932c854
--- /dev/null
+++ b/daemon/pathnames.c
@@ -0,0 +1,139 @@
+/*
+ * pathnames.c - syslogd implementation for windows, miscellaneous functions
+ * operating with file names and paths
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+/******************************************************************************
+ * make_absolute_log_pathname
+ *
+ * Make absolute pathname inside logdir.
+ */
+gchar* make_absolute_log_pathname( char* path_appendix )
+{
+ gchar *path, *ret;
+
+ if( logdir && g_path_is_absolute( logdir ) )
+ path = g_strdup( logdir );
+ else
+ {
+ gchar *dir = g_path_get_dirname( __argv[0] );
+ path = g_build_filename( dir, logdir, NULL );
+ g_free( dir );
+ }
+ ret = g_build_filename( path, path_appendix, NULL );
+ g_free( path );
+ return ret;
+}
+
+/******************************************************************************
+ * create_directories
+ *
+ * Create all the directories in pathname.
+ */
+void create_directories( gchar* pathname )
+{
+ gchar *p;
+ gchar saved;
+
+ TRACE_ENTER( "%s\n", pathname );
+ p = (gchar*) g_path_skip_root( pathname );
+ if( !p )
+ p = pathname;
+ for(;;)
+ {
+ while( *p != '\0' && *p != '/' && *p != '\\' )
+ p++;
+ if( *p == '\0' )
+ break;
+ saved = *p;
+ *p = '\0';
+ CreateDirectory( pathname, NULL );
+ *p++ = saved;
+ }
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * normalize_pathname
+ *
+ * Remove . and .. from pathname.
+ * Return NULL if pathname is invalid.
+ */
+gchar* normalize_pathname( const gchar* pathname )
+{
+ gchar *ret = g_strdup( pathname );
+ gchar *first_element, *current_element, *next_element;
+
+ TRACE_ENTER( "%s\n", pathname );
+
+ first_element = (gchar*) g_path_skip_root( ret );
+ if( !first_element )
+ first_element = ret;
+
+ for( current_element = first_element; *current_element != '\0'; )
+ {
+ next_element = current_element;
+ while( *next_element != '\0' && *next_element != '/' && *next_element != '\\' )
+ next_element++;
+ if( *next_element != '\0' )
+ next_element++; /* skip separator */
+
+ if( current_element[0] != '.' )
+ current_element = next_element;
+ else
+ {
+ if( current_element[1] == '/'
+ || current_element[1] == '\\' || current_element[1] == '\0' )
+ /* /./ remove current element */
+ memmove( current_element, next_element, strlen( next_element ) + 1 );
+ else if( current_element[1] == '.'
+ && (current_element[2] == '/'
+ || current_element[2] == '\\' || current_element[2] == '\0') )
+ {
+ /* /../ find and remove previous element */
+ gchar *prev_element = current_element - 2;
+ if( prev_element < first_element )
+ {
+ ERR( "Invalid pathname: %s\n", pathname );
+ g_free( ret );
+ ret = NULL;
+ break;
+ }
+ while( prev_element > first_element && *prev_element != '/' && *prev_element != '\\' )
+ prev_element--;
+ if( prev_element > first_element )
+ prev_element++; /* skip separator */
+ memmove( prev_element, next_element, strlen( next_element ) + 1 );
+ current_element = prev_element;
+ }
+ else
+ current_element = next_element;
+ }
+ }
+ TRACE_LEAVE( "ret=%s\n", ret? ret : "NULL" );
+ return ret;
+}
diff --git a/daemon/purger.c b/daemon/purger.c
new file mode 100644
index 0000000..43be168
--- /dev/null
+++ b/daemon/purger.c
@@ -0,0 +1,155 @@
+/*
+ * purger.c - syslogd implementation for windows, log dir purger
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <stdio.h>
+#include <windows.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+static HANDLE purger_semaphore = NULL;
+
+/******************************************************************************
+ * recursive_purge
+ *
+ * auxiliary function for purge_log_dirs
+ */
+static void recursive_purge( gchar* pathname, LPFILETIME min_time )
+{
+ gchar *new_pathname;
+ HANDLE find_handle;
+ WIN32_FIND_DATA find_data;
+
+ TRACE_ENTER( "%s\n", pathname );
+
+ new_pathname = g_build_filename( pathname, "*", NULL );
+ find_handle = FindFirstFile( new_pathname, &find_data );
+ if( INVALID_HANDLE_VALUE == find_handle )
+ {
+ TRACE_2( "FindFirstFile(%s) error %lu\n", new_pathname, GetLastError() );
+ g_free( new_pathname );
+ TRACE_LEAVE( "it seems the path does not exist\n" );
+ return;
+ }
+ g_free( new_pathname );
+ do {
+ if( (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '\0')
+ ||
+ (find_data.cFileName[0] == '.'
+ && find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0') )
+ continue;
+
+ new_pathname = g_build_filename( pathname, find_data.cFileName, NULL );
+ if( find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+ recursive_purge( new_pathname, min_time );
+ if( RemoveDirectory( new_pathname ) )
+ {
+ TRACE( "removed %s\n", new_pathname );
+ }
+ else
+ {
+ TRACE( "failed to remove %s\n", new_pathname );
+ }
+ }
+ else
+ {
+ if( CompareFileTime( min_time, &find_data.ftLastWriteTime ) > 0 )
+ {
+ if( DeleteFile( new_pathname ) )
+ {
+ TRACE( "deleted %s\n", new_pathname );
+ }
+ else
+ {
+ TRACE( "failed to delete %s\n", new_pathname );
+ }
+ }
+ }
+ g_free( new_pathname );
+ } while( FindNextFile( find_handle, &find_data ) );
+ FindClose( find_handle );
+
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * purge_log_dirs
+ */
+void purge_log_dirs()
+{
+ GList *item;
+
+ TRACE_ENTER( "\n" );
+ if( WaitForSingleObject( purger_semaphore, 0 ) != WAIT_OBJECT_0 )
+ {
+ TRACE_LEAVE( "done; another thread is active\n" );
+ return;
+ }
+ for( item = purger_dirs; item; item = item->next )
+ {
+ struct purger_dir *p = item->data;
+ gchar *pathname = make_absolute_log_pathname( p->directory );
+ FILETIME min_time;
+ long long min_time_64;
+
+ GetSystemTimeAsFileTime( &min_time );
+ min_time_64 = (((long long) min_time.dwHighDateTime) << 32) + (long long) min_time.dwLowDateTime;
+ min_time_64 -= ((long long) p->keep_days) * ((long long) (24 * 3600)) * (long long) 10000000;
+ min_time.dwHighDateTime = min_time_64 >> 32;
+ min_time.dwLowDateTime = min_time_64;
+
+ recursive_purge( pathname, &min_time );
+ g_free( pathname );
+ }
+ ReleaseSemaphore( purger_semaphore, 1, NULL );
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * init_purger
+ */
+gboolean init_purger()
+{
+ gboolean ret = FALSE;
+
+ TRACE_ENTER( "\n" );
+
+ purger_semaphore = CreateSemaphore( NULL, 1, 1, NULL );
+ if( !purger_semaphore )
+ {
+ ERR( "Cannot create semaphore; error %lu\n", GetLastError() );
+ goto done;
+ }
+ ret = TRUE;
+
+done:
+ TRACE_LEAVE( "done; ret=%d\n", ret );
+ return ret;
+}
+
+/******************************************************************************
+ * fini_purger
+ */
+void fini_purger()
+{
+ TRACE_ENTER( "\n" );
+ if( purger_semaphore ) CloseHandle( purger_semaphore );
+ TRACE_LEAVE( "done\n" );
+}
diff --git a/daemon/syslogd.c b/daemon/syslogd.c
new file mode 100644
index 0000000..59ede6d
--- /dev/null
+++ b/daemon/syslogd.c
@@ -0,0 +1,600 @@
+/*
+ * syslogd.c - syslogd implementation for windows
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <ctype.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <winsock2.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+static struct fifo *raw_message_queue = NULL;
+static HANDLE fifo_semaphore = NULL;
+
+/* cache for source hostnames */
+struct hostname
+{
+ struct sockaddr_in addr;
+ gchar *host;
+ time_t top_age; /* zero prevents aging */
+};
+static GList *hostnames = NULL;
+
+#define HOSTNAME_LIFETIME 60 /* seconds */
+/* FIXME: is this value correct? maybe we should make it configurable? */
+
+static GIConv conversion_descriptor = (GIConv) -1;
+
+char *str_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+/******************************************************************************
+ * refrence_message
+ *
+ * increment reference count
+ */
+void reference_message( struct message* msg )
+{
+ TRACE_ENTER( "message=%p\n", msg );
+ InterlockedIncrement( &msg->refcount );
+}
+
+/******************************************************************************
+ * release_message
+ *
+ * decrement reference count and destroy message structure if it becomes zero
+ */
+void release_message( struct message* msg )
+{
+ TRACE_ENTER( "message=%p\n", msg );
+ if( InterlockedDecrement( &msg->refcount ) )
+ {
+ TRACE_LEAVE( "done; still referenced\n" );
+ return;
+ }
+ g_free( msg->sender );
+ g_free( msg->timestamp );
+ g_free( msg->hostname );
+ g_free( msg->program );
+ g_free( msg->message );
+ g_free( msg );
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * convert_message_encoding
+ */
+static void convert_message_encoding( struct message* msg )
+{
+ gchar *converted_msg;
+
+ TRACE_ENTER( "message=%p\n", msg );
+
+ if( conversion_descriptor == (GIConv) -1 )
+ {
+ TRACE_LEAVE( "nothing to do\n" );
+ return;
+ }
+
+ converted_msg = g_convert_with_iconv( msg->message, -1,
+ conversion_descriptor, NULL, NULL, NULL );
+ if( !converted_msg )
+ {
+ TRACE_LEAVE( "conversion error\n" );
+ return;
+ }
+
+ g_free( msg->message );
+ msg->message = converted_msg;
+
+ TRACE_LEAVE( "done; %s\n", msg->message );
+}
+
+/******************************************************************************
+ * filter_message
+ *
+ * return: TRUE - accepted message, FALSE - rejected message
+ */
+static gboolean filter_message( struct message* msg, struct filter* filter )
+{
+ gboolean ret = FALSE;
+
+ TRACE_ENTER( "message=%p, filter=%s\n", msg, filter? filter->name : "NULL" );
+
+ if( !filter )
+ goto passed;
+
+ /* check facility */
+ if( !filter->facilities[ msg->facility ] )
+ goto done;
+
+ /* check priority */
+ if( !filter->priorities[ msg->priority ] )
+ goto done;
+
+passed:
+ /* message is passed through filter */
+ ret = TRUE;
+
+done:
+ TRACE_LEAVE( "done; ret=%d\n", ret );
+ return ret;
+}
+
+/******************************************************************************
+ * mux_message
+ *
+ * filter and multiplex message to destinations;
+ * release message
+ */
+static void mux_message( struct message* msg )
+{
+ GList *item;
+
+ TRACE_ENTER( "message=%p\n", msg );
+
+ convert_message_encoding( msg );
+
+ for( item = logpaths; item; item = item->next )
+ {
+ struct logpath *logpath = item->data;
+
+ if( logpath->source != msg->source )
+ continue;
+
+ if( !filter_message( msg, logpath->filter ) )
+ continue;
+
+ write_message( msg, logpath->destination );
+ }
+
+ release_message( msg );
+
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * get_hostname
+ *
+ * convert addr to string and return it
+ */
+static gchar* get_hostname( struct sockaddr_in* addr )
+{
+ gchar *ret;
+ time_t current_time;
+ GList *item;
+ struct hostname *new_hostname;
+
+ TRACE_ENTER( "%d.%d.%d.%d\n",
+ addr->sin_addr.S_un.S_un_b.s_b1, addr->sin_addr.S_un.S_un_b.s_b2,
+ addr->sin_addr.S_un.S_un_b.s_b3, addr->sin_addr.S_un.S_un_b.s_b4 );
+
+ current_time = time(NULL);
+
+ /* at first, try to find a cached entry */
+ item = hostnames;
+ while( item )
+ {
+ struct hostname *h = item->data;
+ if( h->top_age && h->top_age < current_time )
+ {
+ GList *next_item = item->next;
+
+ TRACE_2( "delete old entry %s\n", h->host );
+ g_free( h->host );
+ g_free( h );
+ hostnames = g_list_delete_link( hostnames, item );
+ item = next_item;
+ continue;
+ }
+ if( h->addr.sin_addr.S_un.S_addr == addr->sin_addr.S_un.S_addr )
+ {
+ /* found in cache */
+ ret = g_strdup( h->host );
+ /* move entry to the beginning of the list */
+ item->data = hostnames->data;
+ hostnames->data = h;
+ TRACE_LEAVE( "done; found cached entry: %s\n", ret );
+ return ret;
+ }
+ item = item->next;
+ }
+ /* add new entry */
+ new_hostname = g_malloc( sizeof(struct hostname) );
+ memcpy( &new_hostname->addr, addr, sizeof(struct sockaddr_in) );
+ if( use_dns )
+ {
+ struct hostent *he = gethostbyaddr( (char*) &addr->sin_addr.S_un.S_addr,
+ sizeof(addr->sin_addr.S_un.S_addr), AF_INET );
+ new_hostname->top_age = time(NULL) + HOSTNAME_LIFETIME;
+ if( !he )
+ goto use_addr;
+ new_hostname->host = g_strdup( he->h_name );
+ }
+ else
+ {
+ new_hostname->top_age = 0;
+use_addr:
+ new_hostname->host = g_malloc( 16 );
+ sprintf( new_hostname->host, "%d.%d.%d.%d",
+ addr->sin_addr.S_un.S_un_b.s_b1, addr->sin_addr.S_un.S_un_b.s_b2,
+ addr->sin_addr.S_un.S_un_b.s_b3, addr->sin_addr.S_un.S_un_b.s_b4 );
+ }
+ hostnames = g_list_prepend( hostnames, new_hostname );
+ ret = g_strdup( new_hostname->host );
+ TRACE_LEAVE( "done; ret=%s\n", ret );
+ return ret;
+}
+
+/******************************************************************************
+ * free_hostnames
+ */
+static void free_hostnames()
+{
+ GList *item;
+ for( item = hostnames; item; item = item->next )
+ {
+ struct hostname *h = item->data;
+ g_free( h->host );
+ g_free( h );
+ }
+ g_list_free( hostnames );
+ hostnames = NULL;
+}
+
+/******************************************************************************
+ * parse_PRI
+ *
+ * parse PRI part of message;
+ * set facility and priority;
+ * return pointer to the next char after PRI
+ */
+static gchar* parse_PRI( gchar* msg, int* facility, int* priority )
+{
+ int i, pri;
+
+ TRACE_ENTER( "\n" );
+
+ if( *msg != '<' )
+ goto no_pri;
+
+ pri = 0;
+ for( i = 1; i < 5; i++ )
+ {
+ if( msg[ i ] == '>' )
+ break;
+ if( !isdigit( msg[ i ] ) )
+ goto no_pri;
+ pri = (pri * 10) + (msg[ i ] - '0');
+ }
+ if( i == 5 )
+ goto no_pri;
+ if( i == 1 )
+ /* FIXME: is this right? or "<>" is an unidentifiable PRI? */
+ goto no_pri;
+
+ msg += i + 1;
+
+ if( pri > LOG_NFACILITIES * 8 + 7 )
+ {
+ TRACE_2( "Unidentifiable PRI %d\n", pri );
+ *facility = LOG_USER;
+ *priority = LOG_NOTICE;
+ }
+ else
+ {
+ TRACE_2( "PRI=%d\n", pri );
+ *facility = LOG_FAC( pri );
+ *priority = LOG_PRI( pri );
+ }
+
+ TRACE_LEAVE( "done; facility=%d, priority=%d\n", *facility, *priority );
+ return msg;
+
+no_pri:
+ *facility = LOG_USER;
+ *priority = LOG_NOTICE;
+ TRACE_LEAVE( "done; message contains no PRI\n" );
+ return msg;
+}
+
+/******************************************************************************
+ * parse_timestamp
+ *
+ * parse TIMESTAMP part of message;
+ * allocate string;
+ * return pointer to the next char after TIMESTAMP
+ */
+static gchar* parse_timestamp( gchar* msg, gchar** timestamp )
+{
+ int i;
+ SYSTEMTIME stm;
+
+ TRACE_ENTER( "\n" );
+
+ for( i = 0; i < 12; i++ )
+ if( strncasecmp( msg, str_month[ i ], 3 ) == 0 )
+ break;
+ if( i == 12 )
+ goto no_timestamp;
+ stm.wMonth = i + 1;
+
+ if( msg[3] != ' ' )
+ goto no_timestamp;
+
+ if( msg[4] != ' ' )
+ {
+ if( (!isdigit( msg[4] )) || (!isdigit( msg[5] )) )
+ goto no_timestamp;
+ stm.wDay = (msg[4] - '0') * 10 + (msg[5] - '0');
+ }
+ else
+ {
+ if( !isdigit( msg[5] ) )
+ goto no_timestamp;
+ stm.wDay = msg[5] - '0';
+ }
+
+ if( msg[6] != ' ' )
+ goto no_timestamp;
+
+ if( (!isdigit( msg[7] )) || (!isdigit( msg[8] )) || msg[9] != ':' )
+ goto no_timestamp;
+ stm.wHour = (msg[7] - '0') * 10 + (msg[8] - '0');
+
+ if( (!isdigit( msg[10] )) || (!isdigit( msg[11] )) || msg[12] != ':' )
+ goto no_timestamp;
+ stm.wMinute = (msg[10] - '0') * 10 + (msg[11] - '0');
+
+ if( (!isdigit( msg[13] )) || (!isdigit( msg[14] )) || msg[15] != ' ' )
+ goto no_timestamp;
+ stm.wSecond = (msg[13] - '0') * 10 + (msg[14] - '0');
+ msg += 16;
+ goto done;
+
+no_timestamp:
+ TRACE_2( "no timestamp\n" );
+ GetLocalTime( &stm );
+
+done:
+ *timestamp = g_strdup_printf( "%s %2d %02d:%02d:%02d",
+ str_month[ stm.wMonth - 1 ],
+ stm.wDay, stm.wHour, stm.wMinute, stm.wSecond );
+ TRACE_LEAVE( "done\n" );
+ return msg;
+}
+
+/******************************************************************************
+ * parse_raw_message
+ *
+ * parse raw message and make a new message;
+ * destroy raw message
+ */
+static struct message* parse_raw_message( struct raw_message* raw_msg )
+{
+ struct message* msg;
+ gchar *current_part, *next_part;
+
+ TRACE_ENTER( "raw message=%p\n", raw_msg );
+
+ /* allocate and initialize message structure */
+ msg = g_malloc( sizeof(struct message) );
+ msg->refcount = 1;
+ msg->source = raw_msg->source;
+
+ /* get sender's hostname */
+ msg->sender = get_hostname( &raw_msg->sender_addr );
+
+ current_part = raw_msg->msg;
+ next_part = parse_PRI( current_part, &msg->facility, &msg->priority );
+
+ current_part = next_part;
+ next_part = parse_timestamp( current_part, &msg->timestamp );
+ if( next_part == current_part )
+ {
+ /* no valid timestamp */
+ TRACE_2( "no valid timestamp: msg=%s\n", current_part );
+ msg->hostname = g_strdup( msg->sender );
+ msg->message = g_strdup( current_part );
+ }
+ else
+ {
+ /* have valid timestamp, go ahead */
+ current_part = next_part;
+ while( isalnum( *next_part ) || *next_part == '-' || *next_part == '.' )
+ next_part++;
+ if( *next_part != ' ' )
+ {
+ /* invalid hostname */
+ msg->hostname = g_strdup( msg->sender );
+ msg->message = g_strdup( current_part );
+ TRACE_2( "invalid hostname; set sender (%s); msg=%s\n", msg->hostname, msg->message );
+ }
+ else
+ {
+ msg->hostname = g_strndup( current_part, next_part - current_part );
+ while( *next_part == ' ' && *next_part != 0 )
+ next_part++;
+ msg->message = g_strdup( next_part );
+ TRACE_2( "hostname=%s; msg=%s\n", msg->hostname, msg->message );
+ }
+ }
+
+ /* try to find program name */
+ current_part = msg->message;
+ next_part = current_part;
+ while( *next_part != ' ' && *next_part != ':' && *next_part != '[' && *next_part != 0 )
+ next_part++;
+ if( *next_part == ' ' || *next_part == 0 )
+ msg->program = g_strdup("");
+ else
+ msg->program = g_strndup( current_part, next_part - current_part );
+
+ /* destroy raw message */
+ g_free( raw_msg->msg );
+ g_free( raw_msg );
+
+ TRACE_LEAVE( "done; message=%p\n", msg );
+ return msg;
+}
+
+/******************************************************************************
+ * message_processor
+ *
+ * main function; extract raw messages from queue and parse them
+ */
+static unsigned __stdcall message_processor( void* arg )
+{
+ HANDLE wait_handles[2] = { fifo_semaphore, service_stop_event };
+
+ TRACE_ENTER( "\n" );
+ for(;;)
+ {
+ DWORD w;
+ struct raw_message *raw_msg;
+
+ w = WaitForMultipleObjects( 2, wait_handles, FALSE, INFINITE );
+ if( WAIT_OBJECT_0 + 1 == w )
+ /* shutdown */
+ break;
+ if( w != WAIT_OBJECT_0 )
+ {
+ ERR( "WaitForMultipleObjects() error %lu\n", GetLastError() );
+ SetEvent( service_stop_event );
+ break;
+ }
+ /* extract raw message from queue */
+ raw_msg = fifo_pop( raw_message_queue );
+
+ TRACE_2( "got message %p from queue\n", raw_msg );
+
+ mux_message( parse_raw_message( raw_msg ) );
+ }
+ TRACE_LEAVE( "done\n" );
+ return 0;
+}
+
+/******************************************************************************
+ * main syslogd function
+ *
+ * global initialization; invoke listener
+ */
+void syslogd_main()
+{
+ HANDLE message_processor_thread = NULL;
+ unsigned tid;
+
+ TRACE_ENTER( "\n" );
+
+ SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
+
+ if( !read_configuration() )
+ goto done;
+
+ if( !init_purger() )
+ goto done;
+
+ if( !init_listener() )
+ goto done;
+
+ purge_log_dirs();
+
+ if( source_encoding && destination_encoding )
+ {
+ conversion_descriptor = g_iconv_open( destination_encoding, source_encoding );
+ if( conversion_descriptor == (GIConv) -1 )
+ {
+ ERR( "Cannot convert messages from %s to %s\n",
+ source_encoding, destination_encoding );
+ }
+ }
+
+ /* create message queue and semaphore;
+ * avoid using Glib's asynchronous queues and threading support
+ * because synchronization capabilities are very limited;
+ */
+ raw_message_queue = fifo_create();
+ fifo_semaphore = CreateSemaphore( NULL, 0, LONG_MAX, NULL );
+ if( !fifo_semaphore )
+ {
+ ERR( "Cannot create semaphore; error %lu\n", GetLastError() );
+ goto done;
+ }
+
+ message_processor_thread = (HANDLE) _beginthreadex( NULL, 0, message_processor, NULL, 0, &tid );
+ if( !message_processor_thread )
+ {
+ ERR( "Cannot create thread; error %lu\n", GetLastError() );
+ goto done;
+ }
+
+ /* get messages from the listener */
+ for(;;)
+ {
+ struct raw_message *raw_msg;
+
+ switch( listener( &raw_msg ) )
+ {
+ case LSNR_ERROR:
+ case LSNR_SHUTDOWN:
+ goto done;
+
+ case LSNR_GOT_MESSAGE:
+ TRACE_2( "got message from %d.%d.%d.%d; source name %s; ptr=%p: %s\n",
+ raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b1,
+ raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b2,
+ raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b3,
+ raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b4,
+ raw_msg->source->name, raw_msg, raw_msg->msg );
+ /* add raw message to queue */
+ fifo_push( raw_message_queue, raw_msg );
+ ReleaseSemaphore( fifo_semaphore, 1, NULL );
+ break;
+ }
+ }
+
+done:
+ /* signal to all possibly running threads */
+ SetEvent( service_stop_event );
+
+ if( message_processor_thread )
+ {
+ /* wait for message processor shutdown */
+ WaitForSingleObject( message_processor_thread, INFINITE );
+ CloseHandle( message_processor_thread );
+ }
+
+ fini_writer();
+ fini_purger();
+ free_hostnames();
+
+ if( raw_message_queue ) fifo_destroy( raw_message_queue );
+ if( conversion_descriptor != (GIConv) -1 )
+ g_iconv_close( conversion_descriptor );
+
+ fini_listener();
+
+ if( fifo_semaphore ) CloseHandle( fifo_semaphore );
+
+ TRACE_LEAVE( "done\n" );
+}
diff --git a/daemon/syslogd.h b/daemon/syslogd.h
new file mode 100644
index 0000000..65dc042
--- /dev/null
+++ b/daemon/syslogd.h
@@ -0,0 +1,204 @@
+/*
+ * syslogd.h - syslogd implementation for windows, common definitions
+ *
+ * Created by Alexander Yaworsky
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+extern int verbosity_level;
+extern void display_message( FILE* fd, char* file, int line, const char* func, char* fmt, ... );
+
+#ifdef HAVE_DEBUG
+# define DO_TRACE( verbosity, fmt... ) \
+ do { \
+ if( verbosity <= verbosity_level ) \
+ display_message( stderr, __FILE__, __LINE__, __FUNCTION__, fmt ); \
+ } while(0)
+# define TRACE_2( fmt... ) DO_TRACE( 2, fmt )
+# define TRACE( fmt... ) DO_TRACE( 1, fmt )
+#else
+# define TRACE_2( fmt... )
+# define TRACE( fmt... )
+#endif
+#define TRACE_ENTER TRACE_2
+#define TRACE_LEAVE TRACE_2
+#define ERR( fmt... ) display_message( stderr, __FILE__, __LINE__, __FUNCTION__, fmt )
+
+extern HANDLE service_stop_event;
+
+extern char local_hostname[];
+
+extern char *str_month[];
+
+extern void syslogd_main();
+
+/* options and their default values */
+extern gboolean use_dns;
+extern gchar *source_encoding;
+extern gchar *destination_encoding;
+extern int mark_interval;
+extern gchar *mark_message;
+extern int hold;
+extern gchar *logdir;
+
+/* sources, destinations, filters and logpaths */
+enum source_type
+{
+ ST_UNDEFINED,
+ ST_INTERNAL,
+ ST_UDP
+};
+
+struct source
+{
+ gchar *name;
+ enum source_type type;
+ struct sockaddr_in udp;
+};
+
+enum rotation_period
+{
+ RP_UNDEFINED = 0,
+ RP_INVALID,
+ RP_DAILY,
+ RP_WEEKLY,
+ RP_MONTHLY
+};
+
+struct destination
+{
+ gchar *name;
+ gchar *file;
+ GList *file_writers;
+ CRITICAL_SECTION cs_file_writers;
+ enum rotation_period rotate;
+ int size;
+ int backlogs;
+ gboolean ifempty;
+ gchar *olddir;
+ gchar *compresscmd;
+ gchar *compressoptions;
+};
+
+struct filter
+{
+ gchar *name;
+ gboolean facilities[ LOG_NFACILITIES ];
+ gboolean priorities[ 8 ];
+};
+
+struct logpath
+{
+ struct source *source;
+ struct filter *filter;
+ struct destination *destination;
+};
+
+extern GList *sources;
+extern GList *destinations;
+extern GList *filters;
+extern GList *logpaths;
+
+extern gboolean read_configuration();
+
+/* queue */
+struct fifo_item
+{
+ struct fifo_item *next; /* queue is a single-linked list */
+ void *payload;
+};
+
+struct fifo
+{
+ struct fifo_item *first; /* first pushed item */
+ struct fifo_item *last; /* last pushed item */
+};
+
+extern struct fifo* fifo_create();
+extern void fifo_destroy( struct fifo* queue );
+extern void fifo_push( struct fifo* queue, void* data );
+extern void* fifo_pop( struct fifo* queue );
+
+/* listener */
+enum listener_status
+{
+ LSNR_ERROR,
+ LSNR_SHUTDOWN,
+ LSNR_GOT_MESSAGE
+};
+
+struct raw_message
+{
+ gchar *msg;
+ struct sockaddr_in sender_addr;
+ struct source *source;
+};
+
+extern gboolean init_listener();
+extern void fini_listener();
+extern enum listener_status listener( struct raw_message** msg );
+extern void log_internal( int pri, char* fmt, ... );
+
+/* message */
+struct message
+{
+ LONG refcount;
+ struct source *source;
+ gchar *sender;
+ int facility;
+ int priority;
+ gchar *timestamp;
+ gchar *hostname;
+ gchar *program;
+ gchar *message;
+};
+
+extern void reference_message( struct message* msg );
+extern void release_message( struct message* msg );
+
+/* writer */
+extern void fini_writer();
+extern void write_message( struct message* msg, struct destination* destination );
+
+/* logrotate */
+extern void rotate_logfile( const gchar* pathname, struct destination* destination );
+
+/* purger */
+struct purger_dir
+{
+ gchar *directory;
+ int keep_days;
+};
+
+extern GList *purger_dirs;
+
+extern gboolean init_purger();
+extern void fini_purger();
+extern void purge_log_dirs();
+
+/* pathnames */
+extern gchar* make_absolute_log_pathname( char* path_appendix );
+extern void create_directories( gchar* pathname );
+extern gchar* normalize_pathname( const gchar* pathname );
+
+/* workaround for syslog.h: included with SYSLOG_NAMES in names.c */
+typedef struct _code {
+ char *c_name;
+ int c_val;
+} CODE;
+
+extern CODE prioritynames[];
+extern CODE facilitynames[];
+
+extern char* get_priority_name( int pri );
+extern char* get_facility_name( int pri );
diff --git a/daemon/writer.c b/daemon/writer.c
new file mode 100644
index 0000000..ea4f915
--- /dev/null
+++ b/daemon/writer.c
@@ -0,0 +1,498 @@
+/*
+ * writer.c - syslogd implementation for windows, message writer
+ *
+ * Created by Alexander Yaworsky
+ *
+ * Asynchronous i/o is not supported under win9x so we have to use
+ * a separate thread for each file.
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+#define WRITER_KEEPALIVE_TIME 10000
+
+struct file_writer
+{
+ struct destination *destination;
+ gchar *file_name;
+ struct fifo *message_queue;
+ HANDLE fifo_semaphore;
+ HANDLE shutdown_event; /* manual-reset event */
+ HANDLE fd;
+ struct message *first_msg, *second_msg, *current_msg;
+ int coalesce_count;
+ time_t max_hold;
+};
+
+/* forward declarations */
+static unsigned __stdcall writer_thread_proc( void* arg );
+
+/******************************************************************************
+ * compare_current_and_first_messages
+ *
+ * Auxiliary function for coalescer.
+ * If messages aren't identical, reset coalescer.
+ */
+static gboolean compare_current_and_first_messages( struct file_writer* writer )
+{
+ TRACE_ENTER( "%p\n", writer );
+
+ if( strcmp( writer->current_msg->hostname, writer->first_msg->hostname )
+ || strcmp( writer->current_msg->message, writer->first_msg->message ) )
+ {
+ release_message( writer->first_msg );
+ writer->first_msg = NULL;
+ TRACE_LEAVE( "%p done; messages aren't identical\n", writer );
+ return FALSE;
+ }
+
+ TRACE_LEAVE( "%p ok\n" );
+ return TRUE;
+}
+
+/******************************************************************************
+ * init_coalescer
+ *
+ * Should be called when we've got the first unique message.
+ * Save message for subsequent comparsions and set max hold time.
+ */
+static void init_coalescer( struct file_writer* writer )
+{
+ writer->first_msg = writer->current_msg;
+ reference_message( writer->first_msg );
+ writer->max_hold = time(NULL) + hold;
+ writer->coalesce_count = 1;
+ TRACE_2( "%p max_hold=%ld\n", writer, writer->max_hold );
+}
+
+/******************************************************************************
+ * coalesce
+ *
+ * coalesce messages;
+ * If there are more than two sequential identical messages then we
+ * should write first of them followed by message with the number of
+ * subsequent messages.
+ *
+ * The caller always must process current_msg unless it is set NULL
+ * when coalesced;
+ * return: TRUE if messages are coalesced; FALSE if flushing is required
+ */
+static gboolean coalesce( struct file_writer* writer )
+{
+ time_t current_time;
+
+ TRACE_ENTER( "%p\n", writer );
+
+ switch( writer->coalesce_count )
+ {
+ case 0:
+ /* the first message */
+ init_coalescer( writer );
+ return TRUE;
+
+ case 1:
+ /* the second message */
+ TRACE_2( "%p second message\n", writer );
+ if( !compare_current_and_first_messages( writer ) )
+ return FALSE;
+ writer->second_msg = writer->current_msg;
+ writer->current_msg = NULL;
+ writer->coalesce_count = 2;
+ goto check_hold_time;
+
+ case 2:
+ /* the third message */
+ TRACE_2( "%p third message\n", writer );
+ if( !compare_current_and_first_messages( writer ) )
+ /* leave the second message; it will be written by flush_coalescer */
+ return FALSE;
+ release_message( writer->second_msg );
+ writer->second_msg = NULL;
+ release_message( writer->current_msg );
+ writer->current_msg = NULL;
+ writer->coalesce_count = 3;
+ goto check_hold_time;
+
+ default:
+ /* the fourth and subsequent messages */
+ TRACE_2( "%p fourth+ message\n", writer );
+ if( !compare_current_and_first_messages( writer ) )
+ return FALSE;
+ release_message( writer->current_msg );
+ writer->current_msg = NULL;
+ writer->coalesce_count++;
+ TRACE_2( "%p coalesce_count=%d\n", writer, writer->coalesce_count );
+ goto check_hold_time;
+ }
+
+check_hold_time:
+ current_time = time(NULL);
+ TRACE_2( "%p current_time=%ld\n", writer, current_time );
+ if( writer->max_hold < current_time )
+ {
+ TRACE_LEAVE( "%p done; elapsed hold time\n", writer );
+ return FALSE;
+ }
+ TRACE_LEAVE( "%p done\n", writer );
+ return TRUE;
+}
+
+/******************************************************************************
+ * write_message_to_logfile
+ */
+static void write_message_to_logfile( struct file_writer* writer, struct message** msg )
+{
+ gchar *buffer;
+ DWORD written;
+
+ if( INVALID_HANDLE_VALUE == writer->fd )
+ return;
+ TRACE_2( "%p: %s\n", writer, (*msg)->message );
+ buffer = g_strconcat( (*msg)->timestamp, " ", (*msg)->hostname, " ", (*msg)->message, "\n", NULL );
+ WriteFile( writer->fd, buffer, strlen( buffer ), &written, NULL );
+ g_free( buffer );
+ release_message( *msg );
+ *msg = NULL;
+}
+
+/******************************************************************************
+ * flush_coalescer
+ */
+static void flush_coalescer( struct file_writer* writer )
+{
+ if( writer->second_msg )
+ {
+ write_message_to_logfile( writer, &writer->second_msg );
+ TRACE_2( "%p written second message\n", writer );
+ }
+
+ if( writer->coalesce_count > 2 )
+ {
+ SYSTEMTIME stm;
+ char buffer[ 1024 ];
+ int size;
+ DWORD written;
+
+ GetLocalTime( &stm );
+
+ /* make informational message */
+ size = snprintf( buffer, sizeof(buffer),
+ "%s %2d %02d:%02d:%02d %s syslog: last message repeated %d times\n",
+ str_month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond,
+ local_hostname,
+ writer->coalesce_count - 1 );
+ WriteFile( writer->fd, buffer, size, &written, NULL );
+ TRACE_2( "%p made informational message\n", writer );
+ }
+
+ writer->coalesce_count = 0;
+ if( writer->first_msg )
+ {
+ release_message( writer->first_msg );
+ writer->first_msg = NULL;
+ }
+ if( writer->current_msg )
+ {
+ /* we just got the first message and should initialize coalescer */
+ init_coalescer( writer );
+ write_message_to_logfile( writer, &writer->current_msg );
+ TRACE_2( "%p written current message\n", writer );
+ }
+}
+
+/******************************************************************************
+ * destroy_file_writer
+ */
+static void destroy_file_writer( struct file_writer* writer )
+{
+ TRACE_ENTER( "%p\n", writer );
+ if( writer->fifo_semaphore ) CloseHandle( writer->fifo_semaphore );
+ if( writer->shutdown_event ) CloseHandle( writer->shutdown_event );
+ fifo_destroy( writer->message_queue );
+ g_free( writer->file_name );
+ g_free( writer );
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * create_file_writer
+ */
+static struct file_writer* create_file_writer( gchar* file_name )
+{
+ struct file_writer *ret;
+ unsigned writer_thread_id;
+ HANDLE *writer_thread;
+
+ TRACE_ENTER( "file_name=%s\n", file_name );
+ ret = g_malloc0( sizeof(struct file_writer) );
+ ret->file_name = g_strdup( file_name );
+ ret->message_queue = fifo_create();
+ ret->fifo_semaphore = CreateSemaphore( NULL, 0, LONG_MAX, NULL );
+ if( !ret->fifo_semaphore )
+ {
+ ERR( "Cannot create semaphore; error %lu\n", GetLastError() );
+ goto error;
+ }
+ ret->shutdown_event = CreateEvent( NULL, TRUE, FALSE, NULL );
+ if( !ret->shutdown_event )
+ {
+ ERR( "Cannot create event; error %lu\n", GetLastError() );
+ goto error;
+ }
+ writer_thread = (HANDLE) _beginthreadex( NULL, 0, writer_thread_proc, ret,
+ 0, &writer_thread_id );
+ if( !writer_thread )
+ {
+ ERR( "Cannot create thread; error %lu\n", GetLastError() );
+ goto error;
+ }
+ CloseHandle( writer_thread );
+ TRACE_LEAVE( "done; ret=%p\n", ret );
+ return ret;
+
+error:
+ destroy_file_writer( ret );
+ TRACE_LEAVE( "error\n" );
+ return NULL;
+}
+
+/******************************************************************************
+ * detach_writer_from_destination
+ */
+static void detach_writer_from_destination( struct file_writer* writer )
+{
+ TRACE_ENTER( "%p\n", writer );
+ if( !writer->destination )
+ {
+ TRACE_LEAVE( "done; already detached\n" );
+ return;
+ }
+ EnterCriticalSection( &writer->destination->cs_file_writers );
+ writer->destination->file_writers = g_list_remove( writer->destination->file_writers, writer );
+ LeaveCriticalSection( &writer->destination->cs_file_writers );
+ writer->destination = NULL;
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * writer_thread_proc
+ *
+ * Open file, extract messages from queue and write them to file.
+ * If queue is empty long enough, close file and destroy itself.
+ */
+static unsigned __stdcall writer_thread_proc( void* arg )
+{
+ struct file_writer *writer = arg;
+ HANDLE wait_objects[2] = { writer->fifo_semaphore, writer->shutdown_event };
+ gchar *pathname;
+ FILETIME systime;
+
+ TRACE_ENTER( "writer=%p\n", writer );
+
+ pathname = make_absolute_log_pathname( writer->file_name );
+ create_directories( pathname );
+
+ rotate_logfile( pathname, writer->destination );
+
+ writer->fd = CreateFile( pathname, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+ g_free( pathname );
+ if( INVALID_HANDLE_VALUE == writer->fd )
+ {
+ ERR( "CreateFile(%s) error %lu\n", pathname, GetLastError() );
+ goto done;
+ }
+ SetFilePointer( writer->fd, 0, NULL, FILE_END );
+
+ /* there's a strange bug or feature in windows: if there was a file with the same
+ name in the directory, a new file will inherit its creation time;
+ because of this logs will be rotate each time;
+ here is a workaround:
+ */
+ GetSystemTimeAsFileTime( &systime );
+ if( !SetFileTime( writer->fd, &systime, &systime, &systime ) )
+ ERR( "SetFileTime error %lu\n", GetLastError() );
+
+ for(;;)
+ {
+ DWORD w = WaitForMultipleObjects( 2, wait_objects, FALSE, WRITER_KEEPALIVE_TIME );
+ if( WAIT_TIMEOUT == w )
+ {
+ /* prepare to suicide; at this point a new message may be put into queue;
+ detach writer from destination and continue to write any pending messages */
+ detach_writer_from_destination( writer );
+ /* from now no new messages will be put into queue */
+ SetEvent( writer->shutdown_event );
+ }
+
+ writer->current_msg = fifo_pop( writer->message_queue );
+ if( !writer->current_msg )
+ {
+ /* shutdown */
+ goto done;
+ }
+
+ if( coalesce( writer ) )
+ {
+ if( writer->current_msg )
+ {
+ write_message_to_logfile( writer, &writer->current_msg );
+ TRACE_2( "%p written current message\n", writer );
+ }
+ }
+ else
+ {
+ flush_coalescer( writer );
+ }
+ }
+done:
+ detach_writer_from_destination( writer );
+ flush_coalescer( writer );
+ if( writer->fd != INVALID_HANDLE_VALUE ) CloseHandle( writer->fd );
+ destroy_file_writer( writer );
+
+ purge_log_dirs();
+
+ TRACE_LEAVE( "done\n" );
+ return 0;
+}
+
+/******************************************************************************
+ * make_file_name
+ *
+ * expand filename pattern (message->file)
+ */
+static void make_file_name( char* pattern, struct message* message, char* buffer )
+{
+ char *dest = buffer;
+ SYSTEMTIME stm;
+
+ GetLocalTime( &stm );
+ for(;;)
+ {
+ char c = *pattern++;
+ if( c != '%' )
+ {
+ *dest++ = c;
+ if( '\0' == c )
+ break;
+ continue;
+ }
+ c = *pattern++;
+ switch( c )
+ {
+ case 'Y': dest += sprintf( dest, "%u", stm.wYear ); break;
+ case 'M': dest += sprintf( dest, "%02u", stm.wMonth ); break;
+ case 'm': dest += sprintf( dest, "%u", stm.wMonth ); break;
+ case 'D': dest += sprintf( dest, "%02u", stm.wDay ); break;
+ case 'd': dest += sprintf( dest, "%u", stm.wDay ); break;
+ case 'W': dest += sprintf( dest, "%u", stm.wDayOfWeek + 1 ); break;
+ case 'F': dest += sprintf( dest, "%s", get_facility_name( message->facility ) ); break;
+ case 'f': dest += sprintf( dest, "%d", message->facility ); break;
+ case 'L': dest += sprintf( dest, "%s", get_priority_name( message->priority ) ); break;
+ case 'l': dest += sprintf( dest, "%d", message->priority ); break;
+ case 'H': dest += sprintf( dest, "%s", message->hostname ); break;
+ case 'h': dest += sprintf( dest, "%s", message->sender ); break;
+ case 'P': dest += sprintf( dest, "%s", message->program ); break;
+ default: *dest++ = c; break;
+ }
+ }
+ strlwr( buffer );
+}
+
+/******************************************************************************
+ * write_message
+ */
+void write_message( struct message* msg, struct destination* destination )
+{
+ char file_name[ MAX_PATH ];
+ GList *item;
+ struct file_writer *writer;
+
+ TRACE_ENTER( "msg=%p, destination=%s\n", msg, destination->name );
+ make_file_name( destination->file, msg, file_name );
+ EnterCriticalSection( &destination->cs_file_writers );
+ /* find existing writer */
+ for( writer = NULL, item = destination->file_writers; item; item = item->next )
+ if( strcmp( ((struct file_writer*) (item->data))->file_name, file_name ) == 0 )
+ {
+ writer = item->data;
+ break;
+ }
+ if( !writer )
+ {
+ /* create new writer */
+ writer = create_file_writer( file_name );
+ if( !writer )
+ goto done;
+ /* add writer to destination */
+ destination->file_writers = g_list_append( destination->file_writers, writer );
+ writer->destination = destination;
+ }
+ /* put message into queue */
+ reference_message( msg );
+ fifo_push( writer->message_queue, msg );
+ ReleaseSemaphore( writer->fifo_semaphore, 1, NULL );
+
+done:
+ LeaveCriticalSection( &destination->cs_file_writers );
+ TRACE_LEAVE( "done\n" );
+}
+
+/******************************************************************************
+ * fini_writer
+ */
+void fini_writer()
+{
+ GList *dest_item;
+
+ TRACE_ENTER( "\n" );
+ /* setting shutdown event */
+ for( dest_item = destinations; dest_item; dest_item = dest_item->next )
+ {
+ struct destination *destination = dest_item->data;
+ GList *wr_item;
+
+ EnterCriticalSection( &destination->cs_file_writers );
+ for( wr_item = destination->file_writers; wr_item; wr_item = wr_item->next )
+ {
+ struct file_writer *writer = wr_item->data;
+ SetEvent( writer->shutdown_event );
+ }
+ LeaveCriticalSection( &destination->cs_file_writers );
+ }
+ TRACE_2( "waiting for shutdown\n" );
+ for(;;)
+ {
+ for( dest_item = destinations; dest_item; dest_item = dest_item->next )
+ if( ((struct destination*) (dest_item->data))->file_writers )
+ break;
+ if( !dest_item )
+ break;
+ Sleep( 60 );
+ }
+ TRACE_LEAVE( "done\n" );
+}
diff --git a/etc/syslog.conf b/etc/syslog.conf
new file mode 100644
index 0000000..09b06ee
--- /dev/null
+++ b/etc/syslog.conf
@@ -0,0 +1,253 @@
+<?xml version="1.0"?>
+
+<!--
+syslog.conf Configuration file for syslogd.
+ Based on Debian's syslog.conf.
+-->
+
+<conf>
+
+<source name="src_udp" type="udp"/>
+
+<destination name="auth" file="auth.log" rotate="weekly" backlogs="4"/>
+<destination name="syslog" file="syslog" rotate="weekly" backlogs="4"/>
+<destination name="daemon" file="daemon.log" rotate="weekly" backlogs="4"/>
+<destination name="kern" file="kern.log" rotate="weekly" backlogs="4"/>
+<destination name="lpr" file="lpr.log" rotate="weekly" backlogs="4"/>
+<destination name="mail" file="mail.log" rotate="weekly" backlogs="4"/>
+<destination name="user" file="user.log" rotate="weekly" backlogs="4"/>
+<destination name="uucp" file="uucp.log" rotate="weekly" backlogs="4"/>
+<destination name="mail.info" file="mail.info" rotate="weekly" backlogs="4"/>
+<destination name="mail.warn" file="mail.warn" rotate="weekly" backlogs="4"/>
+<destination name="mail.err" file="mail.err" rotate="weekly" backlogs="4"/>
+<destination name="news.crit" file="news/news.crit" rotate="weekly" backlogs="4"/>
+<destination name="news.err" file="news/news.err" rotate="weekly" backlogs="4"/>
+<destination name="news.notice" file="news/news.notice" rotate="weekly" backlogs="4"/>
+<destination name="debug" file="debug" rotate="weekly" backlogs="4"/>
+<destination name="messages" file="messages" rotate="weekly" backlogs="4"/>
+
+<filter name="auth">
+ <facility name="auth"/>
+ <facility name="authpriv"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="syslog">
+ <facility name="kern"/>
+ <facility name="user"/>
+ <facility name="mail"/>
+ <facility name="daemon"/>
+ <facility name="syslog"/>
+ <facility name="lpr"/>
+ <facility name="news"/>
+ <facility name="uucp"/>
+ <facility name="cron"/>
+ <facility name="ftp"/>
+ <facility name="local0"/>
+ <facility name="local1"/>
+ <facility name="local2"/>
+ <facility name="local3"/>
+ <facility name="local4"/>
+ <facility name="local5"/>
+ <facility name="local6"/>
+ <facility name="local7"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="daemon">
+ <facility name="daemon"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="kern">
+ <facility name="kern"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="lpr">
+ <facility name="lpr"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="mail">
+ <facility name="mail"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="user">
+ <facility name="user"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="uucp">
+ <facility name="uucp"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="mail.info">
+ <facility name="mail"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+</filter>
+
+<filter name="mail.warn">
+ <facility name="mail"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+</filter>
+
+<filter name="mail.err">
+ <facility name="mail"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+</filter>
+
+<filter name="news.crit">
+ <facility name="news"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+</filter>
+
+<filter name="news.err">
+ <facility name="news"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+</filter>
+
+<filter name="news.notice">
+ <facility name="news"/>
+ <priority name="emerg"/>
+ <priority name="alert"/>
+ <priority name="crit"/>
+ <priority name="error"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+</filter>
+
+<filter name="debug">
+ <facility name="kern"/>
+ <facility name="user"/>
+ <facility name="daemon"/>
+ <facility name="syslog"/>
+ <facility name="lpr"/>
+ <facility name="uucp"/>
+ <facility name="cron"/>
+ <facility name="ftp"/>
+ <facility name="local0"/>
+ <facility name="local1"/>
+ <facility name="local2"/>
+ <facility name="local3"/>
+ <facility name="local4"/>
+ <facility name="local5"/>
+ <facility name="local6"/>
+ <facility name="local7"/>
+ <priority name="debug"/>
+</filter>
+
+<filter name="messages">
+ <facility name="kern"/>
+ <facility name="user"/>
+ <facility name="syslog"/>
+ <facility name="lpr"/>
+ <facility name="uucp"/>
+ <facility name="ftp"/>
+ <facility name="local0"/>
+ <facility name="local1"/>
+ <facility name="local2"/>
+ <facility name="local3"/>
+ <facility name="local4"/>
+ <facility name="local5"/>
+ <facility name="local6"/>
+ <facility name="local7"/>
+ <priority name="warning"/>
+ <priority name="notice"/>
+ <priority name="info"/>
+</filter>
+
+<logpath source="src_udp" filter="auth" destination="auth"/>
+<logpath source="src_udp" filter="syslog" destination="syslog"/>
+<logpath source="src_udp" filter="daemon" destination="daemon"/>
+<logpath source="src_udp" filter="kern" destination="kern"/>
+<logpath source="src_udp" filter="lpr" destination="lpr"/>
+<logpath source="src_udp" filter="mail" destination="mail"/>
+<logpath source="src_udp" filter="user" destination="user"/>
+<logpath source="src_udp" filter="uucp" destination="uucp"/>
+<logpath source="src_udp" filter="mail.info" destination="mail.info"/>
+<logpath source="src_udp" filter="mail.warn" destination="mail.warn"/>
+<logpath source="src_udp" filter="mail.err" destination="mail.err"/>
+<logpath source="src_udp" filter="news.crit" destination="news.crit"/>
+<logpath source="src_udp" filter="news.err" destination="news.err"/>
+<logpath source="src_udp" filter="news.notice" destination="news.notice"/>
+<logpath source="src_udp" filter="debug" destination="debug"/>
+<logpath source="src_udp" filter="messages" destination="messages"/>
+
+<options logdir="log"/>
diff --git a/extras/cross-compile-libs b/extras/cross-compile-libs
new file mode 100755
index 0000000..984c597
--- /dev/null
+++ b/extras/cross-compile-libs
@@ -0,0 +1,214 @@
+#!/bin/bash
+#
+# cross-compile win32 libraries for syslog-win32
+# on exit libraries are in cross-libs directory
+#
+# required environment variables: DISTFILES
+# honoured environment variables: CFLAGS, HOST
+#
+
+#
+# define distfiles
+#
+libiconv=libiconv-1.9.2.tar.gz
+gettext=gettext-0.14.1.tar.gz
+zlib=zlib-1.2.2.tar.bz2
+regex=regex-0.12.tar.gz
+glib=glib-2.6.3.tar.bz2
+
+function distfiles()
+{
+ echo $libiconv
+ echo $gettext
+ echo $zlib
+ echo $regex
+ echo $glib
+}
+
+#
+# check DISTFILES and distfiles
+#
+if [ -z "$DISTFILES" ] ; then
+ echo "You should specify location of source tarballs with the help of DISTFILES environment variable"
+ exit 1
+fi
+
+for file in `distfiles` ; do
+ if [ -f "$DISTFILES"/$file ] ; then continue ; fi
+ echo $file not found
+ exit 1
+done
+
+#
+# set environment variables if not set yet
+#
+if [ -z "$CFLAGS" ] ; then
+ export CFLAGS="-Os -g"
+fi
+
+if [ -z "$HOST" ] ; then
+ export HOST=i686-pc-mingw32
+fi
+
+#
+# a wrapper for subsequent commands
+#
+function run()
+{
+ echo "$@"
+ "$@" || exit 1
+}
+
+#
+# unpack routine
+#
+function unpack_tarball()
+{
+ if [[ zip == ${1:(-3)} ]] ; then
+ run unzip "$1"
+ else
+ if [[ bz2 == ${1:(-3)} ]] ; then arch=j
+ elif [[ gz == ${1:(-2)} ]] ; then arch=z
+ elif [[ tar == ${1:(-3)} ]] ; then arch=
+ else
+ echo "Unknown distfile type"
+ exit 1
+ fi
+ run tar -x${arch}f "$1"
+ fi
+}
+
+#
+# create build directories
+#
+export PREFIX=`pwd`/cross-libs
+run mkdir -p cross-libs
+run mkdir -p cross-build
+run pushd cross-build
+
+#
+# build libiconv
+#
+unpack_tarball "$DISTFILES"/$libiconv
+run pushd libiconv*
+run ./configure --prefix="$PREFIX" --host=$HOST --disable-nls
+run make
+run make install
+run popd
+run rm -Rf libiconv*
+
+#
+# build gettext
+#
+unpack_tarball "$DISTFILES"/$gettext
+run pushd gettext*/gettext-runtime
+CPPFLAGS=-I"$PREFIX"/include LDFLAGS=-L"$PREFIX"/lib \
+run ./configure --prefix="$PREFIX" --host=$HOST --enable-relocatable --with-libiconv-prefix="$PREFIX"
+run make
+run make install
+run popd
+run rm -Rf gettext*
+
+#
+# build zlib
+# TO DO: use original makefile; dllname must be like in official windoze distro
+#
+unpack_tarball "$DISTFILES"/$zlib
+run pushd zlib*
+cat >configure.ac <<EOF
+AC_INIT(zlib,1.2.2)
+AM_INIT_AUTOMAKE
+AC_CANONICAL_HOST
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_OUTPUT(Makefile)
+EOF
+cat >Makefile.am <<EOF
+lib_LTLIBRARIES = libz.la
+libz_la_SOURCES = adler32.c compress.c crc32.c deflate.c gzio.c \
+ infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c
+include_HEADERS = zlib.h
+libz_la_LDFLAGS = -no-undefined -release 1
+EOF
+run aclocal
+run libtoolize
+run autoconf
+run automake --foreign --add-missing
+run ./configure --prefix="$PREFIX" --host=$HOST
+run make
+run make install
+run popd
+run rm -Rf zlib*
+
+#
+# build regex
+# NOT NEEDED
+#
+unpack_tarball "$DISTFILES"/$regex
+run pushd regex*
+cat >configure.ac <<EOF
+AC_INIT(regex,0.12)
+AM_INIT_AUTOMAKE
+AC_CANONICAL_HOST
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_OUTPUT(Makefile)
+EOF
+cat >Makefile.am <<EOF
+lib_LTLIBRARIES = libregex.la
+libregex_la_SOURCES = regex.c
+include_HEADERS = regex.h
+libregex_la_LDFLAGS = -no-undefined
+EOF
+run rm -f configure.in
+run aclocal
+run libtoolize
+run autoconf
+run automake --foreign --add-missing
+run ./configure --prefix="$PREFIX" --host=$HOST
+run make
+run make install
+run popd
+run rm -Rf regex*
+
+#
+# build glib
+#
+unpack_tarball "$DISTFILES"/$glib
+run pushd glib*
+#
+# according to glib documentation, win32.cache is needed to skip tests that cannot be run in cross-compile environment
+#
+cat >win32.cache <<EOF
+glib_cv_long_long_format=I64
+glib_cv_stack_grows=no
+EOF
+run chmod a-w win32.cache
+#
+# remove policy that does not allow us to build static library for win32
+#
+run patch -Np1 -i ../../glib-static-win32.patch
+run aclocal --force
+run libtoolize --force
+run autoconf --force
+run automake --foreign --add-missing --force
+OBJDUMP=$HOST-objdump CFLAGS='-Os -g' \
+CPPFLAGS=-I"$PREFIX"/include \
+LDFLAGS=-L"$PREFIX"/lib \
+run ./configure --prefix="$PREFIX" --host=$HOST --with-libintl --disable-gtk-doc --cache-file=win32.cache --enable-static
+WINDRES=$HOST-windres run make
+run make install
+run popd
+run rm -Rf glib*
+#
+# explanations:
+# - setting OBJDUMP is required for libtool's file format checking
+# - WINDRES is required for resource compiling
+#
+
+#
+# strip libraries and cleanup
+#
+run popd
+$HOST-strip -s cross-libs/bin/*
+run rmdir cross-build
diff --git a/extras/glib-static-win32.patch b/extras/glib-static-win32.patch
new file mode 100644
index 0000000..46f0aba
--- /dev/null
+++ b/extras/glib-static-win32.patch
@@ -0,0 +1,21 @@
+diff -urN glib-2.6.3-orig/configure.in glib-2.6.3/configure.in
+--- glib-2.6.3-orig/configure.in 2005-02-24 10:23:02.000000000 +0600
++++ glib-2.6.3/configure.in 2005-07-08 19:37:53.000000000 +0700
+@@ -245,17 +245,6 @@
+ AC_MSG_RESULT([yes])
+ fi
+
+-if test "$glib_native_win32" = "yes"; then
+- if test x$enable_static = xyes -o x$enable_static = x; then
+- AC_MSG_WARN([Disabling static library build, must build as DLL on Windows.])
+- enable_static=no
+- fi
+- if test x$enable_shared = xno; then
+- AC_MSG_WARN([Enabling shared library build, must build as DLL on Windows.])
+- fi
+- enable_shared=yes
+-fi
+-
+ dnl Checks for programs.
+ AC_PROG_CC
+
diff --git a/test/dropcount.c b/test/dropcount.c
new file mode 100644
index 0000000..1345b73
--- /dev/null
+++ b/test/dropcount.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main( int argc, char* argv[] )
+{
+ FILE *fd;
+ char buffer[ 256 ];
+ int msg_index = 1, drop_count = 0;
+
+ if( argc < 2 )
+ {
+ puts( "required filename" );
+ return 1;
+ }
+
+ fd = fopen( argv[1], "r" );
+ if( !fd )
+ {
+ perror( "fopen" );
+ return 1;
+ }
+ while( fgets( buffer, sizeof(buffer), fd ) )
+ {
+ char *p;
+ int i;
+
+ p = buffer + strlen( buffer );
+ while( *p != ' ' ) p--;
+ i = strtoul( p, NULL, 10 );
+ while( msg_index < i )
+ {
+ msg_index++;
+ drop_count++;
+ }
+ msg_index++;
+ }
+ printf( "dropped %d message(s)\n", drop_count );
+ fclose( fd );
+ return 0;
+}
diff --git a/test/test.c b/test/test.c
new file mode 100644
index 0000000..fe6d90e
--- /dev/null
+++ b/test/test.c
@@ -0,0 +1,201 @@
+/*
+ * This test is based on logger.c.
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixed strerr(errno) in gettext calls
+ * 2005-06-17 Alexander Yaworsky <yaworsky@mail.ru>
+ * - modified for native win32
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <windows.h>
+
+#define SYSLOG_NAMES
+#include <syslog.h>
+
+#define _(s) s
+
+int decode (char *, CODE *);
+int pencode (char *);
+void usage (void);
+
+int
+main(int argc, char **argv) {
+ int ch;
+ int pri = LOG_NOTICE;
+ int burst = 1, sleep = 1;
+ int max_identical = 0;
+ int iterations = 1;
+ char *tag = NULL;
+ FILETIME current_time, max_time;
+ unsigned long long max_time_64;
+ int msg_index, total_sent;
+
+ while ((ch = getopt(argc, argv, "p:t:b:s:i:n:")) != -1)
+ switch((char)ch) {
+ case 'p': /* priority */
+ pri = pencode(optarg);
+ break;
+ case 't': /* tag */
+ tag = optarg;
+ break;
+ case 'b': /* message burst time in seconds */
+ burst = strtoul( optarg, NULL, 0 );
+ break;
+ case 's': /* sleep time in seconds */
+ sleep = strtoul( optarg, NULL, 0 );
+ break;
+ case 'i': /* max number of identical messages */
+ max_identical = strtoul( optarg, NULL, 0 );
+ break;
+ case 'n': /* number of test iterations */
+ iterations = strtoul( optarg, NULL, 0 );
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ openlog(tag ? tag : "test", 0, 0);
+
+ msg_index = 1;
+ total_sent = 0;
+ for(;;) {
+ int messages_sent = 0;
+ int identic_count = 0, identic_index = 0;
+
+ GetSystemTimeAsFileTime( &current_time );
+ max_time = current_time;
+ max_time_64 = (((long long) max_time.dwHighDateTime) << 32) + (long long) max_time.dwLowDateTime;
+ max_time_64 += ((long long) burst) * (long long) 10000000;
+ max_time.dwHighDateTime = max_time_64 >> 32;
+ max_time.dwLowDateTime = max_time_64;
+ for(;;) {
+ int i;
+ for( i = 0; i < 100; i++ ) {
+ syslog(pri, "test message %d", msg_index );
+ messages_sent++;
+ total_sent++;
+ if( identic_index < identic_count )
+ identic_index++;
+ else {
+ msg_index++;
+ identic_index = 0;
+ if( identic_count < max_identical )
+ identic_count++;
+ else
+ identic_count = 0;
+ }
+ }
+ GetSystemTimeAsFileTime( &current_time );
+ if( CompareFileTime( &current_time, &max_time ) >= 0 )
+ break;
+ }
+ printf( "%d messages sent in burst, total %d messages sent\n", messages_sent, total_sent );
+ if( --iterations == 0 )
+ break;
+ printf( "%d iteration(s) remain; sleeping %d sec.\n", iterations, sleep );
+ Sleep( sleep * 1000 );
+ }
+ closelog();
+ exit(0);
+}
+
+/*
+ * Decode a symbolic name to a numeric value
+ */
+int
+pencode(s)
+ register char *s;
+{
+ char *save;
+ int fac, lev;
+
+ for (save = s; *s && *s != '.'; ++s);
+ if (*s) {
+ *s = '\0';
+ fac = decode(save, facilitynames);
+ if (fac < 0) {
+ (void)fprintf(stderr,
+ _("logger: unknown facility name: %s.\n"), save);
+ exit(1);
+ }
+ *s++ = '.';
+ }
+ else {
+ fac = LOG_USER;
+ s = save;
+ }
+ lev = decode(s, prioritynames);
+ if (lev < 0) {
+ (void)fprintf(stderr,
+ _("logger: unknown priority name: %s.\n"), save);
+ exit(1);
+ }
+ return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+int
+decode(name, codetab)
+ char *name;
+ CODE *codetab;
+{
+ register CODE *c;
+
+ if (isdigit(*name))
+ return (atoi(name));
+
+ for (c = codetab; c->c_name; c++)
+ if (!strcasecmp(name, c->c_name))
+ return (c->c_val);
+
+ return (-1);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ _("usage: test [-i] [-p pri] [-t tag] [-b burst time] [-s sleep time] [-i max identical] [-n iterations]\n"));
+ exit(1);
+}