pax_global_header00006660000000000000000000000064130413572310014511gustar00rootroot0000000000000052 comment=f78148e2a3017f9eacb2341015b6fefe42f10d09 argonaut-1.0/000077500000000000000000000000001304135723100131715ustar00rootroot00000000000000argonaut-1.0/AUTHORS000066400000000000000000000013421304135723100142410ustar00rootroot00000000000000Argonaut AUTHORS ======================= This is the alphabetical list of all people that have contributed to the Argonaut project, beeing code, translations, documentation and additional help. * Benoit Mortier Butracking, QA * Antoine Gallavardin Quota original code * Bernigaud Côme All the new deployment stuff :) * Samuel Bosquin Help and test with the RPM code and FAI * Jonathan Swaelens QA, Testing, Systemd units * Sean Thatcher papertray3@gmail.com Corrected Argonaut systemd units * Thomas Niercke Code for the samba share argonaut module argonaut-1.0/COPYING000066400000000000000000000355731304135723100142410ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS argonaut-1.0/Changelog000066400000000000000000000313001304135723100150000ustar00rootroot00000000000000Argonaut changelog ================== * Argonaut 1.0 [Fix] Bugs #5273: Incompatibility between recovery password and user-reminder [Feature] Bugs #5275: redesign the argonaut-fusiondirectory tools [Feature] Bugs #5281: create a function in argonaut to read correctly the fusiondirectory config [Fix] Bugs #5288: function branch_exists should go into common.pm [Fix] Bugs #5300: Errors when I try to start argonaut-client * Argonaut 0.9.8 [Feature] Bugs #5059: module for creating samba shares [Fix] Bugs #5146: argonaut fuse is broken and doesnt add the initrd=initrd.img-install line to pxe configuration for fai [Fix] Bugs #5147: removed man file created in pxelinux.cfg and replace it by a default file [Fix] Bugs #5148: the default mode stored inside the argonaut-fuse should be used in argonaut-fuse [Fix] Bugs #5158: FAI_ARGONAUT variable is not used inside the script [Fix] Bugs #5159: we should add some debug to the ldap query in fai.pm [Fix] Bugs #5164: argonaut-fai-monitor typo * Argonaut 0.9.7 [Fix] Bugs #4691: the systemd unit files have a syntax error [Feature] Bugs #4864: migrate the script for user-reminder to the argonaut-framework [Feature] Bugs #4865: create argonaut-clean-audit to remove old audit entries from ldap * Argonaut 0.9.6 [Feature] Bugs #4493: It would be nice to accept more than one reverse zone with argonaut-ldap2zone [Feature] Bugs #4494: Add aaaa record in argonaut-ldap2zone [Feature] Bugs #4503: Merge reverse zone with argonaut-ldap2zone [Feature] Bugs #4506: Support for split horizon should be added [Fix] Bugs #4513: Argonaut-ldap2zone have an perl error when we not have a reverse zone in the LDAP [Feature] Bugs #4607: add a --ldap2acl option in ldap2zone * Argonaut 0.9.5 [Fix] Bugs #4293: Problem when I try to restart a service on Centos7 [Fix] Bugs #4432: renomed and added the Service.systemd.pm and System.systemd.pm * Argonaut 0.9.4 [Fix] Bugs #4110: Remove all uses of gotoBootKernel [Fix] Bugs #4291: Error when I try to grab centos packages with an argonaut-server on centos [Fix] Bugs #4293: Problem when I try to restart a service on Centos7 [Fix] Bugs #4322: generate-fusioninventory-schema should read Agent/Inventory.pm directly from an installed fusioninventory-agent [Fix] Bugs #4355: Use App::Daemon for argonaut-fai-monitor and argonaut-fuse * Argonaut 0.9.3 [Feature] Bugs #943: argonaut should work on ssl mode https [Fix] Bugs #3791: Add TXT values in global zone record [Feature] Bugs #3803: ldap2zone needs an option to not write reverse zone [Feature] Bugs #3945: Add a option to not touch at the reverse zone [Fix] Bugs #4046: I have an error in argonaut-fai-monitor log when I try to use it [Feature] Bugs #4048: read the configuration from argonaut-fai-monitor service [Feature] Bugs #4049: split argonaut-fai-monitor in his own package [Fix] Bugs #4052: Argonaut-server must support that we send a MAC in uppercase [Fix] Bugs #4166: update all manpages for 0.9.3 * Argonaut 0.9.2 [Feature] Bugs #2229: adding software on demand onto the opsi service [Fix] Bugs #2454: gotoLdapServer seems unused [Fix] Bugs #2577: Reboot with Opsi Client [Fix] Bugs #2906: debconf2ldif is missing an help option [Fix] Bugs #3140: adding debconf and freeradius to the argonaut 0.9.2 branch [Fix] Bugs #3146: OPSI havewrong module place [Fix] Bugs #3155: when I want to configure OPSI softwarelist with FD I got : Erreur: Request error: Error : No such a method : 'Argonaut.ClientDaemon.Modules.OPSI.get_localboots'. [Fix] Bugs #3286: put comment around pod explanation in argonaut-server to correct error in manpage [Fix] Bugs #3376: No client module can handle action Deployment.reinstall for target ff:ff:aa:aa:aa:aa [Fix] Bugs #3382: quota plugins et block size [Fix] Bugs #3390: rename ldap2fai into argonaut-ldap2fai to remove potential clash with the goto software from gonicus [Fix] Bugs #3449: fusioninventory ldap schema generator is not in git anymore [Feature] Bugs #3481: adding centos/rpm support to argonaut Packages.pm library [Fix] Bugs #3526: add a switch to select the good library when in wheezy or jessie for argonaut-client [Fix] Bugs #3541: fail2ldif manpage is wrong in the description [Fix] Bugs #3542: when the parent serveur is on level up than the son for the fai repository it doesnt find it correctly [Fix] Bugs #3543: when asking for the help of the fai2ldif command we get an extra h 1 [Feature] Bugs #3556: Support de Centos dans argonaut-common-fai [Fix] Bugs #3558: argonaut-common-fai - Création de partition LVM [Fix] Bugs #3574: fusioninventory ldap schema generator is not in git anymore [Fix] Bugs #3576: clean and rename the freeradius argonaut code [Fix] Bugs #3611: we should merge fai-monitor and argonaut-fai-monitor [Fix] Bugs #3612: argonaut fuse module fai should check if the system is lock or not [Fix] Bugs #3646: argonaut-fuse should be cleaned up [Fix] Bugs #3649: fai2ldif -o missed some information [Fix] Bugs #3656: fai2ldif doesnt convert the script associated to a class inside a class [Fix] Bugs #3657: argonaut-repository doesnt find the parent depot server [Fix] Bugs #3658: logfile name is wrong in argonaut-repository [Fix] Bugs #3661: fai2ldif creates a package list even if there is no need [Feature] Bugs #3662: Convert yumgroup to ldif [Fix] Bugs #3664: Daemon for argonaut-fai-monitor [Fix] Bugs #3665: remove debconf code as it is glpv3 only [Fix] Bugs #3668: timeout when using distant repository for debian packages [Fix] Bugs #3697: Ask task id back with the error "This task does not exists" [Fix] Bugs #3707: update all man pages to 0.9.2 and adapt date also [Fix] Bugs #3714: argonaut-repository should have a --verbose option [Fix] Bugs #3715: argonaut2repository tell it cannot find parent servers of parent servers [Feature] Wishlist #3314: optional "named-checkconf -z" after running argonaut-ldap2zone on dns servers with output to see if configuration is correct * Argonaut 0.9.1 [Fix] Bugs #282: in fai-backend the fai-backend hooks don't finish correctly if NFSROOT_LOCALE is used [Fix] Bugs #303: when using setup storage with latest svn ldap2fai always return Invalid disk config entry 'cn=toto,cn=DEBIAN-DISK,ou=disk, ...' - skipped [Fix] Bugs #305: when using setup storage fonctionnality the size of the partition is wrongly writtent in the ldap tree [Fix] Bugs #576: fuse and server init.d scripts fail if interface is not up yet [Fix] Bugs #583: FAIstate stuck at "install" [Fix] Bugs #1364: mirror-update-cronjob couldn't be executed [Fix] Bugs #2032: clean the fai classes in ldif format to be more easy to use [Fix] Bugs #2230: Inverted logic for FAI mirror [Fix] Bugs #2439: Argonaut-fuse stuck on mounting filesystem [Fix] Bugs #2455: Argonaut FAI server module is not working [Feature] Bugs #2459: create an argonaut-client module for dovecot [Fix] Bugs #2475: Error while running make-fai-nfsroot (in Wheezy) [Fix] Bugs #2582: argonaut-fai-client - modified to use fai live boot [Fix] Bugs #2887: adding manpages and licence [Fix] Bugs #2889: cleanup copyright and manpages [Fix] Bugs #2890: remove old code [Fix] Bugs #2891: move code into the correct directories [Feature] Bugs #2905: ldap2fai should have a mode when given a directory containing fai config file it make ldif out of them [Fix] Bugs #2909: Could not download deb http://debian.der.edf.fr/debian-security//dists/wheezy/main/binary-amd64/Packages.bz2 in the case of an update repository [Fix] Bugs #2911: argonaut server service should have an option to not get packages even if a mirror is created in FusionDirectory [Fix] Bugs #2959: removing argonaut-fai-progress [Fix] Bugs #2960: removing argonaut-fusioninventory [Fix] Bugs #2961: make-fai-nfsroot is now called fai-make-nfsroot [Fix] Bugs #2964: clean the argonaut-fai-server source [Fix] Bugs #2965: create a replacement for fai-monitor-gui that send status about fai client to argonaut [Fix] Bugs #2968: removing the argonaut-fai-client package and moving tools into argonaut-nfsroot packages [Fix] Bugs #2972: Argonaut doesn't do TLS (with beginnings of patch) [Fix] Bugs #2973: error message in argonaut tools should not send critical data to the console [Fix] Bugs #2974: Useless functions in Argonaut::Common [Fix] Bugs #2975: argonaut-fuse is using old code and should be migrated to argonaut-common ldap code [Fix] Bugs #2976: removing old obsolete option from argonaut-fuse [Fix] Bugs #2980: in argonaut-fuse when it create the file to be put in pxeling.cfg it doesnt put the ip in the commentary [Fix] Bugs #2981: in argonaut-nfsroot-integration script we can not symlink vmlinuz-install and initrd.img [Fix] Bugs #2982: remove tftp_static_root fonctionnality from argonaut-fuse [Fix] Bugs #2983: remove commented module option loading [Fix] Bugs #2985: group membership checking in argonaut-fuse should be reworked [Fix] Bugs #2986: remove old integration scripts inside the nfsroot [Fix] Bugs #2990: moving the get-config-dir-argonaut to the argonaut-fai-nfsroot package [Fix] Bugs #2991: bug with verbose mode in fai when running ldap2fai [Fix] Bugs #2992: we need a subroutine in ldap2fai to create dirs in the config space of fai client [Fix] Bugs #2993: ldap2fai doesnt write the script stored in the ldap tree [Fix] Bugs #2994: ldap2fai crash when trying to export variables [Fix] Bugs #2995: options of ldap2fai should be reorganised to be more logical [Fix] Bugs #2998: adding the argonaut-fai-monitor to the argonaut-fai-server package [Fix] Bugs #2999: the protocol is stored as http inside the argonautProtocol attribute but the code does no add :// after it [Fix] Bugs #3000: error in the filter when running argonaut-server and FAi module [Fix] Bugs #3001: remove fixed code inside ldap2fai [Fix] Bugs #3003: redo the get-config-dir-argonaut to make it more standard [Fix] Bugs #3004: Config file read code is duplicated [Fix] Bugs #3008: perl error : Global symbol "$client_ip" [Fix] Bugs #3009: perl error : Global symbol "$server_ip" [Fix] Bugs #3010: error when trying to create the cronjob for creating debian mirror [Fix] Bugs #3012: Name "main::ID" used only once: possible typo at /usr/sbin/argonaut-fai-monitor line 98, line 558. [Fix] Bugs #3013: argonaut-repository make use of /usr/lib/argonaut, but it doesnt exist anymore [Fix] Bugs #3016: rename argonaut-apply-quota to argonaut-quota to be more in sync with all the tools [Fix] Bugs #3017: rename argonaut-client-fai-getid to remove the fai inside the name [Fix] Bugs #3018: argonaut-client-fai-sendmon should be removed now that we are using argonaut-fai-monitor to integrate directly with faimond [Fix] Bugs #3019: remove empty lib dir in argonaut-fai-server [Fix] Bugs #3025: moving fai2ldif to argonaut-fai-server [Fix] Bugs #3043: OPSI.pm needs a task_processed method [Feature] Bugs #3046: adding three variable into argonaut-nfsroot-integration [Fix] Bugs #3054: Problem installing argonaut packages [Fix] Bugs #3057: argonaut-server doesn't run [Fix] Bugs #3061: FAL ssl certs not copied if /etc/ssl doesn't exist [Fix] Bugs #3062: argonaut-fuse [Fix] Bugs #3068: Weird issues with custom ldap.conf in argonaut.conf [Fix] Bugs #3069: fai.conf isn't copied in the livefs when runninf fai-setup [Fix] Bugs #3070: argonaut.conf copied on new FAI setup isn't correct (wrong IP) [Feature] Bugs #3093: add an option to not refresh zone when running ldap2zone from console [Feature] Bugs #3094: add an option to wirte the zone file in an other location for testing purpose [Fix] Bugs #3101: Can't call method "start_tls" on an undefined value at /usr/share/perl5/Argonaut/Libraries/Common.pm line 176 [Fix] Bugs #3119: argonaut.conf isn't copied in right place during fai-setup [Fix] Bugs #3130: add a switch to select the good library when in wheezy or jessie for argonaut-client [Fix] Bugs #3131: add a switch to select the good library when in wheezy or jessie for argonaut-server [Fix] Bugs #3133: fetching list of package [Fix] Bugs #3135: all the argonaut tools should check for the presence of the console tools they need and abort if not present [Fix] Bugs #3136: argonaut client didn't start anymore [Fix] Bugs #3139: since argonaut-fai-monitor argonaut-getid is not needed anymore [Fix] Bugs #3142: Clearer error messages are needed for fai-monitor [Fix] Bugs #3143: the fai installing does not work [Fix] Bugs #3154: when plan a (re) install action for FAI I always got a task "reboot" in error [Fix] Bugs #3166: put back the opsi.pm argonaut server component back into argonaut 0.9.1 [Fix] Bugs #3167: put back the opsi.pm fuse component back into argonaut 0.9.1 [Fix] Bugs #3173: When trying to add a OPSI profile to a windows workstation I got an error [Fix] Bugs #3176: Switch modules is uselessly loaded [Fix] Bugs #3180: fai2ldif misses a man page [Fix] Bugs #3183: argonaut-fai-monitor need a manpage [Fix] Wishlist #558: special default classes [Fix] Wishlist #1978: Being able of getting settings with asref in get_generic_settings * Argonaut 0.9 First upstream release argonaut-1.0/README000066400000000000000000000000001304135723100140370ustar00rootroot00000000000000argonaut-1.0/README.md000066400000000000000000000003631304135723100144520ustar00rootroot00000000000000[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=fusiondirectory&url=https://github.com/fusiondirectory&title=FusionDirectory&language=en_GB&tags=github&category=software) argonaut-1.0/argonaut-client/000077500000000000000000000000001304135723100162655ustar00rootroot00000000000000argonaut-1.0/argonaut-client/Argonaut/000077500000000000000000000000001304135723100200455ustar00rootroot00000000000000argonaut-1.0/argonaut-client/Argonaut/ClientDaemon.pm000066400000000000000000000032231304135723100227450ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon -- Action to be done on clients # # Copyright (C) 2011-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::ClientDaemon; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:config); my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item echo return the parameters passed to it =cut sub echo : Public { my ($s, $args) = @_; $main::log->notice("echo method called with args $args"); return $args; } package Argonaut::ClientDaemon::system; =item describe should be the answer of the system.describe standard JSONRPC call. It seems broken. =cut sub describe { return { sdversion => "1.0", name => 'Argonaut::ClientDaemon', }; } 1; __END__ argonaut-1.0/argonaut-client/Argonaut/ClientDaemon/000077500000000000000000000000001304135723100224075ustar00rootroot00000000000000argonaut-1.0/argonaut-client/Argonaut/ClientDaemon/ModulesInitd/000077500000000000000000000000001304135723100250075ustar00rootroot00000000000000argonaut-1.0/argonaut-client/Argonaut/ClientDaemon/ModulesInitd/Service.pm000066400000000000000000000056211304135723100267510ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon::Modules::Service -- Service management # Init.d version # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::ClientDaemon::ModulesInitd::Service; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config); my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item getServiceName Returns the local name of a service =cut sub getServiceName : Private { my ($nameFD) = @_; my ($ldap,$ldap_base) = argonaut_ldap_handle($main::config); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(objectClass=argonautClient)(ipHostNumber=".$main::client_settings->{'ip'}."))", attrs => [ 'argonautServiceName' ] ); if (scalar($mesg->entries)==1) { foreach my $service (($mesg->entries)[0]->get_value("argonautServiceName")) { my ($name,$value) = split(':',$service); return $value if ($name eq $nameFD); } } die "Service not found"; } =item manage execute an action on a service return a string that begins with "done" if it worked. =cut sub manage : Public { my ($s, $args) = @_; my ($service,$action) = @{$args}; my $folder = getServiceName("folder"); my $exec = getServiceName($service); $main::log->notice("manage service called: $service ($folder/$exec) $action"); system ("$folder/$exec $action\n") == 0 or die "$folder/$exec $action returned error $?";; return "done : $action $exec"; } =item is_running returns "yes" or "no" wether if a service is running or not =cut sub is_running : Public { my ($s, $args) = @_; my ($service) = @{$args}; my $folder = getServiceName("folder"); my $exec = getServiceName($service); $main::log->notice("is_running service called: $service ($folder/$exec) status"); my $lsb_code = system ("$folder/$exec status\n"); if ($lsb_code == 0) { return "yes"; } else { return "no"; } } 1; __END__ argonaut-1.0/argonaut-client/Argonaut/ClientDaemon/ModulesInitd/System.pm000066400000000000000000000032201304135723100266260ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon::Modules::System -- System management # init.d version # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::ClientDaemon::ModulesInitd::System; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config); my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item halt shutdown the computer =cut sub halt : Public { my ($s, $args) = @_; $main::log->notice("halt called"); system("sleep 5 && halt &"); return "shuting down"; } =item reboot reboot the computer =cut sub reboot : Public { my ($s, $args) = @_; $main::log->notice("reboot called, rebooting…"); system("sleep 5 && reboot &"); return "rebooting"; } 1; __END__ argonaut-1.0/argonaut-client/Argonaut/ClientDaemon/ModulesSystemd/000077500000000000000000000000001304135723100253705ustar00rootroot00000000000000argonaut-1.0/argonaut-client/Argonaut/ClientDaemon/ModulesSystemd/Service.pm000066400000000000000000000054561304135723100273400ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon::Modules::Service -- Service management # Systemd version # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::ClientDaemon::ModulesSystemd::Service; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config); my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item getServiceName Returns the local name of a service =cut sub getServiceName : Private { my ($nameFD) = @_; my ($ldap,$ldap_base) = argonaut_ldap_handle($main::config); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(objectClass=argonautClient)(ipHostNumber=".$main::client_settings->{'ip'}."))", attrs => [ 'argonautServiceName' ] ); if (scalar($mesg->entries)==1) { foreach my $service (($mesg->entries)[0]->get_value("argonautServiceName")) { my ($name,$value) = split(':',$service); return $value if ($name eq $nameFD); } } die "Service not found"; } =item manage execute an action on a service return a string that begins with "done" if it worked. =cut sub manage : Public { my ($s, $args) = @_; my ($service,$action) = @{$args}; my $exec = getServiceName($service); $main::log->notice("manage service called: $service ($exec) $action"); system ("systemctl $action $exec\n") == 0 or die "systemctl $action $exec returned error $?";; return "done : $action $exec"; } =item is_running returns "yes" or "no" wether if a service is running or not =cut sub is_running : Public { my ($s, $args) = @_; my ($service) = @{$args}; my $exec = getServiceName($service); $main::log->notice("is_running service called: $service ($exec) status"); my $code = system ("systemctl status $exec\n"); if ($code == 0) { return "yes"; } else { return "no"; } } 1; __END__ argonaut-1.0/argonaut-client/Argonaut/ClientDaemon/ModulesSystemd/System.pm000066400000000000000000000032711304135723100272150ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon::Modules::System -- System management # systemd version # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::ClientDaemon::ModulesSystemd::System; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config); my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item halt shutdown the computer =cut sub halt : Public { my ($s, $args) = @_; $main::log->notice("systemctl poweroff called"); system("sleep 5 && systemctl poweroff &"); return "shuting down"; } =item reboot reboot the computer =cut sub reboot : Public { my ($s, $args) = @_; $main::log->notice("reboot called, rebooting…"); system("sleep 5 && systemctl reboot &"); return "rebooting"; } 1; __END__ argonaut-1.0/argonaut-client/bin/000077500000000000000000000000001304135723100170355ustar00rootroot00000000000000argonaut-1.0/argonaut-client/bin/argonaut-client000066400000000000000000000110011304135723100220450ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # argonaut-client-management - standalone binary # # Copyright (C) 2011-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config :file); use if (USE_LEGACY_JSON_RPC), 'JSON::RPC::Legacy::Server::Daemon'; use if not (USE_LEGACY_JSON_RPC), 'JSON::RPC::Server::Daemon'; use Log::Handler; use App::Daemon qw(daemonize); # where to look for modules files use Module::Pluggable search_path => 'Argonaut::ClientDaemon::Modules', sub_name => 'modules'; our ($config, $client_settings, $server_settings); my $logfile = "argonaut-client.log"; my $piddir = "/var/run/argonaut"; my $pidfile = "argonaut-client.pid"; $SIG{TERM}=\&sig_term_handler; $SIG{INT}=\&sig_int_handler; readConfig(); argonaut_create_dir($client_settings->{'logdir'}); our $log = Log::Handler->create_logger("argonaut-client-management"); $log->add( file => { filename => $client_settings->{'logdir'}."/$logfile", maxlevel => "debug", minlevel => "emergency", newline => 1, } ); argonaut_create_dir($piddir); $App::Daemon::pidfile = "$piddir/$pidfile"; $App::Daemon::logfile = $client_settings->{'logdir'}."/$logfile"; $App::Daemon::as_user = "root"; daemonize(); my $serverClass; if (USE_LEGACY_JSON_RPC) { $serverClass = "JSON::RPC::Legacy::Server::Daemon"; } else { $serverClass = "JSON::RPC::Server::Daemon"; } my $server = $serverClass->new( LocalPort => $client_settings->{'port'}, ($client_settings->{'protocol'} eq 'https') ? (SSL_server => 1, SSL_key_file => $client_settings->{'keyfile'}, SSL_cert_file => $client_settings->{'certfile'}, SSL_ca_file => $client_settings->{'cacertfile'}, ) : ()); $log->notice("argonaut-client-management started on port ".$client_settings->{'port'}); $server->version(0); $server->return_die_message(1); my $modules = import_modules(); $server->dispatch_to($modules)->handle(); sub readConfig { $config = argonaut_read_config; $server_settings = argonaut_get_server_settings($config,$config->{'server_ip'}); $client_settings = argonaut_get_client_settings($config,$config->{'client_ip'}); } sub import_modules { foreach my $module (modules()) { $log->notice("Loaded module $module"); } return ['Argonaut::ClientDaemon',modules()]; } sub sig_int_handler { $log->notice("argonaut-client-management on port ".$client_settings->{'port'}." terminated by sigint"); exit(0); } sub sig_term_handler { $log->notice("argonaut-client-management on port ".$client_settings->{'port'}." terminated by sigterm"); exit(0); } __END__ =head1 NAME argonaut-client - running actions given by the argonaut server =head1 SYNOPSIS argonaut-client =head1 DESCRIPTION argonaut-client is getting actions from argonaut server and run them. It is modular and can load various modules at run time. =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 1 =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-client/lib/000077500000000000000000000000001304135723100170335ustar00rootroot00000000000000argonaut-1.0/argonaut-client/lib/systemd/000077500000000000000000000000001304135723100205235ustar00rootroot00000000000000argonaut-1.0/argonaut-client/lib/systemd/system/000077500000000000000000000000001304135723100220475ustar00rootroot00000000000000argonaut-1.0/argonaut-client/lib/systemd/system/argonaut-client.service000066400000000000000000000003501304135723100265230ustar00rootroot00000000000000[Unit] Description=Start argonaut-client ConditionPathExists=/usr/sbin/argonaut-client [Service] Type=forking ExecStart=/usr/sbin/argonaut-client PIDFile=/var/run/argonaut/argonaut-client.pid [Install] WantedBy=multi-user.target argonaut-1.0/argonaut-client/man/000077500000000000000000000000001304135723100170405ustar00rootroot00000000000000argonaut-1.0/argonaut-client/man/argonaut-client.1000066400000000000000000000116601304135723100222220ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-CLIENT 1" .TH ARGONAUT-CLIENT 1 "2016-01-06" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-client \- running actions given by the argonaut server .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-client .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-client is getting actions from argonaut server and run them. It is modular and can load various modules at run time. .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2011\-2016 FusionDirectory project" 1 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-common/000077500000000000000000000000001304135723100162775ustar00rootroot00000000000000argonaut-1.0/argonaut-common/Argonaut/000077500000000000000000000000001304135723100200575ustar00rootroot00000000000000argonaut-1.0/argonaut-common/Argonaut/Libraries/000077500000000000000000000000001304135723100217735ustar00rootroot00000000000000argonaut-1.0/argonaut-common/Argonaut/Libraries/Common.pm000066400000000000000000000657401304135723100235750ustar00rootroot00000000000000####################################################################### # # Argonaut::Libraries::Common -- Argonaut basic functions. # # Copyright (c) 2008 Landeshauptstadt München # Copyright (C) 2011-2016 FusionDirectory project # # Authors: Matthias S. Benkmann # Come Bernigaud # Benoit Mortier # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Libraries::Common; use strict; use warnings; use 5.008; use JSON::RPC (); use constant USE_LEGACY_JSON_RPC => ($JSON::RPC::VERSION > 0.96); use Net::LDAP; use Net::LDAP::Constant qw(LDAP_NO_SUCH_OBJECT LDAP_REFERRAL); use URI; use File::Path; use Config::IniFiles; use Digest::SHA; use MIME::Base64; my $iptool = "ifconfig"; my $die_endl = "\n"; # Change to "" to have verbose dies my $configfile = "/etc/argonaut/argonaut.conf"; BEGIN { use Exporter (); use vars qw(%EXPORT_TAGS @ISA $VERSION); $VERSION = '2011-04-11'; @ISA = qw(Exporter); %EXPORT_TAGS = ( 'ldap' => [qw( &argonaut_ldap_parse_config &argonaut_ldap_parse_config_ex &argonaut_ldap_parse_config_multi &argonaut_ldap_fsearch &argonaut_ldap_rsearch &argonaut_ldap_is_single_result &argonaut_ldap_split_dn &argonaut_ldap_branch_exists &argonaut_ldap_init &argonaut_ldap_handle &argonaut_read_ldap_config &argonaut_get_generic_settings &argonaut_get_client_settings &argonaut_get_server_settings &argonaut_get_crawler_settings &argonaut_get_ldap2zone_settings &argonaut_get_fuse_settings )], 'file' => [qw( &argonaut_file_write &argonaut_file_chown &argonaut_options_parse &argonaut_get_mac_pxe &argonaut_create_dir )], 'array' => [qw( &argonaut_array_find_and_remove )], 'string' => [qw( &argonaut_gen_random_str &argonaut_gen_ssha_token &argonaut_check_ssha_token )], 'net' => [qw( &argonaut_get_mac )], 'config' => [qw( &argonaut_read_config USE_LEGACY_JSON_RPC )] ); Exporter::export_ok_tags(keys %EXPORT_TAGS); } #----------------------------------------------------------------------------- # routine to get mac from a defined interface # # $interface = name of the interface # # Returns the mac of the interface # sub argonaut_get_mac { my ($interface) = @_; my $mac = `LANG=C $iptool $interface | awk '/$interface/{ print \$5 }'`; chomp ($mac); return $mac; } #----------------------------------------------------------------------------- # routine to get mac from a pxe file # # $filename = name of the pxe file # # Returns the mac of the interface # sub argonaut_get_mac_pxe { my ($filename) = @_; my $mac = $filename; $mac =~ tr/-/:/; $mac = substr( $mac, -1*(5*3+2) ); chomp ($mac); return $mac; } #------------------------------------------------------------------------------ # Common LDAP initialization routine # # $ldap_conf = LDAP config file - may be undef # $prompt_dn = Prompt user for bind dn if true # $bind_dn = Use DN to bind to LDAP server # $prompt_pwd = Prompt user for bind password if true # $bind_pwd = Use password to bind to LDAP server # $obfuscate_pwd = Show stars instead omiting echo # # Returns a hash of results # 'BASE' => LDAP search base from config # 'URIS' => LDAP server URIs from config # 'HANDLE' => Net::LDAP handle # 'BINDDN' => Bind DN from config or prompt # 'BINDPWD' => Bind password from prompt # 'BINDMSG' => Bind result messages # 'CFGFILE' => Config file used # 'ERROR' => Error Number # 'ERRORMSG' => Error Message # # These values are just filled, if they weren't provided, # i.e. # sub argonaut_ldap_init { my( $ldap_conf, $prompt_dn, $bind_dn, $prompt_pwd, $bind_pwd, $obfuscate_pwd, $ldap_tls ) = @_; my %results; undef $bind_dn if ($bind_dn eq ''); # Parse ldap config my ($base,$ldapuris,$tlsoptions) = argonaut_ldap_parse_config( $ldap_conf ); %results = ( 'BASE' => $base, 'URIS' => $ldapuris); if ( ! defined $base ) { %results = ( 'ERROR' => 1, 'ERRORMSG' => "Couldn't find LDAP base in config!"); return \%results; } if ( ! defined $ldapuris ) { %results = ( 'ERROR' => 1, 'ERRORMSG' => "Couldn't find LDAP URI in config!"); return \%results; } my $ldap = Net::LDAP->new( $ldapuris ); if ( ! defined $ldap ) { %results = ( 'ERROR' => 1, 'ERRORMSG' => "LDAP 'new' error: '$@' with parameters '".join(",",@{$ldapuris})."'"); return \%results; } if ($ldap_tls) { $ldap->start_tls( verify => $tlsoptions->{'REQCERT'}, clientcert => $tlsoptions->{'CERT'}, clientkey => $tlsoptions->{'KEY'}, capath => $tlsoptions->{'CACERTDIR'} ); } $results{ 'HANDLE' } = $ldap; # Prompt for DN if( (! defined $bind_dn) && (defined $prompt_dn && $prompt_dn) ) { $| = 1; print( 'Bind DN: ' ); $| = 0; $bind_dn = ; $results{ 'BINDDN' } = $bind_dn; } my $mesg; if( defined $bind_dn ) { if( defined $bind_pwd ) { $mesg = $ldap->bind( $bind_dn, password => $bind_pwd ); } elsif( defined $prompt_pwd && $prompt_pwd) { # Prompt for password $| = 1; print( 'Password: ' ); $| = 0; $bind_pwd = ''; # Disable terminal echo system "stty -echo -icanon"; my $inchr; while (sysread STDIN, $inchr, 1) { if (ord($inchr) < 32) { last; } $bind_pwd .= $inchr; syswrite( STDOUT, "*", 1 ) # print asterisk instead if( defined $obfuscate_pwd && $obfuscate_pwd ); } system "stty echo icanon"; $results{ 'BINDPWD' } = $bind_pwd; $mesg = $ldap->bind( $bind_dn, password => $bind_pwd ); } else { $mesg = $ldap->bind( $bind_dn ); } } else { $mesg = $ldap->bind(); $results{ 'BINDMSG' } = $mesg; } # Anonymous bind if ( $mesg->code != 0 ) { %results = ( 'ERROR' => 1, 'ERRORMSG' => "LDAP bind error: " . $mesg->error . "(" . $mesg->code . ")"); return \%results; } $results{ 'ERROR' } = 0; return \%results; } sub argonaut_ldap_handle { my ($config) = @_; my $ldapinfos = argonaut_ldap_init ($config->{'ldap_configfile'}, 0, $config->{'ldap_dn'}, 0, $config->{'ldap_password'}, 0, $config->{'ldap_tls'}); if ( $ldapinfos->{'ERROR'} > 0) { die $ldapinfos->{'ERRORMSG'}."$die_endl"; } return ($ldapinfos->{'HANDLE'},$ldapinfos->{'BASE'},$ldapinfos); } #------------------------------------------------------------------------------ sub argonaut_ldap_parse_config { my ($ldap_config) = @_; my $ldapconf; # Try to guess the location of the ldap.conf - file $ldap_config = $ENV{ 'LDAPCONF' } if (!defined $ldap_config && exists $ENV{ 'LDAPCONF' }); $ldap_config = "/etc/ldap/ldap.conf" if (!defined $ldap_config); $ldap_config = "/etc/openldap/ldap.conf" if (!defined $ldap_config); $ldap_config = "/etc/ldap.conf" if (!defined $ldap_config); # Read LDAP return if( ! open ($ldapconf,q{<},"${ldap_config}") ); my @content=<$ldapconf>; close($ldapconf); my ($ldap_base, @ldap_uris, %tls_options); # Scan LDAP config foreach my $line (@content) { $line =~ /^\s*(#|$)/ && next; chomp($line); if ($line =~ /^BASE\s+(.*)$/i) { $ldap_base= $1; next; } if ($line =~ /^URI\s+(.*)\s*$/i) { my (@ldap_servers) = split( ' ', $1 ); foreach my $server (@ldap_servers) { if ( $server =~ /^((ldap[si]?:\/\/)([^\/:\s]+)?(:([0-9]+))?\/?)$/ ) { my $ldap_server = $3 ? $1 : $2.'localhost'; $ldap_server =~ s/\/\/127\.0\.0\.1/\/\/localhost/; push @ldap_uris, $ldap_server if ( ! grep { $_ =~ /^$ldap_server$/ } @ldap_uris ); } } next; } if ($line =~ m/^TLS_(REQCERT|CERT|KEY|CACERTDIR)\s+(.*)\s*$/) { $tls_options{$1} = $2; next; } } return( $ldap_base, \@ldap_uris, \%tls_options); } # Split the dn (works with escaped commas) sub argonaut_ldap_split_dn { my ($dn) = @_; # Split at comma my @comma_rdns = split( ',', $dn ); my @result_rdns = (); my $line = ''; foreach my $rdn (@comma_rdns) { # Append comma and rdn to line if( '' eq $line ) { $line = $rdn; } else { $line .= ',' . $rdn; } # Count the backslashes at the end. If we have even length # of $bs add to result array and set empty line my($bs) = $rdn =~ m/([\\]+)$/; $bs = "" if( ! defined $bs ); if( 0 == (length($bs) % 2) ) { push( @result_rdns, $line ); $line = ""; } } return @result_rdns; } # Check if a designated branch exists sub argonaut_ldap_branch_exists { my ($ldap, $branch) = @_; # search for branch my $branch_mesg = $ldap->search (base => $branch, filter => '(objectClass=*)', scope => 'base'); if ($branch_mesg->code == LDAP_NO_SUCH_OBJECT) { return 0; } $branch_mesg->code && die "Error while searching for branch \"$branch\":".$branch_mesg->error; my @entries = $branch_mesg->entries; return (defined ($entries[0])); } #------------------------------------------------------------------------------ # sub argonaut_file_write { my @opts = @_; my $len = scalar @_; ($len < 2) && return; my $filename = shift; my $data = shift; my $script; open ($script,q{>},${filename}) || warn "Can't create ${filename}. $!\n"; print $script $data; close ($script); ($opts[2] ne "") && chmod oct($opts[2]),${filename}; ($opts[3] ne "") && argonaut_file_chown(${filename}, $opts[3]); } #------------------------------------------------------------------------------ # sub argonaut_file_chown { my @owner = split('.',$_[1]); my $filename = $_[0]; my ($uid,$gid); $uid = getpwnam($owner[0]); $gid = getgrnam($owner[1]); chown $uid, $gid, $filename; } =item argonaut_create_dir Create a directory =cut sub argonaut_create_dir { my ($dir) = @_; mkdir($dir,0755); } #------------------------------------------------------------------------------ # # Common checks for forward and reverse searches # sub argonaut_ldap_search_checks { my( $base, $sbase ) = (@_)[1,2]; if( scalar @_ < 3 ) { warn( "argonaut_ldap_search needs at least 3 parameters" ); return; }; if( defined $sbase && (length($sbase) > 0) ) { # Check, if $sbase is a base of $base if( $sbase ne substr($base,-1 * length($sbase)) ) { warn( "argonaut_ldap_search: (1) '$sbase' isn't the base of '$base'" ); return; } $base = substr( $base, 0, length( $base ) - length( $sbase ) ); # Check, if $base ends with ',' after $sbase strip if( ',' ne substr( $base, -1 ) ) { warn( "argonaut_ldap_search: (2) '$sbase' isn't the base of '$base'" ); return; } $base = substr( $base, 0, length($base) - 1 ); $sbase = ',' . $sbase; } else { $sbase = ''; } return( $base, $sbase ); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $ldap = Net::LDAP handle # $base = Search base ( i.e.: ou=test,ou=me,ou=very,ou=well ) # $sbase = Stop base ( i.e.: ou=very,ou=well ) # $filter = LDAP filter # $scope = LDAP scope # $subbase = On every $base look into $subbase,$base ( i.e.: ou=do ) # $attrs = Result attributes # # Example searches in: # ou=do,ou=test,ou=me,ou=very,ou=well # ou=do,ou=me,ou=very,ou=well # ou=do,ou=very,ou=well # # Returns (Net::LDAP::Search, $search_base) on LDAP failure # Returns (Net::LDAP::Search, $search_base) on success # Returns undef on non-LDAP failures # sub argonaut_ldap_rsearch { my ($ldap,$base,$sbase,$filter,$scope,$subbase,$attrs) = @_; ( $base, $sbase ) = argonaut_ldap_search_checks( @_ ); return if( ! defined $base ); my (@rdns,$search_base,$mesg); @rdns = argonaut_ldap_split_dn( $base ); return if( 0 == scalar @rdns ); while( 1 ) { # Walk the DN tree if (scalar @rdns == 0) { # We also want to search the stop base, if it was defined return if( ! defined $sbase ); if( length( $sbase ) > 0 ) { $search_base = substr( $sbase, 1 ); } else { $search_base = ''; } undef( $sbase ); } else { $search_base = join( ',', @rdns ); shift(@rdns); $search_base .= $sbase; } # Initialize hash with filter my %opts = ( 'filter' => $filter ); # Set searchbase if( defined $subbase && $subbase ) { $opts{ 'base' } = "${subbase},${search_base}" } else { $opts{ 'base' } = "${search_base}" } # Set scope $opts{ 'scope' } = "$scope" if( defined $scope && $scope ); $opts{ 'attrs' } = @$attrs if( defined $attrs ); # LDAP search # The referral chasing is much simpler then the OpenLDAP one. # It's just single level support, therefore it can't really # chase a trail of referrals, but will check a list of them. my @referrals; my $chase_referrals = 0; RETRY_SEARCH: $mesg = $ldap->search( %opts ); if( LDAP_REFERRAL == $mesg->code ) { # Follow the referral if( ! $chase_referrals ) { my @result_referrals = $mesg->referrals(); foreach my $referral (@result_referrals) { my $uri = new URI( $referral ); next if( $uri->dn ne $opts{ 'base' } ); # But just if we have the same base push( @referrals, $uri ); } $chase_referrals = 1; } NEXT_REFERRAL: next if( ! scalar @referrals ); my $uri = new URI( $referrals[ 0 ] ); $ldap = new Net::LDAP( $uri->host ); @referrals = splice( @referrals, 1 ); goto NEXT_REFERRAL if( ! defined $ldap ); $mesg = $ldap->bind(); goto NEXT_REFERRAL if( 0 != $mesg->code ); goto RETRY_SEARCH; } if( LDAP_NO_SUCH_OBJECT == $mesg->code ) { # Ignore missing objects (32) goto NEXT_REFERRAL if( scalar @referrals ); next; } return $mesg if( $mesg->code ); # Return undef on other failures last if( $mesg->count() > 0 ); } return( $mesg, ${search_base} ); } #------------------------------------------------------------------------------ # See argonaut_ldap_fsearch # # sbase = start base # # Example searches in: # ou=do,ou=very,ou=well # ou=do,ou=me,ou=very,ou=well # ou=do,ou=test,ou=me,ou=very,ou=well # sub argonaut_ldap_fsearch { my ($ldap,$base,$sbase,$filter,$scope,$subbase,$attrs) = @_; ( $base, $sbase ) = argonaut_ldap_search_checks( @_ ); return if( ! defined $base ); my (@rdns,$search_base,$mesg,$rdn_count); @rdns = reverse argonaut_ldap_split_dn( $base ); $rdn_count = scalar @rdns; return if( 0 == $rdn_count ); while( 1 ) { # Walk the DN tree if( ! defined $search_base ) { # We need to strip the leading ",", which is needed for research if( length( $sbase ) > 0 ) { $search_base = substr( $sbase, 1 ); } else { $search_base = ''; } } elsif( 0 == scalar @rdns ) { return; } else { $search_base = $rdns[ 0 ] . ',' . $search_base; shift(@rdns); } # Initialize hash with filter my %opts = ( 'filter' => $filter ); # Set searchbase if( defined $subbase && $subbase ) { $opts{ 'base' } = "${subbase},${search_base}"; } else { $opts{ 'base' } = "${search_base}"; } # Set scope $opts{ 'scope' } = "$scope" if( defined $scope && $scope ); $opts{ 'attrs' } = @$attrs if( defined $attrs ); # LDAP search $mesg = $ldap->search( %opts ); next if( $mesg->code == LDAP_NO_SUCH_OBJECT ); # Ignore missing objects (32) return $mesg if( $mesg->code ); # Return undef on other failures last if( $mesg->count() > 0 ); } return( $mesg, ${search_base} ); } #------------------------------------------------------------------------------ # function for reading argonaut config # sub argonaut_read_config { my %res = (); my $config = Config::IniFiles->new( -file => $configfile, -allowempty => 1, -nocase => 1); $res{'server_ip'} = $config->val( server => "server_ip", ""); $res{'client_ip'} = $config->val( client => "client_ip", ""); $res{'ldap_configfile'} = $config->val( ldap => "config", "/etc/ldap/ldap.conf"); $res{'ldap_dn'} = $config->val( ldap => "dn", ""); $res{'ldap_password'} = $config->val( ldap => "password", ""); $res{'ldap_tls'} = $config->val( ldap => "tls", "off"); if ($res{'ldap_tls'} !~ m/^off|on$/i) { warn "Unknown value for option ldap/tls: ".$res{'ldap_tls'}." (valid values are on/off)\n"; } $res{'ldap_tls'} = ($res{'ldap_tls'} =~ m/^on$/i); return \%res; } # Read a config in the LDAP sub argonaut_read_ldap_config { my ($ldap, $ldap_base, $config, $configfilter, $params) = @_; my $mesg = $ldap->search (base => $ldap_base, filter => $configfilter); if (($mesg->code != 0) && ($mesg->code != LDAP_NO_SUCH_OBJECT)) { die $mesg->error; } if ($mesg->count > 0) { while (my ($key,$value) = each(%{$params})) { if (ref $value eq ref []) { $config->{"$key"} = ($mesg->entries)[0]->get_value(@$value); } else { if (($mesg->entries)[0]->get_value("$value")) { $config->{"$key"} = ($mesg->entries)[0]->get_value("$value"); } else { $config->{"$key"} = ""; } } } } else { die "Could not find configuration node in the LDAP (filter:$configfilter)".$die_endl; } return ($mesg->entries)[0]; } #------------------------------------------------------------------------------ # generic functions for get settings functions # sub argonaut_get_generic_settings { my ($objectClass,$params,$config,$filter,$inheritance) = @_; unless (defined $inheritance) { $inheritance = 1; } my ($ldap,$ldap_base) = argonaut_ldap_handle($config); if ($filter =~ m/([0-9]{1,3}\.?){4}/ or $filter eq '*') { $filter = "(ipHostNumber=$filter)"; } elsif ($filter !~ m/^\(/) { $filter = "($filter)"; } my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(objectClass=$objectClass)$filter)" ); my $settings = { }; if(scalar($mesg->entries)==1) { $settings->{'dn'} = ($mesg->entries)[0]->dn(); $settings->{'mac'} = ($mesg->entries)[0]->get_value("macAddress"); $settings->{'ip'} = ($mesg->entries)[0]->get_value("ipHostNumber"); if (($mesg->entries)[0]->exists('gotoMode')) { $settings->{'locked'} = ($mesg->entries)[0]->get_value("gotoMode") eq 'locked'; } else { $settings->{'locked'} = 0; } while (my ($key,$value) = each(%{$params})) { if (ref $value eq ref []) { $settings->{"$key"} = ($mesg->entries)[0]->get_value(@$value); } else { if (($mesg->entries)[0]->get_value("$value")) { $settings->{"$key"} = ($mesg->entries)[0]->get_value("$value"); } else { $settings->{"$key"} = ""; } } } return $settings; } elsif(scalar($mesg->entries)==0) { unless ($inheritance) { die "This computer ($filter) is not configured in LDAP to run this module (missing service $objectClass).$die_endl"; } $mesg = $ldap->search( # perform a search base => $ldap_base, filter => $filter, attrs => [ 'dn', 'macAddress', 'gotoMode' ] ); if (scalar($mesg->entries)>1) { die "Several computers matches $filter.$die_endl"; } elsif (scalar($mesg->entries)<1) { die "There is no computer matching $filter.$die_endl"; } $settings->{'dn'} = ($mesg->entries)[0]->dn(); $settings->{'mac'} = ($mesg->entries)[0]->get_value("macAddress"); $settings->{'ip'} = ($mesg->entries)[0]->get_value("ipHostNumber"); if (($mesg->entries)[0]->exists('gotoMode')) { $settings->{'locked'} = ($mesg->entries)[0]->get_value("gotoMode") eq 'locked'; } else { $settings->{'locked'} = 0; } my $dn = ($mesg->entries)[0]->dn(); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(objectClass=$objectClass)(member=$dn))", attrs => [values(%{$params})] ); if(scalar($mesg->entries)==1) { while (my ($key,$value) = each(%{$params})) { if (($mesg->entries)[0]->get_value("$value")) { $settings->{"$key"} = ($mesg->entries)[0]->get_value("$value"); } else { $settings->{"$key"} = ""; } } return $settings; } else { die "This computer ($filter) is not configured in LDAP to run this module (missing service $objectClass).$die_endl"; } } else { die "Several computers matches $filter.$die_endl"; } } #------------------------------------------------------------------------------ # get server argonaut settings # sub argonaut_get_server_settings { my ($config,$ip) = @_; if ((not defined $ip) or ($ip eq "")) { $ip = "*"; } return argonaut_get_generic_settings( 'argonautServer', { 'ip' => "ipHostNumber", 'port' => "argonautPort", 'protocol' => "argonautProtocol", 'token' => "argonautServerToken", 'keyfile' => "argonautKeyPath", 'certfile' => "argonautCertPath", 'cacertfile' => "argonautCaCertPath", 'certcn' => "argonautCertCN", 'iptool' => "argonautIpTool", 'delete_finished_tasks' => "argonautDeleteFinished", 'fetch_packages' => "argonautFetchPackages", 'interface' => "argonautWakeOnLanInterface", 'logdir' => "argonautLogDir" }, $config,$ip ); } #------------------------------------------------------------------------------ # get client argonaut settings # sub argonaut_get_client_settings { return argonaut_get_generic_settings( 'argonautClient', { 'port' => "argonautClientPort", 'protocol' => "argonautClientProtocol", 'keyfile' => "argonautClientKeyPath", 'certfile' => "argonautClientCertPath", 'cacertfile' => "argonautClientCaCertPath", 'certcn' => "argonautClientCertCN", 'interface' => "argonautClientWakeOnLanInterface", 'logdir' => "argonautClientLogDir", 'taskidfile' => "argonautTaskIdFile" }, @_ ); } #------------------------------------------------------------------------------ # get crawler argonaut settings # sub argonaut_get_crawler_settings { return argonaut_get_generic_settings( 'argonautMirrorConfig', { 'mirrordir' => 'argonautMirrorDir', 'packagesfolder' => 'argonautCrawlerPackagesFolder', }, @_ ); } #------------------------------------------------------------------------------ # get ldap2zone settings # sub argonaut_get_ldap2zone_settings { return argonaut_get_generic_settings( 'argonautDNSConfig', { 'binddir' => 'argonautLdap2zoneBindDir', 'bindcachedir' => 'argonautLdap2zoneBindCacheDir', 'allownotify' => 'argonautLdap2zoneAllowNotify', 'allowupdate' => 'argonautLdap2zoneAllowUpdate', 'allowtransfer' => 'argonautLdap2zoneAllowTransfer', 'ttl' => 'argonautLdap2zoneTTL', 'rndc' => 'argonautLdap2zoneRndc', 'noreverse' => 'argonautLdap2zoneNoReverse', }, @_ ); } #------------------------------------------------------------------------------ # get fuse settings # sub argonaut_get_fuse_settings { return argonaut_get_generic_settings( 'argonautFuseConfig', { 'default_mode' => 'argonautFuseDefaultMode', 'logdir' => 'argonautFuseLogDir', 'pxelinux_cfg' => 'argonautFusePxelinuxCfg' }, @_ ); } #------------------------------------------------------------------------------ # # $search_result = Net::LDAP::Serach # $get_entry = boolean # # if $get_entry == true, return $entry # == false, return 1 # # returns 0 on failure # sub argonaut_ldap_is_single_result { my ($search_result,$get_entry) = @_; my $result = 0; if( (defined $search_result) && (0 == $search_result->code) && (1 == $search_result->count()) ) { if( defined $get_entry && $get_entry ) { $result = ($search_result->entries())[ 0 ]; } else { $result = 1; } } return $result; } #------------------------------------------------------------------------------ # sub argonaut_array_find_and_remove { my ($haystack,$needle) = @_; my $index = 0; foreach my $item (@$haystack) { if ($item eq $needle) { splice( @$haystack, $index, 1 ); return 1; } $index++; } return 0; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Generate a random string based on a symbolset # # @param int $strlen: length of result string # @param array ref: symbol set (optional) # @return string or undef # sub argonaut_gen_random_str { my ($strlen, $symbolset) = @_; return if( (! defined $strlen) || (0 > $strlen) ); return '' if( 0 == $strlen ); if( (! defined $symbolset) || ('ARRAY' ne ref( $symbolset )) || (0 >= scalar( @$symbolset )) ) { my @stdset = (0..9, 'a'..'z', 'A'..'Z'); $symbolset = \@stdset; } my $randstr = join '', map @$symbolset[rand @$symbolset], 0..($strlen-1); return $randstr; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Hash token using SSHA scheme # sub argonaut_gen_ssha_token { my ($token, $salt) = @_; if (not defined $salt) { $salt = argonaut_gen_random_str(8); } my $ctx = Digest::SHA->new(1); $ctx->add($token); $ctx->add($salt); return '{SSHA}'.encode_base64($ctx->digest.$salt, ''); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Check if token match ssha hash # sub argonaut_check_ssha_token { my ($hash, $token) = @_; my $salt = substr(decode_base64(substr($hash, 6)), 20); if ($hash eq argonaut_gen_ssha_token($token, $salt)) { return 1; } else { return 0; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Parse options from array into hash # # Copied from Net::LDAP # sub argonaut_options_parse { my %ret = @_; my $once = 0; for my $v (grep { /^-/ } keys %ret) { require Carp; $once++ or Carp::carp("deprecated use of leading - for options"); $ret{substr($v,1)} = $ret{$v}; } $ret{control} = [ map { (ref($_) =~ /[^A-Z]/) ? $_->to_asn : $_ } ref($ret{control}) eq 'ARRAY' ? @{$ret{control}} : $ret{control} ] if exists $ret{control}; \%ret; } END {} 1; __END__ # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-common/Argonaut/Libraries/FAI.pm000066400000000000000000001270531304135723100227400ustar00rootroot00000000000000####################################################################### # # Argonaut::FAI packages - functions to get info for install from ldap # # Copyright (c) 2008 Landeshauptstadt München # Copyright (C) 2011-2016 FusionDirectory project # # Authors: Jan-Marek Glogowski # Come Bernigaud # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Libraries::FAI; use strict; use warnings; use 5.008; use Data::Dumper; use Net::LDAP; use Net::LDAP::Constant qw(LDAP_NO_SUCH_OBJECT); use File::Path; use Argonaut::Libraries::Common qw(:ldap :string :file); BEGIN { use Exporter (); use vars qw(%EXPORT_TAGS @ISA $VERSION); $VERSION = '2016-06-01'; @ISA = qw(Exporter); %EXPORT_TAGS = ( 'flags' => [qw( FAI_FLAG_VERBOSE FAI_FLAG_DRY_RUN )] ); Exporter::export_ok_tags(keys %EXPORT_TAGS); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Constructor for Argonaut::FAI object # # $ldap = Net::LDAP handle # %options = Hash of options like (Net::LDAP) # sub new { my $self = shift; my $type = ref($self) || $self; my $args = &argonaut_options_parse; my $obj = bless {}, $type; $obj->{ 'LDAP' } = undef; $obj->{ 'flags' } = 0; foreach my $arg (keys %$args) { $obj->{ $arg } = $args->{ $arg }; } return $obj; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Get or set Net::LDAP handle # sub handle { my( $self, $ldap ) = @_; if( defined $ldap ) { return if( ! $ldap->isa( 'Net::LDAP' ) ); $self->{ 'LDAP' } = $ldap; } else { return $self->{ 'LDAP' }; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Get or set LDAP base DN # sub base { my( $self, $base ) = @_; if( defined $base ) { $self->{ 'base' } = $base; } else { return $self->{ 'base' }; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Get or set the dump directory # sub dumpdir { my( $self, $dumpdir ) = @_; if( defined $dumpdir ) { $self->{ 'dumpdir' } = $dumpdir; } else { return $self->{ 'dumpdir' }; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Get or set class flags # # Prints progress information to stdout use constant FAI_FLAG_VERBOSE => 1; # Suppresses any data use constant FAI_FLAG_DRY_RUN => 2; sub flags { my( $self, $flags ) = @_; if( defined $flags ) { if( 0 > $flags ) { $self->{ 'flags' } = 0; return; } $self->{ 'flags' } = $flags; } elsif( exists $self->{ 'flags' } ) { return $self->{ 'flags' }; } return; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $release = Release version # $force = Ignore cached values and recheck # # Returns \@rdns, \@releases # The versions RDNs and their corresponding release name. # sub release_check { my( $self, $release, $force ) = @_; my $fai_base = 'ou=fai,ou=configs,ou=systems'; my @result_rdns = (); # Return cached values if not enforced if( ! defined $force && $force ) { return $self->{ 'CHECKS' }{ $release } if( exists $self->{ 'CHECKS' }{ $release } ); } my $ldap = $self->{ 'LDAP' }; my $base = $self->{ 'base' }; my $mesg = $ldap->search( base => "$fai_base,$base", filter => "(&(objectClass=FAIbranch)(ou=$release))", attrs => [ 'ou', 'FAIstate' ], scope => 'sub' ); $mesg->code && return( sprintf( "Release not found (%s)!" . " Release LDAP base not accessible (%s) - LDAP error: %s\n", $release, "$fai_base,$base", $mesg->error ) ); my $full_base = 0; foreach my $entry ($mesg->entries()) { $full_base = 1; my $rdn = $entry->dn; $rdn =~ s/,$base$//; push( @result_rdns, $rdn ); } return( sprintf( "No release base for (%s) found!\n", $release ) ) if( ! $full_base ); $self->{ 'CHECKS' }->{ $release } = [ \@result_rdns ]; return( \@result_rdns ); } my %fai_items = ( 'debconf' => [ undef, 'FAIdebconfInfo' ], 'disk' => [ 'FAIpartitionTable', undef ], # FAIpartitionDisk, FAIpartitionEntry 'hooks' => [ 'FAIhook', 'FAIhookEntry', 'cn', 'FAItask', 'FAIscript' ], 'packages' => [ 'FAIpackageList', 'FAIpackageList' ], 'profiles' => [ 'FAIprofile' ], 'scripts' => [ 'FAIscript', 'FAIscriptEntry', 'cn', 'FAIpriority', 'FAIscript' ], 'templates' => [ 'FAItemplate', 'FAItemplateEntry' ], 'variables' => [ 'FAIvariable', 'FAIvariableEntry', 'cn', 'FAIvariableContent' ], ); # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $release = Release version # $flags = Bit flags for cache lookup # FAI_CACHE_GENERATE = generate cache if not available # FAI_CACHE_FORCE = force cache regeneration # Defaults to FAI_CACHE_GENERATE. # # Returns a hashref including the classes for the FAI types # $result->{ ''profile', 'hook', ... }->{ 'class' } # In case of profiles it points to a hashref of profile subclasses # use constant FAI_CACHE_GENERATE => 1; use constant FAI_CACHE_FORCE => 2; sub get_class_cache { my( $self, $release, $flags ) = @_; # Set variables from flags $flags = 1 if( ! defined $flags ); my $generate = $flags & FAI_CACHE_GENERATE ? 1 : 0; my $force = $flags & FAI_CACHE_FORCE ? 1 : 0; # Return cached values if not enforced or looked up if( !$force ) { return $self->{ 'FAI_TREES' }{ $release } if( exists $self->{ 'FAI_TREES' }{ $release } ); return if( ! $generate ); } return $self->generate_class_cache( $release, $force ); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $release = Release version # # Returns a hashref including the classes for the FAI types # $result->{ ''profile', 'hook', ... }->{ 'class' } # In case of profiles it points to a hashref of profile subclasses # sub generate_class_cache { my( $self, $release, $force ) = @_; my %cache = (); my( $rdns ) = $self->release_check( $release, $force ); return $rdns if( ref( $rdns ) ne 'ARRAY' ); my $ldap = $self->{ 'LDAP' }; my $base = $self->{ 'base' }; # Check all FAI OUs for classnames while( my( $type, $class ) = each %fai_items) { # We skip debconf infos next if( ! defined @{$class}[0] ); my $mesg = $ldap->search( base => "ou=${type},@{$rdns}[0],${base}", filter => '(objectClass=' . @{$class}[0] . ')', scope => 'one', attrs => [ 'cn', 'FAIclass', 'FAIstate' ]); next if( LDAP_NO_SUCH_OBJECT == $mesg->code ); # Skip non-existent objects return( "LDAP search error while searching for (objectClass=".@{$class}[0].") on base : ou=${type},@{$rdns}[0],${base}" . $mesg->error . ' (' . $mesg->code . ")\n" ) if( 0 != $mesg->code ); $cache{ $type } = (); next if( 0 == $mesg->count ); if( $type eq 'profiles' ) { next if( 0 == $mesg->count ); foreach my $entry ($mesg->entries()) { my $cn = $entry->get_value( 'cn' ); my $classlist_str = $entry->get_value( 'FAIclass' ); $cache{ $type }{ $cn }{ '_classes' } = (); $cache{ $type }{ $cn }{ '_state' } = $entry->get_value( 'FAIstate' ); foreach my $profile_class (split( ' ', $classlist_str )) { if( ":" eq substr( $profile_class, 0, 1 ) ) { warn( "Release '$cn' found in profile '$class' of '$release'." ); } else { push( @{$cache{ $type }{ $cn }{ '_classes' }}, $profile_class ); } } } } else { foreach my $entry ($mesg->entries()) { $cache{ $type }{ $entry->get_value( 'cn' ) } = undef; $cache{ 'debconf' }{ $entry->get_value( 'cn' ) } = undef if( 'packages' eq $type ); } } } $self->{ 'FAI_TREES' }{ $release } = \%cache; return \%cache; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $release = Release version # $flags = @see get_class_cache # # Returns a hashref including the classes for the FAI types # $result->{ ''profile', 'hook', ... }->{ 'class' } # In case of profiles it points to a hashref of profile subclasses # sub extend_class_cache { my( $self, $release, $flags ) = @_; # Set variables from flags $flags = 1 if( ! defined $flags ); my $generate = $flags & FAI_CACHE_GENERATE ? 1 : 0; my $force = $flags & FAI_CACHE_FORCE ? 1 : 0; my( $rdns ) = $self->release_check( $release, $force ); return $rdns if( ref( $rdns ) ne 'ARRAY' ); my %cache = (); my $cache_ref; # Return cached values if not enforced if( ! $force ) { $cache_ref = $self->{ 'FAI_TREES' }{ $release } if( exists $self->{ 'FAI_TREES' }{ $release } ); return $self->{ 'FAI_TREES' }{ $release } if( defined $cache_ref && defined $cache_ref->{ 'extended' } ); return if( ! $generate ); } $cache_ref = $self->get_class_cache( $release, $flags ) if( ! defined $cache_ref ); return $cache_ref if( 'HASH' ne ref( $cache_ref ) ); my $ldap = $self->{ 'LDAP' }; my $base = $self->{ 'base' }; my( $entry, $type, $faiclasses ); # Check all FAI OUs for classnames while( ($type, $faiclasses) = each %$cache_ref ) { # Skip, if this entry is not an FAI type next if(! exists $fai_items{ $type }); # Skip, if this type doesn't have additional information my @attrs = @{$fai_items{ $type }}; next if( 1 == scalar @attrs ); my $objclass = $attrs[ 1 ]; # Filter attributes @attrs = splice( @attrs, 2 ); push( @attrs, 'FAIstate' ) if( scalar @attrs ); foreach my $class (keys( %{$faiclasses} )) { my $mesg; my $class_base; # For package lists we have to store the actual data in an extra object if( 'debconf' eq $type ) { $class_base = "cn=${class},ou=packages,@{$rdns}[0],${base}"; $mesg = $ldap->search( base => $class_base, filter => "(objectClass=$objclass)", scope => 'one' ); } elsif( 'packages' eq $type ) { $class_base = "cn=${class},ou=${type},@{$rdns}[0],${base}"; $mesg = $ldap->search( base => $class_base, filter => "(objectClass=$objclass)", scope => 'base' ); if ($mesg->code != 0) { return( sprintf( "LDAP search error at line %i when searching for %s of type %s: %s (%i)\n", __LINE__, $class_base, $type, $mesg->error, $mesg->code ) ) } # Store entries $cache_ref->{ ${type} }->{ ${class} } = ($mesg->entries())[0]; next; } elsif( 'disk' eq $type ) { # print( "Disk config lookup for '${class}'...\n" ); my $setup_storage = 0; $class_base = "cn=${class},ou=${type},@{$rdns}[0],${base}"; $mesg = $ldap->search( base => ${class_base}, filter => '(|(objectClass=FAIpartitionDisk)(objectClass=FAIpartitionEntry)(objectClass=FAIpartitionTable))', scope => 'sub' ); if ($mesg->code != 0) { return( sprintf( "LDAP search error at line %i when searching for %s of type %s: %s (%i)\n", __LINE__, $class_base, $type, $mesg->error, $mesg->code ) ) } # Decode disks and partition tables my @entries = $mesg->entries(); my %disk_configs; my $checked_entries = scalar @entries; while( scalar @entries ) { $entry = shift( @entries ); my @objclasses = $entry->get_value( 'objectClass' ); my $valid_object = 0; foreach my $obj (@objclasses) { my $dn_tail; my @rdns; # Check partition if( $obj =~ /^FAIpartitionTable$/i ) { if (defined $entry->get_value( 'FAIpartitionMethod' )){ $setup_storage = $entry->get_value( 'FAIpartitionMethod' ) eq 'setup-storage'; } $entry = undef; last; } # Check disk if( $obj =~ /^FAIpartitionDisk$/i ) { @rdns = argonaut_ldap_split_dn( $entry->dn() ); shift( @rdns ); $dn_tail = join( ',', @rdns ); my $cn = $entry->get_value( 'cn' ); last if( $dn_tail !~ /^${class_base}$/ || (exists $disk_configs{${cn}}) ); if( ! is_removed( $entry ) ) { my %partitions = (); $disk_configs{${cn}} = \%partitions; $disk_configs{${cn}}->{'disk'} = $entry; $disk_configs{${cn}}->{'setup-storage'} = $setup_storage; # print( " + disk '${cn}'\n" ); } else { $disk_configs{${cn}} = undef; } $entry = undef; $valid_object = 1; last; } # Check partition if( $obj =~ /^FAIpartitionEntry$/i ) { my @rdns = argonaut_ldap_split_dn( $entry->dn() ); shift @rdns; my $disk = shift @rdns; $dn_tail = join( ',', @rdns ); ($disk) = $disk =~ /^[^=]+=(.*)/; last if( $dn_tail !~ /^${class_base}$/ ); # Since the LDAP result is unordered, there might be a # valid disk later - mark partition as valid $valid_object = 1; last if( ! defined $disk_configs{${disk}} ); $disk_configs{${disk}}-> { $entry->get_value( 'FAIpartitionNr' ) } = $entry; # print( " + partition '" . $entry->get_value( 'FAIpartitionNr' ) # . "' to disk '${disk}'\n" ); $entry = undef; last; } } $checked_entries--; if( defined $entry ) { # If we didn't store the entry yet, check if it's valid if( $checked_entries < 0 ) { print( "Unable to find disk for partition '" . $entry->get_value( 'cn' ) . "' - skipped\n" ); next; } if( ! $valid_object ) { print( "Invalid disk config entry '" . $entry->dn() . "' - skipped\n" ); next; } push( @entries, $entry ) if( defined $entry ); } } # Store disk config $cache_ref->{ ${type} }->{ ${class} } = \%disk_configs; next; } else { $class_base = "cn=${class},ou=${type},@{$rdns}[0],${base}"; my %search = ( base => $class_base, filter => "(objectClass=$objclass)", scope => 'one' ); $search{ 'attrs' } = \@attrs if( scalar @attrs ); $mesg = $ldap->search( %search ); } if ($mesg->code != 0) { return( sprintf( "LDAP search error at line %i when searching for %s of type %s: %s (%i)\n", __LINE__, $class_base, $type, $mesg->error, $mesg->code ) ) } # Store entries if( 0 != $mesg->count ) { my %values; foreach my $entry ($mesg->entries()) { my $key; if( 'debconf' eq ${type} ) { $key = $entry->get_value( 'FAIvariable' ); } elsif( 'templates' eq ${type} ) { $key = $entry->get_value( 'FAItemplatePath' ); } else { $key = $entry->get_value( 'cn' ); } if( exists $values{ $key } ) { warn( "Duplicated key '$key' in '$class' for type '$type'" ); } else { $values{ $key } = $entry; } } $cache_ref->{ ${type} }->{ ${class} } = \%values; } else { delete( $cache_ref->{ ${type} }->{ ${class} } ); } } } $cache_ref->{ 'extended' } = 1; $self->{ 'FAI_TREES' }{ $release } = $cache_ref; return $self->{ 'FAI_TREES' }{ $release }; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Verify, if the FAI object has the 'removed' state # # $entry = Net::LDAP::Entry # # Returns true, if the 'FAIstate' contains a removed # sub is_removed { my $entry = shift; my $state = $entry->get_value( 'FAIstate' ); if (defined $state) { my %states = map { $_ => 1 } split( "\\|", $state ); return 1 if( exists $states{ 'removed' } ); } return 0; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Common init code for all dump_ functions # # $self = Argonaut::FAI handle # $release = Release to dump # $classref = # $flags = # $type = # $nomerge = # # $classref = Arrayref to the requested FAIclass list (already expanded) # $dumpdir = # $cow_merge = # sub init_dump_function { my( $self, $release, $classref, $flags, $type ) = @_; my $dumpdir = $self->{ 'dumpdir' }; my $typeref = $self->extend_class_cache( $release, $flags )->{ $type }; # Fill $classref with all classes, if not supplied if( ! defined $classref ) { my %seen; foreach my $item (keys %$typeref) { $seen{ $item } = 1; } my @classlist = keys( %seen ); $classref = \@classlist; } # Merge release hashes into COW hash my %cow_merge; foreach my $class (@$classref) { next if( ! exists $typeref->{ $class } ); if( ref( $typeref->{ $class } ) eq 'HASH' ) { while( my($key, $value) = each %{$typeref->{ $class }} ) { $cow_merge{ $class }{ $key } = $value; } } else { $cow_merge{ $class } = $typeref->{ $class }; } } return( $classref, $dumpdir, \%cow_merge ); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Dumps variables from class cache # # $self = Argonaut::FAI handle # $release = Release string # $classref = Arrayref to the requested FAIclass list (already expanded) # $flags = @see get_class_cache, but defaults to 0; # # Returns undef, if no error occured, otherwise the error message # sub dump_variables { my( $self, $release, $classref, $flags ) = @_; my( $dumpdir, $cow_cacheref, $faivar ); ( $classref, $dumpdir, $cow_cacheref ) = $self->init_dump_function( $release, $classref, $flags, 'variables' ); return $classref if( ! defined $dumpdir ); foreach my $class (@$classref) { next if( ! exists $cow_cacheref->{ $class } ); my %vars = (); foreach my $entry (values %{$cow_cacheref->{ $class }}) { next if( is_removed( $entry ) ); my $cn = $entry->get_value( 'cn' ); $vars{ $cn } = $entry->get_value( 'FAIvariableContent' ); } next if( 0 == scalar keys( %vars ) ); if( $self->{ 'flags' } & FAI_FLAG_VERBOSE ) { print( "Generate variable file for class '${class}'.\n" ); print( " Vars: " . join( ", ", keys %vars ) . "\n" ); } next if( $self->{ 'flags' } & FAI_FLAG_DRY_RUN ); if( ! -d "$dumpdir/class" ) { eval { mkpath( "$dumpdir/class" ); }; return( "Can't create dir '$dumpdir/class': $!\n" ) if( $@ ); } open ($faivar,q{>},"$dumpdir/class/${class}.var") || return( "Can't create '$dumpdir/class/${class}.var': $!\n" ); while( my( $key, $value ) = each( %vars ) ) { print( $faivar "${key}='${value}'\n" ); } close ($faivar); } return; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Dumps package lists from class cache # # $self = Argonaut::FAI handle # $release = Release string # $classref = Arrayref to the requested FAIclass list (already expanded) # $flags = @see get_class_cache, but defaults to 0; # # Returns undef, if no error occured, otherwise the error message # sub dump_package_list { my( $self, $release, $classref, $flags ) = @_; my( $dumpdir, $cow_cacheref, $faipackages ); ( $classref, $dumpdir, $cow_cacheref ) = $self->init_dump_function( $release, $classref, $flags, 'packages' ); return $classref if( ! defined $dumpdir ); my( $entry, $method ); if( ! -d "$dumpdir/package_config" ) { eval { mkpath( "$dumpdir/package_config" ); }; return( "Can't create dir '$dumpdir/package_config': $!\n" ) if( $@ ); } my %uniq_sections = (); my %uniq_customs = (); foreach my $class (@$classref) { next if( ! exists $cow_cacheref->{ $class } ); $entry = $cow_cacheref->{ $class }; $method = $entry->get_value( 'FAIinstallMethod' ); print( "Generate package list for class '${class}'.\n" ) if( $self->{ 'flags' } & FAI_FLAG_VERBOSE ); foreach my $section ( $entry->get_value( 'FAIdebianSection' ) ) { $uniq_sections{ $section } = undef; } foreach my $custom ( $entry->get_value( 'FAIcustomRelease' ) ) { $uniq_customs{ $custom } = undef; } next if( $self->{ 'flags' } & FAI_FLAG_DRY_RUN ); open( $faipackages, q{>}, "$dumpdir/package_config/$class" ) || do_exit( 4, "Can't create $dumpdir/package_config/$class. $!\n" ); print $faipackages "PACKAGES $method\n"; print $faipackages join( "\n", $entry->get_value('FAIpackage') ); print $faipackages "\n"; close( $faipackages ); } my @sections = keys( %uniq_sections ); my @customs = keys( %uniq_customs ); return( undef, \@sections, \@customs ); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Dumps debconf information from class cache # # $self = Argonaut::FAI handle # $release = Release string # $classref = Arrayref to the requested FAIclass list (already expanded) # $flags = @see get_class_cache, but defaults to 0; # # Returns undef, if no error occured, otherwise the error message # sub dump_debconf_info { my( $self, $release, $classref, $flags ) = @_; my( $dumpdir, $cow_cacheref, $debconf ); ( $classref, $dumpdir, $cow_cacheref ) = $self->init_dump_function( $release, $classref, $flags, 'debconf' ); return $classref if( ! defined $dumpdir ); if( ! -d "$dumpdir/debconf" ) { eval { mkpath( "$dumpdir/debconf" ); }; return( "Can't create dir '$dumpdir/debconf': $!\n" ) if( $@ ); } foreach my $class (@$classref) { next if( ! exists $cow_cacheref->{ $class } ); my @lines = (); foreach my $entry (values %{$cow_cacheref->{ $class }}) { next if( is_removed( $entry ) ); push( @lines, sprintf( "%s %s %s %s", $entry->get_value('FAIpackage'), $entry->get_value('FAIvariable'), $entry->get_value('FAIvariableType'), $entry->get_value('FAIvariableContent') ) ); } next if( 0 == scalar @lines ); open( $debconf, q{>}, "$dumpdir/debconf/$class" ) || return( "Can't create $dumpdir/debconf/$class. $!\n" ); print $debconf join( "\n", sort {$a cmp $b} @lines ); print $debconf "\n"; close( $debconf ); } return; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Dumps disk configurations from class cache # # $self = Argonaut::FAI handle # $release = Release string # $classref = Arrayref to the requested FAIclass list (already expanded) # $flags = @see get_class_cache, but defaults to 0; # # Returns undef, if no error occured, otherwise the error message # sub dump_disk_config { my( $self, $release, $classref, $flags ) = @_; my( $cow_cacheref, $dumpdir, $faidiskconfig, $faivar ); ( $classref, $dumpdir, $cow_cacheref ) = $self->init_dump_function( $release, $classref, $flags, 'disk' ); return $classref if( ! defined $dumpdir ); if( ! -d "$dumpdir/disk_config" ) { eval { mkpath( "$dumpdir/disk_config" ); }; return( "Can't create dir '$dumpdir/disk_config': $!\n" ) if( $@ ); } my $first_lvm_disk= 1; my $disk_index= 0; my $setup_storage= 0; foreach my $class (@$classref) { next if( ! exists $cow_cacheref->{ $class } ); my $disk_config = $cow_cacheref->{ $class }; my( %all_disks ); foreach my $type ("disk", "raid", "lvm") { foreach my $disk (keys %{$disk_config}) { next if( ! defined $disk_config->{ $disk } ); # Extract setup storage mode my $dc = $disk_config->{ $disk }->{ 'disk' }; $setup_storage= $disk_config->{ $disk }->{ 'setup-storage' }; # Extract disk information my $disk_type = "disk"; my $disk_options = ""; my $lvm_name= ""; if (defined $dc->get_value('FAIdiskOption')) { foreach ($dc->get_value('FAIdiskOption')) { $disk_options= $disk_options . " " . $_; } } if (defined $dc->get_value('FAIdiskType')) { $disk_type = $dc->get_value('FAIdiskType'); } # Skip workaround to manage order of disk types next if( $disk_type ne $type); # Update index my $disk_label= $disk_index."-".$disk; $all_disks{ $disk_label } = {}; # In case of LVM, we need a special handling, because the volumes # get handled as disks internally if ($disk_type eq "lvm") { $lvm_name = $dc->get_value('cn'); my $size = ""; foreach ($dc->get_value('FAIlvmDevice')) { $size= $size . "," . $_; } $size=~ s/^.//; if ($first_lvm_disk) { $first_lvm_disk= 0; $all_disks{ $disk_label }{ 0 } = "disk_config lvm$disk_options\nvg $lvm_name $size\n"; } else { $all_disks{ $disk_label }{ 0 } = "vg $lvm_name $size\n"; } } else { $all_disks{ $disk_label }{ 0 } = "disk_config $disk$disk_options\n"; } # Remove disk information from hash delete $disk_config->{ $disk }->{ 'disk' }; delete $disk_config->{ $disk }->{ 'setup-storage' }; my $logic_count = 4; my $primary_count = 0; foreach my $partition_nr (sort {$a <=> $b} (keys %{$disk_config->{ $disk }}) ) { my $line; my $dl = $disk_config->{ $disk }->{ $partition_nr }; if ($dl->get_value('FAIpartitionType') eq 'primary'){ $primary_count++; } else { $logic_count++; } my $part_flags = $dl->get_value('FAIpartitionFlags'); my $mount_opts = $dl->get_value('FAImountOptions'); $mount_opts = 'rw' if( ! defined $mount_opts || ($mount_opts eq '') ); my $combined_opts= ""; my $c_opts = $dl->get_value('FAIfsCreateOptions'); my $t_opts = $dl->get_value('FAIfsTuneOptions'); if (defined $c_opts) { $combined_opts= "createopts=\"$c_opts\" "; } if (defined $t_opts) { $combined_opts.= "tuneopts=\"$t_opts\""; } if ($setup_storage) { if ($disk_type eq 'lvm') { $line= sprintf( "%-20s %-18s %-12s %-10s %s %s\n", $lvm_name."-".$dl->get_value('cn'), $dl->get_value('FAImountPoint'), $dl->get_value('FAIpartitionSize'), $dl->get_value('FAIfsType'), $mount_opts, $combined_opts); } else { $line= sprintf( "%-20s %-18s %-12s %-10s %s %s\n", $dl->get_value('FAIpartitionType'), $dl->get_value('FAImountPoint'), $dl->get_value('FAIpartitionSize'), $dl->get_value('FAIfsType'), $mount_opts, $combined_opts); } } else { if (defined $part_flags && ($part_flags eq 'preserve') ){ my $part_type; if ($dl->get_value('FAIpartitionType') eq 'primary'){ $part_type = 'preserve' . $primary_count; } else { $part_type = 'preserve' . $logic_count; } $line = sprintf( "%-7s %-12s %-12s %-10s ; %s mounttype=uuid\n", $dl->get_value('FAIpartitionType'), $dl->get_value('FAImountPoint'), $part_type, $mount_opts, $dl->get_value('FAIfsOptions') ); } elsif ($dl->get_value('FAIfsType') eq 'swap') { # Labels are limited to 15 chars my $swaplabel = 'swap-' . argonaut_gen_random_str( 10 ); $line = sprintf( "%-7s %-12s %-12s %-10s ; mounttype=label label='%s'\n", $dl->get_value('FAIpartitionType'), $dl->get_value('FAImountPoint'), $dl->get_value('FAIpartitionSize'), $mount_opts, $swaplabel ); } else { $line= sprintf( "%-7s %-12s %-12s %-10s ; %s %s mounttype=uuid\n", $dl->get_value('FAIpartitionType'), $dl->get_value('FAImountPoint'), $dl->get_value('FAIpartitionSize'), $mount_opts, $dl->get_value('FAIfsOptions'), $dl->get_value('FAIfsType') ); } } $all_disks{ $disk_label }{ $partition_nr } = $line; } $disk_config->{ $disk }->{ 'disk' }= $dc; $disk_config->{ $disk }->{ 'setup-storage' }= $setup_storage; } $disk_index++; } my @disk_config_lines; if( %all_disks ) { foreach my $disk (sort {$a cmp $b} keys %all_disks) { foreach my $part (sort {$a <=> $b} keys %{$all_disks{ $disk }} ) { push( @disk_config_lines, $all_disks{ $disk }{ $part } ); } } } open( $faidiskconfig, q{>}, "$dumpdir/disk_config/${class}" ) || return( "Can't create $dumpdir/disk_config/$class. $!\n" ); print $faidiskconfig join( '', @disk_config_lines ); close( $faidiskconfig ); # Enable setup storage if needed if ($setup_storage && ! ($self->{ 'flags' } & FAI_FLAG_DRY_RUN)) { if( ! -d "$dumpdir/class" ) { eval { mkpath( "$dumpdir/class" ); }; return( "Can't create dir '$dumpdir/class': $!\n" ) if( $@ ); } open ($faivar,q{>>},"$dumpdir/class/${class}.var") || return( "Can't create/append '$dumpdir/class/${class}.var': $!\n" ); print( $faivar "USE_SETUP_STORAGE=1\n" ); close ($faivar); } } return; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Writes the file in 'FAI mode' and adds mode info # # $filename = full path to real file # $data = file data # $mode = file mode # $owner = file owner # $class = FAIclass, the file belongs to # # Returns nothing # sub write_fai_file { my ( $filename, $data, $mode, $owner, $class ) = @_; my $fclass = ''; my ( $faifile, $faifilemodes ) ; return if( scalar @_ < 2 ); # Append class to filename $fclass = '/' . $class if( defined $class ); open( $faifile,q{>},"${filename}${fclass}" ) || return( "Can't create file '${filename}${fclass}': $!\n" ); print( $faifile $data ) if( defined $data ); close( $faifile ); if( defined $class && ('' ne $class) ) { # ($owner,$group,$mode,$class) = split my (@modelines) = (); if( -f "${filename}/file-modes" ) { open( $faifilemodes, q{<}, "${filename}/file-modes" ) || return( "Couldn't open modefile '${filename}/file-modes': $!\n" ); (@modelines) = <$faifilemodes>; close( $faifilemodes ); } open( $faifilemodes, q{>}, "${filename}/file-modes" ) || return( "Couldn't open modefile '${filename}/file-modes': $!\n" ); # Remove old mode entry from file-modes foreach my $line ( @modelines ) { chomp( $line ); print( $faifilemodes "$line\n" ) if( ! ($line =~ /${class}$/) ); } # Fix empty mode $mode = '0640' if( ! defined $mode || ($mode !~ /^0*[0-7]{1,4}$/) ); # Fix empty owners if( defined $owner && ('' ne $owner) ) { $owner =~ tr/\.:/ /; } else { $owner = 'root root'; } print( $faifilemodes "$owner $mode $class\n" ); close( $faifilemodes ); } else { chmod( oct($mode), ${filename} ) if( defined $mode && ($mode =~ /^0*[0-7]{1,4}$/) ); argonaut_file_chown( ${filename}, $owner ) if( defined $owner && ($owner ne '') ); } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Dumps scripts from class cache # # $self = Argonaut::FAI handle # $release = Release string # $classref = Arrayref to the requested FAIclass list (already expanded) # $flags = @see get_class_cache, but defaults to 0; # # Returns undef, if no error occured, otherwise the error message # sub dump_scripts { my( $self, $release, $classref, $flags ) = @_; my( $cow_cacheref, $dumpdir ); ( $classref, $dumpdir, $cow_cacheref ) = $self->init_dump_function( $release, $classref, $flags, 'scripts' ); return $classref if( ! defined $dumpdir ); foreach my $class (@$classref) { next if( ! exists $cow_cacheref->{ $class } ); if( ! -d "$dumpdir/scripts/${class}" ) { eval { mkpath( "$dumpdir/scripts/${class}" ); }; return( "Can't create dir '$dumpdir/scripts/${class}': $!\n" ) if( $@ ); } my @lines = (); foreach my $entry (values %{$cow_cacheref->{ $class }}) { my $name = $entry->get_value( 'cn' ); my $prio = $entry->get_value( 'FAIpriority' ); my $script_name = sprintf( '%02d-%s', $prio, $name ); my $script_path = "${dumpdir}/scripts/${class}/${script_name}"; if( is_removed( $entry ) ) { unlink( "${script_path}" ) if( -f "${script_path}" ); next; } print( "Generate script '${script_name}' for class '${class}'.\n" ) if( $self->{ 'flags' } & FAI_FLAG_VERBOSE ); write_fai_file( "${script_path}", $entry->get_value( 'FAIscript' ), '0700' ) if( ! ($self->{ 'flags' } & FAI_FLAG_DRY_RUN) ); } } return; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Dumps templates from LDAP # # $self = Argonaut::FAI handle # $release = Release string # $classref = Arrayref to the requested FAIclass list (already expanded) # $flags = @see get_class_cache, but defaults to 0; # # Returns undef, if no error occured, otherwise the error message # sub dump_templates { my( $self, $release, $classref, $flags ) = @_; my( $cow_cacheref, $dumpdir ); ( $classref, $dumpdir, $cow_cacheref ) = $self->init_dump_function( $release, $classref, $flags, 'templates' ); return $classref if( ! defined $dumpdir ); my( $release_base ) = $self->release_check( $release ); my $ldap = $self->{ 'LDAP' }; my $base = $self->{ 'base' }; if( ! -d "$dumpdir/files" ) { eval { mkpath( "$dumpdir/files" ); }; return( "Can't create dir '$dumpdir/files': $!\n" ) if( $@ ); } foreach my $class (@$classref) { next if( ! exists $cow_cacheref->{ $class } ); foreach my $entry (values %{$cow_cacheref->{ $class }}) { my $template_path = $entry->get_value( 'FAItemplatePath' ); chomp( $template_path ); my $target_path = "${dumpdir}/files/${template_path}/${class}"; # Remove removed files ;-) if( is_removed( $entry ) ) { unlink( "${target_path}" ) if( -f "${target_path}" ); next; } if( ! -d "$dumpdir/files/$template_path" ) { eval { mkpath( "$dumpdir/files/$template_path" ); }; return( "Can't create dir '$dumpdir/files/$template_path': $!\n" ) if( $@ ); } print( "Generate template '${template_path}' for class '${class}'.\n" ) if( $self->{ 'flags' } & FAI_FLAG_VERBOSE ); write_fai_file( "${dumpdir}/files/${template_path}", $entry->get_value('FAItemplateFile'), $entry->get_value('FAImode'), $entry->get_value('FAIowner'), $class ) if( ! ($self->{ 'flags' } & FAI_FLAG_DRY_RUN) ); } } return; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $release = Release version # $classref = Arrayref of classes to dump (will be expanded) # # Returns undef, if no error occured, otherwise the error message # sub dump_hooks { my( $self, $release, $classref, $flags ) = @_; my( $dumpdir, $cow_cacheref ); ( $classref, $dumpdir, $cow_cacheref ) = $self->init_dump_function( $release, $classref, $flags, 'hooks' ); return $classref if( ! defined $dumpdir ); foreach my $class (@$classref) { next if( ! exists $cow_cacheref->{ $class } ); if( ! -d "$dumpdir/hooks" ) { eval { mkpath( "$dumpdir/hooks" ); }; return( "Can't create dir '$dumpdir/hooks': $!\n" ) if( $@ ); } my @lines = (); foreach my $entry (values %{$cow_cacheref->{ $class }}) { my $task = $entry->get_value( 'FAItask' ); my $hook_path = "${dumpdir}/hooks/${task}.${class}"; my $cn = $entry->get_value( 'cn' ); if( is_removed( $entry ) ) { unlink( "${hook_path}" ) if( -f "${hook_path}" ); next; } print( "Generate hook '$cn' ($task) for class '${class}'.\n" ) if( $self->{ 'flags' } & FAI_FLAG_VERBOSE ); write_fai_file( ${hook_path}, $entry->get_value( 'FAIscript' ), '0700' ) if( ! ($self->{ 'flags' } & FAI_FLAG_DRY_RUN) ); } } return; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $release = Release version # $classref = Arrayref of classes to dump (will be expanded) # $hostname = Host to use in classlist expansion # # Returns a hashref including the classes for the FAI types # $result->{ ''profile', 'hook', ... }->{ 'class' } # In case of profiles it points to a hashref of profile subclasses # Returns error, sections and customs. # error is undef if no error happened. # sub dump_release { my( $self, $release, $classref, $hostname ) = @_; my $cls_release; my $faiclasslist; if( defined $classref ) { ($classref, $cls_release) = $self->resolve_classlist( $classref, $release, $hostname ); return( $classref ) if( 'ARRAY' ne ref( $classref ) ); $release = $cls_release if( ! defined $release ); } return( "No release specified\n" ) if( ! defined $release ); my $cacheref = $self->extend_class_cache( $release ); return( $cacheref ) if( 'HASH' ne ref( $cacheref ) ); return( "No dump directory specified" ) if( ! defined $self->{ 'dumpdir' } ); my $dumpdir = $self->{ 'dumpdir' }; $dumpdir .= '/class' if( defined $hostname ); # Create dump directory and hosts classfile if( ! -d "${dumpdir}" ) { eval { mkpath( "${dumpdir}" ); }; return( "Can't create dir '${dumpdir}': $!\n" ) if( $@ ); } if( defined ${hostname} ) { open( $faiclasslist,q{<},"${dumpdir}/${hostname}" ) || return( "Can't create ${dumpdir}/${hostname}. $!\n" ); print( $faiclasslist join( ' ', @${classref} ) ); close( $faiclasslist ); } # Add FAI standard classes for dump $classref = $self->expand_fai_classlist( $classref, $hostname ) if( defined $classref ); # Dump variables, packages, debconf, scripts, templates and disk_config my $dump_result = $self->dump_variables( $release, $classref ); return( $dump_result ) if( defined $dump_result ); my ($sections, $customs); ($dump_result, $sections, $customs) = $self->dump_package_list( $release, $classref ); return( $dump_result ) if( defined $dump_result ); $dump_result = $self->dump_debconf_info( $release, $classref ); return( $dump_result ) if( defined $dump_result ); $dump_result = $self->dump_scripts( $release, $classref ); return( $dump_result ) if( defined $dump_result ); $dump_result = $self->dump_templates( $release, $classref ); return( $dump_result ) if( defined $dump_result ); $dump_result = $self->dump_disk_config( $release, $classref ); return( $dump_result ) if( defined $dump_result ); $dump_result = $self->dump_hooks( $release, $classref ); return( $dump_result ) if( defined $dump_result ); return( undef, $sections, $customs ); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $class_str = A space seperated string of classes or arrayref # $release = Overwrite or set release provided by $class_str # $hostname = Host to use in classlist expansion # $force = Ignore cached values and rebuild # sub resolve_classlist { my( $self, $class_str, $release, $hostname, $force ) = @_; my( @classes, @newclasses, $cls_release, $class ); # Set @classes depending on parameter type if( 'ARRAY' eq ref( $class_str ) ) { @classes = @{$class_str}; } else { @classes = split( ' ', $class_str ); } # Check for release in classlist foreach my $class (@classes) { if( ":" eq substr( $class, 0, 1 ) ) { return ("Duplicated release in classlist\n") if( defined $cls_release ); if (length(${class}) > 1) { $cls_release = substr( $class, 1 ); } else { return( "Invalid release ':' in classlist\n" ); } } else { push @newclasses, $class; } } # Overwrite release if supplied $cls_release = $release if( defined $release ); return( "No release for lookup defined\n" ) if( ! defined $cls_release ); # Always prepend release @classes = @newclasses; $class_str = ':' . $cls_release . join( ' ', @classes ); # Return cached values if not enforced if (!(defined $force && $force)) { return $self->{ 'RESOLVED' }{ $class_str } if( exists $self->{ 'RESOLVED' }{ $class_str } ); } my $ldap = $self->{ 'LDAP' }; my $base = $self->{ 'base' }; @newclasses = (); my %seen = ( 'LAST' => 1, 'DEFAULT' => 1 ); $seen{ $hostname } = 1 if( defined $hostname ); my @faiprofiles = (); my( $entry, $mesg ); # We need to walk through the list of classes and watch out for # a profile, which is named like the class. Replace the profile # name by the names of the included classes. while( 0 != scalar @classes ) { $class = shift( @classes ); # Skip duplicated profiles and classes next if( exists $seen{ $class } ); my $cache = $self->get_class_cache( $cls_release ); return $cache if( 'HASH' ne ref( $cache ) ); if( exists $cache->{ 'profiles' } ) { if( exists $cache->{ 'profiles' }->{ $class } ) { my @profile_classes = @{$cache->{ 'profiles' }->{ $class }{ '_classes' }}; foreach my $profile_class (reverse @profile_classes) { # Check if the class is already in the list? next if( exists $seen{ $profile_class } ); # Prepend class - it may also be a profile unshift( @classes, $profile_class ) if( ! exists $seen{ $profile_class } ); } $seen{ $class } = 1; } } # Just push non-profile classes if( ! exists $seen{ $class } ) { push( @newclasses, $class ); $seen{ $class } = 1; } } return( \@newclasses, $cls_release ); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # $self = Argonaut::FAI handle # $class_str = A space seperated string of classes or arrayref # $hostname = The non-FQDN hostname # # Little convenience function to add standard FAI classes, which are added # automatically by FAI. These are needed for a correct dump. # sub expand_fai_classlist { my( $self, $classref, $hostname ) = @_; my( @newclasses ); return if( ! defined $classref ); if( 'ARRAY' eq ref( $classref ) ) { @newclasses = @$classref; } else { @newclasses = split(' ', $classref); } # These classes are added automatically by FAI... unshift( @newclasses, "DEFAULT" ); push( @newclasses, "${hostname}" ) if( defined $hostname ); push( @newclasses, "LAST" ); return \@newclasses if( 'ARRAY' eq ref( $classref ) ); return join(' ', @newclasses); } END {} 1; __END__ # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-common/Argonaut/Libraries/Packages.pm000066400000000000000000000433141304135723100240540ustar00rootroot00000000000000####################################################################### # # Argonaut::Libraries::Packages -- get and parse Debian Packages. # # Copyright (C) 2011-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Libraries::Packages; use strict; use warnings; use 5.008; use MIME::Base64; use Path::Class; use Net::LDAP; use File::Path; use IO::Uncompress::Bunzip2 qw(bunzip2 $Bunzip2Error); use IO::Uncompress::Gunzip qw(gunzip $GunzipError); use LWP::Simple; use Encode qw(encode); use XML::SAX::RPMHandler; use XML::SAX::RPMRepomdHandler; use XML::SAX; use Argonaut::Libraries::Common qw(:ldap :config); BEGIN { use Exporter (); use vars qw(@EXPORT_OK @ISA $VERSION); $VERSION = '2011-04-11'; @ISA = qw(Exporter); @EXPORT_OK = qw(get_repolines get_packages_info store_packages_file cleanup_and_extract); } =pod =item get_repolines Get repolines from ldap =cut sub get_repolines { my ($mac,$cn) = @_; my $config = argonaut_read_config; my ($ldap,$ldap_base) = argonaut_ldap_handle($config); my $mesg; if(defined $mac) { $mesg = $ldap->search( base => $ldap_base, filter => "(&(objectClass=FAIrepositoryServer)(macAddress=$mac))", attrs => [ 'FAIrepository' ] ); } elsif(defined $cn) { $mesg = $ldap->search( base => $ldap_base, filter => "(&(objectClass=FAIrepositoryServer)(cn=$cn))", attrs => [ 'FAIrepository' ] ); } else { $mesg = $ldap->search( base => $ldap_base, filter => "(&(objectClass=FAIrepositoryServer))", attrs => [ 'FAIrepository' ] ); } $mesg->code && die "Error while searching repositories :".$mesg->error; $ldap->unbind(); my @repolines = (); foreach my $entry ($mesg->entries()) { foreach my $repoline ($entry->get_value('FAIrepository')) { my ($uri,$parent,$release,$sections,$install,$local,$archs,$dist,$pathmask) = split('\|',$repoline); my $sections_array = [split(',',$sections)]; if ($install eq 'update') { foreach my $section (@$sections_array) { if ($section !~ m/^updates/) { $section = "updates/$section"; } } } my $repo = { 'line' => $repoline, 'uri' => $uri, 'parent' => $parent, 'release' => $release, 'sections' => $sections_array, 'installrepo' => $install, 'localmirror' => ($local eq "local"), 'archs' => [split(',',$archs)], 'dist' => $dist, 'pathmask' => $pathmask }; push @repolines, $repo; } } return @repolines; } =item get_packages_info Get packages list with all requested attrs. Uses the Packages file from the server for that. If no mac is provided, all servers (for the specified release) in the ldap are checked. =cut sub get_packages_info { my ($packages_folder,$mac,$release,$attrs,$filters,$from,$to) = @_; if((defined $from) && ($from < 0)) { undef $from; } if((defined $to) && ($to < 0)) { undef $to; } my @filters_temp = grep { $_ ne '' } @{$filters}; if(@filters_temp) { $filters = \@filters_temp; } else { undef $filters; } push @{$attrs},'PACKAGE' if (not (grep {uc($_) eq 'PACKAGE'} @{$attrs})); #~ push @{$attrs},'VERSION' if (not (grep {uc($_) eq 'VERSION'} @{$attrs})); my @repolines = get_repolines($mac); my $package_indice = 0; my $distributions = {}; mkpath($packages_folder); foreach my $repo (@repolines) { if(defined($release) && ($repo->{'release'} ne $release)) { next; } if ($repo->{'dist'} eq 'debian') { parse_package_list_debian($packages_folder,\$package_indice,$distributions,$repo,$attrs,$filters,$from,$to); } elsif ($repo->{'dist'} eq 'centos') { parse_package_list_centos($packages_folder,\$package_indice,$distributions,$repo,$attrs,$filters,$from,$to); } } foreach my $key (keys(%{$distributions})) { my @tmp = values(%{$distributions->{$key}}); $distributions->{$key} = \@tmp; } return $distributions; } sub parse_package_list_centos { my ($packages_folder,$package_indice,$distributions,$repo,$attrs,$filters,$from,$to) = @_; my $uri = $repo->{'uri'}; my $localuri = $uri; $localuri =~ s/^http:\/\///; my $handler = XML::SAX::RPMHandler->new( undef, { 'name' => 'PACKAGE', 'description' => sub { my ($package, undef, $data, $attrs) = @_; $package->{'DESCRIPTION'} = encode_base64(encode('utf8',$data)); }, 'version' => sub { my ($package, undef, $data, $attrs) = @_; $package->{'VERSION'} = $attrs->{'{}ver'}->{'Value'}.'-'.$attrs->{'{}rel'}->{'Value'}; } }, $filters, $from, $to, $$package_indice ); my $parser = XML::SAX::ParserFactory->parser( Handler => $handler ); foreach my $section (@{$repo->{'sections'}}) { if(!defined $distributions->{$repo->{'release'}."/$section"}) { $distributions->{$repo->{'release'}."/$section"} = {}; } $handler->{packages} = $distributions->{$repo->{'release'}."/$section"}; foreach my $arch (@{$repo->{'archs'}}) { my $relpath = $repo->{'pathmask'}; $relpath =~ s/%RELEASE%/$repo->{'release'}/i; $relpath =~ s/%SECTION%/$section/i; $relpath =~ s/%ARCH%/$arch/i; my $primary_file = "$packages_folder/$localuri/".$relpath."/primary.xml"; eval { $parser->parse_uri($primary_file); }; if ($@ && ($@ !~ m/^LIMIT_REACHED/)) { die $@; } } } $$package_indice = $handler->{indice}; } sub parse_package_list_debian { my ($packages_folder,$package_indice,$distributions,$repo,$attrs,$filters,$from,$to) = @_; my $uri = $repo->{'uri'}; my $localuri = $uri; $localuri =~ s/^http:\/\///; my $localmirror = $repo->{'localmirror'}; if(!$localmirror && ((grep {uc($_) eq 'TEMPLATE'} @{$attrs}) || (grep {uc($_) eq 'HASTEMPLATE'} @{$attrs}))) { push @{$attrs},'FILENAME' if (not (grep {uc($_) eq 'FILENAME'} @{$attrs})); } foreach my $section (@{$repo->{'sections'}}) { if(!defined $distributions->{$repo->{'release'}."/$section"}) { $distributions->{$repo->{'release'}."/$section"} = {}; } my $packages = $distributions->{$repo->{'release'}."/$section"}; foreach my $arch (@{$repo->{'archs'}}) { my $packages_filepath = "$packages_folder/$localuri/dists/".$repo->{'release'}."/$section/binary-$arch/Packages"; my $packages_file; open ($packages_file, q{<}, $packages_filepath) or next; my $parsed = {}; while (<$packages_file>) { if (/^$/) { # Empty line means this package info lines are over $$package_indice++; if((! defined $from) || ($$package_indice>$from)) { if($localmirror) { # If it's a local mirror, it's supposed to run the debconf crawler and have the template extracted # So we just download it (if it's not there, we assume there is no template for this package) if (grep {uc($_) eq 'TEMPLATE'} @{$attrs}) { my $template = get("$uri/debconf.d/".$repo->{'release'}."/$section/".$parsed->{'PACKAGE'}); if(defined $template) { $parsed->{'HASTEMPLATE'} = 1; $parsed->{'TEMPLATE'} = $template; } } elsif (grep {uc($_) eq 'HASTEMPLATE'} @{$attrs}) { if(head("$uri/debconf.d/".$repo->{'release'}."/$section/".$parsed->{'PACKAGE'})) { $parsed->{'HASTEMPLATE'} = 1; } } } else { # If it's not a local mirror, we just download the package, we'll extract the template later if ((grep {uc($_) eq 'TEMPLATE'} @{$attrs}) || (grep {uc($_) eq 'HASTEMPLATE'} @{$attrs})) { my $filedir = $parsed->{'FILENAME'}; $filedir =~ s/[^\/]+$//; mkpath($packages_folder."/".$filedir); mirror("$uri/".$parsed->{'FILENAME'},$packages_folder."/".$parsed->{'FILENAME'}); } } $packages->{$parsed->{'PACKAGE'}} = $parsed; } $parsed = {}; if((! defined $to) || ($$package_indice<$to)) { next; } else { last; } } if (my ($key, $value) = m/^(.*): (.*)/) { if((defined $filters) && (uc($key) eq "PACKAGE")) { my $match = 0; foreach my $filter (@{$filters}) { if($value =~ /$filter/) { $match = 1; last; } } if($match == 0) { while(<$packages_file>) { if (/^$/) { last; } } $parsed = {}; next; } } if (grep {uc($_) eq uc($key)} @{$attrs}) { if (uc($key) eq 'DESCRIPTION') { $parsed->{'DESCRIPTION'} = encode_base64($value); } elsif ((uc($key) eq 'PACKAGE') && (defined $packages->{$value}) && !(grep {uc($_) eq 'VERSION'} @{$attrs})) { # We already have the info on this package and version was not asked, skip to next one. while(<$packages_file>) { if (/^$/) { last; } } $parsed = {}; next; } elsif ((uc($key) eq 'VERSION') && (defined $packages->{$parsed->{'PACKAGE'}}->{'VERSION'})) { # We already have the info on this package and this is the version, add it to the list and then skip to next one my @versions = split(',',$packages->{$parsed->{'PACKAGE'}}->{'VERSION'}); if (!(grep {uc($_) eq uc($value)} @versions)) { push @versions, $value; } $packages->{$parsed->{'PACKAGE'}}->{'VERSION'} = join(',',@versions); while(<$packages_file>) { if (/^$/) { last; } } $parsed = {}; next; } else { $parsed->{uc($key)} = $value; } } } else { s/ //; s/^\.$//; my $body = $_; if(grep {uc($_) eq uc('BODY')} @{$attrs}) { $parsed->{'BODY'} .= $body; } } } close($packages_file); } if((defined $to) && ($$package_indice>$to)) { last; } if(!$localmirror && ((grep {uc($_) eq 'TEMPLATE'} @{$attrs}) || (grep {uc($_) eq 'HASTEMPLATE'} @{$attrs}))) { # If it's not a local mirror and templates where asked, we still need to extract and store them my $distribs = {}; my @tmp = values(%{$packages}); $distribs->{$repo->{'release'}."/$section"} = \@tmp; cleanup_and_extract($packages_folder,$distribs); foreach my $key (keys(%{$packages})) { if(defined $packages->{$key}->{'TEMPLATE'}) { next; } my $filename = $packages_folder."/debconf.d/".$repo->{'release'}."/$section/".$packages->{$key}->{'PACKAGE'}; if(-f $filename) { $packages->{$key}->{'HASTEMPLATE'} = 1; if(grep {uc($_) eq 'TEMPLATE'} @{$attrs}) { $packages->{$key}->{'TEMPLATE'} = file($filename)->slurp(); } } } } } } =item store_packages_file Store and extract the Packages file from the repositories. =cut sub store_packages_file { my ($packages_folder,$mac,$release) = @_; my @repolines = get_repolines($mac); my @errors; foreach my $repo (@repolines) { if(defined($release) && ($repo->{'release'} ne $release)) { next; } my $repo_errors; if ($repo->{'dist'} eq 'debian') { $repo_errors = store_package_list_debian($packages_folder,$repo); } elsif ($repo->{'dist'} eq 'centos') { $repo_errors = store_package_list_centos($packages_folder,$repo); } @errors = (@errors, @$repo_errors); } return \@errors; } sub store_package_list_centos { my ($packages_folder,$repo) = @_; my $uri = $repo->{'uri'}; my $dir = $uri; my @errors; $dir =~ s/^http:\/\///; $dir = "$packages_folder/$dir"; my $parser = XML::SAX::ParserFactory->parser( Handler => XML::SAX::RPMRepomdHandler->new() ); foreach my $section (@{$repo->{'sections'}}) { foreach my $arch (@{$repo->{'archs'}}) { my $relpath = $repo->{'pathmask'}; $relpath =~ s/%RELEASE%/$repo->{'release'}/i; $relpath =~ s/%ARCH%/$arch/i; $relpath =~ s/%SECTION%/$section/i; mkpath($dir.$relpath."/repodata"); my $res = mirror($uri.$relpath."repodata/repomd.xml" => $dir.$relpath."repodata/repomd.xml"); if(is_error($res)) { push @errors,"Could not download $uri".$relpath."repodata/repomd.xml: $res"; next; } $parser->parse_uri($dir.$relpath."repodata/repomd.xml"); my $primary = $parser->{Handler}->{result}; $res = mirror($uri."/$relpath/".$primary => $dir."/$relpath/".$primary); if(is_error($res)) { push @errors,"Could not download $uri"."/$relpath/".$primary.": $res"; next; } gunzip ($dir."/$relpath/".$primary => $dir."/$relpath/primary.xml") or push @errors,"could not extract Packages file : $GunzipError"; } } return \@errors; } sub store_package_list_debian { my ($packages_folder,$repo) = @_; my $uri = $repo->{'uri'}; my $dir = $uri; my @errors; $dir =~ s/^http:\/\///; $dir = "$packages_folder/$dir"; foreach my $section (@{$repo->{'sections'}}) { my $relpath = "dists/".$repo->{'release'}."/$section"; foreach my $arch (@{$repo->{'archs'}}) { my $packages_file = "/$relpath/binary-$arch/Packages"; mkpath("$dir/$relpath/binary-$arch/"); my $res = mirror($uri.$packages_file.".bz2" => $dir.$packages_file.".bz2"); if(is_error($res)) { my $res2 = mirror($uri.$packages_file.".gz" => $dir.$packages_file.".gz"); if(is_error($res2)) { push @errors,"Could not download $uri".$packages_file.".bz2 : $res"; push @errors,"Could not download $uri".$packages_file.".gz : $res2"; } else { gunzip ($dir.$packages_file.".gz" => $dir.$packages_file) or push @errors,"could not extract Packages file : $GunzipError"; } } else { bunzip2 ($dir.$packages_file.".bz2" => $dir.$packages_file) or push @errors,"could not extract Packages file : $Bunzip2Error"; } } } return \@errors; } =item cleanup_and_extract Extract templates from packages. =cut sub cleanup_and_extract { my ($servdir,$distribs) = @_; my $file; my $tmpdir = "/tmp"; mkpath($tmpdir); while (my ($distsection,$packages) = each(%{$distribs})) { my $outdir = "$servdir/debconf.d/$distsection"; mkpath($outdir); foreach my $package (@{$packages}) { if ((-f "$outdir/".$package->{'PACKAGE'}) || (-f "$outdir/".$package->{'PACKAGE'}.'-NOTEMPLATE')) { next; } system( "dpkg -e '$servdir/".$package->{'FILENAME'}."' '$tmpdir/DEBIAN'" ); if( -f "$tmpdir/DEBIAN/templates" ) { my $tmpl = encode_base64(file("$tmpdir/DEBIAN/templates")->slurp()); open ($file,q{>},"$outdir/".$package->{'PACKAGE'}) or die "cannot open file"; print $file $tmpl; close ($file); unlink ("$tmpdir/DEBIAN/templates"); } else { open ($file,q{>},"$outdir/".$package->{'PACKAGE'}.'-NOTEMPLATE') or die "cannot open file"; print $file "1\n"; close ($file); } } } unlink("$tmpdir/DEBIAN"); return; } 1; __END__ # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-common/XML/000077500000000000000000000000001304135723100167375ustar00rootroot00000000000000argonaut-1.0/argonaut-common/XML/SAX/000077500000000000000000000000001304135723100173725ustar00rootroot00000000000000argonaut-1.0/argonaut-common/XML/SAX/RPMHandler.pm000066400000000000000000000100551304135723100216650ustar00rootroot00000000000000####################################################################### # # XML::SAX::RPMHandler -- get and parse RPM Packages. # # Copyright (C) 2015-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package XML::SAX::RPMHandler; use strict; use warnings; use base qw(XML::SAX::Base); sub new { my $type = shift; my $self = { 'packages' => shift, 'package' => undef, 'key' => undef, 'fields' => shift, 'regexps' => shift, 'from' => shift, 'to' => shift, 'indice' => shift || 0, }; return bless $self, $type; } sub start_document { my ($self, $doc) = @_; $self->{package} = undef; $self->{key} = undef; } sub start_element { my ($self, $el) = @_; if ($el->{LocalName} eq 'package') { $self->{package} = {}; $self->{key} = undef; } elsif ((defined $self->{package}) && (defined $self->{fields}->{$el->{LocalName}})) { $self->{key} = $el->{LocalName}; $self->{attrs} = $el->{Attributes}; $self->{data} = ''; } } sub characters { my ($self, $data) = @_; if ((defined $self->{package}) && (defined $self->{key})) { if ($self->{key} eq 'name') { if ((ref($self->{packages}) eq 'HASH') && (defined $self->{packages}->{$data->{Data}})) { $self->{package} = undef; return; } if (defined $self->{regexps}) { my $match = 0; foreach my $regexp (@{$self->{regexps}}) { if ($data->{Data} =~ /$regexp/) { $match = 1; last; } } if($match == 0) { $self->{package} = undef; return; } } } $self->{data} .= $data->{Data}; } } sub end_element { my ($self, $el) = @_; if (defined $self->{package}) { if ($el->{LocalName} eq 'package') { if (ref($self->{packages}) eq 'ARRAY') { push @{$self->{packages}}, $self->{package}; } elsif (ref($self->{packages}) eq 'HASH') { $self->{packages}->{$self->{package}->{$self->{fields}->{'name'}}} = $self->{package}; } $self->{package} = undef; $self->{indice}++; if ((defined $self->{to}) && ($self->{indice} >= $self->{to})) { die 'LIMIT_REACHED'; } } elsif (defined $self->{fields}->{$el->{LocalName}}) { if (ref($self->{fields}->{$el->{LocalName}}) eq 'CODE') { $self->{fields}->{$el->{LocalName}}($self->{package}, $self->{key}, $self->{data}, $self->{attrs}); } else { $self->{package}->{$self->{fields}->{$el->{LocalName}}} = $self->{data}; } $self->{key} = undef; } } } 1; =pod Example: my $packages = []; my $parser = XML::SAX::ParserFactory->parser( Handler => XML::SAX::RPMHandler->new( $packages, { 'name' => 'PACKAGE', 'description' => sub { my ($package, undef, $data) = @_; $package->{'DESCRIPTION'} = $data; }, 'version' => sub { my ($package, undef, $data, $attrs) = @_; $package->{'VERSION'} = $attrs->{'{}ver'}->{'Value'}.'-'.$attrs->{'{}rel'}->{'Value'}; }, }, [ '^kernel', ], ) ); $parser->parse_uri("Centos/6/os/x86_64/primary.xml"); =cut argonaut-1.0/argonaut-common/XML/SAX/RPMRepomdHandler.pm000066400000000000000000000042751304135723100230430ustar00rootroot00000000000000####################################################################### # # XML::SAX::RPMRepomdHandler -- get and parse RPM Packages. # # Copyright (C) 2015-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package XML::SAX::RPMRepomdHandler; use strict; use warnings; use base qw(XML::SAX::Base); sub new { my $class = shift; my $self = { 'type' => shift || 'primary', }; return bless $self, $class; } sub start_document { my ($self, $doc) = @_; $self->{waiting} = 0; $self->{result} = undef; } sub start_element { my ($self, $el) = @_; if (($self->{waiting} == 0) && ($el->{LocalName} eq 'data') && ($el->{Attributes}->{'{}type'}->{'Value'} eq $self->{type})) { $self->{waiting} = 1; $self->{key} = undef; } elsif (($self->{waiting} == 1) && ($el->{LocalName} eq 'location')) { $self->{waiting}++; $self->{result} = $el->{Attributes}->{'{}href'}->{'Value'}; } } 1; =pod Example: use LWP::Simple; use XML::SAX; use XML::SAX::RPMRepomdHandler; my $uri = 'http://mirror.centos.org/centos/6/os/x86_64/repodata/'; my $dir = '/tmp/'; my $res = mirror($uri."repomd.xml" => $dir."repomd.xml"); if (is_error($res)) { die 'Could not download '.$uri.'repomd.xml: '.$res; } my $parser = XML::SAX::ParserFactory->parser( Handler => XML::SAX::RPMRepomdHandler->new() ); $parser->parse_uri($dir."repomd.xml"); print $parser->{Handler}->{result}; =cut argonaut-1.0/argonaut-common/argonaut.conf000066400000000000000000000002711304135723100207660ustar00rootroot00000000000000[server] server_ip = 127.0.0.1 [client] client_ip = 127.0.0.1 [ldap] config = /etc/ldap/ldap.conf dn = cn=admin,dc=fusiondirectory,dc=org password = secret tls = off argonaut-1.0/argonaut-dovecot/000077500000000000000000000000001304135723100164525ustar00rootroot00000000000000argonaut-1.0/argonaut-dovecot/Argonaut/000077500000000000000000000000001304135723100202325ustar00rootroot00000000000000argonaut-1.0/argonaut-dovecot/Argonaut/ClientDaemon/000077500000000000000000000000001304135723100225745ustar00rootroot00000000000000argonaut-1.0/argonaut-dovecot/Argonaut/ClientDaemon/Modules/000077500000000000000000000000001304135723100242045ustar00rootroot00000000000000argonaut-1.0/argonaut-dovecot/Argonaut/ClientDaemon/Modules/Dovecot.pm000066400000000000000000000044461304135723100261550ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon::Modules::Dovecot -- Dovecot mailbox creation # # Copyright (C) 2013-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::ClientDaemon::Modules::Dovecot; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config); my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item create_mailbox Creates the folder so that Dovecot will be able of creating the mailbox on first connection =cut sub create_mailbox : Public { my ($s, $args) = @_; my ($account_id, $uid, $gid) = @{$args}; my $msg = "Creating mailbox folder for user '$account_id'"; if ($uid) { $msg .= " with uid '$uid' of gid '$gid'"; } $main::log->notice($msg); mkdir get_maildir().'/'.$account_id, oct(2700) or die 'Could not create directory: '.$!; if ($uid) { chown $uid, $gid, get_maildir().'/'.$account_id or die 'Could not change directory owner: '.$!; } return 1; } sub get_maildir : Private { my ($ldap,$ldap_base) = argonaut_ldap_handle($main::config); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(objectClass=fdDovecotServer)(ipHostNumber=".$main::client_settings->{'ip'}."))", attrs => [ 'fdDovecotMailDir' ] ); if (scalar($mesg->entries)==1) { return ($mesg->entries)[0]->get_value("fdDovecotMailDir"); } die "Dovecot server not found in LDAP"; } 1; __END__ argonaut-1.0/argonaut-fai-mirror/000077500000000000000000000000001304135723100170565ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-mirror/bin/000077500000000000000000000000001304135723100176265ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-mirror/bin/argonaut-debconf-crawler000066400000000000000000000054141304135723100244300ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # argonaut-debconf-crawler - Extract debconf templates from packages # # Copyright (C) 2011-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :net :config); use Argonaut::Libraries::Packages qw(get_packages_info store_packages_file cleanup_and_extract); my $config = argonaut_read_config; my $settings = argonaut_get_crawler_settings($config,$config->{'client_ip'}); my $servpath = $settings->{'mirrordir'}; my $packagesfolder = $settings->{'packagesfolder'}; my $mymac = $settings->{'macaddress'}; #=pod #=item cleanup_and_extract #Extract templates from packages. #Templates are saved as $servdir/debconf.d/release/section/nameofthepackage # #=cut store_packages_file($packagesfolder,$mymac); # FIXME : may be replaced by an mv+extract (no getstore in this case). (we should avoid using http get to access a local file) cleanup_and_extract($servpath,get_packages_info($packagesfolder,$mymac,undef,["package","version","filename"])); print "done\n"; __END__ =head1 NAME argonaut-debconf-crawler - Extract debconf templates from packages =head1 SYNOPSIS argonaut-debconf-crawler =head1 DESCRIPTION argonaut-debconf-crwaler is a program used to extract debconf templates from packages =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 1 =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-fai-mirror/bin/argonaut-repository000066400000000000000000000210531304135723100236070ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # argonaut-repository # # Copyright (c) 2005,2006 Jan-Marek Glogowski # Copyrignt (c) 2007,2009 The GOsa project # Copyright (C) 2011-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use MIME::Base64; use Getopt::Std; use File::Path; use File::Copy; use Log::Handler; use Argonaut::Libraries::Common qw(:ldap :config :file :array :net); use Argonaut::Libraries::Packages qw(get_repolines); my ($archs,$mirrordir,$proxy,$mac,$client_ip); my ($verbose,$errors,$source,$gpgcheck,$contents,$pdiff,$logdir); my $result; my $outdir = "/etc/argonaut/fai"; my $crond_dir= "/etc/cron.daily"; my $logfile = "argonaut-repository.log"; my $config; readConfig(); # Verbose can be overridden by command line argument foreach my $arg ( @ARGV ) { if (lc($arg) eq "--verbose") { $verbose = "TRUE"; print "Verbose mode set with --verbose\n"; } } argonaut_create_dir($logdir); my $log = Log::Handler->create_logger("ldap2repository"); $log->add( file => { filename => "$logdir/$logfile", maxlevel => "debug", minlevel => "emergency", newline => 1, } ); $log->info ("ldap2repository started\n"); argonaut_create_dir($outdir); if( $verbose eq "TRUE" ) { print "Generating sources.list and debmirror cronjob for server\n" . "IP: $config->{'client_ip'}\n" . "Outdir: ${outdir}\n\n"; } #generate the cronjob script generate_cronjob_script(); #generate the cronjob daily generate_cronjob_daily(); exit 0; sub readConfig { $config = argonaut_read_config; my $settings = argonaut_get_ldap2repository_settings($config,$config->{'client_ip'}); $mirrordir = $settings->{'mirrordir'}; $proxy = $settings->{'proxy'}; $errors = $settings->{'errors'}; $source = $settings->{'source'}; $gpgcheck = $settings->{'gpgcheck'}; $contents = $settings->{'contents'}; $verbose = $settings->{'verbose'}; $mac = $settings->{'mac'}; $logdir = $settings->{'logdir'}; if ($logdir eq "") { $logdir = "/var/log/argonaut/"; } if ($errors eq "TRUE") { $errors = "--ignore-small-errors"; } else { $errors = ""; } if ($source eq "FALSE") { $source = "--nosource"; } else { $source = ""; } if ($gpgcheck eq "FALSE") { $gpgcheck = "--ignore-release-gpg"; } else { $gpgcheck = ""; } if ($contents eq "TRUE") { $contents = "--getcontents"; } else { $contents = ""; } } #------------------------------------------------------------------------------ # get ldap2repository argonaut settings # sub argonaut_get_ldap2repository_settings { return argonaut_get_generic_settings( 'argonautMirrorConfig', { 'mirrordir' => 'argonautMirrorDir', 'proxy' => 'argonautLdap2repProxy', 'errors' => 'argonautLdap2repErrors', 'source' => 'argonautLdap2repSource', 'gpgcheck' => 'argonautLdap2repGPGCheck', 'contents' => 'argonautLdap2repContents', 'verbose' => 'argonautLdap2repVerbose', 'logdir' => 'argonautLdap2repLogDir', }, @_ ); } sub generate_cronjob_daily { # generate new /etc/cron.d/argonaut-mirror my $crond; open ($crond, q{>}, "${crond_dir}/argonaut-mirror") || die "Can't create ${crond_dir}/argonaut-mirror: $!\n"; print $crond "PATH=/sbin:/bin:/usr/sbin:/usr/bin\n\n"; print $crond "\@daily root $outdir/mirror-update-cronjob\n"; close ($crond); } sub generate_cronjob_script { my @repos = get_repolines($mac); my $sources; if (scalar(@repos) == 0) { print( "This server doesn't contain software repositories\n" ); $log->info ("This server doesn't contain software repositories\n"); unlink "${outdir}/mirror-update-cronjob"; exit 0; } my $cron; open ($cron, q{>}, "${outdir}/mirror-update-cronjob") || die "Can't create ${outdir}/mirror-update-cronjob: $!\n"; # Write cron script print $cron "#!/bin/sh\n"; open ($sources, q{>}, "${outdir}/sources.list"); foreach my $repo (@repos) { my $uri = $repo->{'uri'}; my ($localuri) = $uri; my $parent_or_opts = $repo->{'parent'}; my $release = $repo->{'release'}; my $sections = join(',',@{$repo->{'sections'}}); my (@section_list) = @{$repo->{'sections'}}; my $archs = join(',',@{$repo->{'archs'}}); # archs comma-separated my $repoline = $repo->{'line'}; if ("" eq "$parent_or_opts") { print( "No parent for '$repoline'\n" ) if( $verbose eq "TRUE" ); $log->info ("No parent for '$repoline'\n"); next; } print $sources "deb $uri $release @section_list\n"; print "parent: $parent_or_opts\n" if( $verbose eq "TRUE" ); print "sources.list: deb $uri $release @section_list\n" if( $verbose eq "TRUE" ); my @par_repos = get_repolines(undef,$parent_or_opts); if (scalar(@par_repos) == 0) { print( "Couldn't find parent repository server for '$repoline'\n" ); $log->info ("Couldn't find parent repository server for '$repoline'\n"); next; } foreach my $par_repo (@par_repos) { my ($method,$host,$root); if ($par_repo->{'uri'} =~ m#([^:]+)://([^/]+)(.*)#) { $method = $1; $host = $2; $root = $3; $root =~ s|/$||; # Remove potential slash at the end } else { my $error = "Could not parse '".$par_repo->{'uri'}."' as a valid repo URI.\n"; print $error; $log->info($error); next; } my $par_release = $par_repo->{'release'}; my @outline = (); if ("$release" eq "$par_release") { foreach my $section (@{$par_repo->{'sections'}}) { if (argonaut_array_find_and_remove( \@section_list, ${section})) { push (@outline, $section); last if (0 == scalar @section_list); } } if (0 != scalar @{outline}) { my $cron_line = "\ndebmirror --nocleanup ${gpgcheck} ${source} ${errors} ${contents} --arch=${archs} --dist=${release} --section=" . join(",", @{outline}) . " --method=${method} --host=${host} --root=${root} ${proxy} ${mirrordir}/\n"; print $cron $cron_line; print "mirror-update-cronjob: ".$cron_line."\n" if( $verbose eq "TRUE" ); } last if (0 == scalar @section_list); } } if (scalar @section_list != 0) { print "No repository was found in parent $parent_or_opts for release $release, for sections ".join(",", @section_list)."\n" if( $verbose eq "TRUE" ); } } close ($sources); close ($cron); chmod 0750, "${outdir}/mirror-update-cronjob"; } __END__ =head1 NAME argonaut-repository - creating debian repositories cronjob for the Argonaut deployment system. =head1 SYNOPSIS argonaut-repository =head1 DESCRIPTION argonaut-repository is a program used to create the cronjob for creation the local debian mirrors. =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 3 =item Copyright (C) 2005-2006 Jan-Marek Glogowski =item Copyright (C) 2007-2010 The GOsa project =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-fai-mirror/man/000077500000000000000000000000001304135723100176315ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-mirror/man/argonaut-debconf-crawler.1000066400000000000000000000116501304135723100245710ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-DEBCONF-CRAWLER 1" .TH ARGONAUT-DEBCONF-CRAWLER 1 "2016-01-06" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-debconf\-crawler \- Extract debconf templates from packages .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-debconf-crawler .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-debconf-crwaler is a program used to extract debconf templates from packages .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2011\-2016 FusionDirectory project" 1 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fai-mirror/man/argonaut-repository.1000066400000000000000000000122351304135723100237530ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-REPOSITORY 1" .TH ARGONAUT-REPOSITORY 1 "2017-01-12" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-repository \- creating debian repositories cronjob for the Argonaut deployment system. .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-repository .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-repository is a program used to create the cronjob for creation the local debian mirrors. .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2005\-2006 Jan-Marek Glogowski" 3 .IX Item "Copyright (C) 2005-2006 Jan-Marek Glogowski" .PD 0 .IP "Copyright (C) 2007\-2010 The GOsa project" 3 .IX Item "Copyright (C) 2007-2010 The GOsa project" .IP "Copyright (C) 2011\-2016 FusionDirectory project" 3 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PD .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fai-monitor/000077500000000000000000000000001304135723100172335ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-monitor/bin/000077500000000000000000000000001304135723100200035ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-monitor/bin/argonaut-fai-monitor000066400000000000000000000211421304135723100237700ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # argonaut-fai-monitor - read status of installation and send information # to argonaut-server for FusionDirectory # # Copyright (C) 2014-2016 FusionDirectory project # # Using code from fai-monitor: # Copyright (C) 2003-2012 by Thomas Lange # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :file :config :string); use if (USE_LEGACY_JSON_RPC), 'JSON::RPC::Legacy::Client'; use if not (USE_LEGACY_JSON_RPC), 'JSON::RPC::Client'; use Log::Handler; use Socket; use Getopt::Std; use App::Daemon qw(daemonize); my ($port, $timeout); our ($opt_h,$opt_p,$opt_t); my $logfile = "argonaut-fai-monitor.log"; my $piddir = "/var/run/argonaut"; my $pidfile = "argonaut-fai-monitor.pid"; my $config = argonaut_read_config; my $settings = argonaut_get_generic_settings( 'argonautFAIMonitorConfig', { 'logdir' => "argonautFAIMonitorLogDir", 'cacertfile' => "argonautFAIMonitorCaCertPath", 'port' => "argonautFAIMonitorPort", 'timeout' => "argonautFAIMonitorTimeout" }, $config,$config->{'client_ip'} ); my $logdir = $settings->{'logdir'}; my $server_settings = argonaut_get_server_settings($config,$config->{'server_ip'}); my $server_port = $server_settings->{'port'}; my $protocol = $server_settings->{'protocol'}; my %taskids; my %progress_value = ( "confdir" => 0, "setup" => 1, "defclass" => 2, "defvar" => 3, "action" => 4, "install" => 5, "partition" => 6, "extrbase" => 7, "debconf" => 15, "prepareapt"=> 16, "updatebase"=> 17, "instsoft" => 18, "configure" => 80, "savelog" => 90 ); $App::Daemon::pidfile = "$piddir/$pidfile"; $App::Daemon::logfile = "$logdir/$logfile"; $App::Daemon::as_user = "root"; argonaut_create_dir($logdir); daemonize(); my $log = Log::Handler->create_logger("argonaut-fai-monitor"); $log->add( file => { filename => "$logdir/$logfile", maxlevel => "debug", minlevel => "emergency" } ); sub get_id { my $host = shift; unless (exists $taskids{$host}) { my $taskid = rpc_call( "get_host_id", [$host, '(objectClass=FAIobject)'] ); if($taskid) { if ($taskid->is_error) { die "Error for host '$host': ", $taskid->error_message."\n"; } else { $taskids{$host} = $taskid->content->{result}; } } else { die "Error while trying to contact Argonaut server\n"; } } return $taskids{$host}; } sub rpc_call { my ($method, $params) = @_; my $client; if (USE_LEGACY_JSON_RPC) { $client = new JSON::RPC::Legacy::Client; } else { $client = new JSON::RPC::Client; } $client->version('1.0'); if ($protocol eq 'https') { if ($client->ua->can('ssl_opts')) { $client->ua->ssl_opts( verify_hostname => 1, SSL_ca_file => $settings->{'cacertfile'}, SSL_verifycn_name => $server_settings->{'certcn'} ); } $client->ua->credentials($config->{'server_ip'}.":".$server_port, "JSONRPCRealm", "", argonaut_gen_ssha_token($server_settings->{'token'})); } my $callobj = { method => "$method", params => $params, }; my $res = $client->call($protocol."://".$config->{'server_ip'}.":".$server_port, $callobj); if($res) { if ($res->is_error) { $log->error("Error : ".$res->error_message); print "Error : ", $res->error_message."\n"; } } else { $log->error("Error while trying to contact Argonaut server : ".$client->status_line); print "Error while trying to contact Argonaut server : ".$client->status_line."\n"; } return $res; } sub parse_line { my ($line) = @_; chomp $line; my ($host,$keyword,$taskname,$errorcode) = split(/\s+/,$line); if(($keyword eq "TASKBEGIN") && ($taskname eq "confdir")) { # Clear cache as this is a new task delete $taskids{$host}; } my $taskid = eval {get_id($host);}; if ($@) { if (($keyword eq "TASKBEGIN") || ($keyword eq "TASKERROR") || (($keyword eq "TASKEND") && ($taskname eq "faiend"))) { print "Could not find taskid for line '$line':\n $@\n"; $log->error($@); return; } else { $log->debug($@); } } if($keyword eq "TASKBEGIN") { print "[monitor:$host] Task $taskname begun\n"; my $progress = undef; if(defined $progress_value{$taskname}) { $progress = $progress_value{$taskname}; } rpc_call( "set_task_substatus", [$taskid,$taskname,$progress] ); } elsif($keyword eq "TASKEND") { print "[monitor:$host] Task $taskname ended\n"; if($taskname eq "faiend") { rpc_call( "set_task_substatus", [$taskid,$taskname,100] ); delete $taskids{$host}; } } elsif($keyword eq "TASKERROR") { print "[monitor:$host] Task error $taskname $errorcode\n"; rpc_call( "set_error", [$taskid,$taskname." ".$errorcode], ); delete $taskids{$host}; } else { print "$line\n"; } } sub server_init { my ($port) = @_; $log->info("Argonaut FAI monitoring daemon starting..\n") or die "log: $!"; # Listen my $proto = getprotobyname('tcp'); socket(SERVER, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or die "setsockopt: $!"; my $paddr = sockaddr_in($port, INADDR_ANY); bind(SERVER, $paddr) or die "bind: $!"; listen(SERVER, SOMAXCONN) or die "listen: $!"; $log->info("Argonaut FAI monitoring daemon started on port $port with pid $$\n") or die "log: $!"; } sub big_loop { # accept a connection, print message received and close my ($client_addr); while ($client_addr = accept(CLIENT, SERVER)) { my ($port, $iaddr) = sockaddr_in($client_addr); my $ip = inet_ntoa($iaddr); my $inp = ''; eval { local $SIG{__DIE__}; local $SIG{__WARN__}; local $SIG{'ALRM'} = sub { die("Timeout"); }; alarm($timeout); $inp = ; alarm(0); }; close CLIENT; if (!defined($inp) || $inp eq '') { # Client did not send anything, or alarm went off $log->info("$ip:$port: No data or timeout.\n") or die "log: $!"; next; } parse_line($inp); } $log->error("accept returned: $!\n"); } sub usage { print << "EOF"; argonaut-fai-monitor, Argonaut FAI monitor daemon. Usage: argonaut-fai-monitor [OPTIONS] -p PORT Set port to listen to. Default is 4711. -t TIMEOUT Timeout for bad clients. 0 to disable. EOF exit 0; } getopts('hp:t:') || usage; $opt_h && usage; $port = $opt_p || $settings->{'port'}; if (defined $opt_t) { $timeout = $opt_t; } else { $timeout = $settings->{'timeout'}; } server_init($port); big_loop; __END__ =head1 NAME argonaut-fai-monitor - read status of installation and send information to argonaut-server for FusionDirectory =head1 SYNOPSIS argonaut-fai-monitor [OPTIONS] =head1 DESCRIPTION argonaut-fai-monitor replaces fai-monitor and send information to argonaut-server for FusionDirectory to show them in deployment queue =head1 OPTIONS =over 2 =item B

Set port to listen to. Default is 4711. =item B Timeout for bad clients. 0 to disable. =back =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 3 =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-fai-monitor/lib/000077500000000000000000000000001304135723100200015ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-monitor/lib/systemd/000077500000000000000000000000001304135723100214715ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-monitor/lib/systemd/system/000077500000000000000000000000001304135723100230155ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-monitor/lib/systemd/system/argonaut-fai-monitor.service000066400000000000000000000003741304135723100304450ustar00rootroot00000000000000[Unit] Description=Start argonaut-fai-monitor ConditionPathExists=/usr/sbin/argonaut-fai-monitor [Service] Type=forking ExecStart=/usr/sbin/argonaut-fai-monitor PIDFile=/var/run/argonaut/argonaut-fai-monitor.pid [Install] WantedBy=multi-user.target argonaut-1.0/argonaut-fai-monitor/man/000077500000000000000000000000001304135723100200065ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-monitor/man/argonaut-fai-monitor.1000066400000000000000000000123211304135723100241310ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-FAI-MONITOR 1" .TH ARGONAUT-FAI-MONITOR 1 "2016-11-13" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-fai\-monitor \- read status of installation and send information to argonaut\-server for FusionDirectory .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-fai-monitor [\s-1OPTIONS\s0] .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-fai-monitor replaces fai-monitor and send information to argonaut-server for FusionDirectory to show them in deployment queue .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fBp port\fR" 2 .IX Item "p port" Set port to listen to. Default is 4711. .IP "\fBt timeout\fR" 2 .IX Item "t timeout" Timeout for bad clients. 0 to disable. .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2011\-2016 FusionDirectory project" 3 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fai-nfsroot/000077500000000000000000000000001304135723100172365ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-nfsroot/bin/000077500000000000000000000000001304135723100200065ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-nfsroot/bin/argonaut-ldap2fai000077500000000000000000000374031304135723100232430ustar00rootroot00000000000000#!/usr/bin/perl -w ####################################################################### # # argonaut-ldap2fai # # Copyright (c) 2008 Landeshauptstadt München # Copyright (c) 2008-2010 GONICUS GmbH # Copyright (c) 2011-2016 The FusionDirectory Project # # Authors: Jan-Marek Glogowski # Cajus Pollmeier # Benoit Mortier # Come Bernigaud # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Net::LDAP; use Net::LDAP::Util qw(:escape); use Getopt::Long; use File::Path; use Argonaut::Libraries::Common qw(:ldap :config :net :array); use Argonaut::Libraries::FAI qw(:flags); my $ldapuris; my $dump_dir = "/var/lib/fai/config"; my $verbose = 0; my $print_classes = 0; my( $hostname, $host_base, $host_dn ); my $fai_mirror; my $dry_run = 0; my $release_var = 'FAIclientRelease'; my $check_hostname; my $sources_list; my $ldapinfos; my $mac = ''; my $ip = ''; my $config = argonaut_read_config; Getopt::Long::Configure ("bundling"); GetOptions( 'v|verbose' => \$verbose, 'h|help' => \&usage, 'c|config-space=s' => \$dump_dir, 'd|dry-run' => \$dry_run, 'n|hostname=s' => \$check_hostname, 's|sources-list' => \$sources_list, 'i|ip=s' => \$ip, 'm|mac=s' => \$mac ) or usage( 'Wrong parameters' ); # If we use dry-run, be verbose $verbose = 1 if( $dry_run ); if (($mac eq '') && ($ip eq '')) { usage( "Neither MAC address nor IP specified." ); } if (($mac ne '') && (!($mac =~ m/^([0-9a-f]{2}:){5}[0-9a-f]{2}/i))) { usage( "MAC address not valid." ); } # Is dump_dir a directory if( ! $dry_run ) { -d "$dump_dir" || usage("'$dump_dir' is not a directory.\n"); } else { print ("[DRY RUN]\n"); } my ($ldap,$base) = argonaut_ldap_handle($config); # Get FAI object my $faiobj = Argonaut::FAI->new( 'LDAP' => $ldap, 'base' => $base, 'dumpdir' => $dump_dir ); # Set FAI flags $faiobj->flags( $faiobj->FAI_FLAG_VERBOSE ) if( $verbose ); $faiobj->flags( $faiobj->FAI_FLAG_VERBOSE | $faiobj->FAI_FLAG_DRY_RUN ) if( $dry_run ); my $class_str = get_classes( $mac, $ip ); print( " + FAIclass string: $class_str\n" ) if( $verbose ); my ($res_classlist, $release) = $faiobj->resolve_classlist( $class_str ); if( 'ARRAY' eq ref( $res_classlist ) ) { if( $verbose ) { print( " + Release: $release\n" ); print( " + Resolved classlist: " . join( ' ', @$res_classlist ) . "\n" ); } } else { do_exit( 8, $res_classlist ); } if( ! $dry_run ) { create_dir( "$dump_dir/class" ); my $faiclass; open ($faiclass, q{>}, "$dump_dir/class/${hostname}") || do_exit( 4, "Can't create $dump_dir/class/${hostname}. $!\n" ); print($faiclass join( ' ', @$res_classlist ) ); close($faiclass); } $res_classlist = $faiobj->expand_fai_classlist( $res_classlist, $hostname ); if( 'ARRAY' eq ref( $res_classlist ) ) { print( " + FAI classlist: " . join( ' ', @$res_classlist ) . "\n" ) if( $verbose ); } print( "Extending FAI classtree with real objects...\n" ); $faiobj->extend_class_cache( $release ); print( "Dumping config space to '$dump_dir'...\n" ); my( $error, $sections, $customs ) = $faiobj->dump_release( $release, $res_classlist, $hostname ); print $error . "\n" if( defined $error ); generate_files_dir_configspace(); generate_sources_list( $sections ) if ($sources_list); $ldap->unbind(); # take down session $ldap->disconnect(); exit 0; # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub usage { (@_) && $_[0] ne "h" && print STDERR "\n@_\n\n"; print STDERR << "EOF"; usage: $0 [-hnvW] [-c config_space] [-n hostname] [-m mac_address | -i ip_address] -h : this (help) message -d : dry run (includes verbose) -v : be verbose -c : config space (default: ${dump_dir}) -n : check hostname -m : mac address -i : ip address EOF exit -1; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub do_exit { my ($code,$msg) = @_; my @exit_msg = ( 0, # Ok 0, # Usage 0, # LDAP error 0, # No entries found 0, # Create file 0, # Mkdir (5) 0, # LDAP lookup 0, # FAI object "No releases found in classlist. Releases are classes starting with ':'.", "Multiple releases found! Fix your classes or profiles.\n", 0, # Hostname mismatch (10) 0, # Release object not found 0, # Multiple profiles ); if( ! defined $msg ) { if( exists $exit_msg[ $code ] ) { $msg = $exit_msg[ $code ]; } } else { if( ! exists $exit_msg[ $code ] ) { $msg .= "\nMissing exit ID - assign one!"; } elsif( $exit_msg[ $code ] ) { $msg .= "\n" . $exit_msg[ $code ]; } } print( "$msg\n" ) if( defined $msg ); $ldap->unbind() if( defined $ldap ); exit( -1 * $code ); } sub create_dir { if( ! -d "$_[0]" ) { return if( $dry_run ); eval { mkpath "$_[0]"; }; do_exit( 5, "Can't create dir $_[0]: $!\n" ) if( $@ ); } } sub get_classes { # return list of FAI classes defined for host my $mac = shift; my $ip = shift; my (@classes,$mesg,$entry); my $host_info; my $real_hostname; my $filter = "(&(objectClass=goHard)"; if ($mac ne '') { print( "Lookup host for MAC '$mac'...\n" ) if( $verbose ); $filter .= "(macAddress=$mac))"; } else { print( "Lookup host for IP '$ip'...\n" ) if( $verbose ); $filter .= "(ipHostNumber=$ip))"; } $mesg = $ldap->search( base => "$base", filter => $filter, attrs => [ 'FAIclass', 'cn', 'FAIdebianMirror' ]); $mesg->code && do_exit( 2, sprintf( "LDAP error: %s (%i)", $mesg->error, __LINE__ ) ); # normally, only one value should be returned if( 1 != $mesg->count ) { if( 0 == $mesg->count ) { do_exit( 3, "LDAP search for client failed!\n" . "No entries have been returned.\n" . " - Base: $base\n" . " - Filter: $filter\n" ); } else { do_exit( 3, "LDAP search for client failed!\n" . $mesg->count . " entries have been returned.\n" . " - Base: $base\n" . " - Filter: $filter\n" ); } } # get the entry, host DN and hostname $entry = ($mesg->entries)[0]; $host_dn = $entry->dn; $hostname = $entry->get_value( 'cn' ); $real_hostname = $hostname; # set $host_base my @rdn = argonaut_ldap_split_dn( $host_dn ); shift( @rdn ); # hostname shift( @rdn ); # servers / workstations / terminals shift( @rdn ); # systems $host_base = join( ',', @rdn ); # strip domain from LDAP hostname for FAI class $hostname =~ s/\..*//; $host_info = " + Host DN: $host_dn\n" . " + Base: $host_base\n" . " + Hostname: $hostname"; $host_info .= ' (' . $real_hostname . ')' if ( $hostname ne $real_hostname ); $host_info .= "\n"; # Check for hostname mismatch if( defined $check_hostname ) { if( $real_hostname !~ m/^${check_hostname}$/i ) { # Try stripped domain (non-FQDN) hostname do_exit( 10, "Hostname mismatch: net='$check_hostname', " . "LDAP='$real_hostname', non-FQDN='$hostname'" ) if( $hostname !~ m/^${check_hostname}$/i ); } } # check, if we have a FAIclass value, otherwise check groups my $fai_class_str = $entry->get_value( 'FAIclass' ); if( (! defined $fai_class_str) || ('' eq $fai_class_str) ) { print( "No FAI information stored in host object - looking for host groups...\n" ) if( $verbose ); $filter = '(&(member=' . escape_filter_value(${host_dn}) . ')(objectClass=gosaGroupOfNames)(gosaGroupObjects=[*])(objectClass=FAIobject))'; $mesg = $ldap->search( base => "$base", filter => $faiobj->prepare_filter( $filter ), attrs => [ 'FAIclass', 'cn', 'FAIdebianMirror' ]); $mesg->code && do_exit( 2, sprintf( "LDAP error: %s (%i)", $mesg->error, __LINE__ ) ); if( 1 != $mesg->count ) { if( 0 == $mesg->count ) { do_exit( 3, "LDAP search for object groups with FAIobject containing the client failed!\n" . "No entries have been returned.\n" . " - Base: $base\n" . " - Filter: $filter\n" ); } else { do_exit( 3, "LDAP search for object groups with FAIobject containing the client failed!\n" . $mesg->count . " entries have been returned.\n" . " - Base: $base\n" . " - Filter: $filter\n" ); } } $entry = ($mesg->entries())[0]; print( "Found FAI information in object group '" . $entry->get_value( 'cn' ) . "'\n" . ' + Object group: ' . $entry->dn() . "\n" ) if( $verbose ); } $fai_mirror = $entry->get_value( 'FAIdebianMirror' ); print( $host_info ) if $verbose; return $entry->get_value( 'FAIclass' ); } sub generate_files_dir_configspace { mkpath("${dump_dir}/files"); mkpath("${dump_dir}/files/etc"); mkpath("${dump_dir}/files/etc/default"); mkpath("${dump_dir}/files/etc/resolv.conf"); mkpath("${dump_dir}/files/etc/hosts"); mkpath("${dump_dir}/files/etc/dhcp/dhcpd.conf"); mkpath("${dump_dir}/files/etc/apt/sources.list"); mkpath("${dump_dir}/files/etc/apt/preferences"); mkpath("${dump_dir}/files/etc/fai/apt/sources.list"); mkpath("${dump_dir}/files/etc/fai/fai.conf"); mkpath("${dump_dir}/files/etc/fai/nfsroot.conf"); mkpath("${dump_dir}/files/motd"); mkpath("${dump_dir}/files/etc/rc.local"); mkpath("${dump_dir}/files/etc/selinux"); mkpath("${dump_dir}/files/etc/selinux/config"); } sub generate_sources_list { my( $sections ) = @_; my( $line, @deblines, @modsections, @rdns, %saw, $debline ); # Create unique list undef %saw; @saw{@$sections} = (); @$sections = sort keys %saw; if ($verbose) { print "Generate template '/etc/apt/sources.list' for class 'LAST'\n" . " - searching server(s) for\n" . " + release: ${release}\n" . " + sections: @$sections\n"; } create_dir( "${dump_dir}/files/etc/apt/sources.list" ); my $sources; if( ! $dry_run ) { open ($sources, q{>}, "${dump_dir}/files/etc/apt/sources.list/LAST") || do_exit( 4, "Can't create ${dump_dir}/files/etc/apt/sources.list/LAST. $!\n" ); } if( "auto" ne "$fai_mirror" ) { if( ! $dry_run ) { print $sources "deb $fai_mirror $release @$sections\n"; close ($sources); } print( " = Using default: $fai_mirror\n" ) if( $verbose ); return 0; } add_repo_for_release($sources,$release,$sections); foreach my $custom (@$customs) { print "Searching custom $custom for sections @$sections\n"; add_repo_for_release($sources,$custom,$sections); } close ($sources) if( ! $dry_run ); } sub add_repo_for_release { my ($filehandle,$release_name,$sections) = @_; my @sec = @$sections; # copying sections my %release_sections = (); my ($mesg,$search_base,@entries); $release_sections{ "$release_name" } = \@sec; #reference the copy my $fin = 0; while (!$fin) { # Prepare search base if (! defined $search_base) { $search_base = $host_base; } else { my @rdn = argonaut_ldap_split_dn( $search_base ); shift( @rdn ); $search_base = join( ',', @rdn ); } print( " - using search start base: $search_base\n" ) if $verbose; # Look for repository servers ($mesg,$search_base) = argonaut_ldap_rsearch( $ldap, $host_base, '', $faiobj->prepare_filter( '(objectClass=FAIrepositoryServer)' ), 'one', 'ou=servers,ou=systems', [ 'FAIrepository', 'cn' ] ); goto BAILOUT_CHECK_SERVER if( ! defined $mesg ); $mesg->code && do_exit ( 2, sprintf( "LDAP error: %s (%i)", $mesg->error, __LINE__ ) ); if (scalar $mesg->entries == 0) { next; } # Check all found servers print( " - found matches in base: $search_base\n" ) if( $verbose && $mesg->count() ); $fin = 1; foreach my $entry ($mesg->entries) { print " - inspecting repository server: " . $entry->get_value('cn') . "\n" if $verbose; foreach my $repoline ($entry->get_value('FAIrepository')) { my (@items) = split( '\|', ${repoline} ); my (@modsections) = split( ',', $items[3] ); # Check repository release if( exists $release_sections{ $items[2] } ) { # Check sections # Idea: try to remove local section from global section list. # If not remove, removed from local list # and add to my $index = 0; foreach my $section (@modsections) { if (argonaut_array_find_and_remove ( $release_sections{ $items[2] }, $section ) ) { $index++; # The section is needed, we keep ip } else { splice( @modsections, $index, 1 ); # We don't want this section, remove it } } if (scalar $release_sections{$items[2]} == 0) { delete $release_sections{$items[2]}; } # Add deb-line for server, if we have local sections if( scalar @modsections > 0 ) { my $debline = "deb $items[ 0 ] $items[ 2 ] " . join(' ',@modsections) . "\n"; print " + add: $debline" if $verbose; print SOURCES "$debline" if( ! $dry_run ); } last if( scalar keys ( %release_sections ) == 0); } } # Check, if there we still have some sections in any release $fin = 1; while ( my ($key, $value) = each(%release_sections) ) { if (scalar @$value != 0) { $fin = 0; last; } } last if $fin; } } BAILOUT_CHECK_SERVER: if( ! $fin ) { if( $verbose ) { print "Missing sections for release:\n"; while ( my ($key, $value) = each(%release_sections) ) { print " + $key: @$value\n" } } exit -2; } } __END__ =head1 NAME argonaut-ldap2fai - read FAI config from LDAP and create config space. =head1 SYNOPSIS argonaut-ldap2fai [-hnv] [-c config_space] [-h hostname] [-m mac_address | -i ip_address] =head1 OPTIONS B<-h> print out this help message B<-v> be verbose (multiple v's will increase verbosity) B<-d> dry run (includes verbose) B<-c> output dir (default: /var/lib/fai/config) B<-h> check hostname B<-m> mac address B<-i> ip address =head1 DESCRIPTION argonaut-ldap2fai is a script to read the fai config space from LDAP and create it on the disk. =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 3 =item Copyright (c) 2008 Landeshauptstadt Munchen =item Copyright (C) 2007-2010 The GOsa project =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-fai-nfsroot/lib/000077500000000000000000000000001304135723100200045ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-nfsroot/lib/get-config-dir-argonaut000077500000000000000000000005731304135723100243530ustar00rootroot00000000000000#!/bin/bash # (c) 2011-2016 The FusionDirectory Project ### BEGIN SUBROUTINE INFO # Provides-Var: # Requires-Var: $FAI $LOGDIR $IPADDR $HOSTNAME # Suggests-Var: # Short-Description: get $FAI from an ldap server. ### END SUBROUTINE INFO # Create configuration space argonaut-ldap2fai -v -c $FAI -n $HOSTNAME -i $IPADDR | tee /tmp/fai/ldap2fai.log argonaut-1.0/argonaut-fai-nfsroot/man/000077500000000000000000000000001304135723100200115ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-nfsroot/man/argonaut-ldap2fai.1000066400000000000000000000130661304135723100234010ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-LDAP2FAI 1" .TH ARGONAUT-LDAP2FAI 1 "2017-01-12" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-ldap2fai \- read FAI config from LDAP and create config space. .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut\-ldap2fai [\-hnv] [\-c config_space] [\-h hostname] [\-m mac_address | \-i ip_address] .SH "OPTIONS" .IX Header "OPTIONS" \&\fB\-h\fR print out this help message .PP \&\fB\-v\fR be verbose (multiple v's will increase verbosity) .PP \&\fB\-d\fR dry run (includes verbose) .PP \&\fB\-c\fR output dir (default: /var/lib/fai/config) .PP \&\fB\-h\fR check hostname .PP \&\fB\-m\fR mac address .PP \&\fB\-i\fR ip address .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut\-ldap2fai is a script to read the fai config space from \s-1LDAP\s0 and create it on the disk. .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (c) 2008 Landeshauptstadt Munchen" 3 .IX Item "Copyright (c) 2008 Landeshauptstadt Munchen" .PD 0 .IP "Copyright (C) 2007\-2010 The GOsa project" 3 .IX Item "Copyright (C) 2007-2010 The GOsa project" .IP "Copyright (C) 2011\-2016 FusionDirectory project" 3 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PD .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fai-server/000077500000000000000000000000001304135723100170525ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-server/bin/000077500000000000000000000000001304135723100176225ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-server/bin/fai2ldif000066400000000000000000000367771304135723100212510ustar00rootroot00000000000000#!/usr/bin/perl -w ####################################################################### # # fai2ldif - script to read the fai classes files and create ldif files. # # Copyright (c) 2014-2016 The FusionDirectory Project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Net::LDAP; use Getopt::Long; # used to manage files use Path::Class; use File::Find; use Fcntl qw(:mode); use MIME::Base64; my $dump_dir = "/var/lib/fai/config"; my $verbose = 0; my $base = ''; my $faibaserdn = 'ou=fai,ou=configs,ou=systems'; my %faitypes = ( 'package' => { 'rdn' => 'ou=packages', }, 'disk' => { 'rdn' => 'ou=disk', }, 'variable' => { 'rdn' => 'ou=variables', }, 'hook' => { 'rdn' => 'ou=hooks', 'class' => 'FAIhook', 'subclass' => 'FAIhookEntry' }, 'script' => { 'rdn' => 'ou=scripts', 'class' => 'FAIscript', 'subclass' => 'FAIscriptEntry' }, 'template' => { 'rdn' => 'ou=templates', 'class' => 'FAItemplate', 'subclass' => 'FAItemplateEntry' } ); my $dist = ''; my $release = ''; my $outfile; my $outfilename = ''; Getopt::Long::Configure ("bundling"); GetOptions( 'v|verbose' => \$verbose, 'h|help' => \&usage, 'c|config-space=s' => \$dump_dir, 'd|dist=s' => \$dist, 'r|release=s' => \$release, 'o|output-file=s' => \$outfilename, 'b|base=s' => \$base ) or usage( 'Wrong parameters' ); # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub usage { (@_) && $_[0] ne "h" && print STDERR "\n@_\n\n"; print STDERR << "EOF"; usage: $0 [-hv] [-c config_space] [-d dist] [-r release] [-o file] -b base class -h : this (help) message -v : be verbose -c : config space (default: ${dump_dir}) -b : ldap base -d : distribution -r : release -o : output file EOF exit -1; } my $class = shift or usage('Missing class parameter'); if ($dist) { $faibaserdn = "ou=$dist,$faibaserdn"; } if ($release) { $faibaserdn = "ou=$release,$faibaserdn"; } if ($outfilename) { open($outfile, q{>}, $outfilename) or die "Could not open '$outfilename'\n"; } else { $outfile = *STDOUT; } my %configured_packages; my $debconf_output; parse_complete_class($class); sub parse_complete_class { my ($parsing_class) = @_; $class = $parsing_class; # Debconf first as it could change package_config handling %configured_packages = (); $debconf_output = ''; parse_class("debconf/$class", sub {return {};}, \&line_parser_debconf); my $package_parser = parse_class("package_config/$class", sub {return {'used' => [], 'package_classes' => []};}, \&line_parser_package); handle_package_list_end($package_parser); parse_class("disk_config/$class", \&parse_init_disk, \&line_parser_disk); parse_class("class/$class.var", \&parse_init_variables, \&line_parser_variables); parse_files('script', \&file_parser_script, "scripts/$class"); parse_files('template', \&file_parser_template, "files"); parse_files('hook', \&file_parser_hook, "hooks"); # Classes having the same name as package sublist *may* be related so we pull them as well foreach my $package_class (@{$package_parser->{'package_classes'}}) { parse_complete_class($package_class); } } sub parse_class { my ($filepath, $init_parser, $line_parser) = @_; $filepath = "$dump_dir/$filepath"; if (-f $filepath) { my $file = file($filepath); print "# parsing $file\n" if $verbose; my $parser = &$init_parser(); my @lines = $file->slurp; foreach my $line ( @lines ) { # remove comments $line =~ s/#.*$//; # remove \n from the end of each line chomp $line; # ignore empty lines next if ( $line =~ /^$/ ); $parser->$line_parser($line); } return $parser; } } sub line_parser_package { my $infos = shift; my $line = shift; # only process for lines beginning with "class", and extracting the 2nd word (the class name) if ( $line =~ /^PACKAGES\s+([^ ]+)(\s+([^ ]*))?/ ) { my $cn = $class; if ($3) { if ($infos->{'main'}) { # We just ended main package list while (my ($package, $v) = each %configured_packages) { if ($v) { print $outfile "FAIpackage: $package\n"; $configured_packages{$package} = 0; } } } $cn .= '-'.$3; $infos->{'main'} = 0; push @{$infos->{'package_classes'}}, $3; } else { $infos->{'main'} = 1; } if (grep {$_ eq $cn} @{$infos->{'used'}}) { $cn .= '-'.$1; while (grep {$_ eq $cn} @{$infos->{'used'}}) { $cn .= '-'; } } push @{$infos->{'used'}}, $cn; print $outfile "\n"; print $outfile "dn: cn=$cn,".$faitypes{'package'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile "cn: $cn\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: FAIpackageList\n"; print $outfile "FAIinstallMethod: $1\n"; } else { my @packages = split(/\s+/, $line); foreach my $package (@packages) { print $outfile "FAIpackage: $package\n"; if ($infos->{'main'}) { $configured_packages{$package} = 0; } } } } sub handle_package_list_end { my $infos = shift; if ((not $infos->{'main'}) && (grep {$configured_packages{$_}} keys %configured_packages)) { # We had no main package list print $outfile "\n"; print $outfile "dn: cn=$class,".$faitypes{'package'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile "cn: $class\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile 'objectClass: FAIpackageList'."\n"; } while (my ($package, $v) = each %configured_packages) { if ($v) { print $outfile "FAIpackage: $package\n"; $configured_packages{$package} = 0; } } print $outfile "\n"; print $outfile $debconf_output; } sub parse_init_disk { print $outfile "dn: cn=$class,".$faitypes{'disk'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile "cn: $class\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: FAIpartitionTable\n"; print $outfile "FAIpartitionMethod: setup-storage\n"; print $outfile "\n"; return {}; } sub line_parser_disk { my $infos = shift; my $line = shift; if ( $line =~ /^disk_config\s+([^\s]+)(\s+(.+))?/ ) { if ($1 eq 'lvm') { $infos->{'disk_type'} = 'lvm'; } else { $infos->{'disk_type'} = 'disk'; $infos->{'disk_cn'} = $1; print $outfile 'dn: cn='.$infos->{'disk_cn'}.",cn=$class,".$faitypes{'disk'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile 'cn: '.$infos->{'disk_cn'}."\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: FAIpartitionDisk\n"; print $outfile 'FAIdiskType: '.$infos->{'disk_type'}."\n"; foreach my $option (split ' ',$3) { print $outfile "FAIdiskOption: $option\n"; } print $outfile "\n"; $infos->{'partitionNr'} = 1; } } elsif (($infos->{'disk_type'} eq 'lvm') and ($line =~ /^vg\s+([^\s]+)\s+([^\s]+)/)) { $infos->{'disk_cn'} = $1; print $outfile 'dn: cn='.$infos->{'disk_cn'}.",cn=$class,".$faitypes{'disk'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile 'cn: '.$infos->{'disk_cn'}."\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: FAIpartitionDisk\n"; print $outfile 'FAIdiskType: '.$infos->{'disk_type'}."\n"; foreach my $option (split ',',$2) { print $outfile "FAIlvmDevice: $option\n"; } print $outfile "\n"; $infos->{'partitionNr'} = 1; } elsif ($line =~ /^([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)(\s+createopts="([^"]*)")?(\s+tuneopts="([^"]*)")?/) { print $outfile 'dn: FAIpartitionNr='.$infos->{'partitionNr'}.',cn='.$infos->{'disk_cn'}.",cn=$class,".$faitypes{'disk'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile 'FAIpartitionNr: '.$infos->{'partitionNr'}."\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: FAIpartitionEntry\n"; if ($infos->{'disk_type'} eq 'lvm') { my $cn = $1; $cn =~ s/^$infos->{'disk_cn'}-//; print $outfile "cn: $cn\n"; print $outfile "FAIpartitionType: lvm\n"; } else { print $outfile 'cn: '.$infos->{'partitionNr'}."\n"; print $outfile "FAIpartitionType: $1\n"; } print $outfile "FAImountPoint: $2\nFAIpartitionSize: $3\nFAIfsType: $4\nFAImountOptions: $5\n"; chomp $7 if defined $7; chomp $9 if defined $9; print $outfile "FAIfsCreateOptions: $7\n" if defined $7; print $outfile "FAIfsTuneOptions: $9\n" if defined $9; print $outfile "\n"; $infos->{'partitionNr'}++; } else { print STDERR "Could not parse line $line\n"; } } sub parse_init_variables { print $outfile "dn: cn=$class,".$faitypes{'variable'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: FAIvariable\n"; print $outfile "cn: $class\n"; print $outfile "\n"; return {}; } sub line_parser_variables { my $infos = shift; my $line = shift; if ( $line =~ /^([^=]+)=(.*)$/ ) { my $cn = $1; my $value = $2; if (($value =~ m/^'(.*)'$/) || ($value =~ m/^"(.*)"$/)) { $value = $1; } print $outfile "dn: cn=$cn,cn=$class,".$faitypes{'variable'}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: FAIvariableEntry\n"; print $outfile "cn: $cn\nFAIvariableContent: $value\n"; print $outfile "\n"; } else { print STDERR "Could not parse line $line\n"; } } sub line_parser_debconf { my $infos = shift; my $line = shift; if ( $line =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$/ ) { $debconf_output .= "dn: FAIvariable=$2,cn=$class,".$faitypes{'package'}->{'rdn'}.",$faibaserdn,$base\n"; $debconf_output .= "objectClass: FAIdebconfInfo\n"; $debconf_output .= "FAIpackage: $1\n"; $debconf_output .= "FAIvariable: $2\n"; $debconf_output .= "FAIvariableType: $3\n"; $debconf_output .= "FAIvariableContent: $4\n"; $debconf_output .= "\n"; if (not $configured_packages{$1}) { $configured_packages{$1} = 1; } } else { print STDERR "Could not parse line $line\n"; } } sub parse_files { my ($type, $file_parser, $filepath) = @_; $filepath = "$dump_dir/$filepath"; return if ! -d $filepath; my $parser = Argonaut::Librairies::FAI::ClassParser->new($type); my $tf_finder = sub { print '# Parsing '.$File::Find::name."\n" if $verbose; $parser->$file_parser($_, $File::Find::name, $filepath); }; find( $tf_finder, $filepath ); $parser->print_ldif(); } sub file_parser_template { my $parser = shift; shift; return if ! -f; return if ! /^$class$/; my $path = shift; my $dirname = shift; $path =~ s/^$dirname//; $path =~ s|/$class$||; my $stats = file($_)->stat; my $content = encode_base64(file($_)->slurp, "\n "); $content =~ s/\s+$//; push @{$parser->{'nodes'}}, { 'cn' => $path, 'lines' => [ "FAItemplatePath: $path\n", "FAItemplateFile:: $content\n", sprintf ("FAImode: %04o\n", S_IMODE($stats->mode)), "FAIowner: ".getpwuid($stats->uid).'.'.getgrgid($stats->gid)."\n", ] }; } sub file_parser_script { my $parser = shift; shift; return if ! -f; /^([0-9]+)-(.+)$/; my $prio = $1; my $cn = $2; my $content = encode_base64(file($_)->slurp, "\n "); $content =~ s/\s+$//; push @{$parser->{'nodes'}}, { 'cn' => $cn, 'lines' => [ "FAIpriority: $prio\n", "FAIscript:: $content\n", ] }; } sub file_parser_hook { my $parser = shift; shift; return if ! -f; return if ! /\.$class(\.source)?$/; if (/\.source$/) { print "# Skipping $_ because LDAP schemas do not support .source\n"; return; } /^(.+)\.$class$/; my $task = $1; my $content = encode_base64(file($_)->slurp, "\n "); $content =~ s/\s+$//; push @{$parser->{'nodes'}}, { 'cn' => $task, 'lines' => [ "FAItask: $task\n", "FAIscript:: $content\n", ] }; } package Argonaut::Librairies::FAI::ClassParser; sub new { my $class = shift; bless { 'type' => shift, 'nodes' => [], }, $class; } sub print_ldif { my $parser = shift; return if (scalar @{$parser->{'nodes'}} eq 0); my $dn_base; if ($faitypes{$parser->{'type'}}->{'class'}) { print $outfile "dn: cn=$class,".$faitypes{$parser->{'type'}}->{'rdn'}.",$faibaserdn,$base\n"; print $outfile 'cn: '.$class."\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: ".$faitypes{$parser->{'type'}}->{'class'}."\n"; print $outfile "\n"; $dn_base = ",cn=$class,".$faitypes{$parser->{'type'}}->{'rdn'}.",$faibaserdn,$base\n"; } else { $dn_base = ",".$faitypes{$parser->{'type'}}->{'rdn'}.",$faibaserdn,$base\n"; } foreach my $node (@{$parser->{'nodes'}}) { print $outfile 'dn: cn='.$node->{'cn'}.$dn_base; print $outfile 'cn: '.$node->{'cn'}."\n"; print $outfile 'objectClass: top'."\n"; print $outfile 'objectClass: FAIclass'."\n"; print $outfile "objectClass: ".$faitypes{$parser->{'type'}}->{'subclass'}."\n"; foreach (@{$node->{'lines'}}) { print $outfile $_; } print $outfile "\n"; } } __END__ =head1 NAME fai2ldif - read fai classes and create an ldif file to be imported into an ldap server =head1 SYNOPSIS fai2ldif [-hv] [-c config_space] [-d dist] [-r release] [-o output filename] -b base class =head1 OPTIONS B<-h> print out this help message B<-v> be verbose (multiple v's will increase verbosity) B<-c> config sapce (default: /var/lib/fai/config) B<-d> Distribution name B<-b> ldap base B<-o> output filename =head1 DESCRIPTION fai2ldif is a script to read the fai classes files and create ldif files. =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 3 =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-fai-server/bin/yumgroup2yumi000066400000000000000000000115621304135723100224270ustar00rootroot00000000000000#!/usr/bin/perl -w ####################################################################### # # yumgroup2yumi # # Copyright (c) 2015-2016 The FusionDirectory Project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Getopt::Long; use Path::Class; my $dump_dir = "/var/lib/fai/config"; my $verbose = 0; my $outfile; my $outfilename = ''; Getopt::Long::Configure ("bundling"); GetOptions( 'v|verbose' => \$verbose, 'h|help' => \&usage, 'c|config-space=s' => \$dump_dir, 'o|output-file=s' => \$outfilename, ) or usage( 'Wrong parameters' ); # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub usage { (@_) && $_[0] ne "h" && print STDERR "\n@_\n\n"; print STDERR << "EOF"; usage: $0 [-hv] [-c config_space] [-o file] class -h : this (help) message -v : be verbose -c : config space (default: ${dump_dir}) -o : output file EOF exit -1; } my $class = shift or usage('Missing class parameter'); if ($outfilename) { open($outfile, q{>}, $outfilename) or die "Could not open '$outfilename'\n"; } else { $outfile = *STDOUT; } parse_class("package_config/$class", sub {return {};}, \&line_parser_package); sub parse_class { my ($filepath, $init_parser, $line_parser) = @_; $filepath = "$dump_dir/$filepath"; if (-f $filepath) { my $file = file($filepath); print "# parsing $file\n" if $verbose; my $parser = &$init_parser(); my @lines = $file->slurp; foreach my $line ( @lines ) { # remove comments $line =~ s/#.*$//; # remove \n from the end of each line chomp $line; # ignore empty lines next if ( $line =~ /^$/ ); $parser->$line_parser($line); } return $parser; } } sub line_parser_package { my $infos = shift; my $line = shift; # only process for lines beginning with "class", and extracting the 2nd word (the class name) if ( $line =~ /^PACKAGES\s+([^ ]+)(\s+([^ ]*))?/ ) { my $cn = $class; my $name = $3; if ($3) { $cn .= '-'.$3; $infos->{'main'} = 0; } else { $name = 'YUMGROUP'; $infos->{'main'} = 1; } $infos->{'method'} = $1; if ($infos->{'method'} eq 'yumgroup') { print $outfile "\n"; print $outfile "PACKAGES yumi $name\n"; } } elsif ($infos->{'method'} eq 'yumgroup') { my @groups = split(/\s+/, $line); foreach my $group (@groups) { my @packages = qx"env LANGUAGE=C yum groupinfo $group"; my $list = -1; my %lists = ( 0 => [], 1 => [], 2 => [], ); foreach my $package (@packages) { if ($package =~ m/^\s*(Mandatory|Optional|Default) Packages/i) { $list++; } elsif ($list >= 0) { chomp $package; $package =~ s/(^\s+|\s+$)//; push @{$lists{$list}}, $package; } } foreach my $package (@{$lists{0}}) { print $outfile "$package\n"; } foreach my $package (@{$lists{1}}) { print $outfile "$package\n"; } } } } __END__ =head1 NAME yumgroup2yumi - read yumgroups and create ldif files for use =head1 SYNOPSIS yumgroup2yumi [-hv] [-c config_space] [-o output filename] =head1 DESCRIPTION yumgroup2yumi is a script to read yumgroups and create ldif files in yumi format =head1 OPTIONS B<-h> print out this help message B<-v> be verbose (multiple v's will increase verbosity) B<-c> config sapce (default: /var/lib/fai/config) B<-o> output filename =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 3 =item Copyright (C) 2015-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-fai-server/man/000077500000000000000000000000001304135723100176255ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-server/man/fai2ldif.1000066400000000000000000000124241304135723100213720ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "FAI2LDIF 1" .TH FAI2LDIF 1 "2017-01-12" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" fai2ldif \- read fai classes and create an ldif file to be imported into an ldap server .SH "SYNOPSIS" .IX Header "SYNOPSIS" fai2ldif [\-hv] [\-c config_space] [\-d dist] [\-r release] [\-o output filename] \-b base class .SH "OPTIONS" .IX Header "OPTIONS" \&\fB\-h\fR print out this help message .PP \&\fB\-v\fR be verbose (multiple v's will increase verbosity) .PP \&\fB\-c\fR config sapce (default: /var/lib/fai/config) .PP \&\fB\-d\fR Distribution name .PP \&\fB\-b\fR ldap base .PP \&\fB\-o\fR output filename .SH "DESCRIPTION" .IX Header "DESCRIPTION" fai2ldif is a script to read the fai classes files and create ldif files. .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2011\-2016 FusionDirectory project" 3 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fai-server/man/yumgroup2yumi.1000066400000000000000000000122441304135723100225670ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "YUMGROUP2YUMI 1" .TH YUMGROUP2YUMI 1 "2017-01-12" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" yumgroup2yumi \- read yumgroups and create ldif files for use .SH "SYNOPSIS" .IX Header "SYNOPSIS" yumgroup2yumi [\-hv] [\-c config_space] [\-o output filename] .SH "DESCRIPTION" .IX Header "DESCRIPTION" yumgroup2yumi is a script to read yumgroups and create ldif files in yumi format .SH "OPTIONS" .IX Header "OPTIONS" \&\fB\-h\fR print out this help message .PP \&\fB\-v\fR be verbose (multiple v's will increase verbosity) .PP \&\fB\-c\fR config sapce (default: /var/lib/fai/config) .PP \&\fB\-o\fR output filename .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2015\-2016 FusionDirectory project" 3 .IX Item "Copyright (C) 2015-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fai-server/nfsroot-hooks/000077500000000000000000000000001304135723100216655ustar00rootroot00000000000000argonaut-1.0/argonaut-fai-server/nfsroot-hooks/argonaut-nfsroot-integration000077500000000000000000000075361304135723100274570ustar00rootroot00000000000000#!/bin/sh ####################################################################### # # argonaut-nfsroot-integration # # Copyright (c) 2008 by Cajus Pollmeier # Copyright (C) 2011-2016 FusionDirectory project # # 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, see # ####################################################################### msg() { echo "nfsroot-hooks/argonaut-nfsroot-integration: $1" } msg "Integrating argonaut tools inside the FAI nfsroot" mkdir -p "${NFSROOT}"/etc/argonaut if [ -n "$FAI_ARGONAUT" ]; then msg "removing argonaut.conf installed by the package" rm -Rf ${NFSROOT}/etc/argonaut/argonaut.conf msg "installing $FAI_ARGONAUT into ${NFSROOT}" cp ${FAI_ARGONAUT} ${NFSROOT}${FAI_ARGONAUT} if [ -f "$NFSROOT"/etc/argonaut/argonaut.conf ]; then msg "Using the classical argonaut.conf nothing left to do" else msg "Using a customized argonaut.conf, copying over the classical one" mv -v "${NFSROOT}${FAI_ARGONAUT}" "${NFSROOT}"/etc/argonaut/argonaut.conf fi else msg "FAI_ARGONAUT not mentionned in /etc/fai/nfsroot.conf" msg "installation will not work please indicate the file to be copied into FAI_ARGONAUT" fi if [ -n "$FAI_CONF" ]; then cp ${FAI_CONF} ${NFSROOT}${FAI_CONF} else msg "FAI_CONF not mentionned in /etc/fai/nfsroot.conf" msg "installation will not work please indicate the file to be copied into FAI_CONF" fi if [ -n "$FAI_LDAP" ]; then msg "installing $FAI_LDAP into ${NFSROOT}" cp ${FAI_LDAP} ${NFSROOT}${FAI_LDAP} else msg "FAI_LDAP not mentionned in /etc/fai/nfsroot.conf" msg "installation will not work please indicate the file to be copied into FAI_LDAP" fi if [ -n "$FAI_SSL_CERT" ]; then msg "installing ssl certificate into nfs root" cp ${FAI_SSL_CERT} ${NFSROOT}/etc/ssl/certs else msg "FAI_SSL_CERT not mentionned in /etc/fai/nfsroot.conf" msg "installation will not be able to use ssl certificate with ldap, please indicate the certs to be copied into FAI_SSL_CERT" fi if [ -n "$FAI_SSL_CERT_KEY" ]; then msg "installing ssl certificate private key into nfs root" cp ${FAI_SSL_CERT_KEY} ${NFSROOT}/etc/ssl/private else msg "FAI_SSL_CERT_KEY not mentionned in /etc/fai/nfsroot.conf" msg "installation will not be able to use ssl certifcate with ldap, please indicate the certificate private key to be copied into FAI_SSL_CERT_KEY" fi if [ -n "$FAI_SSL_CERT_CA" ]; then msg "installing ssl ca certificate into nfs root" cp ${FAI_SSL_CERT_CA} ${NFSROOT}/etc/ssl/certs else msg "FAI_SSL_CERT_CA not mentionned in /etc/fai/nfsroot.conf" msg "installation will not be able to verify ssl certificate with ldap, please indicate the ca certificate to be copied into FAI_SSL_CERT_CA" fi # Get newest kernel installed inside the nfsroot version=$(echo $NFSROOT/boot/vmlinuz-* | sort -n | head -n1 | cut -d- -f2-) # Copy as default kernel if [ -f $TFTPROOT/vmlinuz-$version ]; then msg "installing vmlinuz-install" cp -f $TFTPROOT/vmlinuz-$version $TFTPROOT/vmlinuz-install else msg "ERROR: cannot find kernel to use as vmlinuz-install!!!" fi # Copy initrd if present if [ -f $TFTPROOT/initrd.img-$version ]; then msg "installing initrd.img-install" cp -f $TFTPROOT/initrd.img-$version $TFTPROOT/initrd.img-install else msg "ERROR: cannot find initrd image to use as initrd.img-xx !!!" fi argonaut-1.0/argonaut-freeradius/000077500000000000000000000000001304135723100171405ustar00rootroot00000000000000argonaut-1.0/argonaut-freeradius/bin/000077500000000000000000000000001304135723100177105ustar00rootroot00000000000000argonaut-1.0/argonaut-freeradius/bin/argonaut-freeradius-get-vlan000066400000000000000000000103611304135723100253160ustar00rootroot00000000000000#!/usr/bin/perl -w ####################################################################### # # argonaut-freeradius-get-vlan - script used to get the vlan from the # user radius group from ldap # # Copyright (C) 2011-2016 FusionDirectory project # # Based in the example code of rlm_perl # # Authors: Côme BERNIGAUD # Alejandro Escanero Blanco # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use Net::LDAP; use Net::LDAP::Util; use Net::LDAP::Message; use Net::LDAP::Search; use Argonaut::Libraries::Common qw(:ldap); # use ... # This is very important ! Without this script will not get the filled hashesh from main. use vars qw(%RAD_REQUEST %RAD_REPLY %RAD_CHECK); use Data::Dumper; # This is hash wich hold original request from radius #my %RAD_REQUEST; # In this hash you add values that will be returned to NAS. #my %RAD_REPLY; #This is for check items #my %RAD_CHECK; #LDAP vars my $configfile = "/etc/argonaut/argonaut.conf"; my $config = Config::IniFiles->new( -file => $configfile, -allowempty => 1, -nocase => 1); my $ldap_configfile = $config->val( ldap => "config" ,"/etc/ldap/ldap.conf"); my $ldap_dn = $config->val( ldap => "dn" ,""); my $ldap_password = $config->val( ldap => "password" ,""); # # This the remapping of return values # use constant RLM_MODULE_REJECT=> 0;# /* immediately reject the request */ use constant RLM_MODULE_FAIL=> 1;# /* module failed, don't reply */ use constant RLM_MODULE_OK=> 2;# /* the module is OK, continue */ use constant RLM_MODULE_HANDLED=> 3;# /* the module handled the request, so stop. */ use constant RLM_MODULE_INVALID=> 4;# /* the module considers the request invalid. */ use constant RLM_MODULE_USERLOCK=> 5;# /* reject the request (user is locked out) */ use constant RLM_MODULE_NOTFOUND=> 6;# /* user not found */ use constant RLM_MODULE_NOOP=> 7;# /* module succeeded without doing anything */ use constant RLM_MODULE_UPDATED=> 8;# /* OK (pairs modified) */ use constant RLM_MODULE_NUMCODES=> 9;# /* How many return codes there are */ # Function to handle authorize sub authorize { my $ldapinfos = argonaut_ldap_init ($ldap_configfile, 0, $ldap_dn, 0, $ldap_password); if ( $ldapinfos->{'ERROR'} > 0) { &radiusd::radlog(1, $ldapinfos->{'ERRORMSG'}); return RLM_MODULE_FAIL; } my ($ldap,$LDAPBASE) = ($ldapinfos->{'HANDLE'},$ldapinfos->{'BASE'}); my $sres = $ldap->search( base => $LDAPBASE, filter => "(&(objectClass=radiusProfile)(memberUid=$RAD_REQUEST{'User-Name'}))", scope => "sub", attrs => ['cn','radiusAuthType','radiusSessionTimeout','radiusIdleTimeout','radiusTunnelType','radiusTunnelMediumType','radiusTunnelPrivateGroupId'] ); if ($sres->count == 0) { return RLM_MODULE_NOOP; } my $entry = $sres->entry(0); if ($entry->exists('radiusAuthType')) { &radiusd::radlog(1, "radiusAuthType: ".$entry->get_value('radiusAuthType')); } if ($entry->exists('radiusTunnelPrivateGroupId')) { &radiusd::radlog(1, "radiusTunnelPrivateGroupId: ".$entry->get_value('radiusTunnelPrivateGroupId')); } if ($entry->exists('radiusTunnelPrivateGroupId')) { $RAD_REPLY{'Tunnel-Private-Group-Id'} = $entry->get_value('radiusTunnelPrivateGroupId'); } if ($entry->exists('cn')) { &radiusd::radlog(1, "cn: ".$entry->get_value('cn')); } $ldap->unbind; return RLM_MODULE_OK; } sub xlat { } sub detach { &radiusd::radlog(0,"rlm_perl::Detaching. Reloading. Done."); } argonaut-1.0/argonaut-fuse/000077500000000000000000000000001304135723100157515ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/Argonaut/000077500000000000000000000000001304135723100175315ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/Argonaut/Fuse/000077500000000000000000000000001304135723100204335ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/Argonaut/Fuse/Modules/000077500000000000000000000000001304135723100220435ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/Argonaut/Fuse/Modules/FAI.pm000066400000000000000000000136361304135723100230110ustar00rootroot00000000000000####################################################################### # # Argonaut::Fuse::FAI # # Copyright (c) 2005,2006,2007 by Jan-Marek Glogowski # Copyright (c) 2008 by Cajus Pollmeier # Copyright (c) 2008,2009, 2010 by Jan Wenzel # Copyright (C) 2011-2016 FusionDirectory project # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Fuse::Modules::FAI; use strict; use warnings; use 5.008; use Net::LDAP; use Net::LDAP::Util qw(:escape); use Log::Handler; use Argonaut::Libraries::Common qw(:ldap :file); use Exporter; our @ISA = ("Exporter"); use constant USEC => 1000000; my $log = Log::Handler->get_logger("argonaut-fuse"); sub get_module_info { # Add additional config sections return "Fully Automatic Installation"; } sub get_module_settings { return argonaut_get_generic_settings( 'argonautFuseFAIConfig', { 'fai_flags' => "argonautFuseFaiFlags", 'nfs_root' => "argonautFuseNfsRoot", }, $main::config,$main::config->{'client_ip'} ); } sub get_pxe_config { my $class = shift; my ($filename) = shift || return; my $settings = get_module_settings(); my $nfs_root = $settings->{'nfs_root'}; my $nfs_opts = ""; my $fai_flags = $settings->{'fai_flags'}; my $union = "aufs"; my $mac = argonaut_get_mac_pxe($filename); my $result = undef; # Search for the host to examine the FAI state my $infos = argonaut_get_generic_settings( 'FAIobject', { 'status' => 'FAIstate', 'hostname' => 'cn', }, $main::config,"(macAddress=$mac)" ); if ($infos->{'locked'}) { # Locked machine: go to 'localboot' $infos->{'status'} = 'localboot'; $log->info("$filename - is locked so localboot\n"); } elsif ($infos->{'status'} eq '') { # If we don't have a FAI state # Handle our default action if ($main::default_mode eq 'fallback') { # Remove PXE config and rely on 'default' fallback if (-f "$main::tftp_root/$filename") { if (0 == unlink( "$main::tftp_root/$filename" )) { $log->error("$filename - removing from '$main::tftp_root' failed: $!\n"); return; } } else { $log->info("$filename - no LDAP status - continue PXE boot\n"); } return 0; } else { # "Super"-Default is 'localboot' - just use the built in disc $infos->{'status'} = 'localboot'; $log->info("$filename - defaulting to localboot\n"); } } my $host_dn = $infos->{'dn'}; my $tftp_parent; if ($main::tftp_root =~ /^(.*?)\/pxelinux.cfg$/) { $tftp_parent = $1; } # Get kernel and initrd from TFTP root $infos->{'kernel'} = 'vmlinuz-install'; $infos->{'cmdline'} = ' initrd=initrd.img-install'; my $chboot_cmd; my $output; my $valid_status = 1; # Add NFS options and root, if available my $nfsroot_cmdline = (defined $nfs_root && ($nfs_root ne '')); $infos->{'cmdline'} .= " nfsroot=$nfs_root" if( $nfsroot_cmdline ); if (defined $nfs_opts && ($nfs_opts ne '')) { $infos->{'cmdline'} .= ' nfsroot=' if( ! $nfsroot_cmdline ); $infos->{'cmdline'} .= ",$nfs_opts"; } if ($infos->{'status'} =~ /^(install|install-init)$/) { $infos->{'kernel'} = 'kernel '.$infos->{'kernel'}; $infos->{'cmdline'} .= " ip=dhcp root=/dev/nfs boot=live union=$union" . " FAI_ACTION=${main::default_mode} FAI_FLAGS=${fai_flags}"; } elsif ($infos->{'status'} =~ /^(error:|installing:)/) { # If we had an error, show an error message # The only difference is to install is "faierror" on cmdline my $faierror = ($infos->{'status'} =~ /^installing:/) ? 'inst-' : ''; $faierror .= (split( ':', $infos->{'status'} ))[1]; $infos->{'kernel'} = 'kernel '.$infos->{'kernel'}; $infos->{'cmdline'} .= " ip=dhcp root=/dev/nfs boot=live union=$union" . " FAI_ACTION=${main::default_mode} FAI_FLAGS=${fai_flags} faierror:${faierror}"; } elsif ($infos->{'status'} eq 'softupdate') { # Softupdate has to be run by the client, so do a localboot $infos->{'kernel'} = 'localboot 0'; $infos->{'cmdline'} = ''; } elsif ($infos->{'status'} eq 'sysinfo') { # Remove reboot flag in sysinfo mode - doesn't make sense my @sysflags = split( ',', ${fai_flags} ); my $i = 0; while ($i < scalar(@sysflags)) { if ('reboot' eq $sysflags[ $i ]) { splice(@sysflags, $i, 1); next; } $i++; } my $noreboot = join( ',', @sysflags ); $infos->{'kernel'} = 'kernel '.$infos->{'kernel'}; $infos->{'cmdline'} .= " ip=dhcp root=/dev/nfs boot=live union=$union" . " FAI_ACTION=${main::default_mode} FAI_FLAGS=${noreboot} "; } elsif ($infos->{'status'} eq 'localboot') { $infos->{'kernel'} = 'localboot 0'; $infos->{'cmdline'} = ''; } else { $valid_status = 0; } if ($valid_status) { $log->info("$filename - PXE status: $infos->{'status'}\n"); my $code = &main::write_pxe_config_file( $infos->{'hostname'}, $filename, $infos->{'kernel'}, $infos->{'cmdline'} ); if ($code == 0) { return time; } } else { $log->error("$filename - unknown FAIstate: $infos->{'status'}\n"); } return $result; } 1; __END__ # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-fuse/Argonaut/Fuse/Modules/OPSI.pm000066400000000000000000000123721304135723100231600ustar00rootroot00000000000000####################################################################### # # Argonaut::Fuse::OPSI # # Copyright (c) 2005,2006,2007 by Jan-Marek Glogowski # Copyright (c) 2008 by Cajus Pollmeier # Copyright (c) 2008,2009, 2010 by Jan Wenzel # Copyright (C) 2011-2016 FusionDirectory project # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Fuse::Modules::OPSI; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :file :config); use Net::LDAP; use Net::LDAP::Util qw(:escape); use if (USE_LEGACY_JSON_RPC), 'JSON::RPC::Legacy::Client'; use if not (USE_LEGACY_JSON_RPC), 'JSON::RPC::Client'; use Log::Handler; use Exporter; our @ISA = ("Exporter"); my $sclient=""; my $log = Log::Handler->get_logger("argonaut-fuse"); sub get_module_info { return "Automatic Windows Installation"; } sub get_module_settings { return argonaut_get_generic_settings( 'argonautFuseOPSIConfig', { 'admin' => "argonautFuseOpsiAdmin", 'password' => "argonautFuseOpsiPassword", 'server' => "argonautFuseOpsiServer", 'lang' => "argonautFuseOpsiLang" }, $main::config,$main::config->{'client_ip'} ); } sub get_pxe_config { my $class = shift; my ($filename) = shift || return; my $settings = get_module_settings(); my $lang = $settings->{'lang'}; my $mac = argonaut_get_mac_pxe($filename); my $opsi_url = "https://".$settings->{'admin'}.":".$settings->{'password'}."\@".$settings->{'server'}.":4447/rpc"; my $opsi_client; if (USE_LEGACY_JSON_RPC) { $opsi_client = new JSON::RPC::Legacy::Client; } else { $opsi_client = new JSON::RPC::Client; } my $result = undef; # Load actions my $callobj = { method => 'getClientIdByMac', params => [$mac], id => 1, }; my $res = $opsi_client->call($opsi_url, $callobj); my $state= 0; my $status= "localboot"; my $kernel = "kernel opsi-install"; my $cmdline; my $product= ""; if ($res) { if ($res->is_error) { $log->error("ch $$: Error : $res->error_message\n"); } else { $sclient=$res->result; $callobj = { method => 'getNetBootProductStates_hash', params => [ $sclient ], id => 2, }; my $res2 = $opsi_client->call($opsi_url, $callobj); if ($res2) { if ($res2->is_error) { $log->error("ch $$: Error : ". $res2->error_message."\n"); } else { foreach my $element (@{$res2->result->{$sclient}}){ if( $element->{'actionRequest'} ne '' && $element->{'actionRequest'} ne 'undefined' && $element->{'actionRequest'} ne 'none' ) { $state= 1; $status= "install"; $product= "product=".$element->{'productId'}; last; } } } } else { $log->error("ch $$: Error : $opsi_client->status_line\n"); } if ($state) { # Installation requested my $service= ""; my $pckey= ""; # Load pc key $callobj = { method => 'getOpsiHostKey', params => [ $sclient ], id => 4, }; $res = $opsi_client->call($opsi_url, $callobj); if (defined $res->result) { $pckey= "pckey=".$res->result; $log->info("setting pckey for $sclient\n"); } else { $log->warning("no pc key for $sclient found\n"); } # Load depot server for this client $callobj = { method => 'getDepotId', params => [ $sclient ], id => 5, }; $res = $opsi_client->call($opsi_url, $callobj); if (defined $res->result){ $service= "service=".$res->result; $log->info("setting depot server for $sclient to $service\n"); } else { $log->info("no depot server for $sclient defined\n"); } $cmdline = "noapic lang=$lang ramdisk_size=175112 init=/etc/init initrd=opsi-root.gz reboot=b video=vesa:ywrap,mtrr $service $pckey vga=791 quiet splash $product"; } else { # Localboot $kernel = 'localboot 0'; $cmdline = ''; } } } else { $log->error("ch $$: Error : $opsi_client->status_line\n"); } $log->info("$filename - PXE status: $status\n"); my $code = &main::write_pxe_config_file( $sclient, $filename, $kernel, $cmdline ); if ($code == 0) { return time; } if ($code == -1) { $log->info("$filename - unknown state: $status\n"); } # Return our result return $result; } 1; __END__ # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-fuse/bin/000077500000000000000000000000001304135723100165215ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/bin/argonaut-fuse000066400000000000000000000224611304135723100212310ustar00rootroot00000000000000#!/usr/bin/perl -w ####################################################################### # # argonaut-fuse -- fuse-supplicant which allows one to create pxelinux # configurations for different types of clients using # external modules. # # Copyright (c) 2005,2006,2007 by Jan-Marek Glogowski # Copyright (c) 2008 by Cajus Pollmeier # Copyright (c) 2008,2009, 2010 by Jan Wenzel # Copyright (C) 2011-2016 FusionDirectory project # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use POSIX; use FindBin; use Socket; use Fuse; use Data::Dumper; use File::Pid; use Storable qw(freeze thaw); use Time::HiRes qw(gettimeofday usleep); use Net::LDAP; use Log::Handler; use Argonaut::Libraries::Common qw(:ldap :config :file); use constant USEC => 1000000; use App::Daemon qw(daemonize); # Predefined variables for config our ($default_mode, $tftp_root); our ($config, $ldap_base, $ldap_handle, $ldap_uris, $ldapinfos); our $filesystem; our $last_attred_file; our ($known_modules); my $configfile = "/etc/argonaut/argonaut.conf"; my $logfile = "argonaut-fuse.log"; my $piddir = "/var/run/argonaut"; my $pidfile = "argonaut-fuse.pid"; my $logdir; $SIG{TERM}=\&sig_int_handler; $SIG{INT}=\&sig_int_handler; readConfig(); argonaut_create_dir($logdir); my $log = Log::Handler->create_logger("argonaut-fuse"); $log->add( file => { filename => "$logdir/$logfile", maxlevel => "debug", minlevel => "emergency", newline => 1, } ); ($ldap_handle,$ldap_base,$ldapinfos) = argonaut_ldap_handle($config); $ldap_uris = $ldapinfos->{'URIS'}; $log->info("Argonaut-Fuse Started\n"); # Scan for modules use Module::Pluggable search_path => 'Argonaut::Fuse::Modules', sub_name => 'modules', require => 1; import_modules(); $App::Daemon::pidfile = "$piddir/$pidfile"; $App::Daemon::logfile = "$logdir/$logfile"; $App::Daemon::as_user = "root"; argonaut_create_dir($piddir); daemonize(); $filesystem = { 'default' => { 'content' => "# generated by argonaut-fuse for host default with no IP\n". "default argonaut-fuse generated\n\n". "label argonaut-fuse generated\n\n". "localboot 0\n" } }; $log->info("Argonaut-Fuse Mounting $tftp_root\n"); # Mount FUSE Filesystem eval { Fuse::main( mountpoint => $tftp_root, mountopts => "nonempty,allow_other", getattr => \&getattr, read => \&read, getdir => \&getdir, debug => 0, threaded => 0, ); }; if ($@) { $log->error("Fuse error: $@\n"); die $@; } exit 0; sub sig_int_handler { $pidfile->remove; exit(0); } #=== FUNCTION ================================================================ # NAME: readConfig # PARAMETERS: none # RETURNS: nothing # DESCRIPTION: read the config file and put everything needed into the # corresponding variables #=============================================================================== sub readConfig { $config = argonaut_read_config; my $settings = argonaut_get_fuse_settings($config,$config->{'client_ip'}); $default_mode = $settings->{"default_mode"}; # Used by modules $tftp_root = $settings->{"pxelinux_cfg"}; $logdir = $settings->{"logdir"}; } #=== FUNCTION ================================================================ # NAME: import_modules # PARAMETERS: none # RETURNS: nothing # DESCRIPTION: Import modules from Argonaut::Fuse namespace, # store their get_module_info result #=============================================================================== sub import_modules { foreach my $module (modules()) { eval { if(my $info = $module->get_module_info()) { $log->info("Loaded module $module ($info)\n"); $known_modules->{$module} = $info; } else { $log->warn("$module is not a module\n"); } 1; } or do { $log->warn("Exception when trying to call ->get_module_info on $module - $@"); } } } #=== FUNCTION ================================================================ # NAME: getattr # PARAMETERS: $filename - string - # RETURNS: Returns a list, very similar to the 'stat' function (see perlfunc). # On error, simply return a single numeric scalar value # (e.g. "return -ENOENT();"). # DESCRIPTION: get attributes #=============================================================================== sub getattr { my ($filename) = @_; # regular file my $type = oct(100); my $bits = oct(644); my $size = 0; # if directory, set type to dir and mode to 0755 if ($filename eq '/') { $type = oct(40); $bits = oct(755); } else { $filename =~ s|^.*/||; # Keep only filename if ($filename =~ /^([0-9a-f]{1,2}-){6}[0-9a-f]{1,2}$/i) { # Always generate a fresh config delete $filesystem->{$filename} if(exists($filesystem->{$filename})); # Process known Modules MODULE: foreach my $module (keys %{$known_modules}) { $log->info("Processing Module $module with argument ${filename}\n"); eval { my $answer = $module->get_pxe_config($filename); if (exists($filesystem->{$filename})) { last MODULE; } 1; } or do { $log->error("ERROR: Processing Module $module failed with $@\n"); delete $filesystem->{$filename} if(exists($filesystem->{$filename})); } } } if (not exists($filesystem->{$filename})) { return -ENOENT(); } $size = length( $filesystem->{$filename}->{'content'} ) if($filesystem->{$filename}->{'content'}); } my $mode = $type << 9 | $bits; my $nlink = 1; my $uid = $<; my ($gid) = split / /, $(; my $rdev = 0; my $atime = time; my $mtime = $atime; my $ctime = $atime; my $blksize = 1024; my $blocks = 1; my $dev = 0; my $ino = 0; return ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks); } sub read { my ($filename, $requestedsize, $offset) = @_; $filename =~ s|^.*/||; # Keep only filename if (defined $filesystem->{$filename}) { return $filesystem->{$filename}->{'content'}; } return -ENOENT(); } sub getdir { my ($filename) = @_; my @result = ('.', '..'); if ($filename eq '/') { push @result, keys(%{$filesystem}); } push @result, 0; return @result; } #=== FUNCTION ================================================================ # NAME: write_pxe_config_file # PARAMETERS: $host # $file # $kernel # $append # RETURNS: 0 # DESCRIPTION: create the pxelinux.cfg file for a host #=============================================================================== sub write_pxe_config_file { my ($host,$file,$kernel,$append) = @_; my $file_content = "# Generated by argonaut-fuse for host $host\n"; $file_content .= "default argonaut-fuse-generated\n\n"; $file_content .= "label argonaut-fuse-generated\n"; $file_content .= "$kernel\n"; if ($append) { $file_content .= "append $append\n"; } # store in hash $filesystem->{$file}->{'type'} = 'file'; $filesystem->{$file}->{'content'} = $file_content; return 0; } 1; __END__ =head1 NAME argonaut-fuse - FUSE/TFTP supplicant targeted to work with LDAP entries written by FusionDirectory =head1 SYNOPSIS argonaut-fuse =head1 DESCRIPTION B is a modular fuse-tftp-supplicant written in perl which allows one to create pxelinux configurations for different types of clients using external modules. =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project This code was Based on ctftpd =over 5 =item Copyright (c) 2005,2006,2007 by Jan-Marek Glogowski =item Copyright (c) 2008 by Cajus Pollmeier =item Copyright (c) 2008,2009 by Jan Wenzel =item Copyright (C) 2010 by Jan Wenzel =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-fuse/lib/000077500000000000000000000000001304135723100165175ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/lib/systemd/000077500000000000000000000000001304135723100202075ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/lib/systemd/system/000077500000000000000000000000001304135723100215335ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/lib/systemd/system/argonaut-fuse.service000066400000000000000000000005531304135723100257000ustar00rootroot00000000000000[Unit] Description=Start argonaut-fuse ConditionPathExists=/usr/sbin/argonaut-fuse [Service] Type=forking User=root ExecStart=/usr/sbin/argonaut-fuse ExecStop=/bin/sh -c "/bin/fusermount -u $(cat /var/log/argonaut/argonaut-fuse.log | grep Mounting | tail -n 1 | cut -d ' ' -f7)" PIDFile=/var/run/argonaut/argonaut-fuse.pid [Install] WantedBy=multi-user.target argonaut-1.0/argonaut-fuse/man/000077500000000000000000000000001304135723100165245ustar00rootroot00000000000000argonaut-1.0/argonaut-fuse/man/argonaut-fuse.1000066400000000000000000000131431304135723100213700ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-FUSE 1" .TH ARGONAUT-FUSE 1 "2017-01-12" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-fuse \- FUSE/TFTP supplicant targeted to work with LDAP entries written by FusionDirectory .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-fuse .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBargonaut-fuse\fR is a modular fuse-tftp-supplicant written in perl which allows one to create pxelinux configurations for different types of clients using external modules. .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .PP This code was Based on ctftpd .IP "Copyright (c) 2005,2006,2007 by Jan-Marek Glogowski " 5 .IX Item "Copyright (c) 2005,2006,2007 by Jan-Marek Glogowski " .PD 0 .IP "Copyright (c) 2008 by Cajus Pollmeier " 5 .IX Item "Copyright (c) 2008 by Cajus Pollmeier " .IP "Copyright (c) 2008,2009 by Jan Wenzel " 5 .IX Item "Copyright (c) 2008,2009 by Jan Wenzel " .IP "Copyright (C) 2010 by Jan Wenzel " 5 .IX Item "Copyright (C) 2010 by Jan Wenzel " .IP "Copyright (C) 2011\-2016 FusionDirectory project" 5 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PD .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fusiondirectory/000077500000000000000000000000001304135723100202375ustar00rootroot00000000000000argonaut-1.0/argonaut-fusiondirectory/bin/000077500000000000000000000000001304135723100210075ustar00rootroot00000000000000argonaut-1.0/argonaut-fusiondirectory/bin/argonaut-clean-audit000066400000000000000000000063631304135723100247460ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # Cleaning old audit entries # # Copyright (C) 2015-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config); my $verbose = 0; sub print_usage { print "Usage : argonaut-clean-audit [--verbose]\n"; exit(0); } foreach my $arg ( @ARGV ) { if (lc($arg) eq "--verbose") { $verbose = 1; } else { print_usage(); } } my $config = argonaut_read_config; my ($ldap,$ldap_base) = argonaut_ldap_handle($config); argonaut_read_ldap_config( $ldap, $ldap_base, $config, '(&(objectClass=fdAuditPluginConf)(fdAuditRotationDelay=*))', { 'delay' => "fdAuditRotationDelay" } ); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time() - $config->{'delay'} * 24 * 60 * 60); my $date = sprintf("%04d%02d%02d%02d%02d%02dZ", 1900 + $year, 1 + $mon, $mday, $hour, $min, $sec); my $mesg = $ldap->search( # list obsolete audit events base => $ldap_base, filter => "(&(objectClass=fdAuditEvent)(fdAuditDateTime<=$date))", attrs => [ 'dn' ] ); if ($mesg->code != 0) { die "LDAP error: " . $mesg->error . "(" . $mesg->code . ")\n"; } my $count = 0; foreach my $entry ($mesg->entries()) { $mesg = $ldap->delete($entry); if ($mesg->is_error()) { print "Error: " . $mesg->error . "(" . $mesg->code . ")\n"; } else { $count++; } } print "Deleted $count audit event entries\n"; __END__ =head1 NAME argonaut-clean-audit - delete old audit entries from the LDAP =head1 SYNOPSIS argonaut-clean-audit [--verbose] =head1 DESCRIPTION argonaut-clean-audit is a program used to delete old audit entries from the LDAP. It reads the delay before deletion from LDAP in fdAuditRotationDelay. =head1 OPTIONS =over 3 =item B<--verbose> be verbose =back =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 AUTHORS Come Bernigaud =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 1 =item Copyright (C) 2015-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-fusiondirectory/bin/argonaut-user-reminder000066400000000000000000000213321304135723100253320ustar00rootroot00000000000000#!/usr/bin/perl ######################################################################## # # argonaut-user-reminder # # Check for expired users and send them a mail allowing to postpone expiration # # This code is part of FusionDirectory (http://www.fusiondirectory.org/) # Copyright (C) 2016 FusionDirectory # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ######################################################################## use strict; use warnings; use 5.008; use Digest::SHA qw(sha256_base64); use Argonaut::Libraries::Common qw(:ldap :string :config); use Net::LDAP::Constant qw(LDAP_NO_SUCH_OBJECT); # first, create your message use Email::MIME; # send the message use Email::Sender::Simple qw(sendmail); my $config; $config = argonaut_read_config; $config->{'fd_rdn'} = 'ou=fusiondirectory'; my $verbose = 0; sub print_usage { print "Usage : argonaut-user-reminder [--verbose]\n"; exit(0); } foreach my $arg ( @ARGV ) { if (lc($arg) eq "--verbose") { $verbose = 1; } else { print_usage(); } } check_expired_users(); exit 0; ########################################################################################## # Die on all LDAP error except for «No such object» sub die_on_ldap_errors { my ($mesg) = @_; if (($mesg->code != 0) && ($mesg->code != LDAP_NO_SUCH_OBJECT)) { die $mesg->error; } } ############################################################# # Read FD config in the LDAP sub read_reminder_ldap_config { my ($ldap) = @_; # Default values $config->{'user_rdn'} = 'ou=people'; $config->{'token_rdn'} = 'ou=reminder'; # Days before expiration to send the first mail $config->{'alert_delay'} = 15; # Days after first mail to send a new one $config->{'resend_delay'} = 7; # Should alert mails be forwarded to the manager $config->{'forward_alert'} = 1; my $entry = argonaut_read_ldap_config( $ldap, $config->{'ldap_base'}, $config, '(&(objectClass=fusionDirectoryConf)(objectClass=fdUserReminderPluginConf))', { 'user_rdn' => "fdUserRDN", 'token_rdn' => "fdReminderTokenRDN", 'alert_delay' => "fdUserReminderAlertDelay", 'resend_delay' => "fdUserReminderResendDelay", 'alert_mailsubject' => "fdUserReminderAlertSubject", 'alert_mailbody' => "fdUserReminderAlertBody", 'alert_mailaddress' => "fdUserReminderEmail" } ); if ($entry->exists('fdUserReminderForwardAlert')) { $config->{'forward_alert'} = ($entry->get_value('fdUserReminderForwardAlert') eq "TRUE"); } } sub check_expired_users { my ($ldap,$ldap_base) = argonaut_ldap_handle($config); $config->{'ldap_base'} = $ldap_base; read_reminder_ldap_config($ldap); my $today = int(time() / 86400); # 24 * 60 * 60 my $next_expired_date = ($today + $config->{'alert_delay'}); my $filter = '(&(objectClass=person)(shadowExpire=*))'; my $mesg = $ldap->search( base => $config->{'ldap_base'}, filter => $filter, scope => 'subtree' ); die_on_ldap_errors($mesg); foreach my $entry ($mesg->entries()) { my $cn = $entry->get_value('cn'); if ($entry->get_value('shadowExpire') <= $today) { print "$cn is Expired\n"; } elsif ($entry->get_value('shadowExpire') <= $next_expired_date) { #~ Check if we have a mail address for this user. my $mail_address = $entry->get_value('mail'); if (not defined $mail_address) { print "User $cn has no mail address, skipping…\n"; next; } my ($token_hash, $token_date) = get_ldap_token($ldap, $entry->get_value('uid')); #~ Check if we already sent an email. if ((defined $token_date) && ($token_date + $config->{'resend_delay'} > $today)) { print "User $cn was already sent a mail, not resending yet.\n"; next; } my ($manager_cn, $manager_mail); if ($config->{'forward_alert'}) { #~ Find the manager my $manager_dn = $entry->get_value('manager'); if (not defined $manager_dn) { my $ou = $entry->dn; $ou =~ s/^[^,]+,$config->{'user_rdn'}//; my $manager_mesg = $ldap->search( base => $ou, filter => '(objectClass=*)', scope => 'base' ); if ($manager_mesg->count() > 0) { $manager_dn = ($manager_mesg->entries)[0]->get_value('manager'); } } if (not defined $manager_dn) { print "No manager found for $cn\n"; } my $manager_mesg = $ldap->search( base => $manager_dn, filter => '(objectClass=*)', scope => 'base' ); if ($manager_mesg->count() > 0) { $manager_cn = ($manager_mesg->entries)[0]->get_value('cn'); $manager_mail = ($manager_mesg->entries)[0]->get_value('mail'); } } send_alert_mail($ldap, $entry->get_value('uid'), $today, $cn, $mail_address, $manager_cn, $manager_mail); } } } sub send_alert_mail { my ($ldap, $uid, $date, $user_cn, $user_mail, $manager_cn, $manager_mail) = @_; my $token = store_ldap_token($ldap, $uid, $date); print "Sending mail to $user_cn<$user_mail>"; my $cc = ""; if (defined $manager_mail) { print ", copy to $manager_cn<$manager_mail>"; $cc = "$manager_cn<$manager_mail>"; } print " with token $token\n"; my $message = Email::MIME->create( header_str => [ From => $config->{'alert_mailaddress'}, To => "$user_cn<$user_mail>", Cc => $cc, Subject => $config->{'alert_mailsubject'}, ], attributes => { encoding => 'quoted-printable', charset => 'UTF-8', }, body_str => sprintf($config->{'alert_mailbody'},$user_cn,$uid,$token), ); sendmail($message); } sub get_ldap_token { my ($ldap, $uid) = @_; my $dn = "ou=$uid,".$config->{'token_rdn'}.','.$config->{'fd_rdn'}.','.$config->{'ldap_base'}; my $mesg = $ldap->search( base => $dn, filter => "(ou=$uid)", scope => 'base' ); if ($mesg->count()) { return (($mesg->entries)[0]->get_value('userPassword'), ($mesg->entries)[0]->get_value('description')); } else { return (); } } sub store_ldap_token { my ($ldap, $uid, $date) = @_; my $token_password = argonaut_gen_random_str(48); my $token_hash = sha256_base64('expired'.$token_password); while (length($token_hash) % 4) { $token_hash .= '='; } $token_hash = "{SHA}".$token_hash; my $dn = "ou=$uid,".$config->{'token_rdn'}.','.$config->{'fd_rdn'}.','.$config->{'ldap_base'}; if (!argonaut_ldap_branch_exists($ldap, $config->{'token_rdn'}.','.$config->{'fd_rdn'}.','.$config->{'ldap_base'})) { die "! Branch ".$config->{'token_rdn'}.','.$config->{'fd_rdn'}.','.$config->{'ldap_base'}." doesnt exist \n"; } my $mesg = $ldap->add( $dn, attr => [ 'ou' => $uid, 'objectClass' => 'organizationalUnit', 'userPassword' => $token_hash, 'description' => $date ] ); $mesg->code && die "! failed to add LDAP's $dn token: ".$mesg->error."\n"; return $token_password; } __END__ =head1 NAME argonaut-user-reminder - read account expiration date from ldap and send emails reminders =head1 SYNOPSIS argonaut-user-reminder [--verbose] =head1 DESCRIPTION argonaut-user-reminder is a program used to read account expiration dates from the LDAP. It reads the delay before expiration from the LDAP and send emails for user to postpone expiration date. =head1 OPTIONS =over 3 =item B<--verbose> be verbose =back =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 AUTHORS Come Bernigaud =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 1 =item Copyright (C) 2015-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-fusiondirectory/man/000077500000000000000000000000001304135723100210125ustar00rootroot00000000000000argonaut-1.0/argonaut-fusiondirectory/man/argonaut-clean-audit.1000066400000000000000000000122051304135723100251000ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-CLEAN-AUDIT 1" .TH ARGONAUT-CLEAN-AUDIT 1 "2016-12-07" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-clean\-audit \- delete old audit entries from the LDAP .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-clean-audit [\-\-verbose] .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-clean-audit is a program used to delete old audit entries from the \s-1LDAP.\s0 It reads the delay before deletion from \s-1LDAP\s0 in fdAuditRotationDelay. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-\-verbose\fR" 3 .IX Item "--verbose" be verbose .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "AUTHORS" .IX Header "AUTHORS" Come Bernigaud .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2015\-2016 FusionDirectory project" 1 .IX Item "Copyright (C) 2015-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fusiondirectory/man/argonaut-user-reminder.1000066400000000000000000000123211304135723100254720ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-USER-REMINDER 1" .TH ARGONAUT-USER-REMINDER 1 "2016-12-15" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-user\-reminder \- read account expiration date from ldap and send emails reminders .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-user-reminder [\-\-verbose] .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-user-reminder is a program used to read account expiration dates from the \s-1LDAP.\s0 It reads the delay before expiration from the \s-1LDAP\s0 and send emails for user to postpone expiration date. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-\-verbose\fR" 3 .IX Item "--verbose" be verbose .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "AUTHORS" .IX Header "AUTHORS" Come Bernigaud .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2015\-2016 FusionDirectory project" 1 .IX Item "Copyright (C) 2015-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-fusioninventory/000077500000000000000000000000001304135723100202705ustar00rootroot00000000000000argonaut-1.0/argonaut-fusioninventory/Agent/000077500000000000000000000000001304135723100213265ustar00rootroot00000000000000argonaut-1.0/argonaut-fusioninventory/Agent/Config/000077500000000000000000000000001304135723100225335ustar00rootroot00000000000000argonaut-1.0/argonaut-fusioninventory/Agent/Config/Ldap.pm000066400000000000000000000210221304135723100237460ustar00rootroot00000000000000####################################################################### # # FusionInventory::Agent::Config::Ldap - get fusioninventory config from ldap # # Copyright (C) 2013-2016 FusionDirectory project # # Authors: Côme BERNIGAUD # # This program is free software; # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package FusionInventory::Agent::Config::Ldap; use strict; use warnings; use base qw(FusionInventory::Agent::Config::Backend); use English qw(-no_match_vars); use Net::LDAP; sub new { my ($class, %params) = @_; my $file = $params{file} ? $params{file} : $params{directory} ? $params{directory} . '/agent.cfg' : 'agent.cfg'; if ($file) { die "non-existing file $file" unless -f $file; die "non-readable file $file" unless -r $file; } else { die "no configuration file"; } my $handle; if (!open $handle, q{<}, $file) { die "Config: Failed to open $file: $ERRNO"; } while (my $line = <$handle>) { $line =~ s/#.+//; if ($line =~ /([\w-]+)\s*=\s*(.+)/) { my $key = $1; my $value = $2; # remove the quotes $value =~ s/\s+$//; $value =~ s/^'(.*)'$/$1/; $value =~ s/^"(.*)"$/$1/; if ($key =~ m/^ldap_(.*)$/) { $params{$1} = $value; } } } close $handle; die "Missing parameter uri" unless $params{uri}; die "Missing parameter ip" unless $params{ip}; my $self = { uri => $params{uri}, ip => $params{ip} }; if ($params{base}) { $self->{base} = $params{base}; } else { $self->{uri} =~ m|^(ldap://[^/]+)/([^/]+)$| or die "Missing ldap base"; $self->{base} = $2; $self->{uri} = $1; } $self->{bind_dn} = $params{bind_dn} if $params{bind_dn}; $self->{bind_pwd} = $params{bind_pwd} if $params{bind_pwd}; bless $self, $class; return $self; } sub getValues { my ($self) = @_; my $ldap = Net::LDAP->new( $self->{uri} ); if ( ! defined $ldap ) { warn "LDAP 'new' error: '$@' with uri '".$self->{uri}."'"; return; } my $mesg; if( defined $self->{bind_dn} ) { if( defined $self->{bind_pwd} ) { $mesg = $ldap->bind( $self->{bind_dn}, password => $self->{bind_pwd} ); } else { $mesg = $ldap->bind( $self->{bind_dn} ); } } else { $mesg = $ldap->bind(); } if ( $mesg->code != 0 ) { warn "LDAP bind error: ".$mesg->error." (".$mesg->code.")"; return; } my %values; my %params = ( 'server' => 'fiAgentServer', 'local' => 'fiAgentLocal', 'delaytime' => 'fiAgentDelaytime', 'wait' => 'fiAgentWait', 'lazy' => 'fiAgentLazy', 'stdout' => 'fiAgentStdout', 'no-task' => 'fiAgentNoTask', 'scan-homedirs' => 'fiAgentScanHomedirs', 'html' => 'fiAgentHtml', 'backend-collect-timeout' => 'fiAgentBackendCollectTimeout', 'force' => 'fiAgentForce', 'tag' => 'fiAgentTag', 'additional-content' => 'fiAgentAdditionalContent', 'no-p2p' => 'fiAgentNoP2p', 'proxy' => 'fiAgentProxy', 'user' => 'fiAgentUser', 'password' => 'fiAgentPassword', 'ca-cert-dir' => 'fiAgentCaCertDir', 'ca-cert-file' => 'fiAgentCaCertFile', 'no-ssl-check' => 'fiAgentNoSslCheck', 'timeout' => 'fiAgentTimeout', 'no-httpd' => 'fiAgentNoHttpd', 'httpd-ip' => 'fiAgentHttpdIp', 'httpd-port' => 'fiAgentHttpdPort', 'httpd-trust' => 'fiAgentHttpdTrust', 'logger' => 'fiAgentLogger', 'logfile' => 'fiAgentLogfile', 'logfile-maxsize' => 'fiAgentLogfileMaxsize', 'logfacility' => 'fiAgentLogfacility', 'color' => 'fiAgentColor', 'daemon' => 'fiAgentDaemon', 'no-fork' => 'fiAgentNoFork', 'debug' => 'fiAgentDebug', ); my @booleans = ( 'no-httpd', 'no-fork', 'no-p2p', 'daemon' ); $mesg = $ldap->search( base => $self->{base}, filter => "(&(objectClass=fusionInventoryAgent)(ipHostNumber=".$self->{ip}."))", attrs => [values(%params)] ); if(scalar($mesg->entries)==1) { while (my ($key,$value) = each(%params)) { if (($mesg->entries)[0]->exists("$value")) { if (grep {$_ eq $key} @booleans) { $values{"$key"} = ($mesg->entries)[0]->get_value("$value") eq "TRUE" ? 1 : undef; } else { $values{"$key"} = ($mesg->entries)[0]->get_value("$value"); } } else { if (not (grep {$_ eq $key} @booleans)) { $values{"$key"} = ""; } } } return %values; } elsif(scalar($mesg->entries)==0) { $mesg = $ldap->search( # perform a search base => $self->{base}, filter => "ipHostNumber=".$self->{ip}, attrs => [ 'dn' ] ); if (scalar($mesg->entries)>1) { warn "Several computers are associated to IP ".$self->{ip}."."; return; } elsif (scalar($mesg->entries)<1) { warn "There is no computer associated to IP ".$self->{ip}."."; return; } my $dn = ($mesg->entries)[0]->dn(); my $mesg = $ldap->search( # perform a search base => $self->{base}, filter => "(&(objectClass=fusionInventoryAgent)(member=$dn))", attrs => [values(%params)] ); if(scalar($mesg->entries)==1) { while (my ($key,$value) = each(%params)) { if (($mesg->entries)[0]->get_value("$value")) { if (grep {$_ eq $key} @booleans) { $values{"$key"} = ($mesg->entries)[0]->get_value("$value") eq "TRUE" ? 1 : undef; } else { $values{"$key"} = ($mesg->entries)[0]->get_value("$value"); } } else { if (not (grep {$_ eq $key} @booleans)) { $values{"$key"} = ""; } } } return %values; } else { warn "This computer (".$self->{ip}.") is not configured in LDAP to run this module (missing service fusionInventoryAgent)."; return; } } else { warn "Several computers are associated to IP ".$self->{ip}."."; return; } } 1; __END__ =head1 NAME FusionInventory::Agent::Config::LDAP - LDAP-based configuration backend =head1 DESCRIPTION This is a FusionInventory LDAP configuration backend. =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 5 =item Copyright (C) 2013-2016 FusionDirectory project =back 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. =cut # vim:ts=2:sw=2:expandtab:shiftwidth=2:syntax:paste argonaut-1.0/argonaut-fusioninventory/bin/000077500000000000000000000000001304135723100210405ustar00rootroot00000000000000argonaut-1.0/argonaut-fusioninventory/bin/argonaut-generate-fusioninventory-schema000066400000000000000000000052271304135723100311160ustar00rootroot00000000000000#!/usr/bin/perl -w ####################################################################### # # argonaut-generate-fusioninventory-schema # # grab data from fusioniventory and create a proper ldap schema # # Copyright (C) 2013-2016 FusionDirectory project # # Authors: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; # This is taken from Agent/Inventory.pm use lib '/usr/share/fusioninventory/lib'; use FusionInventory::Agent::Inventory; my $inventory = FusionInventory::Agent::Inventory->new(); my %fields; while (my ($a, $b) = each(%{$inventory->{'fields'}})) { $fields{$a} = [keys(%$b)] } my %fields2; while (my ($a, $b) = each(%fields)) { foreach my $c (@$b) { $c =~ s/_//g; } @fields2{@$b} = (); } print "##\n## inventory-fd.schema - Needed by Fusion Directory for managing inventories\n##\n"; print "\n# Attributes\n"; print "attributetype ( 1.3.6.1.4.1.38414.39.1.1 NAME 'fdInventoryVERSIONCLIENT' DESC 'FusionDirectory - inventory, client version' EQUALITY caseExactMatch SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )\n\n"; my $i = 2; for my $f (keys(%fields2)) { print "attributetype ( 1.3.6.1.4.1.38414.39.1.$i NAME 'fdInventory$f' DESC 'FusionDirectory - inventory, $f' EQUALITY caseExactMatch SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )\n\n"; $i++; } print "\n# Object classes\n"; print "objectclass ( 1.3.6.1.4.1.38414.39.2.1 NAME 'fdInventoryContent' DESC 'FusionDirectory inventory information' MUST ( cn ) MAY ( macAddress \$ ipHostNumber \$ fdInventoryVERSIONCLIENT ) )\n\n"; $i = 2; while (my ($a, $b) = each(%fields)) { $a =~ s/_//g; print "objectclass ( 1.3.6.1.4.1.38414.39.2.$i NAME 'fdInventory$a' DESC 'FusionDirectory inventory information - $a' MUST ( cn ) MAY ( "; foreach my $c (@$b) { $c =~ s/_//g; $c = "fdInventory$c"; } print join(' $ ',@$b); print " ) )\n\n"; $i++; } argonaut-1.0/argonaut-ldap2zone/000077500000000000000000000000001304135723100167055ustar00rootroot00000000000000argonaut-1.0/argonaut-ldap2zone/Argonaut/000077500000000000000000000000001304135723100204655ustar00rootroot00000000000000argonaut-1.0/argonaut-ldap2zone/Argonaut/ClientDaemon/000077500000000000000000000000001304135723100230275ustar00rootroot00000000000000argonaut-1.0/argonaut-ldap2zone/Argonaut/ClientDaemon/Modules/000077500000000000000000000000001304135723100244375ustar00rootroot00000000000000argonaut-1.0/argonaut-ldap2zone/Argonaut/ClientDaemon/Modules/Ldap2Zone.pm000066400000000000000000000031111304135723100265670ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon::Modules::Ldap2Zone -- Ldap2Zone remote call # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::ClientDaemon::Modules::Ldap2Zone; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:config); use Argonaut::Libraries::Ldap2zone qw(argonaut_ldap2zone); my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item start start ldap2zone on the computer and store the result in the right place =cut sub start : Public { my ($s, $args) = @_; my ($zone) = @{$args}; $main::log->notice("ldap2zone called"); argonaut_ldap2zone($zone); return "ldap2zone done"; } 1; __END__ argonaut-1.0/argonaut-ldap2zone/Argonaut/Libraries/000077500000000000000000000000001304135723100224015ustar00rootroot00000000000000argonaut-1.0/argonaut-ldap2zone/Argonaut/Libraries/Ldap2zone.pm000066400000000000000000000324271304135723100246050ustar00rootroot00000000000000####################################################################### # # Argonaut::Libraries::Ldap2zone -- create zone files from LDAP DNS zones # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Libraries::Ldap2zone; use strict; use warnings; use 5.008; use Exporter 'import'; # gives you Exporter's import() method directly our @EXPORT_OK = qw(&argonaut_ldap2zone); # symbols to export on request use DNS::ZoneParse; use Argonaut::Libraries::Common qw(:ldap :config); my @record_types = ('a','aaaa','cname','mx','ns','ptr','txt','srv','hinfo','rp','loc'); my $NAMEDCHECKCONF = 'named-checkconf'; =item argonaut_ldap2zone Write a zone file for the LDAP zone and its reverse, generate named.conf files and assure they are included Params : zone name, verbose flag =cut sub argonaut_ldap2zone { my($zone,$verbose,$norefresh,$dumpdir,$noreverse,$ldap2view) = @_; my $config = argonaut_read_config; my $settings = argonaut_get_ldap2zone_settings($config,$config->{'client_ip'}); my $BIND_DIR = $settings->{'binddir'}; my $BIND_CACHE_DIR = $settings->{'bindcachedir'}; my ($output_BIND_DIR, $output_BIND_CACHE_DIR); if ($dumpdir) { $output_BIND_DIR = $dumpdir; $output_BIND_CACHE_DIR = $dumpdir; } else { $output_BIND_DIR = $BIND_DIR; $output_BIND_CACHE_DIR = $BIND_CACHE_DIR; } my $ALLOW_NOTIFY = $settings->{'allownotify'}; my $ALLOW_UPDATE = $settings->{'allowupdate'}; my $ALLOW_TRANSFER = $settings->{'allowtransfer'}; my $TTL = $settings->{'ttl'}; my $RNDC = $settings->{'rndc'}; if (not defined $noreverse) { $noreverse = ($settings->{'noreverse'} eq 'TRUE'); } if (not -d $output_BIND_DIR) { die "Bind directory '$output_BIND_DIR' does not exist\n"; } if (not -d $output_BIND_CACHE_DIR) { die "Bind cache directory '$output_BIND_CACHE_DIR' does not exist\n"; } if (!-e $RNDC) { die "Rndc path '$RNDC' doesn't seem to exists\n"; } my ($ldap,$ldap_base) = argonaut_ldap_handle($config); if ($ldap2view) { print "Searching DNS View '$zone'\n" if $verbose; my $acls = aclsparse($ldap,$ldap_base,$verbose); create_acl_namedconf($acls,$BIND_DIR,$BIND_CACHE_DIR,$output_BIND_DIR,$verbose); if ($ldap2view eq 'view') { my $view = viewparse($ldap,$ldap_base,$zone,$verbose); if (not defined($view)) { die "Could not find the view $zone\n"; } create_namedconf($zone,$BIND_DIR,$BIND_CACHE_DIR,$output_BIND_DIR,$ALLOW_NOTIFY,$ALLOW_UPDATE,$ALLOW_TRANSFER,$verbose, $view); } } else { if (substr($zone,-1) ne ".") { # If the end point is not there, add it $zone = $zone."."; } print "Searching DNS Zone '$zone'\n" if $verbose; my $dn = zoneparse($ldap,$ldap_base,$zone,$output_BIND_CACHE_DIR,$TTL,$verbose); create_namedconf($zone,$BIND_DIR,$BIND_CACHE_DIR,$output_BIND_DIR,$ALLOW_NOTIFY,$ALLOW_UPDATE,$ALLOW_TRANSFER,$verbose); unless ($noreverse) { my $reverse_zones = get_reverse_zones($ldap,$ldap_base,$dn); foreach my $reverse_zone (@$reverse_zones) { print "Parsing reverse zone '$reverse_zone'\n" if $verbose; zoneparse($ldap,$ldap_base,$reverse_zone,$output_BIND_CACHE_DIR,$TTL,$verbose); create_namedconf($reverse_zone,$BIND_DIR,$BIND_CACHE_DIR,$output_BIND_DIR,$ALLOW_NOTIFY,$ALLOW_UPDATE,$ALLOW_TRANSFER,$verbose); } } } refresh_main_namedconf($BIND_DIR,$output_BIND_DIR,$verbose); unless ($norefresh) { my $output = `$NAMEDCHECKCONF -z`; $? == 0 or die "$NAMEDCHECKCONF failed:\n$output\n"; system("$RNDC reconfig") == 0 or die "$RNDC reconfig failed : $?"; system("$RNDC freeze") == 0 or die "$RNDC freeze failed : $?"; system("$RNDC reload") == 0 or die "$RNDC reload failed : $?"; system("$RNDC thaw") == 0 or die "$RNDC thaw failed : $?"; } } =item zoneparse Create a Zone file for a zone taken from the LDAP Params : ldap handle, ldap base, zone name, bind dir, TTL, verbose flag Returns : dn of the zone =cut sub zoneparse { my ($ldap,$ldap_base,$zone,$output_BIND_CACHE_DIR,$TTL,$verbose) = @_; my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "zoneName=$zone", #~ attrs => [ 'ipHostNumber' ] ); $mesg->code && die "Error while searching DNS Zone '$zone' :".$mesg->error; print "Found ".scalar($mesg->entries())." results\n" if $verbose; my $zonefile = DNS::ZoneParse->new(\"", $zone); my $records = {}; foreach my $record (@record_types) { eval { #try $records->{$record} = $zonefile->$record(); }; if ($@) { # catch print "This DNS::ZoneParse version does not support '$record' record\n" if $verbose; }; } my $dn; # Dn of zone entry; my %unicityTest = (); foreach my $entry ($mesg->entries()) { my $name = $entry->get_value("relativeDomainName"); if(!$name) { print "no name\n"; next; } my $class = $entry->get_value("dnsClass"); if(!$class) { print "no class\n"; next; } my $ttl = $entry->get_value("dNSTTL"); if(!$ttl) { $ttl = "";#$default_ttl; } while(my ($type,$list) = each %{$records}){ foreach my $value ($entry->get_value($type."Record")) { if (defined $unicityTest{$type.$name.$value.$class.$ttl}) { # Avoid putting twice the same record next; } else { $unicityTest{$type.$name.$value.$class.$ttl} = 1; } if($type eq "txt") { push @{$list},{ name => $name, class => $class, text => $value, ttl => $ttl, ORIGIN => $zone }; } else { if($name ne "@") { push @{$list},{ name => $name, class => $class, host => $value, ttl => $ttl, ORIGIN => $zone }; } else { push @{$list},{ host => $value, ttl => $ttl, ORIGIN => $zone }; } } print "Added record $type $name $class $value $ttl\n" if $verbose; } } my $soa = $entry->get_value("soaRecord"); if($soa) { my $soa_record = $zonefile->soa(); my (@soa_fields) = split(' ',$soa); $soa_record->{'primary'} = $soa_fields[0]; $soa_record->{'email'} = $soa_fields[1]; $soa_record->{'serial'} = $soa_fields[2]; $soa_record->{'refresh'} = $soa_fields[3]; $soa_record->{'retry'} = $soa_fields[4]; $soa_record->{'expire'} = $soa_fields[5]; $soa_record->{'minimumTTL'} = $soa_fields[6]; $soa_record->{'class'} = $class; $soa_record->{'ttl'} = $TTL; $soa_record->{'origin'} = $name; $soa_record->{'ORIGIN'} = $zone; print "Added record SOA $name $class $soa $TTL\n" if $verbose; $dn = $entry->dn(); } } if (not defined $dn) { die "Zone $zone was not found in LDAP!\n"; } # write the new zone file to disk print "Writing DNS Zone '$zone' in $output_BIND_CACHE_DIR/db.$zone\n" if $verbose; my $file_output = "$output_BIND_CACHE_DIR/db.$zone"; my $newzone; open($newzone, q{>}, $file_output) or die "error while trying to open $file_output"; print $newzone $zonefile->output(); close $newzone; return $dn; } =item viewparse =cut sub viewparse { my ($ldap,$ldap_base,$view,$verbose) = @_; my $mesg = $ldap->search( base => $ldap_base, filter => "(&(objectClass=fdDNSView)(cn=$view))", ); $mesg->code && die "Error while searching DNS View '$view' :".$mesg->error."\n"; print "Found ".scalar($mesg->entries())." results\n" if $verbose; if (scalar($mesg->entries()) == 0) { return; } my %view = ( 'name' => ($mesg->entries)[0]->get_value('cn'), 'clientsacl' => (($mesg->entries)[0]->get_value('fdDNSViewMatchClientsAcl') or ''), 'destinationsacl' => (($mesg->entries)[0]->get_value('fdDNSViewMatchDestinationsAcl') or ''), 'recursiveonly' => (($mesg->entries)[0]->get_value('fdDNSViewMatchRecursiveOnly') or 'FALSE'), 'zones' => [], ); my $zonesDN = ($mesg->entries)[0]->get_value('fdDNSZoneDn', asref => 1); foreach my $zoneDN (@$zonesDN) { my $mesg = $ldap->search (base => $zoneDN, filter => '(objectClass=*)', scope => 'base'); $mesg->code && die "Error while loading zone $zoneDN for DNS View '$view' :".$mesg->error."\n"; if (scalar($mesg->entries()) == 0) { die "Could not find zone $zoneDN for DNS View '$view'\n"; } push @{$view{zones}}, ($mesg->entries)[0]->get_value('zoneName'); } return \%view; } =item aclsparse =cut sub aclsparse { my ($ldap,$ldap_base,$verbose) = @_; my $mesg = $ldap->search( base => $ldap_base, filter => "(objectClass=fdDNSAcl)", attrs => ['cn','fdDNSAclMatchList'] ); $mesg->code && die "Error while searching DNS acls:".$mesg->error."\n"; print "Found ".scalar($mesg->entries())." results\n" if $verbose; my @entries = $mesg->entries(); my @acls = (); foreach my $entry (@entries) { my @matchlist = $entry->get_value('fdDNSAclMatchList'); push @acls, { 'name' => $entry->get_value('cn'), 'matchlist' => join(';', @matchlist), } } return \@acls; } =item get_reverse_zones Params : ldap handle, ldap base, zone dn Returns : reverse zones names =cut sub get_reverse_zones { my($ldap,$ldap_base,$zone_dn) = @_; my $mesg = $ldap->search( # Searching reverse zone name base => $zone_dn, filter => "(&(zoneName=*arpa*)(relativeDomainName=@))", scope => 'one', attrs => [ 'zoneName' ] ); $mesg->code && die "Error while searching DNS reverse zone :".$mesg->error; my @reverse_zones = (); foreach my $entry ($mesg->entries()) { push @reverse_zones, $entry->get_value("zoneName"); } return \@reverse_zones; } =item create_namedconf Create file $output_BIND_DIR/named.conf.ldap2zone Params : zone name, reverse zone names Returns : =cut sub create_namedconf { my($zone,$BIND_DIR,$BIND_CACHE_DIR,$output_BIND_DIR,$ALLOW_NOTIFY,$ALLOW_UPDATE,$ALLOW_TRANSFER,$verbose,$view) = @_; if($ALLOW_NOTIFY eq "TRUE") { $ALLOW_NOTIFY = "notify yes;"; } else { $ALLOW_NOTIFY = ""; } if ($ALLOW_UPDATE ne "") { $ALLOW_UPDATE = "allow-update {$ALLOW_UPDATE};"; } else { $ALLOW_UPDATE = ""; } if ($ALLOW_TRANSFER ne "") { $ALLOW_TRANSFER = "allow-transfer {$ALLOW_TRANSFER};"; } else { $ALLOW_TRANSFER = ""; } print "Writing named.conf file in $output_BIND_DIR/named.conf.ldap2zone.$zone\n" if $verbose; my $namedfile; open($namedfile, q{>}, "$output_BIND_DIR/named.conf.ldap2zone.$zone") or die "error while trying to open $output_BIND_DIR/named.conf.ldap2zone.$zone"; my $zones; if (defined $view) { $zones = $view->{'zones'}; print $namedfile <{'name'}" { EOF if ($view->{'clientsacl'} ne '') { print $namedfile <{'clientsacl'}; }; EOF } if ($view->{'destinationsacl'} ne '') { print $namedfile <{'destinationsacl'}; }; EOF } my $recursiveonly = ($view->{'recursiveonly'} eq "TRUE" ? "yes" : "no"); print $namedfile <}, "$output_BIND_DIR/named.conf.acls") or die "error while trying to open $output_BIND_DIR/named.conf.acls"; foreach my $acl (@$acls) { print $namedfile <{'name'} {$acl->{'matchlist'}; }; EOF } } sub refresh_main_namedconf { my($BIND_DIR,$output_BIND_DIR,$verbose) = @_; print "Writing file $output_BIND_DIR/named.conf.ldap2zone\n" if $verbose; my $namedfile; open($namedfile, q{>}, "$output_BIND_DIR/named.conf.ldap2zone") or die "error while trying to open $output_BIND_DIR/named.conf.ldap2zone"; opendir DIR, $output_BIND_DIR or die "Error while openning $output_BIND_DIR!"; my @files = readdir DIR; foreach my $file (grep { /^named\.conf\.ldap2zone\./ } @files) { print $namedfile qq{include "$BIND_DIR/$file";\n}; } close $namedfile; } 1; __END__ argonaut-1.0/argonaut-ldap2zone/bin/000077500000000000000000000000001304135723100174555ustar00rootroot00000000000000argonaut-1.0/argonaut-ldap2zone/bin/argonaut-ldap2zone000066400000000000000000000101501304135723100231110ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # Ldap2Zone - write DNS Zone file from LDAP informations # # Copyright (C) 2011-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Argonaut::Libraries::Ldap2zone qw(argonaut_ldap2zone); my $zone_arg = undef; my $ldap2view = 0; my $verbose_arg = 0; my $norefresh = 0; my $noreverse; my $dumpdir = ''; sub print_usage { print "Usage : argonaut-ldap2zone [--verbose] [--noreverse] [--norestart] [--dumpdir dir] [--ldap2acls] [--ldap2view view] ZONE\n"; exit(0); } my $i_arg = 0; foreach my $arg ( @ARGV ) { if (not defined $dumpdir) { $dumpdir = $arg; } elsif (lc($arg) eq "--verbose") { $verbose_arg = 1; } elsif (lc($arg) eq "--noreverse") { $noreverse = 1; } elsif (lc($arg) eq "--norestart") { $norefresh = 1; } elsif (lc($arg) eq "--dumpdir") { undef $dumpdir; } elsif (lc($arg) eq "--ldap2acls") { $ldap2view = 'acls'; } elsif (lc($arg) eq "--ldap2view") { $ldap2view = 'view'; } elsif ($i_arg==0) { $zone_arg = $arg; $i_arg++; } else { print_usage(); } } if (not defined $dumpdir) { print_usage(); } elsif ($dumpdir eq '') { undef $dumpdir; } if (!$zone_arg) { if ($ldap2view eq 'acls') { $zone_arg = ''; } else { print_usage(); } } argonaut_ldap2zone($zone_arg,$verbose_arg,$norefresh,$dumpdir,$noreverse,$ldap2view); __END__ =head1 NAME argonaut-ldap2zone - creating bind zone files and refreshing the server =head1 SYNOPSIS argonaut-ldap2zone [--verbose] [--noreverse] [--norestart] [--dumpdir dir] [--ldap2acls] [--ldap2view view] ZONE =head1 DESCRIPTION argonaut-ldap2zone is a program used to create bind zone files and refresh the bind service =head1 OPTIONS =over 3 =item B<--verbose> be verbose =item B<--noreverse> Do not write reverse zone =item B<--norestart> Do not refresh bind zone with rndc =item B<--dumpdir> Dump the zones and bind configuration to another dir for testing purposes =item B<--ldap2acls> Write the the acls needed for bind in named.conf.acls =item B<--ldap2view> Write the zones in view format when you want to do split horizon dns =back =head1 EXAMPLE B Write the updated zones from ldap and refresh bind B Write the updated zones from ldap to another directory and don't refresh bind B Write the updated zones from ldap in a bind view named internal and refresh bind B Write the acls from ldap into named.conf.acls =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 AUTHORS Come Bernigaud =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 1 =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-ldap2zone/man/000077500000000000000000000000001304135723100174605ustar00rootroot00000000000000argonaut-1.0/argonaut-ldap2zone/man/argonaut-ldap2zone.1000066400000000000000000000144741304135723100232700ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-LDAP2ZONE 1" .TH ARGONAUT-LDAP2ZONE 1 "2016-03-18" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-ldap2zone \- creating bind zone files and refreshing the server .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut\-ldap2zone [\-\-verbose] [\-\-noreverse] [\-\-norestart] [\-\-dumpdir dir] [\-\-ldap2acls] [\-\-ldap2view view] \s-1ZONE\s0 .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut\-ldap2zone is a program used to create bind zone files and refresh the bind service .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-\-verbose\fR" 3 .IX Item "--verbose" be verbose .IP "\fB\-\-noreverse\fR" 3 .IX Item "--noreverse" Do not write reverse zone .IP "\fB\-\-norestart\fR" 3 .IX Item "--norestart" Do not refresh bind zone with rndc .IP "\fB\-\-dumpdir\fR" 3 .IX Item "--dumpdir" Dump the zones and bind configuration to another dir for testing purposes .IP "\fB\-\-ldap2acls\fR" 3 .IX Item "--ldap2acls" Write the the acls needed for bind in named.conf.acls .IP "\fB\-\-ldap2view\fR" 3 .IX Item "--ldap2view" Write the zones in view format when you want to do split horizon dns .SH "EXAMPLE" .IX Header "EXAMPLE" \&\fBargonaut\-ldap2zone \-\-verbose fusiondirectory.org.\fR .PP .Vb 1 \& Write the updated zones from ldap and refresh bind .Ve .PP \&\fBargonaut\-ldap2zone \-\-verbose \-\-norestart \-\-dumpdir dnszone/ fusiondirectory.org\fR .PP .Vb 1 \& Write the updated zones from ldap to another directory and don\*(Aqt refresh bind .Ve .PP \&\fBargonaut\-ldap2zone \-\-verbose \-\-ldap2view internal fusiondirectory.org\fR .PP .Vb 1 \& Write the updated zones from ldap in a bind view named internal and refresh bind .Ve .PP \&\fBargonaut\-ldap2zone \-\-verbose \-\-ldap2acls\fR .PP .Vb 1 \& Write the acls from ldap into named.conf.acls .Ve .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "AUTHORS" .IX Header "AUTHORS" Come Bernigaud .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2011\-2016 FusionDirectory project" 1 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-quota/000077500000000000000000000000001304135723100161405ustar00rootroot00000000000000argonaut-1.0/argonaut-quota/Argonaut/000077500000000000000000000000001304135723100177205ustar00rootroot00000000000000argonaut-1.0/argonaut-quota/Argonaut/Libraries/000077500000000000000000000000001304135723100216345ustar00rootroot00000000000000argonaut-1.0/argonaut-quota/Argonaut/Libraries/Quota.pm000066400000000000000000000142601304135723100232660ustar00rootroot00000000000000####################################################################### # # Argonaut::Libraries::Quota packages - get quota from ldap # # Copyright (c) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Libraries::Quota; use strict; use warnings; use 5.008; use Quota; use Argonaut::Libraries::Common qw(:ldap); BEGIN { use Exporter (); use vars qw(@EXPORT_OK @ISA $VERSION); $VERSION = '2012-04-24'; @ISA = qw(Exporter); @EXPORT_OK = qw(write_warnquota_file write_quotatab_file get_quota_settings apply_quotas); } =head1 Warnquota Write warnquota and quotatab files =cut sub write_warnquota_file { my ($settings,$warnquota_file) = @_; my $warnquota; open ($warnquota, q{>}, $warnquota_file) or die "Could not open file $warnquota_file"; # edition of warnquota.conf print $warnquota "MAIL_CMD = ".$settings->{'mail_cmd'}."\n"; print $warnquota "CC_TO = ".$settings->{'cc_to'}."\n"; print $warnquota "FROM = ".$settings->{'from'}."\n"; print $warnquota "SUBJECT = ".$settings->{'subject'}."\n"; # Support email for assistance (included in generated mail) print $warnquota "SUPPORT = ".$settings->{'support'}."\n"; # Support phone for assistance (included in generated mail) # The message to send print $warnquota "MESSAGE = ".$settings->{'message'}."\n"; # The signature of the mail print $warnquota "SIGNATURE = ".$settings->{'signature'}."\n"; # character set the email is to be send in print $warnquota "CHARSET = ".$settings->{'charset'}."\n"; # add LDAP support print $warnquota "LDAP_MAIL = true"."\n"; print $warnquota "LDAP_SEARCH_ATTRIBUTE = ".$settings->{'ldap_searchattribute'}."\n"; print $warnquota "LDAP_MAIL_ATTRIBUTE = mail\n"; print $warnquota "LDAP_BASEDN = ".$settings->{'ldap_basedn'}."\n"; print $warnquota "LDAP_URI = ".$settings->{'ldap_uri'}."\n"; print $warnquota "LDAP_USER_DN = ".$settings->{'ldap_userdn'}."\n"; print $warnquota "LDAP_PASSWORD = ".$settings->{'ldap_userpwd'}."\n"; # end of warnquota.conf close ($warnquota); } sub write_quotatab_file { my ($settings,$quotatab_file) = @_; my $quotatab; open ($quotatab, q{>}, $quotatab_file) or die "Could not open file $quotatab_file"; # Begin of quota tab edition my @quotaDeviceParameters = @{$settings->{'device_parameters'}}; if ($#quotaDeviceParameters >= 0) { foreach (@quotaDeviceParameters) { my @quotaDeviceParameter = split /:/, $_, -1; print $quotatab $quotaDeviceParameter[0].":".$quotaDeviceParameter[2]."\n"; } } close ($quotatab); # end of quota tab edition } sub get_quota_settings { my ($config,$filter,$inheritance) = @_; my $settings = argonaut_get_generic_settings( 'quotaService', { 'hostname' => 'cn', 'mail_cmd' => 'quotaMailCommand', 'cc_to' => 'quotaCarbonCopyMail', 'from' => 'quotaMsgFromSupport', 'subject' => 'quotaMsgSubjectSupport', 'support' => 'quotaMsgContactSupport', 'message' => 'quotaMsgContentSupport', 'signature' => 'quotaMsgSignatureSupport', 'charset' => 'quotaMsgCharsetSupport', 'ldap_searchattribute' => 'quotaLdapSearchIdAttribute', 'ldap_userdn' => 'quotaLdapServerUserDn', 'ldap_userpwd' => 'quotaLdapServerUserPassword', 'ldap_dn' => 'quotaLdapServer', 'device_parameters' => ['quotaDeviceParameters', asref => 1], }, $config,$filter,$inheritance ); my ($ldap,$ldap_base) = argonaut_ldap_handle($config); my $mesg = $ldap->search( # perform a search base => $settings->{'ldap_dn'}, scope => 'base', filter => "(objectClass=goLdapServer)", attrs => ['goLdapBase','goLdapURI'] ); if ($mesg->count <= 0) { die "Could not found LDAP server ".$settings->{'ldap_dn'}."\n"; } $settings->{'ldap_basedn'} = ($mesg->entries)[0]->get_value('goLdapBase'); $settings->{'ldap_uri'} = ($mesg->entries)[0]->get_value('goLdapURI'); return $settings; } sub apply_quotas { my ($config,$hostname) = @_; my ($ldap,$ldap_base) = argonaut_ldap_handle($config); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(objectClass=systemQuotas)", attrs => ['quota','uid','uidNumber','gidNumber'] ); foreach my $entry ($mesg->entries) { my $uid = $entry->get_value("uidNumber"); my $gid = $entry->get_value("gidNumber"); my $isUser = (defined $entry->get_value("uid")); my @quotas = $entry->get_value("quota"); foreach my $quota (@quotas) { my ($dev,$blocksoft,$blockhard,$inodesoft,$inodehard,$server,$adminlist) = split (':',$quota); if ($server eq $hostname) { if ($isUser) { print "applying quota ($blocksoft, $blockhard, $inodesoft, $inodehard) on $dev for uid $uid\n"; Quota::setqlim($dev, $uid, $blocksoft,$blockhard, $inodesoft,$inodehard); } else { print "applying quota ($blocksoft, $blockhard, $inodesoft, $inodehard) on $dev for gid $gid\n"; Quota::setqlim($dev, $gid, $blocksoft, $blockhard, $inodesoft, $inodehard, 0, 1); } } } } Quota::sync(); } END {} 1; __END__ argonaut-1.0/argonaut-quota/bin/000077500000000000000000000000001304135723100167105ustar00rootroot00000000000000argonaut-1.0/argonaut-quota/bin/argonaut-quota000066400000000000000000000045101304135723100216020ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # argonaut-quota # # Copyright (C) 2011-2016 FusionDirectory project # # Authors: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:config); use Argonaut::Libraries::Quota qw(get_quota_settings write_warnquota_file write_quotatab_file apply_quotas); my $warnquota_file = "/etc/warnquota.conf"; my $quotatab_file = "/etc/quotatab"; my $config = argonaut_read_config; my $settings = get_quota_settings($config,$config->{'client_ip'}); write_warnquota_file ($settings,$warnquota_file); write_quotatab_file ($settings,$quotatab_file); apply_quotas ($config,$settings->{'hostname'}); __END__ =head1 NAME argonaut-apply-quota - applying quota from data stored in the ldap server =head1 SYNOPSIS argonaut-quota =head1 DESCRIPTION argonaut-quota is a program used to apply quota on server based on the data stored in the ldap server =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 1 =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-quota/man/000077500000000000000000000000001304135723100167135ustar00rootroot00000000000000argonaut-1.0/argonaut-quota/man/argonaut-quota.1000066400000000000000000000116421304135723100217500ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-QUOTA 1" .TH ARGONAUT-QUOTA 1 "2016-01-06" "Argonaut 1.0" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-apply\-quota \- applying quota from data stored in the ldap server .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-quota .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-quota is a program used to apply quota on server based on the data stored in the ldap server .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2011\-2016 FusionDirectory project" 1 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details. argonaut-1.0/argonaut-samba/000077500000000000000000000000001304135723100160725ustar00rootroot00000000000000argonaut-1.0/argonaut-samba/Argonaut/000077500000000000000000000000001304135723100176525ustar00rootroot00000000000000argonaut-1.0/argonaut-samba/Argonaut/ClientDaemon/000077500000000000000000000000001304135723100222145ustar00rootroot00000000000000argonaut-1.0/argonaut-samba/Argonaut/ClientDaemon/Modules/000077500000000000000000000000001304135723100236245ustar00rootroot00000000000000argonaut-1.0/argonaut-samba/Argonaut/ClientDaemon/Modules/SambaShares.pm000066400000000000000000000147521304135723100263640ustar00rootroot00000000000000####################################################################### # # Argonaut::ClientDaemon::Modules::SambaShares -- Creating Samba-Share Definitions from FusionDirectory # # Author : Thomas Niercke # Version: 0.0.1 # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### # # This module will generate a file named /etc/samba/fustiondirectory.shares.conf # and writes the shares defined from within FusionDirectory's Share-Plugin to it. # The share's "Type:" has to be "samba" in order to be exported with this module. # # The samba (smbd) will also be reloaded automatically, but only when the # md5-checksum of the newly created file differs from the md5-checkdum of the old file. # # To not disturb any existing samba-configuration the share-definitions are written # to a seperate file which has to be includes in the [global]-Secion of your smb.conf # # However there are a few culpits in this very first version: # 1. unless an update of the shares-plugin, all additional samba-options # goes to the "Option"-Field, seperated by a backslash (\). # #---------------------------------------------------------------------- # Format of the "Option"-Field: # \ \ \ # where: # = reserved for future use. empty for now. # = a list of comma-seperates group as defined in fusion-directory # which members are granted read- and write-access. # = a list of comma-seperates group as defined in fusion-directory # which members are granted read-only access. # BEWARE: this is higher priority than # = if 1 then the share is created with the hidden flag (browseable = no) # otherwise the share can be seen by anyone, regardless access to it. ####################################################################### package Argonaut::ClientDaemon::Modules::SambaShares; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :config); use Digest::MD5 qw(md5_hex); use File::Slurp; my $base; BEGIN { $base = (USE_LEGACY_JSON_RPC ? "JSON::RPC::Legacy::Procedure" : "JSON::RPC::Procedure"); } use base $base; =item trim trims whitespaces from a given string =cut sub trim : Private { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } =item writeShareConfig writes the shareconfiguration to /etc/samba/shares.conf which then must be included in /etc/samba/smb.conf =cut sub writeShareConfig : Private { my ($SambaShares, $fname) = @_; my $serverName = $SambaShares->{'serverName'}; my $serverIP = $SambaShares->{'serverIP'}; my $serverDesc = $SambaShares->{'description'}; my $shares = $SambaShares->{'shares'}; $main::log->notice("SambaShares -> writing to file: $fname \n" ); my $fd; open($fd, q{>}, $fname) or die "error while trying to open $fname"; print $fd <<"END_SHARE"; ;=============================================================== ; This is a share-configuration file auto-generated ; using fusion-directory and argonaut-module 'SambaShares' ; see https://www.fusiondirectory.org for more information. ; ;=============================================================== END_SHARE foreach my $share (@{$shares}) { # remove the following 2 lines if the extension of the configuration # for the shares (ticket #5054) has been implenented. my ( $name, $desc, $fstype, $encoding, $path, $opt) = split(/\|/, $share); my ( $options, $write, $read, $hide) = split(/\\/, $opt); # uncomment the following line, if ticket #5054 has been implemented #my ( $name, $desc, $fstype, $encoding, $path, $options, $write, $read, $hide) = split(/\|/, $share); next if ( lc(trim($fstype)) ne "samba" ); my @wl = split(",", $write); my $validusers = "root"; my $writelist = "root"; if (length(trim($write)) > 0) { $writelist = $writelist . ", @" . join(", @", @wl); $validusers = $validusers . ", @" . join(", @", @wl); } my $readlist = ""; if (length(trim($read)) > 0) { $readlist = "read list = \@" . join(", @", split(",",$read)); $validusers = $validusers . ", \@" . join(", @", split(",",$read)); } my $browseable = "yes"; if ($hide eq "1") { $browseable = "no"; } my $forcegroup = $wl[0]; print $fd <<"END_SHARE"; ;--------------------------------------------------------------- ; Definition for Share '$name' ;--------------------------------------------------------------- [$name] comment = $desc path = $path browseable = $browseable $readlist write list = $writelist # force group = $forcegroup valid users = $validusers directory mask = 2770 force directory mode = 2770 directory security mask = 2770 force directory security mode = 2770 guest ok = no END_SHARE } # of for close($fd); } # start of main =item start execute SambaShares complex operation on the computer =cut sub start : Public { $main::log->notice("SambaShares -> Module has been started"); my ($server, $args) = @_; my $fname = "/etc/samba/fusiondirectory.shares.conf"; my $old = md5_hex(read_file($fname)); my $gotSamba = argonaut_get_generic_settings( 'goShareServer', { 'serverName' => 'cn', 'serverIP' => 'ipHostNumber', 'description' => 'description', 'shares' => ['goExportEntry', asref => 1] }, $main::config, $main::config->{'client_ip'} ); writeShareConfig( $gotSamba, $fname ); my $new = md5_hex(read_file($fname)); if ( $old eq $new ) { $main::log->notice("SambaShares -> nothing changed. finished here."); } else { $main::log->notice("SambaShares -> share-configuration changed. Reloading samba's smbd."); system("service smbd reload"); } } 1; __END__ argonaut-1.0/argonaut-server/000077500000000000000000000000001304135723100163155ustar00rootroot00000000000000argonaut-1.0/argonaut-server/Argonaut/000077500000000000000000000000001304135723100200755ustar00rootroot00000000000000argonaut-1.0/argonaut-server/Argonaut/Server/000077500000000000000000000000001304135723100213435ustar00rootroot00000000000000argonaut-1.0/argonaut-server/Argonaut/Server/Modules/000077500000000000000000000000001304135723100227535ustar00rootroot00000000000000argonaut-1.0/argonaut-server/Argonaut/Server/Modules/Argonaut.pm000066400000000000000000000103121304135723100250660ustar00rootroot00000000000000####################################################################### # # Argonaut::Server::Modules::Argonaut -- Argonaut client module # # Copyright (C) 2012-2016 FusionDirectory project # # Authors: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Server::Modules::Argonaut; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :file :config :string); my @unlocked_actions = ['System.halt', 'System.reboot']; sub new { my ($class) = @_; my $self = {}; bless( $self, $class ); return $self; } sub handle_client { my ($self, $mac,$action) = @_; if ($action =~ m/^Deployment.(reboot|wake)$/) { $action =~ s/^Deployment./System./; } elsif ($action =~ m/^Deployment./) { $main::log->debug("[Argonaut] Can't handle Deployment actions"); return 0; } if ($action eq 'System.wake') { $self->{mac} = $mac; $self->{action} = $action; return 1; } my $ip = main::getIpFromMac($mac); eval { #try my $settings = argonaut_get_client_settings($main::config,$ip); %$self = %$settings; $self->{action} = $action; }; if ($@) { #catch $main::log->debug("[Argonaut] Can't handle client : $@"); return 0; }; my $server_settings = argonaut_get_server_settings($main::config,$main::server_ip); $self->{cacertfile} = $server_settings->{cacertfile}; $self->{token} = $server_settings->{token}; return 1; } =pod =item do_action Execute a JSON-RPC method on a client which the ip is given. Parameters : ip,action,params =cut sub do_action { my ($self, $params) = @_; my $action = $self->{action}; if ($action eq 'System.wake') { main::wakeOnLan($self->{'mac'}); return 1; } if ($self->{'locked'} && (grep {$_ eq $action} @unlocked_actions)) { die 'This computer is locked'; } if ($action eq 'ping') { my $ok = 'OK'; my $res = $self->launch('echo',$ok); return ($res eq $ok); } else { return $self->launch($action,$params); } } =pod =item launch Execute a JSON-RPC method on a client which the ip is given. Parameters : ip,action,params =cut sub launch { # if ip pings, send the request my ($self, $action,$params) = @_; if ($action =~ m/^[^.]+\.[^.]+$/) { $action = 'Argonaut.ClientDaemon.Modules.'.$action; } my $ip = $self->{'ip'}; # this line is only needed when debugging stuff on localhost #$ip = "localhost"; $main::log->info("sending action $action to $ip"); my $client; if (USE_LEGACY_JSON_RPC) { $client = new JSON::RPC::Legacy::Client; } else { $client = new JSON::RPC::Client; } $client->version('1.0'); if ($self->{'protocol'} eq 'https') { if ($client->ua->can('ssl_opts')) { $client->ua->ssl_opts( verify_hostname => 1, SSL_ca_file => $self->{'cacertfile'}, SSL_verifycn_name => $self->{'certcn'} ); $client->ua->credentials($ip.":".$self->{'port'}, "JSONRPCRealm", "", argonaut_gen_ssha_token($self->{'token'})); } } my $callobj = { method => $action, params => [$params], }; my $res = $client->call($self->{'protocol'}."://".$ip.":".$self->{'port'}, $callobj); if($res) { if ($res->is_error) { $main::log->error("Error : ".$res->error_message); die "Error : ", $res->error_message."\n"; } else { $main::log->info("Result : ".$res->result); return $res->result; } } else { $main::log->info("Status : ".$client->status_line); die "Status : ".$client->status_line."\n"; } } 1; __END__ argonaut-1.0/argonaut-server/Argonaut/Server/Modules/FAI.pm000066400000000000000000000071141304135723100237130ustar00rootroot00000000000000####################################################################### # # Argonaut::Server::Modules::FAI -- FAI client module # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Server::Modules::FAI; use strict; use warnings; use 5.008; use Argonaut::Libraries::Common qw(:ldap :file :config); my @fai_actions = ("Deployment.reinstall", "Deployment.update", "Deployment.wake", "Deployment.reboot"); sub new { my ($class) = @_; my $self = {}; bless( $self, $class ); return $self; } sub handle_client { my ($self, $mac, $action) = @_; if (grep {$_ eq $action} @fai_actions) { my $ip = main::getIpFromMac($mac); eval { #try my $settings = argonaut_get_generic_settings( 'FAIobject', {'state' => "FAIstate"}, $main::config,$ip ); %$self = %$settings; $self->{action} = $action; }; if ($@) { #catch $main::log->debug("[FAI] Can't handle client : $@"); return 0; }; return 1; } else { $main::log->debug("[FAI] Can't handle action '$action'"); return 0; } } =pod =item do_action Execute a JSON-RPC method on a client which the ip is given. Parameters : ip,action,params =cut sub do_action { my ($self, $params) = @_; if ($self->{'locked'}) { die 'This computer is locked'; } my $substatus = $self->handler_fai($self->{taskid},$self->{action},$params); $self->{task} = { 'substatus' => $substatus, 'handler' => 1 }; return 0; } =pod item handler_fai Put the right boot mode in the ldap and send the right thing to the client. Parameters : the targetted mac address, the action received, the args received for it (args are currently unused). =cut sub handler_fai { my($self, $taskid,$action,$args) = @_; my $fai_state = { "Deployment.reinstall" => "install", "Deployment.update" => "softupdate", "Deployment.reboot" => "localboot", "Deployment.wake" => "localboot" }; my $need_reboot = ($action ne "Deployment.wake"); $self->flag($fai_state->{$action}); if($need_reboot) { $self->{launch_actions} = [["System.reboot", [$self->{'mac'}], {'args' => []}]]; return "rebooting"; } else { main::wakeOnLan($self->{'mac'}); return "wake on lan"; } } =item =cut sub flag { my ($self, $fai_state) = @_; my ($handle) = argonaut_ldap_handle($main::config); my $mesg = $handle->modify($self->{'dn'}, replace => {"FAIstate" => $fai_state}); $mesg->code && die "Error while setting FAIstate for object '".$self->{'dn'}."' :".$mesg->error; $mesg = $handle->unbind; # take down session } sub update_task { my ($self, $task) = @_; return $task; } sub task_processed { my ($self, $task) = @_; if ($task->{status} ne 'processing') { return $task; } $self->flag("localboot"); return $task; } 1; __END__ argonaut-1.0/argonaut-server/Argonaut/Server/Modules/OPSI.pm000066400000000000000000000455061304135723100240750ustar00rootroot00000000000000####################################################################### # # Argonaut::Server::Modules::OPSI -- OPSI client module # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Server::Modules::OPSI; use strict; use warnings; use Data::Dumper; use JSON; use 5.008; use Argonaut::Libraries::Common qw(:ldap :file :config); my $actions = { 'ping' => 'hostControl_reachable', 'System.halt' => 'hostControl_shutdown', 'System.reboot' => 'hostControl_reboot', 'Deployment.reboot' => 'hostControl_reboot', 'Deployment.reinstall' => \&reinstall, 'Deployment.update' => \&update, 'OPSI.update_or_insert' => \&update_or_insert, 'OPSI.delete' => 'host_delete', 'OPSI.host_getObjects' => 'host_getObjects', 'OPSI.get_netboots' => 'product_getObjects', 'OPSI.get_localboots' => 'product_getObjects', 'OPSI.get_product_properties' => 'productProperty_getObjects', }; my @locked_actions = ( 'ping', 'OPSI.update_or_insert', 'OPSI.delete', 'OPSI.host_getObjects', 'OPSI.get_netboots', 'OPSI.get_localboots', ); my $settings; sub new { my ($class) = @_; my $self = {}; bless( $self, $class ); return $self; } sub needs_host_param { my ($self, $action, $params) = @_; #Right now update_or_insert and host_getObjects are the only actions # that does not require the host as first parameter return 0 if ($action eq 'productProperty_getObjects'); return 0 if ($action eq 'host_getObjects'); return 0 if ($action eq 'product_getObjects'); return 0 if (($action eq 'host_delete') && (@$params > 0)); return 1; } sub get_opsi_settings { my $settings; eval { #try $settings = argonaut_get_generic_settings( 'opsiClient', { 'server-dn' => "fdOpsiServerDn", 'profile-dn' => "fdOpsiProfileDn", }, @_ ); }; if ($@) { #catch my $error = $@; eval { $settings = argonaut_get_generic_settings( 'opsiServer', { 'server-uri' => "fdOpsiServerURI", 'server-usr' => "fdOpsiServerUser", 'server-pwd' => "fdOpsiServerPassword", }, @_ ); }; if ($@) { die $error; }; }; my ($ldap, $ldap_base) = argonaut_ldap_handle($main::config); if (not defined $settings->{'server-uri'}) { my $mesg = $ldap->search( # perform a search base => $settings->{'server-dn'}, scope => 'base', filter => "(objectClass=opsiServer)", attrs => ['fdOpsiServerURI', 'fdOpsiServerUser', 'fdOpsiServerPassword'] ); if ($mesg->count <= 0) { die "[OPSI] Client with OPSI activated but server ".$settings->{'server-dn'}." not found"; } $settings->{'server-uri'} = ($mesg->entries)[0]->get_value("fdOpsiServerURI"); $settings->{'server-usr'} = ($mesg->entries)[0]->get_value("fdOpsiServerUser"); $settings->{'server-pwd'} = ($mesg->entries)[0]->get_value("fdOpsiServerPassword"); } my $host_settings = get_winstation_fqdn_settings(@_); @$settings{keys %$host_settings} = @$host_settings{keys %$host_settings}; return $settings; } sub get_winstation_fqdn_settings { my $settings = argonaut_get_generic_settings( '*', { 'cn' => 'cn', 'description' => 'description', }, @_, 0 ); my $cn = $settings->{'cn'}; $cn =~ s/\$$//; my ($ldap, $ldap_base) = argonaut_ldap_handle($main::config); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(relativeDomainName=$cn)(aRecord=".$settings->{'ip'}.")(zoneName=*))", attrs => ['zoneName'] ); if ($mesg->count <= 0) { die "[OPSI] Could not find any DNS domain name for $cn"; } my $zoneName = ($mesg->entries)[0]->get_value("zoneName"); $zoneName =~ s/\.$//; $settings->{'fqdn'} = $cn.'.'.$zoneName; return $settings; } sub handle_client { my ($self, $mac,$action) = @_; if (not defined $actions->{$action}) { return 0; } my $ip = main::getIpFromMac($mac); eval { #try my $settings = get_opsi_settings($main::config,$ip); %$self = %$settings; $self->{action} = $action; }; if ($@) { #catch if ($@ =~ /^[OPSI]/) { $main::log->notice($@); } else { $main::log->debug("[OPSI] Can't handle client : $@"); } return 0; }; return 1; } =item update_task Update a task status. Takes the task infos as parameter, return the new tasks infos. =cut sub update_task { my ($self, $task) = @_; if ($task->{status} ne 'processing') { return $task; } if ($task->{action} eq 'Deployment.reinstall') { my $attrs = [ 'actionResult', 'actionRequest', 'actionProgress', 'installationStatus', ]; $task->{progress} = 0; $task->{substatus} = ""; if (defined $self->{'netboot'}) { my $filter = { "productId" => $self->{'netboot'}, "clientId" => $self->{'fqdn'}, "productType" => "NetbootProduct", }; my $results = $self->launch('productOnClient_getObjects',[$attrs, $filter]); my $res = shift @$results; if ($res->{'actionRequest'} eq 'setup') { $task->{substatus} = $res->{'actionProgress'}; $task->{progress} = 10; return; } elsif ($res->{'installationStatus'} eq 'installed') { $task->{substatus} = 'netboot installed'; $task->{progress} = 50; } elsif ($res->{'actionResult'} eq 'failed') { $task->{status} = "error"; $task->{error} = $res->{'actionProgress'}; } } my $nblocals = 0; my $nbinstalled = 0; my $status = ""; if (defined $self->{'localboots'}) { foreach my $localboot (@{$self->{'localboots'}}) { my ($product, $action) = split('\|',$localboot); $nblocals++; my $filter = { "productId" => $product, "clientId" => $self->{'fqdn'}, "productType" => "LocalbootProduct", }; my $results = $self->launch('productOnClient_getObjects',[$attrs, $filter]); my $res = shift @$results; if ($res->{'actionRequest'} eq $action) { if ($res->{'actionProgress'} ne "") { $status = $product.": ".$res->{'actionProgress'}; } } elsif ($res->{'installationStatus'} eq 'installed') { $nbinstalled++; } elsif ($res->{'actionResult'} eq 'failed') { $task->{status} = "error"; $task->{error} = $res->{'actionProgress'}; } } } if ($nblocals eq 0) { $task->{progress} = 100; } else { $task->{progress} += (100 - $task->{progress})*$nbinstalled/$nblocals; if ($status ne "") { $task->{substatus} = $status; } } } return $task; } sub task_processed { my ($self, $task) = @_; return $task; } sub update_or_insert { my ($self, $action,$params) = @_; my $res; my $infos = { "id" => $self->{'fqdn'}, "description" => $self->{'description'}, "hardwareAddress" => $self->{'mac'}, "ipAddress" => $self->{'ip'}, "type" => "OpsiClient", }; my $opsiaction = 'host_updateObject'; my $tmpres = $self->launch('host_getObjects',[['id'],{'id' => $self->{'fqdn'}}]); if (scalar(@$tmpres) < 1) { $opsiaction = 'host_insertObject'; $infos->{"notes"} = "Created by FusionDirectory"; } $res = $self->launch($opsiaction,[$infos]); if (defined $self->{'depot'}) { $res = $self->launch('configState_create',["clientconfig.depot.id", $self->{'fqdn'}, $self->{'depot'}]); } return $res; } sub reinstall_or_update { my ($self, $reinstall,$action,$params) = @_; my $res; #1 - fetch the host profile my ($ldap, $ldap_base) = argonaut_ldap_handle($main::config); my $mesg = $ldap->search( # perform a search base => $self->{'profile-dn'}, scope => 'base', filter => "(objectClass=opsiProfile)", attrs => ['fdOpsiNetbootProduct', 'fdOpsiSoftwareList', 'fdOpsiProductProperty'] ); if ($mesg->count <= 0) { die "[OPSI] Client with OPSI activated but profile '".$settings->{'profile-dn'}."' could not be found"; } $self->{'netboot'} = ($mesg->entries)[0]->get_value("fdOpsiNetbootProduct"); $self->{'softlists'} = ($mesg->entries)[0]->get_value("fdOpsiSoftwareList", asref => 1); $self->{'localboots'} = []; $self->{'properties'} = ($mesg->entries)[0]->get_value("fdOpsiProductProperty", asref => 1); #2 - remove existing setups and properties my $productOnClients = $self->launch('productOnClient_getObjects', [[], { "clientId" => $self->{'fqdn'}, "type" => "ProductOnClient", }] ); my $productStates = {}; foreach my $product (@$productOnClients) { $productStates->{$product->{'productId'}} = $product->{'installationStatus'}; $product->{"actionRequest"} = 'none'; } $res = $self->launch('productOnClient_updateObjects', [$productOnClients]); $productOnClients = $self->launch('productPropertyState_getObjects', [[], { "objectId" => $self->{'fqdn'}, "type" => "ProductPropertyState", }] ); $res = $self->launch('productPropertyState_deleteObjects', [$productOnClients]); #3 - set netboot as the profile specifies if (!$reinstall && defined $self->{'netboot'}) { # Check if netboot is correctly installed my $attrs = [ 'actionResult', 'actionRequest', 'actionProgress', 'installationStatus', ]; my $filter = { "productId" => $self->{'netboot'}, "clientId" => $self->{'fqdn'}, "productType" => "NetbootProduct", }; my $results = $self->launch('productOnClient_getObjects',[$attrs, $filter]); my $res = shift @$results; if ($res->{'installationStatus'} ne 'installed') { $reinstall = 1; } } if ($reinstall && defined $self->{'netboot'}) { my $infos = { "productId" => $self->{'netboot'}, "clientId" => $self->{'fqdn'}, "actionRequest" => "setup", "type" => "ProductOnClient", "productType" => "NetbootProduct", }; $res = $self->launch('productOnClient_updateObject',[$infos]); } else { #3 bis - set to uninstall product that are not in the profile $productOnClients = $self->launch('productOnClient_getObjects', [[], { "clientId" => $self->{'fqdn'}, "type" => "ProductOnClient", "installationStatus" => "installed", }] ); foreach my $product (@$productOnClients) { if (($product->{"productId"} ne 'opsi-client-agent') && ($product->{"productId"} ne 'opsi-winst')) { $product->{"actionRequest"} = "uninstall"; $main::log->debug("[OPSI] uninstall ".$product->{"productId"}); } } $res = $self->launch('productOnClient_updateObjects', [$productOnClients]); } #4 - set localboot as the profile specifies (maybe remove the old ones that are not in the profile - see 3 bis) if (defined $self->{'softlists'}) { my $infos = []; foreach my $softlistdn (@{$self->{'softlists'}}) { my $mesg = $ldap->search( # perform a search base => $softlistdn, scope => 'base', filter => "(|(objectClass=opsiSoftwareList)(objectClass=opsiOnDemandList))", attrs => ['objectClass', 'fdOpsiLocalbootProduct', 'cn', 'fdOpsiOnDemandShowDetails'] ); my $ocs = ($mesg->entries)[0]->get_value("objectClass", asref => 1); my $localboots = ($mesg->entries)[0]->get_value("fdOpsiLocalbootProduct", asref => 1); if (not defined $localboots) { next; } if (grep {$_ eq 'opsiSoftwareList'} @$ocs) { foreach my $localboot (@{$localboots}) { my ($product, $action) = split('\|',$localboot); push @{$self->{'localboots'}}, $localboot; if ($reinstall || ($action ne 'setup') || (! defined $productStates->{$product}) || ($productStates->{$product} ne 'installed')) { push @$infos, { "productId" => $product, "clientId" => $self->{'fqdn'}, "actionRequest" => $action, "type" => "ProductOnClient", "productType" => "LocalbootProduct" }; } else { push @$infos, { "productId" => $product, "clientId" => $self->{'fqdn'}, "actionRequest" => "none", "type" => "ProductOnClient", "productType" => "LocalbootProduct" }; } } } else { # Handle OnDemandList my $groupid = 'fd_ondemand_'.($mesg->entries)[0]->get_value('cn'); my $showdetails = (($mesg->entries)[0]->get_value('fdOpsiOnDemandShowDetails') eq "TRUE"); $self->launch('group_delete',[$groupid]); $self->launch('group_createProductGroup',[$groupid]); foreach my $localboot (@{$localboots}) { $self->launch('objectToGroup_create',['ProductGroup', $groupid, $localboot]); } $self->launch('configState_create',['software-on-demand.active', $self->{'fqdn'}, JSON::true]); $self->launch('configState_create',['software-on-demand.product-group-ids', $self->{'fqdn'}, [$groupid]]); $self->launch('configState_create',['software-on-demand.show-details', $self->{'fqdn'}, ($showdetails?JSON::true:JSON::false)]); } } $res = $self->launch('productOnClient_updateObjects',[$infos]); } #5 - set properties as the profile specifies if (defined $self->{'properties'}) { my $infos = []; foreach my $property (@{$self->{'properties'}}) { my ($product, $propid, $values) = split('\|',$property); push @$infos, { "productId" => $product, "propertyId" => $propid, "objectId" => $self->{'fqdn'}, "values" => decode_json($values), "type" => "ProductPropertyState", }; } $res = $self->launch('productPropertyState_updateObjects',[$infos]); } #6 - reboot the host or fire the event if (defined $self->{'netboot'}) { $res = $self->launch('hostControl_reboot',[$self->{'fqdn'}]); } else { $res = $self->launch('hostControl_fireEvent',['on_demand', $self->{'fqdn'}]); } return $res; } sub reinstall { my ($self, $action,$params) = @_; return $self->reinstall_or_update(1, $action, $params); } sub update { my ($self, $action,$params) = @_; return $self->reinstall_or_update(0, $action, $params); } =pod =item do_action Execute a JSON-RPC method on a client which the ip is given. Parameters :$target,$taskid,$params =cut sub do_action { my ($self, $params) = @_; my $action = $self->{action}; my $taskid = $self->{taskid}; if ($self->{'locked'} && not (grep {$_ eq $action} @locked_actions)) { die 'This computer is locked'; } $self->{task}->{handler} = 1; my $res; if ($action eq 'OPSI.get_netboots') { if (scalar @$params < 1) { $params->[0] = []; } if (scalar @$params < 2) { $params->[1] = {'type' => 'NetbootProduct'}; } } elsif ($action eq 'OPSI.get_localboots') { if (scalar @$params < 1) { $params->[0] = []; } if (scalar @$params < 2) { $params->[1] = {'type' => 'LocalbootProduct'}; } } elsif (($action eq 'OPSI.delete') && (scalar @$params > 0)) { my @fqdns = (); foreach my $host (@{$params->[0]}) { if (lc($host) =~ m/([0-9a-f]{2}:){5}[0-9a-f]{2}/) { # If host is a macAddress my $ip = main::getIpFromMac($host); my $host_settings = get_winstation_fqdn_settings($main::config,$ip); push @fqdns, $host_settings->{'fqdn'}; } else { push @fqdns, $host; } } $params->[0] = \@fqdns; } if (ref $actions->{$action} eq ref "") { if ($action eq 'ping') { $params = ['1000']; } my $hostParam = $self->needs_host_param($actions->{$action}, $params); if ($hostParam) { unshift @$params, $self->{'fqdn'}; } $main::log->info("[OPSI] sending action ".$actions->{$action}." to ".$self->{'fqdn'}); $res = $self->launch($actions->{$action},$params); if ($hostParam) { if ((ref $res eq ref {}) && defined $res->{$self->{'fqdn'}}) { my $result = $res->{$self->{'fqdn'}}; if (JSON::XS::is_bool($result)) { $res = $result; } elsif (defined $result->{'error'}) { $main::log->error("[OPSI] Error : ".$result->{'error'}); die "Error while sending '".$actions->{$action}."' to '".$self->{'fqdn'}."' : ", $result->{'error'}."\n"; } elsif (defined $result->{'result'}) { $res = $result->{'result'}; } else { undef $res; } } } } else { my $sub = $actions->{$action}; $res = $self->$sub($action, $params); } if (not defined $res) { $main::log->info("[OPSI] Result is empty (no errors though)"); return 1; } $main::log->info("[OPSI] Result : ".$res); return $res; } =pod =item launch Execute a JSON-RPC method on a client which the ip is given. Parameters : ip,action,params =cut sub launch { my ($self, $action,$params) = @_; if (not defined $params) { $params = []; } my $client; if (USE_LEGACY_JSON_RPC) { $client = new JSON::RPC::Legacy::Client; } else { $client = new JSON::RPC::Client; } $client->version('1.0'); my $host = $self->{'server-uri'}; $host =~ s|^http(s?)://||; $host =~ s|/.*$||; $client->ua->credentials($host, "OPSI Service", $self->{'server-usr'}, $self->{'server-pwd'}); $client->ua->ssl_opts(verify_hostname => 0); # Do not check certificate hostname match my $callobj = { method => $action, params => [@$params], }; $main::log->debug("[OPSI] Call : ".Dumper($callobj)); my $res = $client->call($self->{'server-uri'}, $callobj); if($res) { $main::log->debug("[OPSI] Answer : ".Dumper($res)); if ($res->is_error) { $main::log->error("[OPSI] Error : ".$res->error_message->{'message'}); die "Error : ", $res->error_message->{'message'}."\n"; } else { return $res->result; } } else { $main::log->info("[OPSI] Status : ".$client->status_line); die "Status : ".$client->status_line."\n"; } } 1; __END__ argonaut-1.0/argonaut-server/Argonaut/Server/ModulesPool.pm000066400000000000000000000104471304135723100241510ustar00rootroot00000000000000####################################################################### # # Argonaut::Server::ModulesPool -- Argonaut server pool management # # Copyright (C) 2012-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### package Argonaut::Server::ModulesPool; use strict; use POE qw( Component::Pool::Thread ); use threads::shared; use Module::Pluggable search_path => 'Argonaut::Server::Modules', require => 1; BEGIN { plugins(); } # To be sure server modules are required sub rclone { my $ref = shift; my $type = ref $ref; if( $type eq 'HASH' ) { return { map rclone( $_ ), %{ $ref } }; } elsif( $type eq 'ARRAY' ) { return [ map rclone( $_ ),@{ $ref } ]; } elsif( $type eq 'REF' ) { return \ rclone( $$ref ); } else { print "ignoring type '$type'\n" if $type ne ''; return $ref; } } sub thread_sendobject { my $object = shift; my $blob :shared = shared_clone({'class' => ref $object}); $blob->{object} = shared_clone({ %$object }); # Copying object as a hash return $blob; } sub thread_getobject { my $blob = shift; my $object = rclone($blob->{object}); bless $object, $blob->{class}; return $object; } sub new { my ($class, %args) = @_; my $self = bless \%args, $class; # This creates the threadpool which does the actual work. The entry point # for the actual threads themselves is the query_database function. $self->{session} = POE::Component::Pool::Thread->new ( MinFree => 2, MaxFree => 5, MaxThreads => 15, StartThreads => 5, EntryPoint => \&module_thread_entry_point, CallBack => \&module_thread_result_handler, Name => "$self", inline_states => { do => sub { my ($kernel, $heap, $sender, $object, $taskid, $args) = @_[ KERNEL, HEAP, SENDER, ARG0 .. $#_ ]; $heap->{sender} = $sender; $args ||= []; $object->{taskid} = $taskid; $kernel->yield(run => thread_sendobject($object), shared_clone($args)); }, } ); return "$self"; } sub module_thread_entry_point { my ($o, $a) = @_; my $object = thread_getobject($o); my $args = rclone($a); my $res :shared; eval { $res = shared_clone($object->do_action($args)); }; if ($@) { return ($@, undef, thread_sendobject($object)); }; return (undef, $res, thread_sendobject($object)); } =pod $object->{task} might contain the following keys: substatus : new substatus of the task handler : 1 if the task handler should be filled =cut sub module_thread_result_handler { my ($kernel, $error, $result, $o) = @_[ KERNEL, ARG0..$#_]; my $object = thread_getobject($o); if (defined $error) { $kernel->post( $_[HEAP]->{sender}, "set_task_error", $object->{taskid}, $error); return; } if (defined $object->{task}) { if (defined $object->{task}->{substatus}) { $kernel->post( $_[HEAP]->{sender}, "set_task_substatus", $object->{taskid}, $object->{task}->{substatus}, $object->{task}->{progress}); } if ($object->{task}->{handler}) { $kernel->post( $_[HEAP]->{sender}, "set_task_handler", $object->{taskid}, $object); } } if (defined $object->{launch_actions}) { foreach my $action (@{$object->{launch_actions}}) { if (not defined $action->[2]->{timestamp}) { $action->[2]->{timestamp} = time(); } $action->[2]->{parent_taskid} = $object->{taskid}; $kernel->post( $_[HEAP]->{sender}, "add", undef, undef, @$action); } } $kernel->post( $_[HEAP]->{sender}, "set_task_result", $object->{taskid}, $result); } 1; __END__ argonaut-1.0/argonaut-server/bin/000077500000000000000000000000001304135723100170655ustar00rootroot00000000000000argonaut-1.0/argonaut-server/bin/argonaut-server000066400000000000000000000740161304135723100221440ustar00rootroot00000000000000#!/usr/bin/perl ####################################################################### # # argonaut-server -- Dispatching action received from FusionDirectory # to the clients # # Copyright (C) 2011-2016 FusionDirectory project # # Author: Côme BERNIGAUD # # 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 St, Fifth Floor, Boston, MA 02110-1301, USA. # ####################################################################### ####################################################################### # The wakeonlan part is taken from FusionInventory # Copyright (C) 2011 FusionInventory ####################################################################### use strict; use warnings; use 5.010; use Argonaut::Libraries::Common qw(:ldap :config :file :string); use Argonaut::Libraries::Packages qw(get_packages_info store_packages_file); use POE qw(Component::Schedule Component::Server::JSONRPC::Http); use DateTime; use DateTime::Set; use Data::Dumper; use Net::LDAP; use if (USE_LEGACY_JSON_RPC), 'JSON::RPC::Legacy::Client'; use if not (USE_LEGACY_JSON_RPC), 'JSON::RPC::Client'; use JSON; use File::Path; use Log::Handler; use App::Daemon qw(daemonize); # where to look for modules files use Module::Pluggable search_path => 'Argonaut::Server::Modules', sub_name => 'modules', instantiate => 'new', except => 'Argonaut::Server::Modules::Argonaut'; use Argonaut::Server::Modules::Argonaut; use Argonaut::Server::ModulesPool; use constant ETH_P_ALL => 0x0003; use constant PF_PACKET => 17; use constant SOCK_PACKET => 10; use English qw(-no_match_vars); use Socket; our ($config,$protocol,$server_ip,$server_port); my ($sslkeyfile,$sslcertfile,$sslcacertfile,$iptool,$delete_finished_tasks, $interface,$logdir,$packages_folder,$fetch_packages,$token); my $logfile = "argonaut-server.log"; my $piddir = "/var/run/argonaut"; my $pidfile = "argonaut-server.pid"; readConfig(); argonaut_create_dir($logdir); our $log = Log::Handler->create_logger("argonaut-server"); $App::Daemon::pidfile = "$piddir/$pidfile"; $App::Daemon::logfile = "$logdir/$logfile"; $App::Daemon::as_user = "root"; argonaut_create_dir($piddir); daemonize(); use Log::Log4perl qw(:levels); $log->add( file => { filename => "$logdir/$logfile", maxlevel => ($App::Daemon::loglevel == $DEBUG?"debug":"info"), minlevel => "emergency", newline => 1, } ); sub readConfig { $config = argonaut_read_config; my $settings = argonaut_get_server_settings($config,$config->{'server_ip'}); $protocol = $settings->{'protocol'}; $server_port = $settings->{'port'}; $iptool = $settings->{'iptool'}; $delete_finished_tasks = ($settings->{'delete_finished_tasks'} eq "TRUE"); $fetch_packages = ($settings->{'fetch_packages'} eq "TRUE"); $interface = $settings->{'interface'}; $logdir = $settings->{'logdir'}; $sslkeyfile = $settings->{'keyfile'}; $sslcertfile = $settings->{'certfile'}; $sslcacertfile = $settings->{'cacertfile'}; $token = $settings->{'token'}; $packages_folder = "/var/cache/argonaut/packages"; } sub getIpFromMac { my ($mac) = @_; my ($ldap,$ldap_base) = argonaut_ldap_handle($config); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(macAddress=$mac)(ipHostNumber=*))", attrs => [ 'ipHostNumber' ] ); $mesg->code && die "Error while searching IP for mac address '$mac' :".$mesg->error; if(scalar($mesg->entries)>1) { $log->error("Multiple IPs were found for the Mac address '$mac'!"); die "Multiple IPs were found for the Mac address '$mac'!"; } elsif(scalar($mesg->entries)<1) { $log->error("No IPs were found for the Mac address '$mac'!"); die "No IPs were found for the Mac address '$mac'!"; } my $ip = ($mesg->entries)[0]->get_value("ipHostNumber"); $mesg = $ldap->unbind; # take down session return $ip; } #=pod #=item getMacFromCn #Search in the ldap for the Mac associated to a host name. #Parameter : the host name #=cut sub getMacFromCn { my ($host, $filter) = @_; my ($ldap,$ldap_base) = argonaut_ldap_handle($config); my $mesg = $ldap->search( # perform a search base => $ldap_base, filter => "(&(cn=$host)(macAddress=*)$filter)", attrs => [ 'macAddress' ] ); $mesg->code && die "Error while searching mac for host '$host' :".$mesg->error; if(scalar($mesg->entries)>1) { $log->error("Multiple mac addresses were found for the host '$host'!"); die "Multiple mac addresses were found for the host '$host'!"; } elsif(scalar($mesg->entries)<1) { $log->error("No macs was found for the host '$host'!"); die "No mac address was found for the host '$host'!"; } my $mac = ($mesg->entries)[0]->get_value("macAddress"); $mesg = $ldap->unbind; # take down session return $mac; } #=pod #=item wakeOnLan #Send a wake on lan package to a mac address #Parameter : the mac address #inspired by WakeOnLan.pm file from FusionInventory #=cut sub wakeOnLan { my ($macaddress) = @_; $log->info("wake on lan"); return unless defined $macaddress; $macaddress =~ s/://g; ### for LINUX ONLY ### if ( eval { socket(SOCKET, PF_PACKET, SOCK_PACKET, getprotobyname('tcp')) or $log->info($!) and 0; }) { setsockopt(SOCKET, SOL_SOCKET, SO_BROADCAST, 1) or warn "Can't do setsockopt: $ERRNO\n"; open my $handle, '-|', "$iptool -a $interface" or die "Can't run $iptool: $ERRNO"; while (my $line = <$handle>) { next unless $line =~ /$interface \s+ Link \s \S+ \s+ HWaddr \s (\S+)/x; my $netMac = $1; $log->info("Send magic packet to $macaddress directly on card driver"); $netMac =~ s/://g; my $magic_packet = (pack('H12', $macaddress)) . (pack('H12', $netMac)) . (pack('H4', "0842")); $magic_packet .= chr(0xFF) x 6 . (pack('H12', $macaddress) x 16); my $destination = pack("Sa14", 0, $interface); send(SOCKET, $magic_packet, 0, $destination) or warn "Couldn't send packet on $interface: $ERRNO\n"; } close $handle; # TODO : For FreeBSD, send to /dev/bpf .... } else { # degraded wol by UDP if ( eval { socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('udp')); }) { my $magic_packet = chr(0xFF) x 6 . (pack('H12', $macaddress) x 16); my $sinbroadcast = sockaddr_in("9", inet_aton("255.255.255.255")); $log->info("Send magic packet to $macaddress in UDP mode (degraded wol)"); send(SOCKET, $magic_packet, 0, $sinbroadcast); } else { $log->error("Impossible to send magic packet..."); } } } sub convert_entry { my ($entry,$id) = @_; my $res = {}; $res->{$_} = $entry->{$_} for ('action','data','target','status','substatus','progress','error'); $res->{id} = $id; return $res; } sub ldap_authenticate { my ($login, $password) = @_; if (!defined($login) || !defined($password)) { return 0; } my $ldapinfos = argonaut_ldap_init ($config->{'ldap_configfile'}, 0, $login, 0, $password, 0, $config->{'ldap_tls'}); if ( $ldapinfos->{'ERROR'} > 0) { return 0; } return 1; } sub token_authenticate { my ($login, $hash) = @_; if (!defined($hash)) { return 0; } return argonaut_check_ssha_token($hash, $token); } sub refresh_task { my ($kernel,$heap,$session,$id) = @_; if (defined $heap->{tasks}->{$id}->{'handler'}) { $heap->{tasks}->{$id} = $heap->{tasks}->{$id}->{'handler'}->update_task($heap->{tasks}->{$id}); } if($heap->{tasks}->{$id}->{progress} >= 100) { $kernel->call($session => 'set_task_processed' => $id); } } POE::Session->create( inline_states => { _start => sub { $_[KERNEL]->sig( INT => "sigint", ("sigint")); $_[KERNEL]->sig( TERM => "sigint", ("sigterm")); $_[KERNEL]->sig( KILL => "sigint", ("sigkill")); $_[KERNEL]->sig( HUP => "sighup" ); $_[HEAP]{tasks} = {}; $_[HEAP]{id} = 0; $_[HEAP]{jsonserver} = POE::Component::Server::JSONRPC::Http->new( json => JSON->new->utf8, Port => $server_port, Handler => { 'echo' => 'echo', 'ping' => 'ping', 'action' => 'add', 'get_entries' => 'get_entries_by_id', 'get_entries_by_id' => 'get_entries_by_id', 'get_entries_by_mac' => 'get_entries_by_mac', 'remove_entries' => 'remove_entries', 'process_entries_now' => 'process_entries', 'get_my_id' => 'id_of_mac', 'get_host_id' => 'id_of_host', 'set_task_substatus' => 'jsonrpc_set_task_substatus', 'set_error' => 'set_error', 'get_packages' => 'get_packages' }, ($protocol eq 'https') ? ( SslKey => $sslkeyfile, SslCert => $sslcertfile, SslCaCert => $sslcacertfile, Authenticate => \&token_authenticate,) : () ); $_[HEAP]{modulepool} = Argonaut::Server::ModulesPool->new( ); $_[HEAP]{scheduled_only} = [ "Deployment.update", "Deployment.reinstall", "Deployment.memcheck", "detect_hardware" ]; #~ $_[KERNEL]->yield(add=>undef,undef,"job_trigger_action_halt",["00:11:22:33:44:55"],{timestamp=>1314107700}); $log->notice("Argonaut server launched on port $server_port"); if ($fetch_packages) { $_[HEAP]{crawler} = POE::Component::Schedule->add( $_[SESSION], packages_crawler => DateTime::Set->from_recurrence( after => DateTime->now, recurrence => sub { return $_[0]->add( days => 1 ) }, )); $_[KERNEL]->yield("packages_crawler"); } }, echo => sub { my ($kernel, $jsonrpc, $id, @params) = @_[KERNEL, ARG0..$#_ ]; $log->info("echo (usually means server has been pinged)"); $kernel->post( $jsonrpc => 'result' => $id, @params ); }, ping => sub { my ($kernel, $session, $heap, $jsonrpc, $id, @params) = @_[KERNEL,SESSION,HEAP, ARG0..$#_ ]; my ($mac) = @params; $log->info("ping $mac"); $kernel->yield(add => $jsonrpc, $id, 'ping', [$mac], {'args' => []}); }, id_of_mac => sub { my ($kernel, $heap, $jsonrpc, $id, @params) = @_[KERNEL,HEAP, ARG0..$#_ ]; my ($mac) = @params; $log->info("searching taskid for $mac"); while (my ($taskid,$entry) = each(%{$heap->{tasks}})) { if(($entry->{target} eq lc($mac))&&($entry->{status} eq "processing")) { $heap->{tasks}->{$taskid}->{substatus} = ""; $kernel->post( $jsonrpc => 'result' => $id, $taskid); return; } } $kernel->post( $jsonrpc => 'error' => $id, "Mac address '$mac' was not found in queue"); }, id_of_host => sub { my ($kernel, $heap, $session, $jsonrpc, $id, @params) = @_[KERNEL,HEAP,SESSION, ARG0..$#_ ]; my ($host, $filter) = @params; $log->info("searching taskid for $host $filter"); my $mac = getMacFromCn($host, $filter); $kernel->call($session => 'id_of_mac' => $jsonrpc, $id, $mac); }, jsonrpc_set_task_substatus => sub { my ($kernel, $heap, $session, $jsonrpc, $id, @params) = @_[KERNEL,HEAP,SESSION, ARG0..$#_ ]; my ($taskid,$substatus,$progress) = @params; if(!defined $heap->{tasks}->{$taskid}) { $kernel->post( $jsonrpc => 'error' => $id, "This task does not exists"); return; } $kernel->call($session => 'set_task_substatus' => $taskid,$substatus,$progress); $kernel->post($jsonrpc => 'result' => $id, "ok"); }, set_task_substatus => sub { my ($kernel, $heap, $session, @params) = @_[KERNEL,HEAP,SESSION, ARG0..$#_ ]; my ($taskid,$substatus,$progress) = @params; if (defined $progress) { $log->info("setting task $taskid substatus to '$substatus' ($progress %)"); } else { $log->info("setting task $taskid substatus to '$substatus'"); } $heap->{tasks}->{$taskid}->{substatus} = $substatus; if (defined $progress) { $heap->{tasks}->{$taskid}->{progress} = $progress; if (($progress > 0) && (defined $heap->{tasks}->{$taskid}->{child_taskids})) { my @delList; my $i = 0; foreach my $id (@{$heap->{tasks}->{$taskid}->{child_taskids}}) { if ($heap->{tasks}->{$id}->{action} eq 'System.reboot') { $kernel->call($session => 'set_task_substatus' => $id,'Canceled by parent task',100); push @delList, $i; } $i++; } # Delete canceled tasks from child list splice @{$heap->{tasks}->{$taskid}->{child_taskids}}, $_, 1 for reverse @delList; } if ($progress >= 100) { $kernel->call($session => 'set_task_processed' => $taskid); } } }, set_error => sub { my ($kernel, $heap, $session, $jsonrpc, $id, @params) = @_[KERNEL,HEAP,SESSION, ARG0..$#_ ]; my ($taskid,$error) = @params; $log->info("setting task $taskid error to '$error'"); if(!defined $heap->{tasks}->{$taskid}) { $kernel->post( $jsonrpc => 'error' => $id, "This task does not exists"); } else { $heap->{tasks}->{$taskid}->{status} = "error"; $heap->{tasks}->{$taskid}->{error} = $error; $kernel->post( $jsonrpc => 'result' => $id, "ok"); } }, set_task_handler => sub { my ($heap,$taskid,$handler) = @_[HEAP,ARG0 .. $#_]; $heap->{tasks}->{$taskid}->{handler} = $handler; }, schedule => sub { my ($kernel,$session,$heap,$date,$action,$target,$data) = @_[KERNEL,SESSION,HEAP,ARG0 .. $#_]; my $taskid = $kernel->call($session => 'get_new_task_id'); $heap->{tasks}->{$taskid} = { handle => POE::Component::Schedule->add($session, action => $date, ($taskid,$action,$target,$data)), date => $date, action => $action, target => $target, data => $data, status => "waiting", substatus => "", progress => 0, error => "" }; if (defined $data->{parent_taskid} && defined $heap->{tasks}->{$data->{parent_taskid}}) { if (not defined $heap->{tasks}->{$data->{parent_taskid}}->{child_taskids}) { $heap->{tasks}->{$data->{parent_taskid}}->{child_taskids} = []; } push @{$heap->{tasks}->{$data->{parent_taskid}}->{child_taskids}}, $taskid; } $log->debug("action $action scheduled on target $target"); }, add => sub { my ($kernel, $heap, $session, $jsonrpc, $id, @params) = @_[KERNEL,HEAP,SESSION, ARG0..$#_ ]; my ($action,$targets,$data) = @params; if (ref $data ne ref {}) { #If data is not an hash reference, make it so to avoid errors. $data = {}; } $log->info("adding action $action"); if( ((! defined $data->{timestamp}) || ($data->{timestamp} eq "")) && (grep {$_ eq $action} @{$heap->{scheduled_only}})) { # If the action is scheduled_only and there is no timestamp # We schedule it for now $data->{timestamp} = time(); } if(defined $data->{timestamp} && ($data->{timestamp} ne "")) { $kernel->post( $jsonrpc => 'result' => $id, "OK" ); # asynchronous my $date = DateTime->from_epoch(epoch => $data->{timestamp}); my $datetime; if (defined $data->{periodic} && ($data->{periodic} =~ /^(\d+)_(\w+)$/)) { my $periodic_nb = $1; my $periodic_keyword = $2; $datetime = DateTime::Set->from_recurrence ( after => $date, recurrence => sub { return $_[0]->add( $periodic_keyword => $periodic_nb ) }, ); } else { $datetime = DateTime::Set->from_datetimes(dates => [ $date ]); } foreach my $target (@{$targets}) { $kernel->yield(schedule => $datetime->clone, $action, lc($target), $data); } $log->info("action $action scheduled"); } else { my $errors = ""; my @results; my $taskid = $kernel->call($session => 'get_new_task_id'); $heap->{tasks}->{$taskid}->{nb_targets} = scalar(@$targets); $heap->{tasks}->{$taskid}->{jsonid} = $id; $heap->{tasks}->{$taskid}->{jsonsession} = $jsonrpc; foreach my $target (@{$targets}) { $kernel->yield('action' => $taskid,$action,lc($target),$data); } } }, get_entries_by_id => sub { my ($kernel, $heap, $session, $jsonrpc, $id, @params) = @_[KERNEL, HEAP, SESSION, ARG0..$#_ ]; my ($ids) = @params; my $entries = []; foreach my $id (keys(%{$heap->{tasks}})) { if ((not defined $heap->{tasks}->{$id}->{jsonid}) && ((!defined $ids) || (grep {$_ == $id} $ids))) { refresh_task($kernel,$heap,$session,$id); push @{$entries}, convert_entry($heap->{tasks}->{$id},$id); } } $kernel->post( $jsonrpc => 'result' => $id, $entries); }, get_entries_by_mac => sub { # this has not been tested my ($kernel, $heap, $session, $jsonrpc, $id, @params) = @_[KERNEL, HEAP, SESSION, ARG0..$#_ ]; my ($macs) = @params; my $entries = []; foreach my $id (keys(%{$heap->{tasks}})) { if ((not defined $heap->{tasks}->{$id}->{jsonid}) && ((!defined $macs) || (grep {lc($_) eq $heap->{tasks}->{$id}->{'target'}} $macs))) { refresh_task($kernel,$heap,$session,$id); push @{$entries}, convert_entry($heap->{tasks}->{$id},$id); } } $kernel->post( $jsonrpc => 'result' => $id, $entries); }, remove_entries => sub { my ($kernel, $heap, $session, $jsonrpc, $id, @params) = @_[KERNEL, HEAP, SESSION, ARG0..$#_ ]; my ($ids) = @params; foreach my $id (@{$ids}) { if ((defined $id) && (defined $heap->{tasks}->{$id})) { $kernel->call($session => 'delete_task' => $id); } } $kernel->post( $jsonrpc => 'result' => $id, "ok"); }, process_entries => sub { my ($kernel, $heap, $jsonrpc, $id, @params) = @_[KERNEL, HEAP,ARG0..$#_ ]; my ($ids) = @params; my $errors = ""; foreach my $id (@{$ids}) { if ((defined $id) && (defined $heap->{tasks}->{$id})) { delete $heap->{tasks}->{$id}->{handle}; $kernel->yield(action => $id, $heap->{tasks}->{$id}->{action}, $heap->{tasks}->{$id}->{target}, $heap->{tasks}->{$id}->{data}); } elsif (defined $id) { $errors.="$id unknown\n"; } else { $errors.="id undefined\n"; } } if ($errors ne "") { $kernel->post( $jsonrpc => 'error' => $id, $errors); } else { $kernel->post( $jsonrpc => 'result' => $id, "ok"); } }, action => sub { my ($kernel,$heap,$session,$taskid,$action,$target,$data) = @_[KERNEL,HEAP,SESSION,ARG0 .. $#_]; $heap->{tasks}->{$taskid}->{action} = $action; my $handled = 0; my @modules = modules(); push @modules, Argonaut::Server::Modules::Argonaut->new(); MODULES: foreach my $module (@modules) { # Is this module able to handle this client and action? if (eval {$module->handle_client($target,$action)}) { $handled = 1; # If it is, send him the infos my $args = undef; if(defined $data->{args}) { $args = $data->{args}; } $kernel->post($heap->{modulepool} => "do" => $module, $taskid, $args); last MODULES; } } unless ($handled) { $kernel->yield('set_task_error' => $taskid, "No client module can handle action $action for target $target"); } }, set_task_error => sub { my ($kernel,$heap,$session,$taskid,$error) = @_[KERNEL,HEAP,SESSION,ARG0 .. $#_]; if (defined $heap->{tasks}->{$taskid}->{jsonid}) { $heap->{tasks}->{$taskid}->{error} .= $error; $kernel->call($session => 'send_result' => $taskid); } else { $log->warning("Error occured : ".$error); $heap->{tasks}->{$taskid}->{status} = "error"; $heap->{tasks}->{$taskid}->{error} = $error; } }, set_task_result => sub { my ($kernel,$heap,$session,$taskid,$res) = @_[KERNEL,HEAP,SESSION,ARG0 .. $#_]; $log->debug("Setting task result '$res' for task '$taskid'"); if (defined $heap->{tasks}->{$taskid}->{jsonid}) { push @{$heap->{tasks}->{$taskid}->{result}}, $res; $kernel->call($session => 'send_result' => $taskid); } else { $heap->{tasks}->{$taskid}->{error} = ""; if (grep {$_ eq $heap->{tasks}->{$taskid}->{action}} @{$heap->{scheduled_only}}) { $heap->{tasks}->{$taskid}->{status} = "processing"; } else { $kernel->call($session => 'set_task_processed' => $taskid); } } }, send_result => sub { my ($kernel,$heap,$session,$taskid) = @_[KERNEL,HEAP,SESSION,ARG0 .. $#_]; if (--$heap->{tasks}->{$taskid}->{nb_targets} == 0) { if ($heap->{tasks}->{$taskid}->{error} ne "") { $kernel->post( $heap->{tasks}->{$taskid}->{jsonsession} => 'error' => $heap->{tasks}->{$taskid}->{jsonid}, $heap->{tasks}->{$taskid}->{error} ); } else { $kernel->post( $heap->{tasks}->{$taskid}->{jsonsession} => 'result' => $heap->{tasks}->{$taskid}->{jsonid}, $heap->{tasks}->{$taskid}->{result} ); } $kernel->call($session => 'delete_task' => $taskid); } }, delete_task => sub { my ($kernel,$heap,$session,$taskid) = @_[KERNEL,HEAP,SESSION,ARG0 .. $#_]; if (not defined $heap->{tasks}->{$taskid}) { return; } $log->debug("deleting task $taskid"); if (defined $heap->{tasks}->{$taskid}->{handler}) { $kernel->call("".$heap->{tasks}->{$taskid}->{handler} => 'stop'); delete $heap->{tasks}->{$taskid}->{handler}; } delete $heap->{tasks}->{$taskid}; }, set_task_processed => sub { my ($kernel,$heap,$session,$taskid) = @_[KERNEL,HEAP,SESSION,ARG0 .. $#_]; if (not defined $heap->{tasks}->{$taskid}) { return; } if (defined $heap->{tasks}->{$taskid}->{'handler'}) { $heap->{tasks}->{$taskid} = $heap->{tasks}->{$taskid}->{'handler'}->task_processed($heap->{tasks}->{$taskid}); } $heap->{tasks}->{$taskid}->{status} = "processed"; if($delete_finished_tasks) { $kernel->call($session => 'delete_task' => $taskid); } }, get_new_task_id => sub { my $heap = $_[HEAP]; while (defined $heap->{tasks}->{$heap->{id}}) { $heap->{id}++; if ($heap->{id}>=2147483648) { # limit to 4 bytes $heap->{id} = 0; } } $heap->{tasks}->{$heap->{id}} = { 'error' => '', 'result' => [], 'nb_targets' => 1 }; return $heap->{id}; }, get_packages => sub { my ($kernel, $jsonrpc, $id, @params) = @_[KERNEL, ARG0..$#_ ]; my ($release,$attrs,$filters,$from,$to) = @params; $log->info("get_packages($release,[".join(',',@{$attrs})."],[".join(',',@{$filters})."],$from,$to)"); eval { my $distributions = get_packages_info($packages_folder,undef,$release,$attrs,$filters,$from,$to); $kernel->post( $jsonrpc => 'result' => $id, $distributions); }; if($@) { $log->error($@); $kernel->post( $jsonrpc => 'error' => $id, $@); } }, packages_crawler => sub { $log->info("Getting Packages files from repositories"); my $errors = store_packages_file($packages_folder); if(@{$errors} > 0) { $log->notice("Errors while getting Packages files : ".join(',',@{$errors})); } $log->info("done"); }, load_dump => sub { # this function has not been tested my ($kernel,$filename) = @_[KERNEL,ARG0..$#_]; my $dump; open ($dump, q{>}, $filename) or die "cannot open file"; while (<$dump>) { my $task = from_json($_); if(($task->{status} eq "waiting") && ($task->{data}->{timestamp} > time)) { $kernel->yield(schedule=>$task->{date},$task->{action},$task->{target},$task->{data}); } } close($dump); }, sighup => sub { my ($kernel,$heap) = @_[KERNEL,HEAP]; $log->notice("reloading config…"); readConfig(); $kernel->signal($heap->{jsonserver},"KILL"); $heap->{jsonserver} = POE::Component::Server::JSONRPC::Http->new( Port => $server_port, Handler => { # FIXME this hash should not be duplicated 'echo' => 'echo', 'ping' => 'ping', 'action' => 'add', 'get_entries' => 'get_entries_by_id', 'get_entries_by_id' => 'get_entries_by_id', 'get_entries_by_mac' => 'get_entries_by_mac', 'remove_entries' => 'remove_entries', 'process_entries_now' => 'process_entries', 'get_my_id' => 'id_of_mac', 'set_task_substatus' => 'jsonrpc_set_task_substatus', 'set_error' => 'set_error', 'get_packages' => 'get_packages' }, ); $kernel->sig_handled(); }, sigint => sub { my ($kernel,$heap,$signal) = @_[KERNEL,HEAP,ARG0..$#_]; $log->notice("exiting because of $signal…"); #here, do something with waiting tasks if(scalar(keys(%{$_[HEAP]->{tasks}})) > 0) { my $dump; open ($dump, q{>}, "dump_".time.".json") or die "cannot open file"; foreach my $task (values(%{$_[HEAP]->{tasks}})) { delete $task->{handle}; print $dump (to_json($task)."\n"); } close($dump); } }, sigchild => sub { $log->notice("child process exiting…"); #~ delete $_[HEAP]->{bloquant}; }, _stop => sub { $log->notice("_stop"); }, }, ); POE::Kernel->run(); exit 0; __END__ =head1 NAME argonaut-server - Dispatching action received from FusionDirectory to the clients =head1 SYNOPSIS argonaut-server =head1 DESCRIPTION argonaut-server - argonaut-server dispatch actions received from FusionDirectory and send it to the clients. It is modular and can load various modules at run time. =head1 BUGS Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to =head1 LICENCE AND COPYRIGHT This code is part of Argonaut Project =over 1 =item Copyright (C) 2011-2016 FusionDirectory project =back 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. =cut argonaut-1.0/argonaut-server/lib/000077500000000000000000000000001304135723100170635ustar00rootroot00000000000000argonaut-1.0/argonaut-server/lib/systemd/000077500000000000000000000000001304135723100205535ustar00rootroot00000000000000argonaut-1.0/argonaut-server/lib/systemd/system/000077500000000000000000000000001304135723100220775ustar00rootroot00000000000000argonaut-1.0/argonaut-server/lib/systemd/system/argonaut-server.service000066400000000000000000000003471304135723100266110ustar00rootroot00000000000000[Unit] Description=Start argonaut-server ConditionPathExists=/usr/sbin/argonaut-server [Service] Type=forking ExecStart=/usr/sbin/argonaut-server PIDFile=/var/run/argonaut/argonaut-server.pid [Install] WantedBy=multi-user.target argonaut-1.0/argonaut-server/man/000077500000000000000000000000001304135723100170705ustar00rootroot00000000000000argonaut-1.0/argonaut-server/man/argonaut-server.1000066400000000000000000000117561304135723100223100ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ARGONAUT-SERVER 1" .TH ARGONAUT-SERVER 1 "2016-01-06" "Argonaut 0.9.7" "Argonaut Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" argonaut\-server \- Dispatching action received from FusionDirectory to the clients .SH "SYNOPSIS" .IX Header "SYNOPSIS" argonaut-server .SH "DESCRIPTION" .IX Header "DESCRIPTION" argonaut-server \- argonaut-server dispatch actions received from FusionDirectory and send it to the clients. It is modular and can load various modules at run time. .SH "BUGS" .IX Header "BUGS" Please report any bugs, or post any suggestions, to the fusiondirectory mailing list fusiondirectory-users or to .SH "LICENCE AND COPYRIGHT" .IX Header "LICENCE AND COPYRIGHT" This code is part of Argonaut Project .IP "Copyright (C) 2011\-2016 FusionDirectory project" 1 .IX Item "Copyright (C) 2011-2016 FusionDirectory project" .PP This program is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE. \s0 See the \&\s-1GNU\s0 General Public License for more details.