diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | COPYING | 543 | ||||
-rw-r--r-- | NEWS | 0 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | client/syslog-client.c | 464 | ||||
-rw-r--r-- | daemon/conf.c | 730 | ||||
-rw-r--r-- | daemon/fifo.c | 105 | ||||
-rw-r--r-- | daemon/listener.c | 335 | ||||
-rw-r--r-- | daemon/logrotate.c | 264 | ||||
-rw-r--r-- | daemon/main.c | 660 | ||||
-rw-r--r-- | daemon/names.c | 63 | ||||
-rw-r--r-- | daemon/pathnames.c | 139 | ||||
-rw-r--r-- | daemon/purger.c | 155 | ||||
-rw-r--r-- | daemon/syslogd.c | 600 | ||||
-rw-r--r-- | daemon/syslogd.h | 204 | ||||
-rw-r--r-- | daemon/writer.c | 498 | ||||
-rw-r--r-- | etc/syslog.conf | 253 | ||||
-rwxr-xr-x | extras/cross-compile-libs | 214 | ||||
-rw-r--r-- | extras/glib-static-win32.patch | 21 | ||||
-rw-r--r-- | test/dropcount.c | 41 | ||||
-rw-r--r-- | test/test.c | 201 |
21 files changed, 5494 insertions, 0 deletions
@@ -0,0 +1 @@ +Alexander Yaworsky <yaworsky@mail.ru> @@ -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! @@ -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( ¤t_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( ¤t_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( ¤t_time ); + if( CompareFileTime( ¤t_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); +} |