pax_global_header00006660000000000000000000000064147401262450014517gustar00rootroot0000000000000052 comment=b03cc3b1a75048c7cf19467d8918a4b7320767e6 systemd-netlogd-1.4.4/000077500000000000000000000000001474012624500146475ustar00rootroot00000000000000systemd-netlogd-1.4.4/.ctags000066400000000000000000000000131474012624500157430ustar00rootroot00000000000000--links=no systemd-netlogd-1.4.4/.editorconfig000066400000000000000000000012511474012624500173230ustar00rootroot00000000000000# EditorConfig configuration for systemd # http://EditorConfig.org # NOTE: If you update this file make sure to update .dir-locals.el and .vimrc, # too. # Top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file, utf-8 charset [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 [NEWS] indent_style = space indent_size = 4 # Match config files, set indent to spaces with width of eight [*.{c,h}] indent_style = space indent_size = 8 max_line_length = 109 [*.sh,mkosi.build,mkosi.prepare,mkosi.postinst] indent_style = space indent_size = 4 [meson.build] indent_style = space indent_size = 8 systemd-netlogd-1.4.4/.gitattributes000066400000000000000000000007211474012624500175420ustar00rootroot00000000000000*.[ch] whitespace=tab-in-indent,trailing-space *.gpg binary generated *.bmp binary *.base64 generated # Mark files as "generated", i.e. no license applies to them. # This includes output from programs, directive lists generated by grepping # for all possibilities, samples from fuzzers, files from /proc, packet samples, # and anything else where no copyright can be asserted. # # Use 'git check-attr generated -- ' to query the attribute. [attr]generated systemd-netlogd-1.4.4/.github/000077500000000000000000000000001474012624500162075ustar00rootroot00000000000000systemd-netlogd-1.4.4/.github/workflows/000077500000000000000000000000001474012624500202445ustar00rootroot00000000000000systemd-netlogd-1.4.4/.github/workflows/ci.yml000066400000000000000000000015211474012624500213610ustar00rootroot00000000000000name: systemd-netlogd CI on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: update run: sudo apt-get update - name: install build essentials run: sudo apt-get install -y python3-sphinx ninja-build meson glib-2.0-dev libudev-dev libsystemd-dev clang gperf libcap-dev build-essential - name: build run: make - name: install run: sudo make install - name: add systemd-journal-netlog user run: sudo useradd -r -d / -s /usr/sbin/nologin -g systemd-journal systemd-journal-netlog - name: start systemd-netlogd run: sudo systemctl daemon-reload; sudo systemctl start systemd-netlogd - name: show status systemd-netlogd run: sudo systemctl status systemd-netlogd systemd-netlogd-1.4.4/.gitignore000066400000000000000000000007011474012624500166350ustar00rootroot00000000000000*.cache *.plist *.py[co] *.swp *.trs *~ .config.args .gdb_history .deps/ .mypy_cache/ __pycache__/ /*.gcda /*.gcno /*.tar.bz2 /*.tar.gz /*.tar.xz /GPATH /GRTAGS /GSYMS /GTAGS /TAGS /ID /build* /coverage/ /image.raw /.#image.raw.lck /image.raw.cache-pre-dev /image.raw.cache-pre-inst /image.raw.manifest /install-tree /.mkosi-* /mkosi.builddir/ /mkosi.output/ /mkosi.installdir/ /mkosi.key /mkosi.crt mkosi.local.conf /tags .dir-locals-2.el .vscode/ systemd-netlogd-1.4.4/.vimrc000066400000000000000000000014711474012624500157730ustar00rootroot00000000000000" 'set exrc' in ~/.vimrc will read .vimrc from the current directory " Warning: Enabling exrc is dangerous! You can do nearly everything from a " vimrc configuration file, including write operations and shell execution. " You should consider setting 'set secure' as well, which is highly " recommended! " Note that we set a line width of 109 for .c and XML files, but for everything " else (such as journal catalog files, unit files, README files) we stick to a " more conservative 79 characters. " NOTE: If you update this file make sure to update .dir-locals.el and " .editorconfig, too. set tabstop=8 set shiftwidth=8 set expandtab set makeprg=GCC_COLORS=\ make set tw=79 au BufRead,BufNewFile *.xml set tw=109 shiftwidth=2 smarttab au FileType sh set tw=109 shiftwidth=4 smarttab au FileType c set tw=109 shiftwidth=8 systemd-netlogd-1.4.4/LICENSE.GPL2000066400000000000000000000431031474012624500163600ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. systemd-netlogd-1.4.4/LICENSE.LGPL2.1000066400000000000000000000636421474012624500166450ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] 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 Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these 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 other code 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. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. 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, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) 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. d) 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. e) 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 materials to be 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 with 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 Lesser 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 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. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. 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. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! systemd-netlogd-1.4.4/Makefile000066400000000000000000000006521474012624500163120ustar00rootroot00000000000000all: build ninja -C build .PHONY: all build: meson setup build clean: rm -rf build/ .PHONY: clean install: build ninja -C build install .PHONY: install format: @for f in lib/*.[ch] tool/*.[ch]; do \ echo $$f; \ astyle --quiet --options=.astylerc $$f; \ done .PHONY: format install-tree: build rm -rf build/install-tree DESTDIR=install-tree ninja -C build install tree build/install-tree .PHONY: install-tree systemd-netlogd-1.4.4/README.md000066400000000000000000000171661474012624500161410ustar00rootroot00000000000000systemd-netlogd =================== ![build](https://github.com/systemd/systemd-netlogd/actions/workflows/ci.yml/badge.svg) Forwards messages from the journal to other hosts over the network using the Syslog Protocol (RFC 5424 and RFC 3339). It can be configured to send messages to both unicast and multicast addresses. systemd-netlogd runs with own user systemd-journal-netlog. Starts sending logs when network is up and stops sending as soon as network is down (uses sd-network). It reads from journal and forwards to network one by one. It does not use any extra disk space. systemd-netlogd supports UDP, TCP, TLS and DTLS (Datagram Transport Layer Security RFC 6012). -------------------------------------------------------------------------- Installing from source ---------------------- Install build dependencies: # On Debian/Ubuntu sudo apt install build-essential gperf libcap-dev libsystemd-dev pkg-config meson python3-sphinx # On CentOS/RHEL/Fedora sudo dnf group install 'Development Tools' sudo dnf install gperf libcap-devel pkg-config systemd-devel meson python3-sphinx Build and install: make sudo make install Creating user: ``` bash sudo useradd -r -d / -s /usr/sbin/nologin -g systemd-journal systemd-journal-netlog ``` or via sysusers ``` /usr/lib/sysusers.d/systemd-netlogd.conf``` ```bash #Type Name ID GECOS Home directory Shell u systemd-journal-netlog -:systemd-journal - / /bin/nologin ``` Configuration ------------- systemd-netlogd reads configuration files named `/etc/systemd/netlogd.conf` and `/etc/systemd/netlogd.conf.d/*.conf`. **[NETWORK]** SECTION OPTIONS The "[Network]" section only applies for UDP multicast address and Port: Address= Controls whether log messages received by the systemd-netlogd daemon shall be forwarded to a unicast UDP address or multicast UDP network group in syslog RFC 5424 format. The the address string format is similar to socket units. See systemd.socket(1) Protocol= Specifies whether to use udp, tcp, tls or dtls (Datagram Transport Layer Security) protocol. Defaults to udp. LogFormat= Specifies whether to use RFC 5424 format or RFC 3339 format. Takes one of rfc5424 or rfc3339. Defaults to rfc5424. Directory= Takes a directory path. Specifies whether to operate on the specified journal directory DIR instead of the default runtime and system journal paths. Namespace= Takes a journal namespace identifier string as argument. If not specified the data collected by the default namespace is shown. If specified shows the log data of the specified namespace instead. If the namespace is specified as "*" data from all namespaces is shown, interleaved. If the namespace identifier is prefixed with "+" data from the specified namespace and the default namespace is shown, interleaved, but no other. ConnectionRetrySec= Specifies the minimum delay before subsequent attempts to contact a Log server are made. Takes a time span value. The default unit is seconds, but other units may be specified, see systemd.time(5). Defaults to 30 seconds and must not be smaller than 1 second. TLSCertificateAuthMode= Specifies whether to validate the certificate. Takes one of no, allow, deny, warn. Defaults to 'deny' which rejects certificates failed to validate. TLSServerCertificate= Specify a custom certificate to validate the server against. Takes a path to a certificate file in PEM format. KeepAlive= Takes a boolean argument. If true, the TCP/IP stack will send a keep alive message after 2h (depending on the configuration of /proc/sys/net/ipv4/tcp_keepalive_time) for all TCP streams accepted on this socket. This controls the SO_KEEPALIVE socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Defaults to false. KeepAliveTimeSec= Takes time (in seconds) as argument. The connection needs to remain idle before TCP starts sending keepalive probes. This controls the TCP_KEEPIDLE socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Default value is 7200 seconds (2 hours). KeepAliveIntervalSec= Takes time (in seconds) as argument between individual keepalive probes, if the socket option SO_KEEPALIVE has been set on this socket. This controls the TCP_KEEPINTVL socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Default value is 75 seconds. KeepAliveProbes= Takes an integer as argument. It is the number of unacknowledged probes to send before considering the connection dead and notifying the application layer. This controls the TCP_KEEPCNT socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Default value is 9. SendBuffer= Takes an integer argument controlling the receive or send buffer sizes of this socket, respectively. This controls the SO_SNDBUF socket options (see socket(7) for details.). The usual suffixes K, M, G are supported and are understood to the base of 1024. NoDelay= Takes a boolean argument. TCP Nagle's algorithm works by combining a number of small outgoing messages, and sending them all at once. This controls the TCP_NODELAY socket option (see tcp(7)). Defaults to false. Optional settings StructuredData= Meta information about the syslog message, which can be used for Cloud Based syslog servers, such as Loggly UseSysLogStructuredData= A boolean. Specifies whether to extract SYSLOG_STRUCTURED_DATA= from journal. Defaults to false. UseSysLogMsgId= A boolean. Specifies whether to extract SYSLOG_MSGID= from journal. Defaults to false. ExcludeSyslogFacility= A list of strings. Specifies the syslog facilities to skip forwarding. Possible values are: "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "cron", "authpriv", "ftp", "ntp", "security", "console", "solaris-cron", "local0", "local1", "local2", "local3", "local4", "local5", "local6" and "local7". ExcludeSyslogLevel= A list of strings. Specifies the syslog levels to skip forwarding. Possible values are: "emerg", "alert", "crit", "err", "warning", "notice", "info" and "debug". **EXAMPLE** Example 1.UDP Multicast ``` toml [Network] Address=239.0.0.1:6000 #Protocol=udp #LogFormat=rfc5424 ``` Example 2.UDP ``` toml [Network] Address=192.168.8.101:514 #Protocol=udp LogFormat=rfc3339 ``` Example 3. Structured data ``` toml [Network] Address=192.168.8.101:514 #Protocol=udp LogFormat=rfc5424 StructuredData=[1ab456b6-90bb-6578-abcd-5b734584aaaa@41058] ``` Example 4. Custom syslog structured data and message ID ``` toml [Network] Address=192.168.8.101:514 #Protocol=udp LogFormat=rfc5424 UseSysLogStructuredData=yes UseSysLogMsgId=yes ``` Example 5. Skipping messages with facility AUTH or AUTHPRIV and messages with level DEBUG ``` toml [Network] Address=192.168.8.101:514 #Protocol=udp LogFormat=rfc3339 ExcludeSyslogFacility=auth authpriv ExcludeSyslogLevel=debug ``` Example 6. TLS with certificate authentocation mode ``` toml [Network] Address=192.168.8.101:4433 Protocol=tls #LogFormat=rfc5424 TLSCertificateAuthMode=warn ``` Example 7. DTLS with certificate authentocation mode ``` toml [Network] Address=192.168.8.101:4433 Protocol=dtls #LogFormat=rfc5424 TLSCertificateAuthMode=allow ``` Use case of ```UseSysLogStructuredData=``` and ```UseSysLogMsgId=``` ```C sd_journal_send( "MESSAGE=%s", "Message to process", "PRIORITY=%s", "4", "SYSLOG_FACILITY=%s", "1", "SYSLOG_MSGID=%s", "1011", "SYSLOG_STRUCTURED_DATA=%s", R"([exampleSDID@32473 iut="3" eventSource="Application"])", NULL ); ``` systemd-netlogd-1.4.4/TODO000066400000000000000000000000251474012624500153340ustar00rootroot00000000000000- remove unused code systemd-netlogd-1.4.4/conf/000077500000000000000000000000001474012624500155745ustar00rootroot00000000000000systemd-netlogd-1.4.4/conf/netlogd.conf.in000066400000000000000000000005651474012624500205120ustar00rootroot00000000000000[Network] #Address=239.0.0.1:6000 #Protocol=udp #TLSCertificateAuthMode=deny #TLSServerCertificate= #LogFormat=rfc5424 #Directory= #Namespace= #StructuredData= #UseSysLogStructuredData=no #UseSysLogMsgId=no #ConnectionRetrySec=30s #KeepAlive= #KeepAliveTimeSec= #KeepAliveIntervalSec= #KeepAliveProbes= #NoDelay=no #SendBuffer= #ExcludeSyslogFacility= #ExcludeSyslogLevel= systemd-netlogd-1.4.4/doc/000077500000000000000000000000001474012624500154145ustar00rootroot00000000000000systemd-netlogd-1.4.4/doc/conf.py000066400000000000000000000071011474012624500167120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # systemd-netlogd documentation build configuration file, created by # sphinx-quickstart on Sat Oct 28 19:20:43 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.todo'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'systemd-netlogd' copyright = u'2024, Susant Sahani' author = u'Susant Sahani ' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'1.4' # The full version, including alpha/beta/rc tags. release = u'1.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'systemd-netlogddoc' # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'systemd-netlogd', u'systemd-netlogd Documentation', [author], 8) ] systemd-netlogd-1.4.4/doc/index.rst000066400000000000000000000145161474012624500172640ustar00rootroot00000000000000:orphan: systend-netlogd manual page =========================== Description ----------- Forwards messages from the journal to other hosts over the network using the Syslog Protocol (RFC 5424 and RFC 3339). It can be configured to send messages to both unicast and multicast addresses. systemd-netlogd runs with own user systemd-journal-netlog. Starts sending logs when network is up and stops sending as soon as network is down (uses sd-network). It reads from journal and forwards to network one by one. It does not use any extra disk space. systemd-netlogd supports ``UDP``, ``TCP``, ``TLS`` and ``DTLS`` (Datagram Transport Layer Security RFC 6012). Configuration ------------- | | **useradd** -G systemd-journal systemd-journal-netlog This will create a user systemd-journal-netlog [NETWORK] SECTION OPTIONS ------------------------- | | The "[Network]" section only applies for UDP multicast address and Port: | ``Address=`` Controls whether log messages received by the systemd daemon shall be forwarded to a unicast UDP address or multicast UDP network group in syslog RFC 5424 format. The address string format is similar to socket units. See systemd.socket(1) | ``Protocol=`` Specifies whether to use udp, tcp, tls or dtls (Datagram Transport Layer Security) protocol. Defaults to udp. | ``LogFormat=`` Specifies whether to use RFC 5424 format or RFC 3339 format. Takes one of rfc5424 or rfc3339. Defaults to rfc5424. | ``Directory=`` Takes a directory path. Specifies whether to operate on the specified journal directory DIR instead of the default runtime and system journal paths. | ``Namespace=`` Takes a journal namespace identifier string as argument. If not specified the data collected by the default namespace is shown. If specified shows the log data of the specified namespace instead. If the namespace is specified as "*" data from all namespaces is shown, interleaved. If the namespace identifier is prefixed with "+" data from the specified namespace and the default namespace is shown, interleaved, but no other. | ``ConnectionRetrySec=`` Specifies the minimum delay before subsequent attempts to contact a Log server are made. Takes a time span value. The default unit is seconds, but other units may be specified, see systemd.time(5). Defaults to 30 seconds and must not be smaller than 1 second. | ``TLSCertificateAuthMode=`` Specifies whether to validate the certificate. Takes one of no, allow, deny, warn. Defaults to 'no' which disables certificate validation. | ``KeepAlive=`` Takes a boolean argument. If true, the TCP/IP stack will send a keep alive message after 2h (depending on the configuration of /proc/sys/net/ipv4/tcp_keepalive_time) for all TCP streams accepted on this socket. This controls the SO_KEEPALIVE socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Defaults to false. | ``KeepAliveTimeSec=`` Takes time (in seconds) as argument. The connection needs to remain idle before TCP starts sending keepalive probes. This controls the TCP_KEEPIDLE socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Default value is 7200 seconds (2 hours). | ``KeepAliveIntervalSec=`` Takes time (in seconds) as argument between individual keepalive probes, if the socket option SO_KEEPALIVE has been set on this socket. This controls the TCP_KEEPINTVL socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Default value is 75 seconds. | ``KeepAliveProbes=`` Takes an integer as argument. It is the number of unacknowledged probes to send before considering the connection dead and notifying the application layer. This controls the TCP_KEEPCNT socket option (see socket(7) and the TCP Keepalive HOWTO for details.) Default value is 9. | ``SendBuffer=`` Takes an integer argument controlling the receive or send buffer sizes of this socket, respectively. This controls the SO_SNDBUF socket options (see socket(7) for details.). The usual suffixes K, M, G are supported and are understood to the base of 1024. | ``NoDelay=`` Takes a boolean argument. TCP Nagle's algorithm works by combining a number of small outgoing messages, and sending them all at once. This controls the TCP_NODELAY socket option (see tcp(7)). Defaults to false. | Optional settings | ``StructuredData=`` Specifies the meta information about the syslog message, which can be used for Cloud Based syslog servers, such as Loggly. | ``UseSysLogStructuredData=`` A boolean. Specifies whether to extract SYSLOG_STRUCTURED_DATA= from journal. Defaults to false. | ``UseSysLogMsgId=`` A boolean. Specifies whether to extract SYSLOG_MSGID= from journal. Defaults to false. EXAMPLES -------- - Example 1. UDP Multicast:: .. code-block:: bash [Network] Address=239.0.0.1:6000 - Example 2. UDP:: .. code-block:: bash [Network] Address=192.168.8.101:514 - Example 3. Structured Data:: .. code-block:: bash [Network] Address=192.168.8.101:514 StructuredData=[1ab456b6-90bb-6578-abcd-5b734584aaaa@41058] - Example 4. TLS:: .. code-block:: bash [Network] Address=192.168.8.101:514 Protocol=tls - Example 5. DTLS:: .. code-block:: bash [Network] Address=192.168.8.101:4433 Protocol=dtls - Example 6. Custom Structured Data and Message Id:: .. code-block:: bash [Network] Address=192.168.8.101:514 #Protocol=udp LogFormat=rfc5424 UseSysLogStructuredData=yes UseSysLogMsgId=yes - Example 7. TCP:: .. code-block:: bash [Network] Address=192.168.8.101:514 Protocol=tcp - Example 8. TLS with certificate authentication mode:: .. code-block:: bash [Network] Address=192.168.8.101:514 Protocol=tls TLSCertificateAuthMode=warn - Example 9. DTLS with certificate authentication mode:: .. code-block:: bash [Network] Address=192.168.8.101:514 Protocol=tls TLSCertificateAuthMode=deny - Use case of UseSysLogStructuredData= and UseSysLogMsgId=:: .. code-block:: bash sd_journal_send( "MESSAGE=%s", "Message to process", "PRIORITY=%s", "4", "SYSLOG_FACILITY=%s", "1", "SYSLOG_MSGID=%s", "1011", "SYSLOG_STRUCTURED_DATA=%s", R"([exampleSDID@32473 iut="3" eventSource="Application"])", NULL ); systemd-netlogd-1.4.4/doc/meson.build000066400000000000000000000011121474012624500175510ustar00rootroot00000000000000sphinx_sources = [ 'conf.py', 'index.rst'] man_pages = [ 'systemd-netlogd.8'] mandir1 = join_paths(get_option('mandir'), 'man1') sphinx_build = find_program('sphinx-build-3', 'sphinx-build') custom_target( 'man', command : [sphinx_build, '-b', 'man', meson.current_source_dir(), meson.current_build_dir()], input : sphinx_sources, output : man_pages, install : true, install_dir : '/usr/share/man/man8') systemd-netlogd-1.4.4/meson.build000066400000000000000000000133421474012624500170140ustar00rootroot00000000000000project('systemd-netlogd', 'c', version : '1.4.4', license : 'LGPL-2.1-or-later', default_options: [ 'c_std=gnu11', 'prefix=/usr/lib/systemd', 'sysconfdir=/etc/systemd', 'localstatedir=/var', 'warning_level=2', ], meson_version : '>= 0.51') conf = configuration_data() conf.set_quoted('PACKAGE_VERSION', meson.project_version()) conf.set_quoted('PACKAGE_STRING', meson.project_name() + ' ' + meson.project_version()) conf.set('PACKAGE_URL', 'https://github.com/systemd/systemd-netlogd') conf.set('PKGPREFIX', get_option('prefix')) conf.set_quoted('PKGSYSCONFDIR', get_option('sysconfdir')) ##################################################################### cc = meson.get_compiler('c') c_args = ''' -Werror=undef -Werror=format=2 -Wformat-security -Wformat-nonliteral -Wlogical-op -Wmissing-include-dirs -Werror=old-style-definition -Werror=pointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wsuggest-attribute=noreturn -Werror=missing-prototypes -Werror=implicit-function-declaration -Werror=missing-declarations -Werror=return-type -Werror=incompatible-pointer-types -Werror=shadow -Wstrict-prototypes -Wredundant-decls -Wmissing-noreturn -Wendif-labels -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Werror=overflow -Werror=sign-compare -Wdate-time -Wnested-externs -ffast-math -fno-common -fdiagnostics-show-option -fno-strict-aliasing -fvisibility=hidden -fstack-protector -fstack-protector-strong -fPIE --param=ssp-buffer-size=4 '''.split() foreach arg : c_args if cc.has_argument(arg) add_project_arguments(arg, language : 'c') endif endforeach conf.set('_GNU_SOURCE', true) conf.set('__SANE_USERSPACE_TYPES__', true) conf.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix : '#include ')) conf.set('SIZEOF_UID_T', cc.sizeof('uid_t', prefix : '#include ')) conf.set('SIZEOF_GID_T', cc.sizeof('gid_t', prefix : '#include ')) conf.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix : '#include ')) conf.set('SIZEOF_INO_T', cc.sizeof('ino_t', prefix : '#include ')) conf.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix : '#include ')) conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include ')) foreach ident : [ ['gettid', '''#include #include '''], ] have = cc.has_function(ident[0], prefix : ident[1]) conf.set10('HAVE_' + ident[0].to_upper(), have) endforeach if cc.has_function('getrandom', prefix : '''#include ''') conf.set10('USE_SYS_RANDOM_H', true) conf.set10('HAVE_GETRANDOM', true) else have = cc.has_function('getrandom', prefix : '''#include ''') conf.set10('USE_SYS_RANDOM_H', false) conf.set10('HAVE_GETRANDOM', have) endif ############################################################ gperf = find_program('gperf') gperf_test_format = ''' #include const char * in_word_set(const char *, @0@); @1@ ''' gperf_snippet = run_command('sh', '-c', 'echo foo,bar | "$1" -L ANSI-C', '_', gperf, check: true) gperf_test = gperf_test_format.format('size_t', gperf_snippet.stdout()) if cc.compiles(gperf_test) gperf_len_type = 'size_t' else gperf_test = gperf_test_format.format('unsigned', gperf_snippet.stdout()) if cc.compiles(gperf_test) gperf_len_type = 'unsigned' else error('unable to determine gperf len type') endif endif message('gperf len type is @0@'.format(gperf_len_type)) conf.set('GPERF_LEN_TYPE', gperf_len_type, description : 'The type of gperf "len" parameter') ############################################################ libopenssl = dependency('openssl', version : '>= 1.1.0', required : get_option('openssl')) conf.set10('HAVE_OPENSSL', libopenssl.found()) ############################################################ config_h = configure_file( output : 'config.h', configuration : conf) add_project_arguments('-include', 'config.h', language : 'c') subdir('src') includes = include_directories('src/share', 'src/netlog') subdir('units') subdir('doc') ############################################################ libsystemd = dependency('libsystemd', version : '>= 230') libcap = dependency('libcap', required : false) if not libcap.found() # Compat with Ubuntu 14.04 which ships libcap w/o .pc file libcap = cc.find_library('cap') endif systemd_netlogd_conf = configure_file( input : 'conf/netlogd.conf.in', output : 'netlogd.conf', configuration : conf) install_data(systemd_netlogd_conf, install_dir : get_option('sysconfdir')) systemd_netlogd = executable( 'systemd-netlogd', systemd_netlogd_sources, include_directories : includes, link_with : libshared, dependencies : [ libcap, libopenssl, libsystemd], install : true, install_dir : get_option('prefix')) systemd-netlogd-1.4.4/meson_options.txt000066400000000000000000000003011474012624500202760ustar00rootroot00000000000000option('version-tag', type : 'string', description : 'override the git version string') option('openssl', type : 'boolean', value : true, description : 'enable openssl support') systemd-netlogd-1.4.4/src/000077500000000000000000000000001474012624500154365ustar00rootroot00000000000000systemd-netlogd-1.4.4/src/.gitignore000066400000000000000000000000461474012624500174260ustar00rootroot00000000000000/netlog-gperf.c /systemd-netlogd.conf systemd-netlogd-1.4.4/src/meson.build000066400000000000000000000113571474012624500176070ustar00rootroot00000000000000 libshared_sources = files(''' share/missing_syscall.h share/missing.h share/def.h share/capability-util.c share/capability-util.h share/conf-parser.c share/conf-parser.h share/conf-files.c share/conf-files.h share/dns-def.h share/dns-domain.c share/dns-domain.h share/hostname-util.c share/hostname-util.h share/alloc-util.c share/alloc-util.h share/build.h share/set.h share/hashmap.c share/hashmap.h share/siphash24.c share/siphash24.h share/utf8.c share/utf8.h share/strv.c share/strv.h share/network-util.c share/network-util.h share/in-addr-util.c share/in-addr-util.h share/extract-word.c share/extract-word.h share/util.c share/util.h share/log.c share/log.h share/macro.h share/signal-util.c share/signal-util.h share/syslog-util.c share/syslog-util.h share/time-util.c share/time-util.h share/ioprio.h share/io-util.c share/io-util.h share/iovec-util.c share/iovec-util.h share/escape.c share/escape.h share/user-util.c share/user-util.h share/process-util.c share/process-util.h share/terminal-util.c share/terminal-util.h share/proc-cmdline.c share/proc-cmdline.h share/socket-util.c share/socket-util.h share/dirent-util.c share/dirent-util.h share/fd-util.c share/fd-util.h share/sparse-endian.h share/fileio.c share/fileio.h share/formats-util.h share/hash-funcs.c share/hash-funcs.h share/hexdecoct.c share/hexdecoct.h share/list.h share/mempool.c share/mempool.h share/parse-util.c share/parse-util.h share/path-util.c share/path-util.h share/random-util.c share/random-util.h share/ratelimit.c share/ratelimit.h share/stdio-util.h share/openssl-util.h share/string-table.c share/string-table.h share/string-util.c share/string-util.h share/unaligned.h share/stat-util.c share/stat-util.h share/fs-util.c share/fs-util.h share/mkdir.c share/mkdir.h share/virt.c share/virt.h share/sd-network.h share/sd-network.c share/sd-resolve.h share/sd-resolve.c '''.split()) libshared = static_library( 'shared', libshared_sources) systemd_netlogd_sources = files(''' netlog/systemd-netlogd.c netlog/netlog-conf.h netlog/netlog-conf.c netlog/netlog-manager.c netlog/netlog-manager.h netlog/netlog-network.c netlog/netlog-network.h netlog/netlog-protocol.c netlog/netlog-protocol.h netlog/netlog-dtls.c netlog/netlog-dtls.h netlog/netlog-ssl.c netlog/netlog-ssl.h netlog/netlog-tls.c netlog/netlog-tls.h '''.split()) netlogd_gperf_c = custom_target( 'netlog-gperf.c', input : 'netlog/netlog-gperf.gperf', output : 'netlog-gperf.c', command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) systemd_netlogd_sources += [netlogd_gperf_c] systemd-netlogd-1.4.4/src/netlog/000077500000000000000000000000001474012624500167265ustar00rootroot00000000000000systemd-netlogd-1.4.4/src/netlog/netlog-conf.c000066400000000000000000000272461474012624500213200ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include "conf-parser.h" #include "def.h" #include "extract-word.h" #include "in-addr-util.h" #include "netlog-conf.h" #include "parse-util.h" #include "sd-resolve.h" #include "string-util.h" int config_parse_netlog_remote_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Manager *m = userdata; char *e; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); assert(m); r = socket_address_parse(&m->address, rvalue); if (r < 0) { const struct addrinfo hints = { .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG, .ai_socktype = SOCK_DGRAM, .ai_family = socket_ipv6_is_supported() ? AF_UNSPEC : AF_INET, }; uint32_t u; e = strchr(rvalue, ':'); if (e) { r = safe_atou(e+1, &u); if (r < 0) return r; if (u <= 0 || u > 0xFFFF) return -EINVAL; m->port = u; m->server_name = strndup(rvalue, e-rvalue); if (!m->server_name) return log_oom(); log_debug("Remote server='%s' port: '%u'...", m->server_name, u); /* Tell the resolver to reread /etc/resolv.conf, in * case it changed. */ res_init(); log_debug("Resolving %s...", m->server_name); r = sd_resolve_getaddrinfo(m->resolve, &m->resolve_query, m->server_name, NULL, &hints, manager_resolve_handler, m); if (r < 0) return log_error_errno(r, "Failed to create resolver: %m"); m->resolving = true; return 0; } log_syntax(unit, LOG_WARNING, filename, line, -r, "Failed to parse '%s=%s', ignoring.", lvalue, rvalue); return 0; } return 0; } int config_parse_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Manager *m = userdata; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); assert(m); r = protocol_from_string(rvalue); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, -r, "Failed to parse '%s=%s', ignoring.", lvalue, rvalue); return 0; } m->protocol = r; return 0; } int config_parse_log_format(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Manager *m = userdata; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); assert(m); r = log_format_from_string(rvalue); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, -r, "Failed to parse '%s=%s', ignoring.", lvalue, rvalue); return 0; } m->log_format = r; return 0; } int config_parse_tls_certificate_auth_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Manager *m = userdata; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); assert(m); r = certificate_auth_mode_from_string(rvalue); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, -r, "Failed to parse '%s=%s', ignoring.", lvalue, rvalue); return 0; } m->auth_mode = r; return 0; } int config_parse_namespace(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Manager *m = userdata; assert(filename); assert(lvalue); assert(rvalue); assert(data); assert(m); if (streq(rvalue, "*")) m->namespace_flags = SD_JOURNAL_ALL_NAMESPACES; else if (startswith(rvalue, "+")) { m->namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE; m->namespace = strdup(rvalue); if (!m->namespace) return log_oom(); } else { m->namespace = strdup(rvalue); if (!m->namespace) return log_oom(); } return 0; } int config_parse_syslog_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Manager *m = userdata; uint32_t val = 0; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); assert(m); for (const char *p = rvalue;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue); return 0; } if (r == 0) break; r = syslog_facility_from_string(word); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syslog facility '%s', ignoring", word); } else val |= UINT32_C(1) << r; } m->excluded_syslog_facilities = val; return 0; } int config_parse_syslog_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { Manager *m = userdata; uint8_t val = 0; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); assert(m); for (const char *p = rvalue;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue); return 0; } if (r == 0) break; r = syslog_level_from_string(word); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syslog level '%s', ignoring", word); } else val |= UINT8_C(1) << r; } m->excluded_syslog_levels = val; return 0; } int manager_parse_config_file(Manager *m) { int r; assert(m); r = config_parse_many(PKGSYSCONFDIR "/netlogd.conf", CONF_PATHS_NULSTR("systemd/netlogd.conf.d"), "Network\0", config_item_perf_lookup, netlog_gperf_lookup, false, m); if (r < 0) return r; if (m->connection_retry_usec < 1 * USEC_PER_SEC) { log_warning("Invalid ConnectionRetrySec=. Using default value."); m->connection_retry_usec = DEFAULT_CONNECTION_RETRY_USEC; } if (m->auth_mode != OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY && m->protocol != SYSLOG_TRANSMISSION_PROTOCOL_TLS && m->protocol != SYSLOG_TRANSMISSION_PROTOCOL_DTLS) log_warning("TLSCertificateAuthMode= set but unencrypted %s connection specified.", protocol_to_string(m->protocol)); if (m->server_cert && m->protocol != SYSLOG_TRANSMISSION_PROTOCOL_TLS && m->protocol != SYSLOG_TRANSMISSION_PROTOCOL_DTLS) log_warning("TLSServerCertificate= set but unencrypted %s connection specified.", protocol_to_string(m->protocol)); if (m->dir && m->namespace) log_warning("Ignoring Namespace= setting since Directory= is set."); if (m->structured_data && m->syslog_structured_data) log_warning("Ignoring UseSysLogStructuredData= since StructuredData= is set."); if (timestamp_is_set(m->keep_alive_time) && !m->keep_alive) log_warning("Ignoring KeepAliveTimeSec= since KeepAlive= is not set."); if (m->keep_alive_interval > 0 && !m->keep_alive) log_warning("Ignoring KeepAliveIntervalSec= since KeepAlive= is not set."); if (m->keep_alive_cnt > 0 && !m->keep_alive) log_warning("Ignoring KeepAliveProbes= since KeepAlive= is not set."); if (m->send_buffer != 0 && (m->send_buffer < 4096 || m->send_buffer > 128 * 1024 * 1024)) log_warning("SendBuffer= set to an suspicious value of %zu.", m->send_buffer); return 0; } systemd-netlogd-1.4.4/src/netlog/netlog-conf.h000066400000000000000000000073151474012624500213200ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "in-addr-util.h" #include "conf-parser.h" #include "netlog-manager.h" const struct ConfigPerfItem* netlog_gperf_lookup(const char *key, size_t length); int config_parse_netlog_remote_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_log_format(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tls_certificate_auth_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_namespace(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_syslog_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_syslog_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int manager_parse_config_file(Manager *m); systemd-netlogd-1.4.4/src/netlog/netlog-dtls.c000066400000000000000000000173321474012624500213340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "netlog-dtls.h" #include #include #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "io-util.h" #include "iovec-util.h" #include "netlog-ssl.h" static int dtls_write(DTLSManager *m, const char *buf, size_t count) { int r; assert(m); assert(m->ssl); assert(m->pretty_address); assert(buf); assert(count > 0); assert(count < INT_MAX); ERR_clear_error(); r = SSL_write(m->ssl, buf, count); if (r <= 0) { int error = SSL_get_error(m->ssl, r); if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) return log_info_errno(SYNTHETIC_ERRNO(EAGAIN), "DTLS: Failed to invoke SSL_write to %s: %s", m->pretty_address, TLS_ERROR_STRING(error)); else return log_error_errno(SYNTHETIC_ERRNO(EPIPE), "DTLS: Failed to invoke SSL_write to %s: %s", m->pretty_address, TLS_ERROR_STRING(error)); } return log_debug("DTLS: Successful SSL_write: %d bytes", r); } int dtls_datagram_writev(DTLSManager *m, const struct iovec *iov, size_t iovcnt) { _cleanup_free_ char *buf = NULL; size_t count; assert(m); assert(iov); /* single buffer. Suboptimal, but better than multiple SSL_write calls. */ count = iovec_total_size(iov, iovcnt); assert(count > 0); buf = new(char, count); if (!buf) return log_oom(); for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++) memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len); return dtls_write(m, buf, count); } int dtls_connect(DTLSManager *m, SocketAddress *address) { _cleanup_(BIO_freep) BIO *bio = NULL; _cleanup_(SSL_freep) SSL *ssl = NULL; _cleanup_free_ char *pretty = NULL; const SSL_CIPHER *cipher; socklen_t salen; struct timeval timeout = { .tv_sec = 3, .tv_usec = 0, }; _cleanup_close_ int fd = -1; int r; assert(m); assert(m->ctx); assert(address); switch (address->sockaddr.sa.sa_family) { case AF_INET: salen = sizeof(address->sockaddr.in); break; case AF_INET6: salen = sizeof(address->sockaddr.in6); break; default: return -EAFNOSUPPORT; } fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return log_error_errno(errno, "DTLS: Failed to allocate socket: %m"); r = sockaddr_pretty(&address->sockaddr.sa, salen, true, true, &pretty); if (r < 0) return r; r = connect(fd, &address->sockaddr.sa, salen); if (r < 0 && errno != EINPROGRESS) return log_error_errno(errno, "DTLS: Failed to connect to remote server='%s': %m", pretty); log_debug("DTLS: Connected to remote server: '%s'", pretty); /* Create BIO from socket array! */ bio = BIO_new_dgram(fd, BIO_NOCLOSE); if (!bio) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "DTLS: Failed to allocate memory for bio: %m"); BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &address); /* Set and activate timeouts */ BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); ssl = SSL_new(m->ctx); if (!ssl) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "DTLS: Failed to allocate memory for ssl: %s", ERR_error_string(ERR_get_error(), NULL)); SSL_set_bio(ssl, bio, bio); bio = NULL; /* Certification verification */ if (m->auth_mode != OPEN_SSL_CERTIFICATE_AUTH_MODE_NONE) { log_debug("DTLS: enable certificate verification"); SSL_set_ex_data(ssl, EX_DATA_TLSMANAGER, m); SSL_set_ex_data(ssl, EX_DATA_PRETTYADDRESS, pretty); SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_certificate_validity); } else { log_debug("DTLS: disable certificate verification"); SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); } r = SSL_connect(ssl); if (r <= 0) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "DTLS: Failed to SSL_connect: %s", ERR_error_string(ERR_get_error(), NULL)); cipher = SSL_get_current_cipher(ssl); log_debug("DTLS: SSL Cipher Version: %s Name: %s", SSL_CIPHER_get_version(cipher), SSL_CIPHER_get_name(cipher)); if (DEBUG_LOGGING) { _cleanup_(X509_freep) X509* cert = NULL; cert = SSL_get_peer_certificate(ssl); if (cert) { _cleanup_(OPENSSL_freep) void *subject = NULL, *issuer = NULL; subject = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); log_debug("DTLS: Subject: %s", (char *) subject); issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); log_debug("DTLS: Issuer: %s", (char *) issuer); } else log_debug("DTLS: No certificates."); } m->ssl = TAKE_PTR(ssl); m->fd = TAKE_FD(fd); m->pretty_address = TAKE_PTR(pretty); m->connected = true; return 0; } void dtls_disconnect(DTLSManager *m) { if (!m) return; ERR_clear_error(); if (m->ssl) { SSL_shutdown(m->ssl); SSL_free(m->ssl); m->ssl = NULL; } m->pretty_address = mfree(m->pretty_address); m->fd = safe_close(m->fd); m->connected = false; } void dtls_manager_free(DTLSManager *m) { if (!m) return; if (m->ctx) SSL_CTX_free(m->ctx); free(m); } int dtls_manager_init(OpenSSLCertificateAuthMode auth_mode, const char *server_cert, DTLSManager **ret) { _cleanup_(dtls_manager_freep) DTLSManager *m = NULL; _cleanup_(SSL_CTX_freep) SSL_CTX *ctx = NULL; int r; ctx = SSL_CTX_new(DTLS_method()); if (!ctx) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "DTLS: Failed to allocate memory for SSL CTX: %m"); if (server_cert) { r = SSL_CTX_load_verify_file(ctx, server_cert); if (r != 1) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),"DTLS: Failed to load CA certificate from '%s': %s", server_cert, ERR_error_string(ERR_get_error(), NULL)); } else { r = SSL_CTX_set_default_verify_paths(ctx); if (r != 1) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "DTLS: Failed to load default CA certificates: %s", ERR_error_string(ERR_get_error(), NULL)); } SSL_CTX_set_verify_depth(ctx, VERIFICATION_DEPTH + 1); m = new(DTLSManager, 1); if (!m) return log_oom(); *m = (DTLSManager) { .auth_mode = auth_mode, .ctx = TAKE_PTR(ctx), .fd = -1, }; *ret = TAKE_PTR(m); return 0; } systemd-netlogd-1.4.4/src/netlog/netlog-dtls.h000066400000000000000000000014331474012624500213340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include "socket-util.h" #include "openssl-util.h" #include "netlog-tls.h" typedef struct DTLSManager DTLSManager; struct DTLSManager { SSL_CTX *ctx; SSL *ssl; char *pretty_address; int fd; bool connected; OpenSSLCertificateAuthMode auth_mode; }; void dtls_manager_free(DTLSManager *m); int dtls_manager_init(OpenSSLCertificateAuthMode auth_mode, const char *server_cert, DTLSManager **ret); int dtls_connect(DTLSManager *m, SocketAddress *addr); void dtls_disconnect(DTLSManager *m); int dtls_datagram_writev(DTLSManager *m, const struct iovec *iov, size_t iovcnt); DEFINE_TRIVIAL_CLEANUP_FUNC(DTLSManager*, dtls_manager_free); systemd-netlogd-1.4.4/src/netlog/netlog-gperf.gperf000066400000000000000000000045641474012624500223550ustar00rootroot00000000000000%{ #include #include "conf-parser.h" #include "netlog-conf.h" #include "netlog-manager.h" %} struct ConfigPerfItem; %null_strings %language=ANSI-C %define slot-name section_and_lvalue %define hash-function-name netlog_gperf_hash %define lookup-function-name netlog_gperf_lookup %readonly-tables %omit-struct-type %struct-type %includes %% Network.Address, config_parse_netlog_remote_address, 0, 0 Network.Protocol, config_parse_protocol, 0, offsetof(Manager, protocol) Network.LogFormat, config_parse_log_format, 0, offsetof(Manager, log_format) Network.Directory, config_parse_string, 0, offsetof(Manager, dir) Network.Namespace, config_parse_namespace, 0, offsetof(Manager, namespace) Network.StructuredData, config_parse_string, 0, offsetof(Manager, structured_data) Network.UseSysLogStructuredData, config_parse_bool, 0, offsetof(Manager, syslog_structured_data) Network.UseSysLogMsgId, config_parse_bool, 0, offsetof(Manager, syslog_msgid) Network.ConnectionRetrySec, config_parse_sec, 0, offsetof(Manager, connection_retry_usec) Network.TLSCertificateAuthMode, config_parse_tls_certificate_auth_mode, 0, offsetof(Manager, auth_mode) Network.TLSServerCertificate, config_parse_string, 0, offsetof(Manager, server_cert) Network.KeepAlive, config_parse_bool, 0, offsetof(Manager, keep_alive) Network.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Manager, keep_alive_time) Network.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Manager, keep_alive_interval) Network.KeepAliveProbes, config_parse_unsigned, 0, offsetof(Manager, keep_alive_cnt) Network.NoDelay, config_parse_bool, 0, offsetof(Manager, no_delay) Network.SendBuffer, config_parse_iec_size, 0, offsetof(Manager, send_buffer) Network.ExcludeSyslogFacility, config_parse_syslog_facility, 0, offsetof(Manager, excluded_syslog_facilities) Network.ExcludeSyslogLevel, config_parse_syslog_level, 0, offsetof(Manager, excluded_syslog_levels) systemd-netlogd-1.4.4/src/netlog/netlog-manager.c000066400000000000000000000562321474012624500220020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include "capability-util.h" #include "fileio.h" #include "mkdir.h" #include "conf-parser.h" #include "fd-util.h" #include "netlog-manager.h" #include "network-util.h" #include "parse-util.h" #include "signal-util.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" #include "util.h" /* Default severity LOG_NOTICE */ #define JOURNAL_DEFAULT_SEVERITY LOG_PRI(LOG_NOTICE) /* Default facility LOG_USER */ #define JOURNAL_DEFAULT_FACILITY LOG_FAC(LOG_USER) #define RATELIMIT_INTERVAL_USEC (10*USEC_PER_SEC) #define RATELIMIT_BURST 10 #define JOURNAL_FOREACH_DATA_RETVAL(j, data, l, retval) \ for (sd_journal_restart_data(j); ((retval) = sd_journal_enumerate_data((j), &(data), &(l))) > 0; ) static const char *const protocol_table[_SYSLOG_TRANSMISSION_PROTOCOL_MAX] = { [SYSLOG_TRANSMISSION_PROTOCOL_UDP] = "udp", [SYSLOG_TRANSMISSION_PROTOCOL_TCP] = "tcp", [SYSLOG_TRANSMISSION_PROTOCOL_DTLS] = "dtls", [SYSLOG_TRANSMISSION_PROTOCOL_TLS] = "tls", }; DEFINE_STRING_TABLE_LOOKUP(protocol, SysLogTransmissionProtocol); static const char *const log_format_table[_SYSLOG_TRANSMISSION_LOG_FORMAT_MAX] = { [SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5424] = "rfc5424", [SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_3339] = "rfc3339", }; DEFINE_STRING_TABLE_LOOKUP(log_format, SysLogTransmissionLogFormat); static const char *const syslog_facility_table[_SYSLOG_FACILITY_MAX] = { [SYSLOG_FACILITY_KERN] = "kern", [SYSLOG_FACILITY_USER] = "user", [SYSLOG_FACILITY_MAIL] = "mail", [SYSLOG_FACILITY_DAEMON] = "daemon", [SYSLOG_FACILITY_AUTH] = "auth", [SYSLOG_FACILITY_SYSLOG] = "syslog", [SYSLOG_FACILITY_LPR] = "lpr", [SYSLOG_FACILITY_NEWS] = "news", [SYSLOG_FACILITY_UUCP] = "uucp", [SYSLOG_FACILITY_CRON] = "cron", [SYSLOG_FACILITY_AUTHPRIV] = "authpriv", [SYSLOG_FACILITY_FTP] = "ftp", [SYSLOG_FACILITY_NTP] = "ntp", [SYSLOG_FACILITY_SECURITY] = "security", [SYSLOG_FACILITY_CONSOLE] = "console", [SYSLOG_FACILITY_SOLARIS_CRON] = "solaris-cron", [SYSLOG_FACILITY_LOCAL0] = "local0", [SYSLOG_FACILITY_LOCAL1] = "local1", [SYSLOG_FACILITY_LOCAL2] = "local2", [SYSLOG_FACILITY_LOCAL3] = "local3", [SYSLOG_FACILITY_LOCAL4] = "local4", [SYSLOG_FACILITY_LOCAL5] = "local5", [SYSLOG_FACILITY_LOCAL6] = "local6", [SYSLOG_FACILITY_LOCAL7] = "local7", }; DEFINE_STRING_TABLE_LOOKUP(syslog_facility, SysLogFacility); static const char *const syslog_level_table[_SYSLOG_LEVEL_MAX] = { [SYSLOG_LEVEL_EMERGENCY] = "emerg", [SYSLOG_LEVEL_ALERT] = "alert", [SYSLOG_LEVEL_CRITICAL] = "crit", [SYSLOG_LEVEL_ERROR] = "err", [SYSLOG_LEVEL_WARNING] = "warning", [SYSLOG_LEVEL_NOTICE] = "notice", [SYSLOG_LEVEL_INFORMATIONAL] = "info", [SYSLOG_LEVEL_DEBUG] = "debug", }; DEFINE_STRING_TABLE_LOOKUP(syslog_level, SysLogLevel); typedef struct ParseFieldVec { const char *field; size_t field_len; char **target; size_t *target_len; } ParseFieldVec; #define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) { \ .field = (_field), \ .field_len = strlen(_field), \ .target = (_target), \ .target_len = (_target_len) \ } static int parse_field( const void *data, size_t length, const char *field, size_t field_len, char **target, size_t *target_len) { size_t nl; char *buf; assert(data); assert(field); assert(target); if (length < field_len) return 0; if (memcmp(data, field, field_len)) return 0; nl = length - field_len; buf = newdup_suffix0(char, (const char*) data + field_len, nl); if (!buf) return log_oom(); free_and_replace(*target, buf); if (target_len) *target_len = nl; return 1; } static int parse_fieldv( const void *data, size_t length, const ParseFieldVec *fields, size_t n_fields) { int r; for (size_t i = 0; i < n_fields; i++) { const ParseFieldVec *f = &fields[i]; r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len); if (r < 0) return r; if (r > 0) break; } return 0; } static int manager_read_journal_input(Manager *m) { _cleanup_free_ char *facility = NULL, *identifier = NULL, *priority = NULL, *message = NULL, *pid = NULL, *hostname = NULL, *structured_data = NULL, *msgid = NULL, *cursor = NULL; size_t hostname_len = 0, identifier_len = 0, message_len = 0, priority_len = 0, facility_len = 0, structured_data_len = 0, msgid_len = 0, pid_len = 0; unsigned sev = JOURNAL_DEFAULT_SEVERITY; unsigned fac = JOURNAL_DEFAULT_FACILITY; struct timeval tv, *tvp = NULL; const void *data; usec_t realtime; size_t length; int r; const ParseFieldVec fields[] = { PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len ), PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len ), PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len ), PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len ), PARSE_FIELD_VEC_ENTRY("SYSLOG_FACILITY=", &facility, &facility_len ), PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len ), PARSE_FIELD_VEC_ENTRY("SYSLOG_STRUCTURED_DATA=", &structured_data, &structured_data_len ), PARSE_FIELD_VEC_ENTRY("SYSLOG_MSGID", &msgid, &msgid_len ), }; assert(m); assert(m->journal); r = sd_journal_get_cursor(m->journal, &cursor); if (r < 0) return log_error_errno(r, "Failed to get cursor: %m"); log_debug("Reading from journal cursor=%s", cursor); JOURNAL_FOREACH_DATA_RETVAL(m->journal, data, length, r) { r = parse_fieldv(data, length, fields, ELEMENTSOF(fields)); if (r < 0) return r; } if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) { log_debug_errno(r, "Skipping message we can't read: %m"); return 0; } if (r < 0) return log_error_errno(r, "Failed to get journal fields: %m"); if (!message) { log_debug("Skipping message without MESSAGE= field."); return 0; } else log_debug("Received from journal MESSAGE='%s'", message); r = sd_journal_get_realtime_usec(m->journal, &realtime); if (r < 0) log_warning_errno(r, "Failed to rerieve realtime from journal: %m"); else { tv = (struct timeval) { .tv_sec = realtime / USEC_PER_SEC, .tv_usec = realtime % USEC_PER_SEC, }; tvp = &tv; } if (facility) { r = safe_atou(facility, &fac); if (r < 0) log_debug("Failed to parse syslog facility: %s", facility); else if (fac < _SYSLOG_FACILITY_MAX && ((UINT32_C(1) << fac) & m->excluded_syslog_facilities)) { log_debug("Skipping message with excluded syslog facility %s.", syslog_facility_to_string(fac)); return 0; } if (fac >= LOG_NFACILITIES) fac = JOURNAL_DEFAULT_FACILITY; } if (priority) { r = safe_atou(priority, &sev); if (r < 0) log_debug("Failed to parse syslog priority: %s", priority); else if (sev < _SYSLOG_LEVEL_MAX && ((UINT8_C(1) << sev) & m->excluded_syslog_levels)) { log_debug("Skipping message with excluded syslog level %s.", syslog_level_to_string(sev)); return 0; } if (sev > LOG_DEBUG) sev = JOURNAL_DEFAULT_SEVERITY; } return manager_push_to_network(m, sev, fac, identifier, message, hostname, pid, tvp, structured_data, m->syslog_msgid ? msgid : NULL); } static int update_cursor_state(Manager *m) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(m); if (!m->state_file || !m->last_cursor) return 0; r = fopen_temporary(m->state_file, &f, &temp_path); if (r < 0) goto finish; r = fchmod(fileno(f), 0644); if (r < 0) log_warning_errno(errno, "Failed to set mode of state %s: %m", m->state_file); fprintf(f, "# This is private data. Do not parse.\n" "LAST_CURSOR=%s\n", m->last_cursor); r = fflush_and_check(f); if (r < 0) goto finish; if (rename(temp_path, m->state_file) < 0) { r = -errno; goto finish; } finish: if (r < 0) log_error_errno(r, "Failed to save state %s: %m", m->state_file); if (temp_path) (void) unlink(temp_path); return r; } static int load_cursor_state(Manager *m) { int r; assert(m); if (!m->state_file) return 0; r = parse_env_file(m->state_file, NEWLINE, "LAST_CURSOR", &m->last_cursor, NULL); if (r < 0 && r != -ENOENT) return r; log_debug("Last cursor was %s.", m->last_cursor ? m->last_cursor : "not available"); return 0; } static int process_journal_input(Manager *m) { _cleanup_free_ char *cursor = NULL; int r; assert(m); assert(m->journal); for (;;) { r = sd_journal_next(m->journal); if (r < 0) return log_error_errno(r, "Failed to get next entry: %m"); if (r == 0) break; r = manager_read_journal_input(m); if (r < 0) { /* Can't send the message. Seek one entry back. */ r = sd_journal_previous(m->journal); if (r < 0) log_error_errno(r, "Failed to iterate through journal: %m"); break; } } r = sd_journal_get_cursor(m->journal, &cursor); if (r < 0) { log_error_errno(r, "Failed to get cursor: %m"); cursor = mfree(cursor); } free(m->last_cursor); m->last_cursor = cursor; cursor = NULL; return update_cursor_state(m); } static int manager_journal_event_handler(sd_event_source *event, int fd, uint32_t revents, void *userp) { Manager *m = userp; int r; assert(m); assert(m->journal); assert(m->journal_watch_fd == fd); if (revents & EPOLLHUP) { log_debug("Received HUP"); return 0; } if (!(revents & EPOLLIN)) { log_warning("Unexpected poll event %"PRIu32".", revents); return -EINVAL; } r = sd_journal_process(m->journal); if (r < 0) { log_error_errno(r, "Failed to process journal: %m"); manager_disconnect(m); return r; } if (r == SD_JOURNAL_NOP) return 0; return process_journal_input(m); } static void close_journal_input(Manager *m) { assert(m); if (m->journal) { log_debug("Closing journal input."); sd_journal_close(m->journal); m->journal = NULL; m->journal_watch_fd = -1; } } static int manager_signal_event_handler(sd_event_source *event, const struct signalfd_siginfo *si, void *userdata) { Manager *m = userdata; assert(m); log_received_signal(LOG_INFO, si); manager_disconnect(m); sd_event_exit(m->event, 0); return 0; } static int open_journal(Manager *m) { int r; assert(m); if (m->dir) r = sd_journal_open_directory(&m->journal, m->dir, 0); else if (m->namespace) r = sd_journal_open_namespace(&m->journal, m->namespace, SD_JOURNAL_LOCAL_ONLY | m->namespace_flags); else r = sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY); if (r < 0) log_error_errno(r, "Failed to open %s: %m", m->dir ?: m->namespace ? "namespace journal" : "journal"); return r; } static int manager_journal_monitor_listen(Manager *m) { int r, events; assert(m); r = open_journal(m); if (r < 0) return r; r = sd_journal_set_data_threshold(m->journal, 0); if (r < 0) log_warning_errno(r, "Failed to set journal data field size threshold"); m->journal_watch_fd = sd_journal_get_fd(m->journal); if (m->journal_watch_fd < 0) return log_error_errno(m->journal_watch_fd, "Failed to get journal fd: %m"); events = sd_journal_get_events(m->journal); r = sd_event_add_io(m->event, &m->event_journal_input, m->journal_watch_fd, events, manager_journal_event_handler, m); if (r < 0) return log_error_errno(r, "Failed to register input event: %m"); /* ignore failure */ if (!m->last_cursor) (void) load_cursor_state(m); if (m->last_cursor) { r = sd_journal_seek_cursor(m->journal, m->last_cursor); if (r < 0) return log_error_errno(r, "Failed to seek to cursor %s: %m", m->last_cursor); } return 0; } static int manager_retry_connect(sd_event_source *source, usec_t usec, void *userdata) { Manager *m = ASSERT_PTR(userdata); return manager_connect(m); } int manager_connect(Manager *m) { int r; assert(m); if (m->resolving) return 0; manager_disconnect(m); log_debug("Connecting network ..."); m->event_retry = sd_event_source_unref(m->event_retry); if (!ratelimit_below(&m->ratelimit)) { log_debug("Delaying attempts to contact servers."); r = sd_event_add_time_relative(m->event, &m->event_retry, CLOCK_BOOTTIME, m->connection_retry_usec, 0, manager_retry_connect, m); if (r < 0) return log_error_errno(r, "Failed to create retry timer: %m"); return 0; } switch (m->protocol) { case SYSLOG_TRANSMISSION_PROTOCOL_DTLS: r = dtls_connect(m->dtls, &m->address); break; case SYSLOG_TRANSMISSION_PROTOCOL_TLS: r = tls_connect(m->tls, &m->address); break; default: r = manager_open_network_socket(m); break; } if (r < 0) { log_error_errno(r, "Failed to create network socket: %m"); return manager_connect(m); } r = manager_journal_monitor_listen(m); if (r < 0) return log_error_errno(r, "Failed to monitor journal: %m"); return 0; } void manager_disconnect(Manager *m) { assert(m); log_debug("Disconnecting network ..."); m->resolve_query = sd_resolve_query_unref(m->resolve_query); manager_close_network_socket(m); dtls_disconnect(m->dtls); tls_disconnect(m->tls); m->event_journal_input = sd_event_source_disable_unref(m->event_journal_input); close_journal_input(m); sd_notifyf(false, "STATUS=Idle."); } int manager_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { Manager *m = userdata; assert(q); assert(m); assert(m->server_name); log_debug("Resolve %s: %s", m->server_name, gai_strerror(ret)); m->resolve_query = sd_resolve_query_unref(m->resolve_query); if (ret != 0) { log_debug("Failed to resolve %s: %s", m->server_name, gai_strerror(ret)); /* Try next host */ return manager_connect(m); } for (; ai; ai = ai->ai_next) { _cleanup_free_ char *pretty = NULL; assert(ai->ai_addr); assert(ai->ai_addrlen >= offsetof(struct sockaddr, sa_data)); if (!IN_SET(ai->ai_addr->sa_family, AF_INET, AF_INET6)) { log_warning("Unsuitable address protocol for %s", m->server_name); continue; } memcpy(&m->address.sockaddr, (const union sockaddr_union*) ai->ai_addr, ai->ai_addrlen); if (ai->ai_addr->sa_family == AF_INET6) m->address.sockaddr.in6.sin6_port = htobe16((uint16_t) m->port); else m->address.sockaddr.in.sin_port = htobe16((uint16_t) m->port); sockaddr_pretty(&m->address.sockaddr.sa, ai->ai_addrlen, true, true, &pretty); log_debug("Resolved address %s for %s.", pretty, m->server_name); /* take the first one */ break; } if (!IN_SET(m->address.sockaddr.sa.sa_family, AF_INET, AF_INET6)) { log_error("Failed to find suitable address for host %s.", m->server_name); /* Try next host */ return manager_connect(m); } m->resolving = false; return manager_connect(m); } static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { Manager *m = userdata; bool connected, online; int r; assert(m); sd_network_monitor_flush(m->network_monitor); /* check if the machine is online */ online = network_is_online(); /* check if the socket is currently open*/ connected = m->socket >= 0; if (connected && !online) { log_info("No network connectivity, watching for changes."); manager_disconnect(m); } else if (!connected && online) { log_info("Network configuration changed, trying to establish connection."); r = manager_connect(m); if (r < 0) return r; } return 0; } static int manager_network_monitor_listen(Manager *m) { int r, fd, events; assert(m); r = sd_network_monitor_new(&m->network_monitor, NULL); if (r < 0) return r; fd = sd_network_monitor_get_fd(m->network_monitor); if (fd < 0) return fd; events = sd_network_monitor_get_events(m->network_monitor); if (events < 0) return events; r = sd_event_add_io(m->event, &m->network_event_source, fd, events, manager_network_event_handler, m); if (r < 0) return r; return 0; } void manager_free(Manager *m) { if (!m) return; manager_disconnect(m); free(m->dtls); free(m->tls); free(m->server_cert); free(m->server_name); free(m->last_cursor); free(m->state_file); free(m->dir); free(m->namespace); sd_resolve_unref(m->resolve); sd_event_source_unref(m->network_event_source); sd_network_monitor_unref(m->network_monitor); sd_event_source_unref(m->event_retry); sd_event_unref(m->event); free(m); } int manager_new(const char *state_file, const char *cursor, Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; int r; assert(ret); m = new(Manager, 1); if (!m) return log_oom(); *m = (Manager) { .socket = -1, .journal_watch_fd = -1, .state_file = strdup(state_file), .protocol = SYSLOG_TRANSMISSION_PROTOCOL_UDP, .log_format = SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5424, .auth_mode = OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY, .connection_retry_usec = DEFAULT_CONNECTION_RETRY_USEC, .ratelimit = (const RateLimit) { RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST }, }; r = socket_address_parse(&m->address, "239.0.0.1:6000"); assert(r == 0); if (!m->state_file) return log_oom(); if (cursor) { m->last_cursor = strdup(cursor); if (!m->last_cursor) return log_oom(); } r = sd_event_default(&m->event); if (r < 0) return log_error_errno(r, "Failed to allocate event loop: %m"); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); r = sd_event_add_signal(m->event, NULL, SIGTERM, manager_signal_event_handler, m); if (r < 0) log_warning_errno(r, "Failed to add SIGTERM event handler: %m"); r = sd_event_add_signal(m->event, NULL, SIGINT, manager_signal_event_handler, m); if (r < 0) log_warning_errno(r, "Failed to add SIGTERM event handler: %m"); sd_event_set_watchdog(m->event, true); r = sd_resolve_default(&m->resolve); if (r < 0) return r; r = sd_resolve_attach_event(m->resolve, m->event, 0); if (r < 0) return r; r = manager_network_monitor_listen(m); if (r < 0) return r; *ret = TAKE_PTR(m); return 0; } systemd-netlogd-1.4.4/src/netlog/netlog-manager.h000066400000000000000000000130241474012624500217770ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include "netlog-dtls.h" #include "netlog-tls.h" #include "sd-network.h" #include "sd-resolve.h" #include "socket-util.h" #include "ratelimit.h" #define DEFAULT_CONNECTION_RETRY_USEC (30 * USEC_PER_SEC) typedef enum SysLogTransmissionProtocol { SYSLOG_TRANSMISSION_PROTOCOL_UDP = 1 << 0, SYSLOG_TRANSMISSION_PROTOCOL_TCP = 1 << 1, SYSLOG_TRANSMISSION_PROTOCOL_DTLS = 1 << 2, SYSLOG_TRANSMISSION_PROTOCOL_TLS = 1 << 3, _SYSLOG_TRANSMISSION_PROTOCOL_MAX, _SYSLOG_TRANSMISSION_PROTOCOL_INVALID = -EINVAL, } SysLogTransmissionProtocol; typedef enum SysLogTransmissionLogFormat { SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5424 = 1 << 0, SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_3339 = 1 << 1, _SYSLOG_TRANSMISSION_LOG_FORMAT_MAX, _SYSLOG_TRANSMISSION_LOG_FORMAT_INVALID = -EINVAL, } SysLogTransmissionLogFormat; /* RFC 5424 Section 6.2.1 */ typedef enum SysLogFacility { SYSLOG_FACILITY_KERN = 0, SYSLOG_FACILITY_USER = 1, SYSLOG_FACILITY_MAIL = 2, SYSLOG_FACILITY_DAEMON = 3, SYSLOG_FACILITY_AUTH = 4, SYSLOG_FACILITY_SYSLOG = 5, SYSLOG_FACILITY_LPR = 6, SYSLOG_FACILITY_NEWS = 7, SYSLOG_FACILITY_UUCP = 8, SYSLOG_FACILITY_CRON = 9, SYSLOG_FACILITY_AUTHPRIV = 10, SYSLOG_FACILITY_FTP = 11, SYSLOG_FACILITY_NTP = 12, SYSLOG_FACILITY_SECURITY = 13, SYSLOG_FACILITY_CONSOLE = 14, SYSLOG_FACILITY_SOLARIS_CRON = 15, SYSLOG_FACILITY_LOCAL0 = 16, SYSLOG_FACILITY_LOCAL1 = 17, SYSLOG_FACILITY_LOCAL2 = 18, SYSLOG_FACILITY_LOCAL3 = 19, SYSLOG_FACILITY_LOCAL4 = 20, SYSLOG_FACILITY_LOCAL5 = 21, SYSLOG_FACILITY_LOCAL6 = 22, SYSLOG_FACILITY_LOCAL7 = 23, _SYSLOG_FACILITY_MAX, _SYSLOG_FACILITY_INVALID = -EINVAL, } SysLogFacility; /* RFC 5424 Section 6.2.1 */ typedef enum SysLogLevel { SYSLOG_LEVEL_EMERGENCY = 0, SYSLOG_LEVEL_ALERT = 1, SYSLOG_LEVEL_CRITICAL = 2, SYSLOG_LEVEL_ERROR = 3, SYSLOG_LEVEL_WARNING = 4, SYSLOG_LEVEL_NOTICE = 5, SYSLOG_LEVEL_INFORMATIONAL = 6, SYSLOG_LEVEL_DEBUG = 7, _SYSLOG_LEVEL_MAX, _SYSLOG_LEVEL_INVALID = -EINVAL, } SysLogLevel; typedef struct Manager Manager; struct Manager { sd_resolve *resolve; sd_event *event; sd_event_source *event_journal_input; usec_t connection_retry_usec; /* network */ sd_event_source *network_event_source; sd_network_monitor *network_monitor; /* Retry connections */ sd_event_source *event_retry; RateLimit ratelimit; /* peer */ sd_resolve_query *resolve_query; int socket; /* Multicast UDP address */ SocketAddress address; uint32_t port; char *server_name; uint32_t excluded_syslog_facilities; uint8_t excluded_syslog_levels; /* journal */ int journal_watch_fd; int namespace_flags; sd_journal *journal; char *state_file; char *last_cursor; char *structured_data; char *dir; char *namespace; SysLogTransmissionProtocol protocol; SysLogTransmissionLogFormat log_format; OpenSSLCertificateAuthMode auth_mode; char *server_cert; bool syslog_structured_data; bool syslog_msgid; DTLSManager *dtls; TLSManager *tls; bool keep_alive; bool no_delay; bool connected; bool resolving; unsigned keep_alive_cnt; size_t send_buffer; usec_t keep_alive_time; usec_t keep_alive_interval; }; int manager_new(const char *state_file, const char *cursor, Manager **ret); void manager_free(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); int manager_connect(Manager *m); void manager_disconnect(Manager *m); void manager_close_network_socket(Manager *m); int manager_open_network_socket(Manager *m); int manager_network_connect_socket(Manager *m); int manager_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata); int manager_push_to_network(Manager *m, int severity, int facility, const char *identifier, const char *message, const char *hostname, const char *pid, const struct timeval *tv, const char *syslog_structured_data, const char *syslog_msgid); const char *protocol_to_string(SysLogTransmissionProtocol v) _const_; SysLogTransmissionProtocol protocol_from_string(const char *s) _pure_; const char *log_format_to_string(SysLogTransmissionLogFormat v) _const_; SysLogTransmissionLogFormat log_format_from_string(const char *s) _pure_; const char *syslog_facility_to_string(SysLogFacility v) _const_; SysLogFacility syslog_facility_from_string(const char *s) _pure_; const char *syslog_level_to_string(SysLogLevel v) _const_; SysLogLevel syslog_level_from_string(const char *s) _pure_; systemd-netlogd-1.4.4/src/netlog/netlog-network.c000066400000000000000000000226151474012624500220570ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "io-util.h" #include "netlog-manager.h" #include "netlog-network.h" #include "netlog-protocol.h" #define RFC_5424_NILVALUE "-" #define RFC_5424_PROTOCOL 1 #define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC) static int sendmsg_loop(Manager *m, struct msghdr *mh) { ssize_t n; int r; assert(m); assert(m->socket >= 0); assert(mh); for (;;) { n = sendmsg(m->socket, mh, MSG_NOSIGNAL); if (n >= 0) { log_debug("Successful sendmsg: %zd bytes", n); return 0; } if (errno == EINTR) continue; if (errno != EAGAIN) return -errno; r = fd_wait_for_event(m->socket, POLLOUT, SEND_TIMEOUT_USEC); if (r < 0) return r; if (r == 0) return -ETIMEDOUT; } return 0; } int network_send(Manager *m, struct iovec *iovec, unsigned n_iovec) { struct msghdr mh = { .msg_iov = iovec, .msg_iovlen = n_iovec, }; assert(m); assert(iovec); assert(n_iovec > 0); if (m->address.sockaddr.sa.sa_family == AF_INET) { mh.msg_name = &m->address.sockaddr.sa; mh.msg_namelen = sizeof(m->address.sockaddr.in); } else if (m->address.sockaddr.sa.sa_family == AF_INET6) { mh.msg_name = &m->address.sockaddr.sa; mh.msg_namelen = sizeof(m->address.sockaddr.in6); } else return -EAFNOSUPPORT; return sendmsg_loop(m, &mh); } int manager_push_to_network(Manager *m, int severity, int facility, const char *identifier, const char *message, const char *hostname, const char *pid, const struct timeval *tv, const char *syslog_structured_data, const char *syslog_msgid) { int r; assert(m); switch (m->protocol) { case SYSLOG_TRANSMISSION_PROTOCOL_DTLS: if (!m->dtls->connected) { log_debug("DTLS not connected, performing reconnect"); r = manager_connect(m); if (r < 0) return r; } break; case SYSLOG_TRANSMISSION_PROTOCOL_TLS: if (!m->tls->connected) { log_debug("TLS not connected, performing reconnect"); r = manager_connect(m); if (r < 0) return r; } break; default: if (!m->connected) { log_debug("%s not connected, performing reconnect", protocol_to_string(m->protocol)); r = manager_connect(m); if (r < 0) return r; } break; } if (m->log_format == SYSLOG_TRANSMISSION_LOG_FORMAT_RFC_5424) r = format_rfc5424(m, severity, facility, identifier, message, hostname, pid, tv, syslog_structured_data, syslog_msgid); else r = format_rfc3339(m, severity, facility, identifier, message, hostname, pid, tv); if (r < 0) return r; return 0; } void manager_close_network_socket(Manager *m) { assert(m); if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_TCP && m->socket >= 0) { int r = shutdown(m->socket, SHUT_RDWR); if (r < 0) log_error_errno(errno, "Failed to shutdown netlog socket: %m"); } m->connected = false; m->socket = safe_close(m->socket); } int manager_network_connect_socket(Manager *m) { _cleanup_free_ char *pretty = NULL; const char *protocol; socklen_t salen; int r; assert(m); assert(m->socket >= 0); switch (m->address.sockaddr.sa.sa_family) { case AF_INET: salen = sizeof(m->address.sockaddr.in); break; case AF_INET6: salen = sizeof(m->address.sockaddr.in6); break; default: return -EAFNOSUPPORT; } r = sockaddr_pretty(&m->address.sockaddr.sa, salen, true, true, &pretty); if (r < 0) return r; protocol = protocol_to_string(m->protocol); log_debug("Connecting to remote server: '%s/%s'", pretty, protocol); r = connect(m->socket, &m->address.sockaddr.sa, salen); if (r < 0 && errno != EINPROGRESS) return log_error_errno(errno, "Failed to connect to remote server='%s/%s': %m", pretty, protocol); if (errno != EINPROGRESS) log_debug("Connected to remote server: '%s/%s'", pretty, protocol); else log_debug("Connection in progress to remote server: '%s/%s'", pretty, protocol); return 0; } static int apply_tcp_socket_options(Manager *m){ int r; assert(m); assert(m->socket >= 0); if (m->no_delay) { r = setsockopt_int(m->socket, IPPROTO_TCP, TCP_NODELAY, true); if (r < 0) log_debug_errno(r, "Failed to enable TCP_NODELAY mode, ignoring: %m"); } if (m->send_buffer > 0) { r = fd_set_sndbuf(m->socket, m->send_buffer, false); if (r < 0) log_debug_errno(r, "TCP: SO_SNDBUF/SO_SNDBUFFORCE failed: %m"); } if (m->keep_alive) { r = setsockopt_int(m->socket, SOL_SOCKET, SO_KEEPALIVE, true); if (r < 0) log_debug_errno(r, "Failed to enable SO_KEEPALIVE: %m"); } if (timestamp_is_set(m->keep_alive_time)) { r = setsockopt_int(m->socket, SOL_TCP, TCP_KEEPIDLE, m->keep_alive_time / USEC_PER_SEC); if (r < 0) log_debug_errno(r, "TCP_KEEPIDLE failed: %m"); } if (m->keep_alive_interval > 0) { r = setsockopt_int(m->socket, SOL_TCP, TCP_KEEPINTVL, m->keep_alive_interval / USEC_PER_SEC); if (r < 0) log_debug_errno(r, "TCP_KEEPINTVL failed: %m"); } if (m->keep_alive_cnt > 0) { r = setsockopt_int(m->socket, SOL_TCP, TCP_KEEPCNT, m->keep_alive_cnt); if (r < 0) log_debug_errno(r, "TCP_KEEPCNT failed: %m"); } return 0; } int manager_open_network_socket(Manager *m) { int r; assert(m); if (!IN_SET(m->address.sockaddr.sa.sa_family, AF_INET, AF_INET6)) return -EAFNOSUPPORT; switch (m->protocol) { case SYSLOG_TRANSMISSION_PROTOCOL_UDP: m->socket = socket(m->address.sockaddr.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); break; case SYSLOG_TRANSMISSION_PROTOCOL_TCP: m->socket = socket(m->address.sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); break; default: return -EPROTONOSUPPORT; } if (m->socket < 0) return log_error_errno(errno, "Failed to create socket: %m"); log_debug("Successfully created socket with fd='%d'", m->socket); switch (m->protocol) { case SYSLOG_TRANSMISSION_PROTOCOL_UDP: { r = setsockopt_int(m->socket, IPPROTO_IP, IP_MULTICAST_LOOP, true); if (r < 0) log_debug_errno(errno, "UDP: Failed to set IP_MULTICAST_LOOP: %m"); if (m->send_buffer > 0) { r = fd_set_sndbuf(m->socket, m->send_buffer, false); if (r < 0) log_debug_errno(r, "UDP: SO_SNDBUF/SO_SNDBUFFORCE failed: %m"); }} break; case SYSLOG_TRANSMISSION_PROTOCOL_TCP: { r = apply_tcp_socket_options(m); if (r < 0) return r; } break; default: break; } r = fd_nonblock(m->socket, true); if (r < 0) log_debug_errno(errno, "Failed to set socket='%d' nonblock: %m", m->socket); r = manager_network_connect_socket(m); if (r < 0) goto fail; m->connected = true; return 0; fail: m->socket = safe_close(m->socket); return r; } systemd-netlogd-1.4.4/src/netlog/netlog-network.h000066400000000000000000000002421474012624500220540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "netlog-manager.h" int network_send(Manager *m, struct iovec *iovec, unsigned n_iovec); systemd-netlogd-1.4.4/src/netlog/netlog-protocol.c000066400000000000000000000174601474012624500222310ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "io-util.h" #include "netlog-protocol.h" #include "netlog-network.h" #define RFC_5424_NILVALUE "-" #define RFC_5424_PROTOCOL 1 #define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC) int protocol_send(Manager *m, struct iovec *iovec, unsigned n_iovec) { int r; switch (m->protocol) { case SYSLOG_TRANSMISSION_PROTOCOL_DTLS: r = dtls_datagram_writev(m->dtls, iovec, n_iovec); if (r < 0 && r != -EAGAIN) { log_debug_errno(r, "Failed to send via DTLS, performing reconnect: %m"); manager_connect(m); return r; } break; case SYSLOG_TRANSMISSION_PROTOCOL_TLS: r = tls_stream_writev(m->tls, iovec, n_iovec); if (r < 0 && r != -EAGAIN) { log_debug_errno(r, "Failed to send via TLS, performing reconnect: %m"); manager_connect(m); return r; } break; default: r = network_send(m, iovec, n_iovec); if (r < 0 && r != -EAGAIN) { log_debug_errno(r, "Failed to send via %s, performing reconnect: %m", protocol_to_string(m->protocol)); manager_connect(m); return r; } break; } return 0; } /* rfc3339 timestamp format: yyyy-mm-ddthh:mm:ss[.frac]<+/->zz:zz */ void format_rfc3339_timestamp(const struct timeval *tv, char *header_time, size_t header_size) { char gm_buf[sizeof("+0530") + 1]; struct tm tm; time_t t; size_t w; int r; assert(header_time); t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC)); localtime_r(&t, &tm); w = strftime(header_time, header_size, "%Y-%m-%dT%T", &tm); assert(w != 0); header_time += w; header_size -= w; /* add fractional part */ if (tv) { r = snprintf(header_time, header_size, ".%06ld", tv->tv_usec); assert(r > 0 && (size_t)r < header_size); header_time += r; header_size -= r; } /* format the timezone according to RFC */ xstrftime(gm_buf, "%z", &tm); r = snprintf(header_time, header_size, "%.3s:%.2s ", gm_buf, gm_buf + 3); assert(r > 0 && (size_t)r < header_size); } /* The Syslog Protocol RFC5424 format : * version sp timestamp sp hostname sp app-name sp procid sp msgid sp [sd-id]s sp msg */ int format_rfc5424(Manager *m, int severity, int facility, const char *identifier, const char *message, const char *hostname, const char *pid, const struct timeval *tv, const char *syslog_structured_data, const char *syslog_msgid) { char header_time[FORMAT_TIMESTAMP_MAX]; char header_priority[sizeof("< >1 ")]; struct iovec iov[14]; uint8_t makepri; int n = 0, r; assert(m); assert(message); makepri = (facility << 3) + severity; /* First: priority field Second: Version 'version' */ r = snprintf(header_priority, sizeof(header_priority), "<%i>%i ", makepri, RFC_5424_PROTOCOL); assert(r > 0 && (size_t)r < sizeof(header_priority)); IOVEC_SET_STRING(iov[n++], header_priority); /* Third: timestamp */ format_rfc3339_timestamp(tv, header_time, sizeof(header_time)); IOVEC_SET_STRING(iov[n++], header_time); /* Fourth: hostname */ if (hostname) IOVEC_SET_STRING(iov[n++], hostname); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], " "); /* Fifth: identifier */ if (identifier) IOVEC_SET_STRING(iov[n++], identifier); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], " "); /* Sixth: procid */ if (pid) IOVEC_SET_STRING(iov[n++], pid); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], " "); /* Seventh: msgid */ if (syslog_msgid) IOVEC_SET_STRING(iov[n++], syslog_msgid); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], " "); /* Eighth: [structured-data] */ if (m->structured_data) IOVEC_SET_STRING(iov[n++], m->structured_data); else if (m->syslog_structured_data && syslog_structured_data) IOVEC_SET_STRING(iov[n++], syslog_structured_data); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], " "); /* Ninth: message */ IOVEC_SET_STRING(iov[n++], message); /* Last Optional newline message separator, if not implicitly terminated by end of UDP frame * De facto standard: separate messages by a newline */ if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_TCP) IOVEC_SET_STRING(iov[n++], "\n"); return protocol_send(m, iov, n); } int format_rfc3339(Manager *m, int severity, int facility, const char *identifier, const char *message, const char *hostname, const char *pid, const struct timeval *tv) { char header_priority[sizeof("< >1 ")]; char header_time[FORMAT_TIMESTAMP_MAX]; struct iovec iov[14]; uint8_t makepri; int n = 0, r; assert(m); assert(message); makepri = (facility << 3) + severity; /* rfc3339 * <35>Oct 12 22:14:15 client_machine su: 'su root' failed for joe on /dev/pts/2 */ /* First: priority field '' */ r = snprintf(header_priority, sizeof(header_priority), "<%i>", makepri); assert(r > 0 && (size_t)r < sizeof(header_priority)); IOVEC_SET_STRING(iov[n++], header_priority); /* Third: timestamp */ format_rfc3339_timestamp(tv, header_time, sizeof(header_time)); IOVEC_SET_STRING(iov[n++], header_time); /* Fourth: hostname */ if (hostname) IOVEC_SET_STRING(iov[n++], hostname); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], " "); /* Fifth: identifier */ if (identifier) IOVEC_SET_STRING(iov[n++], identifier); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], "["); /* Sixth: procid */ if (pid) IOVEC_SET_STRING(iov[n++], pid); else IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); IOVEC_SET_STRING(iov[n++], "]: "); /* Ninth: message */ IOVEC_SET_STRING(iov[n++], message); /* Last Optional newline message separator, if not implicitly terminated by end of UDP frame * De facto standard: separate messages by a newline */ if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_TCP) IOVEC_SET_STRING(iov[n++], "\n"); return protocol_send(m, iov, n); } systemd-netlogd-1.4.4/src/netlog/netlog-protocol.h000066400000000000000000000012731474012624500222310ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "netlog-manager.h" int protocol_send(Manager *m, struct iovec *iovec, unsigned n_iovec); void format_rfc3339_timestamp(const struct timeval *tv, char *header_time, size_t header_size); int format_rfc5424(Manager *m, int severity, int facility, const char *identifier, const char *message, const char *hostname, const char *pid, const struct timeval *tv, const char *syslog_structured_data, const char *syslog_msgid); int format_rfc3339(Manager *m, int severity, int facility, const char *identifier, const char *message, const char *hostname, const char *pid, const struct timeval *tv); systemd-netlogd-1.4.4/src/netlog/netlog-ssl.c000066400000000000000000000071541474012624500211700ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "netlog-ssl.h" #include "alloc-util.h" #include "openssl-util.h" #include "socket-util.h" #include "netlog-dtls.h" #include "netlog-tls.h" static_assert(offsetof(TLSManager, auth_mode) == offsetof(DTLSManager, auth_mode)); /* inspired by SSL_set_verify(3) */ int ssl_verify_certificate_validity(int preverify_ok, X509_STORE_CTX *store) { const SSL* ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); const char *pretty = (const char *) SSL_get_ex_data(ssl, EX_DATA_PRETTYADDRESS); const TLSManager *m = (const TLSManager *) SSL_get_ex_data(ssl, EX_DATA_TLSMANAGER); const X509 *error_cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int error = X509_STORE_CTX_get_error(store); char subject_buf[256]; char issuer_buf[256]; int log_level; assert(store); assert(pretty); assert(m); X509_NAME_oneline(X509_get_subject_name(error_cert), subject_buf, sizeof(subject_buf)); X509_NAME_oneline(X509_get_issuer_name(error_cert), issuer_buf, sizeof(issuer_buf)); log_debug("TLS: Verifying SSL certificates of server %s: certificate: subject='%s' issuer='%s' depth=%d preverify_ok=%d error=%d/%s ...", pretty, subject_buf, issuer_buf, depth, preverify_ok, error, X509_verify_cert_error_string(error)); if (depth > VERIFICATION_DEPTH) { /* * From man:SSL_set_verif(3): * * Catch a too long certificate chain. The depth limit set using * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so * that whenever the "depth>verify_depth" condition is met, we * have violated the limit and want to log this error condition. * We must do it here, because the CHAIN_TOO_LONG error would not * be found explicitly; only errors introduced by cutting off the * additional certificates would be logged. */ preverify_ok = 0; error = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(store, error); } if (preverify_ok) { log_debug("TLS: Verified SSL certificate of server=%s (certificate: subject='%s' issuer='%s' depth=%d): %s", pretty, subject_buf, issuer_buf, depth, X509_verify_cert_error_string(error)); return preverify_ok; } switch (m->auth_mode) { case OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY: log_level = LOG_ERR; break; case OPEN_SSL_CERTIFICATE_AUTH_MODE_WARN: log_level = LOG_WARNING; preverify_ok = 1; break; case OPEN_SSL_CERTIFICATE_AUTH_MODE_ALLOW: log_level = LOG_DEBUG; preverify_ok = 1; break; default: assert_not_reached("Invalid certificate authentication mode"); /* mode NO does not set this callback */ } log_full(log_level, "TLS: Failed to verify certificate of server=%s (certificate: subject='%s' issuer='%s' depth=%d)%s: %s", pretty, subject_buf, issuer_buf, depth, preverify_ok ? ", ignoring" : "", X509_verify_cert_error_string(error)); return preverify_ok; } systemd-netlogd-1.4.4/src/netlog/netlog-ssl.h000066400000000000000000000005161474012624500211700ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include "macro.h" #define VERIFICATION_DEPTH 20 #define EX_DATA_TLSMANAGER 0 #define EX_DATA_PRETTYADDRESS 1 int ssl_verify_certificate_validity(int preverify_ok, X509_STORE_CTX *store); DEFINE_TRIVIAL_CLEANUP_FUNC(SSL_CTX*, SSL_CTX_free); systemd-netlogd-1.4.4/src/netlog/netlog-tls.c000066400000000000000000000175311474012624500211710ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "netlog-tls.h" #include #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "io-util.h" #include "iovec-util.h" #include "string-table.h" #include "netlog-ssl.h" static const char *const certificate_auth_mode_table[_OPEN_SSL_CERTIFICATE_AUTH_MODE_MAX] = { [OPEN_SSL_CERTIFICATE_AUTH_MODE_NONE] = "no", [OPEN_SSL_CERTIFICATE_AUTH_MODE_ALLOW] = "allow", [OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY] = "deny", [OPEN_SSL_CERTIFICATE_AUTH_MODE_WARN] = "warn", }; DEFINE_STRING_TABLE_LOOKUP(certificate_auth_mode, OpenSSLCertificateAuthMode); static int tls_write(TLSManager *m, const char *buf, size_t count) { int r; assert(m); assert(m->ssl); assert(m->pretty_address); assert(buf); assert(count > 0); assert(count < INT_MAX); ERR_clear_error(); r = SSL_write(m->ssl, buf, count); if (r <= 0) { int error = SSL_get_error(m->ssl, r); if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) return log_info_errno(SYNTHETIC_ERRNO(EAGAIN), "TLS: Failed to invoke SSL_write to %s: %s", m->pretty_address, TLS_ERROR_STRING(error)); else return log_error_errno(SYNTHETIC_ERRNO(EPIPE), "TLS: Failed to invoke SSL_write to %s: %s", m->pretty_address, TLS_ERROR_STRING(error)); } return log_debug("TLS: Successful TLS SSL_write: %d bytes", r); } int tls_stream_writev(TLSManager *m, const struct iovec *iov, size_t iovcnt) { _cleanup_free_ char *buf = NULL; size_t count; assert(m); assert(iov); /* single buffer. Suboptimal, but better than multiple SSL_write calls. */ count = iovec_total_size(iov, iovcnt); assert(count > 0); buf = new(char, count); if (!buf) return log_oom(); for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++) memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len); return tls_write(m, buf, count); } int tls_connect(TLSManager *m, SocketAddress *address) { _cleanup_(SSL_freep) SSL *ssl = NULL; _cleanup_free_ char *pretty = NULL; const SSL_CIPHER *cipher; socklen_t salen; _cleanup_close_ int fd = -1; int r; assert(m); assert(m->ctx); assert(address); switch (address->sockaddr.sa.sa_family) { case AF_INET: salen = sizeof(address->sockaddr.in); break; case AF_INET6: salen = sizeof(address->sockaddr.in6); break; default: return -EAFNOSUPPORT; } fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) return log_error_errno(errno, "TLS: Failed to allocate socket: %m"); r = sockaddr_pretty(&address->sockaddr.sa, salen, true, true, &pretty); if (r < 0) return r; r = connect(fd, &address->sockaddr.sa, salen); if (r < 0 && errno != EINPROGRESS) return log_error_errno(errno, "TLS: Failed to connect to remote server='%s': %m", pretty); log_debug("TLS: Connected to remote server: '%s'", pretty); ssl = SSL_new(m->ctx); if (!ssl) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "TLS: Failed to allocate memory for ssl: %s", ERR_error_string(ERR_get_error(), NULL)); r = SSL_set_fd(ssl, fd); if (r <= 0) return log_error_errno(SYNTHETIC_ERRNO(EIO), "TLS: Failed to SSL_set_fd: %s", ERR_error_string(ERR_get_error(), NULL)); /* Certification verification */ if (m->auth_mode != OPEN_SSL_CERTIFICATE_AUTH_MODE_NONE) { log_debug("TLS: enable certificate verification with mode %s", certificate_auth_mode_to_string(m->auth_mode)); SSL_set_ex_data(ssl, EX_DATA_TLSMANAGER, m); SSL_set_ex_data(ssl, EX_DATA_PRETTYADDRESS, pretty); SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_certificate_validity); } else { log_debug("TLS: disable certificate verification"); SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); } SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); r = SSL_connect(ssl); if (r <= 0) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "TLS: Failed to SSL_connect: %s", ERR_error_string(ERR_get_error(), NULL)); cipher = SSL_get_current_cipher(ssl); log_debug("TLS: SSL Cipher Version: %s Name: %s", SSL_CIPHER_get_version(cipher), SSL_CIPHER_get_name(cipher)); if (DEBUG_LOGGING) { _cleanup_(X509_freep) X509* cert = NULL; cert = SSL_get_peer_certificate(ssl); if (cert) { _cleanup_(OPENSSL_freep) void *subject = NULL, *issuer = NULL; subject = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); log_debug("TLS: SSL Subject: %s", (char *) subject); issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); log_debug("TLS: SSL Issuer: %s", (char *) issuer); } else log_debug("TLS: SSL No certificates."); } m->ssl = TAKE_PTR(ssl); m->fd = TAKE_FD(fd); m->pretty_address = TAKE_PTR(pretty); m->connected = true; return 0; } void tls_disconnect(TLSManager *m) { if (!m) return; ERR_clear_error(); if (m->ssl) { SSL_shutdown(m->ssl); SSL_free(m->ssl); m->ssl = NULL; } m->pretty_address = mfree(m->pretty_address); m->fd = safe_close(m->fd); m->connected = false; } void tls_manager_free(TLSManager *m) { if (!m) return; if (m->ctx) SSL_CTX_free(m->ctx); free(m); } int tls_manager_init(OpenSSLCertificateAuthMode auth, const char *server_cert, TLSManager **ret ) { _cleanup_(tls_manager_freep) TLSManager *m = NULL; _cleanup_(SSL_CTX_freep) SSL_CTX *ctx = NULL; int r; ctx = SSL_CTX_new(TLS_client_method()); if (!ctx) return log_error_errno(SYNTHETIC_ERRNO(ENOMEM), "TLS: Failed to allocate memory for SSL CTX: %m"); if (server_cert) { r = SSL_CTX_load_verify_file(ctx, server_cert); if (r != 1) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),"TLS: Failed to load CA certificate from '%s': %s", server_cert, ERR_error_string(ERR_get_error(), NULL)); } else { r = SSL_CTX_set_default_verify_paths(ctx); if (r != 1) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TLS: Failed to load default CA certificates: %s", ERR_error_string(ERR_get_error(), NULL)); } SSL_CTX_set_verify_depth(ctx, VERIFICATION_DEPTH + 1); m = new(TLSManager, 1); if (!m) return log_oom(); *m = (TLSManager) { .auth_mode = auth, .ctx = TAKE_PTR(ctx), .fd = -1, }; *ret = TAKE_PTR(m); return 0; } systemd-netlogd-1.4.4/src/netlog/netlog-tls.h000066400000000000000000000024331474012624500211710ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include "socket-util.h" #include "openssl-util.h" typedef enum OpenSSLCertificateAuthMode { OPEN_SSL_CERTIFICATE_AUTH_MODE_NONE = 0, OPEN_SSL_CERTIFICATE_AUTH_MODE_ALLOW = 1, OPEN_SSL_CERTIFICATE_AUTH_MODE_DENY = 2, OPEN_SSL_CERTIFICATE_AUTH_MODE_WARN = 3, _OPEN_SSL_CERTIFICATE_AUTH_MODE_MAX, _OPEN_SSL_CERTIFICATE_AUTH_MODE_INVALID = -EINVAL, } OpenSSLCertificateAuthMode; typedef struct TLSManager TLSManager; struct TLSManager { SSL_CTX *ctx; SSL *ssl; char *pretty_address; int fd; bool connected; OpenSSLCertificateAuthMode auth_mode; }; void tls_manager_free(TLSManager *m); int tls_manager_init(OpenSSLCertificateAuthMode auth, const char *server_cert, TLSManager **ret); int tls_connect(TLSManager *m, SocketAddress *addr); void tls_disconnect(TLSManager *m); int tls_stream_writev(TLSManager *m, const struct iovec *iov, size_t iovcnt); const char *certificate_auth_mode_to_string(OpenSSLCertificateAuthMode v) _const_; OpenSSLCertificateAuthMode certificate_auth_mode_from_string(const char *s) _pure_; DEFINE_TRIVIAL_CLEANUP_FUNC(TLSManager*, tls_manager_free); systemd-netlogd-1.4.4/src/netlog/systemd-netlogd.c000066400000000000000000000166451474012624500222300ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include "build.h" #include "capability-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "mkdir.h" #include "netlog-conf.h" #include "netlog-manager.h" #include "network-util.h" #include "path-util.h" #include "user-util.h" #include "util.h" #define STATE_FILE "/var/lib/systemd/journal-netlogd/state" static const char *arg_cursor = NULL; static const char *arg_save_state = STATE_FILE; static int setup_cursor_state_file(Manager *m, uid_t uid, gid_t gid) { _cleanup_fclose_ FILE *f = NULL; _cleanup_close_ int fd = -1; int r; assert(m); r = mkdir_parents(m->state_file, 0755); if (r < 0) return log_error_errno(r, "Cannot create parent directory of state file %s: %m", m->state_file); /* change permission of the state file parent dir */ r = chmod_and_chown("/var/lib/systemd/journal-netlogd", 0744, uid, gid); if (r < 0) return log_error_errno(r, "Failed to change permission parent directory of state file %s: %m", m->state_file); fd = open(m->state_file, O_RDWR|O_CLOEXEC, 0644); if (fd >= 0) { /* Try to fix the access mode, so that we can still touch the file after dropping privileges */ r = fchmod(fd, 0644); if (r < 0) log_warning_errno(r, "Failed to set mode of state file %s: %m", m->state_file); r = fchown(fd, uid, gid); if (r < 0) log_warning_errno(r, "Failed to set ownership of state file %s: %m", m->state_file); } else /* create stamp file with the compiled-in date */ return touch_file(m->state_file, true, USEC_INFINITY, uid, gid, 0644); return 0; } static void help(void) { printf("%s ..\n\n" "Forwards messages from the journal to other hosts over the network using the syslog\n" "RFC 5424 or RFC3339 format in both unicast and multicast addresses.\n\n" " -h --help Show this help\n" " --version Show package version\n" " --cursor=CURSOR Start at the specified cursor\n" " --save-state[=FILE] Save uploaded cursors (default \n" " " STATE_FILE ")\n" " -h --help Show this help and exit\n" " --version Print version string and exit\n" , program_invocation_short_name); } static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_CURSOR, ARG_SAVE_STATE, }; static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, { "cursor", required_argument, NULL, ARG_CURSOR }, { "save-state", optional_argument, NULL, ARG_SAVE_STATE }, {} }; int c; assert(argc >= 0); assert(argv); while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) switch(c) { case 'h': help(); return 0; case ARG_VERSION: puts(PACKAGE_STRING); #ifdef HAVE_OPENSSL puts("+OPENSSL"); #else puts("-OPENSSL"); #endif return 0; case ARG_CURSOR: if (arg_cursor) { log_error("cannot use more than one --cursor/--after-cursor"); return -EINVAL; } arg_cursor = optarg; break; case ARG_SAVE_STATE: arg_save_state = optarg ?: STATE_FILE; break; case '?': log_error("Unknown option %s.", argv[optind-1]); return -EINVAL; case ':': log_error("Missing argument to %s.", argv[optind-1]); return -EINVAL; default: assert_not_reached("Unhandled option code."); } if (optind < argc) { log_error("Input arguments make no sense with journal input."); return -EINVAL; } return 1; } int main(int argc, char **argv) { _cleanup_(manager_freep) Manager *m = NULL; const char *user = "systemd-journal-netlog"; uid_t uid; gid_t gid; int r; log_show_color(true); log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); r = parse_argv(argc, argv); if (r <= 0) goto finish; umask(0022); r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } r = manager_new(arg_save_state, arg_cursor, &m); if (r < 0) { log_error_errno(r, "Failed to allocate manager: %m"); goto finish; } r = manager_parse_config_file(m); if (r < 0) { log_error_errno(r, "Failed to parse configuration file: %m"); goto finish; } switch (m->protocol) { case SYSLOG_TRANSMISSION_PROTOCOL_DTLS: r = dtls_manager_init(m->auth_mode, m->server_cert, &m->dtls); break; case SYSLOG_TRANSMISSION_PROTOCOL_TLS: r = tls_manager_init(m->auth_mode, m->server_cert, &m->tls); break; default: break; } if (r < 0) return r; r = setup_cursor_state_file(m, uid, gid); if (r < 0) goto cleanup; r = drop_privileges(uid, gid, (1ULL << CAP_NET_ADMIN) | (1ULL << CAP_NET_BIND_SERVICE) | (1ULL << CAP_NET_BROADCAST)); if (r < 0) goto finish; log_debug("%s running as pid "PID_FMT, program_invocation_short_name, getpid()); sd_notify(false, "READY=1\n" "STATUS=Processing input..."); if (network_is_online()) manager_connect(m); r = sd_event_loop(m->event); if (r < 0) { log_error_errno(r, "Failed to run event loop: %m"); goto finish; } sd_event_get_exit_code(m->event, &r); cleanup: sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down..."); finish: return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; } systemd-netlogd-1.4.4/src/share/000077500000000000000000000000001474012624500165405ustar00rootroot00000000000000systemd-netlogd-1.4.4/src/share/alloc-util.c000066400000000000000000000033131474012624500207510ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include "alloc-util.h" #include "macro.h" #include "util.h" void* memdup(const void *p, size_t l) { void *r; assert(p); r = malloc(l); if (!r) return NULL; memcpy(r, p, l); return r; } void* memdup_suffix0(const void *p, size_t l) { void *ret; assert(l == 0 || p); /* The same as memdup() but place a safety NUL byte after the allocated memory */ if (_unlikely_(l == SIZE_MAX)) /* prevent overflow */ return NULL; ret = malloc(l + 1); if (!ret) return NULL; ((uint8_t*) ret)[l] = 0; return memcpy_safe(ret, p, l); } void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { size_t a, newalloc; void *q; assert(p); assert(allocated); if (*allocated >= need) return *p; newalloc = MAX(need * 2, 64u / size); a = newalloc * size; /* check for overflows */ if (a < size * need) return NULL; q = realloc(*p, a); if (!q) return NULL; *p = q; *allocated = newalloc; return q; } void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) { size_t prev; uint8_t *q; assert(p); assert(allocated); prev = *allocated; q = greedy_realloc(p, allocated, need, size); if (!q) return NULL; if (*allocated > prev) memzero(q + prev * size, (*allocated - prev) * size); return q; } systemd-netlogd-1.4.4/src/share/alloc-util.h000066400000000000000000000120651474012624500207620ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "macro.h" /* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */ static inline void *memcpy_safe(void *dst, const void *src, size_t n) { if (n == 0) return dst; assert(src); return memcpy(dst, src, n); } /* Normal mempcpy() requires src to be nonnull. We do nothing if n is 0. */ static inline void *mempcpy_safe(void *dst, const void *src, size_t n) { if (n == 0) return dst; assert(src); return mempcpy(dst, src, n); } /* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { if (n == 0) return 0; assert(s1); assert(s2); return memcmp(s1, s2, n); } #define zero(x) (memzero(&(x), sizeof(x))) #define new(t, n) ((t*) malloc_multiply(n, sizeof(t))) #define new0(t, n) ((t*) calloc((n), sizeof(t))) #define newa(t, n) ((t*) alloca(sizeof(t)*(n))) #define newa0(t, n) ((t*) alloca0(sizeof(t)*(n))) #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) #define malloc0(n) (calloc(1, (n))) static inline void *mfree(void *memory) { free(memory); return NULL; } static inline void freep(void *p) { free(*(void**) p); } #define _cleanup_free_ _cleanup_(freep) void* memdup(const void *p, size_t l) _alloc_(2); void* memdup_suffix0(const void *p, size_t l); #define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t))) static inline bool size_multiply_overflow(size_t size, size_t need) { return _unlikely_(need != 0 && size > (SIZE_MAX / need)); } _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { if (size_multiply_overflow(size, need)) return NULL; return malloc(size * need); } _alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) { if (size_multiply_overflow(size, need)) return NULL; return realloc(p, size * need); } _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { if (size_multiply_overflow(size, need)) return NULL; return memdup(p, size * need); } /* Note that we can't decorate this function with _alloc_() since the returned memory area is one byte larger * than the product of its parameters. */ static inline void *memdup_suffix0_multiply(const void *p, size_t need, size_t size) { if (size_multiply_overflow(size, need)) return NULL; return memdup_suffix0(p, size * need); } void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); #define GREEDY_REALLOC(array, allocated, need) \ greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0])) #define GREEDY_REALLOC0(array, allocated, need) \ greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0])) #define alloca0(n) \ ({ \ char *_new_; \ size_t _len_ = n; \ _new_ = alloca(_len_); \ (void *) memset(_new_, 0, _len_); \ }) /* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */ #define alloca_align(size, align) \ ({ \ void *_ptr_; \ size_t _mask_ = (align) - 1; \ _ptr_ = alloca((size) + _mask_); \ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ }) #define alloca0_align(size, align) \ ({ \ void *_new_; \ size_t _size_ = (size); \ _new_ = alloca_align(_size_, (align)); \ (void*)memset(_new_, 0, _size_); \ }) #define free_and_replace_full(a, b, free_func) \ ({ \ typeof(a)* _a = &(a); \ typeof(b)* _b = &(b); \ free_func(*_a); \ *_a = *_b; \ *_b = NULL; \ 0; \ }) #define free_and_replace(a, b) \ free_and_replace_full(a, b, free) systemd-netlogd-1.4.4/src/share/build.h000066400000000000000000000065101474012624500200120ustar00rootroot00000000000000#pragma once /* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_PAM #define _PAM_FEATURE_ "+PAM" #else #define _PAM_FEATURE_ "-PAM" #endif #ifdef HAVE_AUDIT #define _AUDIT_FEATURE_ "+AUDIT" #else #define _AUDIT_FEATURE_ "-AUDIT" #endif #ifdef HAVE_SELINUX #define _SELINUX_FEATURE_ "+SELINUX" #else #define _SELINUX_FEATURE_ "-SELINUX" #endif #ifdef HAVE_APPARMOR #define _APPARMOR_FEATURE_ "+APPARMOR" #else #define _APPARMOR_FEATURE_ "-APPARMOR" #endif #ifdef HAVE_IMA #define _IMA_FEATURE_ "+IMA" #else #define _IMA_FEATURE_ "-IMA" #endif #ifdef HAVE_SMACK #define _SMACK_FEATURE_ "+SMACK" #else #define _SMACK_FEATURE_ "-SMACK" #endif #ifdef HAVE_SYSV_COMPAT #define _SYSVINIT_FEATURE_ "+SYSVINIT" #else #define _SYSVINIT_FEATURE_ "-SYSVINIT" #endif #ifdef HAVE_UTMP #define _UTMP_FEATURE_ "+UTMP" #else #define _UTMP_FEATURE_ "-UTMP" #endif #ifdef HAVE_LIBCRYPTSETUP #define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP" #else #define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" #endif #ifdef HAVE_GCRYPT #define _GCRYPT_FEATURE_ "+GCRYPT" #else #define _GCRYPT_FEATURE_ "-GCRYPT" #endif #ifdef HAVE_GNUTLS #define _GNUTLS_FEATURE_ "+GNUTLS" #else #define _GNUTLS_FEATURE_ "-GNUTLS" #endif #ifdef HAVE_ACL #define _ACL_FEATURE_ "+ACL" #else #define _ACL_FEATURE_ "-ACL" #endif #ifdef HAVE_XZ #define _XZ_FEATURE_ "+XZ" #else #define _XZ_FEATURE_ "-XZ" #endif #ifdef HAVE_LZ4 #define _LZ4_FEATURE_ "+LZ4" #else #define _LZ4_FEATURE_ "-LZ4" #endif #ifdef HAVE_SECCOMP #define _SECCOMP_FEATURE_ "+SECCOMP" #else #define _SECCOMP_FEATURE_ "-SECCOMP" #endif #ifdef HAVE_BLKID #define _BLKID_FEATURE_ "+BLKID" #else #define _BLKID_FEATURE_ "-BLKID" #endif #ifdef HAVE_ELFUTILS #define _ELFUTILS_FEATURE_ "+ELFUTILS" #else #define _ELFUTILS_FEATURE_ "-ELFUTILS" #endif #ifdef HAVE_KMOD #define _KMOD_FEATURE_ "+KMOD" #else #define _KMOD_FEATURE_ "-KMOD" #endif #ifdef HAVE_LIBIDN #define _IDN_FEATURE_ "+IDN" #else #define _IDN_FEATURE_ "-IDN" #endif #define SYSTEMD_FEATURES \ _PAM_FEATURE_ " " \ _AUDIT_FEATURE_ " " \ _SELINUX_FEATURE_ " " \ _IMA_FEATURE_ " " \ _APPARMOR_FEATURE_ " " \ _SMACK_FEATURE_ " " \ _SYSVINIT_FEATURE_ " " \ _UTMP_FEATURE_ " " \ _LIBCRYPTSETUP_FEATURE_ " " \ _GCRYPT_FEATURE_ " " \ _GNUTLS_FEATURE_ " " \ _ACL_FEATURE_ " " \ _XZ_FEATURE_ " " \ _LZ4_FEATURE_ " " \ _SECCOMP_FEATURE_ " " \ _BLKID_FEATURE_ " " \ _ELFUTILS_FEATURE_ " " \ _KMOD_FEATURE_ " " \ _IDN_FEATURE_ systemd-netlogd-1.4.4/src/share/capability-util.c000066400000000000000000000165451474012624500220130ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include "alloc-util.h" #include "capability-util.h" #include "fileio.h" #include "log.h" #include "macro.h" #include "parse-util.h" #include "util.h" unsigned long cap_last_cap(void) { static thread_local unsigned long saved; static thread_local bool valid = false; _cleanup_free_ char *content = NULL; unsigned long p = 0; int r; if (valid) return saved; /* available since linux-3.2 */ r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); if (r >= 0) { r = safe_atolu(content, &p); if (r >= 0) { saved = p; valid = true; return p; } } /* fall back to syscall-probing for pre linux-3.2 */ p = (unsigned long) CAP_LAST_CAP; if (prctl(PR_CAPBSET_READ, p) < 0) { /* Hmm, look downwards, until we find one that * works */ for (p--; p > 0; p --) if (prctl(PR_CAPBSET_READ, p) >= 0) break; } else { /* Hmm, look upwards, until we find one that doesn't * work */ for (;; p++) if (prctl(PR_CAPBSET_READ, p+1) < 0) break; } saved = p; valid = true; return p; } int capability_bounding_set_drop(uint64_t keep, bool right_now) { _cleanup_cap_free_ cap_t after_cap = NULL; cap_flag_value_t fv; unsigned long i; int r; /* If we are run as PID 1 we will lack CAP_SETPCAP by default * in the effective set (yes, the kernel drops that when * executing init!), so get it back temporarily so that we can * call PR_CAPBSET_DROP. */ after_cap = cap_get_proc(); if (!after_cap) return -errno; if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) return -errno; if (fv != CAP_SET) { _cleanup_cap_free_ cap_t temp_cap = NULL; static const cap_value_t v = CAP_SETPCAP; temp_cap = cap_dup(after_cap); if (!temp_cap) { r = -errno; goto finish; } if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { r = -errno; goto finish; } if (cap_set_proc(temp_cap) < 0) { r = -errno; goto finish; } } for (i = 0; i <= cap_last_cap(); i++) { if (!(keep & (UINT64_C(1) << i))) { cap_value_t v; /* Drop it from the bounding set */ if (prctl(PR_CAPBSET_DROP, i) < 0) { r = -errno; goto finish; } v = (cap_value_t) i; /* Also drop it from the inheritable set, so * that anything we exec() loses the * capability for good. */ if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { r = -errno; goto finish; } /* If we shall apply this right now drop it * also from our own capability sets. */ if (right_now) { if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { r = -errno; goto finish; } } } } r = 0; finish: if (cap_set_proc(after_cap) < 0) return -errno; return r; } int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { _cleanup_cap_free_ cap_t d = NULL; unsigned i, j = 0; int r; /* Unfortunately we cannot leave privilege dropping to PID 1 * here, since we want to run as user but want to keep some * capabilities. Since file capabilities have been introduced * this cannot be done across exec() anymore, unless our * binary has the capability configured in the file system, * which we want to avoid. */ if (setresgid(gid, gid, gid) < 0) return log_error_errno(errno, "Failed to change group ID: %m"); if (setgroups(0, NULL) < 0) return log_error_errno(errno, "Failed to drop auxiliary groups list: %m"); /* Ensure we keep the permitted caps across the setresuid() */ if (prctl(PR_SET_KEEPCAPS, 1) < 0) return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); r = setresuid(uid, uid, uid); if (r < 0) return log_error_errno(errno, "Failed to change user ID: %m"); if (prctl(PR_SET_KEEPCAPS, 0) < 0) return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); /* Drop all caps from the bounding set, except the ones we want */ r = capability_bounding_set_drop(keep_capabilities, true); if (r < 0) return log_error_errno(r, "Failed to drop capabilities: %m"); /* Now upgrade the permitted caps we still kept to effective caps */ d = cap_init(); if (!d) return log_oom(); if (keep_capabilities) { cap_value_t bits[u64log2(keep_capabilities) + 1]; for (i = 0; i < ELEMENTSOF(bits); i++) if (keep_capabilities & (1ULL << i)) bits[j++] = i; /* use enough bits */ assert(i == 64 || (keep_capabilities >> i) == 0); /* don't use too many bits */ assert(keep_capabilities & (1ULL << (i - 1))); if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) return log_error_errno(errno, "Failed to enable capabilities bits: %m"); if (cap_set_proc(d) < 0) return log_error_errno(errno, "Failed to increase capabilities: %m"); } return 0; } int drop_capability(cap_value_t cv) { _cleanup_cap_free_ cap_t tmp_cap = NULL; tmp_cap = cap_get_proc(); if (!tmp_cap) return -errno; if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) return -errno; if (cap_set_proc(tmp_cap) < 0) return -errno; return 0; } systemd-netlogd-1.4.4/src/share/capability-util.h000066400000000000000000000015151474012624500220070ustar00rootroot00000000000000#pragma once /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "macro.h" #include "util.h" #define CAP_ALL (uint64_t) -1 unsigned long cap_last_cap(void); int capability_bounding_set_drop(uint64_t keep, bool right_now); int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities); int drop_capability(cap_value_t cv); DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free); #define _cleanup_cap_free_ _cleanup_(cap_freep) static inline void cap_free_charpp(char **p) { if (*p) cap_free(*p); } #define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp) static inline bool cap_test_all(uint64_t caps) { uint64_t m; m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1; return (caps & m) == m; } systemd-netlogd-1.4.4/src/share/conf-files.c000066400000000000000000000067131474012624500207400ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "conf-files.h" #include "dirent-util.h" #include "fd-util.h" #include "hashmap.h" #include "log.h" #include "macro.h" #include "missing.h" #include "path-util.h" #include "string-util.h" #include "strv.h" #include "util.h" static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { _cleanup_closedir_ DIR *dir = NULL; const char *dirpath; struct dirent *de; int r; assert(path); assert(suffix); dirpath = prefix_roota(root, path); dir = opendir(dirpath); if (!dir) { if (errno == ENOENT) return 0; return -errno; } FOREACH_DIRENT(de, dir, return -errno) { char *p; if (!dirent_is_file_with_suffix(de, suffix)) continue; p = strjoin(dirpath, "/", de->d_name, NULL); if (!p) return -ENOMEM; r = hashmap_put(h, basename(p), p); if (r == -EEXIST) { log_debug("Skipping overridden file: %s.", p); free(p); } else if (r < 0) { free(p); return r; } else if (r == 0) { log_debug("Duplicate file %s", p); free(p); } } return 0; } static int base_cmp(const void *a, const void *b) { const char *s1, *s2; s1 = *(char * const *)a; s2 = *(char * const *)b; return strcmp(basename(s1), basename(s2)); } static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { _cleanup_hashmap_free_ Hashmap *fh = NULL; char **files, **p; int r; assert(strv); assert(suffix); /* This alters the dirs string array */ if (!path_strv_resolve_uniq(dirs, root)) return -ENOMEM; fh = hashmap_new(&string_hash_ops); if (!fh) return -ENOMEM; STRV_FOREACH(p, dirs) { r = files_add(fh, root, *p, suffix); if (r == -ENOMEM) return r; if (r < 0) log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p); } files = hashmap_get_strv(fh); if (!files) return -ENOMEM; qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp); *strv = files; return 0; } int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) { _cleanup_strv_free_ char **copy = NULL; assert(strv); assert(suffix); copy = strv_copy((char**) dirs); if (!copy) return -ENOMEM; return conf_files_list_strv_internal(strv, suffix, root, copy); } int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) { _cleanup_strv_free_ char **dirs = NULL; assert(strv); assert(suffix); dirs = strv_split_nulstr(d); if (!dirs) return -ENOMEM; return conf_files_list_strv_internal(strv, suffix, root, dirs); } systemd-netlogd-1.4.4/src/share/conf-files.h000066400000000000000000000004071474012624500207370ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs); int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs); systemd-netlogd-1.4.4/src/share/conf-parser.c000066400000000000000000000362621474012624500211340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include "alloc-util.h" #include "conf-files.h" #include "conf-parser.h" #include "extract-word.h" #include "fd-util.h" #include "fs-util.h" #include "log.h" #include "macro.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "signal-util.h" #include "socket-util.h" #include "string-util.h" #include "strv.h" #include "syslog-util.h" #include "time-util.h" #include "utf8.h" int config_item_table_lookup( const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata) { const ConfigTableItem *t; assert(table); assert(lvalue); assert(func); assert(ltype); assert(data); for (t = table; t->lvalue; t++) { if (!streq(lvalue, t->lvalue)) continue; if (!streq_ptr(section, t->section)) continue; *func = t->parse; *ltype = t->ltype; *data = t->data; return 1; } return 0; } int config_item_perf_lookup( const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata) { ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; const ConfigPerfItem *p; assert(table); assert(lvalue); assert(func); assert(ltype); assert(data); if (!section) p = lookup(lvalue, strlen(lvalue)); else { char *key; key = strjoin(section, ".", lvalue, NULL); if (!key) return -ENOMEM; p = lookup(key, strlen(key)); free(key); } if (!p) return 0; *func = p->parse; *ltype = p->ltype; *data = (uint8_t*) userdata + p->offset; return 1; } /* Run the user supplied parser for an assignment */ static int next_assignment(const char *unit, const char *filename, unsigned line, ConfigItemLookup lookup, const void *table, const char *section, unsigned section_line, const char *lvalue, const char *rvalue, bool relaxed, void *userdata) { ConfigParserCallback func = NULL; int ltype = 0; void *data = NULL; int r; assert(filename); assert(line > 0); assert(lookup); assert(lvalue); assert(rvalue); r = lookup(table, section, lvalue, &func, <ype, &data, userdata); if (r < 0) return r; if (r > 0) { if (func) return func(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata); return 0; } /* Warn about unknown non-extension fields. */ if (!relaxed && !startswith(lvalue, "X-")) log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section); return 0; } /* Parse a variable assignment line */ static int parse_line(const char* unit, const char *filename, unsigned line, const char *sections, ConfigItemLookup lookup, const void *table, bool relaxed, bool allow_include, char **section, unsigned *section_line, bool *section_ignored, char *l, void *userdata) { char *e; assert(filename); assert(line > 0); assert(lookup); assert(l); l = strstrip(l); if (!*l) return 0; if (strchr(COMMENTS "\n", *l)) return 0; if (startswith(l, ".include ")) { _cleanup_free_ char *fn = NULL; /* .includes are a bad idea, we only support them here * for historical reasons. They create cyclic include * problems and make it difficult to detect * configuration file changes with an easy * stat(). Better approaches, such as .d/ drop-in * snippets exist. * * Support for them should be eventually removed. */ if (!allow_include) { log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); return 0; } fn = file_in_same_dir(filename, strstrip(l+9)); if (!fn) return -ENOMEM; return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata); } if (*l == '[') { size_t k; char *n; k = strlen(l); assert(k > 0); if (l[k-1] != ']') { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l); return -EBADMSG; } n = strndup(l+1, k-2); if (!n) return -ENOMEM; if (sections && !nulstr_contains(sections, n)) { if (!relaxed && !startswith(n, "X-")) log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); free(n); *section = mfree(*section); *section_line = 0; *section_ignored = true; } else { free(*section); *section = n; *section_line = line; *section_ignored = false; } return 0; } if (sections && !*section) { if (!relaxed && !*section_ignored) log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring."); return 0; } e = strchr(l, '='); if (!e) { log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='."); return -EINVAL; } *e = 0; e++; return next_assignment(unit, filename, line, lookup, table, *section, *section_line, strstrip(l), strstrip(e), relaxed, userdata); } /* Go through the file and parse each line */ int config_parse(const char *unit, const char *filename, FILE *f, const char *sections, ConfigItemLookup lookup, const void *table, bool relaxed, bool allow_include, bool warn, void *userdata) { _cleanup_free_ char *section = NULL, *continuation = NULL; _cleanup_fclose_ FILE *ours = NULL; unsigned line = 0, section_line = 0; bool section_ignored = false, allow_bom = true; int r; assert(filename); assert(lookup); if (!f) { f = ours = fopen(filename, "re"); if (!f) { /* Only log on request, except for ENOENT, * since we return 0 to the caller. */ if (warn || errno == ENOENT) log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR, "Failed to open configuration file '%s': %m", filename); return errno == ENOENT ? 0 : -errno; } } fd_warn_permissions(filename, fileno(f)); for (;;) { char buf[LINE_MAX], *l, *p, *c = NULL, *e; bool escaped = false; if (!fgets(buf, sizeof buf, f)) { if (feof(f)) break; log_error_errno(errno, "Failed to read configuration file '%s': %m", filename); return -errno; } l = buf; if (allow_bom && startswith(l, UTF8_BYTE_ORDER_MARK)) l += strlen(UTF8_BYTE_ORDER_MARK); allow_bom = false; truncate_nl(l); if (continuation) { c = strappend(continuation, l); if (!c) { if (warn) log_oom(); return -ENOMEM; } continuation = mfree(continuation); p = c; } else p = l; for (e = p; *e; e++) { if (escaped) escaped = false; else if (*e == '\\') escaped = true; } if (escaped) { *(e-1) = ' '; if (c) continuation = c; else { continuation = strdup(l); if (!continuation) { if (warn) log_oom(); return -ENOMEM; } } continue; } r = parse_line(unit, filename, ++line, sections, lookup, table, relaxed, allow_include, §ion, §ion_line, §ion_ignored, p, userdata); free(c); if (r < 0) { if (warn) log_warning_errno(r, "Failed to parse file '%s': %m", filename); return r; } } return 0; } /* Parse each config file in the specified directories. */ int config_parse_many(const char *conf_file, const char *conf_file_dirs, const char *sections, ConfigItemLookup lookup, const void *table, bool relaxed, void *userdata) { _cleanup_strv_free_ char **files = NULL; char **fn; int r; r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); if (r < 0) return r; if (conf_file) { r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata); if (r < 0) return r; } STRV_FOREACH(fn, files) { r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata); if (r < 0) return r; } return 0; } int config_parse_string( const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { char **s = data, *n; assert(filename); assert(lvalue); assert(rvalue); assert(data); if (!utf8_is_valid(rvalue)) { log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); return 0; } if (isempty(rvalue)) n = NULL; else { n = strdup(rvalue); if (!n) return log_oom(); } free(*s); *s = n; return 0; } int config_parse_bool( const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { bool fatal = ltype; bool *b = data; int k; assert(filename); assert(lvalue); assert(rvalue); k = parse_boolean(rvalue); if (k < 0) { log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, k, "Failed to parse boolean value%s: %s", fatal ? "" : ", ignoring", rvalue); return fatal ? -ENOEXEC : 0; } *b = k; return 0; } int config_parse_iec_size( const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { size_t *sz = ASSERT_PTR(data); uint64_t v; int r; assert(filename); assert(lvalue); assert(rvalue); r = parse_size(rvalue, 1024, &v); if (r >= 0 && (uint64_t) (size_t) v != v) r = -ERANGE; if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue); return 0; } *sz = (size_t) v; return 0; } #define DEFINE_PARSER(type, vartype, conv_func) \ DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value") DEFINE_PARSER(sec, usec_t, parse_sec); DEFINE_PARSER(unsigned, unsigned, safe_atou); systemd-netlogd-1.4.4/src/share/conf-parser.h000066400000000000000000000356441474012624500211440ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include "alloc-util.h" #include "log.h" #include "macro.h" /* An abstract parser for simple, line based, shallow configuration * files consisting of variable assignments only. */ /* Argument list for parsers of specific configuration settings. */ #define CONFIG_PARSER_ARGUMENTS \ const char *unit, \ const char *filename, \ unsigned line, \ const char *section, \ unsigned section_line, \ const char *lvalue, \ int ltype, \ const char *rvalue, \ void *data, \ void *userdata /* Prototype for a parser for a specific configuration setting */ typedef int (*ConfigParserCallback)(CONFIG_PARSER_ARGUMENTS); /* A macro declaring a function prototype, following the typedef above, simply because it's so cumbersomely long * otherwise. (And current emacs gets irritatingly slow when editing files that contain lots of very long function * prototypes on the same screen…) */ #define CONFIG_PARSER_PROTOTYPE(name) int name(CONFIG_PARSER_ARGUMENTS) /* Prototype for a parser for a specific configuration setting */ typedef int (*ConfigParserCallback)(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* Wraps information for parsing a specific configuration variable, to * be stored in a simple array */ typedef struct ConfigTableItem { const char *section; /* Section */ const char *lvalue; /* Name of the variable */ ConfigParserCallback parse; /* Function that is called to parse the variable's value */ int ltype; /* Distinguish different variables passed to the same callback */ void *data; /* Where to store the variable's data */ } ConfigTableItem; /* Wraps information for parsing a specific configuration variable, to * be stored in a gperf perfect hashtable */ typedef struct ConfigPerfItem { const char *section_and_lvalue; /* Section + "." + name of the variable */ ConfigParserCallback parse; /* Function that is called to parse the variable's value */ int ltype; /* Distinguish different variables passed to the same callback */ size_t offset; /* Offset where to store data, from the beginning of userdata */ } ConfigPerfItem; /* Prototype for a low-level gperf lookup function */ typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length); /* Prototype for a generic high-level lookup function */ typedef int (*ConfigItemLookup)( const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); /* Linear table search implementation of ConfigItemLookup, based on * ConfigTableItem arrays */ int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); /* gperf implementation of ConfigItemLookup, based on gperf * ConfigPerfItem tables */ int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); int config_parse(const char *unit, const char *filename, FILE *f, const char *sections, /* nulstr */ ConfigItemLookup lookup, const void *table, bool relaxed, bool allow_include, bool warn, void *userdata); int config_parse_many(const char *conf_file, /* possibly NULL */ const char *conf_file_dirs, /* nulstr */ const char *sections, /* nulstr */ ConfigItemLookup lookup, const void *table, bool relaxed, void *userdata); #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ int function(const char *unit, \ const char *filename, \ unsigned line, \ const char *section, \ unsigned section_line, \ const char *lvalue, \ int ltype, \ const char *rvalue, \ void *data, \ void *userdata) { \ \ type *i = data, x; \ \ assert(filename); \ assert(lvalue); \ assert(rvalue); \ assert(data); \ \ if ((x = name##_from_string(rvalue)) < 0) { \ log_syntax(unit, LOG_ERR, filename, line, -x, \ msg ", ignoring: %s", rvalue); \ return 0; \ } \ \ *i = x; \ return 0; \ } #define DEFINE_CONFIG_PARSE_ENUMV(function,name,type,invalid,msg) \ int function(const char *unit, \ const char *filename, \ unsigned line, \ const char *section, \ unsigned section_line, \ const char *lvalue, \ int ltype, \ const char *rvalue, \ void *data, \ void *userdata) { \ \ type **enums = data, x, *ys; \ _cleanup_free_ type *xs = NULL; \ const char *word, *state; \ size_t l, i = 0; \ \ assert(filename); \ assert(lvalue); \ assert(rvalue); \ assert(data); \ \ xs = new0(type, 1); \ if (!xs) \ return -ENOMEM; \ \ *xs = invalid; \ \ FOREACH_WORD(word, l, rvalue, state) { \ _cleanup_free_ char *en = NULL; \ type *new_xs; \ \ en = strndup(word, l); \ if (!en) \ return -ENOMEM; \ \ if ((x = name##_from_string(en)) < 0) { \ log_syntax(unit, LOG_ERR, filename, line, \ -x, msg ", ignoring: %s", en); \ continue; \ } \ \ for (ys = xs; x != invalid && *ys != invalid; ys++) { \ if (*ys == x) { \ log_syntax(unit, LOG_ERR, filename, \ line, -x, \ "Duplicate entry, ignoring: %s", \ en); \ x = invalid; \ } \ } \ \ if (x == invalid) \ continue; \ \ *(xs + i) = x; \ new_xs = realloc(xs, (++i + 1) * sizeof(type)); \ if (new_xs) \ xs = new_xs; \ else \ return -ENOMEM; \ \ *(xs + i) = invalid; \ } \ \ free(*enums); \ *enums = xs; \ xs = NULL; \ \ return 0; \ } #define DEFINE_CONFIG_PARSE(function, parser, msg) \ CONFIG_PARSER_PROTOTYPE(function) { \ int *i = data, r; \ \ assert(filename); \ assert(lvalue); \ assert(rvalue); \ assert(data); \ \ r = parser(rvalue); \ if (r < 0) { \ log_syntax(unit, LOG_WARNING, filename, line, r, \ msg ", ignoring: %s", rvalue); \ return 0; \ } \ \ *i = r; \ return 0; \ } #define DEFINE_CONFIG_PARSE_PTR(function, parser, type, msg) \ CONFIG_PARSER_PROTOTYPE(function) { \ type *i = ASSERT_PTR(data); \ int r; \ \ assert(filename); \ assert(lvalue); \ assert(rvalue); \ \ r = parser(rvalue, i); \ if (r < 0) \ log_syntax(unit, LOG_WARNING, filename, line, r, \ msg ", ignoring: %s", rvalue); \ \ return 0; \ } /* Generic parsers */ CONFIG_PARSER_PROTOTYPE(config_parse_string); CONFIG_PARSER_PROTOTYPE(config_parse_bool); CONFIG_PARSER_PROTOTYPE(config_parse_sec); CONFIG_PARSER_PROTOTYPE(config_parse_unsigned); CONFIG_PARSER_PROTOTYPE(config_parse_iec_size); systemd-netlogd-1.4.4/src/share/def.h000066400000000000000000000046341474012624500174560ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "util.h" #define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC) #define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) #define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC) #define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC) #define DEFAULT_START_LIMIT_BURST 5 /* The default time after which exit-on-idle services exit. This * should be kept lower than the watchdog timeout, because otherwise * the watchdog pings will keep the loop busy. */ #define DEFAULT_EXIT_USEC (30*USEC_PER_SEC) /* The default value for the net.unix.max_dgram_qlen sysctl */ #define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL #define SYSTEMD_CGROUP_CONTROLLER "name=systemd" #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT #define SIGNALS_IGNORE SIGPIPE #ifdef HAVE_SPLIT_USR #define KBD_KEYMAP_DIRS \ "/usr/share/keymaps/\0" \ "/usr/share/kbd/keymaps/\0" \ "/usr/lib/kbd/keymaps/\0" \ "/lib/kbd/keymaps/\0" #else #define KBD_KEYMAP_DIRS \ "/usr/share/keymaps/\0" \ "/usr/share/kbd/keymaps/\0" \ "/usr/lib/kbd/keymaps/\0" #endif #define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" #define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus" #define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS #define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" #define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus" #define PLYMOUTH_SOCKET { \ .un.sun_family = AF_UNIX, \ .un.sun_path = "\0/org/freedesktop/plymouthd", \ } #ifndef TTY_GID #define TTY_GID 5 #endif #define NOTIFY_FD_MAX 768 #define NOTIFY_BUFFER_MAX PIPE_BUF #ifdef HAVE_SPLIT_USR #define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0" #else #define _CONF_PATHS_SPLIT_USR(n) #endif /* Return a nulstr for a standard cascade of configuration paths, * suitable to pass to conf_files_list_nulstr() or config_parse_many() * to implement drop-in directories for extending configuration * files. */ #define CONF_PATHS_NULSTR(n) \ "/etc/" n "\0" \ "/run/" n "\0" \ "/usr/local/lib/" n "\0" \ "/usr/lib/" n "\0" \ _CONF_PATHS_SPLIT_USR(n) systemd-netlogd-1.4.4/src/share/dirent-util.c000066400000000000000000000013341474012624500211450ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include "dirent-util.h" #include "path-util.h" #include "string-util.h" bool dirent_is_file(const struct dirent *de) { assert(de); if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; if (hidden_or_backup_file(de->d_name)) return false; return true; } bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { assert(de); if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; if (de->d_name[0] == '.') return false; return endswith(de->d_name, suffix); } systemd-netlogd-1.4.4/src/share/dirent-util.h000066400000000000000000000027771474012624500211660ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include "macro.h" #include "path-util.h" bool dirent_is_file(const struct dirent *de) _pure_; bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_; #define FOREACH_DIRENT(de, d, on_error) \ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \ if (!de) { \ if (errno > 0) { \ on_error; \ } \ break; \ } else if (hidden_or_backup_file((de)->d_name)) \ continue; \ else #define FOREACH_DIRENT_ALL(de, d, on_error) \ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \ if (!de) { \ if (errno > 0) { \ on_error; \ } \ break; \ } else systemd-netlogd-1.4.4/src/share/dns-def.h000066400000000000000000000012641474012624500202340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once /* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */ #define DNS_LABEL_MAX 63 /* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */ #define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) /* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */ #define DNS_HOSTNAME_MAX 253 /* Maximum length of a full hostname, on the wire, including the final NUL byte */ #define DNS_WIRE_FORMAT_HOSTNAME_MAX 255 /* Maximum number of labels per valid hostname */ #define DNS_N_LABELS_MAX 127 systemd-netlogd-1.4.4/src/share/dns-domain.c000066400000000000000000000215421474012624500207410ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "alloc-util.h" #include "dns-domain.h" #include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" #include "string-util.h" int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) { const char *n; char *d, last_char = 0; int r = 0; assert(name); assert(*name); n = *name; d = dest; for (;;) { if (IN_SET(*n, 0, '.')) { if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') /* Trailing dash */ return -EINVAL; if (n[0] == '.' && (n[1] != 0 || !FLAGS_SET(flags, DNS_LABEL_LEAVE_TRAILING_DOT))) n++; break; } if (r >= DNS_LABEL_MAX) return -EINVAL; if (sz <= 0) return -ENOBUFS; if (*n == '\\') { /* Escaped character */ if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES)) return -EINVAL; n++; if (*n == 0) /* Ending NUL */ return -EINVAL; else if (IN_SET(*n, '\\', '.')) { /* Escaped backslash or dot */ if (FLAGS_SET(flags, DNS_LABEL_LDH)) return -EINVAL; last_char = *n; if (d) *(d++) = *n; sz--; r++; n++; } else if (n[0] >= '0' && n[0] <= '9') { unsigned k; /* Escaped literal ASCII character */ if (!(n[1] >= '0' && n[1] <= '9') || !(n[2] >= '0' && n[2] <= '9')) return -EINVAL; k = ((unsigned) (n[0] - '0') * 100) + ((unsigned) (n[1] - '0') * 10) + ((unsigned) (n[2] - '0')); /* Don't allow anything that doesn't fit in 8 bits. Note that we do allow * control characters, as some servers (e.g. cloudflare) are happy to * generate labels with them inside. */ if (k > 255) return -EINVAL; if (FLAGS_SET(flags, DNS_LABEL_LDH) && !valid_ldh_char((char) k)) return -EINVAL; last_char = (char) k; if (d) *(d++) = (char) k; sz--; r++; n += 3; } else return -EINVAL; } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) { /* Normal character */ if (FLAGS_SET(flags, DNS_LABEL_LDH)) { if (!valid_ldh_char(*n)) return -EINVAL; if (r == 0 && *n == '-') /* Leading dash */ return -EINVAL; } last_char = *n; if (d) *(d++) = *n; sz--; r++; n++; } else return -EINVAL; } /* Empty label that is not at the end? */ if (r == 0 && *n) return -EINVAL; /* More than one trailing dot? */ if (n[0] == '.' && !FLAGS_SET(flags, DNS_LABEL_LEAVE_TRAILING_DOT)) return -EINVAL; if (sz >= 1 && d) *d = 0; *name = n; return r; } int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { char *q; /* DNS labels must be between 1 and 63 characters long. A * zero-length label does not exist. See RFC 2181, Section * 11. */ if (l <= 0 || l > DNS_LABEL_MAX) return -EINVAL; if (sz < 1) return -ENOBUFS; assert(p); assert(dest); q = dest; while (l > 0) { if (IN_SET(*p, '.', '\\')) { /* Dot or backslash */ if (sz < 3) return -ENOBUFS; *(q++) = '\\'; *(q++) = *p; sz -= 2; } else if (IN_SET(*p, '_', '-') || ascii_isdigit(*p) || ascii_isalpha(*p)) { /* Proper character */ if (sz < 2) return -ENOBUFS; *(q++) = *p; sz -= 1; } else { /* Everything else */ if (sz < 5) return -ENOBUFS; *(q++) = '\\'; *(q++) = '0' + (char) ((uint8_t) *p / 100); *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); *(q++) = '0' + (char) ((uint8_t) *p % 10); sz -= 4; } p++; l--; } *q = 0; return (int) (q - dest); } int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) { _cleanup_free_ char *ret = NULL; size_t allocated = 0, n = 0; const char *p; bool first = true; int r; if (a) p = a; else if (b) p = TAKE_PTR(b); else goto finish; for (;;) { char label[DNS_LABEL_MAX+1]; r = dns_label_unescape(&p, label, sizeof label, flags); if (r < 0) return r; if (r == 0) { if (*p != 0) return -EINVAL; if (b) { /* Now continue with the second string, if there is one */ p = TAKE_PTR(b); continue; } break; } if (_ret) { if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); if (r < 0) return r; if (!first) ret[n] = '.'; } else { char escaped[DNS_LABEL_ESCAPED_MAX]; r = dns_label_escape(label, r, escaped, sizeof(escaped)); if (r < 0) return r; } n += r + !first; first = false; } finish: if (n > DNS_HOSTNAME_MAX) return -EINVAL; if (_ret) { if (n == 0) { /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ if (!GREEDY_REALLOC(ret, allocated, 2)) return -ENOMEM; ret[n++] = '.'; } else { if (!GREEDY_REALLOC(ret, allocated, n + 1)) return -ENOMEM; } ret[n] = 0; *_ret = TAKE_PTR(ret); } return 0; } int dns_name_is_valid_or_address(const char *name) { /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */ if (isempty(name)) return 0; if (in_addr_from_string_auto(name, NULL, NULL) >= 0) return 1; return dns_name_is_valid(name); } systemd-netlogd-1.4.4/src/share/dns-domain.h000066400000000000000000000026071474012624500207470ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "dns-def.h" #include "hashmap.h" #include "in-addr-util.h" typedef enum DNSLabelFlags { DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */ DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */ DNS_LABEL_LEAVE_TRAILING_DOT = 1 << 2, /* Leave trailing dot in place */ } DNSLabelFlags; int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret); static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) { /* dns_name_concat() normalizes as a side-effect */ return dns_name_concat(s, NULL, flags, ret); } static inline int dns_name_is_valid(const char *s) { int r; /* dns_name_concat() verifies as a side effect */ r = dns_name_concat(s, NULL, 0, NULL); if (r == -EINVAL) return 0; if (r < 0) return r; return 1; } int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags); int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); int dns_name_address(const char *p, int *family, union in_addr_union *a); int dns_name_is_valid_or_address(const char *name); systemd-netlogd-1.4.4/src/share/escape.c000066400000000000000000000121331474012624500201440ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include "alloc-util.h" #include "escape.h" #include "hexdecoct.h" #include "macro.h" #include "utf8.h" int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) { int r = 1; assert(p); assert(*p); assert(ret); /* Unescapes C style. Returns the unescaped character in ret. * Sets *eight_bit to true if the escaped sequence either fits in * one byte in UTF-8 or is a non-unicode literal byte and should * instead be copied directly. */ if (length != (size_t) -1 && length < 1) return -EINVAL; switch (p[0]) { case 'a': *ret = '\a'; break; case 'b': *ret = '\b'; break; case 'f': *ret = '\f'; break; case 'n': *ret = '\n'; break; case 'r': *ret = '\r'; break; case 't': *ret = '\t'; break; case 'v': *ret = '\v'; break; case '\\': *ret = '\\'; break; case '"': *ret = '"'; break; case '\'': *ret = '\''; break; case 's': /* This is an extension of the XDG syntax files */ *ret = ' '; break; case 'x': { /* hexadecimal encoding */ int a, b; if (length != (size_t) -1 && length < 3) return -EINVAL; a = unhexchar(p[1]); if (a < 0) return -EINVAL; b = unhexchar(p[2]); if (b < 0) return -EINVAL; /* Don't allow NUL bytes */ if (a == 0 && b == 0) return -EINVAL; *ret = (a << 4U) | b; *eight_bit = true; r = 3; break; } case 'u': { /* C++11 style 16bit unicode */ int a[4]; unsigned i; uint32_t c; if (length != (size_t) -1 && length < 5) return -EINVAL; for (i = 0; i < 4; i++) { a[i] = unhexchar(p[1 + i]); if (a[i] < 0) return a[i]; } c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3]; /* Don't allow 0 chars */ if (c == 0) return -EINVAL; *ret = c; r = 5; break; } case 'U': { /* C++11 style 32bit unicode */ int a[8]; unsigned i; char32_t c; if (length != (size_t) -1 && length < 9) return -EINVAL; for (i = 0; i < 8; i++) { a[i] = unhexchar(p[1 + i]); if (a[i] < 0) return a[i]; } c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) | ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; /* Don't allow 0 chars */ if (c == 0) return -EINVAL; /* Don't allow invalid code points */ if (!unichar_is_valid(c)) return -EINVAL; *ret = c; r = 9; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { /* octal encoding */ int a, b, c; char32_t m; if (length != (size_t) -1 && length < 3) return -EINVAL; a = unoctchar(p[0]); if (a < 0) return -EINVAL; b = unoctchar(p[1]); if (b < 0) return -EINVAL; c = unoctchar(p[2]); if (c < 0) return -EINVAL; /* don't allow NUL bytes */ if (a == 0 && b == 0 && c == 0) return -EINVAL; /* Don't allow bytes above 255 */ m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c; if (m > 255) return -EINVAL; *ret = m; *eight_bit = true; r = 3; break; } default: return -EINVAL; } return r; } systemd-netlogd-1.4.4/src/share/escape.h000066400000000000000000000011471474012624500201540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include "string-util.h" #include "missing.h" /* What characters are special in the shell? */ /* must be escaped outside and inside double-quotes */ #define SHELL_NEED_ESCAPE "\"\\`$" /* can be escaped or double-quoted */ #define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;" typedef enum UnescapeFlags { UNESCAPE_RELAX = 1, } UnescapeFlags; int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit); systemd-netlogd-1.4.4/src/share/extract-word.c000066400000000000000000000166471474012624500213450ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "escape.h" #include "extract-word.h" #include "log.h" #include "macro.h" #include "string-util.h" #include "utf8.h" int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { _cleanup_free_ char *s = NULL; size_t allocated = 0, sz = 0; char c; int r; char quote = 0; /* 0 or ' or " */ bool backslash = false; /* whether we've just seen a backslash */ assert(p); assert(ret); /* Bail early if called after last value or with no input */ if (!*p) goto finish_force_terminate; c = **p; if (!separators) separators = WHITESPACE; /* Parses the first word of a string, and returns it in * *ret. Removes all quotes in the process. When parsing fails * (because of an uneven number of quotes or similar), leaves * the pointer *p at the first invalid character. */ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) if (!GREEDY_REALLOC(s, allocated, sz+1)) return -ENOMEM; for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { (*p)++; goto finish_force_next; } } else { /* We found a non-blank character, so we will always * want to return a string (even if it is empty), * allocate it here. */ if (!GREEDY_REALLOC(s, allocated, sz+1)) return -ENOMEM; break; } } for (;; (*p)++, c = **p) { if (backslash) { if (!GREEDY_REALLOC(s, allocated, sz+7)) return -ENOMEM; if (c == 0) { if ((flags & EXTRACT_CUNESCAPE_RELAX) && (!quote || flags & EXTRACT_RELAX)) { /* If we find an unquoted trailing backslash and we're in * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the * output. * * Unbalanced quotes will only be allowed in EXTRACT_RELAX * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. */ s[sz++] = '\\'; goto finish_force_terminate; } if (flags & EXTRACT_RELAX) goto finish_force_terminate; return -EINVAL; } if (flags & EXTRACT_CUNESCAPE) { bool eight_bit = false; char32_t u; r = cunescape_one(*p, (size_t) -1, &u, &eight_bit); if (r < 0) { if (flags & EXTRACT_CUNESCAPE_RELAX) { s[sz++] = '\\'; s[sz++] = c; } else return -EINVAL; } else { (*p) += r - 1; if (eight_bit) s[sz++] = u; else sz += utf8_encode_unichar(s + sz, u); } } else s[sz++] = c; backslash = false; } else if (quote) { /* inside either single or double quotes */ for (;; (*p)++, c = **p) { if (c == 0) { if (flags & EXTRACT_RELAX) goto finish_force_terminate; return -EINVAL; } else if (c == quote) { /* found the end quote */ quote = 0; break; } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { backslash = true; break; } else { if (!GREEDY_REALLOC(s, allocated, sz+2)) return -ENOMEM; s[sz++] = c; } } } else { for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) { quote = c; break; } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { backslash = true; break; } else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { (*p)++; goto finish_force_next; } /* Skip additional coalesced separators. */ for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; if (!strchr(separators, c)) break; } goto finish; } else { if (!GREEDY_REALLOC(s, allocated, sz+2)) return -ENOMEM; s[sz++] = c; } } } } finish_force_terminate: *p = NULL; finish: if (!s) { *p = NULL; *ret = NULL; return 0; } finish_force_next: s[sz] = 0; *ret = s; s = NULL; return 1; } systemd-netlogd-1.4.4/src/share/extract-word.h000066400000000000000000000007661474012624500213450ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "macro.h" typedef enum ExtractFlags { EXTRACT_RELAX = 1, EXTRACT_CUNESCAPE = 2, EXTRACT_CUNESCAPE_RELAX = 4, EXTRACT_QUOTES = 8, EXTRACT_DONT_COALESCE_SEPARATORS = 16, EXTRACT_RETAIN_ESCAPE = 32, } ExtractFlags; int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); systemd-netlogd-1.4.4/src/share/fd-util.c000066400000000000000000000120261474012624500202510ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "fd-util.h" #include "fs-util.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" #include "socket-util.h" #include "stdio-util.h" #include "util.h" int close_nointr(int fd) { assert(fd >= 0); if (close(fd) >= 0) return 0; /* * Just ignore EINTR; a retry loop is the wrong thing to do on * Linux. * * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html * https://bugzilla.gnome.org/show_bug.cgi?id=682819 * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain */ if (errno == EINTR) return 0; return -errno; } int safe_close(int fd) { /* * Like close_nointr() but cannot fail. Guarantees errno is * unchanged. Is a NOP with negative fds passed, and returns * -1, so that it can be used in this syntax: * * fd = safe_close(fd); */ if (fd >= 0) { PROTECT_ERRNO; /* The kernel might return pretty much any error code * via close(), but the fd will be closed anyway. The * only condition we want to check for here is whether * the fd was invalid at all... */ assert_se(close_nointr(fd) != -EBADF); } return -1; } void close_many(const int fds[], unsigned n_fd) { unsigned i; assert(fds || n_fd <= 0); for (i = 0; i < n_fd; i++) safe_close(fds[i]); } int fclose_nointr(FILE *f) { assert(f); /* Same as close_nointr(), but for fclose() */ if (fclose(f) == 0) return 0; if (errno == EINTR) return 0; return -errno; } FILE* safe_fclose(FILE *f) { /* Same as safe_close(), but for fclose() */ if (f) { PROTECT_ERRNO; assert_se(fclose_nointr(f) != EBADF); } return NULL; } int fd_cloexec(int fd, bool cloexec) { int flags, nflags; assert(fd >= 0); flags = fcntl(fd, F_GETFD, 0); if (flags < 0) return -errno; if (cloexec) nflags = flags | FD_CLOEXEC; else nflags = flags & ~FD_CLOEXEC; if (nflags == flags) return 0; if (fcntl(fd, F_SETFD, nflags) < 0) return -errno; return 0; } int fd_nonblock(int fd, bool nonblock) { int flags, nflags; assert(fd >= 0); flags = fcntl(fd, F_GETFL, 0); if (flags < 0) return -errno; nflags = UPDATE_FLAG(flags, O_NONBLOCK, nonblock); if (nflags == flags) return 0; return RET_NERRNO(fcntl(fd, F_SETFL, nflags)); } void stdio_unset_cloexec(void) { fd_cloexec(STDIN_FILENO, false); fd_cloexec(STDOUT_FILENO, false); fd_cloexec(STDERR_FILENO, false); } void cmsg_close_all(struct msghdr *mh) { struct cmsghdr *cmsg; assert(mh); CMSG_FOREACH(cmsg, mh) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); } int fd_move_above_stdio(int fd) { int flags, copy; PROTECT_ERRNO; /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as * stdin/stdout/stderr of unrelated code. * * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has * been closed before. * * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an * error we simply return the original file descriptor, and we do not touch errno. */ if (fd < 0 || fd > 2) return fd; flags = fcntl(fd, F_GETFD, 0); if (flags < 0) return fd; if (flags & FD_CLOEXEC) copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); else copy = fcntl(fd, F_DUPFD, 3); if (copy < 0) return fd; assert(copy > 2); (void) close(fd); return copy; } systemd-netlogd-1.4.4/src/share/fd-util.h000066400000000000000000000032471474012624500202630ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "macro.h" /* Make sure we can distinguish fd 0 and NULL */ #define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) #define PTR_TO_FD(p) (PTR_TO_INT(p)-1) int close_nointr(int fd); int safe_close(int fd); void close_many(const int fds[], unsigned n_fd); int fclose_nointr(FILE *f); FILE* safe_fclose(FILE *f); static inline void closep(int *fd) { safe_close(*fd); } static inline void fclosep(FILE **f) { safe_fclose(*f); } DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose); DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); #define _cleanup_close_ _cleanup_(closep) #define _cleanup_fclose_ _cleanup_(fclosep) #define _cleanup_pclose_ _cleanup_(pclosep) #define _cleanup_closedir_ _cleanup_(closedirp) #define _cleanup_close_pair_ _cleanup_(close_pairp) int fd_cloexec(int fd, bool cloexec); int fd_nonblock(int fd, bool nonblock); void stdio_unset_cloexec(void); int fd_move_above_stdio(int fd); void cmsg_close_all(struct msghdr *mh); /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ #define ERRNO_IS_DISCONNECT(r) \ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) /* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ #define TAKE_FD(fd) \ ({ \ int *_fd_ = &(fd); \ int _ret_ = *_fd_; \ *_fd_ = -1; \ _ret_; \ }) systemd-netlogd-1.4.4/src/share/fileio.c000066400000000000000000000441011474012624500201530ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "ctype.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "hexdecoct.h" #include "log.h" #include "macro.h" #include "parse-util.h" #include "path-util.h" #include "random-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" #include "umask-util.h" #include "utf8.h" int write_string_stream(FILE *f, const char *line, bool enforce_newline) { assert(f); assert(line); fputs(line, f); if (enforce_newline && !endswith(line, "\n")) fputc('\n', f); return fflush_and_check(f); } int read_one_line_file(const char *fn, char **line) { _cleanup_fclose_ FILE *f = NULL; char t[LINE_MAX], *c; assert(fn); assert(line); f = fopen(fn, "re"); if (!f) return -errno; if (!fgets(t, sizeof(t), f)) { if (ferror(f)) return errno > 0 ? -errno : -EIO; t[0] = 0; } c = strdup(t); if (!c) return -ENOMEM; truncate_nl(c); *line = c; return 0; } int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *buf = NULL; size_t l, k; assert(fn); assert(blob); l = strlen(blob); if (accept_extra_nl && endswith(blob, "\n")) accept_extra_nl = false; buf = malloc(l + accept_extra_nl + 1); if (!buf) return -ENOMEM; f = fopen(fn, "re"); if (!f) return -errno; /* We try to read one byte more than we need, so that we know whether we hit eof */ errno = 0; k = fread(buf, 1, l + accept_extra_nl + 1, f); if (ferror(f)) return errno > 0 ? -errno : -EIO; if (k != l && k != l + accept_extra_nl) return 0; if (memcmp(buf, blob, l) != 0) return 0; if (k > l && buf[l] != '\n') return 0; return 1; } int read_full_stream(FILE *f, char **contents, size_t *size) { size_t n, l; _cleanup_free_ char *buf = NULL; struct stat st; assert(f); assert(contents); if (fstat(fileno(f), &st) < 0) return -errno; n = LINE_MAX; if (S_ISREG(st.st_mode)) { /* Safety check */ if (st.st_size > 4*1024*1024) return -E2BIG; /* Start with the right file size, but be prepared for * files from /proc which generally report a file size * of 0 */ if (st.st_size > 0) n = st.st_size; } l = 0; for (;;) { char *t; size_t k; t = realloc(buf, n+1); if (!t) return -ENOMEM; buf = t; k = fread(buf + l, 1, n - l, f); if (k <= 0) { if (ferror(f)) return -errno; break; } l += k; n *= 2; /* Safety check */ if (n > 4*1024*1024) return -E2BIG; } buf[l] = 0; *contents = buf; buf = NULL; /* do not free */ if (size) *size = l; return 0; } int read_full_file(const char *fn, char **contents, size_t *size) { _cleanup_fclose_ FILE *f = NULL; assert(fn); assert(contents); f = fopen(fn, "re"); if (!f) return -errno; return read_full_stream(f, contents, size); } static int parse_env_file_internal( FILE *f, const char *fname, const char *newline, int (*push) (const char *filename, unsigned line, const char *key, char *value, void *userdata, int *n_pushed), void *userdata, int *n_pushed) { _cleanup_free_ char *contents = NULL, *key = NULL; size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; char *p, *value = NULL; int r; unsigned line = 1; enum { PRE_KEY, KEY, PRE_VALUE, VALUE, VALUE_ESCAPE, SINGLE_QUOTE_VALUE, SINGLE_QUOTE_VALUE_ESCAPE, DOUBLE_QUOTE_VALUE, DOUBLE_QUOTE_VALUE_ESCAPE, COMMENT, COMMENT_ESCAPE } state = PRE_KEY; assert(newline); if (f) r = read_full_stream(f, &contents, NULL); else r = read_full_file(fname, &contents, NULL); if (r < 0) return r; for (p = contents; *p; p++) { char c = *p; switch (state) { case PRE_KEY: if (strchr(COMMENTS, c)) state = COMMENT; else if (!strchr(WHITESPACE, c)) { state = KEY; last_key_whitespace = (size_t) -1; if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { r = -ENOMEM; goto fail; } key[n_key++] = c; } break; case KEY: if (strchr(newline, c)) { state = PRE_KEY; line++; n_key = 0; } else if (c == '=') { state = PRE_VALUE; last_value_whitespace = (size_t) -1; } else { if (!strchr(WHITESPACE, c)) last_key_whitespace = (size_t) -1; else if (last_key_whitespace == (size_t) -1) last_key_whitespace = n_key; if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { r = -ENOMEM; goto fail; } key[n_key++] = c; } break; case PRE_VALUE: if (strchr(newline, c)) { state = PRE_KEY; line++; key[n_key] = 0; if (value) value[n_value] = 0; /* strip trailing whitespace from key */ if (last_key_whitespace != (size_t) -1) key[last_key_whitespace] = 0; r = push(fname, line, key, value, userdata, n_pushed); if (r < 0) goto fail; n_key = 0; value = NULL; value_alloc = n_value = 0; } else if (c == '\'') state = SINGLE_QUOTE_VALUE; else if (c == '\"') state = DOUBLE_QUOTE_VALUE; else if (c == '\\') state = VALUE_ESCAPE; else if (!strchr(WHITESPACE, c)) { state = VALUE; if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case VALUE: if (strchr(newline, c)) { state = PRE_KEY; line++; key[n_key] = 0; if (value) value[n_value] = 0; /* Chomp off trailing whitespace from value */ if (last_value_whitespace != (size_t) -1) value[last_value_whitespace] = 0; /* strip trailing whitespace from key */ if (last_key_whitespace != (size_t) -1) key[last_key_whitespace] = 0; r = push(fname, line, key, value, userdata, n_pushed); if (r < 0) goto fail; n_key = 0; value = NULL; value_alloc = n_value = 0; } else if (c == '\\') { state = VALUE_ESCAPE; last_value_whitespace = (size_t) -1; } else { if (!strchr(WHITESPACE, c)) last_value_whitespace = (size_t) -1; else if (last_value_whitespace == (size_t) -1) last_value_whitespace = n_value; if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case VALUE_ESCAPE: state = VALUE; if (!strchr(newline, c)) { /* Escaped newlines we eat up entirely */ if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case SINGLE_QUOTE_VALUE: if (c == '\'') state = PRE_VALUE; else if (c == '\\') state = SINGLE_QUOTE_VALUE_ESCAPE; else { if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case SINGLE_QUOTE_VALUE_ESCAPE: state = SINGLE_QUOTE_VALUE; if (!strchr(newline, c)) { if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case DOUBLE_QUOTE_VALUE: if (c == '\"') state = PRE_VALUE; else if (c == '\\') state = DOUBLE_QUOTE_VALUE_ESCAPE; else { if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case DOUBLE_QUOTE_VALUE_ESCAPE: state = DOUBLE_QUOTE_VALUE; if (!strchr(newline, c)) { if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case COMMENT: if (c == '\\') state = COMMENT_ESCAPE; else if (strchr(newline, c)) { state = PRE_KEY; line++; } break; case COMMENT_ESCAPE: state = COMMENT; break; } } if (state == PRE_VALUE || state == VALUE || state == VALUE_ESCAPE || state == SINGLE_QUOTE_VALUE || state == SINGLE_QUOTE_VALUE_ESCAPE || state == DOUBLE_QUOTE_VALUE || state == DOUBLE_QUOTE_VALUE_ESCAPE) { key[n_key] = 0; if (value) value[n_value] = 0; if (state == VALUE) if (last_value_whitespace != (size_t) -1) value[last_value_whitespace] = 0; /* strip trailing whitespace from key */ if (last_key_whitespace != (size_t) -1) key[last_key_whitespace] = 0; r = push(fname, line, key, value, userdata, n_pushed); if (r < 0) goto fail; } return 0; fail: free(value); return r; } static int parse_env_file_push( const char *filename, unsigned line, const char *key, char *value, void *userdata, int *n_pushed) { const char *k; va_list aq, *ap = userdata; if (!utf8_is_valid(key)) { _cleanup_free_ char *p = NULL; p = utf8_escape_invalid(key); log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); return -EINVAL; } if (value && !utf8_is_valid(value)) { _cleanup_free_ char *p = NULL; p = utf8_escape_invalid(value); log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); return -EINVAL; } va_copy(aq, *ap); while ((k = va_arg(aq, const char *))) { char **v; v = va_arg(aq, char **); if (streq(key, k)) { va_end(aq); free(*v); *v = value; if (n_pushed) (*n_pushed)++; return 1; } } va_end(aq); free(value); return 0; } int parse_env_file( const char *fname, const char *newline, ...) { va_list ap; int r, n_pushed = 0; if (!newline) newline = NEWLINE; va_start(ap, newline); r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed); va_end(ap); return r < 0 ? r : n_pushed; } int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { FILE *f; char *t; int r, fd; assert(path); assert(_f); assert(_temp_path); r = tempfn_xxxxxx(path, NULL, &t); if (r < 0) return r; fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC); if (fd < 0) { free(t); return -errno; } f = fdopen(fd, "we"); if (!f) { unlink_noerrno(t); free(t); safe_close(fd); return -errno; } *_f = f; *_temp_path = t; return 0; } int fflush_and_check(FILE *f) { assert(f); errno = 0; fflush(f); if (ferror(f)) return errno > 0 ? -errno : -EIO; return 0; } /* This is much like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern, int flags) { _cleanup_umask_ mode_t u = 0; int fd; assert(pattern); u = umask(077); fd = mkostemp(pattern, flags); if (fd < 0) return -errno; return fd; } int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { const char *fn; char *t; assert(p); assert(ret); /* * Turns this: * /foo/bar/waldo * * Into this: * /foo/bar/.#waldoXXXXXX */ fn = basename(p); if (!filename_is_valid(fn)) return -EINVAL; if (extra == NULL) extra = ""; t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); if (!t) return -ENOMEM; strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); *ret = path_kill_slashes(t); return 0; } systemd-netlogd-1.4.4/src/share/fileio.h000066400000000000000000000027711474012624500201670ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include "macro.h" #include "time-util.h" typedef enum { WRITE_STRING_FILE_CREATE = 1, WRITE_STRING_FILE_ATOMIC = 2, WRITE_STRING_FILE_AVOID_NEWLINE = 4, WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8, } WriteStringFileFlags; int write_string_stream(FILE *f, const char *line, bool enforce_newline); int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); int read_full_stream(FILE *f, char **contents, size_t *size); int verify_file(const char *fn, const char *blob, bool accept_extra_nl); int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; #define FOREACH_LINE(line, f, on_error) \ for (;;) \ if (!fgets(line, sizeof(line), f)) { \ if (ferror(f)) { \ on_error; \ } \ break; \ } else int fflush_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); int mkostemp_safe(char *pattern, int flags); int tempfn_xxxxxx(const char *p, const char *extra, char **ret); systemd-netlogd-1.4.4/src/share/formats-util.h000066400000000000000000000021471474012624500213430ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #if SIZEOF_PID_T == 4 # define PID_PRI PRIi32 #elif SIZEOF_PID_T == 2 # define PID_PRI PRIi16 #else # error Unknown pid_t size #endif #define PID_FMT "%" PID_PRI #if SIZEOF_UID_T == 4 # define UID_FMT "%" PRIu32 #elif SIZEOF_UID_T == 2 # define UID_FMT "%" PRIu16 #else # error Unknown uid_t size #endif #if SIZEOF_GID_T == 4 # define GID_FMT "%" PRIu32 #elif SIZEOF_GID_T == 2 # define GID_FMT "%" PRIu16 #else # error Unknown gid_t size #endif #if SIZEOF_TIME_T == 8 # define PRI_TIME PRIi64 #elif SIZEOF_TIME_T == 4 # define PRI_TIME "li" #else # error Unknown time_t size #endif #if SIZEOF_RLIM_T == 8 # define RLIM_FMT "%" PRIu64 #elif SIZEOF_RLIM_T == 4 # define RLIM_FMT "%" PRIu32 #else # error Unknown rlim_t size #endif #if SIZEOF_DEV_T == 8 # define DEV_FMT "%" PRIu64 #elif SIZEOF_DEV_T == 4 # define DEV_FMT "%" PRIu32 #else # error Unknown dev_t size #endif #if SIZEOF_INO_T == 8 # define INO_FMT "%" PRIu64 #elif SIZEOF_INO_T == 4 # define INO_FMT "%" PRIu32 #else # error Unknown ino_t size #endif systemd-netlogd-1.4.4/src/share/fs-util.c000066400000000000000000000076001474012624500202720ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "log.h" #include "macro.h" #include "missing.h" #include "mkdir.h" #include "parse-util.h" #include "path-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" #include "user-util.h" #include "util.h" int unlink_noerrno(const char *path) { PROTECT_ERRNO; int r; r = unlink(path); if (r < 0) return -errno; return 0; } int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { assert(path); /* Under the assumption that we are running privileged we * first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ if (mode != MODE_INVALID) if (chmod(path, mode) < 0) return -errno; if (uid != UID_INVALID || gid != GID_INVALID) if (chown(path, uid, gid) < 0) return -errno; return 0; } int fchmod_umask(int fd, mode_t m) { mode_t u; int r; u = umask(0777); r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; umask(u); return r; } int fd_warn_permissions(const char *path, int fd) { struct stat st; if (fstat(fd, &st) < 0) return -errno; if (st.st_mode & 0111) log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); if (st.st_mode & 0002) log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); if (getpid() == 1 && (st.st_mode & 0044) != 0044) log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); return 0; } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { _cleanup_close_ int fd; int r; assert(path); if (parents) mkdir_parents(path, 0755); fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, (mode == 0 || mode == MODE_INVALID) ? 0644 : mode); if (fd < 0) return -errno; if (mode != MODE_INVALID) { r = fchmod(fd, mode); if (r < 0) return -errno; } if (uid != UID_INVALID || gid != GID_INVALID) { r = fchown(fd, uid, gid); if (r < 0) return -errno; } if (stamp != USEC_INFINITY) { struct timespec ts[2]; timespec_store(&ts[0], stamp); ts[1] = ts[0]; r = futimens(fd, ts); } else r = futimens(fd, NULL); if (r < 0) return -errno; return 0; } int touch(const char *path) { return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); } int inotify_add_watch_fd(int fd, int what, uint32_t mask) { char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; int r; /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ xsprintf(path, "/proc/self/fd/%i", what); r = inotify_add_watch(fd, path, mask); if (r < 0) return -errno; return r; } systemd-netlogd-1.4.4/src/share/fs-util.h000066400000000000000000000022241474012624500202740ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include #include #include "time-util.h" int unlink_noerrno(const char *path); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_umask(int fd, mode_t mode); int fd_warn_permissions(const char *path, int fd); #define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); int touch(const char *path); #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) #define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ for ((e) = &buffer.ev; \ (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) union inotify_event_buffer { struct inotify_event ev; uint8_t raw[INOTIFY_EVENT_MAX]; }; int inotify_add_watch_fd(int fd, int what, uint32_t mask); systemd-netlogd-1.4.4/src/share/hash-funcs.c000066400000000000000000000031331474012624500207430ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "hash-funcs.h" void string_hash_func(const void *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); } int string_compare_func(const void *a, const void *b) { return strcmp(a, b); } const struct hash_ops string_hash_ops = { .hash = string_hash_func, .compare = string_compare_func }; void trivial_hash_func(const void *p, struct siphash *state) { siphash24_compress(&p, sizeof(p), state); } int trivial_compare_func(const void *a, const void *b) { return a < b ? -1 : (a > b ? 1 : 0); } const struct hash_ops trivial_hash_ops = { .hash = trivial_hash_func, .compare = trivial_compare_func }; void uint64_hash_func(const void *p, struct siphash *state) { siphash24_compress(p, sizeof(uint64_t), state); } int uint64_compare_func(const void *_a, const void *_b) { uint64_t a, b; a = *(const uint64_t*) _a; b = *(const uint64_t*) _b; return a < b ? -1 : (a > b ? 1 : 0); } const struct hash_ops uint64_hash_ops = { .hash = uint64_hash_func, .compare = uint64_compare_func }; #if SIZEOF_DEV_T != 8 void devt_hash_func(const void *p, struct siphash *state) { siphash24_compress(p, sizeof(dev_t), state); } int devt_compare_func(const void *_a, const void *_b) { dev_t a, b; a = *(const dev_t*) _a; b = *(const dev_t*) _b; return a < b ? -1 : (a > b ? 1 : 0); } const struct hash_ops devt_hash_ops = { .hash = devt_hash_func, .compare = devt_compare_func }; #endif systemd-netlogd-1.4.4/src/share/hash-funcs.h000066400000000000000000000032451474012624500207540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "macro.h" #include "siphash24.h" typedef void (*hash_func_t)(const void *p, struct siphash *state); typedef int (*compare_func_t)(const void *a, const void *b); struct hash_ops { hash_func_t hash; compare_func_t compare; }; void string_hash_func(const void *p, struct siphash *state); int string_compare_func(const void *a, const void *b) _pure_; extern const struct hash_ops string_hash_ops; /* This will compare the passed pointers directly, and will not * dereference them. This is hence not useful for strings or * suchlike. */ void trivial_hash_func(const void *p, struct siphash *state); int trivial_compare_func(const void *a, const void *b) _const_; extern const struct hash_ops trivial_hash_ops; /* 32bit values we can always just embed in the pointer itself, but * in order to support 32bit archs we need store 64bit values * indirectly, since they don't fit in a pointer. */ void uint64_hash_func(const void *p, struct siphash *state); int uint64_compare_func(const void *a, const void *b) _pure_; extern const struct hash_ops uint64_hash_ops; /* On some archs dev_t is 32bit, and on others 64bit. And sometimes * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */ #if SIZEOF_DEV_T != 8 void devt_hash_func(const void *p, struct siphash *state) _pure_; int devt_compare_func(const void *a, const void *b) _pure_; extern const struct hash_ops devt_hash_ops = { .hash = devt_hash_func, .compare = devt_compare_func }; #else #define devt_hash_func uint64_hash_func #define devt_compare_func uint64_compare_func #define devt_hash_ops uint64_hash_ops #endif systemd-netlogd-1.4.4/src/share/hashmap.c000066400000000000000000001602761474012624500203410ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "alloc-util.h" #include "hashmap.h" #include "macro.h" #include "mempool.h" #include "process-util.h" #include "random-util.h" #include "set.h" #include "siphash24.h" #include "strv.h" #include "util.h" #ifdef ENABLE_DEBUG_HASHMAP #include #include "list.h" #endif /* * Implementation of hashmaps. * Addressing: open * - uses less RAM compared to closed addressing (chaining), because * our entries are small (especially in Sets, which tend to contain * the majority of entries in systemd). * Collision resolution: Robin Hood * - tends to equalize displacement of entries from their optimal buckets. * Probe sequence: linear * - though theoretically worse than random probing/uniform hashing/double * hashing, it is good for cache locality. * * References: * Celis, P. 1986. Robin Hood Hashing. * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf * - The results are derived for random probing. Suggests deletion with * tombstones and two mean-centered search methods. None of that works * well for linear probing. * * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 * http://www.math.uu.se/~svante/papers/sj157.pdf * - Applies to Robin Hood with linear probing. Contains remarks on * the unsuitability of mean-centered search with linear probing. * * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes * in a successful search), and Janson writes about displacement. C = d + 1. * * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ * - Explanation of backward shift deletion with pictures. * * Khuong, P. 2013. The Other Robin Hood Hashing. * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ * - Short summary of random vs. linear probing, and tombstones vs. backward shift. */ /* * XXX Ideas for improvement: * For unordered hashmaps, randomize iteration order, similarly to Perl: * http://blog.booking.com/hardening-perls-hash-function.html */ /* INV_KEEP_FREE = 1 / (1 - max_load_factor) * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ #define INV_KEEP_FREE 5U /* Fields common to entries of all hashmap/set types */ struct hashmap_base_entry { const void *key; }; /* Entry types for specific hashmap/set types * hashmap_base_entry must be at the beginning of each entry struct. */ struct plain_hashmap_entry { struct hashmap_base_entry b; void *value; }; struct ordered_hashmap_entry { struct plain_hashmap_entry p; unsigned iterate_next, iterate_previous; }; struct set_entry { struct hashmap_base_entry b; }; /* In several functions it is advantageous to have the hash table extended * virtually by a couple of additional buckets. We reserve special index values * for these "swap" buckets. */ #define _IDX_SWAP_BEGIN (UINT_MAX - 3) #define IDX_PUT (_IDX_SWAP_BEGIN + 0) #define IDX_TMP (_IDX_SWAP_BEGIN + 1) #define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) #define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ #define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ assert_cc(IDX_FIRST == _IDX_SWAP_END); assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); /* Storage space for the "swap" buckets. * All entry types can fit into a ordered_hashmap_entry. */ struct swap_entries { struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; }; /* Distance from Initial Bucket */ typedef uint8_t dib_raw_t; #define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ #define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ #define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ #define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ #define DIB_FREE UINT_MAX #ifdef ENABLE_DEBUG_HASHMAP struct hashmap_debug_info { LIST_FIELDS(struct hashmap_debug_info, debug_list); unsigned max_entries; /* high watermark of n_entries */ /* who allocated this hashmap */ int line; const char *file; const char *func; /* fields to detect modification while iterating */ unsigned put_count; /* counts puts into the hashmap */ unsigned rem_count; /* counts removals from hashmap */ unsigned last_rem_idx; /* remembers last removal index */ }; /* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; #define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; #else /* !ENABLE_DEBUG_HASHMAP */ #define HASHMAP_DEBUG_FIELDS #endif /* ENABLE_DEBUG_HASHMAP */ enum HashmapType { HASHMAP_TYPE_PLAIN, HASHMAP_TYPE_ORDERED, HASHMAP_TYPE_SET, _HASHMAP_TYPE_MAX }; struct _packed_ indirect_storage { void *storage; /* where buckets and DIBs are stored */ uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ unsigned n_entries; /* number of stored entries */ unsigned n_buckets; /* number of buckets */ unsigned idx_lowest_entry; /* Index below which all buckets are free. Makes "while(hashmap_steal_first())" loops O(n) instead of O(n^2) for unordered hashmaps. */ uint8_t _pad[3]; /* padding for the whole HashmapBase */ /* The bitfields in HashmapBase complete the alignment of the whole thing. */ }; struct direct_storage { /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ uint8_t storage[sizeof(struct indirect_storage)]; }; #define DIRECT_BUCKETS(entry_t) \ (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) /* We should be able to store at least one entry directly. */ assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); /* We have 3 bits for n_direct_entries. */ assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); /* Hashmaps with directly stored entries all use this shared hash key. * It's no big deal if the key is guessed, because there can be only * a handful of directly stored entries in a hashmap. When a hashmap * outgrows direct storage, it gets its own key for indirect storage. */ static uint8_t shared_hash_key[HASH_KEY_SIZE]; static bool shared_hash_key_initialized; /* Fields that all hashmap/set types must have */ struct HashmapBase { const struct hash_ops *hash_ops; /* hash and compare ops to use */ union _packed_ { struct indirect_storage indirect; /* if has_indirect */ struct direct_storage direct; /* if !has_indirect */ }; enum HashmapType type:2; /* HASHMAP_TYPE_* */ bool has_indirect:1; /* whether indirect storage is used */ unsigned n_direct_entries:3; /* Number of entries in direct storage. * Only valid if !has_indirect. */ bool from_pool:1; /* whether was allocated from mempool */ HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ }; /* Specific hash types * HashmapBase must be at the beginning of each hashmap struct. */ struct Hashmap { struct HashmapBase b; }; struct OrderedHashmap { struct HashmapBase b; unsigned iterate_list_head, iterate_list_tail; }; struct Set { struct HashmapBase b; }; DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); /* No need for a separate Set pool */ assert_cc(sizeof(Hashmap) == sizeof(Set)); struct hashmap_type_info { size_t head_size; size_t entry_size; struct mempool *mempool; unsigned n_direct_buckets; }; static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { [HASHMAP_TYPE_PLAIN] = { .head_size = sizeof(Hashmap), .entry_size = sizeof(struct plain_hashmap_entry), .mempool = &hashmap_pool, .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), }, [HASHMAP_TYPE_ORDERED] = { .head_size = sizeof(OrderedHashmap), .entry_size = sizeof(struct ordered_hashmap_entry), .mempool = &ordered_hashmap_pool, .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), }, [HASHMAP_TYPE_SET] = { .head_size = sizeof(Set), .entry_size = sizeof(struct set_entry), .mempool = &hashmap_pool, .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), }, }; static unsigned n_buckets(HashmapBase *h) { return h->has_indirect ? h->indirect.n_buckets : hashmap_type_info[h->type].n_direct_buckets; } static unsigned n_entries(HashmapBase *h) { return h->has_indirect ? h->indirect.n_entries : h->n_direct_entries; } static void n_entries_inc(HashmapBase *h) { if (h->has_indirect) h->indirect.n_entries++; else h->n_direct_entries++; } static void n_entries_dec(HashmapBase *h) { if (h->has_indirect) h->indirect.n_entries--; else h->n_direct_entries--; } static void *storage_ptr(HashmapBase *h) { return h->has_indirect ? h->indirect.storage : h->direct.storage; } static uint8_t *hash_key(HashmapBase *h) { return h->has_indirect ? h->indirect.hash_key : shared_hash_key; } static unsigned base_bucket_hash(HashmapBase *h, const void *p) { struct siphash state; uint64_t hash; siphash24_init(&state, hash_key(h)); h->hash_ops->hash(p, &state); hash = siphash24_finalize(&state); return (unsigned) (hash % n_buckets(h)); } #define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { static uint8_t current[HASH_KEY_SIZE]; static bool current_initialized = false; /* Returns a hash function key to use. In order to keep things * fast we will not generate a new key each time we allocate a * new hash table. Instead, we'll just reuse the most recently * generated one, except if we never generated one or when we * are rehashing an entire hash table because we reached a * fill level */ if (!current_initialized || !reuse_is_ok) { random_bytes(current, sizeof(current)); current_initialized = true; } memcpy(hash_key, current, sizeof(current)); } static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { return (struct hashmap_base_entry*) ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); } static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); } static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); } static struct set_entry *set_bucket_at(Set *h, unsigned idx) { return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); } static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { return &swap->e[idx - _IDX_SWAP_BEGIN]; } /* Returns a pointer to the bucket at index idx. * Understands real indexes and swap indexes, hence "_virtual". */ static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, unsigned idx) { if (idx < _IDX_SWAP_BEGIN) return bucket_at(h, idx); if (idx < _IDX_SWAP_END) return &bucket_at_swap(swap, idx)->p.b; assert_not_reached("Invalid index"); } static dib_raw_t *dib_raw_ptr(HashmapBase *h) { return (dib_raw_t*) ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); } static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { return idx >= from ? idx - from : n_buckets(h) + idx - from; } static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { unsigned initial_bucket; if (raw_dib == DIB_RAW_FREE) return DIB_FREE; if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) return raw_dib; /* * Having an overflow DIB value is very unlikely. The hash function * would have to be bad. For example, in a table of size 2^24 filled * to load factor 0.9 the maximum observed DIB is only about 60. * In theory (assuming I used Maxima correctly), for an infinite size * hash table with load factor 0.8 the probability of a given entry * having DIB > 40 is 1.9e-8. * This returns the correct DIB value by recomputing the hash value in * the unlikely case. XXX Hitting this case could be a hint to rehash. */ initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); return bucket_distance(h, idx, initial_bucket); } static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; } static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { dib_raw_t *dibs; dibs = dib_raw_ptr(h); for ( ; idx < n_buckets(h); idx++) if (dibs[idx] != DIB_RAW_FREE) return idx; return IDX_NIL; } static void bucket_mark_free(HashmapBase *h, unsigned idx) { memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size); bucket_set_dib(h, idx, DIB_FREE); } static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, unsigned from, unsigned to) { struct hashmap_base_entry *e_from, *e_to; assert(from != to); e_from = bucket_at_virtual(h, swap, from); e_to = bucket_at_virtual(h, swap, to); memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); if (h->type == HASHMAP_TYPE_ORDERED) { OrderedHashmap *lh = (OrderedHashmap*) h; struct ordered_hashmap_entry *le, *le_to; le_to = (struct ordered_hashmap_entry*) e_to; if (le_to->iterate_next != IDX_NIL) { le = (struct ordered_hashmap_entry*) bucket_at_virtual(h, swap, le_to->iterate_next); le->iterate_previous = to; } if (le_to->iterate_previous != IDX_NIL) { le = (struct ordered_hashmap_entry*) bucket_at_virtual(h, swap, le_to->iterate_previous); le->iterate_next = to; } if (lh->iterate_list_head == from) lh->iterate_list_head = to; if (lh->iterate_list_tail == from) lh->iterate_list_tail = to; } } static unsigned next_idx(HashmapBase *h, unsigned idx) { return (idx + 1U) % n_buckets(h); } static unsigned prev_idx(HashmapBase *h, unsigned idx) { return (n_buckets(h) + idx - 1U) % n_buckets(h); } static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { switch (h->type) { case HASHMAP_TYPE_PLAIN: case HASHMAP_TYPE_ORDERED: return ((struct plain_hashmap_entry*)e)->value; case HASHMAP_TYPE_SET: return (void*) e->key; default: assert_not_reached("Unknown hashmap type"); } } static void base_remove_entry(HashmapBase *h, unsigned idx) { unsigned left, right, prev, dib; dib_raw_t raw_dib, *dibs; dibs = dib_raw_ptr(h); assert(dibs[idx] != DIB_RAW_FREE); #ifdef ENABLE_DEBUG_HASHMAP h->debug.rem_count++; h->debug.last_rem_idx = idx; #endif left = idx; /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ for (right = next_idx(h, left); ; right = next_idx(h, right)) { raw_dib = dibs[right]; if (raw_dib == 0 || raw_dib == DIB_RAW_FREE) break; /* The buckets are not supposed to be all occupied and with DIB > 0. * That would mean we could make everyone better off by shifting them * backward. This scenario is impossible. */ assert(left != right); } if (h->type == HASHMAP_TYPE_ORDERED) { OrderedHashmap *lh = (OrderedHashmap*) h; struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); if (le->iterate_next != IDX_NIL) ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; else lh->iterate_list_tail = le->iterate_previous; if (le->iterate_previous != IDX_NIL) ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; else lh->iterate_list_head = le->iterate_next; } /* Now shift all buckets in the interval (left, right) one step backwards */ for (prev = left, left = next_idx(h, left); left != right; prev = left, left = next_idx(h, left)) { dib = bucket_calculate_dib(h, left, dibs[left]); assert(dib != 0); bucket_move_entry(h, NULL, left, prev); bucket_set_dib(h, prev, dib - 1); } bucket_mark_free(h, prev); n_entries_dec(h); } #define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { struct ordered_hashmap_entry *e; unsigned idx; assert(h); assert(i); if (i->idx == IDX_NIL) goto at_end; if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) goto at_end; if (i->idx == IDX_FIRST) { idx = h->iterate_list_head; e = ordered_bucket_at(h, idx); } else { idx = i->idx; e = ordered_bucket_at(h, idx); /* * We allow removing the current entry while iterating, but removal may cause * a backward shift. The next entry may thus move one bucket to the left. * To detect when it happens, we remember the key pointer of the entry we were * going to iterate next. If it does not match, there was a backward shift. */ if (e->p.b.key != i->next_key) { idx = prev_idx(HASHMAP_BASE(h), idx); e = ordered_bucket_at(h, idx); } assert(e->p.b.key == i->next_key); } #ifdef ENABLE_DEBUG_HASHMAP i->prev_idx = idx; #endif if (e->iterate_next != IDX_NIL) { struct ordered_hashmap_entry *n; i->idx = e->iterate_next; n = ordered_bucket_at(h, i->idx); i->next_key = n->p.b.key; } else i->idx = IDX_NIL; return idx; at_end: i->idx = IDX_NIL; return IDX_NIL; } static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { unsigned idx; assert(h); assert(i); if (i->idx == IDX_NIL) goto at_end; if (i->idx == IDX_FIRST) { /* fast forward to the first occupied bucket */ if (h->has_indirect) { i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); h->indirect.idx_lowest_entry = i->idx; } else i->idx = skip_free_buckets(h, 0); if (i->idx == IDX_NIL) goto at_end; } else { struct hashmap_base_entry *e; assert(i->idx > 0); e = bucket_at(h, i->idx); /* * We allow removing the current entry while iterating, but removal may cause * a backward shift. The next entry may thus move one bucket to the left. * To detect when it happens, we remember the key pointer of the entry we were * going to iterate next. If it does not match, there was a backward shift. */ if (e->key != i->next_key) e = bucket_at(h, --i->idx); assert(e->key == i->next_key); } idx = i->idx; #ifdef ENABLE_DEBUG_HASHMAP i->prev_idx = idx; #endif i->idx = skip_free_buckets(h, i->idx + 1); if (i->idx != IDX_NIL) i->next_key = bucket_at(h, i->idx)->key; else i->idx = IDX_NIL; return idx; at_end: i->idx = IDX_NIL; return IDX_NIL; } static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { if (!h) { i->idx = IDX_NIL; return IDX_NIL; } #ifdef ENABLE_DEBUG_HASHMAP if (i->idx == IDX_FIRST) { i->put_count = h->debug.put_count; i->rem_count = h->debug.rem_count; } else { /* While iterating, must not add any new entries */ assert(i->put_count == h->debug.put_count); /* ... or remove entries other than the current one */ assert(i->rem_count == h->debug.rem_count || (i->rem_count == h->debug.rem_count - 1 && i->prev_idx == h->debug.last_rem_idx)); /* Reset our removals counter */ i->rem_count = h->debug.rem_count; } #endif return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) : hashmap_iterate_in_internal_order(h, i); } bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { struct hashmap_base_entry *e; void *data; unsigned idx; idx = hashmap_iterate_entry(h, i); if (idx == IDX_NIL) { if (value) *value = NULL; if (key) *key = NULL; return false; } e = bucket_at(h, idx); data = entry_value(h, e); if (value) *value = data; if (key) *key = e->key; return true; } bool set_iterate(Set *s, Iterator *i, void **value) { return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL); } #define HASHMAP_FOREACH_IDX(idx, h, i) \ for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ (idx != IDX_NIL); \ (idx) = hashmap_iterate_entry((h), &(i))) static void reset_direct_storage(HashmapBase *h) { const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; void *p; assert(!h->has_indirect); p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); } static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *h; const struct hashmap_type_info *hi = &hashmap_type_info[type]; bool use_pool; use_pool = is_main_thread(); h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); if (!h) return NULL; h->type = type; h->from_pool = use_pool; h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; if (type == HASHMAP_TYPE_ORDERED) { OrderedHashmap *lh = (OrderedHashmap*)h; lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; } reset_direct_storage(h); if (!shared_hash_key_initialized) { random_bytes(shared_hash_key, sizeof(shared_hash_key)); shared_hash_key_initialized= true; } #ifdef ENABLE_DEBUG_HASHMAP h->debug.func = func; h->debug.file = file; h->debug.line = line; assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); #endif return h; } Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *q; assert(h); if (*h) return 0; q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); if (!q) return -ENOMEM; *h = q; return 0; } int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } static void hashmap_free_no_clear(HashmapBase *h) { assert(!h->has_indirect); assert(!h->n_direct_entries); #ifdef ENABLE_DEBUG_HASHMAP assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); #endif if (h->from_pool) mempool_free_tile(hashmap_type_info[h->type].mempool, h); else free(h); } HashmapBase *internal_hashmap_free(HashmapBase *h) { /* Free the hashmap, but nothing in it */ if (h) { internal_hashmap_clear(h); hashmap_free_no_clear(h); } return NULL; } HashmapBase *internal_hashmap_free_free(HashmapBase *h) { /* Free the hashmap and all data objects in it, but not the * keys */ if (h) { internal_hashmap_clear_free(h); hashmap_free_no_clear(h); } return NULL; } Hashmap *hashmap_free_free_free(Hashmap *h) { /* Free the hashmap and all data and key objects in it */ if (h) { hashmap_clear_free_free(h); hashmap_free_no_clear(HASHMAP_BASE(h)); } return NULL; } void internal_hashmap_clear(HashmapBase *h) { if (!h) return; if (h->has_indirect) { free(h->indirect.storage); h->has_indirect = false; } h->n_direct_entries = 0; reset_direct_storage(h); if (h->type == HASHMAP_TYPE_ORDERED) { OrderedHashmap *lh = (OrderedHashmap*) h; lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; } } void internal_hashmap_clear_free(HashmapBase *h) { unsigned idx; if (!h) return; for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; idx = skip_free_buckets(h, idx + 1)) free(entry_value(h, bucket_at(h, idx))); internal_hashmap_clear(h); } void hashmap_clear_free_free(Hashmap *h) { unsigned idx; if (!h) return; for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { struct plain_hashmap_entry *e = plain_bucket_at(h, idx); free((void*)e->b.key); free(e->value); } internal_hashmap_clear(HASHMAP_BASE(h)); } static int resize_buckets(HashmapBase *h, unsigned entries_add); /* * Finds an empty bucket to put an entry into, starting the scan at 'idx'. * Performs Robin Hood swaps as it goes. The entry to put must be placed * by the caller into swap slot IDX_PUT. * If used for in-place resizing, may leave a displaced entry in swap slot * IDX_PUT. Caller must rehash it next. * Returns: true if it left a displaced entry to rehash next in IDX_PUT, * false otherwise. */ static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, struct swap_entries *swap) { dib_raw_t raw_dib, *dibs; unsigned dib, distance; #ifdef ENABLE_DEBUG_HASHMAP h->debug.put_count++; #endif dibs = dib_raw_ptr(h); for (distance = 0; ; distance++) { raw_dib = dibs[idx]; if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) { if (raw_dib == DIB_RAW_REHASH) bucket_move_entry(h, swap, idx, IDX_TMP); if (h->has_indirect && h->indirect.idx_lowest_entry > idx) h->indirect.idx_lowest_entry = idx; bucket_set_dib(h, idx, distance); bucket_move_entry(h, swap, IDX_PUT, idx); if (raw_dib == DIB_RAW_REHASH) { bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); return true; } return false; } dib = bucket_calculate_dib(h, idx, raw_dib); if (dib < distance) { /* Found a wealthier entry. Go Robin Hood! */ bucket_set_dib(h, idx, distance); /* swap the entries */ bucket_move_entry(h, swap, idx, IDX_TMP); bucket_move_entry(h, swap, IDX_PUT, idx); bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); distance = dib; } idx = next_idx(h, idx); } } /* * Puts an entry into a hashmap, boldly - no check whether key already exists. * The caller must place the entry (only its key and value, not link indexes) * in swap slot IDX_PUT. * Caller must ensure: the key does not exist yet in the hashmap. * that resize is not needed if !may_resize. * Returns: 1 if entry was put successfully. * -ENOMEM if may_resize==true and resize failed with -ENOMEM. * Cannot return -ENOMEM if !may_resize. */ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, struct swap_entries *swap, bool may_resize) { struct ordered_hashmap_entry *new_entry; int r; assert(idx < n_buckets(h)); new_entry = bucket_at_swap(swap, IDX_PUT); if (may_resize) { r = resize_buckets(h, 1); if (r < 0) return r; if (r > 0) idx = bucket_hash(h, new_entry->p.b.key); } assert(n_entries(h) < n_buckets(h)); if (h->type == HASHMAP_TYPE_ORDERED) { OrderedHashmap *lh = (OrderedHashmap*) h; new_entry->iterate_next = IDX_NIL; new_entry->iterate_previous = lh->iterate_list_tail; if (lh->iterate_list_tail != IDX_NIL) { struct ordered_hashmap_entry *old_tail; old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); assert(old_tail->iterate_next == IDX_NIL); old_tail->iterate_next = IDX_PUT; } lh->iterate_list_tail = IDX_PUT; if (lh->iterate_list_head == IDX_NIL) lh->iterate_list_head = IDX_PUT; } assert_se(hashmap_put_robin_hood(h, idx, swap) == false); n_entries_inc(h); #ifdef ENABLE_DEBUG_HASHMAP h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); #endif return 1; } #define hashmap_put_boldly(h, idx, swap, may_resize) \ hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) /* * Returns 0 if resize is not needed. * 1 if successfully resized. * -ENOMEM on allocation failure. */ static int resize_buckets(HashmapBase *h, unsigned entries_add) { struct swap_entries swap; void *new_storage; dib_raw_t *old_dibs, *new_dibs; const struct hashmap_type_info *hi; unsigned idx, optimal_idx; unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; uint8_t new_shift; bool rehash_next; assert(h); hi = &hashmap_type_info[h->type]; new_n_entries = n_entries(h) + entries_add; /* overflow? */ if (_unlikely_(new_n_entries < entries_add)) return -ENOMEM; /* For direct storage we allow 100% load, because it's tiny. */ if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) return 0; /* * Load factor = n/m = 1 - (1/INV_KEEP_FREE). * From it follows: m = n + n/(INV_KEEP_FREE - 1) */ new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); /* overflow? */ if (_unlikely_(new_n_buckets < new_n_entries)) return -ENOMEM; if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) return -ENOMEM; old_n_buckets = n_buckets(h); if (_likely_(new_n_buckets <= old_n_buckets)) return 0; new_shift = log2u_round_up(MAX( new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), 2 * sizeof(struct direct_storage))); /* Realloc storage (buckets and DIB array). */ new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, 1U << new_shift); if (!new_storage) return -ENOMEM; /* Must upgrade direct to indirect storage. */ if (!h->has_indirect) { memcpy(new_storage, h->direct.storage, old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); h->indirect.n_entries = h->n_direct_entries; h->indirect.idx_lowest_entry = 0; h->n_direct_entries = 0; } /* Get a new hash key. If we've just upgraded to indirect storage, * allow reusing a previously generated key. It's still a different key * from the shared one that we used for direct storage. */ get_hash_key(h->indirect.hash_key, !h->has_indirect); h->has_indirect = true; h->indirect.storage = new_storage; h->indirect.n_buckets = (1U << new_shift) / (hi->entry_size + sizeof(dib_raw_t)); old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); new_dibs = dib_raw_ptr(h); /* * Move the DIB array to the new place, replacing valid DIB values with * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. * Note: Overlap is not possible, because we have at least doubled the * number of buckets and dib_raw_t is smaller than any entry type. */ for (idx = 0; idx < old_n_buckets; idx++) { assert(old_dibs[idx] != DIB_RAW_REHASH); new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE : DIB_RAW_REHASH; } /* Zero the area of newly added entries (including the old DIB area) */ memzero(bucket_at(h, old_n_buckets), (n_buckets(h) - old_n_buckets) * hi->entry_size); /* The upper half of the new DIB array needs initialization */ memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); /* Rehash entries that need it */ n_rehashed = 0; for (idx = 0; idx < old_n_buckets; idx++) { if (new_dibs[idx] != DIB_RAW_REHASH) continue; optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); /* * Not much to do if by luck the entry hashes to its current * location. Just set its DIB. */ if (optimal_idx == idx) { new_dibs[idx] = 0; n_rehashed++; continue; } new_dibs[idx] = DIB_RAW_FREE; bucket_move_entry(h, &swap, idx, IDX_PUT); /* bucket_move_entry does not clear the source */ memzero(bucket_at(h, idx), hi->entry_size); do { /* * Find the new bucket for the current entry. This may make * another entry homeless and load it into IDX_PUT. */ rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); n_rehashed++; /* Did the current entry displace another one? */ if (rehash_next) optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); } while (rehash_next); } assert(n_rehashed == n_entries(h)); return 1; } /* * Finds an entry with a matching key * Returns: index of the found entry, or IDX_NIL if not found. */ static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { struct hashmap_base_entry *e; unsigned dib, distance; dib_raw_t *dibs = dib_raw_ptr(h); assert(idx < n_buckets(h)); for (distance = 0; ; distance++) { if (dibs[idx] == DIB_RAW_FREE) return IDX_NIL; dib = bucket_calculate_dib(h, idx, dibs[idx]); if (dib < distance) return IDX_NIL; if (dib == distance) { e = bucket_at(h, idx); if (h->hash_ops->compare(e->key, key) == 0) return idx; } idx = next_idx(h, idx); } } #define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) int hashmap_put(Hashmap *h, const void *key, void *value) { struct swap_entries swap; struct plain_hashmap_entry *e; unsigned hash, idx; assert(h); hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx != IDX_NIL) { e = plain_bucket_at(h, idx); if (e->value == value) return 0; return -EEXIST; } e = &bucket_at_swap(&swap, IDX_PUT)->p; e->b.key = key; e->value = value; return hashmap_put_boldly(h, hash, &swap, true); } int set_put(Set *s, const void *key) { struct swap_entries swap; struct hashmap_base_entry *e; unsigned hash, idx; assert(s); hash = bucket_hash(s, key); idx = bucket_scan(s, hash, key); if (idx != IDX_NIL) return 0; e = &bucket_at_swap(&swap, IDX_PUT)->p.b; e->key = key; return hashmap_put_boldly(s, hash, &swap, true); } int hashmap_replace(Hashmap *h, const void *key, void *value) { struct swap_entries swap; struct plain_hashmap_entry *e; unsigned hash, idx; assert(h); hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx != IDX_NIL) { e = plain_bucket_at(h, idx); #ifdef ENABLE_DEBUG_HASHMAP /* Although the key is equal, the key pointer may have changed, * and this would break our assumption for iterating. So count * this operation as incompatible with iteration. */ if (e->b.key != key) { h->b.debug.put_count++; h->b.debug.rem_count++; h->b.debug.last_rem_idx = idx; } #endif e->b.key = key; e->value = value; return 0; } e = &bucket_at_swap(&swap, IDX_PUT)->p; e->b.key = key; e->value = value; return hashmap_put_boldly(h, hash, &swap, true); } int hashmap_update(Hashmap *h, const void *key, void *value) { struct plain_hashmap_entry *e; unsigned hash, idx; assert(h); hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx == IDX_NIL) return -ENOENT; e = plain_bucket_at(h, idx); e->value = value; return 0; } void *internal_hashmap_get(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; if (!h) return NULL; hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx == IDX_NIL) return NULL; e = bucket_at(h, idx); return entry_value(h, e); } void *hashmap_get2(Hashmap *h, const void *key, void **key2) { struct plain_hashmap_entry *e; unsigned hash, idx; if (!h) return NULL; hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx == IDX_NIL) return NULL; e = plain_bucket_at(h, idx); if (key2) *key2 = (void*) e->b.key; return e->value; } bool internal_hashmap_contains(HashmapBase *h, const void *key) { unsigned hash; if (!h) return false; hash = bucket_hash(h, key); return bucket_scan(h, hash, key) != IDX_NIL; } void *internal_hashmap_remove(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; void *data; if (!h) return NULL; hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx == IDX_NIL) return NULL; e = bucket_at(h, idx); data = entry_value(h, e); remove_entry(h, idx); return data; } void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { struct plain_hashmap_entry *e; unsigned hash, idx; void *data; if (!h) { if (rkey) *rkey = NULL; return NULL; } hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx == IDX_NIL) { if (rkey) *rkey = NULL; return NULL; } e = plain_bucket_at(h, idx); data = e->value; if (rkey) *rkey = (void*) e->b.key; remove_entry(h, idx); return data; } int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { struct swap_entries swap; struct plain_hashmap_entry *e; unsigned old_hash, new_hash, idx; if (!h) return -ENOENT; old_hash = bucket_hash(h, old_key); idx = bucket_scan(h, old_hash, old_key); if (idx == IDX_NIL) return -ENOENT; new_hash = bucket_hash(h, new_key); if (bucket_scan(h, new_hash, new_key) != IDX_NIL) return -EEXIST; remove_entry(h, idx); e = &bucket_at_swap(&swap, IDX_PUT)->p; e->b.key = new_key; e->value = value; assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); return 0; } int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { struct swap_entries swap; struct hashmap_base_entry *e; unsigned old_hash, new_hash, idx; if (!s) return -ENOENT; old_hash = bucket_hash(s, old_key); idx = bucket_scan(s, old_hash, old_key); if (idx == IDX_NIL) return -ENOENT; new_hash = bucket_hash(s, new_key); if (bucket_scan(s, new_hash, new_key) != IDX_NIL) return -EEXIST; remove_entry(s, idx); e = &bucket_at_swap(&swap, IDX_PUT)->p.b; e->key = new_key; assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); return 0; } int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { struct swap_entries swap; struct plain_hashmap_entry *e; unsigned old_hash, new_hash, idx_old, idx_new; if (!h) return -ENOENT; old_hash = bucket_hash(h, old_key); idx_old = bucket_scan(h, old_hash, old_key); if (idx_old == IDX_NIL) return -ENOENT; old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; new_hash = bucket_hash(h, new_key); idx_new = bucket_scan(h, new_hash, new_key); if (idx_new != IDX_NIL) if (idx_old != idx_new) { remove_entry(h, idx_new); /* Compensate for a possible backward shift. */ if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) idx_old = prev_idx(HASHMAP_BASE(h), idx_old); assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); } remove_entry(h, idx_old); e = &bucket_at_swap(&swap, IDX_PUT)->p; e->b.key = new_key; e->value = value; assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); return 0; } void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { struct plain_hashmap_entry *e; unsigned hash, idx; if (!h) return NULL; hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx == IDX_NIL) return NULL; e = plain_bucket_at(h, idx); if (e->value != value) return NULL; remove_entry(h, idx); return value; } static unsigned find_first_entry(HashmapBase *h) { Iterator i = ITERATOR_FIRST; if (!h || !n_entries(h)) return IDX_NIL; return hashmap_iterate_entry(h, &i); } void *internal_hashmap_first(HashmapBase *h) { unsigned idx; idx = find_first_entry(h); if (idx == IDX_NIL) return NULL; return entry_value(h, bucket_at(h, idx)); } void *internal_hashmap_first_key(HashmapBase *h) { struct hashmap_base_entry *e; unsigned idx; idx = find_first_entry(h); if (idx == IDX_NIL) return NULL; e = bucket_at(h, idx); return (void*) e->key; } void *internal_hashmap_steal_first(HashmapBase *h) { struct hashmap_base_entry *e; void *data; unsigned idx; idx = find_first_entry(h); if (idx == IDX_NIL) return NULL; e = bucket_at(h, idx); data = entry_value(h, e); remove_entry(h, idx); return data; } void *internal_hashmap_steal_first_key(HashmapBase *h) { struct hashmap_base_entry *e; void *key; unsigned idx; idx = find_first_entry(h); if (idx == IDX_NIL) return NULL; e = bucket_at(h, idx); key = (void*) e->key; remove_entry(h, idx); return key; } unsigned internal_hashmap_size(HashmapBase *h) { if (!h) return 0; return n_entries(h); } unsigned internal_hashmap_buckets(HashmapBase *h) { if (!h) return 0; return n_buckets(h); } int internal_hashmap_merge(Hashmap *h, Hashmap *other) { Iterator i; unsigned idx; assert(h); HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); int r; r = hashmap_put(h, pe->b.key, pe->value); if (r < 0 && r != -EEXIST) return r; } return 0; } int set_merge(Set *s, Set *other) { Iterator i; unsigned idx; assert(s); HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { struct set_entry *se = set_bucket_at(other, idx); int r; r = set_put(s, se->b.key); if (r < 0) return r; } return 0; } int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { int r; assert(h); r = resize_buckets(h, entries_add); if (r < 0) return r; return 0; } /* * The same as hashmap_merge(), but every new item from other is moved to h. * Keys already in h are skipped and stay in other. * Returns: 0 on success. * -ENOMEM on alloc failure, in which case no move has been done. */ int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { struct swap_entries swap; struct hashmap_base_entry *e, *n; Iterator i; unsigned idx; int r; assert(h); if (!other) return 0; assert(other->type == h->type); /* * This reserves buckets for the worst case, where none of other's * entries are yet present in h. This is preferable to risking * an allocation failure in the middle of the moving and having to * rollback or return a partial result. */ r = resize_buckets(h, n_entries(other)); if (r < 0) return r; HASHMAP_FOREACH_IDX(idx, other, i) { unsigned h_hash; e = bucket_at(other, idx); h_hash = bucket_hash(h, e->key); if (bucket_scan(h, h_hash, e->key) != IDX_NIL) continue; n = &bucket_at_swap(&swap, IDX_PUT)->p.b; n->key = e->key; if (h->type != HASHMAP_TYPE_SET) ((struct plain_hashmap_entry*) n)->value = ((struct plain_hashmap_entry*) e)->value; assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); remove_entry(other, idx); } return 0; } int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { struct swap_entries swap; unsigned h_hash, other_hash, idx; struct hashmap_base_entry *e, *n; int r; assert(h); h_hash = bucket_hash(h, key); if (bucket_scan(h, h_hash, key) != IDX_NIL) return -EEXIST; if (!other) return -ENOENT; assert(other->type == h->type); other_hash = bucket_hash(other, key); idx = bucket_scan(other, other_hash, key); if (idx == IDX_NIL) return -ENOENT; e = bucket_at(other, idx); n = &bucket_at_swap(&swap, IDX_PUT)->p.b; n->key = e->key; if (h->type != HASHMAP_TYPE_SET) ((struct plain_hashmap_entry*) n)->value = ((struct plain_hashmap_entry*) e)->value; r = hashmap_put_boldly(h, h_hash, &swap, true); if (r < 0) return r; remove_entry(other, idx); return 0; } HashmapBase *internal_hashmap_copy(HashmapBase *h) { HashmapBase *copy; int r; assert(h); copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); if (!copy) return NULL; switch (h->type) { case HASHMAP_TYPE_PLAIN: case HASHMAP_TYPE_ORDERED: r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); break; case HASHMAP_TYPE_SET: r = set_merge((Set*)copy, (Set*)h); break; default: assert_not_reached("Unknown hashmap type"); } if (r < 0) { internal_hashmap_free(copy); return NULL; } return copy; } char **internal_hashmap_get_strv(HashmapBase *h) { char **sv; Iterator i; unsigned idx, n; sv = new(char*, n_entries(h)+1); if (!sv) return NULL; n = 0; HASHMAP_FOREACH_IDX(idx, h, i) sv[n++] = entry_value(h, bucket_at(h, idx)); sv[n] = NULL; return sv; } void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { struct ordered_hashmap_entry *e; unsigned hash, idx; if (!h) return NULL; hash = bucket_hash(h, key); idx = bucket_scan(h, hash, key); if (idx == IDX_NIL) return NULL; e = ordered_bucket_at(h, idx); if (e->iterate_next == IDX_NIL) return NULL; return ordered_bucket_at(h, e->iterate_next)->p.value; } int set_consume(Set *s, void *value) { int r; assert(s); assert(value); r = set_put(s, value); if (r <= 0) free(value); return r; } int set_put_strdup(Set *s, const char *p) { char *c; assert(s); assert(p); if (set_contains(s, (char*) p)) return 0; c = strdup(p); if (!c) return -ENOMEM; return set_consume(s, c); } int set_put_strdupv(Set *s, char **l) { int n = 0, r; char **i; assert(s); STRV_FOREACH(i, l) { r = set_put_strdup(s, *i); if (r < 0) return r; n += r; } return n; } int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { const char *p = v; int r; assert(s); assert(v); for (;;) { char *word; r = extract_first_word(&p, &word, separators, flags); if (r <= 0) return r; r = set_consume(s, word); if (r < 0) return r; } } systemd-netlogd-1.4.4/src/share/hashmap.h000066400000000000000000000354431474012624500203430ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include "hash-funcs.h" #include "macro.h" #include "util.h" /* * A hash table implementation. As a minor optimization a NULL hashmap object * will be treated as empty hashmap for all read operations. That way it is not * necessary to instantiate an object for each Hashmap use. * * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), * the implementation will: * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) * - perform extra checks for invalid use of iterators */ #define HASH_KEY_SIZE 16 /* The base type for all hashmap and set types. Many functions in the * implementation take (HashmapBase*) parameters and are run-time polymorphic, * though the API is not meant to be polymorphic (do not call functions * internal_*() directly). */ typedef struct HashmapBase HashmapBase; /* Specific hashmap/set types */ typedef struct Hashmap Hashmap; /* Maps keys to values */ typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ typedef struct Set Set; /* Stores just keys */ /* Ideally the Iterator would be an opaque struct, but it is instantiated * by hashmap users, so the definition has to be here. Do not use its fields * directly. */ typedef struct { unsigned idx; /* index of an entry to be iterated next */ const void *next_key; /* expected value of that entry's key pointer */ #ifdef ENABLE_DEBUG_HASHMAP unsigned put_count; /* hashmap's put_count recorded at start of iteration */ unsigned rem_count; /* hashmap's rem_count in previous iteration */ unsigned prev_idx; /* idx in previous iteration */ #endif } Iterator; #define _IDX_ITERATOR_FIRST (UINT_MAX - 1) #define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) /* Macros for type checking */ #define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ __builtin_types_compatible_p(typeof(h), Hashmap*) || \ __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ __builtin_types_compatible_p(typeof(h), Set*)) #define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ #define HASHMAP_BASE(h) \ __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ (HashmapBase*)(h), \ (void)0) #define PLAIN_HASHMAP(h) \ __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ (Hashmap*)(h), \ (void)0) #ifdef ENABLE_DEBUG_HASHMAP # define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line # define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ # define HASHMAP_DEBUG_PASS_ARGS , func, file, line #else # define HASHMAP_DEBUG_PARAMS # define HASHMAP_DEBUG_SRC_ARGS # define HASHMAP_DEBUG_PASS_ARGS #endif Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) HashmapBase *internal_hashmap_free(HashmapBase *h); static inline Hashmap *hashmap_free(Hashmap *h) { return (void*)internal_hashmap_free(HASHMAP_BASE(h)); } static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { return (void*)internal_hashmap_free(HASHMAP_BASE(h)); } HashmapBase *internal_hashmap_free_free(HashmapBase *h); static inline Hashmap *hashmap_free_free(Hashmap *h) { return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); } static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); } Hashmap *hashmap_free_free_free(Hashmap *h); static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); } HashmapBase *internal_hashmap_copy(HashmapBase *h); static inline Hashmap *hashmap_copy(Hashmap *h) { return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); } static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); } int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int hashmap_put(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { return hashmap_put(PLAIN_HASHMAP(h), key, value); } int hashmap_update(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { return hashmap_update(PLAIN_HASHMAP(h), key, value); } int hashmap_replace(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { return hashmap_replace(PLAIN_HASHMAP(h), key, value); } void *internal_hashmap_get(HashmapBase *h, const void *key); static inline void *hashmap_get(Hashmap *h, const void *key) { return internal_hashmap_get(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { return internal_hashmap_get(HASHMAP_BASE(h), key); } void *hashmap_get2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); } bool internal_hashmap_contains(HashmapBase *h, const void *key); static inline bool hashmap_contains(Hashmap *h, const void *key) { return internal_hashmap_contains(HASHMAP_BASE(h), key); } static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { return internal_hashmap_contains(HASHMAP_BASE(h), key); } void *internal_hashmap_remove(HashmapBase *h, const void *key); static inline void *hashmap_remove(Hashmap *h, const void *key) { return internal_hashmap_remove(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { return internal_hashmap_remove(HASHMAP_BASE(h), key); } void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } void *hashmap_remove_value(Hashmap *h, const void *key, void *value); static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); } int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); } int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); } /* Since merging data from a OrderedHashmap into a Hashmap or vice-versa * should just work, allow this by having looser type-checking here. */ int internal_hashmap_merge(Hashmap *h, Hashmap *other); #define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) #define ordered_hashmap_merge(h, other) hashmap_merge(h, other) int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); } int internal_hashmap_move(HashmapBase *h, HashmapBase *other); /* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ static inline int hashmap_move(Hashmap *h, Hashmap *other) { return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } unsigned internal_hashmap_size(HashmapBase *h) _pure_; static inline unsigned hashmap_size(Hashmap *h) { return internal_hashmap_size(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { return internal_hashmap_size(HASHMAP_BASE(h)); } static inline bool hashmap_isempty(Hashmap *h) { return hashmap_size(h) == 0; } static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { return ordered_hashmap_size(h) == 0; } unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; static inline unsigned hashmap_buckets(Hashmap *h) { return internal_hashmap_buckets(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { return internal_hashmap_buckets(HASHMAP_BASE(h)); } bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); } static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); } void internal_hashmap_clear(HashmapBase *h); static inline void hashmap_clear(Hashmap *h) { internal_hashmap_clear(HASHMAP_BASE(h)); } static inline void ordered_hashmap_clear(OrderedHashmap *h) { internal_hashmap_clear(HASHMAP_BASE(h)); } void internal_hashmap_clear_free(HashmapBase *h); static inline void hashmap_clear_free(Hashmap *h) { internal_hashmap_clear_free(HASHMAP_BASE(h)); } static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { internal_hashmap_clear_free(HASHMAP_BASE(h)); } void hashmap_clear_free_free(Hashmap *h); static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { hashmap_clear_free_free(PLAIN_HASHMAP(h)); } /* * Note about all *_first*() functions * * For plain Hashmaps and Sets the order of entries is undefined. * The functions find whatever entry is first in the implementation * internal order. * * Only for OrderedHashmaps the order is well defined and finding * the first entry is O(1). */ void *internal_hashmap_steal_first(HashmapBase *h); static inline void *hashmap_steal_first(Hashmap *h) { return internal_hashmap_steal_first(HASHMAP_BASE(h)); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { return internal_hashmap_steal_first(HASHMAP_BASE(h)); } void *internal_hashmap_steal_first_key(HashmapBase *h); static inline void *hashmap_steal_first_key(Hashmap *h) { return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); } static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); } void *internal_hashmap_first_key(HashmapBase *h) _pure_; static inline void *hashmap_first_key(Hashmap *h) { return internal_hashmap_first_key(HASHMAP_BASE(h)); } static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { return internal_hashmap_first_key(HASHMAP_BASE(h)); } void *internal_hashmap_first(HashmapBase *h) _pure_; static inline void *hashmap_first(Hashmap *h) { return internal_hashmap_first(HASHMAP_BASE(h)); } static inline void *ordered_hashmap_first(OrderedHashmap *h) { return internal_hashmap_first(HASHMAP_BASE(h)); } /* no hashmap_next */ void *ordered_hashmap_next(OrderedHashmap *h, const void *key); char **internal_hashmap_get_strv(HashmapBase *h); static inline char **hashmap_get_strv(Hashmap *h) { return internal_hashmap_get_strv(HASHMAP_BASE(h)); } static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { return internal_hashmap_get_strv(HASHMAP_BASE(h)); } /* * Hashmaps are iterated in unpredictable order. * OrderedHashmaps are an exception to this. They are iterated in the order * the entries were inserted. * It is safe to remove the current entry. */ #define HASHMAP_FOREACH(e, h, i) \ for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); ) #define ORDERED_HASHMAP_FOREACH(e, h, i) \ for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); ) #define HASHMAP_FOREACH_KEY(e, k, h, i) \ for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) #define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); #define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) #define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) #define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) #define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) #define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) #define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) systemd-netlogd-1.4.4/src/share/hexdecoct.c000066400000000000000000000016531474012624500206570ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" #include "util.h" char octchar(int x) { return '0' + (x & 7); } int unoctchar(char c) { if (c >= '0' && c <= '7') return c - '0'; return -EINVAL; } char decchar(int x) { return '0' + (x % 10); } int undecchar(char c) { if (c >= '0' && c <= '9') return c - '0'; return -EINVAL; } char hexchar(int x) { static const char table[16] = "0123456789abcdef"; return table[x & 15]; } int unhexchar(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -EINVAL; } systemd-netlogd-1.4.4/src/share/hexdecoct.h000066400000000000000000000005351474012624500206620ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "macro.h" char octchar(int x) _const_; int unoctchar(char c) _const_; char decchar(int x) _const_; int undecchar(char c) _const_; char hexchar(int x) _const_; int unhexchar(char c) _const_; systemd-netlogd-1.4.4/src/share/hostname-util.c000066400000000000000000000071751474012624500215070ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "alloc-util.h" #include "hostname-util.h" #include "string-util.h" #include "strv.h" bool valid_ldh_char(char c) { /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */ return ascii_isalpha(c) || ascii_isdigit(c) || c == '-'; } bool hostname_is_valid(const char *s, ValidHostnameFlags flags) { unsigned n_dots = 0; const char *p; bool dot, hyphen; /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only * checks if the name is composed of allowed characters and the length is not above the maximum * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note * that due to the restricted charset and length this call is substantially more conservative than * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */ if (isempty(s)) return false; if (streq(s, ".host")) /* Used by the container logic to denote the "root container" */ return FLAGS_SET(flags, VALID_HOSTNAME_DOT_HOST); for (p = s, dot = hyphen = true; *p; p++) if (*p == '.') { if (dot || hyphen) return false; dot = true; hyphen = false; n_dots++; } else if (*p == '-') { if (dot) return false; dot = false; hyphen = true; } else { if (!valid_ldh_char(*p)) return false; dot = false; hyphen = false; } if (dot && (n_dots < 2 || !FLAGS_SET(flags, VALID_HOSTNAME_TRAILING_DOT))) return false; if (hyphen) return false; if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to * 255 characters */ return false; return true; } char* hostname_cleanup(char *s) { char *p, *d; bool dot, hyphen; assert(s); for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++) if (*p == '.') { if (dot || hyphen) continue; *(d++) = '.'; dot = true; hyphen = false; } else if (*p == '-') { if (dot) continue; *(d++) = '-'; dot = false; hyphen = true; } else if (valid_ldh_char(*p)) { *(d++) = *p; dot = false; hyphen = false; } if (d > s && IN_SET(d[-1], '-', '.')) /* The dot can occur at most once, but we might have multiple * hyphens, hence the loop */ d--; *d = 0; return s; } systemd-netlogd-1.4.4/src/share/hostname-util.h000066400000000000000000000030461474012624500215050ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include "macro.h" #include "strv.h" typedef enum GetHostnameFlags { GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 0, /* accepts "localhost" or friends. */ GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */ GET_HOSTNAME_SHORT = 1 << 2, /* kills the FQDN part if present. */ } GetHostnameFlags; int gethostname_full(GetHostnameFlags flags, char **ret); static inline int gethostname_strict(char **ret) { return gethostname_full(0, ret); } static inline char* gethostname_malloc(void) { char *s; if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0) return NULL; return s; } static inline char* gethostname_short_malloc(void) { char *s; if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0) return NULL; return s; } char* get_default_hostname(void); bool valid_ldh_char(char c) _const_; typedef enum ValidHostnameFlags { VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */ VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */ } ValidHostnameFlags; bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_; char* hostname_cleanup(char *s); bool is_localhost(const char *hostname); int get_pretty_hostname(char **ret); systemd-netlogd-1.4.4/src/share/in-addr-util.c000066400000000000000000000262701474012624500212040ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "alloc-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" #include "util.h" bool in4_addr_is_null(const struct in_addr *a) { return a->s_addr == 0; } bool in6_addr_is_null(const struct in6_addr *a) { return a->s6_addr32[0] == 0 && a->s6_addr32[1] == 0 && a->s6_addr32[2] == 0 && a->s6_addr32[3] == 0; } int in_addr_is_null(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) return in4_addr_is_null(&u->in); if (family == AF_INET6) return in6_addr_is_null(&u->in6); return -EAFNOSUPPORT; } int in_addr_is_link_local(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); if (family == AF_INET6) return IN6_IS_ADDR_LINKLOCAL(&u->in6); return -EAFNOSUPPORT; } int in_addr_is_localhost(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) /* All of 127.x.x.x is localhost. */ return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; if (family == AF_INET6) return IN6_IS_ADDR_LOOPBACK(&u->in6); return -EAFNOSUPPORT; } int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { assert(a); assert(b); if (family == AF_INET) return a->in.s_addr == b->in.s_addr; if (family == AF_INET6) return a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; return -EAFNOSUPPORT; } int in_addr_prefix_intersect( int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen) { unsigned m; assert(a); assert(b); /* Checks whether there are any addresses that are in both * networks */ m = MIN(aprefixlen, bprefixlen); if (family == AF_INET) { uint32_t x, nm; x = be32toh(a->in.s_addr ^ b->in.s_addr); nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); return (x & nm) == 0; } if (family == AF_INET6) { unsigned i; if (m > 128) m = 128; for (i = 0; i < 16; i++) { uint8_t x, nm; x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; if (m < 8) nm = 0xFF << (8 - m); else nm = 0xFF; if ((x & nm) != 0) return 0; if (m > 8) m -= 8; else m = 0; } return 1; } return -EAFNOSUPPORT; } int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { assert(u); /* Increases the network part of an address by one. Returns * positive it that succeeds, or 0 if this overflows. */ if (prefixlen <= 0) return 0; if (family == AF_INET) { uint32_t c, n; if (prefixlen > 32) prefixlen = 32; c = be32toh(u->in.s_addr); n = c + (1UL << (32 - prefixlen)); if (n < c) return 0; n &= 0xFFFFFFFFUL << (32 - prefixlen); u->in.s_addr = htobe32(n); return 1; } if (family == AF_INET6) { struct in6_addr add = {}, result; uint8_t overflow = 0; unsigned i; if (prefixlen > 128) prefixlen = 128; /* First calculate what we have to add */ add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); for (i = 16; i > 0; i--) { unsigned j = i - 1; result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); } if (overflow) return 0; u->in6 = result; return 1; } return -EAFNOSUPPORT; } int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { char *x; size_t l; assert(u); assert(ret); if (family == AF_INET) l = INET_ADDRSTRLEN; else if (family == AF_INET6) l = INET6_ADDRSTRLEN; else return -EAFNOSUPPORT; x = new(char, l); if (!x) return -ENOMEM; errno = 0; if (!inet_ntop(family, u, x, l)) { free(x); return errno > 0 ? -errno : -EINVAL; } *ret = x; return 0; } int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { size_t l; char *x; int r; assert(u); assert(ret); /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly * handle IPv6 link-local addresses. */ if (family != AF_INET6) goto fallback; if (ifindex <= 0) goto fallback; r = in_addr_is_link_local(family, u); if (r < 0) return r; if (r == 0) goto fallback; l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; x = new(char, l); if (!x) return -ENOMEM; errno = 0; if (!inet_ntop(family, u, x, l)) { free(x); return errno > 0 ? -errno : -EINVAL; } sprintf(strchr(x, 0), "%%%i", ifindex); *ret = x; return 0; fallback: return in_addr_to_string(family, u, ret); } int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { assert(s); assert(ret); if (!IN_SET(family, AF_INET, AF_INET6)) return -EAFNOSUPPORT; errno = 0; if (inet_pton(family, s, ret) <= 0) return errno > 0 ? -errno : -EINVAL; return 0; } int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) { int r; assert(s); assert(family); assert(ret); r = in_addr_from_string(AF_INET, s, ret); if (r >= 0) { *family = AF_INET; return 0; } r = in_addr_from_string(AF_INET6, s, ret); if (r >= 0) { *family = AF_INET6; return 0; } return -EINVAL; } int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { const char *suffix; int r, ifi = 0; assert(s); assert(family); assert(ret); /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") * if one is found. */ suffix = strchr(s, '%'); if (suffix) { if (ifindex) { /* If we shall return the interface index, try to parse it */ r = parse_ifindex(suffix + 1, &ifi); if (r < 0) { unsigned u; u = if_nametoindex(suffix + 1); if (u <= 0) return -errno; ifi = (int) u; } } s = strndupa(s, suffix - s); } r = in_addr_from_string_auto(s, family, ret); if (r < 0) return r; if (ifindex) *ifindex = ifi; return r; } unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); return 32 - u32ctz(be32toh(addr->s_addr)); } struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { assert(addr); assert(prefixlen <= 32); /* Shifting beyond 32 is not defined, handle this specially. */ if (prefixlen == 0) addr->s_addr = 0; else addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); return addr; } int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { uint8_t msb_octet = *(uint8_t*) addr; /* addr may not be aligned, so make sure we only access it byte-wise */ assert(addr); assert(prefixlen); if (msb_octet < 128) /* class A, leading bits: 0 */ *prefixlen = 8; else if (msb_octet < 192) /* class B, leading bits 10 */ *prefixlen = 16; else if (msb_octet < 224) /* class C, leading bits 110 */ *prefixlen = 24; else /* class D or E, no default prefixlen */ return -ERANGE; return 0; } int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { unsigned char prefixlen; int r; assert(addr); assert(mask); r = in_addr_default_prefixlen(addr, &prefixlen); if (r < 0) return r; in_addr_prefixlen_to_netmask(mask, prefixlen); return 0; } int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { assert(addr); if (family == AF_INET) { struct in_addr mask; if (!in_addr_prefixlen_to_netmask(&mask, prefixlen)) return -EINVAL; addr->in.s_addr &= mask.s_addr; return 0; } if (family == AF_INET6) { unsigned i; for (i = 0; i < 16; i++) { uint8_t mask; if (prefixlen >= 8) { mask = 0xFF; prefixlen -= 8; } else { mask = 0xFF << (8 - prefixlen); prefixlen = 0; } addr->in6.s6_addr[i] &= mask; } return 0; } return -EAFNOSUPPORT; } systemd-netlogd-1.4.4/src/share/in-addr-util.h000066400000000000000000000050471474012624500212100ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include "macro.h" #include "util.h" union in_addr_union { struct in_addr in; struct in6_addr in6; }; struct in_addr_data { int family; union in_addr_union address; }; bool in4_addr_is_null(const struct in_addr *a); bool in6_addr_is_null(const struct in6_addr *a); int in_addr_is_null(int family, const union in_addr_union *u); int in_addr_is_link_local(int family, const union in_addr_union *u); int in_addr_is_localhost(int family, const union in_addr_union *u); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); static inline size_t FAMILY_ADDRESS_SIZE(int family) { assert(family == AF_INET || family == AF_INET6); return family == AF_INET6 ? 16 : 4; } #define IN_ADDR_NULL ((union in_addr_union) {}) /* Note: the lifetime of the compound literal is the immediately surrounding block, * see C11 §6.5.2.5, and * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */ #define IN_ADDR_MAX CONST_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) #define IN_ADDR_TO_STRING(family, addr) typesafe_inet_ntop(family, addr, (char[IN_ADDR_MAX]){}, IN_ADDR_MAX) #define IN4_ADDR_TO_STRING(addr) typesafe_inet_ntop4(addr, (char[INET_ADDRSTRLEN]){}, INET_ADDRSTRLEN) #define IN6_ADDR_TO_STRING(addr) typesafe_inet_ntop6(addr, (char[INET6_ADDRSTRLEN]){}, INET6_ADDRSTRLEN) systemd-netlogd-1.4.4/src/share/io-util.c000066400000000000000000000042101474012624500202630ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "io-util.h" #include "time-util.h" ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { uint8_t *p = buf; ssize_t n = 0; assert(fd >= 0); assert(buf); /* If called with nbytes == 0, let's call read() at least * once, to validate the operation */ if (nbytes > (size_t) SSIZE_MAX) return -EINVAL; do { ssize_t k; k = read(fd, p, nbytes); if (k < 0) { if (errno == EINTR) continue; if (errno == EAGAIN && do_poll) { /* We knowingly ignore any return value here, * and expect that any error/EOF is reported * via read() */ (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY); continue; } return n > 0 ? n : -errno; } if (k == 0) return n; assert((size_t) k <= nbytes); p += k; nbytes -= k; n += k; } while (nbytes > 0); return n; } int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { ssize_t n; n = loop_read(fd, buf, nbytes, do_poll); if (n < 0) return (int) n; if ((size_t) n != nbytes) return -EIO; return 0; } int fd_wait_for_event(int fd, int event, usec_t t) { struct pollfd pollfd = { .fd = fd, .events = event, }; struct timespec ts; int r; r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL); if (r < 0) return -errno; if (r == 0) return 0; return pollfd.revents; } systemd-netlogd-1.4.4/src/share/io-util.h000066400000000000000000000036321474012624500202770ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include "macro.h" #include "time-util.h" ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll); int fd_wait_for_event(int fd, int event, usec_t timeout); #define IOVEC_SET_STRING(i, s) \ do { \ struct iovec *_i = &(i); \ char *_s = (char *)(s); \ _i->iov_base = _s; \ _i->iov_len = strlen(_s); \ } while (false) static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { unsigned j; size_t r = 0; for (j = 0; j < n; j++) r += i[j].iov_len; return r; } static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { unsigned j; for (j = 0; j < n; j++) { size_t sub; if (_unlikely_(k <= 0)) break; sub = MIN(i[j].iov_len, k); i[j].iov_len -= sub; i[j].iov_base = (uint8_t*) i[j].iov_base + sub; k -= sub; } return k; } static inline bool FILE_SIZE_VALID(uint64_t l) { /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */ return (l >> 63) == 0; } static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { /* Same as above, but allows one extra value: -1 as indication for infinity. */ if (l == (uint64_t) -1) return true; return FILE_SIZE_VALID(l); } systemd-netlogd-1.4.4/src/share/ioprio.h000066400000000000000000000025721474012624500202200ustar00rootroot00000000000000#ifndef IOPRIO_H #define IOPRIO_H #include #include /* * Gives us 8 prio classes with 13-bits of data for each class */ #define IOPRIO_BITS (16) #define IOPRIO_CLASS_SHIFT (13) #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) #define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE) /* * These are the io priority groups as implemented by CFQ. RT is the realtime * class, it always gets premium service. BE is the best-effort scheduling * class, the default for any process. IDLE is the idle scheduling class, it * is only served when no one else is using the disk. */ enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE, }; /* * 8 best effort priority levels are supported */ #define IOPRIO_BE_NR (8) enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, }; static inline int ioprio_set(int which, int who, int ioprio) { return syscall(__NR_ioprio_set, which, who, ioprio); } static inline int ioprio_get(int which, int who) { return syscall(__NR_ioprio_get, which, who); } #endif systemd-netlogd-1.4.4/src/share/iovec-util.c000066400000000000000000000024741474012624500207730ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "iovec-util.h" #include "string-util.h" size_t iovec_total_size(const struct iovec *iovec, size_t n) { size_t sum = 0; assert(iovec || n == 0); FOREACH_ARRAY(j, iovec, n) sum += j->iov_len; return sum; } bool iovec_increment(struct iovec *iovec, size_t n, size_t k) { assert(iovec || n == 0); /* Returns true if there is nothing else to send (bytes written cover all of the iovec), * false if there's still work to do. */ FOREACH_ARRAY(j, iovec, n) { size_t sub; if (j->iov_len == 0) continue; if (k == 0) return false; sub = MIN(j->iov_len, k); j->iov_len -= sub; j->iov_base = (uint8_t*) j->iov_base + sub; k -= sub; } assert(k == 0); /* Anything else would mean that we wrote more bytes than available, * or the kernel reported writing more bytes than sent. */ return true; } void iovec_array_free(struct iovec *iovec, size_t n_iovec) { assert(iovec || n_iovec == 0); FOREACH_ARRAY(i, iovec, n_iovec) free(i->iov_base); free(iovec); } systemd-netlogd-1.4.4/src/share/iovec-util.h000066400000000000000000000045611474012624500207770ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include "alloc-util.h" #include "macro.h" /* An iovec pointing to a single NUL byte */ #define IOVEC_NUL_BYTE (const struct iovec) { \ .iov_base = (void*) (const uint8_t[1]) { 0 }, \ .iov_len = 1, \ } size_t iovec_total_size(const struct iovec *iovec, size_t n); bool iovec_increment(struct iovec *iovec, size_t n, size_t k); /* This accepts both const and non-const pointers */ #define IOVEC_MAKE(base, len) \ (struct iovec) { \ .iov_base = (void*) (base), \ .iov_len = (len), \ } static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) { assert(iovec); /* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */ *iovec = IOVEC_MAKE(s, s ? strlen(s) : 0); return iovec; } #define IOVEC_MAKE_STRING(s) \ *iovec_make_string(&(struct iovec) {}, s) #define CONST_IOVEC_MAKE_STRING(s) \ (const struct iovec) { \ .iov_base = (char*) s, \ .iov_len = STRLEN(s), \ } static inline void iovec_done(struct iovec *iovec) { /* A _cleanup_() helper that frees the iov_base in the iovec */ assert(iovec); iovec->iov_base = mfree(iovec->iov_base); iovec->iov_len = 0; } static inline bool iovec_is_set(const struct iovec *iovec) { /* Checks if the iovec points to a non-empty chunk of memory */ return iovec && iovec->iov_len > 0 && iovec->iov_base; } static inline bool iovec_is_valid(const struct iovec *iovec) { /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */ return !iovec || (iovec->iov_base || iovec->iov_len == 0); } char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value); void iovec_array_free(struct iovec *iovec, size_t n_iovec); systemd-netlogd-1.4.4/src/share/list.h000066400000000000000000000230451474012624500176700ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once /* The head of the linked list. Use this in the structure that shall * contain the head of the linked list */ #define LIST_HEAD(t,name) \ t *name /* The pointers in the linked list's items. Use this in the item structure */ #define LIST_FIELDS(t,name) \ t *name##_next, *name##_prev /* Initialize the list's head */ #define LIST_HEAD_INIT(head) \ do { \ (head) = NULL; } \ while (false) /* Initialize a list item */ #define LIST_INIT(name,item) \ do { \ typeof(*(item)) *_item = (item); \ assert(_item); \ _item->name##_prev = _item->name##_next = NULL; \ } while (false) /* Prepend an item to the list */ #define LIST_PREPEND(name,head,item) \ do { \ typeof(*(head)) **_head = &(head), *_item = (item); \ assert(_item); \ if ((_item->name##_next = *_head)) \ _item->name##_next->name##_prev = _item; \ _item->name##_prev = NULL; \ *_head = _item; \ } while (false) /* Append an item to the list */ #define LIST_APPEND(name,head,item) \ do { \ typeof(*(head)) *_tail; \ LIST_FIND_TAIL(name,head,_tail); \ LIST_INSERT_AFTER(name,head,_tail,item); \ } while (false) /* Remove an item from the list */ #define LIST_REMOVE(name,head,item) \ do { \ typeof(*(head)) **_head = &(head), *_item = (item); \ assert(_item); \ if (_item->name##_next) \ _item->name##_next->name##_prev = _item->name##_prev; \ if (_item->name##_prev) \ _item->name##_prev->name##_next = _item->name##_next; \ else { \ assert(*_head == _item); \ *_head = _item->name##_next; \ } \ _item->name##_next = _item->name##_prev = NULL; \ } while (false) /* Find the head of the list */ #define LIST_FIND_HEAD(name,item,head) \ do { \ typeof(*(item)) *_item = (item); \ if (!_item) \ (head) = NULL; \ else { \ while (_item->name##_prev) \ _item = _item->name##_prev; \ (head) = _item; \ } \ } while (false) /* Find the tail of the list */ #define LIST_FIND_TAIL(name,item,tail) \ do { \ typeof(*(item)) *_item = (item); \ if (!_item) \ (tail) = NULL; \ else { \ while (_item->name##_next) \ _item = _item->name##_next; \ (tail) = _item; \ } \ } while (false) /* Insert an item after another one (a = where, b = what) */ #define LIST_INSERT_AFTER(name,head,a,b) \ do { \ typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ assert(_b); \ if (!_a) { \ if ((_b->name##_next = *_head)) \ _b->name##_next->name##_prev = _b; \ _b->name##_prev = NULL; \ *_head = _b; \ } else { \ if ((_b->name##_next = _a->name##_next)) \ _b->name##_next->name##_prev = _b; \ _b->name##_prev = _a; \ _a->name##_next = _b; \ } \ } while (false) /* Insert an item before another one (a = where, b = what) */ #define LIST_INSERT_BEFORE(name,head,a,b) \ do { \ typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ assert(_b); \ if (!_a) { \ if (!*_head) { \ _b->name##_next = NULL; \ _b->name##_prev = NULL; \ *_head = _b; \ } else { \ typeof(*(head)) *_tail = (head); \ while (_tail->name##_next) \ _tail = _tail->name##_next; \ _b->name##_next = NULL; \ _b->name##_prev = _tail; \ _tail->name##_next = _b; \ } \ } else { \ if ((_b->name##_prev = _a->name##_prev)) \ _b->name##_prev->name##_next = _b; \ _b->name##_next = _a; \ _a->name##_prev = _b; \ } \ } while (false) #define LIST_JUST_US(name,item) \ (!(item)->name##_prev && !(item)->name##_next) \ #define LIST_FOREACH(name,i,head) \ for ((i) = (head); (i); (i) = (i)->name##_next) #define LIST_FOREACH_SAFE(name,i,n,head) \ for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) #define LIST_FOREACH_BEFORE(name,i,p) \ for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) #define LIST_FOREACH_AFTER(name,i,p) \ for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) /* Iterate through all the members of the list p is included in, but skip over p */ #define LIST_FOREACH_OTHERS(name,i,p) \ for (({ \ (i) = (p); \ while ((i) && (i)->name##_prev) \ (i) = (i)->name##_prev; \ if ((i) == (p)) \ (i) = (p)->name##_next; \ }); \ (i); \ (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) /* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */ #define LIST_LOOP_BUT_ONE(name,i,head,p) \ for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ (i) != (p); \ (i) = (i)->name##_next ? (i)->name##_next : (head)) systemd-netlogd-1.4.4/src/share/log.c000066400000000000000000001010161474012624500174640ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "formats-util.h" #include "io-util.h" #include "log.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "proc-cmdline.h" #include "process-util.h" #include "signal-util.h" #include "socket-util.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "syslog-util.h" #include "terminal-util.h" #include "time-util.h" #include "util.h" #define SNDBUF_SIZE (8*1024*1024) static LogTarget log_target = LOG_TARGET_CONSOLE; static int log_max_level = LOG_INFO; static int log_facility = LOG_DAEMON; static int console_fd = STDERR_FILENO; static int syslog_fd = -1; static int kmsg_fd = -1; static int journal_fd = -1; static bool syslog_is_stream = false; static bool show_color = false; static bool show_location = false; static bool upgrade_syslog_to_journal = false; /* Akin to glibc's __abort_msg; which is private and we hence cannot * use here. */ static char *log_abort_msg = NULL; void log_close_console(void) { if (console_fd < 0) return; if (getpid() == 1) { if (console_fd >= 3) safe_close(console_fd); console_fd = -1; } } static int log_open_console(void) { if (console_fd >= 0) return 0; if (getpid() == 1) { console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); if (console_fd < 0) return console_fd; } else console_fd = STDERR_FILENO; return 0; } void log_close_kmsg(void) { kmsg_fd = safe_close(kmsg_fd); } static int log_open_kmsg(void) { if (kmsg_fd >= 0) return 0; kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); if (kmsg_fd < 0) return -errno; return 0; } void log_close_syslog(void) { syslog_fd = safe_close(syslog_fd); } static int create_log_socket(int type) { struct timeval tv; int fd; fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0); if (fd < 0) return -errno; fd_inc_sndbuf(fd, SNDBUF_SIZE); /* We need a blocking fd here since we'd otherwise lose messages way too early. However, let's not hang forever in the unlikely case of a deadlock. */ if (getpid() == 1) timeval_store(&tv, 10 * USEC_PER_MSEC); else timeval_store(&tv, 10 * USEC_PER_SEC); (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); return fd; } static int log_open_syslog(void) { static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/dev/log", }; int r; if (syslog_fd >= 0) return 0; syslog_fd = create_log_socket(SOCK_DGRAM); if (syslog_fd < 0) { r = syslog_fd; goto fail; } if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { safe_close(syslog_fd); /* Some legacy syslog systems still use stream * sockets. They really shouldn't. But what can we * do... */ syslog_fd = create_log_socket(SOCK_STREAM); if (syslog_fd < 0) { r = syslog_fd; goto fail; } if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = -errno; goto fail; } syslog_is_stream = true; } else syslog_is_stream = false; return 0; fail: log_close_syslog(); return r; } void log_close_journal(void) { journal_fd = safe_close(journal_fd); } static int log_open_journal(void) { static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/socket", }; int r; if (journal_fd >= 0) return 0; journal_fd = create_log_socket(SOCK_DGRAM); if (journal_fd < 0) { r = journal_fd; goto fail; } if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = -errno; goto fail; } return 0; fail: log_close_journal(); return r; } int log_open(void) { int r; /* If we don't use the console we close it here, to not get * killed by SAK. If we don't use syslog we close it here so * that we are not confused by somebody deleting the socket in * the fs. If we don't use /dev/kmsg we still keep it open, * because there is no reason to close it. */ if (log_target == LOG_TARGET_NULL) { log_close_journal(); log_close_syslog(); log_close_console(); return 0; } if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) || getpid() == 1 || isatty(STDERR_FILENO) <= 0) { if (log_target == LOG_TARGET_AUTO || log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_JOURNAL) { r = log_open_journal(); if (r >= 0) { log_close_syslog(); log_close_console(); return r; } } if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_SYSLOG) { r = log_open_syslog(); if (r >= 0) { log_close_journal(); log_close_console(); return r; } } if (log_target == LOG_TARGET_AUTO || log_target == LOG_TARGET_SAFE || log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_KMSG) { r = log_open_kmsg(); if (r >= 0) { log_close_journal(); log_close_syslog(); log_close_console(); return r; } } } log_close_journal(); log_close_syslog(); return log_open_console(); } void log_set_target(LogTarget target) { assert(target >= 0); assert(target < _LOG_TARGET_MAX); if (upgrade_syslog_to_journal) { if (target == LOG_TARGET_SYSLOG) target = LOG_TARGET_JOURNAL; else if (target == LOG_TARGET_SYSLOG_OR_KMSG) target = LOG_TARGET_JOURNAL_OR_KMSG; } log_target = target; } void log_close(void) { log_close_journal(); log_close_syslog(); log_close_kmsg(); log_close_console(); } void log_forget_fds(void) { console_fd = kmsg_fd = syslog_fd = journal_fd = -1; } void log_set_max_level(int level) { assert((level & LOG_PRIMASK) == level); log_max_level = level; } void log_set_facility(int facility) { log_facility = facility; } static int write_to_console( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *buffer) { char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2]; struct iovec iovec[6] = {}; unsigned n = 0; bool highlight; if (console_fd < 0) return 0; if (log_target == LOG_TARGET_CONSOLE_PREFIXED) { sprintf(prefix, "<%i>", level); IOVEC_SET_STRING(iovec[n++], prefix); } highlight = LOG_PRI(level) <= LOG_ERR && show_color; if (show_location) { snprintf(location, sizeof(location), "(%s:%i) ", file, line); IOVEC_SET_STRING(iovec[n++], location); } if (highlight) IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED); IOVEC_SET_STRING(iovec[n++], buffer); if (highlight) IOVEC_SET_STRING(iovec[n++], ANSI_NORMAL); IOVEC_SET_STRING(iovec[n++], "\n"); if (writev(console_fd, iovec, n) < 0) { if (errno == EIO && getpid() == 1) { /* If somebody tried to kick us from our * console tty (via vhangup() or suchlike), * try to reconnect */ log_close_console(); log_open_console(); if (console_fd < 0) return 0; if (writev(console_fd, iovec, n) < 0) return -errno; } else return -errno; } return 1; } static int write_to_syslog( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *buffer) { char header_priority[2 + DECIMAL_STR_MAX(int) + 1], header_time[64], header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; struct iovec iovec[5] = {}; struct msghdr msghdr = { .msg_iov = iovec, .msg_iovlen = ELEMENTSOF(iovec), }; time_t t; struct tm *tm; if (syslog_fd < 0) return 0; xsprintf(header_priority, "<%i>", level); t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC); tm = localtime(&t); if (!tm) return -EINVAL; if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) return -EINVAL; xsprintf(header_pid, "["PID_FMT"]: ", getpid()); IOVEC_SET_STRING(iovec[0], header_priority); IOVEC_SET_STRING(iovec[1], header_time); IOVEC_SET_STRING(iovec[2], program_invocation_short_name); IOVEC_SET_STRING(iovec[3], header_pid); IOVEC_SET_STRING(iovec[4], buffer); /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ if (syslog_is_stream) iovec[4].iov_len++; for (;;) { ssize_t n; n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL); if (n < 0) return -errno; if (!syslog_is_stream || (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) break; IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); } return 1; } static int write_to_kmsg( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *buffer) { char header_priority[2 + DECIMAL_STR_MAX(int) + 1], header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; struct iovec iovec[5] = {}; if (kmsg_fd < 0) return 0; xsprintf(header_priority, "<%i>", level); xsprintf(header_pid, "["PID_FMT"]: ", getpid()); IOVEC_SET_STRING(iovec[0], header_priority); IOVEC_SET_STRING(iovec[1], program_invocation_short_name); IOVEC_SET_STRING(iovec[2], header_pid); IOVEC_SET_STRING(iovec[3], buffer); IOVEC_SET_STRING(iovec[4], "\n"); if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) return -errno; return 1; } static int log_do_header( char *header, size_t size, int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object) { snprintf(header, size, "PRIORITY=%i\n" "SYSLOG_FACILITY=%i\n" "%s%s%s" "%s%.*i%s" "%s%s%s" "%s%.*i%s" "%s%s%s" "SYSLOG_IDENTIFIER=%s\n", LOG_PRI(level), LOG_FAC(level), isempty(file) ? "" : "CODE_FILE=", isempty(file) ? "" : file, isempty(file) ? "" : "\n", line ? "CODE_LINE=" : "", line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */ line ? "\n" : "", isempty(func) ? "" : "CODE_FUNCTION=", isempty(func) ? "" : func, isempty(func) ? "" : "\n", error ? "ERRNO=" : "", error ? 1 : 0, error, error ? "\n" : "", isempty(object) ? "" : object_field, isempty(object) ? "" : object, isempty(object) ? "" : "\n", program_invocation_short_name); return 0; } static int write_to_journal( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *buffer) { char header[LINE_MAX]; struct iovec iovec[4] = {}; struct msghdr mh = {}; if (journal_fd < 0) return 0; log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object); IOVEC_SET_STRING(iovec[0], header); IOVEC_SET_STRING(iovec[1], "MESSAGE="); IOVEC_SET_STRING(iovec[2], buffer); IOVEC_SET_STRING(iovec[3], "\n"); mh.msg_iov = iovec; mh.msg_iovlen = ELEMENTSOF(iovec); if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) return -errno; return 1; } static int log_dispatch( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, char *buffer) { assert(buffer); if (log_target == LOG_TARGET_NULL) return -error; /* Patch in LOG_DAEMON facility if necessary */ if ((level & LOG_FACMASK) == 0) level = log_facility | LOG_PRI(level); if (error < 0) error = -error; do { char *e; int k = 0; buffer += strspn(buffer, NEWLINE); if (buffer[0] == 0) break; if ((e = strpbrk(buffer, NEWLINE))) *(e++) = 0; if (log_target == LOG_TARGET_AUTO || log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_JOURNAL) { k = write_to_journal(level, error, file, line, func, object_field, object, buffer); if (k < 0) { if (k != -EAGAIN) log_close_journal(); log_open_kmsg(); } } if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_SYSLOG) { k = write_to_syslog(level, error, file, line, func, object_field, object, buffer); if (k < 0) { if (k != -EAGAIN) log_close_syslog(); log_open_kmsg(); } } if (k <= 0 && (log_target == LOG_TARGET_AUTO || log_target == LOG_TARGET_SAFE || log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_KMSG)) { k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer); if (k < 0) { log_close_kmsg(); log_open_console(); } } if (k <= 0) (void) write_to_console(level, error, file, line, func, object_field, object, buffer); buffer = e; } while (buffer); return -error; } int log_dump_internal( int level, int error, const char *file, int line, const char *func, char *buffer) { PROTECT_ERRNO; /* This modifies the buffer... */ if (error < 0) error = -error; if (_likely_(LOG_PRI(level) > log_max_level)) return -error; return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); } int log_internalv( int level, int error, const char *file, int line, const char *func, const char *format, va_list ap) { PROTECT_ERRNO; char buffer[LINE_MAX]; if (error < 0) error = -error; if (_likely_(LOG_PRI(level) > log_max_level)) return -error; /* Make sure that %m maps to the specified error */ if (error != 0) errno = error; vsnprintf(buffer, sizeof(buffer), format, ap); return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); } int log_internal( int level, int error, const char *file, int line, const char *func, const char *format, ...) { va_list ap; int r; va_start(ap, format); r = log_internalv(level, error, file, line, func, format, ap); va_end(ap); return r; } int log_object_internalv( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *format, va_list ap) { PROTECT_ERRNO; char *buffer, *b; size_t l; if (error < 0) error = -error; if (_likely_(LOG_PRI(level) > log_max_level)) return -error; /* Make sure that %m maps to the specified error */ if (error != 0) errno = error; /* Prepend the object name before the message */ if (object) { size_t n; n = strlen(object); l = n + 2 + LINE_MAX; buffer = newa(char, l); b = stpcpy(stpcpy(buffer, object), ": "); } else { l = LINE_MAX; b = buffer = newa(char, l); } vsnprintf(b, l, format, ap); return log_dispatch(level, error, file, line, func, object_field, object, buffer); } int log_object_internal( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *format, ...) { va_list ap; int r; va_start(ap, format); r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap); va_end(ap); return r; } static void log_assert( int level, const char *text, const char *file, int line, const char *func, const char *format) { static char buffer[LINE_MAX]; if (_likely_(LOG_PRI(level) > log_max_level)) return; DISABLE_WARNING_FORMAT_NONLITERAL; xsprintf(buffer, format, text, file, line, func); REENABLE_WARNING; log_abort_msg = buffer; log_dispatch(level, 0, file, line, func, NULL, NULL, buffer); } _noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func) { log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); abort(); } _noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) { log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); abort(); } void log_assert_failed_return(const char *text, const char *file, int line, const char *func) { PROTECT_ERRNO; log_assert(LOG_DEBUG, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring."); } int log_oom_internal(const char *file, int line, const char *func) { log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory."); return -ENOMEM; } int log_format_iovec( struct iovec *iovec, unsigned iovec_len, unsigned *n, bool newline_separator, int error, const char *format, va_list ap) { static const char nl = '\n'; while (format && *n + 1 < iovec_len) { va_list aq; char *m; int r; /* We need to copy the va_list structure, * since vasprintf() leaves it afterwards at * an undefined location */ if (error != 0) errno = error; va_copy(aq, ap); r = vasprintf(&m, format, aq); va_end(aq); if (r < 0) return -EINVAL; /* Now, jump enough ahead, so that we point to * the next format string */ VA_FORMAT_ADVANCE(format, ap); IOVEC_SET_STRING(iovec[(*n)++], m); if (newline_separator) { iovec[*n].iov_base = (char*) &nl; iovec[*n].iov_len = 1; (*n)++; } format = va_arg(ap, char *); } return 0; } int log_struct_internal( int level, int error, const char *file, int line, const char *func, const char *format, ...) { char buf[LINE_MAX]; bool found = false; PROTECT_ERRNO; va_list ap; if (error < 0) error = -error; if (_likely_(LOG_PRI(level) > log_max_level)) return -error; if (log_target == LOG_TARGET_NULL) return -error; if ((level & LOG_FACMASK) == 0) level = log_facility | LOG_PRI(level); if ((log_target == LOG_TARGET_AUTO || log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_JOURNAL) && journal_fd >= 0) { char header[LINE_MAX]; struct iovec iovec[17] = {}; unsigned n = 0, i; int r; struct msghdr mh = { .msg_iov = iovec, }; bool fallback = false; /* If the journal is available do structured logging */ log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL); IOVEC_SET_STRING(iovec[n++], header); va_start(ap, format); r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap); if (r < 0) fallback = true; else { mh.msg_iovlen = n; (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL); } va_end(ap); for (i = 1; i < n; i += 2) free(iovec[i].iov_base); if (!fallback) return -error; } /* Fallback if journal logging is not available or didn't work. */ va_start(ap, format); while (format) { va_list aq; if (error != 0) errno = error; va_copy(aq, ap); vsnprintf(buf, sizeof(buf), format, aq); va_end(aq); if (startswith(buf, "MESSAGE=")) { found = true; break; } VA_FORMAT_ADVANCE(format, ap); format = va_arg(ap, char *); } va_end(ap); if (!found) return -error; return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8); } int log_set_target_from_string(const char *e) { LogTarget t; t = log_target_from_string(e); if (t < 0) return -EINVAL; log_set_target(t); return 0; } int log_set_max_level_from_string(const char *e) { int t; t = log_level_from_string(e); if (t < 0) return -EINVAL; log_set_max_level(t); return 0; } static int parse_proc_cmdline_item(const char *key, const char *value) { /* * The systemd.log_xyz= settings are parsed by all tools, and * so is "debug". * * However, "quiet" is only parsed by PID 1, and only turns of * status output to /dev/console, but does not alter the log * level. */ if (streq(key, "debug") && !value) log_set_max_level(LOG_DEBUG); else if (streq(key, "systemd.log_target") && value) { if (log_set_target_from_string(value) < 0) log_warning("Failed to parse log target '%s'. Ignoring.", value); } else if (streq(key, "systemd.log_level") && value) { if (log_set_max_level_from_string(value) < 0) log_warning("Failed to parse log level '%s'. Ignoring.", value); } else if (streq(key, "systemd.log_color") && value) { if (log_show_color_from_string(value) < 0) log_warning("Failed to parse log color setting '%s'. Ignoring.", value); } else if (streq(key, "systemd.log_location") && value) { if (log_show_location_from_string(value) < 0) log_warning("Failed to parse log location setting '%s'. Ignoring.", value); } return 0; } void log_parse_environment(void) { const char *e; if (get_ctty_devnr(0, NULL) < 0) /* Only try to read the command line in daemons. We assume that anything that has a controlling tty is user stuff. */ (void) parse_proc_cmdline(parse_proc_cmdline_item); e = secure_getenv("SYSTEMD_LOG_TARGET"); if (e && log_set_target_from_string(e) < 0) log_warning("Failed to parse log target '%s'. Ignoring.", e); e = secure_getenv("SYSTEMD_LOG_LEVEL"); if (e && log_set_max_level_from_string(e) < 0) log_warning("Failed to parse log level '%s'. Ignoring.", e); e = secure_getenv("SYSTEMD_LOG_COLOR"); if (e && log_show_color_from_string(e) < 0) log_warning("Failed to parse bool '%s'. Ignoring.", e); e = secure_getenv("SYSTEMD_LOG_LOCATION"); if (e && log_show_location_from_string(e) < 0) log_warning("Failed to parse bool '%s'. Ignoring.", e); } LogTarget log_get_target(void) { return log_target; } int log_get_max_level(void) { return log_max_level; } void log_show_color(bool b) { show_color = b; } bool log_get_show_color(void) { return show_color; } void log_show_location(bool b) { show_location = b; } bool log_get_show_location(void) { return show_location; } int log_show_color_from_string(const char *e) { int t; t = parse_boolean(e); if (t < 0) return t; log_show_color(t); return 0; } int log_show_location_from_string(const char *e) { int t; t = parse_boolean(e); if (t < 0) return t; log_show_location(t); return 0; } bool log_on_console(void) { if (log_target == LOG_TARGET_CONSOLE || log_target == LOG_TARGET_CONSOLE_PREFIXED) return true; return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0; } static const char *const log_target_table[_LOG_TARGET_MAX] = { [LOG_TARGET_CONSOLE] = "console", [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed", [LOG_TARGET_KMSG] = "kmsg", [LOG_TARGET_JOURNAL] = "journal", [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg", [LOG_TARGET_SYSLOG] = "syslog", [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg", [LOG_TARGET_AUTO] = "auto", [LOG_TARGET_SAFE] = "safe", [LOG_TARGET_NULL] = "null" }; DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget); void log_received_signal(int level, const struct signalfd_siginfo *si) { if (si->ssi_pid > 0) { _cleanup_free_ char *p = NULL; get_process_comm(si->ssi_pid, &p); log_full(level, "Received SIG%s from PID %"PRIu32" (%s).", signal_to_string(si->ssi_signo), si->ssi_pid, strna(p)); } else log_full(level, "Received SIG%s.", signal_to_string(si->ssi_signo)); } void log_set_upgrade_syslog_to_journal(bool b) { upgrade_syslog_to_journal = b; } int log_syntax_internal( const char *unit, int level, const char *config_file, unsigned config_line, int error, const char *file, int line, const char *func, const char *format, ...) { PROTECT_ERRNO; char buffer[LINE_MAX]; int r; va_list ap; if (error < 0) error = -error; if (_likely_(LOG_PRI(level) > log_max_level)) return -error; if (log_target == LOG_TARGET_NULL) return -error; if (error != 0) errno = error; va_start(ap, format); vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); if (unit) r = log_struct_internal( level, error, file, line, func, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), "CONFIG_FILE=%s", config_file, "CONFIG_LINE=%u", config_line, LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), NULL); else r = log_struct_internal( level, error, file, line, func, LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), "CONFIG_FILE=%s", config_file, "CONFIG_LINE=%u", config_line, LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), NULL); return r; } systemd-netlogd-1.4.4/src/share/log.h000066400000000000000000000211751474012624500175000ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include #include #include #include "macro.h" typedef enum LogTarget{ LOG_TARGET_CONSOLE, LOG_TARGET_CONSOLE_PREFIXED, LOG_TARGET_KMSG, LOG_TARGET_JOURNAL, LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_SYSLOG, LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ LOG_TARGET_NULL, _LOG_TARGET_MAX, _LOG_TARGET_INVALID = -1 } LogTarget; /* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be * used as a regular log level. */ #define LOG_NULL (LOG_EMERG - 1) /* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */ #define SYNTHETIC_ERRNO(num) (1 << 30 | (num)) #define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) #define ERRNO_VALUE(val) (abs(val) & ~(1 << 30)) void log_set_target(LogTarget target); void log_set_max_level(int level); void log_set_facility(int facility); int log_set_target_from_string(const char *e); int log_set_max_level_from_string(const char *e); void log_show_color(bool b); bool log_get_show_color(void) _pure_; void log_show_location(bool b); bool log_get_show_location(void) _pure_; int log_show_color_from_string(const char *e); int log_show_location_from_string(const char *e); LogTarget log_get_target(void) _pure_; int log_get_max_level(void) _pure_; int log_open(void); void log_close(void); void log_forget_fds(void); void log_close_syslog(void); void log_close_journal(void); void log_close_kmsg(void); void log_close_console(void); void log_parse_environment(void); int log_internal( int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(6,7); int log_internalv( int level, int error, const char *file, int line, const char *func, const char *format, va_list ap) _printf_(6,0); int log_object_internal( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *format, ...) _printf_(8,9); int log_object_internalv( int level, int error, const char*file, int line, const char *func, const char *object_field, const char *object, const char *format, va_list ap) _printf_(8,0); int log_struct_internal( int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(6,0) _sentinel_; int log_oom_internal( const char *file, int line, const char *func); int log_format_iovec( struct iovec *iovec, unsigned iovec_len, unsigned *n, bool newline_separator, int error, const char *format, va_list ap) _printf_(6,0); /* This modifies the buffer passed! */ int log_dump_internal( int level, int error, const char *file, int line, const char *func, char *buffer); /* Logging for various assertions */ _noreturn_ void log_assert_failed( const char *text, const char *file, int line, const char *func); _noreturn_ void log_assert_failed_unreachable( const char *text, const char *file, int line, const char *func); void log_assert_failed_return( const char *text, const char *file, int line, const char *func); /* Logging with level */ #define log_full_errno(level, error, ...) \ ({ \ int _level = (level), _e = (error); \ (log_get_max_level() >= LOG_PRI(_level)) \ ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ : -abs(_e); \ }) #define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) /* Normal logging */ #define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) #define log_info(...) log_full(LOG_INFO, __VA_ARGS__) #define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) #define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) #define log_error(...) log_full(LOG_ERR, __VA_ARGS__) #define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) /* Logging triggered by an errno-like error */ #define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) #define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) #define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) #define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) #define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) #ifdef LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) #else # define log_trace(...) do {} while (0) #endif /* Structured logging */ #define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) #define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) /* This modifies the buffer passed! */ #define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) #define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) bool log_on_console(void) _pure_; const char *log_target_to_string(LogTarget target) _const_; LogTarget log_target_from_string(const char *s) _pure_; /* Helpers to prepare various fields for structured logging */ #define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ #define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) void log_received_signal(int level, const struct signalfd_siginfo *si); void log_set_upgrade_syslog_to_journal(bool b); int log_syntax_internal( const char *unit, int level, const char *config_file, unsigned config_line, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(9, 10); #define log_syntax(unit, level, config_file, config_line, error, ...) \ ({ \ int _level = (level), _e = (error); \ (log_get_max_level() >= LOG_PRI(_level)) \ ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ : -abs(_e); \ }) #define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ ({ \ int _level = (level); \ if (log_get_max_level() >= LOG_PRI(_level)) { \ _cleanup_free_ char *_p = NULL; \ _p = utf8_escape_invalid(rvalue); \ log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \ "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \ } \ }) #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) systemd-netlogd-1.4.4/src/share/macro.h000066400000000000000000000517011474012624500200160ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include #define _printf_(a,b) __attribute__ ((format (printf, a, b))) #ifdef __clang__ # define _alloc_(...) #else # define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) #endif #define _sentinel_ __attribute__ ((sentinel)) #define _unused_ __attribute__ ((unused)) #define _destructor_ __attribute__ ((destructor)) #define _pure_ __attribute__ ((pure)) #define _const_ __attribute__ ((const)) #define _deprecated_ __attribute__ ((deprecated)) #define _packed_ __attribute__ ((packed)) #define _malloc_ __attribute__ ((malloc)) #define _weak_ __attribute__ ((weak)) #define _likely_(x) (__builtin_expect(!!(x),1)) #define _unlikely_(x) (__builtin_expect(!!(x),0)) #define _public_ __attribute__ ((visibility("default"))) #define _hidden_ __attribute__ ((visibility("hidden"))) #define _weakref_(x) __attribute__((weakref(#x))) #define _alignas_(x) __attribute__((aligned(__alignof(x)))) #define _cleanup_(x) __attribute__((cleanup(x))) #define _noreturn_ _Noreturn /* Temporarily disable some warnings */ #define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") #define DISABLE_WARNING_FORMAT_NONLITERAL \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") #define DISABLE_WARNING_MISSING_PROTOTYPES \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") #define DISABLE_WARNING_NONNULL \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wnonnull\"") #define DISABLE_WARNING_SHADOW \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wshadow\"") #define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") #define DISABLE_WARNING_ADDRESS \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Waddress\"") #define REENABLE_WARNING \ _Pragma("GCC diagnostic pop") /* automake test harness */ #define EXIT_TEST_SKIP 77 #define XSTRINGIFY(x) #x #define STRINGIFY(x) XSTRINGIFY(x) #define XCONCATENATE(x, y) x ## y #define CONCATENATE(x, y) XCONCATENATE(x, y) #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) #define UNIQ __COUNTER__ /* builtins */ #if __SIZEOF_INT__ == 4 #define BUILTIN_FFS_U32(x) __builtin_ffs(x); #elif __SIZEOF_LONG__ == 4 #define BUILTIN_FFS_U32(x) __builtin_ffsl(x); #else #error "neither int nor long are four bytes long?!?" #endif /* Rounds up */ #define ALIGN4(l) (((l) + 3) & ~3) #define ALIGN8(l) (((l) + 7) & ~7) #if __SIZEOF_POINTER__ == 8 #define ALIGN(l) ALIGN8(l) #elif __SIZEOF_POINTER__ == 4 #define ALIGN(l) ALIGN4(l) #else #error "Wut? Pointers are neither 4 nor 8 bytes long?" #endif #define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) #define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) #define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) static inline size_t ALIGN_TO(size_t l, size_t ali) { return ((l + ali - 1) & ~(ali - 1)); } #define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ static inline unsigned long ALIGN_POWER2(unsigned long u) { /* clz(0) is undefined */ if (u == 1) return 1; /* left-shift overflow is undefined */ if (__builtin_clzl(u - 1UL) < 1) return 0; return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); } #define ELEMENTSOF(x) \ __extension__ (__builtin_choose_expr( \ !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ sizeof(x)/sizeof((x)[0]), \ (void)0)) /* * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. */ #define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member) #define __container_of(uniq, ptr, type, member) \ __extension__ ({ \ const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \ (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \ }) #undef MAX #define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) #define __MAX(aq, a, bq, b) \ __extension__ ({ \ const typeof(a) UNIQ_T(A, aq) = (a); \ const typeof(b) UNIQ_T(B, bq) = (b); \ UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ }) /* evaluates to (void) if _A or _B are not constant or of different types */ #define CONST_MAX(_A, _B) \ __extension__ (__builtin_choose_expr( \ __builtin_constant_p(_A) && \ __builtin_constant_p(_B) && \ __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ ((_A) > (_B)) ? (_A) : (_B), \ (void)0)) /* takes two types and returns the size of the larger one */ #define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) #define MAX3(x,y,z) \ __extension__ ({ \ const typeof(x) _c = MAX(x,y); \ MAX(_c, z); \ }) #undef MIN #define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) #define __MIN(aq, a, bq, b) \ __extension__ ({ \ const typeof(a) UNIQ_T(A, aq) = (a); \ const typeof(b) UNIQ_T(B, bq) = (b); \ UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ }) #define MIN3(x,y,z) \ __extension__ ({ \ const typeof(x) _c = MIN(x,y); \ MIN(_c, z); \ }) #define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) #define __LESS_BY(aq, a, bq, b) \ __extension__ ({ \ const typeof(a) UNIQ_T(A, aq) = (a); \ const typeof(b) UNIQ_T(B, bq) = (b); \ UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \ }) #undef CLAMP #define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) #define __CLAMP(xq, x, lowq, low, highq, high) \ __extension__ ({ \ const typeof(x) UNIQ_T(X,xq) = (x); \ const typeof(low) UNIQ_T(LOW,lowq) = (low); \ const typeof(high) UNIQ_T(HIGH,highq) = (high); \ UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \ UNIQ_T(HIGH,highq) : \ UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \ UNIQ_T(LOW,lowq) : \ UNIQ_T(X,xq); \ }) /* [(x + y - 1) / y] suffers from an integer overflow, even though the * computation should be possible in the given type. Therefore, we use * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the * quotient and the remainder, so both should be equally fast. */ #define DIV_ROUND_UP(_x, _y) \ __extension__ ({ \ const typeof(_x) __x = (_x); \ const typeof(_y) __y = (_y); \ (__x / __y + !!(__x % __y)); \ }) #define assert_message_se(expr, message) \ do { \ if (_unlikely_(!(expr))) \ log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ } while (false) #define assert_se(expr) assert_message_se(expr, #expr) /* We override the glibc assert() here. */ #undef assert #ifdef NDEBUG #define assert(expr) do {} while (false) #else #define assert(expr) assert_message_se(expr, #expr) #endif #define assert_not_reached(t) \ do { \ log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ } while (false) #if defined(static_assert) /* static_assert() is sometimes defined in a way that trips up * -Wdeclaration-after-statement, hence let's temporarily turn off * this warning around it. */ #define assert_cc(expr) \ DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ static_assert(expr, #expr); \ REENABLE_WARNING #else #define assert_cc(expr) \ DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ struct CONCATENATE(_assert_struct_, __COUNTER__) { \ char x[(expr) ? 0 : -1]; \ }; \ REENABLE_WARNING #endif #define assert_log(expr, message) ((_likely_(expr)) \ ? (true) \ : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false)) #define assert_return(expr, r) \ do { \ if (!assert_log(expr, #expr)) \ return (r); \ } while (false) #define assert_return_errno(expr, r, err) \ do { \ if (!assert_log(expr, #expr)) { \ errno = err; \ return (r); \ } \ } while (false) #define PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) #define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) #define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) #define LONG_TO_PTR(u) ((void *) ((intptr_t) (u))) #define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) #define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) #define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) #define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) #define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p))) #define INT64_TO_PTR(u) ((void *) ((intptr_t) (u))) #define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) #define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) #define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define CHAR_TO_STR(x) ((char[2]) { x, 0 }) #define char_array_0(x) x[sizeof(x)-1] = 0; /* Returns the number of chars needed to format variables of the * specified type as a decimal string. Adds in extra space for a * negative '-' prefix (hence works correctly on signed * types). Includes space for the trailing NUL. */ #define DECIMAL_STR_MAX(type) \ (2+(sizeof(type) <= 1 ? 3 : \ sizeof(type) <= 2 ? 5 : \ sizeof(type) <= 4 ? 10 : \ sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) #define DECIMAL_STR_WIDTH(x) \ ({ \ typeof(x) _x_ = (x); \ unsigned ans = 1; \ while (_x_ /= 10) \ ans++; \ ans; \ }) #define UPDATE_FLAG(orig, flag, b) \ ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) #define SET_FLAG(v, flag, b) \ (v) = UPDATE_FLAG(v, flag, b) #define FLAGS_SET(v, flags) \ ((~(v) & (flags)) == 0) #define CASE_F(X) case X: #define CASE_F_1(CASE, X) CASE_F(X) #define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__) #define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__) #define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__) #define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__) #define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__) #define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__) #define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__) #define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__) #define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__) #define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__) #define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__) #define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__) #define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__) #define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__) #define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__) #define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__) #define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__) #define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__) #define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__) #define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME #define FOR_EACH_MAKE_CASE(...) \ GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ (CASE_F,__VA_ARGS__) #define IN_SET(x, ...) \ ({ \ bool _found = false; \ /* If the build breaks in the line below, you need to extend the case macros */ \ static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \ switch(x) { \ FOR_EACH_MAKE_CASE(__VA_ARGS__) \ _found = true; \ break; \ default: \ break; \ } \ _found; \ }) #define SWAP_TWO(x, y) do { \ typeof(x) _t = (x); \ (x) = (y); \ (y) = (_t); \ } while (false) /* Define C11 thread_local attribute even on older gcc compiler * version */ #ifndef thread_local /* * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 */ #if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) #define thread_local _Thread_local #else #define thread_local __thread #endif #endif #define _FOREACH_ARRAY(i, array, num, m, end) \ for (typeof(array[0]) *i = (array), *end = ({ \ typeof(num) m = (num); \ (i && m > 0) ? i + m : NULL; \ }); end && i < end; i++) #define FOREACH_ARRAY(i, array, num) \ _FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ)) #define FOREACH_ELEMENT(i, array) \ FOREACH_ARRAY(i, array, ELEMENTSOF(array)) #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ func(*p); \ } \ struct __useless_struct_to_allow_trailing_semicolon__ /* When func() doesn't return the appropriate type, set variable to empty afterwards. * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */ #define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \ static inline void func##p(type *p) { \ if (*p != (empty)) { \ DISABLE_WARNING_ADDRESS; \ assert(func); \ REENABLE_WARNING; \ func(*p); \ *p = (empty); \ } \ } /* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */ #define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \ static inline void func##p(type *p) { \ if (*p != (empty)) { \ func(*p); \ *p = (empty); \ } \ } /* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ #define TAKE_PTR(ptr) \ ({ \ typeof(ptr) *_pptr_ = &(ptr); \ typeof(ptr) _ptr_ = *_pptr_; \ *_pptr_ = NULL; \ _ptr_; \ }) /* This passes the argument through after (if asserts are enabled) checking that it is not null. */ #define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert) #define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se) #define _ASSERT_PTR(expr, var, check) \ ({ \ typeof(expr) var = (expr); \ check(var); \ var; \ }) #define ASSERT_NONNEG(expr) \ ({ \ typeof(expr) _expr_ = (expr), _zero = 0; \ assert(_expr_ >= _zero); \ _expr_; \ }) #define ASSERT_SE_NONNEG(expr) \ ({ \ typeof(expr) _expr_ = (expr), _zero = 0; \ assert_se(_expr_ >= _zero); \ _expr_; \ }) #include "log.h" systemd-netlogd-1.4.4/src/share/mempool.c000066400000000000000000000035041474012624500203560ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include "macro.h" #include "mempool.h" #include "util.h" struct pool { struct pool *next; unsigned n_tiles; unsigned n_used; }; void* mempool_alloc_tile(struct mempool *mp) { unsigned i; /* When a tile is released we add it to the list and simply * place the next pointer at its offset 0. */ assert(mp->tile_size >= sizeof(void*)); assert(mp->at_least > 0); if (mp->freelist) { void *r; r = mp->freelist; mp->freelist = * (void**) mp->freelist; return r; } if (_unlikely_(!mp->first_pool) || _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { unsigned n; size_t size; struct pool *p; n = mp->first_pool ? mp->first_pool->n_tiles : 0; n = MAX(mp->at_least, n * 2); size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size); n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size; p = malloc(size); if (!p) return NULL; p->next = mp->first_pool; p->n_tiles = n; p->n_used = 0; mp->first_pool = p; } i = mp->first_pool->n_used++; return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; } void* mempool_alloc0_tile(struct mempool *mp) { void *p; p = mempool_alloc_tile(mp); if (p) memzero(p, mp->tile_size); return p; } void mempool_free_tile(struct mempool *mp, void *p) { * (void**) p = mp->freelist; mp->freelist = p; } systemd-netlogd-1.4.4/src/share/mempool.h000066400000000000000000000011601474012624500203570ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include struct pool; struct mempool { struct pool *first_pool; void *freelist; size_t tile_size; unsigned at_least; }; void* mempool_alloc_tile(struct mempool *mp); void* mempool_alloc0_tile(struct mempool *mp); void mempool_free_tile(struct mempool *mp, void *p); #define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ static struct mempool pool_name = { \ .tile_size = sizeof(tile_type), \ .at_least = alloc_at_least, \ } #ifdef VALGRIND void mempool_drop(struct mempool *mp); #endif systemd-netlogd-1.4.4/src/share/missing.h000066400000000000000000000021451474012624500203640ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once /* Missing glibc definitions to access certain kernel APIs */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ARCH_MIPS #include #endif #ifdef HAVE_LINUX_BTRFS_H #include #endif #include "macro.h" #if defined(__i386__) || defined(__x86_64__) /* The precise definition of __O_TMPFILE is arch specific, so let's * just define this on x86 where we know the value. */ #ifndef __O_TMPFILE #define __O_TMPFILE 020000000 #endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #ifndef O_TMPFILE #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #endif #endif #ifndef HAVE_KEY_SERIAL_T typedef int32_t key_serial_t; #endif #include "missing_syscall.h" systemd-netlogd-1.4.4/src/share/missing_syscall.h000066400000000000000000000031071474012624500221150ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once /* Missing glibc definitions to access certain kernel APIs */ /* ======================================================================= */ #if !HAVE_GETRANDOM # ifndef __NR_getrandom # if defined __x86_64__ # define __NR_getrandom 318 # elif defined(__i386__) # define __NR_getrandom 355 # elif defined(__arm__) # define __NR_getrandom 384 # elif defined(__aarch64__) # define __NR_getrandom 278 # elif defined(__ia64__) # define __NR_getrandom 1339 # elif defined(__m68k__) # define __NR_getrandom 352 # elif defined(__s390x__) # define __NR_getrandom 349 # elif defined(__powerpc__) # define __NR_getrandom 359 # elif defined _MIPS_SIM # if _MIPS_SIM == _MIPS_SIM_ABI32 # define __NR_getrandom 4353 # endif # if _MIPS_SIM == _MIPS_SIM_NABI32 # define __NR_getrandom 6317 # endif # if _MIPS_SIM == _MIPS_SIM_ABI64 # define __NR_getrandom 5313 # endif # else # warning "__NR_getrandom unknown for your architecture" # endif # endif static inline int getrandom(void *buffer, size_t count, unsigned flags) { # ifdef __NR_getrandom return syscall(__NR_getrandom, buffer, count, flags); # else errno = ENOSYS; return -1; # endif } #endif /* ======================================================================= */ static inline pid_t raw_getpid(void) { #if defined(__alpha__) return (pid_t) syscall(__NR_getxpid); #else return (pid_t) syscall(__NR_getpid); #endif } systemd-netlogd-1.4.4/src/share/mkdir.c000066400000000000000000000057361474012624500200250ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "fs-util.h" #include "macro.h" #include "mkdir.h" #include "path-util.h" #include "stat-util.h" #include "user-util.h" int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) { struct stat st; if (_mkdir(path, mode) >= 0) if (chmod_and_chown(path, mode, uid, gid) < 0) return -errno; if (lstat(path, &st) < 0) return -errno; if ((st.st_mode & 0007) > (mode & 0007) || (st.st_mode & 0070) > (mode & 0070) || (st.st_mode & 0700) > (mode & 0700) || (uid != UID_INVALID && st.st_uid != uid) || (gid != GID_INVALID && st.st_gid != gid) || !S_ISDIR(st.st_mode)) return -EEXIST; return 0; } int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) { return mkdir_safe_internal(path, mode, uid, gid, mkdir); } int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { const char *p, *e; int r; assert(path); if (prefix && !path_startswith(path, prefix)) return -ENOTDIR; /* return immediately if directory exists */ e = strrchr(path, '/'); if (!e) return -EINVAL; if (e == path) return 0; p = strndupa(path, e - path); r = is_dir(p, true); if (r > 0) return 0; if (r == 0) return -ENOTDIR; /* create every parent directory in the path, except the last component */ p = path + strspn(path, "/"); for (;;) { char t[strlen(path) + 1]; e = p + strcspn(p, "/"); p = e + strspn(e, "/"); /* Is this the last component? If so, then we're * done */ if (*p == 0) return 0; memcpy(t, path, e - path); t[e-path] = 0; if (prefix && path_startswith(prefix, t)) continue; r = _mkdir(t, mode); if (r < 0 && errno != EEXIST) return -errno; } } int mkdir_parents(const char *path, mode_t mode) { return mkdir_parents_internal(NULL, path, mode, mkdir); } int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { int r; /* Like mkdir -p */ r = mkdir_parents_internal(prefix, path, mode, _mkdir); if (r < 0) return r; r = _mkdir(path, mode); if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0)) return -errno; return 0; } int mkdir_p(const char *path, mode_t mode) { return mkdir_p_internal(NULL, path, mode, mkdir); } systemd-netlogd-1.4.4/src/share/mkdir.h000066400000000000000000000015321474012624500200200ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid); int mkdir_parents(const char *path, mode_t mode); int mkdir_p(const char *path, mode_t mode); /* mandatory access control(MAC) versions */ int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid); int mkdir_parents_label(const char *path, mode_t mode); int mkdir_p_label(const char *path, mode_t mode); /* internally used */ typedef int (*mkdir_func_t)(const char *pathname, mode_t mode); int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir); int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); systemd-netlogd-1.4.4/src/share/network-util.c000066400000000000000000000007711474012624500213550ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" #include "fd-util.h" #include "network-util.h" #include "strv.h" bool network_is_online(void) { _cleanup_free_ char *state = NULL; int r; r = sd_network_get_operational_state(&state); if (r < 0) /* if we don't know anything, we consider the system online */ return true; if (STR_IN_SET(state, "routable", "degraded")) return true; return false; } systemd-netlogd-1.4.4/src/share/network-util.h000066400000000000000000000001671474012624500213610ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "sd-network.h" bool network_is_online(void); systemd-netlogd-1.4.4/src/share/openssl-util.h000066400000000000000000000015601474012624500213510ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include static inline char *tls_error_string(int ssl_error, char *buf, size_t count) { assert(buf || count == 0); if (ssl_error == SSL_ERROR_SSL) ERR_error_string_n(ERR_get_error(), buf, count); else snprintf(buf, count, "SSL_get_error()=%d", ssl_error); return buf; } #define TLS_ERROR_BUFSIZE 256 #define TLS_ERROR_STRING(error) \ tls_error_string((error), (char[TLS_ERROR_BUFSIZE]){}, TLS_ERROR_BUFSIZE) DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(void *, OPENSSL_free, NULL); systemd-netlogd-1.4.4/src/share/parse-util.c000066400000000000000000000163531474012624500210010ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "alloc-util.h" #include "extract-word.h" #include "macro.h" #include "parse-util.h" #include "string-util.h" int parse_boolean(const char *v) { assert(v); if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) return 1; else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off")) return 0; return -EINVAL; } int parse_ifindex(const char *s, int *ret) { int ifi, r; r = safe_atoi(s, &ifi); if (r < 0) return r; if (ifi <= 0) return -EINVAL; *ret = ifi; return 0; } int safe_atou(const char *s, unsigned *ret_u) { char *x = NULL; unsigned long l; assert(s); assert(ret_u); /* strtoul() is happy to parse negative values, and silently * converts them to unsigned values without generating an * error. We want a clean error, hence let's look for the "-" * prefix on our own, and generate an error. But let's do so * only after strtoul() validated that the string is clean * otherwise, so that we return EINVAL preferably over * ERANGE. */ s += strspn(s, WHITESPACE); errno = 0; l = strtoul(s, &x, 0); if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; if (s[0] == '-') return -ERANGE; if ((unsigned long) (unsigned) l != l) return -ERANGE; *ret_u = (unsigned) l; return 0; } int safe_atoi(const char *s, int *ret_i) { char *x = NULL; long l; assert(s); assert(ret_i); errno = 0; l = strtol(s, &x, 0); if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; if ((long) (int) l != l) return -ERANGE; *ret_i = (int) l; return 0; } int safe_atollu(const char *s, long long unsigned *ret_llu) { char *x = NULL; unsigned long long l; assert(s); assert(ret_llu); s += strspn(s, WHITESPACE); errno = 0; l = strtoull(s, &x, 0); if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; if (*s == '-') return -ERANGE; *ret_llu = l; return 0; } int safe_atolli(const char *s, long long int *ret_lli) { char *x = NULL; long long l; assert(s); assert(ret_lli); errno = 0; l = strtoll(s, &x, 0); if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; *ret_lli = l; return 0; } int parse_size(const char *t, uint64_t base, uint64_t *size) { /* Soo, sometimes we want to parse IEC binary suffixes, and * sometimes SI decimal suffixes. This function can parse * both. Which one is the right way depends on the * context. Wikipedia suggests that SI is customary for * hardware metrics and network speeds, while IEC is * customary for most data sizes used by software and volatile * (RAM) memory. Hence be careful which one you pick! * * In either case we use just K, M, G as suffix, and not Ki, * Mi, Gi or so (as IEC would suggest). That's because that's * frickin' ugly. But this means you really need to make sure * to document which base you are parsing when you use this * call. */ struct table { const char *suffix; unsigned long long factor; }; static const struct table iec[] = { { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, { "G", 1024ULL*1024ULL*1024ULL }, { "M", 1024ULL*1024ULL }, { "K", 1024ULL }, { "B", 1ULL }, { "", 1ULL }, }; static const struct table si[] = { { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, { "T", 1000ULL*1000ULL*1000ULL*1000ULL }, { "G", 1000ULL*1000ULL*1000ULL }, { "M", 1000ULL*1000ULL }, { "K", 1000ULL }, { "B", 1ULL }, { "", 1ULL }, }; const struct table *table; const char *p; unsigned long long r = 0; unsigned n_entries, start_pos = 0; assert(t); assert(IN_SET(base, 1000, 1024)); assert(size); if (base == 1000) { table = si; n_entries = ELEMENTSOF(si); } else { table = iec; n_entries = ELEMENTSOF(iec); } p = t; do { unsigned long long l, tmp; double frac = 0; char *e; unsigned i; p += strspn(p, WHITESPACE); errno = 0; l = strtoull(p, &e, 10); if (errno > 0) return -errno; if (e == p) return -EINVAL; if (*p == '-') return -ERANGE; if (*e == '.') { e++; /* strtoull() itself would accept space/+/- */ if (ascii_isdigit(*e)) { unsigned long long l2; char *e2; l2 = strtoull(e, &e2, 10); if (errno > 0) return -errno; /* Ignore failure. E.g. 10.M is valid */ frac = l2; for (; e < e2; e++) frac /= 10; } } e += strspn(e, WHITESPACE); for (i = start_pos; i < n_entries; i++) if (startswith(e, table[i].suffix)) break; if (i >= n_entries) return -EINVAL; if (l + (frac > 0) > ULLONG_MAX / table[i].factor) return -ERANGE; tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); if (tmp > ULLONG_MAX - r) return -ERANGE; r += tmp; if ((unsigned long long) (uint64_t) r != r) return -ERANGE; p = e + strlen(table[i].suffix); start_pos = i + 1; } while (*p); *size = r; return 0; } systemd-netlogd-1.4.4/src/share/parse-util.h000066400000000000000000000030031474012624500207720ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include "macro.h" #define MODE_INVALID ((mode_t) -1) int parse_boolean(const char *v) _pure_; int parse_ifindex(const char *s, int *ret); #define FORMAT_BYTES_MAX 8 int safe_atou(const char *s, unsigned *ret_u); int safe_atoi(const char *s, int *ret_i); int safe_atollu(const char *s, unsigned long long *ret_u); int safe_atolli(const char *s, long long int *ret_i); static inline int safe_atou32(const char *s, uint32_t *ret_u) { assert_cc(sizeof(uint32_t) == sizeof(unsigned)); return safe_atou(s, (unsigned*) ret_u); } int parse_size(const char *t, uint64_t base, uint64_t *size); #if LONG_MAX == INT_MAX static inline int safe_atolu(const char *s, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned)); return safe_atou(s, (unsigned*) ret_u); } static inline int safe_atoli(const char *s, long int *ret_u) { assert_cc(sizeof(long int) == sizeof(int)); return safe_atoi(s, (int*) ret_u); } #else static inline int safe_atolu(const char *s, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); return safe_atollu(s, (unsigned long long*) ret_u); } static inline int safe_atoli(const char *s, long int *ret_u) { assert_cc(sizeof(long int) == sizeof(long long int)); return safe_atolli(s, (long long int*) ret_u); } #endif systemd-netlogd-1.4.4/src/share/path-util.c000066400000000000000000000260711474012624500206210ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include /* When we include libgen.h because we need dirname() we immediately * undefine basename() since libgen.h defines it as a macro to the * POSIX version which is really broken. We prefer GNU basename(). */ #include #undef basename #include "alloc-util.h" #include "extract-word.h" #include "fs-util.h" #include "log.h" #include "macro.h" #include "missing.h" #include "path-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" bool path_is_absolute(const char *p) { return p[0] == '/'; } bool is_path(const char *p) { return !!strchr(p, '/'); } char **path_strv_resolve(char **l, const char *prefix) { char **s; unsigned k = 0; bool enomem = false; if (strv_isempty(l)) return l; /* Goes through every item in the string list and canonicalize * the path. This works in place and won't rollback any * changes on failure. */ STRV_FOREACH(s, l) { char *t, *u; _cleanup_free_ char *orig = NULL; if (!path_is_absolute(*s)) { free(*s); continue; } if (prefix) { orig = *s; t = strappend(prefix, orig); if (!t) { enomem = true; continue; } } else t = *s; errno = 0; u = canonicalize_file_name(t); if (!u) { if (errno == ENOENT) { if (prefix) { u = orig; orig = NULL; free(t); } else u = t; } else { free(t); if (errno == ENOMEM || errno == 0) enomem = true; continue; } } else if (prefix) { char *x; free(t); x = path_startswith(u, prefix); if (x) { /* restore the slash if it was lost */ if (!startswith(x, "/")) *(--x) = '/'; t = strdup(x); free(u); if (!t) { enomem = true; continue; } u = t; } else { /* canonicalized path goes outside of * prefix, keep the original path instead */ free(u); u = orig; orig = NULL; } } else free(t); l[k++] = u; } l[k] = NULL; if (enomem) return NULL; return l; } char **path_strv_resolve_uniq(char **l, const char *prefix) { if (strv_isempty(l)) return l; if (!path_strv_resolve(l, prefix)) return NULL; return strv_uniq(l); } char *path_kill_slashes(char *path) { char *f, *t; bool slash = false; /* Removes redundant inner and trailing slashes. Modifies the * passed string in-place. * * ///foo///bar/ becomes /foo/bar */ for (f = path, t = path; *f; f++) { if (*f == '/') { slash = true; continue; } if (slash) { slash = false; *(t++) = '/'; } *(t++) = *f; } /* Special rule, if we are talking of the root directory, a trailing slash is good */ if (t == path && slash) *(t++) = '/'; *t = 0; return path; } char* path_startswith(const char *path, const char *prefix) { assert(path); assert(prefix); if ((path[0] == '/') != (prefix[0] == '/')) return NULL; for (;;) { size_t a, b; path += strspn(path, "/"); prefix += strspn(prefix, "/"); if (*prefix == 0) return (char*) path; if (*path == 0) return NULL; a = strcspn(path, "/"); b = strcspn(prefix, "/"); if (a != b) return NULL; if (memcmp(path, prefix, a) != 0) return NULL; path += a; prefix += b; } } int path_compare(const char *a, const char *b) { int d; assert(a); assert(b); /* A relative path and an absolute path must not compare as equal. * Which one is sorted before the other does not really matter. * Here a relative path is ordered before an absolute path. */ d = (a[0] == '/') - (b[0] == '/'); if (d != 0) return d; for (;;) { size_t j, k; a += strspn(a, "/"); b += strspn(b, "/"); if (*a == 0 && *b == 0) return 0; /* Order prefixes first: "/foo" before "/foo/bar" */ if (*a == 0) return -1; if (*b == 0) return 1; j = strcspn(a, "/"); k = strcspn(b, "/"); /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ d = memcmp(a, b, MIN(j, k)); if (d != 0) return (d > 0) - (d < 0); /* sign of d */ /* Sort "/foo/a" before "/foo/aaa" */ d = (j > k) - (j < k); /* sign of (j - k) */ if (d != 0) return d; a += j; b += k; } } bool path_equal(const char *a, const char *b) { return path_compare(a, b) == 0; } char *prefix_root(const char *root, const char *path) { char *n, *p; size_t l; /* If root is passed, prefixes path with it. Otherwise returns * it as is. */ assert(path); /* First, drop duplicate prefixing slashes from the path */ while (path[0] == '/' && path[1] == '/') path++; if (isempty(root) || path_equal(root, "/")) return strdup(path); l = strlen(root) + 1 + strlen(path) + 1; n = new(char, l); if (!n) return NULL; p = stpcpy(n, root); while (p > n && p[-1] == '/') p--; if (path[0] != '/') *(p++) = '/'; strcpy(p, path); return n; } bool filename_is_valid(const char *p) { const char *e; if (isempty(p)) return false; if (streq(p, ".")) return false; if (streq(p, "..")) return false; e = strchrnul(p, '/'); if (*e != 0) return false; if (e - p > FILENAME_MAX) return false; return true; } bool path_is_safe(const char *p) { if (isempty(p)) return false; if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) return false; if (strlen(p)+1 > PATH_MAX) return false; /* The following two checks are not really dangerous, but hey, they still are confusing */ if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) return false; if (strstr(p, "//")) return false; return true; } char *file_in_same_dir(const char *path, const char *filename) { char *e, *ret; size_t k; assert(path); assert(filename); /* This removes the last component of path and appends * filename, unless the latter is absolute anyway or the * former isn't */ if (path_is_absolute(filename)) return strdup(filename); e = strrchr(path, '/'); if (!e) return strdup(filename); k = strlen(filename); ret = new(char, (e + 1 - path) + k + 1); if (!ret) return NULL; memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); return ret; } bool hidden_or_backup_file(const char *filename) { const char *p; assert(filename); if (filename[0] == '.' || streq(filename, "lost+found") || streq(filename, "aquota.user") || streq(filename, "aquota.group") || endswith(filename, "~")) return true; p = strrchr(filename, '.'); if (!p) return false; /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up * with always new suffixes and that everybody else should just adjust to that, then it really should be on * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional * string. Specifically: there's now: * * The generic suffixes "~" and ".bak" for backup files * The generic prefix "." for hidden files * * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. */ return STR_IN_SET(p + 1, "rpmnew", "rpmsave", "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old", "ucf-dist", "swp", "bak", "old", "new"); } systemd-netlogd-1.4.4/src/share/path-util.h000066400000000000000000000103061474012624500206200ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include "macro.h" #include "time-util.h" #define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" #define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" #ifdef HAVE_SPLIT_USR # define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR #else # define DEFAULT_PATH DEFAULT_PATH_NORMAL #endif bool is_path(const char *p) _pure_; bool path_is_absolute(const char *p) _pure_; char* path_kill_slashes(char *path); char* path_startswith(const char *path, const char *prefix) _pure_; int path_compare(const char *a, const char *b) _pure_; bool path_equal(const char *a, const char *b) _pure_; /* Note: the search terminates on the first NULL item. */ #define PATH_IN_SET(p, ...) \ ({ \ char **s; \ bool _found = false; \ STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ if (path_equal(p, *s)) { \ _found = true; \ break; \ } \ _found; \ }) char** path_strv_resolve(char **l, const char *prefix); char** path_strv_resolve_uniq(char **l, const char *prefix); /* Iterates through the path prefixes of the specified path, going up * the tree, to root. Also returns "" (and not "/"!) for the root * directory. Excludes the specified directory itself */ #define PATH_FOREACH_PREFIX(prefix, path) \ for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) /* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ #define PATH_FOREACH_PREFIX_MORE(prefix, path) \ for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) char *prefix_root(const char *root, const char *path); /* Similar to prefix_root(), but returns an alloca() buffer, or * possibly a const pointer into the path parameter */ #define prefix_roota(root, path) \ ({ \ const char* _path = (path), *_root = (root), *_ret; \ char *_p, *_n; \ size_t _l; \ while (_path[0] == '/' && _path[1] == '/') \ _path ++; \ if (isempty(_root) || path_equal(_root, "/")) \ _ret = _path; \ else { \ _l = strlen(_root) + 1 + strlen(_path) + 1; \ _n = alloca(_l); \ _p = stpcpy(_n, _root); \ while (_p > _n && _p[-1] == '/') \ _p--; \ if (_path[0] != '/') \ *(_p++) = '/'; \ strcpy(_p, _path); \ _ret = _n; \ } \ _ret; \ }) bool filename_is_valid(const char *p) _pure_; bool path_is_safe(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); bool hidden_or_backup_file(const char *filename) _pure_; systemd-netlogd-1.4.4/src/share/proc-cmdline.c000066400000000000000000000031201474012624500212540ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include "alloc-util.h" #include "extract-word.h" #include "fileio.h" #include "macro.h" #include "parse-util.h" #include "proc-cmdline.h" #include "process-util.h" #include "string-util.h" #include "util.h" #include "virt.h" int proc_cmdline(char **ret) { assert(ret); if (detect_container() > 0) return get_process_cmdline(1, 0, false, ret); else return read_one_line_file("/proc/cmdline", ret); } int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { _cleanup_free_ char *line = NULL; const char *p; int r; assert(parse_item); r = proc_cmdline(&line); if (r < 0) return r; p = line; for (;;) { _cleanup_free_ char *word = NULL; char *value = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) break; /* Filter out arguments that are intended only for the * initrd */ if (!in_initrd() && startswith(word, "rd.")) continue; value = strchr(word, '='); if (value) *(value++) = 0; r = parse_item(word, value); if (r < 0) return r; } return 0; } systemd-netlogd-1.4.4/src/share/proc-cmdline.h000066400000000000000000000002551474012624500212670ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once int proc_cmdline(char **ret); int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value)); systemd-netlogd-1.4.4/src/share/process-util.c000066400000000000000000000275501474012624500213460ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_VALGRIND_VALGRIND_H #include #endif #include "alloc-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "ioprio.h" #include "log.h" #include "macro.h" #include "missing.h" #include "process-util.h" #include "signal-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "user-util.h" #include "util.h" int get_process_comm(pid_t pid, char **name) { const char *p; int r; assert(name); assert(pid >= 0); p = procfs_file_alloca(pid, "comm"); r = read_one_line_file(p, name); if (r == -ENOENT) return -ESRCH; return r; } int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { _cleanup_fclose_ FILE *f = NULL; bool space = false; char *r = NULL, *k; const char *p; int c; assert(line); assert(pid >= 0); /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a * command line that resolves to the empty string will return the "comm" name of the process instead. * * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and * comm_fallback is false). */ p = procfs_file_alloca(pid, "cmdline"); f = fopen(p, "re"); if (!f) { if (errno == ENOENT) return -ESRCH; return -errno; } if (max_length == 1) { /* If there's only room for one byte, return the empty string */ r = new0(char, 1); if (!r) return -ENOMEM; *line = r; return 0; } else if (max_length == 0) { size_t len = 0, allocated = 0; while ((c = getc(f)) != EOF) { if (!GREEDY_REALLOC(r, allocated, len+3)) { free(r); return -ENOMEM; } if (isprint(c)) { if (space) { r[len++] = ' '; space = false; } r[len++] = c; } else if (len > 0) space = true; } if (len > 0) r[len] = 0; else r = mfree(r); } else { bool dotdotdot = false; size_t left; r = new(char, max_length); if (!r) return -ENOMEM; k = r; left = max_length; while ((c = getc(f)) != EOF) { if (isprint(c)) { if (space) { if (left <= 2) { dotdotdot = true; break; } *(k++) = ' '; left--; space = false; } if (left <= 1) { dotdotdot = true; break; } *(k++) = (char) c; left--; } else if (k > r) space = true; } if (dotdotdot) { if (max_length <= 4) { k = r; left = max_length; } else { k = r + max_length - 4; left = 4; /* Eat up final spaces */ while (k > r && isspace(k[-1])) { k--; left++; } } strncpy(k, "...", left-1); k[left-1] = 0; } else *k = 0; } /* Kernel threads have no argv[] */ if (isempty(r)) { _cleanup_free_ char *t = NULL; int h; free(r); if (!comm_fallback) return -ENOENT; h = get_process_comm(pid, &t); if (h < 0) return h; if (max_length == 0) r = strjoin("[", t, "]", NULL); else { size_t l; l = strlen(t); if (l + 3 <= max_length) r = strjoin("[", t, "]", NULL); else if (max_length <= 6) { r = new(char, max_length); if (!r) return -ENOMEM; memcpy(r, "[...]", max_length-1); r[max_length-1] = 0; } else { char *e; t[max_length - 6] = 0; /* Chop off final spaces */ e = strchr(t, 0); while (e > t && isspace(e[-1])) e--; *e = 0; r = strjoin("[", t, "...]", NULL); } } if (!r) return -ENOMEM; } *line = r; return 0; } int getenv_for_pid(pid_t pid, const char *field, char **_value) { _cleanup_fclose_ FILE *f = NULL; char *value = NULL; int r; bool done = false; size_t l; const char *path; assert(pid >= 0); assert(field); assert(_value); path = procfs_file_alloca(pid, "environ"); f = fopen(path, "re"); if (!f) { if (errno == ENOENT) return -ESRCH; return -errno; } l = strlen(field); r = 0; do { char line[LINE_MAX]; unsigned i; for (i = 0; i < sizeof(line)-1; i++) { int c; c = getc(f); if (_unlikely_(c == EOF)) { done = true; break; } else if (c == 0) break; line[i] = c; } line[i] = 0; if (memcmp(line, field, l) == 0 && line[l] == '=') { value = strdup(line + l + 1); if (!value) return -ENOMEM; r = 1; break; } } while (!done); *_value = value; return r; } /* The cached PID, possible values: * * == UNSET [0] → cache not initialized yet * == BUSY [-1] → some thread is initializing it at the moment * any other → the cached PID */ #define CACHED_PID_UNSET ((pid_t) 0) #define CACHED_PID_BUSY ((pid_t) -1) static pid_t cached_pid = CACHED_PID_UNSET; void reset_cached_pid(void) { /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ cached_pid = CACHED_PID_UNSET; } /* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against * libpthread, as it is part of glibc anyway. */ extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void * __dso_handle); extern void* __dso_handle __attribute__ ((__weak__)); pid_t getpid_cached(void) { static bool installed = false; pid_t current_value; /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally * cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when * objects were used across fork()s. With this caching the old behaviour is somewhat restored. * * https://bugzilla.redhat.com/show_bug.cgi?id=1443976 * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e */ current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); switch (current_value) { case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ pid_t new_pid; new_pid = raw_getpid(); if (!installed) { /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's * only half-documented (glibc doesn't document it but LSB does — though only superficially) * we'll check for errors only in the most generic fashion possible. */ if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { /* OOM? Let's try again later */ cached_pid = CACHED_PID_UNSET; return new_pid; } installed = true; } cached_pid = new_pid; return new_pid; } case CACHED_PID_BUSY: /* Somebody else is currently initializing */ return raw_getpid(); default: /* Properly initialized */ return current_value; } } bool is_main_thread(void) { static thread_local int cached = 0; if (_unlikely_(cached == 0)) cached = getpid() == gettid() ? 1 : -1; return cached > 0; } static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", [CLD_KILLED] = "killed", [CLD_DUMPED] = "dumped", [CLD_TRAPPED] = "trapped", [CLD_STOPPED] = "stopped", [CLD_CONTINUED] = "continued", }; DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int); static const char* const sched_policy_table[] = { [SCHED_OTHER] = "other", [SCHED_BATCH] = "batch", [SCHED_IDLE] = "idle", [SCHED_FIFO] = "fifo", [SCHED_RR] = "rr" }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); systemd-netlogd-1.4.4/src/share/process-util.h000066400000000000000000000035621474012624500213500ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include #include #include #include #include "formats-util.h" #include "macro.h" #if !HAVE_GETTID static inline pid_t missing_gettid(void) { return (pid_t) syscall(__NR_gettid); } # define gettid missing_gettid #endif #define procfs_file_alloca(pid, field) \ ({ \ pid_t _pid_ = (pid); \ const char *_r_; \ if (_pid_ == 0) { \ _r_ = ("/proc/self/" field); \ } else { \ _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ } \ _r_; \ }) int get_process_comm(pid_t pid, char **name); int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); int get_process_uid(pid_t pid, uid_t *uid); int get_process_gid(pid_t pid, gid_t *gid); int getenv_for_pid(pid_t pid, const char *field, char **_value); void reset_cached_pid(void); pid_t getpid_cached(void); bool is_main_thread(void); const char *sigchld_code_to_string(int i) _const_; int sigchld_code_from_string(const char *s) _pure_; int sched_policy_to_string_alloc(int i, char **s); int sched_policy_from_string(const char *s); systemd-netlogd-1.4.4/src/share/random-util.c000066400000000000000000000070761474012624500211510ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_AUXV_H #include #endif #if USE_SYS_RANDOM_H # include #else # include #endif #include "fd-util.h" #include "io-util.h" #include "process-util.h" #include "missing.h" #include "random-util.h" #include "time-util.h" int dev_urandom(void *p, size_t n) { static int have_syscall = -1; _cleanup_close_ int fd = -1; int r; /* Gathers some randomness from the kernel. This call will * never block, and will always return some data from the * kernel, regardless if the random pool is fully initialized * or not. It thus makes no guarantee for the quality of the * returned entropy, but is good enough for our usual usecases * of seeding the hash functions for hashtable */ /* Use the getrandom() syscall unless we know we don't have * it, or when the requested size is too large for it. */ if (have_syscall != 0 || (size_t) (int) n != n) { r = getrandom(p, n, GRND_NONBLOCK); if (r == (int) n) { have_syscall = true; return 0; } if (r < 0) { if (errno == ENOSYS) /* we lack the syscall, continue with * reading from /dev/urandom */ have_syscall = false; else if (errno == EAGAIN) /* not enough entropy for now. Let's * remember to use the syscall the * next time, again, but also read * from /dev/urandom for now, which * doesn't care about the current * amount of entropy. */ have_syscall = true; else return -errno; } else /* too short read? */ return -ENODATA; } fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) return errno == ENOENT ? -ENOSYS : -errno; return loop_read_exact(fd, p, n, true); } void initialize_srand(void) { static bool srand_called = false; unsigned x; #ifdef HAVE_SYS_AUXV_H void *auxv; #endif if (srand_called) return; #ifdef HAVE_SYS_AUXV_H /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the * pseudo-random generator. It's better than nothing... */ auxv = (void*) getauxval(AT_RANDOM); if (auxv) { assert_cc(sizeof(x) < 16); memcpy(&x, auxv, sizeof(x)); } else #endif x = 0; x ^= (unsigned) now(CLOCK_REALTIME); x ^= (unsigned) gettid(); srand(x); srand_called = true; } void random_bytes(void *p, size_t n) { uint8_t *q; int r; r = dev_urandom(p, n); if (r >= 0) return; /* If some idiot made /dev/urandom unavailable to us, he'll * get a PRNG instead. */ initialize_srand(); for (q = p; q < (uint8_t*) p + n; q ++) *q = rand(); } systemd-netlogd-1.4.4/src/share/random-util.h000066400000000000000000000007001474012624500211410ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include int dev_urandom(void *p, size_t n); void random_bytes(void *p, size_t n); void initialize_srand(void); static inline uint64_t random_u64(void) { uint64_t u; random_bytes(&u, sizeof(u)); return u; } static inline uint32_t random_u32(void) { uint32_t u; random_bytes(&u, sizeof(u)); return u; } systemd-netlogd-1.4.4/src/share/ratelimit.c000066400000000000000000000025401474012624500206770ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include "macro.h" #include "ratelimit.h" /* Modelled after Linux' lib/ratelimit.c by Dave Young * , which is licensed GPLv2. */ bool ratelimit_below(RateLimit *r) { usec_t ts; assert(r); if (!ratelimit_configured(r)) return true; ts = now(CLOCK_MONOTONIC); if (r->begin <= 0 || usec_sub_unsigned(ts, r->begin) > r->interval) { r->begin = ts; /* Start a new time window */ r->num = 1; /* Reset counter */ return true; } if (_unlikely_(r->num == UINT_MAX)) return false; r->num++; return r->num <= r->burst; } unsigned ratelimit_num_dropped(RateLimit *r) { assert(r); if (r->num == UINT_MAX) /* overflow, return as special case */ return UINT_MAX; return LESS_BY(r->num, r->burst); } usec_t ratelimit_end(const RateLimit *rl) { assert(rl); if (rl->begin == 0) return 0; return usec_add(rl->begin, rl->interval); } usec_t ratelimit_left(const RateLimit *rl) { assert(rl); if (rl->begin == 0) return 0; return usec_sub_unsigned(ratelimit_end(rl), now(CLOCK_MONOTONIC)); } systemd-netlogd-1.4.4/src/share/ratelimit.h000066400000000000000000000014661474012624500207120ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include "time-util.h" typedef struct RateLimit { usec_t interval; /* Keep those two fields first so they can be initialized easily: */ unsigned burst; /* RateLimit rl = { INTERVAL, BURST }; */ unsigned num; usec_t begin; } RateLimit; #define RATELIMIT_OFF (const RateLimit) { .interval = USEC_INFINITY, .burst = UINT_MAX } static inline void ratelimit_reset(RateLimit *rl) { rl->num = rl->begin = 0; } static inline bool ratelimit_configured(const RateLimit *rl) { return rl->interval > 0 && rl->burst > 0; } bool ratelimit_below(RateLimit *r); unsigned ratelimit_num_dropped(RateLimit *r); usec_t ratelimit_end(const RateLimit *rl); usec_t ratelimit_left(const RateLimit *rl); systemd-netlogd-1.4.4/src/share/sd-network.c000066400000000000000000000242121474012624500210020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include "sd-network.h" #include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "macro.h" #include "parse-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "util.h" _public_ int sd_network_get_operational_state(char **state) { _cleanup_free_ char *s = NULL; int r; assert_return(state, -EINVAL); r = parse_env_file("/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) return r; if (isempty(s)) return -ENODATA; *state = s; s = NULL; return 0; } static int network_get_strv(const char *key, char ***ret) { _cleanup_strv_free_ char **a = NULL; _cleanup_free_ char *s = NULL; int r; assert_return(ret, -EINVAL); r = parse_env_file("/run/systemd/netif/state", NEWLINE, key, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) return r; if (isempty(s)) { *ret = NULL; return 0; } a = strv_split(s, " "); if (!a) return -ENOMEM; strv_uniq(a); r = strv_length(a); *ret = a; a = NULL; return r; } _public_ int sd_network_get_dns(char ***ret) { return network_get_strv("DNS", ret); } _public_ int sd_network_get_ntp(char ***ret) { return network_get_strv("NTP", ret); } _public_ int sd_network_get_search_domains(char ***ret) { return network_get_strv("DOMAINS", ret); } _public_ int sd_network_get_route_domains(char ***ret) { return network_get_strv("ROUTE_DOMAINS", ret); } static int network_link_get_string(int ifindex, const char *field, char **ret) { char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; _cleanup_free_ char *s = NULL; int r; assert_return(ifindex > 0, -EINVAL); assert_return(ret, -EINVAL); xsprintf(path, "/run/systemd/netif/links/%i", ifindex); r = parse_env_file(path, NEWLINE, field, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) return r; if (isempty(s)) return -ENODATA; *ret = s; s = NULL; return 0; } static int network_link_get_strv(int ifindex, const char *key, char ***ret) { char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; _cleanup_strv_free_ char **a = NULL; _cleanup_free_ char *s = NULL; int r; assert_return(ifindex > 0, -EINVAL); assert_return(ret, -EINVAL); xsprintf(path, "/run/systemd/netif/links/%i", ifindex); r = parse_env_file(path, NEWLINE, key, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) return r; if (isempty(s)) { *ret = NULL; return 0; } a = strv_split(s, " "); if (!a) return -ENOMEM; strv_uniq(a); r = strv_length(a); *ret = a; a = NULL; return r; } _public_ int sd_network_link_get_setup_state(int ifindex, char **state) { return network_link_get_string(ifindex, "ADMIN_STATE", state); } _public_ int sd_network_link_get_network_file(int ifindex, char **filename) { return network_link_get_string(ifindex, "NETWORK_FILE", filename); } _public_ int sd_network_link_get_operational_state(int ifindex, char **state) { return network_link_get_string(ifindex, "OPER_STATE", state); } _public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) { return network_link_get_string(ifindex, "LLMNR", llmnr); } _public_ int sd_network_link_get_mdns(int ifindex, char **mdns) { return network_link_get_string(ifindex, "MDNS", mdns); } _public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) { return network_link_get_string(ifindex, "DNSSEC", dnssec); } _public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) { return network_link_get_strv(ifindex, "DNSSEC_NTA", nta); } _public_ int sd_network_link_get_timezone(int ifindex, char **ret) { return network_link_get_string(ifindex, "TIMEZONE", ret); } _public_ int sd_network_link_get_dns(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "DNS", ret); } _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "NTP", ret); } _public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "DOMAINS", ret); } _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); } static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; _cleanup_free_ int *ifis = NULL; _cleanup_free_ char *s = NULL; size_t allocated = 0, c = 0; const char *x; int r; assert_return(ifindex > 0, -EINVAL); assert_return(ret, -EINVAL); xsprintf(path, "/run/systemd/netif/links/%i", ifindex); r = parse_env_file(path, NEWLINE, key, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) return r; if (isempty(s)) { *ret = NULL; return 0; } x = s; for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&x, &word, NULL, 0); if (r < 0) return r; if (r == 0) break; r = parse_ifindex(word, &ifindex); if (r < 0) return r; if (!GREEDY_REALLOC(ifis, allocated, c + 1)) return -ENOMEM; ifis[c++] = ifindex; } if (!GREEDY_REALLOC(ifis, allocated, c + 1)) return -ENOMEM; ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ *ret = ifis; ifis = NULL; return c; } _public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) { return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret); } _public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) { return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret); } static inline int MONITOR_TO_FD(sd_network_monitor *m) { return (int) (unsigned long) m - 1; } static inline sd_network_monitor* FD_TO_MONITOR(int fd) { return (sd_network_monitor*) (unsigned long) (fd + 1); } static int monitor_add_inotify_watch(int fd) { int k; k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE); if (k >= 0) return 0; else if (errno != ENOENT) return -errno; k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR); if (k >= 0) return 0; else if (errno != ENOENT) return -errno; k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR); if (k < 0) return -errno; return 0; } _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) { _cleanup_close_ int fd = -1; int k; bool good = false; assert_return(m, -EINVAL); fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); if (fd < 0) return -errno; if (!category || streq(category, "links")) { k = monitor_add_inotify_watch(fd); if (k < 0) return k; good = true; } if (!good) return -EINVAL; *m = FD_TO_MONITOR(fd); fd = -1; return 0; } _public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) { int fd; if (m) { fd = MONITOR_TO_FD(m); close_nointr(fd); } return NULL; } _public_ int sd_network_monitor_flush(sd_network_monitor *m) { union inotify_event_buffer buffer; struct inotify_event *e; ssize_t l; int fd, k; assert_return(m, -EINVAL); fd = MONITOR_TO_FD(m); l = read(fd, &buffer, sizeof(buffer)); if (l < 0) { if (errno == EAGAIN || errno == EINTR) return 0; return -errno; } FOREACH_INOTIFY_EVENT(e, buffer, l) { if (e->mask & IN_ISDIR) { k = monitor_add_inotify_watch(fd); if (k < 0) return k; k = inotify_rm_watch(fd, e->wd); if (k < 0) return -errno; } } return 0; } _public_ int sd_network_monitor_get_fd(sd_network_monitor *m) { assert_return(m, -EINVAL); return MONITOR_TO_FD(m); } _public_ int sd_network_monitor_get_events(sd_network_monitor *m) { assert_return(m, -EINVAL); /* For now we will only return POLLIN here, since we don't * need anything else ever for inotify. However, let's have * this API to keep our options open should we later on need * it. */ return POLLIN; } _public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) { assert_return(m, -EINVAL); assert_return(timeout_usec, -EINVAL); /* For now we will only return (uint64_t) -1, since we don't * need any timeout. However, let's have this API to keep our * options open should we later on need it. */ *timeout_usec = (uint64_t) -1; return 0; } systemd-netlogd-1.4.4/src/share/sd-network.h000066400000000000000000000127611474012624500210150ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef foosdnetworkhfoo #define foosdnetworkhfoo #include #include #include /* * A few points: * * Instead of returning an empty string array or empty integer array, we * may return NULL. * * Free the data the library returns with libc free(). String arrays * are NULL terminated, and you need to free the array itself in * addition to the strings contained. * * We return error codes as negative errno, kernel-style. On success, we * return 0 or positive. * * These functions access data in /run. This is a virtual file system; * therefore, accesses are relatively cheap. * * See sd-network(3) for more information. */ _SD_BEGIN_DECLARATIONS; /* Get overall operational state * Possible states: down, up, dormant, carrier, degraded, routable * Possible return codes: * -ENODATA: networkd is not aware of any links */ int sd_network_get_operational_state(char **state); /* Get DNS entries for all links. These are string representations of * IP addresses */ int sd_network_get_dns(char ***dns); /* Get NTP entries for all links. These are domain names or string * representations of IP addresses */ int sd_network_get_ntp(char ***ntp); /* Get the search domains for all links. */ int sd_network_get_search_domains(char ***domains); /* Get the search domains for all links. */ int sd_network_get_route_domains(char ***domains); /* Get setup state from ifindex. * Possible states: * pending: udev is still processing the link, we don't yet know if we will manage it * failed: networkd failed to manage the link * configuring: in the process of retrieving configuration or configuring the link * configured: link configured successfully * unmanaged: networkd is not handling the link * linger: the link is gone, but has not yet been dropped by networkd * Possible return codes: * -ENODATA: networkd is not aware of the link */ int sd_network_link_get_setup_state(int ifindex, char **state); /* Get operational state from ifindex. * Possible states: * off: the device is powered down * no-carrier: the device is powered up, but it does not yet have a carrier * dormant: the device has a carrier, but is not yet ready for normal traffic * carrier: the link has a carrier * degraded: the link has carrier and addresses valid on the local link configured * routable: the link has carrier and routable address configured * Possible return codes: * -ENODATA: networkd is not aware of the link */ int sd_network_link_get_operational_state(int ifindex, char **state); /* Get path to .network file applied to link */ int sd_network_link_get_network_file(int ifindex, char **filename); /* Get DNS entries for a given link. These are string representations of * IP addresses */ int sd_network_link_get_dns(int ifindex, char ***ret); /* Get NTP entries for a given link. These are domain names or string * representations of IP addresses */ int sd_network_link_get_ntp(int ifindex, char ***ret); /* Indicates whether or not LLMNR should be enabled for the link * Possible levels of support: yes, no, resolve * Possible return codes: * -ENODATA: networkd is not aware of the link */ int sd_network_link_get_llmnr(int ifindex, char **llmnr); /* Indicates whether or not MulticastDNS should be enabled for the * link. * Possible levels of support: yes, no, resolve * Possible return codes: * -ENODATA: networkd is not aware of the link */ int sd_network_link_get_mdns(int ifindex, char **mdns); /* Indicates whether or not DNSSEC should be enabled for the link * Possible levels of support: yes, no, allow-downgrade * Possible return codes: * -ENODATA: networkd is not aware of the link */ int sd_network_link_get_dnssec(int ifindex, char **dnssec); /* Returns the list of per-interface DNSSEC negative trust anchors * Possible return codes: * -ENODATA: networkd is not aware of the link, or has no such data */ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta); /* Get the search DNS domain names for a given link. */ int sd_network_link_get_search_domains(int ifindex, char ***domains); /* Get the route DNS domain names for a given link. */ int sd_network_link_get_route_domains(int ifindex, char ***domains); /* Get the carrier interface indexes to which current link is bound to. */ int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes); /* Get the CARRIERS that are bound to current link. */ int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes); /* Get the timezone that was learnt on a specific link. */ int sd_network_link_get_timezone(int ifindex, char **timezone); /* Monitor object */ typedef struct sd_network_monitor sd_network_monitor; /* Create a new monitor. Category must be NULL, "links" or "leases". */ int sd_network_monitor_new(sd_network_monitor **ret, const char *category); /* Destroys the passed monitor. Returns NULL. */ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m); /* Flushes the monitor */ int sd_network_monitor_flush(sd_network_monitor *m); /* Get FD from monitor */ int sd_network_monitor_get_fd(sd_network_monitor *m); /* Get poll() mask to monitor */ int sd_network_monitor_get_events(sd_network_monitor *m); /* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_network_monitor, sd_network_monitor_unref); _SD_END_DECLARATIONS; #endif systemd-netlogd-1.4.4/src/share/sd-resolve.c000066400000000000000000001051271474012624500207750ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ /*** ***/ #include #include #include #include #include #include #include #include #include #include #include #include "sd-resolve.h" #include "alloc-util.h" #include "dns-domain.h" #include "fd-util.h" #include "io-util.h" #include "list.h" #include "missing.h" #include "socket-util.h" #include "util.h" #include "process-util.h" #define WORKERS_MIN 1U #define WORKERS_MAX 16U #define QUERIES_MAX 256U #define BUFSIZE 10240U typedef enum { REQUEST_ADDRINFO, RESPONSE_ADDRINFO, REQUEST_NAMEINFO, RESPONSE_NAMEINFO, REQUEST_TERMINATE, RESPONSE_DIED } QueryType; enum { REQUEST_RECV_FD, REQUEST_SEND_FD, RESPONSE_RECV_FD, RESPONSE_SEND_FD, _FD_MAX }; struct sd_resolve { unsigned n_ref; bool dead:1; pid_t original_pid; int fds[_FD_MAX]; pthread_t workers[WORKERS_MAX]; unsigned n_valid_workers; unsigned current_id; sd_resolve_query* query_array[QUERIES_MAX]; unsigned n_queries, n_done, n_outstanding; sd_event_source *event_source; sd_event *event; sd_resolve_query *current; sd_resolve **default_resolve_ptr; pid_t tid; LIST_HEAD(sd_resolve_query, queries); }; struct sd_resolve_query { unsigned n_ref; sd_resolve *resolve; QueryType type:4; bool done:1; bool floating:1; unsigned id; int ret; int _errno; int _h_errno; struct addrinfo *addrinfo; char *serv, *host; union { sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; sd_resolve_getnameinfo_handler_t getnameinfo_handler; }; void *userdata; LIST_FIELDS(sd_resolve_query, queries); }; typedef struct RHeader { QueryType type; unsigned id; size_t length; } RHeader; typedef struct AddrInfoRequest { struct RHeader header; bool hints_valid; int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t node_len, service_len; } AddrInfoRequest; typedef struct AddrInfoResponse { struct RHeader header; int ret; int _errno; int _h_errno; /* followed by addrinfo_serialization[] */ } AddrInfoResponse; typedef struct AddrInfoSerialization { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; size_t canonname_len; /* Followed by ai_addr amd ai_canonname with variable lengths */ } AddrInfoSerialization; typedef struct NameInfoRequest { struct RHeader header; int flags; socklen_t sockaddr_len; bool gethost:1, getserv:1; } NameInfoRequest; typedef struct NameInfoResponse { struct RHeader header; size_t hostlen, servlen; int ret; int _errno; int _h_errno; } NameInfoResponse; typedef union Packet { RHeader rheader; AddrInfoRequest addrinfo_request; AddrInfoResponse addrinfo_response; NameInfoRequest nameinfo_request; NameInfoResponse nameinfo_response; } Packet; static int getaddrinfo_done(sd_resolve_query* q); static int getnameinfo_done(sd_resolve_query *q); static void resolve_query_disconnect(sd_resolve_query *q); #define RESOLVE_DONT_DESTROY(resolve) \ _cleanup_(sd_resolve_unrefp) _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve) static void query_assign_errno(sd_resolve_query *q, int ret, int error, int h_error) { assert(q); q->ret = ret; q->_errno = abs(error); q->_h_errno = h_error; } static int send_died(int out_fd) { RHeader rh = { .type = RESPONSE_DIED, .length = sizeof(RHeader), }; assert(out_fd >= 0); if (send(out_fd, &rh, rh.length, MSG_NOSIGNAL) < 0) return -errno; return 0; } static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) { AddrInfoSerialization s; size_t cnl, l; assert(p); assert(ai); assert(length); assert(*length <= maxlength); cnl = ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0; l = sizeof(AddrInfoSerialization) + ai->ai_addrlen + cnl; if (*length + l > maxlength) return NULL; s.ai_flags = ai->ai_flags; s.ai_family = ai->ai_family; s.ai_socktype = ai->ai_socktype; s.ai_protocol = ai->ai_protocol; s.ai_addrlen = ai->ai_addrlen; s.canonname_len = cnl; memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization)); memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen); memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, ai->ai_canonname, cnl); *length += l; return (uint8_t*) p + l; } static int send_addrinfo_reply( int out_fd, unsigned id, int ret, struct addrinfo *ai, int _errno, int _h_errno) { AddrInfoResponse resp = { .header.type = RESPONSE_ADDRINFO, .header.id = id, .header.length = sizeof(AddrInfoResponse), .ret = ret, ._errno = _errno, ._h_errno = _h_errno, }; union { AddrInfoSerialization ais; uint8_t space[BUFSIZE]; } buffer; struct iovec iov[2]; struct msghdr mh; assert(out_fd >= 0); if (ret == 0 && ai) { void *p = &buffer; struct addrinfo *k; for (k = ai; k; k = k->ai_next) { p = serialize_addrinfo(p, k, &resp.header.length, (uint8_t*) &buffer + BUFSIZE - (uint8_t*) p); if (!p) { freeaddrinfo(ai); return -ENOBUFS; } } } if (ai) freeaddrinfo(ai); iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(AddrInfoResponse) }; iov[1] = (struct iovec) { .iov_base = &buffer, .iov_len = resp.header.length - sizeof(AddrInfoResponse) }; mh = (struct msghdr) { .msg_iov = iov, .msg_iovlen = ELEMENTSOF(iov) }; if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) return -errno; return 0; } static int send_nameinfo_reply( int out_fd, unsigned id, int ret, const char *host, const char *serv, int _errno, int _h_errno) { NameInfoResponse resp = { .header.type = RESPONSE_NAMEINFO, .header.id = id, .ret = ret, ._errno = _errno, ._h_errno = _h_errno, }; struct iovec iov[3]; struct msghdr mh; size_t hl, sl; assert(out_fd >= 0); sl = serv ? strlen(serv)+1 : 0; hl = host ? strlen(host)+1 : 0; resp.header.length = sizeof(NameInfoResponse) + hl + sl; resp.hostlen = hl; resp.servlen = sl; iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) }; iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl }; iov[2] = (struct iovec) { .iov_base = (void*) serv, .iov_len = sl }; mh = (struct msghdr) { .msg_iov = iov, .msg_iovlen = ELEMENTSOF(iov) }; if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) return -errno; return 0; } static int handle_request(int out_fd, const Packet *packet, size_t length) { const RHeader *req; assert(out_fd >= 0); assert(packet); req = &packet->rheader; assert_return(length >= sizeof(RHeader), -EIO); assert_return(length == req->length, -EIO); switch (req->type) { case REQUEST_ADDRINFO: { const AddrInfoRequest *ai_req = &packet->addrinfo_request; struct addrinfo hints, *result = NULL; const char *node, *service; int ret; assert_return(length >= sizeof(AddrInfoRequest), -EBADMSG); assert_return(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len, -EBADMSG); hints = (struct addrinfo) { .ai_flags = ai_req->ai_flags, .ai_family = ai_req->ai_family, .ai_socktype = ai_req->ai_socktype, .ai_protocol = ai_req->ai_protocol, }; node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL; service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL; ret = getaddrinfo(node, service, ai_req->hints_valid ? &hints : NULL, &result); /* send_addrinfo_reply() frees result */ return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno); } case REQUEST_NAMEINFO: { const NameInfoRequest *ni_req = &packet->nameinfo_request; char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; union sockaddr_union sa; int ret; assert_return(length >= sizeof(NameInfoRequest), -EBADMSG); assert_return(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len, -EBADMSG); assert_return(ni_req->sockaddr_len <= sizeof(sa), -EBADMSG); memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len); ret = getnameinfo(&sa.sa, ni_req->sockaddr_len, ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0, ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0, ni_req->flags); return send_nameinfo_reply(out_fd, req->id, ret, ret == 0 && ni_req->gethost ? hostbuf : NULL, ret == 0 && ni_req->getserv ? servbuf : NULL, errno, h_errno); } case REQUEST_TERMINATE: /* Quit */ return -ECONNRESET; default: assert_not_reached("Unknown request"); } return 0; } static void* thread_worker(void *p) { sd_resolve *resolve = p; /* Assign a pretty name to this thread */ (void) pthread_setname_np(pthread_self(), "sd-resolve"); while (!resolve->dead) { union { Packet packet; uint8_t space[BUFSIZE]; } buf; ssize_t length; length = recv(resolve->fds[REQUEST_RECV_FD], &buf, sizeof buf, 0); if (length < 0) { if (errno == EINTR) continue; break; } if (length == 0) break; if (handle_request(resolve->fds[RESPONSE_SEND_FD], &buf.packet, (size_t) length) < 0) break; } send_died(resolve->fds[RESPONSE_SEND_FD]); return NULL; } static int start_threads(sd_resolve *resolve, unsigned extra) { sigset_t ss, saved_ss; unsigned n; int r, k; if (sigfillset(&ss) < 0) return -errno; /* No signals in forked off threads please. We set the mask before forking, so that the threads never exist * with a different mask than a fully blocked one */ r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); if (r > 0) return -r; n = resolve->n_outstanding + extra; n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); while (resolve->n_valid_workers < n) { r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); if (r > 0) { r = -r; goto finish; } resolve->n_valid_workers++; } r = 0; finish: k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); if (k > 0 && r >= 0) r = -k; return r; } static bool resolve_pid_changed(sd_resolve *r) { assert(r); /* We don't support people creating a resolver and keeping it * around after fork(). Let's complain. */ return r->original_pid != getpid_cached(); } _public_ int sd_resolve_new(sd_resolve **ret) { _cleanup_(sd_resolve_unrefp) sd_resolve *resolve = NULL; int i; assert_return(ret, -EINVAL); resolve = new0(sd_resolve, 1); if (!resolve) return -ENOMEM; resolve->n_ref = 1; resolve->original_pid = getpid_cached(); for (i = 0; i < _FD_MAX; i++) resolve->fds[i] = -1; if (socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD) < 0) return -errno; if (socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_RECV_FD) < 0) return -errno; for (i = 0; i < _FD_MAX; i++) resolve->fds[i] = fd_move_above_stdio(resolve->fds[i]); (void) fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); (void) fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); (void) fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); (void) fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); (void) fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); *ret = TAKE_PTR(resolve); return 0; } _public_ int sd_resolve_default(sd_resolve **ret) { static thread_local sd_resolve *default_resolve = NULL; sd_resolve *e = NULL; int r; if (!ret) return !!default_resolve; if (default_resolve) { *ret = sd_resolve_ref(default_resolve); return 0; } r = sd_resolve_new(&e); if (r < 0) return r; e->default_resolve_ptr = &default_resolve; e->tid = gettid(); default_resolve = e; *ret = e; return 1; } _public_ int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) { assert_return(resolve, -EINVAL); assert_return(tid, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); if (resolve->tid != 0) { *tid = resolve->tid; return 0; } if (resolve->event) return sd_event_get_tid(resolve->event, tid); return -ENXIO; } static void resolve_free(sd_resolve *resolve) { PROTECT_ERRNO; sd_resolve_query *q; unsigned i; assert(resolve); while ((q = resolve->queries)) { assert(q->floating); resolve_query_disconnect(q); sd_resolve_query_unref(q); } if (resolve->default_resolve_ptr) *(resolve->default_resolve_ptr) = NULL; resolve->dead = true; sd_resolve_detach_event(resolve); if (resolve->fds[REQUEST_SEND_FD] >= 0) { RHeader req = { .type = REQUEST_TERMINATE, .length = sizeof req, }; /* Send one termination packet for each worker */ for (i = 0; i < resolve->n_valid_workers; i++) (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); } /* Now terminate them and wait until they are gone. If we get an error than most likely the thread already exited. */ for (i = 0; i < resolve->n_valid_workers; i++) (void) pthread_join(resolve->workers[i], NULL); /* Close all communication channels */ close_many(resolve->fds, _FD_MAX); free(resolve); } _public_ sd_resolve* sd_resolve_ref(sd_resolve *resolve) { assert_return(resolve, NULL); assert(resolve->n_ref >= 1); resolve->n_ref++; return resolve; } _public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) { if (!resolve) return NULL; assert(resolve->n_ref >= 1); resolve->n_ref--; if (resolve->n_ref <= 0) resolve_free(resolve); return NULL; } _public_ int sd_resolve_get_fd(sd_resolve *resolve) { assert_return(resolve, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); return resolve->fds[RESPONSE_RECV_FD]; } _public_ int sd_resolve_get_events(sd_resolve *resolve) { assert_return(resolve, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); return resolve->n_queries > resolve->n_done ? POLLIN : 0; } _public_ int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *usec) { assert_return(resolve, -EINVAL); assert_return(usec, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); *usec = (uint64_t) -1; return 0; } static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { sd_resolve_query *q; assert(resolve); q = resolve->query_array[id % QUERIES_MAX]; if (q) if (q->id == id) return q; return NULL; } static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { int r; assert(q); assert(!q->done); assert(q->resolve == resolve); q->done = true; resolve->n_done++; resolve->current = sd_resolve_query_ref(q); switch (q->type) { case REQUEST_ADDRINFO: r = getaddrinfo_done(q); break; case REQUEST_NAMEINFO: r = getnameinfo_done(q); break; default: assert_not_reached("Cannot complete unknown query type"); } resolve->current = NULL; if (q->floating) { resolve_query_disconnect(q); sd_resolve_query_unref(q); } sd_resolve_query_unref(q); return r; } static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) { AddrInfoSerialization s; size_t l; struct addrinfo *ai; assert(p); assert(*p); assert(ret_ai); assert(length); if (*length < sizeof(AddrInfoSerialization)) return -EBADMSG; memcpy(&s, *p, sizeof(s)); l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len; if (*length < l) return -EBADMSG; ai = new0(struct addrinfo, 1); if (!ai) return -ENOMEM; ai->ai_flags = s.ai_flags; ai->ai_family = s.ai_family; ai->ai_socktype = s.ai_socktype; ai->ai_protocol = s.ai_protocol; ai->ai_addrlen = s.ai_addrlen; if (s.ai_addrlen > 0) { ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen); if (!ai->ai_addr) { free(ai); return -ENOMEM; } } if (s.canonname_len > 0) { ai->ai_canonname = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); if (!ai->ai_canonname) { free(ai->ai_addr); free(ai); return -ENOMEM; } } *length -= l; *ret_ai = ai; *p = ((const uint8_t*) *p) + l; return 0; } static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) { const RHeader *resp; sd_resolve_query *q; int r; assert(resolve); assert(packet); resp = &packet->rheader; assert_return(length >= sizeof(RHeader), -EIO); assert_return(length == resp->length, -EIO); if (resp->type == RESPONSE_DIED) { resolve->dead = true; return 0; } assert(resolve->n_outstanding > 0); resolve->n_outstanding--; q = lookup_query(resolve, resp->id); if (!q) return 0; switch (resp->type) { case RESPONSE_ADDRINFO: { const AddrInfoResponse *ai_resp = &packet->addrinfo_response; const void *p; size_t l; struct addrinfo *prev = NULL; assert_return(length >= sizeof(AddrInfoResponse), -EBADMSG); assert_return(q->type == REQUEST_ADDRINFO, -EBADMSG); query_assign_errno(q, ai_resp->ret, ai_resp->_errno, ai_resp->_h_errno); l = length - sizeof(AddrInfoResponse); p = (const uint8_t*) resp + sizeof(AddrInfoResponse); while (l > 0 && p) { struct addrinfo *ai = NULL; r = unserialize_addrinfo(&p, &l, &ai); if (r < 0) { query_assign_errno(q, EAI_SYSTEM, r, 0); freeaddrinfo(q->addrinfo); q->addrinfo = NULL; break; } if (prev) prev->ai_next = ai; else q->addrinfo = ai; prev = ai; } return complete_query(resolve, q); } case RESPONSE_NAMEINFO: { const NameInfoResponse *ni_resp = &packet->nameinfo_response; assert_return(length >= sizeof(NameInfoResponse), -EBADMSG); assert_return(q->type == REQUEST_NAMEINFO, -EBADMSG); if (ni_resp->hostlen > DNS_HOSTNAME_MAX || ni_resp->servlen > DNS_HOSTNAME_MAX || sizeof(NameInfoResponse) + ni_resp->hostlen + ni_resp->servlen > length) query_assign_errno(q, EAI_SYSTEM, EIO, 0); else { query_assign_errno(q, ni_resp->ret, ni_resp->_errno, ni_resp->_h_errno); if (ni_resp->hostlen > 0) { q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1); if (!q->host) query_assign_errno(q, EAI_MEMORY, ENOMEM, 0); } if (ni_resp->servlen > 0) { q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1); if (!q->serv) query_assign_errno(q, EAI_MEMORY, ENOMEM, 0); } } return complete_query(resolve, q); } default: return 0; } } _public_ int sd_resolve_process(sd_resolve *resolve) { RESOLVE_DONT_DESTROY(resolve); union { Packet packet; uint8_t space[BUFSIZE]; } buf; ssize_t l; int r; assert_return(resolve, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); /* We don't allow recursively invoking sd_resolve_process(). */ assert_return(!resolve->current, -EBUSY); l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof buf, 0); if (l < 0) { if (errno == EAGAIN) return 0; return -errno; } if (l == 0) return -ECONNREFUSED; r = handle_response(resolve, &buf.packet, (size_t) l); if (r < 0) return r; return 1; } _public_ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { int r; assert_return(resolve, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); if (resolve->n_done >= resolve->n_queries) return 0; do { r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); } while (r == -EINTR); if (r < 0) return r; if (r == 0) return -ETIMEDOUT; return sd_resolve_process(resolve); } static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q) { sd_resolve_query *q; int r; assert(resolve); assert(_q); if (resolve->n_queries >= QUERIES_MAX) return -ENOBUFS; r = start_threads(resolve, 1); if (r < 0) return r; while (resolve->query_array[resolve->current_id % QUERIES_MAX]) resolve->current_id++; q = resolve->query_array[resolve->current_id % QUERIES_MAX] = new0(sd_resolve_query, 1); if (!q) return -ENOMEM; q->n_ref = 1; q->resolve = resolve; q->floating = floating; q->id = resolve->current_id++; if (!floating) sd_resolve_ref(resolve); LIST_PREPEND(queries, resolve->queries, q); resolve->n_queries++; *_q = q; return 0; } _public_ int sd_resolve_getaddrinfo( sd_resolve *resolve, sd_resolve_query **_q, const char *node, const char *service, const struct addrinfo *hints, sd_resolve_getaddrinfo_handler_t callback, void *userdata) { _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL; AddrInfoRequest req; struct iovec iov[3]; struct msghdr mh = {}; int r; size_t node_len, service_len; assert_return(resolve, -EINVAL); assert_return(node || service, -EINVAL); assert_return(callback, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); r = alloc_query(resolve, !_q, &q); if (r < 0) return r; q->type = REQUEST_ADDRINFO; q->getaddrinfo_handler = callback; q->userdata = userdata; node_len = node ? strlen(node) + 1 : 0; service_len = service ? strlen(service) + 1 : 0; req = (AddrInfoRequest) { .node_len = node_len, .service_len = service_len, .header.id = q->id, .header.type = REQUEST_ADDRINFO, .header.length = sizeof(AddrInfoRequest) + node_len + service_len, .hints_valid = hints, .ai_flags = hints ? hints->ai_flags : 0, .ai_family = hints ? hints->ai_family : 0, .ai_socktype = hints ? hints->ai_socktype : 0, .ai_protocol = hints ? hints->ai_protocol : 0, }; iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(AddrInfoRequest) }; if (node) iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) node, .iov_len = req.node_len }; if (service) iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) service, .iov_len = req.service_len }; mh.msg_iov = iov; if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) return -errno; resolve->n_outstanding++; if (_q) *_q = q; TAKE_PTR(q); return 0; } static int getaddrinfo_done(sd_resolve_query* q) { assert(q); assert(q->done); assert(q->getaddrinfo_handler); errno = q->_errno; h_errno = q->_h_errno; return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata); } _public_ int sd_resolve_getnameinfo( sd_resolve *resolve, sd_resolve_query**_q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata) { _cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL; NameInfoRequest req; struct iovec iov[2]; struct msghdr mh; int r; assert_return(resolve, -EINVAL); assert_return(sa, -EINVAL); assert_return(salen >= sizeof(struct sockaddr), -EINVAL); assert_return(salen <= sizeof(union sockaddr_union), -EINVAL); assert_return((get & ~SD_RESOLVE_GET_BOTH) == 0, -EINVAL); assert_return(callback, -EINVAL); assert_return(!resolve_pid_changed(resolve), -ECHILD); r = alloc_query(resolve, !_q, &q); if (r < 0) return r; q->type = REQUEST_NAMEINFO; q->getnameinfo_handler = callback; q->userdata = userdata; req = (NameInfoRequest) { .header.id = q->id, .header.type = REQUEST_NAMEINFO, .header.length = sizeof(NameInfoRequest) + salen, .flags = flags, .sockaddr_len = salen, .gethost = !!(get & SD_RESOLVE_GET_HOST), .getserv = !!(get & SD_RESOLVE_GET_SERVICE), }; iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) }; iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen }; mh = (struct msghdr) { .msg_iov = iov, .msg_iovlen = ELEMENTSOF(iov) }; if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) return -errno; if (_q) *_q = q; resolve->n_outstanding++; TAKE_PTR(q); return 0; } static int getnameinfo_done(sd_resolve_query *q) { assert(q); assert(q->done); assert(q->getnameinfo_handler); errno = q->_errno; h_errno = q->_h_errno; return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); } _public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) { assert_return(q, NULL); assert(q->n_ref >= 1); q->n_ref++; return q; } static void resolve_freeaddrinfo(struct addrinfo *ai) { while (ai) { struct addrinfo *next = ai->ai_next; free(ai->ai_addr); free(ai->ai_canonname); free(ai); ai = next; } } static void resolve_query_disconnect(sd_resolve_query *q) { sd_resolve *resolve; unsigned i; assert(q); if (!q->resolve) return; resolve = q->resolve; assert(resolve->n_queries > 0); if (q->done) { assert(resolve->n_done > 0); resolve->n_done--; } i = q->id % QUERIES_MAX; assert(resolve->query_array[i] == q); resolve->query_array[i] = NULL; LIST_REMOVE(queries, resolve->queries, q); resolve->n_queries--; q->resolve = NULL; if (!q->floating) sd_resolve_unref(resolve); } static void resolve_query_free(sd_resolve_query *q) { assert(q); resolve_query_disconnect(q); resolve_freeaddrinfo(q->addrinfo); free(q->host); free(q->serv); free(q); } _public_ sd_resolve_query* sd_resolve_query_unref(sd_resolve_query* q) { if (!q) return NULL; assert(q->n_ref >= 1); q->n_ref--; if (q->n_ref <= 0) resolve_query_free(q); return NULL; } _public_ int sd_resolve_query_is_done(sd_resolve_query *q) { assert_return(q, -EINVAL); assert_return(!resolve_pid_changed(q->resolve), -ECHILD); return q->done; } _public_ void* sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata) { void *ret; assert_return(q, NULL); assert_return(!resolve_pid_changed(q->resolve), NULL); ret = q->userdata; q->userdata = userdata; return ret; } _public_ void* sd_resolve_query_get_userdata(sd_resolve_query *q) { assert_return(q, NULL); assert_return(!resolve_pid_changed(q->resolve), NULL); return q->userdata; } _public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { assert_return(q, NULL); assert_return(!resolve_pid_changed(q->resolve), NULL); return q->resolve; } static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_resolve *resolve = userdata; int r; assert(resolve); r = sd_resolve_process(resolve); if (r < 0) return r; return 1; } _public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int64_t priority) { int r; assert_return(resolve, -EINVAL); assert_return(!resolve->event, -EBUSY); assert(!resolve->event_source); if (event) resolve->event = sd_event_ref(event); else { r = sd_event_default(&resolve->event); if (r < 0) return r; } r = sd_event_add_io(resolve->event, &resolve->event_source, resolve->fds[RESPONSE_RECV_FD], POLLIN, io_callback, resolve); if (r < 0) goto fail; r = sd_event_source_set_priority(resolve->event_source, priority); if (r < 0) goto fail; return 0; fail: sd_resolve_detach_event(resolve); return r; } _public_ int sd_resolve_detach_event(sd_resolve *resolve) { assert_return(resolve, -EINVAL); if (!resolve->event) return 0; if (resolve->event_source) { sd_event_source_set_enabled(resolve->event_source, SD_EVENT_OFF); resolve->event_source = sd_event_source_unref(resolve->event_source); } resolve->event = sd_event_unref(resolve->event); return 1; } _public_ sd_event *sd_resolve_get_event(sd_resolve *resolve) { assert_return(resolve, NULL); return resolve->event; } systemd-netlogd-1.4.4/src/share/sd-resolve.h000066400000000000000000000110011474012624500207650ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef foosdresolvehfoo #define foosdresolvehfoo /*** systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ #include #include #include #include #include #include _SD_BEGIN_DECLARATIONS; /* An opaque sd-resolve session structure */ typedef struct sd_resolve sd_resolve; /* An opaque sd-resolve query structure */ typedef struct sd_resolve_query sd_resolve_query; /* A callback on completion */ typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata); typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata); enum { SD_RESOLVE_GET_HOST = 1 << 0, SD_RESOLVE_GET_SERVICE = 1 << 1, SD_RESOLVE_GET_BOTH = SD_RESOLVE_GET_HOST | SD_RESOLVE_GET_SERVICE, }; int sd_resolve_default(sd_resolve **ret); /* Allocate a new sd-resolve session. */ int sd_resolve_new(sd_resolve **ret); /* Free a sd-resolve session. This destroys all attached * sd_resolve_query objects automatically. */ sd_resolve* sd_resolve_unref(sd_resolve *resolve); sd_resolve* sd_resolve_ref(sd_resolve *resolve); /* Return the UNIX file descriptor to poll() for events on. Use this * function to integrate sd-resolve with your custom main loop. */ int sd_resolve_get_fd(sd_resolve *resolve); /* Return the poll() events (a combination of flags like POLLIN, * POLLOUT, ...) to check for. */ int sd_resolve_get_events(sd_resolve *resolve); /* Return the poll() timeout to pass. Returns (uint64_t) -1 as * timeout if no timeout is needed. */ int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *timeout_usec); /* Process pending responses. After this function is called, you can * get the next completed query object(s) using * sd_resolve_get_next(). */ int sd_resolve_process(sd_resolve *resolve); /* Wait for a resolve event to complete. */ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec); int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid); int sd_resolve_attach_event(sd_resolve *resolve, sd_event *e, int64_t priority); int sd_resolve_detach_event(sd_resolve *resolve); sd_event *sd_resolve_get_event(sd_resolve *resolve); /* Issue a name-to-address query on the specified session. The * arguments are compatible with those of libc's * getaddrinfo(3). The function returns a new query object. When the * query is completed, you may retrieve the results using * sd_resolve_getaddrinfo_done(). */ int sd_resolve_getaddrinfo(sd_resolve *resolve, sd_resolve_query **q, const char *node, const char *service, const struct addrinfo *hints, sd_resolve_getaddrinfo_handler_t callback, void *userdata); /* Issue an address-to-name query on the specified session. The * arguments are compatible with those of libc's * getnameinfo(3). The function returns a new query object. When the * query is completed, you may retrieve the results using * sd_resolve_getnameinfo_done(). Set gethost (resp. getserv) to non-zero * if you want to query the hostname (resp. the service name). */ int sd_resolve_getnameinfo(sd_resolve *resolve, sd_resolve_query **q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata); sd_resolve_query *sd_resolve_query_ref(sd_resolve_query* q); sd_resolve_query *sd_resolve_query_unref(sd_resolve_query* q); /* Returns non-zero when the query operation specified by q has been completed. */ int sd_resolve_query_is_done(sd_resolve_query*q); void *sd_resolve_query_get_userdata(sd_resolve_query *q); void *sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata); sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_resolve, sd_resolve_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_resolve_query, sd_resolve_query_unref); _SD_END_DECLARATIONS; #endif systemd-netlogd-1.4.4/src/share/set.h000066400000000000000000000067211474012624500175120ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "extract-word.h" #include "hashmap.h" #include "macro.h" Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { internal_hashmap_free(HASHMAP_BASE(s)); return NULL; } static inline Set *set_free_free(Set *s) { internal_hashmap_free_free(HASHMAP_BASE(s)); return NULL; } /* no set_free_free_free */ static inline Set *set_copy(Set *s) { return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); } int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int set_put(Set *s, const void *key); /* no set_update */ /* no set_replace */ static inline void *set_get(Set *s, void *key) { return internal_hashmap_get(HASHMAP_BASE(s), key); } /* no set_get2 */ static inline bool set_contains(Set *s, const void *key) { return internal_hashmap_contains(HASHMAP_BASE(s), key); } static inline void *set_remove(Set *s, const void *key) { return internal_hashmap_remove(HASHMAP_BASE(s), key); } /* no set_remove2 */ /* no set_remove_value */ int set_remove_and_put(Set *s, const void *old_key, const void *new_key); /* no set_remove_and_replace */ int set_merge(Set *s, Set *other); static inline int set_reserve(Set *h, unsigned entries_add) { return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int set_move(Set *s, Set *other) { return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); } static inline int set_move_one(Set *s, Set *other, const void *key) { return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); } static inline unsigned set_size(Set *s) { return internal_hashmap_size(HASHMAP_BASE(s)); } static inline bool set_isempty(Set *s) { return set_size(s) == 0; } static inline unsigned set_buckets(Set *s) { return internal_hashmap_buckets(HASHMAP_BASE(s)); } bool set_iterate(Set *s, Iterator *i, void **value); static inline void set_clear(Set *s) { internal_hashmap_clear(HASHMAP_BASE(s)); } static inline void set_clear_free(Set *s) { internal_hashmap_clear_free(HASHMAP_BASE(s)); } /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { return internal_hashmap_steal_first(HASHMAP_BASE(s)); } /* no set_steal_first_key */ /* no set_first_key */ static inline void *set_first(Set *s) { return internal_hashmap_first(HASHMAP_BASE(s)); } /* no set_next */ static inline char **set_get_strv(Set *s) { return internal_hashmap_get_strv(HASHMAP_BASE(s)); } int set_consume(Set *s, void *value); int set_put_strdup(Set *s, const char *p); int set_put_strdupv(Set *s, char **l); int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); #define SET_FOREACH(e, s, i) \ for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) #define SET_FOREACH_MOVE(e, d, s) \ for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); #define _cleanup_set_free_ _cleanup_(set_freep) #define _cleanup_set_free_free_ _cleanup_(set_free_freep) systemd-netlogd-1.4.4/src/share/signal-util.c000066400000000000000000000060151474012624500211360ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include "macro.h" #include "parse-util.h" #include "signal-util.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" static int sigset_add_many_ap(sigset_t *ss, va_list ap) { int sig, r = 0; assert(ss); while ((sig = va_arg(ap, int)) >= 0) { if (sig == 0) continue; if (sigaddset(ss, sig) < 0) { if (r >= 0) r = -errno; } } return r; } int sigprocmask_many(int how, sigset_t *old, ...) { va_list ap; sigset_t ss; int r; if (sigemptyset(&ss) < 0) return -errno; va_start(ap, old); r = sigset_add_many_ap(&ss, ap); va_end(ap); if (r < 0) return r; if (sigprocmask(how, &ss, old) < 0) return -errno; return 0; } static const char *const __signal_table[] = { [SIGHUP] = "HUP", [SIGINT] = "INT", [SIGQUIT] = "QUIT", [SIGILL] = "ILL", [SIGTRAP] = "TRAP", [SIGABRT] = "ABRT", [SIGBUS] = "BUS", [SIGFPE] = "FPE", [SIGKILL] = "KILL", [SIGUSR1] = "USR1", [SIGSEGV] = "SEGV", [SIGUSR2] = "USR2", [SIGPIPE] = "PIPE", [SIGALRM] = "ALRM", [SIGTERM] = "TERM", #ifdef SIGSTKFLT [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ #endif [SIGCHLD] = "CHLD", [SIGCONT] = "CONT", [SIGSTOP] = "STOP", [SIGTSTP] = "TSTP", [SIGTTIN] = "TTIN", [SIGTTOU] = "TTOU", [SIGURG] = "URG", [SIGXCPU] = "XCPU", [SIGXFSZ] = "XFSZ", [SIGVTALRM] = "VTALRM", [SIGPROF] = "PROF", [SIGWINCH] = "WINCH", [SIGIO] = "IO", [SIGPWR] = "PWR", [SIGSYS] = "SYS" }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); const char *signal_to_string(int signo) { static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1]; const char *name; name = __signal_to_string(signo); if (name) return name; if (signo >= SIGRTMIN && signo <= SIGRTMAX) xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN); else xsprintf(buf, "%d", signo); return buf; } int signal_from_string(const char *s) { int signo; int offset = 0; unsigned u; signo = __signal_from_string(s); if (signo > 0) return signo; if (startswith(s, "RTMIN+")) { s += 6; offset = SIGRTMIN; } if (safe_atou(s, &u) >= 0) { signo = (int) u + offset; if (SIGNAL_VALID(signo)) return signo; } return -EINVAL; } void nop_signal_handler(int sig) { /* nothing here */ } systemd-netlogd-1.4.4/src/share/signal-util.h000066400000000000000000000016331474012624500211440ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include "macro.h" int sigprocmask_many(int how, sigset_t *old, ...); const char *signal_to_string(int i) _const_; int signal_from_string(const char *s) _pure_; void nop_signal_handler(int sig); static inline void block_signals_reset(sigset_t *ss) { assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); } #define BLOCK_SIGNALS(...) \ _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ sigset_t t; \ assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \ t; \ }) static inline bool SIGNAL_VALID(int signo) { return signo > 0 && signo < _NSIG; } systemd-netlogd-1.4.4/src/share/siphash24.c000066400000000000000000000163021474012624500205130ustar00rootroot00000000000000/* SipHash reference C implementation Written in 2012 by Jean-Philippe Aumasson Daniel J. Bernstein To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd) (Refactored by Tom Gundersen to split up in several functions and follow systemd coding style) */ #include #include "macro.h" #include "siphash24.h" #include "unaligned.h" static inline uint64_t rotate_left(uint64_t x, uint8_t b) { assert(b < 64); return (x << b) | (x >> (64 - b)); } static inline void sipround(struct siphash *state) { assert(state); state->v0 += state->v1; state->v1 = rotate_left(state->v1, 13); state->v1 ^= state->v0; state->v0 = rotate_left(state->v0, 32); state->v2 += state->v3; state->v3 = rotate_left(state->v3, 16); state->v3 ^= state->v2; state->v0 += state->v3; state->v3 = rotate_left(state->v3, 21); state->v3 ^= state->v0; state->v2 += state->v1; state->v1 = rotate_left(state->v1, 17); state->v1 ^= state->v2; state->v2 = rotate_left(state->v2, 32); } void siphash24_init(struct siphash *state, const uint8_t k[16]) { uint64_t k0, k1; assert(state); assert(k); k0 = unaligned_read_le64(k); k1 = unaligned_read_le64(k + 8); *state = (struct siphash) { /* "somepseudorandomlygeneratedbytes" */ .v0 = 0x736f6d6570736575ULL ^ k0, .v1 = 0x646f72616e646f6dULL ^ k1, .v2 = 0x6c7967656e657261ULL ^ k0, .v3 = 0x7465646279746573ULL ^ k1, .padding = 0, .inlen = 0, }; } void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { const uint8_t *in = _in; const uint8_t *end = in + inlen; size_t left = state->inlen & 7; uint64_t m; assert(in); assert(state); /* Update total length */ state->inlen += inlen; /* If padding exists, fill it out */ if (left > 0) { for ( ; in < end && left < 8; in ++, left ++) state->padding |= ((uint64_t) *in) << (left * 8); if (in == end && left < 8) /* We did not have enough input to fill out the padding completely */ return; #ifdef DEBUG printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding); #endif state->v3 ^= state->padding; sipround(state); sipround(state); state->v0 ^= state->padding; state->padding = 0; } end -= (state->inlen % sizeof(uint64_t)); for ( ; in < end; in += 8) { m = unaligned_read_le64(in); #ifdef DEBUG printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m); #endif state->v3 ^= m; sipround(state); sipround(state); state->v0 ^= m; } left = state->inlen & 7; switch (left) { case 7: state->padding |= ((uint64_t) in[6]) << 48; /* fall through */ case 6: state->padding |= ((uint64_t) in[5]) << 40; /* fall through */ case 5: state->padding |= ((uint64_t) in[4]) << 32; /* fall through */ case 4: state->padding |= ((uint64_t) in[3]) << 24; /* fall through */ case 3: state->padding |= ((uint64_t) in[2]) << 16; /* fall through */ case 2: state->padding |= ((uint64_t) in[1]) << 8; /* fall through */ case 1: state->padding |= ((uint64_t) in[0]); /* fall through */ case 0: break; } } uint64_t siphash24_finalize(struct siphash *state) { uint64_t b; assert(state); b = state->padding | (((uint64_t) state->inlen) << 56); #ifdef DEBUG printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); #endif state->v3 ^= b; sipround(state); sipround(state); state->v0 ^= b; #ifdef DEBUG printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); #endif state->v2 ^= 0xff; sipround(state); sipround(state); sipround(state); sipround(state); return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; } uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]) { struct siphash state; assert(in); assert(k); siphash24_init(&state, k); siphash24_compress(in, inlen, &state); return siphash24_finalize(&state); } systemd-netlogd-1.4.4/src/share/siphash24.h000066400000000000000000000011631474012624500205170ustar00rootroot00000000000000#pragma once #include #include #include #include struct siphash { uint64_t v0; uint64_t v1; uint64_t v2; uint64_t v3; uint64_t padding; size_t inlen; }; void siphash24_init(struct siphash *state, const uint8_t k[16]); void siphash24_compress(const void *in, size_t inlen, struct siphash *state); #define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) uint64_t siphash24_finalize(struct siphash *state); uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); systemd-netlogd-1.4.4/src/share/socket-util.c000066400000000000000000000250131474012624500211500ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "formats-util.h" #include "in-addr-util.h" #include "log.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" #include "user-util.h" #include "utf8.h" #include "util.h" int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; unsigned u; int r; assert(a); assert(s); zero(*a); a->type = SOCK_STREAM; if (*s == '[') { /* IPv6 in [x:.....:z]:p notation */ e = strchr(s+1, ']'); if (!e) return -EINVAL; n = strndupa(s+1, e-s-1); errno = 0; if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) return errno > 0 ? -errno : -EINVAL; e++; if (*e != ':') return -EINVAL; e++; r = safe_atou(e, &u); if (r < 0) return r; if (u <= 0 || u > 0xFFFF) return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); a->size = sizeof(struct sockaddr_in6); } else if (*s == '/') { /* AF_UNIX socket */ size_t l; l = strlen(s); if (l >= sizeof(a->sockaddr.un.sun_path)) return -EINVAL; a->sockaddr.un.sun_family = AF_UNIX; memcpy(a->sockaddr.un.sun_path, s, l); a->size = offsetof(struct sockaddr_un, sun_path) + l + 1; } else if (*s == '@') { /* Abstract AF_UNIX socket */ size_t l; l = strlen(s+1); if (l >= sizeof(a->sockaddr.un.sun_path) - 1) return -EINVAL; a->sockaddr.un.sun_family = AF_UNIX; memcpy(a->sockaddr.un.sun_path+1, s+1, l); a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l; } else { e = strchr(s, ':'); if (e) { r = safe_atou(e+1, &u); if (r < 0) return r; if (u <= 0 || u > 0xFFFF) return -EINVAL; n = strndupa(s, e-s); /* IPv4 in w.x.y.z:p notation? */ r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); if (r < 0) return -errno; if (r > 0) { /* Gotcha, it's a traditional IPv4 address */ a->sockaddr.in.sin_family = AF_INET; a->sockaddr.in.sin_port = htobe16((uint16_t)u); a->size = sizeof(struct sockaddr_in); } else { unsigned idx; if (strlen(n) > IF_NAMESIZE-1) return -EINVAL; /* Uh, our last resort, an interface name */ idx = if_nametoindex(n); if (idx == 0) return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); a->sockaddr.in6.sin6_scope_id = idx; a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); } } else { /* Just a port */ r = safe_atou(s, &u); if (r < 0) return r; if (u <= 0 || u > 0xFFFF) return -EINVAL; if (socket_ipv6_is_supported()) { a->sockaddr.in6.sin6_family = AF_INET6; a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); } else { a->sockaddr.in.sin_family = AF_INET; a->sockaddr.in.sin_port = htobe16((uint16_t)u); a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; a->size = sizeof(struct sockaddr_in); } } } return 0; } bool socket_ipv6_is_supported(void) { if (access("/proc/net/sockstat6", F_OK) != 0) return false; return true; } int fd_inc_sndbuf(int fd, size_t n) { int r, value; socklen_t l = sizeof(value); r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) return 0; /* If we have the privileges we will ignore the kernel limit. */ value = (int) n; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) return -errno; return 1; } int fd_inc_rcvbuf(int fd, size_t n) { int r, value; socklen_t l = sizeof(value); r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) return 0; /* If we have the privileges we will ignore the kernel limit. */ value = (int) n; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) return -errno; return 1; } int sockaddr_pretty( const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { union sockaddr_union *sa = (union sockaddr_union*) _sa; char *p; int r; assert(sa); assert(salen >= sizeof(sa->sa.sa_family)); switch (sa->sa.sa_family) { case AF_INET: { uint32_t a; a = be32toh(sa->in.sin_addr.s_addr); if (include_port) r = asprintf(&p, "%u.%u.%u.%u:%u", a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, be16toh(sa->in.sin_port)); else r = asprintf(&p, "%u.%u.%u.%u", a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); if (r < 0) return -ENOMEM; break; } case AF_INET6: { static const unsigned char ipv4_prefix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }; if (translate_ipv6 && memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { const uint8_t *a = sa->in6.sin6_addr.s6_addr+12; if (include_port) r = asprintf(&p, "%u.%u.%u.%u:%u", a[0], a[1], a[2], a[3], be16toh(sa->in6.sin6_port)); else r = asprintf(&p, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); if (r < 0) return -ENOMEM; } else { char a[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); if (include_port) { r = asprintf(&p, "[%s]:%u", a, be16toh(sa->in6.sin6_port)); if (r < 0) return -ENOMEM; } else { p = strdup(a); if (!p) return -ENOMEM; } } break; } default: return -EOPNOTSUPP; } *ret = p; return 0; } int fd_set_sndbuf(int fd, size_t n, bool increase) { int r, value; socklen_t l = sizeof(value); if (n > INT_MAX) return -ERANGE; r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) return 0; /* First, try to set the buffer size with SO_SNDBUF. */ r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n); if (r < 0) return r; /* SO_SNDBUF above may set to the kernel limit, instead of the requested size. * So, we need to check the actual buffer size here. */ l = sizeof(value); r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) return 1; /* If we have the privileges we will ignore the kernel limit. */ r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); if (r < 0) return r; return 1; } systemd-netlogd-1.4.4/src/share/socket-util.h000066400000000000000000000066241474012624500211640ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include #include #include #include #include "macro.h" #include "util.h" union sockaddr_union { struct sockaddr sa; struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr_nl nl; struct sockaddr_storage storage; struct sockaddr_ll ll; }; typedef struct SocketAddress { union sockaddr_union sockaddr; /* We store the size here explicitly due to the weird * sockaddr_un semantics for abstract sockets */ socklen_t size; /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ int type; /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */ int protocol; } SocketAddress; typedef enum SocketAddressBindIPv6Only { SOCKET_ADDRESS_DEFAULT, SOCKET_ADDRESS_BOTH, SOCKET_ADDRESS_IPV6_ONLY, _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 } SocketAddressBindIPv6Only; #define socket_address_family(a) ((a)->sockaddr.sa.sa_family) int socket_address_parse(SocketAddress *a, const char *s); int socket_address_listen( const SocketAddress *a, int flags, int backlog, SocketAddressBindIPv6Only only, const char *bind_to_device, bool reuse_port, bool free_bind, bool transparent, mode_t directory_mode, mode_t socket_mode, const char *label); bool socket_ipv6_is_supported(void); int fd_inc_sndbuf(int fd, size_t n); int fd_inc_rcvbuf(int fd, size_t n); #define CMSG_FOREACH(cmsg, mh) \ for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) /* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ #define SOCKADDR_UN_LEN(sa) \ ({ \ const struct sockaddr_un *_sa = &(sa); \ assert(_sa->sun_family == AF_UNIX); \ offsetof(struct sockaddr_un, sun_path) + \ (_sa->sun_path[0] == 0 ? \ 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ }) int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); static inline int setsockopt_int(int fd, int level, int optname, int value) { if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) return -errno; return 0; } static inline int getsockopt_int(int fd, int level, int optname, int *ret) { int v; socklen_t sl = sizeof(v); if (getsockopt(fd, level, optname, &v, &sl) < 0) return negative_errno(); if (sl != sizeof(v)) return -EIO; *ret = v; return 0; } int fd_set_sndbuf(int fd, size_t n, bool increase); systemd-netlogd-1.4.4/src/share/sparse-endian.h000066400000000000000000000066321474012624500214510ustar00rootroot00000000000000/* Copyright (c) 2012 Josh Triplett * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef SPARSE_ENDIAN_H #define SPARSE_ENDIAN_H #include #include #include #ifdef __CHECKER__ #define __bitwise __attribute__((bitwise)) #define __force __attribute__((force)) #else #define __bitwise #define __force #endif typedef uint16_t __bitwise le16_t; typedef uint16_t __bitwise be16_t; typedef uint32_t __bitwise le32_t; typedef uint32_t __bitwise be32_t; typedef uint64_t __bitwise le64_t; typedef uint64_t __bitwise be64_t; #undef htobe16 #undef htole16 #undef be16toh #undef le16toh #undef htobe32 #undef htole32 #undef be32toh #undef le32toh #undef htobe64 #undef htole64 #undef be64toh #undef le64toh #if __BYTE_ORDER == __LITTLE_ENDIAN #define bswap_16_on_le(x) __bswap_16(x) #define bswap_32_on_le(x) __bswap_32(x) #define bswap_64_on_le(x) __bswap_64(x) #define bswap_16_on_be(x) (x) #define bswap_32_on_be(x) (x) #define bswap_64_on_be(x) (x) #elif __BYTE_ORDER == __BIG_ENDIAN #define bswap_16_on_le(x) (x) #define bswap_32_on_le(x) (x) #define bswap_64_on_le(x) (x) #define bswap_16_on_be(x) __bswap_16(x) #define bswap_32_on_be(x) __bswap_32(x) #define bswap_64_on_be(x) __bswap_64(x) #endif static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); } static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); } static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); } static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); } static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); } static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); } static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); } static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); } static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); } static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); } static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); } static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); } #endif /* SPARSE_ENDIAN_H */ systemd-netlogd-1.4.4/src/share/stat-util.c000066400000000000000000000033171474012624500206360ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include "dirent-util.h" #include "fd-util.h" #include "macro.h" #include "missing.h" #include "stat-util.h" #include "string-util.h" int is_dir(const char* path, bool follow) { struct stat st; int r; assert(path); if (follow) r = stat(path, &st); else r = lstat(path, &st); if (r < 0) return -errno; return !!S_ISDIR(st.st_mode); } bool null_or_empty(const struct stat *st) { assert(st); if (S_ISREG(st->st_mode) && st->st_size <= 0) return true; /* We don't want to hardcode the major/minor of /dev/null, * hence we do a simpler "is this a device node?" check. */ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) return true; return false; } int files_same(const char *filea, const char *fileb) { struct stat a, b; assert(filea); assert(fileb); if (stat(filea, &a) < 0) return -errno; if (stat(fileb, &b) < 0) return -errno; return a.st_dev == b.st_dev && a.st_ino == b.st_ino; } bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { assert(s); assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); return F_TYPE_EQUAL(s->f_type, magic_value); } bool is_temporary_fs(const struct statfs *s) { return is_fs_type(s, TMPFS_MAGIC) || is_fs_type(s, RAMFS_MAGIC); } systemd-netlogd-1.4.4/src/share/stat-util.h000066400000000000000000000016521474012624500206430ustar00rootroot00000000000000#pragma once /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "macro.h" int is_dir(const char *path, bool follow); bool null_or_empty(const struct stat *st) _pure_; int files_same(const char *filea, const char *fileb); /* The .f_type field of struct statfs is really weird defined on * different archs. Let's give its type a name. */ typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value); bool is_temporary_fs(const struct statfs *s) _pure_; /* Because statfs.t_type can be int on some architectures, we have to cast * the const magic to the type, otherwise the compiler warns about * signed/unsigned comparison, because the magic can be 32 bit unsigned. */ #define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b) systemd-netlogd-1.4.4/src/share/stdio-util.h000066400000000000000000000067501474012624500210160ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "macro.h" #define xsprintf(buf, fmt, ...) \ assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), "xsprintf: " #buf "[] must be big enough") #define VA_FORMAT_ADVANCE(format, ap) \ do { \ int _argtypes[128]; \ size_t _i, _k; \ _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \ assert(_k < ELEMENTSOF(_argtypes)); \ for (_i = 0; _i < _k; _i++) { \ if (_argtypes[_i] & PA_FLAG_PTR) { \ (void) va_arg(ap, void*); \ continue; \ } \ \ switch (_argtypes[_i]) { \ case PA_INT: \ case PA_INT|PA_FLAG_SHORT: \ case PA_CHAR: \ (void) va_arg(ap, int); \ break; \ case PA_INT|PA_FLAG_LONG: \ (void) va_arg(ap, long int); \ break; \ case PA_INT|PA_FLAG_LONG_LONG: \ (void) va_arg(ap, long long int); \ break; \ case PA_WCHAR: \ (void) va_arg(ap, wchar_t); \ break; \ case PA_WSTRING: \ case PA_STRING: \ case PA_POINTER: \ (void) va_arg(ap, void*); \ break; \ case PA_FLOAT: \ case PA_DOUBLE: \ (void) va_arg(ap, double); \ break; \ case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \ (void) va_arg(ap, long double); \ break; \ default: \ assert_not_reached("Unknown format string argument."); \ } \ } \ } while (false) systemd-netlogd-1.4.4/src/share/string-table.c000066400000000000000000000006171474012624500213030ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "string-table.h" #include "string-util.h" ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { size_t i; if (!key) return -1; for (i = 0; i < len; ++i) if (streq_ptr(table[i], key)) return (ssize_t) i; return -1; } systemd-netlogd-1.4.4/src/share/string-table.h000066400000000000000000000132111474012624500213020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include "macro.h" #include "parse-util.h" #include "string-util.h" ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); /* For basic lookup tables with strictly enumerated entries */ #define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ scope const char *name##_to_string(type i) { \ if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ return NULL; \ return name##_table[i]; \ } #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ scope type name##_from_string(const char *s) { \ return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ } #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ scope type name##_from_string(const char *s) { \ int b; \ if (!s) \ return -1; \ b = parse_boolean(s); \ if (b == 0) \ return (type) 0; \ else if (b > 0) \ return yes; \ return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ } #define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \ scope int name##_to_string_alloc(type i, char **str) { \ char *s; \ if (i < 0 || i > max) \ return -ERANGE; \ if (i < (type) ELEMENTSOF(name##_table)) { \ s = strdup(name##_table[i]); \ if (!s) \ return -ENOMEM; \ } else { \ if (asprintf(&s, "%i", i) < 0) \ return -ENOMEM; \ } \ *str = s; \ return 0; \ } #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ type name##_from_string(const char *s) { \ type i; \ unsigned u = 0; \ if (!s) \ return (type) -1; \ for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \ if (streq_ptr(name##_table[i], s)) \ return i; \ if (safe_atou(s, &u) >= 0 && u <= max) \ return (type) u; \ return (type) -1; \ } \ #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ struct __useless_struct_to_allow_trailing_semicolon__ #define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ struct __useless_struct_to_allow_trailing_semicolon__ #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) #define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) /* For string conversions where numbers are also acceptable */ #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \ struct __useless_struct_to_allow_trailing_semicolon__ #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) systemd-netlogd-1.4.4/src/share/string-util.c000066400000000000000000000170731474012624500211750ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include "alloc-util.h" #include "macro.h" #include "string-util.h" #include "utf8.h" #include "util.h" int strcmp_ptr(const char *a, const char *b) { /* Like strcmp(), but tries to make sense of NULL pointers */ if (a && b) return strcmp(a, b); if (!a && b) return -1; if (a && !b) return 1; return 0; } char* endswith(const char *s, const char *postfix) { size_t sl, pl; assert(s); assert(postfix); sl = strlen(s); pl = strlen(postfix); if (pl == 0) return (char*) s + sl; if (sl < pl) return NULL; if (memcmp(s + sl - pl, postfix, pl) != 0) return NULL; return (char*) s + sl - pl; } char* endswith_no_case(const char *s, const char *postfix) { size_t sl, pl; assert(s); assert(postfix); sl = strlen(s); pl = strlen(postfix); if (pl == 0) return (char*) s + sl; if (sl < pl) return NULL; if (strcasecmp(s + sl - pl, postfix) != 0) return NULL; return (char*) s + sl - pl; } char* first_word(const char *s, const char *word) { size_t sl, wl; const char *p; assert(s); assert(word); /* Checks if the string starts with the specified word, either * followed by NUL or by whitespace. Returns a pointer to the * NUL or the first character after the whitespace. */ sl = strlen(s); wl = strlen(word); if (sl < wl) return NULL; if (wl == 0) return (char*) s; if (memcmp(s, word, wl) != 0) return NULL; p = s + wl; if (*p == 0) return (char*) p; if (!strchr(WHITESPACE, *p)) return NULL; p += strspn(p, WHITESPACE); return (char*) p; } static size_t strcspn_escaped(const char *s, const char *reject) { bool escaped = false; int n; for (n=0; s[n]; n++) { if (escaped) escaped = false; else if (s[n] == '\\') escaped = true; else if (strchr(reject, s[n])) break; } /* if s ends in \, return index of previous char */ return n - escaped; } /* Split a string into words. */ const char* split(const char **state, size_t *l, const char *separator, bool quoted) { const char *current; current = *state; if (!*current) { assert(**state == '\0'); return NULL; } current += strspn(current, separator); if (!*current) { *state = current; return NULL; } if (quoted && strchr("\'\"", *current)) { char quotechars[2] = {*current, '\0'}; *l = strcspn_escaped(current + 1, quotechars); if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || (current[*l + 2] && !strchr(separator, current[*l + 2]))) { /* right quote missing or garbage at the end */ *state = current; return NULL; } *state = current++ + *l + 2; } else if (quoted) { *l = strcspn_escaped(current, separator); if (current[*l] && !strchr(separator, current[*l])) { /* unfinished escape */ *state = current; return NULL; } *state = current + *l; } else { *l = strcspn(current, separator); *state = current + *l; } return current; } char *strnappend(const char *s, const char *suffix, size_t b) { size_t a; char *r; if (!s && !suffix) return strdup(""); if (!s) return strndup(suffix, b); if (!suffix) return strdup(s); assert(s); assert(suffix); a = strlen(s); if (b > ((size_t) -1) - a) return NULL; r = new(char, a+b+1); if (!r) return NULL; memcpy(r, s, a); memcpy(r+a, suffix, b); r[a+b] = 0; return r; } char *strappend(const char *s, const char *suffix) { return strnappend(s, suffix, suffix ? strlen(suffix) : 0); } char *strjoin(const char *x, ...) { va_list ap; size_t l; char *r, *p; va_start(ap, x); if (x) { l = strlen(x); for (;;) { const char *t; size_t n; t = va_arg(ap, const char *); if (!t) break; n = strlen(t); if (n > ((size_t) -1) - l) { va_end(ap); return NULL; } l += n; } } else l = 0; va_end(ap); r = new(char, l+1); if (!r) return NULL; if (x) { p = stpcpy(r, x); va_start(ap, x); for (;;) { const char *t; t = va_arg(ap, const char *); if (!t) break; p = stpcpy(p, t); } va_end(ap); } else r[0] = 0; return r; } char *strstrip(char *s) { char *e; /* Drops trailing whitespace. Modifies the string in * place. Returns pointer to first non-space character */ s += strspn(s, WHITESPACE); for (e = strchr(s, 0); e > s; e --) if (!strchr(WHITESPACE, e[-1])) break; *e = 0; return s; } char *truncate_nl(char *s) { assert(s); s[strcspn(s, NEWLINE)] = 0; return s; } char ascii_tolower(char x) { if (x >= 'A' && x <= 'Z') return x - 'A' + 'a'; return x; } bool nulstr_contains(const char*nulstr, const char *needle) { const char *i; if (!nulstr) return false; NULSTR_FOREACH(i, nulstr) if (streq(i, needle)) return true; return false; } #pragma GCC push_options #pragma GCC optimize("O0") void* memory_erase(void *p, size_t l) { volatile uint8_t* x = (volatile uint8_t*) p; /* This basically does what memset() does, but hopefully isn't * optimized away by the compiler. One of those days, when * glibc learns memset_s() we should replace this call by * memset_s(), but until then this has to do. */ for (; l > 0; l--) *(x++) = 'x'; return p; } #pragma GCC pop_options char* string_erase(char *x) { if (!x) return NULL; /* A delicious drop of snake-oil! To be called on memory where * we stored passphrases or so, after we used them. */ return memory_erase(x, strlen(x)); } char *string_free_erase(char *s) { return mfree(string_erase(s)); } systemd-netlogd-1.4.4/src/share/string-util.h000066400000000000000000000111771474012624500212010ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "macro.h" /* What is interpreted as whitespace? */ #define WHITESPACE " \t\n\r" #define NEWLINE "\n\r" #define QUOTES "\"\'" #define COMMENTS "#;" #define GLOB_CHARS "*?[" #define DIGITS "0123456789" #define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" #define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS #define ALPHANUMERICAL LETTERS DIGITS #define HEXDIGITS DIGITS "abcdefABCDEF" typedef char sd_char; #define streq(a,b) (strcmp((a),(b)) == 0) #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) #define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) int strcmp_ptr(const char *a, const char *b) _pure_; static inline bool streq_ptr(const char *a, const char *b) { return strcmp_ptr(a, b) == 0; } static inline const char* strempty(const char *s) { return s ? s : ""; } static inline const char* strnull(const char *s) { return s ? s : "(null)"; } static inline const char *strna(const char *s) { return s ? s : "n/a"; } static inline bool isempty(const char *p) { return !p || !p[0]; } static inline const char *empty_to_null(const char *p) { return isempty(p) ? NULL : p; } static inline char *startswith(const char *s, const char *prefix) { size_t l; l = strlen(prefix); if (strncmp(s, prefix, l) == 0) return (char*) s + l; return NULL; } static inline char *startswith_no_case(const char *s, const char *prefix) { size_t l; l = strlen(prefix); if (strncasecmp(s, prefix, l) == 0) return (char*) s + l; return NULL; } char *endswith(const char *s, const char *postfix) _pure_; char *endswith_no_case(const char *s, const char *postfix) _pure_; char *first_word(const char *s, const char *word) _pure_; const char* split(const char **state, size_t *l, const char *separator, bool quoted); #define FOREACH_WORD(word, length, s, state) \ _FOREACH_WORD(word, length, s, WHITESPACE, false, state) #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ _FOREACH_WORD(word, length, s, separator, false, state) #define FOREACH_WORD_QUOTED(word, length, s, state) \ _FOREACH_WORD(word, length, s, WHITESPACE, true, state) #define _FOREACH_WORD(word, length, s, separator, quoted, state) \ for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); char *strjoin(const char *x, ...) _sentinel_; #define strjoina(a, ...) \ ({ \ const char *_appendees_[] = { a, __VA_ARGS__ }; \ char *_d_, *_p_; \ int _len_ = 0; \ unsigned _i_; \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ _p_ = _d_ = alloca(_len_ + 1); \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _p_ = stpcpy(_p_, _appendees_[_i_]); \ *_p_ = 0; \ _d_; \ }) static inline bool ascii_isdigit(sd_char a) { /* A pure ASCII, locale independent version of isdigit() */ return a >= '0' && a <= '9'; } static inline bool ascii_isalpha(sd_char a) { /* A pure ASCII, locale independent version of isalpha() */ return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); } char *strstrip(char *s); char *truncate_nl(char *s); char ascii_tolower(char x); bool nulstr_contains(const char*nulstr, const char *needle); void* memory_erase(void *p, size_t l); char *string_erase(char *x); char *string_free_erase(char *s); DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase); #define _cleanup_string_free_erase_ _cleanup_(string_free_erasep) systemd-netlogd-1.4.4/src/share/strv.c000066400000000000000000000131011474012624500176760ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include "alloc-util.h" #include "escape.h" #include "extract-word.h" #include "fileio.h" #include "string-util.h" #include "strv.h" #include "util.h" char *strv_find(char **l, const char *name) { char **i; assert(name); STRV_FOREACH(i, l) if (streq(*i, name)) return *i; return NULL; } void strv_clear(char **l) { char **k; if (!l) return; for (k = l; *k; k++) free(*k); *l = NULL; } char **strv_free(char **l) { strv_clear(l); free(l); return NULL; } char **strv_free_erase(char **l) { char **i; STRV_FOREACH(i, l) string_erase(*i); return strv_free(l); } char **strv_copy(char * const *l) { char **r, **k; k = r = new(char*, strv_length(l) + 1); if (!r) return NULL; if (l) for (; *l; k++, l++) { *k = strdup(*l); if (!*k) { strv_free(r); return NULL; } } *k = NULL; return r; } unsigned strv_length(char * const *l) { unsigned n = 0; if (!l) return 0; for (; *l; l++) n++; return n; } char **strv_new_ap(const char *x, va_list ap) { const char *s; char **a; unsigned n = 0, i = 0; va_list aq; /* As a special trick we ignore all listed strings that equal * STRV_IGNORE. This is supposed to be used with the * STRV_IFNOTNULL() macro to include possibly NULL strings in * the string list. */ if (x) { n = x == STRV_IGNORE ? 0 : 1; va_copy(aq, ap); while ((s = va_arg(aq, const char*))) { if (s == STRV_IGNORE) continue; n++; } va_end(aq); } a = new(char*, n+1); if (!a) return NULL; if (x) { if (x != STRV_IGNORE) { a[i] = strdup(x); if (!a[i]) goto fail; i++; } while ((s = va_arg(ap, const char*))) { if (s == STRV_IGNORE) continue; a[i] = strdup(s); if (!a[i]) goto fail; i++; } } a[i] = NULL; return a; fail: strv_free(a); return NULL; } char **strv_new(const char *x, ...) { char **r; va_list ap; va_start(ap, x); r = strv_new_ap(x, ap); va_end(ap); return r; } char **strv_split(const char *s, const char *separator) { const char *word, *state; size_t l; unsigned n, i; char **r; assert(s); n = 0; FOREACH_WORD_SEPARATOR(word, l, s, separator, state) n++; r = new(char*, n+1); if (!r) return NULL; i = 0; FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { r[i] = strndup(word, l); if (!r[i]) { strv_free(r); return NULL; } i++; } r[i] = NULL; return r; } int strv_push(char ***l, char *value) { char **c; unsigned n, m; if (!value) return 0; n = strv_length(*l); /* Increase and check for overflow */ m = n + 2; if (m < n) return -ENOMEM; c = realloc_multiply(*l, m, sizeof(char*)); if (!c) return -ENOMEM; c[n] = value; c[n+1] = NULL; *l = c; return 0; } int strv_consume(char ***l, char *value) { int r; r = strv_push(l, value); if (r < 0) free(value); return r; } int strv_extend(char ***l, const char *value) { char *v; if (!value) return 0; v = strdup(value); if (!v) return -ENOMEM; return strv_consume(l, v); } char **strv_uniq(char **l) { char **i; /* Drops duplicate entries. The first identical string will be * kept, the others dropped */ STRV_FOREACH(i, l) strv_remove(i+1, *i); return l; } char **strv_remove(char **l, const char *s) { char **f, **t; if (!l) return NULL; assert(s); /* Drops every occurrence of s in the string list, edits * in-place. */ for (f = t = l; *f; f++) if (streq(*f, s)) free(*f); else *(t++) = *f; *t = NULL; return l; } char **strv_split_nulstr(const char *s) { const char *i; char **r = NULL; NULSTR_FOREACH(i, s) if (strv_extend(&r, i) < 0) { strv_free(r); return NULL; } if (!r) return strv_new(NULL, NULL); return r; } systemd-netlogd-1.4.4/src/share/strv.h000066400000000000000000000036711474012624500177160ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "alloc-util.h" #include "extract-word.h" #include "macro.h" #include "util.h" char *strv_find(char **l, const char *name) _pure_; char *strv_find_prefix(char **l, const char *name) _pure_; char **strv_free(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); #define _cleanup_strv_free_ _cleanup_(strv_freep) char **strv_free_erase(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) void strv_clear(char **l); char **strv_copy(char * const *l); unsigned strv_length(char * const *l) _pure_; int strv_extend(char ***l, const char *value); int strv_push(char ***l, char *value); int strv_consume(char ***l, char *value); char **strv_remove(char **l, const char *s); char **strv_uniq(char **l); #define strv_contains(l, s) (!!strv_find((l), (s))) char **strv_new(const char *x, ...) _sentinel_; char **strv_new_ap(const char *x, va_list ap); #define STRV_IGNORE ((const char *) -1) static inline const char* STRV_IFNOTNULL(const char *x) { return x ? x : STRV_IGNORE; } static inline bool strv_isempty(char * const *l) { return !l || !*l; } char **strv_split(const char *s, const char *separator); char **strv_split_nulstr(const char *s); #define STRV_FOREACH(s, l) \ for ((s) = (l); (s) && *(s); (s)++) #define STRV_FOREACH_BACKWARDS(s, l) \ STRV_FOREACH(s, l) \ ; \ for ((s)--; (l) && ((s) >= (l)); (s)--) #define STRV_FOREACH_PAIR(x, y, l) \ for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) #define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) #define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) systemd-netlogd-1.4.4/src/share/syslog-util.c000066400000000000000000000053351474012624500212050ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include "hexdecoct.h" #include "macro.h" #include "string-table.h" #include "syslog-util.h" int syslog_parse_priority(const char **p, int *priority, bool with_facility) { int a = 0, b = 0, c = 0; int k; assert(p); assert(*p); assert(priority); if ((*p)[0] != '<') return 0; if (!strchr(*p, '>')) return 0; if ((*p)[2] == '>') { c = undecchar((*p)[1]); k = 3; } else if ((*p)[3] == '>') { b = undecchar((*p)[1]); c = undecchar((*p)[2]); k = 4; } else if ((*p)[4] == '>') { a = undecchar((*p)[1]); b = undecchar((*p)[2]); c = undecchar((*p)[3]); k = 5; } else return 0; if (a < 0 || b < 0 || c < 0 || (!with_facility && (a || b || c > 7))) return 0; if (with_facility) *priority = a*100 + b*10 + c; else *priority = (*priority & LOG_FACMASK) | c; *p += k; return 1; } static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = { [LOG_FAC(LOG_KERN)] = "kern", [LOG_FAC(LOG_USER)] = "user", [LOG_FAC(LOG_MAIL)] = "mail", [LOG_FAC(LOG_DAEMON)] = "daemon", [LOG_FAC(LOG_AUTH)] = "auth", [LOG_FAC(LOG_SYSLOG)] = "syslog", [LOG_FAC(LOG_LPR)] = "lpr", [LOG_FAC(LOG_NEWS)] = "news", [LOG_FAC(LOG_UUCP)] = "uucp", [LOG_FAC(LOG_CRON)] = "cron", [LOG_FAC(LOG_AUTHPRIV)] = "authpriv", [LOG_FAC(LOG_FTP)] = "ftp", [LOG_FAC(LOG_LOCAL0)] = "local0", [LOG_FAC(LOG_LOCAL1)] = "local1", [LOG_FAC(LOG_LOCAL2)] = "local2", [LOG_FAC(LOG_LOCAL3)] = "local3", [LOG_FAC(LOG_LOCAL4)] = "local4", [LOG_FAC(LOG_LOCAL5)] = "local5", [LOG_FAC(LOG_LOCAL6)] = "local6", [LOG_FAC(LOG_LOCAL7)] = "local7" }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0)); bool log_facility_unshifted_is_valid(int facility) { return facility >= 0 && facility <= LOG_FAC(~0); } static const char *const log_level_table[] = { [LOG_EMERG] = "emerg", [LOG_ALERT] = "alert", [LOG_CRIT] = "crit", [LOG_ERR] = "err", [LOG_WARNING] = "warning", [LOG_NOTICE] = "notice", [LOG_INFO] = "info", [LOG_DEBUG] = "debug" }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG); bool log_level_is_valid(int level) { return level >= 0 && level <= LOG_DEBUG; } systemd-netlogd-1.4.4/src/share/syslog-util.h000066400000000000000000000007131474012624500212050ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include int log_facility_unshifted_to_string_alloc(int i, char **s); int log_facility_unshifted_from_string(const char *s); bool log_facility_unshifted_is_valid(int faciliy); int log_level_to_string_alloc(int i, char **s); int log_level_from_string(const char *s); bool log_level_is_valid(int level); int syslog_parse_priority(const char **p, int *priority, bool with_facility); systemd-netlogd-1.4.4/src/share/terminal-util.c000066400000000000000000000071701474012624500214770ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "io-util.h" #include "log.h" #include "macro.h" #include "parse-util.h" #include "process-util.h" #include "socket-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" #include "time-util.h" #include "util.h" static volatile unsigned cached_columns = 0; static volatile unsigned cached_lines = 0; int open_terminal(const char *name, int mode) { int fd, r; unsigned c = 0; /* * If a TTY is in the process of being closed opening it might * cause EIO. This is horribly awful, but unlikely to be * changed in the kernel. Hence we work around this problem by * retrying a couple of times. * * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 */ if (mode & O_CREAT) return -EINVAL; for (;;) { fd = open(name, mode, 0); if (fd >= 0) break; if (errno != EIO) return -errno; /* Max 1s in total */ if (c >= 20) return -errno; usleep(50 * USEC_PER_MSEC); c++; } r = isatty(fd); if (r < 0) { safe_close(fd); return -errno; } if (!r) { safe_close(fd); return -ENOTTY; } return fd; } bool on_tty(void) { static int cached_on_tty = -1; if (_unlikely_(cached_on_tty < 0)) cached_on_tty = isatty(STDOUT_FILENO) > 0; return cached_on_tty; } int get_ctty_devnr(pid_t pid, dev_t *d) { int r; _cleanup_free_ char *line = NULL; const char *p; unsigned long ttynr; assert(pid >= 0); p = procfs_file_alloca(pid, "stat"); r = read_one_line_file(p, &line); if (r < 0) return r; p = strrchr(line, ')'); if (!p) return -EIO; p++; if (sscanf(p, " " "%*c " /* state */ "%*d " /* ppid */ "%*d " /* pgrp */ "%*d " /* session */ "%lu ", /* ttynr */ &ttynr) != 1) return -EIO; if (major(ttynr) == 0 && minor(ttynr) == 0) return -ENXIO; if (d) *d = (dev_t) ttynr; return 0; } bool terminal_is_dumb(void) { const char *e; if (!on_tty()) return true; e = getenv("TERM"); if (!e) return true; return streq(e, "dumb"); } bool colors_enabled(void) { static int enabled = -1; if (_unlikely_(enabled < 0)) { const char *colors; colors = getenv("SYSTEMD_COLORS"); if (colors) enabled = parse_boolean(colors) != 0; else enabled = !terminal_is_dumb(); } return enabled; } systemd-netlogd-1.4.4/src/share/terminal-util.h000066400000000000000000000034031474012624500214770ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "macro.h" #include "time-util.h" #define ANSI_RED "\x1B[0;31m" #define ANSI_GREEN "\x1B[0;32m" #define ANSI_UNDERLINE "\x1B[0;4m" #define ANSI_HIGHLIGHT "\x1B[0;1;39m" #define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m" #define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m" #define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m" #define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m" #define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m" #define ANSI_NORMAL "\x1B[0m" #define ANSI_ERASE_TO_END_OF_LINE "\x1B[K" /* Set cursor to top left corner and clear screen */ #define ANSI_HOME_CLEAR "\x1B[H\x1B[2J" int open_terminal(const char *name, int mode); bool on_tty(void); bool terminal_is_dumb(void); bool colors_enabled(void); static inline const char *ansi_underline(void) { return colors_enabled() ? ANSI_UNDERLINE : ""; } static inline const char *ansi_highlight(void) { return colors_enabled() ? ANSI_HIGHLIGHT : ""; } static inline const char *ansi_highlight_underline(void) { return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : ""; } static inline const char *ansi_highlight_red(void) { return colors_enabled() ? ANSI_HIGHLIGHT_RED : ""; } static inline const char *ansi_highlight_green(void) { return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : ""; } static inline const char *ansi_highlight_yellow(void) { return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : ""; } static inline const char *ansi_highlight_blue(void) { return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : ""; } static inline const char *ansi_normal(void) { return colors_enabled() ? ANSI_NORMAL : ""; } int get_ctty_devnr(pid_t pid, dev_t *d); systemd-netlogd-1.4.4/src/share/time-util.c000066400000000000000000000163201474012624500206170ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "log.h" #include "macro.h" #include "parse-util.h" #include "path-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" static const char* extract_multiplier(const char *p, usec_t *ret) { static const struct { const char *suffix; usec_t usec; } table[] = { { "seconds", USEC_PER_SEC }, { "second", USEC_PER_SEC }, { "sec", USEC_PER_SEC }, { "s", USEC_PER_SEC }, { "minutes", USEC_PER_MINUTE }, { "minute", USEC_PER_MINUTE }, { "min", USEC_PER_MINUTE }, { "months", USEC_PER_MONTH }, { "month", USEC_PER_MONTH }, { "M", USEC_PER_MONTH }, { "msec", USEC_PER_MSEC }, { "ms", USEC_PER_MSEC }, { "m", USEC_PER_MINUTE }, { "hours", USEC_PER_HOUR }, { "hour", USEC_PER_HOUR }, { "hr", USEC_PER_HOUR }, { "h", USEC_PER_HOUR }, { "days", USEC_PER_DAY }, { "day", USEC_PER_DAY }, { "d", USEC_PER_DAY }, { "weeks", USEC_PER_WEEK }, { "week", USEC_PER_WEEK }, { "w", USEC_PER_WEEK }, { "years", USEC_PER_YEAR }, { "year", USEC_PER_YEAR }, { "y", USEC_PER_YEAR }, { "usec", 1ULL }, { "us", 1ULL }, { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */ { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */ }; assert(p); assert(ret); for (size_t i = 0; i < ELEMENTSOF(table); i++) { char *e; e = startswith(p, table[i].suffix); if (e) { *ret = table[i].usec; return e; } } return p; } static clockid_t map_clock_id(clockid_t c) { /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on * those archs. */ switch (c) { case CLOCK_BOOTTIME_ALARM: return CLOCK_BOOTTIME; case CLOCK_REALTIME_ALARM: return CLOCK_REALTIME; default: return c; } } usec_t now(clockid_t clock_id) { struct timespec ts; assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); return timespec_load(&ts); } usec_t timespec_load(const struct timespec *ts) { assert(ts); if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) return USEC_INFINITY; if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) return USEC_INFINITY; return (usec_t) ts->tv_sec * USEC_PER_SEC + (usec_t) ts->tv_nsec / NSEC_PER_USEC; } struct timespec *timespec_store(struct timespec *ts, usec_t u) { assert(ts); if (u == USEC_INFINITY) { ts->tv_sec = (time_t) -1; ts->tv_nsec = (long) -1; return ts; } ts->tv_sec = (time_t) (u / USEC_PER_SEC); ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); return ts; } struct timeval *timeval_store(struct timeval *tv, usec_t u) { assert(tv); if (u == USEC_INFINITY) { tv->tv_sec = (time_t) -1; tv->tv_usec = (suseconds_t) -1; } else { tv->tv_sec = (time_t) (u / USEC_PER_SEC); tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); } return tv; } int parse_time(const char *t, usec_t *ret, usec_t default_unit) { const char *p, *s; usec_t usec = 0; bool something = false; assert(t); assert(default_unit > 0); p = t; p += strspn(p, WHITESPACE); s = startswith(p, "infinity"); if (s) { s += strspn(s, WHITESPACE); if (*s != 0) return -EINVAL; if (ret) *ret = USEC_INFINITY; return 0; } for (;;) { usec_t multiplier = default_unit, k; long long l; char *e; p += strspn(p, WHITESPACE); if (*p == 0) { if (!something) return -EINVAL; break; } if (*p == '-') /* Don't allow "-0" */ return -ERANGE; errno = 0; l = strtoll(p, &e, 10); if (errno > 0) return -errno; if (l < 0) return -ERANGE; if (*e == '.') { p = e + 1; p += strspn(p, DIGITS); } else if (e == p) return -EINVAL; else p = e; s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier); if (s == p && *s != '\0') /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */ return -EINVAL; p = s; if ((usec_t) l >= USEC_INFINITY / multiplier) return -ERANGE; k = (usec_t) l * multiplier; if (k >= USEC_INFINITY - usec) return -ERANGE; usec += k; something = true; if (*e == '.') { usec_t m = multiplier / 10; const char *b; for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) { k = (usec_t) (*b - '0') * m; if (k >= USEC_INFINITY - usec) return -ERANGE; usec += k; } /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */ if (b == e + 1) return -EINVAL; } } if (ret) *ret = usec; return 0; } int parse_sec(const char *t, usec_t *ret) { return parse_time(t, ret, USEC_PER_SEC); } systemd-netlogd-1.4.4/src/share/time-util.h000066400000000000000000000071211474012624500206230ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include typedef uint64_t usec_t; typedef uint64_t nsec_t; #define NSEC_FMT "%" PRIu64 #define USEC_FMT "%" PRIu64 #include "macro.h" typedef struct dual_timestamp { usec_t realtime; usec_t monotonic; } dual_timestamp; typedef struct triple_timestamp { usec_t realtime; usec_t monotonic; usec_t boottime; } triple_timestamp; #define USEC_INFINITY ((usec_t) -1) #define NSEC_INFINITY ((nsec_t) -1) #define MSEC_PER_SEC 1000ULL #define USEC_PER_SEC ((usec_t) 1000000ULL) #define USEC_PER_MSEC ((usec_t) 1000ULL) #define NSEC_PER_SEC ((nsec_t) 1000000000ULL) #define NSEC_PER_MSEC ((nsec_t) 1000000ULL) #define NSEC_PER_USEC ((nsec_t) 1000ULL) #define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC)) #define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC)) #define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE)) #define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE)) #define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR)) #define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR)) #define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY)) #define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY)) #define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC)) #define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC)) #define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) #define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) #define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */ #define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ #define FORMAT_TIMESTAMP_RELATIVE_MAX 256 #define FORMAT_TIMESPAN_MAX 64 #define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) #define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) #define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) usec_t now(clockid_t clock); usec_t timespec_load(const struct timespec *ts) _pure_; struct timespec *timespec_store(struct timespec *ts, usec_t u); struct timeval *timeval_store(struct timeval *tv, usec_t u); #define xstrftime(buf, fmt, tm) \ assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \ "xstrftime: " #buf "[] must be big enough") static inline usec_t usec_add(usec_t a, usec_t b) { /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, * and doesn't overflow. */ if (a > USEC_INFINITY - b) /* overflow check */ return USEC_INFINITY; return a + b; } static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ return USEC_INFINITY; if (timestamp < delta) return 0; return timestamp - delta; } static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { if (delta == INT64_MIN) { /* prevent overflow */ assert_cc(-(INT64_MIN + 1) == INT64_MAX); assert_cc(USEC_INFINITY > INT64_MAX); return usec_add(timestamp, (usec_t) INT64_MAX + 1); } if (delta < 0) return usec_add(timestamp, (usec_t) (-delta)); return usec_sub_unsigned(timestamp, (usec_t) delta); } int parse_sec(const char *t, usec_t *ret); int parse_time(const char *t, usec_t *ret, usec_t default_unit); static inline bool timestamp_is_set(usec_t timestamp) { return timestamp > 0 && timestamp != USEC_INFINITY; } systemd-netlogd-1.4.4/src/share/umask-util.h000066400000000000000000000012761474012624500210120ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include "macro.h" static inline void umaskp(mode_t *u) { umask(*u); } #define _cleanup_umask_ _cleanup_(umaskp) struct _umask_struct_ { mode_t mask; bool quit; }; static inline void _reset_umask_(struct _umask_struct_ *s) { umask(s->mask); }; #define RUN_WITH_UMASK(mask) \ for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \ !_saved_umask_.quit ; \ _saved_umask_.quit = true) systemd-netlogd-1.4.4/src/share/unaligned.h000066400000000000000000000057771474012624500206770ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include /* BE */ static inline uint16_t unaligned_read_be16(const void *_u) { const uint8_t *u = _u; return (((uint16_t) u[0]) << 8) | ((uint16_t) u[1]); } static inline uint32_t unaligned_read_be32(const void *_u) { const uint8_t *u = _u; return (((uint32_t) unaligned_read_be16(u)) << 16) | ((uint32_t) unaligned_read_be16(u + 2)); } static inline uint64_t unaligned_read_be64(const void *_u) { const uint8_t *u = _u; return (((uint64_t) unaligned_read_be32(u)) << 32) | ((uint64_t) unaligned_read_be32(u + 4)); } static inline void unaligned_write_be16(void *_u, uint16_t a) { uint8_t *u = _u; u[0] = (uint8_t) (a >> 8); u[1] = (uint8_t) a; } static inline void unaligned_write_be32(void *_u, uint32_t a) { uint8_t *u = _u; unaligned_write_be16(u, (uint16_t) (a >> 16)); unaligned_write_be16(u + 2, (uint16_t) a); } static inline void unaligned_write_be64(void *_u, uint64_t a) { uint8_t *u = _u; unaligned_write_be32(u, (uint32_t) (a >> 32)); unaligned_write_be32(u + 4, (uint32_t) a); } /* LE */ static inline uint16_t unaligned_read_le16(const void *_u) { const uint8_t *u = _u; return (((uint16_t) u[1]) << 8) | ((uint16_t) u[0]); } static inline uint32_t unaligned_read_le32(const void *_u) { const uint8_t *u = _u; return (((uint32_t) unaligned_read_le16(u + 2)) << 16) | ((uint32_t) unaligned_read_le16(u)); } static inline uint64_t unaligned_read_le64(const void *_u) { const uint8_t *u = _u; return (((uint64_t) unaligned_read_le32(u + 4)) << 32) | ((uint64_t) unaligned_read_le32(u)); } static inline void unaligned_write_le16(void *_u, uint16_t a) { uint8_t *u = _u; u[0] = (uint8_t) a; u[1] = (uint8_t) (a >> 8); } static inline void unaligned_write_le32(void *_u, uint32_t a) { uint8_t *u = _u; unaligned_write_le16(u, (uint16_t) a); unaligned_write_le16(u + 2, (uint16_t) (a >> 16)); } static inline void unaligned_write_le64(void *_u, uint64_t a) { uint8_t *u = _u; unaligned_write_le32(u, (uint32_t) a); unaligned_write_le32(u + 4, (uint32_t) (a >> 32)); } #if __BYTE_ORDER == __BIG_ENDIAN #define unaligned_read_ne16 unaligned_read_be16 #define unaligned_read_ne32 unaligned_read_be32 #define unaligned_read_ne64 unaligned_read_be64 #define unaligned_write_ne16 unaligned_write_be16 #define unaligned_write_ne32 unaligned_write_be32 #define unaligned_write_ne64 unaligned_write_be64 #else #define unaligned_read_ne16 unaligned_read_le16 #define unaligned_read_ne32 unaligned_read_le32 #define unaligned_read_ne64 unaligned_read_le64 #define unaligned_write_ne16 unaligned_write_le16 #define unaligned_write_ne32 unaligned_write_le32 #define unaligned_write_ne64 unaligned_write_le64 #endif systemd-netlogd-1.4.4/src/share/user-util.c000066400000000000000000000157371474012624500206520ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include "missing.h" #include "alloc-util.h" #include "fd-util.h" #include "formats-util.h" #include "macro.h" #include "parse-util.h" #include "path-util.h" #include "string-util.h" #include "user-util.h" bool uid_is_valid(uid_t uid) { /* Some libc APIs use UID_INVALID as special placeholder */ if (uid == (uid_t) UINT32_C(0xFFFFFFFF)) return false; /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ if (uid == (uid_t) UINT32_C(0xFFFF)) return false; return true; } int parse_uid(const char *s, uid_t *ret) { uint32_t uid = 0; int r; assert(s); assert_cc(sizeof(uid_t) == sizeof(uint32_t)); r = safe_atou32(s, &uid); if (r < 0) return r; if (!uid_is_valid(uid)) return -ENXIO; /* we return ENXIO instead of EINVAL * here, to make it easy to distinguish * invalid numeric uids from invalid * strings. */ if (ret) *ret = uid; return 0; } char* getlogname_malloc(void) { uid_t uid; struct stat st; if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0) uid = st.st_uid; else uid = getuid(); return uid_to_name(uid); } char *getusername_malloc(void) { const char *e; e = getenv("USER"); if (e) return strdup(e); return uid_to_name(getuid()); } int get_user_creds( const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell) { struct passwd *p; uid_t u; assert(username); assert(*username); /* We enforce some special rules for uid=0: in order to avoid * NSS lookups for root we hardcode its data. */ if (streq(*username, "root") || streq(*username, "0")) { *username = "root"; if (uid) *uid = 0; if (gid) *gid = 0; if (home) *home = "/root"; if (shell) *shell = "/bin/sh"; return 0; } if (parse_uid(*username, &u) >= 0) { errno = 0; p = getpwuid(u); /* If there are multiple users with the same id, make * sure to leave $USER to the configured value instead * of the first occurrence in the database. However if * the uid was configured by a numeric uid, then let's * pick the real username from /etc/passwd. */ if (p) *username = p->pw_name; } else { errno = 0; p = getpwnam(*username); } if (!p) return errno > 0 ? -errno : -ESRCH; if (uid) { if (!uid_is_valid(p->pw_uid)) return -EBADMSG; *uid = p->pw_uid; } if (gid) { if (!gid_is_valid(p->pw_gid)) return -EBADMSG; *gid = p->pw_gid; } if (home) *home = p->pw_dir; if (shell) *shell = p->pw_shell; return 0; } int get_group_creds(const char **groupname, gid_t *gid) { struct group *g; gid_t id; assert(groupname); /* We enforce some special rules for gid=0: in order to avoid * NSS lookups for root we hardcode its data. */ if (streq(*groupname, "root") || streq(*groupname, "0")) { *groupname = "root"; if (gid) *gid = 0; return 0; } if (parse_gid(*groupname, &id) >= 0) { errno = 0; g = getgrgid(id); if (g) *groupname = g->gr_name; } else { errno = 0; g = getgrnam(*groupname); } if (!g) return errno > 0 ? -errno : -ESRCH; if (gid) { if (!gid_is_valid(g->gr_gid)) return -EBADMSG; *gid = g->gr_gid; } return 0; } char* uid_to_name(uid_t uid) { char *ret; int r; /* Shortcut things to avoid NSS lookups */ if (uid == 0) return strdup("root"); if (uid_is_valid(uid)) { long bufsize; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize <= 0) bufsize = 4096; for (;;) { struct passwd pwbuf, *pw = NULL; _cleanup_free_ char *buf = NULL; buf = malloc(bufsize); if (!buf) return NULL; r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw); if (r == 0 && pw) return strdup(pw->pw_name); if (r != ERANGE) break; bufsize *= 2; } } if (asprintf(&ret, UID_FMT, uid) < 0) return NULL; return ret; } char* gid_to_name(gid_t gid) { char *ret; int r; if (gid == 0) return strdup("root"); if (gid_is_valid(gid)) { long bufsize; bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); if (bufsize <= 0) bufsize = 4096; for (;;) { struct group grbuf, *gr = NULL; _cleanup_free_ char *buf = NULL; buf = malloc(bufsize); if (!buf) return NULL; r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr); if (r == 0 && gr) return strdup(gr->gr_name); if (r != ERANGE) break; bufsize *= 2; } } if (asprintf(&ret, GID_FMT, gid) < 0) return NULL; return ret; } int reset_uid_gid(void) { if (setgroups(0, NULL) < 0) return -errno; if (setresgid(0, 0, 0) < 0) return -errno; if (setresuid(0, 0, 0) < 0) return -errno; return 0; } systemd-netlogd-1.4.4/src/share/user-util.h000066400000000000000000000023211474012624500206400ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include bool uid_is_valid(uid_t uid); static inline bool gid_is_valid(gid_t gid) { return uid_is_valid((uid_t) gid); } int parse_uid(const char *s, uid_t* ret_uid); static inline int parse_gid(const char *s, gid_t *ret_gid) { return parse_uid(s, (uid_t*) ret_gid); } char* getlogname_malloc(void); char* getusername_malloc(void); int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); int get_group_creds(const char **groupname, gid_t *gid); char* uid_to_name(uid_t uid); char* gid_to_name(gid_t gid); int reset_uid_gid(void); #define UID_INVALID ((uid_t) -1) #define GID_INVALID ((gid_t) -1) /* The following macros add 1 when converting things, since UID 0 is a * valid UID, while the pointer NULL is special */ #define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1)) #define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) #define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1)) #define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) static inline bool userns_supported(void) { return access("/proc/self/uid_map", F_OK) >= 0; } systemd-netlogd-1.4.4/src/share/utf8.c000066400000000000000000000164051474012624500176000ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Parts of this file are based on the GLIB utf8 validation functions. The * original license text follows. */ /* gutf8.c - Operations on UTF-8 strings. * * Copyright (C) 1999 Tom Tromey * Copyright (C) 2000 Red Hat, Inc. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" #include "utf8.h" bool unichar_is_valid(char32_t ch) { if (ch >= 0x110000) /* End of unicode space */ return false; if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ return false; if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ return false; if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ return false; return true; } /* count of characters used to encode one unicode char */ static int utf8_encoded_expected_len(const char *str) { unsigned char c; assert(str); c = (unsigned char) str[0]; if (c < 0x80) return 1; if ((c & 0xe0) == 0xc0) return 2; if ((c & 0xf0) == 0xe0) return 3; if ((c & 0xf8) == 0xf0) return 4; if ((c & 0xfc) == 0xf8) return 5; if ((c & 0xfe) == 0xfc) return 6; return 0; } /* decode one unicode char */ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { char32_t unichar; int len, i; assert(str); len = utf8_encoded_expected_len(str); switch (len) { case 1: *ret_unichar = (char32_t)str[0]; return 0; case 2: unichar = str[0] & 0x1f; break; case 3: unichar = (char32_t)str[0] & 0x0f; break; case 4: unichar = (char32_t)str[0] & 0x07; break; case 5: unichar = (char32_t)str[0] & 0x03; break; case 6: unichar = (char32_t)str[0] & 0x01; break; default: return -EINVAL; } for (i = 1; i < len; i++) { if (((char32_t)str[i] & 0xc0) != 0x80) return -EINVAL; unichar <<= 6; unichar |= (char32_t)str[i] & 0x3f; } *ret_unichar = unichar; return 0; } const char *utf8_is_valid(const char *str) { const uint8_t *p; assert(str); for (p = (const uint8_t*) str; *p; ) { int len; len = utf8_encoded_valid_unichar((const char *)p); if (len < 0) return NULL; p += len; } return str; } char *utf8_escape_invalid(const char *str) { char *p, *s; assert(str); p = s = malloc(strlen(str) * 4 + 1); if (!p) return NULL; while (*str) { int len; len = utf8_encoded_valid_unichar(str); if (len > 0) { s = mempcpy(s, str, len); str += len; } else { s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); str += 1; } } *s = '\0'; return p; } char *ascii_is_valid(const char *str) { const char *p; assert(str); for (p = str; *p; p++) if ((unsigned char) *p >= 128) return NULL; return (char*) str; } /** * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 * @out_utf8: output buffer of at least 4 bytes or NULL * @g: UCS-4 character to encode * * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8. * The length of the character is returned. It is not zero-terminated! If the * output buffer is NULL, only the length is returned. * * Returns: The length in bytes that the UTF-8 representation does or would * occupy. */ size_t utf8_encode_unichar(char *out_utf8, char32_t g) { if (g < (1 << 7)) { if (out_utf8) out_utf8[0] = g & 0x7f; return 1; } else if (g < (1 << 11)) { if (out_utf8) { out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f); out_utf8[1] = 0x80 | (g & 0x3f); } return 2; } else if (g < (1 << 16)) { if (out_utf8) { out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f); out_utf8[1] = 0x80 | ((g >> 6) & 0x3f); out_utf8[2] = 0x80 | (g & 0x3f); } return 3; } else if (g < (1 << 21)) { if (out_utf8) { out_utf8[0] = 0xf0 | ((g >> 18) & 0x07); out_utf8[1] = 0x80 | ((g >> 12) & 0x3f); out_utf8[2] = 0x80 | ((g >> 6) & 0x3f); out_utf8[3] = 0x80 | (g & 0x3f); } return 4; } return 0; } /* expected size used to encode one unicode char */ static int utf8_unichar_to_encoded_len(char32_t unichar) { if (unichar < 0x80) return 1; if (unichar < 0x800) return 2; if (unichar < 0x10000) return 3; if (unichar < 0x200000) return 4; if (unichar < 0x4000000) return 5; return 6; } /* validate one encoded unicode char and return its length */ int utf8_encoded_valid_unichar(const char *str) { int len, i, r; char32_t unichar; assert(str); len = utf8_encoded_expected_len(str); if (len == 0) return -EINVAL; /* ascii is valid */ if (len == 1) return 1; /* check if expected encoded chars are available */ for (i = 0; i < len; i++) if ((str[i] & 0x80) != 0x80) return -EINVAL; r = utf8_encoded_to_unichar(str, &unichar); if (r < 0) return r; /* check if encoded length matches encoded value */ if (utf8_unichar_to_encoded_len(unichar) != len) return -EINVAL; /* check if value has valid range */ if (!unichar_is_valid(unichar)) return -EINVAL; return len; } systemd-netlogd-1.4.4/src/share/utf8.h000066400000000000000000000017561474012624500176100ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include "macro.h" #include "missing.h" #define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" #define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" bool unichar_is_valid(char32_t c); const char *utf8_is_valid(const char *s) _pure_; char *ascii_is_valid(const char *s) _pure_; char *utf8_escape_invalid(const char *s); size_t utf8_encode_unichar(char *out_utf8, char32_t g); int utf8_encoded_valid_unichar(const char *str); int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); static inline bool utf16_is_surrogate(char16_t c) { return (0xd800 <= c && c <= 0xdfff); } static inline bool utf16_is_trailing_surrogate(char16_t c) { return (0xdc00 <= c && c <= 0xdfff); } static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; } systemd-netlogd-1.4.4/src/share/util.c000066400000000000000000000047041474012624500176660ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alloc-util.h" #include "build.h" #include "def.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" #include "formats-util.h" #include "log.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "set.h" #include "signal-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" #include "umask-util.h" #include "user-util.h" #include "util.h" /* Put this test here for a lack of better place */ assert_cc(EAGAIN == EWOULDBLOCK); int saved_argc = 0; char **saved_argv = NULL; static int saved_in_initrd = -1; size_t page_size(void) { static thread_local size_t pgsz = 0; long r; if (_likely_(pgsz > 0)) return pgsz; r = sysconf(_SC_PAGESIZE); assert(r > 0); pgsz = (size_t) r; return pgsz; } int prot_from_flags(int flags) { switch (flags & O_ACCMODE) { case O_RDONLY: return PROT_READ; case O_WRONLY: return PROT_WRITE; case O_RDWR: return PROT_READ|PROT_WRITE; default: return -EINVAL; } } bool in_initrd(void) { struct statfs s; if (saved_in_initrd >= 0) return saved_in_initrd; /* We make two checks here: * * 1. the flag file /etc/initrd-release must exist * 2. the root file system must be a memory file system * * The second check is extra paranoia, since misdetecting an * initrd can have bad bad consequences due the initrd * emptying when transititioning to the main systemd. */ saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && statfs("/", &s) >= 0 && is_temporary_fs(&s); return saved_in_initrd; } void in_initrd_force(bool value) { saved_in_initrd = value; } int version(void) { puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES); return 0; } systemd-netlogd-1.4.4/src/share/util.h000066400000000000000000000077661474012624500177060ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "formats-util.h" #include "macro.h" #include "missing.h" #include "time-util.h" size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) static inline const char* yes_no(bool b) { return b ? "yes" : "no"; } static inline const char* true_false(bool b) { return b ? "true" : "false"; } static inline const char* one_zero(bool b) { return b ? "1" : "0"; } #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) #define NULSTR_FOREACH_PAIR(i, j, l) \ for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) extern int saved_argc; extern char **saved_argv; int prot_from_flags(int flags) _const_; bool in_initrd(void); void in_initrd_force(bool value); /** * Normal qsort requires base to be nonnull. Here were require * that only if nmemb > 0. */ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) { if (nmemb <= 1) return; assert(base); qsort(base, nmemb, size, compar); } #define memzero(x, l) \ ({ \ size_t _l_ = (l); \ _l_ > 0 ? memset((x), 0, _l_) : (x); \ }) static inline void *mempset(void *s, int c, size_t n) { memset(s, c, n); return (uint8_t*)s + n; } static inline void _reset_errno_(int *saved_errno) { errno = *saved_errno; } #define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno static inline int negative_errno(void) { /* This helper should be used to shut up gcc if you know 'errno' is * negative. Instead of "return -errno;", use "return negative_errno();" * It will suppress bogus gcc warnings in case it assumes 'errno' might * be 0 and thus the caller's error-handling might not be triggered. */ assert_return(errno > 0, -EINVAL); return -errno; } static inline int RET_NERRNO(int ret) { /* Helper to wrap system calls in to make them return negative errno errors. This brings system call * error handling in sync with how we usually handle errors in our own code, i.e. with immediate * returning of negative errno. Usage is like this: * * … * r = RET_NERRNO(unlink(t)); * … * * or * * … * fd = RET_NERRNO(open("/etc/fstab", O_RDONLY|O_CLOEXEC)); * … */ if (ret < 0) return negative_errno(); return ret; } static inline unsigned u64log2(uint64_t n) { #if __SIZEOF_LONG_LONG__ == 8 return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; #else #error "Wut?" #endif } static inline unsigned u32ctz(uint32_t n) { #if __SIZEOF_INT__ == 4 return __builtin_ctz(n); #else #error "Wut?" #endif } static inline unsigned log2i(int x) { assert(x > 0); return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; } static inline unsigned log2u(unsigned x) { assert(x > 0); return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; } static inline unsigned log2u_round_up(unsigned x) { assert(x > 0); if (x == 1) return 0; return log2u(x - 1) + 1; } int version(void); systemd-netlogd-1.4.4/src/share/virt.c000066400000000000000000000110671474012624500176750ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #include "alloc-util.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" #include "macro.h" #include "process-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "virt.h" int detect_container(void) { static const struct { const char *value; int id; } value_table[] = { { "lxc", VIRTUALIZATION_LXC }, { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT }, { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN }, { "docker", VIRTUALIZATION_DOCKER }, { "rkt", VIRTUALIZATION_RKT }, }; static thread_local int cached_found = _VIRTUALIZATION_INVALID; _cleanup_free_ char *m = NULL; const char *e = NULL; unsigned j; int r; if (cached_found >= 0) return cached_found; /* /proc/vz exists in container and outside of the container, * /proc/bc only outside of the container. */ if (access("/proc/vz", F_OK) >= 0 && access("/proc/bc", F_OK) < 0) { r = VIRTUALIZATION_OPENVZ; goto finish; } if (getpid() == 1) { /* If we are PID 1 we can just check our own * environment variable */ e = getenv("container"); if (isempty(e)) { r = VIRTUALIZATION_NONE; goto finish; } } else { /* Otherwise, PID 1 dropped this information into a * file in /run. This is better than accessing * /proc/1/environ, since we don't need CAP_SYS_PTRACE * for that. */ r = read_one_line_file("/run/systemd/container", &m); if (r == -ENOENT) { /* Fallback for cases where PID 1 was not * systemd (for example, cases where * init=/bin/sh is used. */ r = getenv_for_pid(1, "container", &m); if (r <= 0) { /* If that didn't work, give up, * assume no container manager. * * Note: This means we still cannot * detect containers if init=/bin/sh * is passed but privileges dropped, * as /proc/1/environ is only readable * with privileges. */ r = VIRTUALIZATION_NONE; goto finish; } } if (r < 0) return r; e = m; } for (j = 0; j < ELEMENTSOF(value_table); j++) if (streq(e, value_table[j].value)) { r = value_table[j].id; goto finish; } r = VIRTUALIZATION_CONTAINER_OTHER; finish: log_debug("Found container virtualization %s", virtualization_to_string(r)); cached_found = r; return r; } int running_in_chroot(void) { int ret; ret = files_same("/proc/1/root", "/"); if (ret < 0) return ret; return ret == 0; } static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_NONE] = "none", [VIRTUALIZATION_KVM] = "kvm", [VIRTUALIZATION_QEMU] = "qemu", [VIRTUALIZATION_BOCHS] = "bochs", [VIRTUALIZATION_XEN] = "xen", [VIRTUALIZATION_UML] = "uml", [VIRTUALIZATION_VMWARE] = "vmware", [VIRTUALIZATION_ORACLE] = "oracle", [VIRTUALIZATION_MICROSOFT] = "microsoft", [VIRTUALIZATION_ZVM] = "zvm", [VIRTUALIZATION_PARALLELS] = "parallels", [VIRTUALIZATION_VM_OTHER] = "vm-other", [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt", [VIRTUALIZATION_LXC] = "lxc", [VIRTUALIZATION_OPENVZ] = "openvz", [VIRTUALIZATION_DOCKER] = "docker", [VIRTUALIZATION_RKT] = "rkt", [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", }; DEFINE_STRING_TABLE_LOOKUP(virtualization, int); systemd-netlogd-1.4.4/src/share/virt.h000066400000000000000000000027501474012624500177010ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include "macro.h" enum { VIRTUALIZATION_NONE = 0, VIRTUALIZATION_VM_FIRST, VIRTUALIZATION_KVM = VIRTUALIZATION_VM_FIRST, VIRTUALIZATION_QEMU, VIRTUALIZATION_BOCHS, VIRTUALIZATION_XEN, VIRTUALIZATION_UML, VIRTUALIZATION_VMWARE, VIRTUALIZATION_ORACLE, VIRTUALIZATION_MICROSOFT, VIRTUALIZATION_ZVM, VIRTUALIZATION_PARALLELS, VIRTUALIZATION_VM_OTHER, VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER, VIRTUALIZATION_CONTAINER_FIRST, VIRTUALIZATION_SYSTEMD_NSPAWN = VIRTUALIZATION_CONTAINER_FIRST, VIRTUALIZATION_LXC_LIBVIRT, VIRTUALIZATION_LXC, VIRTUALIZATION_OPENVZ, VIRTUALIZATION_DOCKER, VIRTUALIZATION_RKT, VIRTUALIZATION_CONTAINER_OTHER, VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER, _VIRTUALIZATION_MAX, _VIRTUALIZATION_INVALID = -1 }; static inline bool VIRTUALIZATION_IS_VM(int x) { return x >= VIRTUALIZATION_VM_FIRST && x <= VIRTUALIZATION_VM_LAST; } static inline bool VIRTUALIZATION_IS_CONTAINER(int x) { return x >= VIRTUALIZATION_CONTAINER_FIRST && x <= VIRTUALIZATION_CONTAINER_LAST; } int detect_vm(void); int detect_container(void); int running_in_chroot(void); const char *virtualization_to_string(int v) _const_; int virtualization_from_string(const char *s) _pure_; systemd-netlogd-1.4.4/systemd-netlogd.spec000066400000000000000000000033621474012624500206510ustar00rootroot00000000000000Name: systemd-netlogd Version: 1.4 Release: 1%{?dist} Summary: Forwards messages from the journal to other hosts over the network using syslog format RFC 5424 License: GPLv2 and LGPL-2.1+ and CC0 URL: https://github.com/systemd/systemd-netlogd Source0: %{URL}/archive/v%{version}.tar.gz BuildRequires: gperf BuildRequires: libcap-devel BuildRequires: pkg-config BuildRequires: systemd-devel BuildRequires: python3-sphinx BuildRequires: meson >= 0.43 %description Forwards messages from the journal to other hosts over the network using the Syslog Protocol (RFC 5424 and RFC 3339). It can be configured to send messages to both unicast and multicast addresses. systemd-netlogd runs with own user systemd-journal-netlog. Starts sending logs when network is up and stops sending as soon as network is down (uses sd-network). It reads from journal and forwards to network one by one. It does not use any extra disk space. systemd-netlogd supports UDP, TCP, TLS and DTLS (Datagram Transport Layer Security RFC 6012). BuildRequires: gcc BuildRequires: libcap-devel BuildRequires: docbook-style-xsl BuildRequires: gperf BuildRequires: python3-devel BuildRequires: python3-lxml BuildRequires: git BuildRequires: meson >= 0.43 BuildRequires: systemd-devel BuildRequires: openssl-devel %prep %autosetup rm -rf build && mkdir build %build pushd build meson .. ninja-build -v popd %install pushd build DESTDIR=%{buildroot} ninja-build -v install popd %files %{_sysconfdir}/systemd/system/netlogd.conf %{_sysconfdir}/systemd/system/systemd-netlogd.service /lib/systemd/systemd-netlogd %{_mandir}/man8/systemd-netlogd.8* %changelog * Tue May 14 2024 Susant Sahani - 1.4 - Initial spec systemd-netlogd-1.4.4/units/000077500000000000000000000000001474012624500160115ustar00rootroot00000000000000systemd-netlogd-1.4.4/units/.gitignore000066400000000000000000000000311474012624500177730ustar00rootroot00000000000000/systemd-netlogd.service systemd-netlogd-1.4.4/units/meson.build000066400000000000000000000005141474012624500201530ustar00rootroot00000000000000systemd_netlogd_conf = configure_file( input : 'systemd-netlogd.service.in', output : 'systemd-netlogd.service', configuration : conf) install_data(systemd_netlogd_conf, install_dir : get_option('prefix') / 'system') systemd-netlogd-1.4.4/units/systemd-netlogd.service.in000066400000000000000000000012261474012624500231230ustar00rootroot00000000000000# SPDX-License-Identifier: LGPL-2.1-or-later # This file is part of systemd. [Unit] Description=Journal Syslog Unicast and Multicast Daemon Documentation=man:systemd-netlogd(8) After=network.target [Service] ExecStart=@PKGPREFIX@/systemd-netlogd WatchdogSec=20min # hardening LockPersonality=yes MemoryDenyWriteExecute=yes PrivateTmp=yes PrivateDevices=yes ProtectClock=yes ProtectControlGroups=yes ProtectHome=yes ProtectHostname=yes ProtectKernelLogs=yes ProtectKernelModules=yes ProtectKernelTunables=yes ProtectProc=invisible ProtectSystem=strict StateDirectory=systemd/journal-netlogd SystemCallArchitectures=native [Install] WantedBy=multi-user.target