File mgr-osad-git-0.e83fe81.obscpio of Package mgr-osad.15447

07070100000000000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000000900000000mgr-osad07070100000001000081B40000000000000000000000015E282E56000046AC000000000000000000000000000000000000001100000000mgr-osad/LICENSE                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
07070100000002000081B40000000000000000000000015E282E5600000026000000000000000000000000000000000000001200000000mgr-osad/Makefileinclude ../../../rel-eng/Makefile.git
07070100000003000081B40000000000000000000000015E282E56000007A7000000000000000000000000000000000000001700000000mgr-osad/Makefile.defs# Common pathnames and programs for the Spacewalk project
#

# if not defined, definit as a noop
TOP		?= .

# global defines which control this build and where we deploy files
ROOT		?= /usr/share/rhn
export ROOT

PREFIX		?=
export PREFIX

# Compilation stuff
CC		= gcc
PYTHON_INCLUDE	= -I/usr/include/python$(PythonVersion)
CFLAGS		= -Wall -O2 -fomit-frame-pointer $(PYTHON_INCLUDE) -fPIC
SOFLAGS		= -shared -fPIC

# Installation stuff
INSTALL		= /usr/bin/install -c --verbose
INSTALL_BIN	= $(INSTALL) -m 755
INSTALL_DATA	= $(INSTALL) -m 644
INSTALL_DIR	= $(INSTALL) -m 755 -d

# This is for the subdir part
PYFILES		= $(addsuffix .py,$(FILES))

# what do we need to install and where
INSTALL_FILES	+= $(PYFILES)
INSTALL_DEST	?= $(PYTHONPATH)/$(SUBDIR)

DIRS		+= $(addprefix $(PREFIX), \
			$(sort $(EXTRA_DIRS)) $(INSTALL_DEST))

all :: $(INSTALL_FILES) $(INSTALL_LINKS)

install :: all $(DIRS) $(INSTALL_FILES) $(INSTALL_LINKS)
	@$(foreach f,$(INSTALL_FILES), \
		$(INSTALL_DATA) $(f) $(PREFIX)$(INSTALL_DEST)/$(f) ; )
	@$(foreach f,$(INSTALL_LINKS), \
		ln -s `cat $(f)` $(PREFIX)$(INSTALL_DEST)/$(f) ; )

$(DIRS):
	$(INSTALL_DIR) $@

clean ::
	@rm -fv *~ *.pyc *.pyo .??*~
	@rm -fv .\#*
	@rm -fv core

# useful macro
descend-subdirs = @$(foreach d,$(SUBDIRS), $(MAKE) -C $(d) $@ || exit 1; )

# subdirs are treated at the end
all install clean:: $(SUBDIRS)
	$(descend-subdirs)


# extra toy targets
# Python checker support
PYTHONPATH      = $(TOP)
PYCHECKER       = pychecker
PYCHECKEROPTS   = --maxreturns 10 --maxbranches 15
DBCHECKER       = db-checker.py
DBCHECKEROPTS   =
DB              = user/pass@instance

pychecker :: $(PYFILES)
	@PYTHONPATH=$(PYTHONPATH) $(PYCHECKER) $(PYCHECKEROPTS) $(PYFILES) || :
	$(descend-subdirs)

db-checker :: $(PYFILES)
	@PYTHONPATH=$(PYTHONPATH) $(TOP)/$(DBCHECKER) $(DBCHECKEROPTS) $(PYFILES) || :
	$(descend-subdirs)

graphviz :: 
	@PYTHONPATH=$(PYTHONPATH) $(PYCHECKER) -Z $(PYCHECKEROPTS) $(PYFILES) || exit 0

07070100000004000081B40000000000000000000000015E282E5600000703000000000000000000000000000000000000001700000000mgr-osad/Makefile.osad# Makefile for the backend directory
#

PKGNAME = osad
VERSION = $(shell echo `awk '{ print $$1 }' version`)
RELEASE = $(shell echo `awk '{ print $$2 }' version`)

CODE_DIRS = src
CONF_DIRS   = rhn-conf osad-conf logrotate sysconfig tns-admin-osa-dispatcher

# We look for config files in "well known" locations (rhn-conf,
# httpd-conf, logrotate)
EXTRA_DIRS = /var/log/rhn /var/log/rhn/oracle /var/log/rhn/oracle/osa-dispatcher $(BINDIR) $(SBINDIR) $(INITDIR)
BINDIR	    = /usr/bin
SBINDIR	    = /usr/sbin
INITDIR     = /etc/rc.d/init.d

PROGS       = osad osa-dispatcher

all :: all-code all-conf

%-code : Makefile
	@$(foreach d,$(CODE_DIRS), $(MAKE) -C $(d) $* || exit 1; ) 

%-conf : Makefile
	@$(foreach d,$(CONF_DIRS), $(MAKE) -C $(d) $* || exit 1; ) 

# now include some Macros
include Makefile.defs

install :: install-code install-conf install-bins install-rcs 

install-bins : $(addsuffix .inst, $(PROGS))

install-rcs : $(addsuffix .inst.rc, $(PROGS))

%.inst : %.new $(PREFIX)$(SBINDIR)
	$(INSTALL_BIN) $*.new $(PREFIX)$(SBINDIR)/$*-$(PYTHONVERSION)
	rm -f $*.new

%.new : invocation.py
	sed -e 's|@@ROOT@@|$(ROOT)|g' < $^ > $@

%.inst.rc : %.new.rc $(PREFIX)$(INITDIR)
	$(INSTALL_BIN) $*.new.rc $(PREFIX)$(INITDIR)/$*

%.new.rc : prog.init
	sed -e 's|@@PROG@@|$*|g' < $^ > $@

clean :: clean-code clean-conf
	rm -f *.new *.rcnew

archive: _archive

_archive:
	@rm -rf ${PKGNAME}-%{VERSION}.tar.gz
	@rm -rf /tmp/${PKGNAME}-$(VERSION) /tmp/${PKGNAME}
	@dir=$$PWD; cd /tmp; cp -a $$dir ${PKGNAME}
	@mv /tmp/${PKGNAME} /tmp/${PKGNAME}-$(VERSION)
	@dir=$$PWD; cd /tmp; tar cvzf $$dir/${PKGNAME}-$(VERSION).tar.gz ${PKGNAME}-$(VERSION)
	@rm -rf /tmp/${PKGNAME}-$(VERSION)
	@echo "The archive is in ${PKGNAME}-$(VERSION).tar.gz"

srpm: archive
	rpmbuild -ts ${PKGNAME}-$(VERSION).tar.gz

07070100000005000081B40000000000000000000000015E282E5600000344000000000000000000000000000000000000001900000000mgr-osad/Makefile.pythonTHIS_MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIR := $(dir $(THIS_MAKEFILE))
include $(CURRENT_DIR)../../../rel-eng/Makefile.python

# Docker tests variables
DOCKER_CONTAINER_BASE = uyuni-master
DOCKER_REGISTRY       = registry.mgr.suse.de
DOCKER_RUN_EXPORT     = "PYTHONPATH=$PYTHONPATH"
DOCKER_VOLUMES        = -v "$(CURDIR)/../../../:/manager"

__pylint ::
	$(call update_pip_env)
	pylint --rcfile=pylintrc $(shell find -name '*.py') > reports/pylint.log || true

docker_pylint ::
	docker run --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/sh -c "cd /manager/client/tools/mgr-osad; make -f Makefile.python __pylint"

docker_shell ::
	docker run -t -i --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/bash
07070100000006000081FD0000000000000000000000015E282E56000005C9000000000000000000000000000000000000001700000000mgr-osad/invocation.py#!/usr/bin/python
#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import os
import sys

_path = "@@ROOT@@"
if _path not in sys.path:
    sys.path.append(_path)

mod_name = os.path.basename(sys.argv[0])
mod_name = mod_name.replace('-', '_')
try:
    mod = __import__("osad." + mod_name)
except ImportError:
    e = sys.exc_info()[1]
    sys.stderr.write("Unable to load module %s\n" % mod_name)
    sys.stderr.write(str(e) + "\n")
    sys.exit(1)
mod = getattr(mod, mod_name)

if __name__ == '__main__':
    try:
        sys.exit(mod.main() or 0)
    except KeyboardInterrupt:
        e = sys.exc_info()[1]
        sys.stderr.write("\nUser interrupted process.\n")
        sys.exit(0)
    except SystemExit:
        e = sys.exc_info()[1]
        sys.exit(e.code)
    except Exception:
        e = sys.exc_info()[1]
        sys.stderr.write("\nERROR: unhandled exception occurred: (%s).\n" % e)
        sys.exit(-1)

07070100000007000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000001300000000mgr-osad/logrotate07070100000008000081B40000000000000000000000015E282E56000000CD000000000000000000000000000000000000001C00000000mgr-osad/logrotate/Makefile# Makefile for installation of the logrotation configuration files
#

# what is the backend top dir
TOP	= ..

INSTALL_FILES	= $(wildcard osa*)
INSTALL_DEST	= /etc/logrotate.d

include $(TOP)/Makefile.defs
07070100000009000081B40000000000000000000000015E282E5600000175000000000000000000000000000000000000002200000000mgr-osad/logrotate/osa-dispatcher# logrotation file for Red Hat  Satellite's osa-dispatcher

/var/log/rhn/osa-dispatcher.log {
    weekly
    rotate 5
    copytruncate
    compress
    notifempty
    missingok
    size 10M
    su wwwrun www
}

/var/log/rhn/oracle/osa-dispatcher/sqlnet.ora {
    weekly
    rotate 5
    copytruncate
    compress
    notifempty
    missingok
    su root www
    size 10M
}
0707010000000A000081B40000000000000000000000015E282E5600000072000000000000000000000000000000000000001800000000mgr-osad/logrotate/osad/var/log/osad {
    weekly
    rotate 5
    copytruncate
    compress
    notifempty
    missingok
    size 10M
}
0707010000000B000081B40000000000000000000000015E282E560000003C000000000000000000000000000000000000001C00000000mgr-osad/mgr-osad-rpmlintrcaddFilter("suse-filelist-forbidden-sysconfig .*/sysconfig")
0707010000000C000081B40000000000000000000000015E282E560000352D000000000000000000000000000000000000001A00000000mgr-osad/mgr-osad.changes-------------------------------------------------------------------
Wed Jan 22 12:09:22 CET 2020 - jgonzalez@suse.com

- version 4.1.2-1
- take care that osad is not disabled nor deactivated during update
  (bsc#1157700, bsc#1158697)
- separate osa-dispatcher and jabberd so it can be disabled independently

-------------------------------------------------------------------
Wed Nov 27 16:55:56 CET 2019 - jgonzalez@suse.com

- version 4.1.1-1
- replace spacewalk-usix with uyuni-common-libs
- Bump version to 4.1.0 (bsc#1154940)
- Obsolete all old python2-osa* packages to avoid conflicts (bsc#1152290)
- move /usr/share/rhn/config-defaults to uyuni-base-common
- Require uyuni-base-common for /etc/rhn (for osa-dispatcher)

-------------------------------------------------------------------
Wed Jul 31 17:28:01 CEST 2019 - jgonzalez@suse.com

- version 4.0.9-1
- Ensure bytes type when using hashlib to avoid traceback (bsc#1138822)
- Fix obsolete for old osad packages, to allow installing mgr-osad
  even by using osad at yum/zyppper install (bsc#1139453)

-------------------------------------------------------------------
Wed May 15 20:07:58 CEST 2019 - jgonzalez@suse.com

- version 4.0.8-1
- Obsolete newer versions of osad

-------------------------------------------------------------------
Wed May 15 14:59:49 CEST 2019 - jgonzalez@suse.com

- version 4.0.7-1
- SPEC cleanup
- Fix wrong bugzilla entry at changelog

-------------------------------------------------------------------
Mon Apr 22 12:01:44 CEST 2019 - jgonzalez@suse.com

- version 4.0.6-1
- add makefile and pylint configuration

-------------------------------------------------------------------
Thu Jan 31 09:40:02 CET 2019 - jgonzalez@suse.com

- version 4.0.5-1
- Final fixes to make osa-dispatcher compatible with python3

-------------------------------------------------------------------
Wed Jan 16 12:14:14 CET 2019 - jgonzalez@suse.com

- version 4.0.4-1
- Require correct python version for osa-dispatcher

-------------------------------------------------------------------
Fri Oct 26 09:55:23 CEST 2018 - jgonzalez@suse.com

- version 4.0.3-1
- Change dependencies for subpackages to python2-mgr-osa-*
  python3-mgr-osa-* and mgr-osa-* (bsc#1104034)

-------------------------------------------------------------------
Fri Aug 10 17:04:37 CEST 2018 - jgonzalez@suse.com

- version 4.0.2-1
- Fix SPEC issue for subpackage mgr-osa-dispatcher-selinux (bsc#1104034)

-------------------------------------------------------------------
Fri Aug 10 16:02:12 CEST 2018 - jgonzalez@suse.com

- version 4.0.1-1
- Bump version to 4.0.0 (bsc#1104034)
- Rename package to mgr-osad to allow version 4.0.0 (bsc#1104034)
- Fix copyright for the package specfile (bsc#1103696)

-------------------------------------------------------------------
Thu Mar 29 01:21:06 CEST 2018 - jgonzalez@suse.com

- version 5.11.102.2-1
- use full package name python-jabberpy as dependency (bsc#1087299)

-------------------------------------------------------------------
Mon Mar 26 08:30:09 CEST 2018 - jgonzalez@suse.com

- version 5.11.102.1-1
- Sync with upstream (bsc#1083294)
- Remove osad files when packaging only for python3
- Run osa-dispatcher on python3 when possible

-------------------------------------------------------------------
Mon Mar 05 08:35:34 CET 2018 - jgonzalez@suse.com

- version 5.11.100.2-1
- remove clean section from spec (bsc#1083294)

-------------------------------------------------------------------
Fri Feb 23 10:36:23 CET 2018 - jgonzalez@suse.com

- version 5.11.100.1-1
- remove unused python-xml requirement (bsc#1082211)

-------------------------------------------------------------------
Wed Jan 17 11:06:04 CET 2018 - jgonzalez@suse.com

- version 5.11.98.2-1
- Fix update mechanism when updating the updateservice (bsc#1073619)

-------------------------------------------------------------------
Fri Nov 10 16:19:00 CET 2017 - mc@suse.de

- version 5.11.98.1-1
- add missing directory to filelist

-------------------------------------------------------------------
Thu Oct 26 16:42:22 CEST 2017 - mc@suse.de

- version 5.11.97.1-1
- splitinto python2/python3 specific packages
- Fixed TypeError for force flag in setup_config that could happen
  when jabberd restart was needed. (bsc#1064393)

-------------------------------------------------------------------
Tue Aug 08 09:32:23 CEST 2017 - fkobzik@suse.de

- version 5.11.80.3-1
- reduce maximal size of osad log before rotating
- perform osad restart in posttrans (bsc#1039913)

-------------------------------------------------------------------
Tue May 23 07:57:05 CEST 2017 - mc@suse.de

- version 5.11.80.2-1
- require rhnlib version with i18n module (bsc#1038483)

-------------------------------------------------------------------
Tue Mar 07 14:38:36 CET 2017 - mc@suse.de

- version 5.11.80.1-1
- Updated links to github in spec files
- fix TypeError: descriptor 'with_traceback'
- remove running rhn_check on osad start
- require spacewalk-usix indead of spacewalk-backend-usix
- fix osa_dispatcher so it can successfully register with jabberd

-------------------------------------------------------------------
Tue Feb 07 17:49:37 CET 2017 - michele.bologna@suse.com

- version 5.11.77.1-1
- Align with upstream versioning

-------------------------------------------------------------------
Wed Jan 11 15:14:52 CET 2017 - michele.bologna@suse.com

- version 5.11.76.1-1
- Initial submission for Manager-3.1

-------------------------------------------------------------------
Fri Dec 16 16:01:48 CET 2016 - michele.bologna@suse.com

- version 5.11.64.3-1
- fix logfile option for osa-dispatcher (bsc#980752)

-------------------------------------------------------------------
Mon Mar 21 16:39:52 CET 2016 - mc@suse.de

- version 5.11.64.2-1
- fix file permissions (bsc#970550)

-------------------------------------------------------------------
Wed Mar 02 12:02:31 CET 2016 - mc@suse.de

- version 5.11.64.1-1
- Add possibility for OSAD to work in failover mode

-------------------------------------------------------------------
Wed Feb 10 08:28:15 CET 2016 - mc@suse.de

- version 5.11.63.2-1
- set osa-dispatcher notify_threshold to 100 as default

-------------------------------------------------------------------
Mon Nov 30 10:57:19 CET 2015 - mc@suse.de

- version 5.11.63.1-1
- fix TypeError: unbound method set_jabber_connection()

-------------------------------------------------------------------
Wed Oct 07 15:33:35 CEST 2015 - mc@suse.de

- version 5.11.62.1-1
- Bump version

-------------------------------------------------------------------
Wed Sep 23 15:02:44 CEST 2015 - mc@suse.de

- version 5.11.33.10-1
- osad: re-send subscription stanzas after a while (bsc#933738)

-------------------------------------------------------------------
Mon Jun 22 16:04:29 CEST 2015 - jrenner@suse.de

- version 5.11.33.9-1
- fix duplicate jabber ids (bsc#869888, bsc#931685)
- improve error logging

-------------------------------------------------------------------
Tue Mar 31 14:32:18 CEST 2015 - mc@suse.de

- version 5.11.33.8-1
- Apply needed SElinux fix for RHEL7 and make use of systemd unit files
- introduce notify_threshold for osa-dispatcher (bsc#915581)
- really check for action type reboot

-------------------------------------------------------------------
Tue Feb 03 13:14:54 CET 2015 - mc@suse.de

- version 5.11.33.7-1
- Getting rid of Tabs and trailing spaces

-------------------------------------------------------------------
Thu Dec 04 13:16:34 CET 2014 - mc@suse.de

- version 5.11.33.6-1
- removed PyXML dependency for RHEL systems
- fix osad through unauthenticated proxy case

-------------------------------------------------------------------
Fri Nov 07 13:06:40 CET 2014 - mc@suse.de

- version 5.11.33.5-1
- enable and install osad during first installation (bsc#901958)

-------------------------------------------------------------------
Wed Sep  3 01:41:37 CEST 2014 - ro@suse.de

- sanitize release line in specfile

-------------------------------------------------------------------
Mon Sep 01 17:04:21 CEST 2014 - mc@suse.de

- version 5.11.33.4-1
- osad: fix traceback if http proxy is not configured
- osad: support communication over proxy

-------------------------------------------------------------------
Tue Jun 17 10:57:46 CEST 2014 - jrenner@suse.de

- version 5.11.33.3-1
- Call python using the -s option

-------------------------------------------------------------------
Thu Apr 10 17:21:11 CEST 2014 - mc@suse.de

- version 5.11.33.2-1

-------------------------------------------------------------------
Thu Apr 10 17:20:09 CEST 2014 - mc@suse.de

- fix release in specfile for SLE12 (bnc#872970)

-------------------------------------------------------------------
Fri Feb 07 12:53:56 CET 2014 - mc@suse.de

- version 5.11.33.1-1
- create rc links on SUSE distributions
- make reboot_in_progress a public function (bnc#859541)

-------------------------------------------------------------------
Mon Jan 13 09:35:06 CET 2014 - mc@suse.de

- version 5.11.32.2-1
- do not notify osad of a server which reboot is in progress
  (FATE#312591)

-------------------------------------------------------------------
Mon Dec 09 16:10:08 CET 2013 - mc@suse.de

- version 5.11.32.1-1
- switch to 2.1

-------------------------------------------------------------------
Thu Nov 28 16:21:13 CET 2013 - mc@suse.de

- version 5.10.41.10-1
- remove extraneous 'except'
- catch jabberd connection errors

-------------------------------------------------------------------
Fri Sep 27 10:01:57 CEST 2013 - mc@suse.de

- version 5.10.41.9-1
- require python-xml on SLE11 (bnc#838509)

-------------------------------------------------------------------
Fri Feb 08 11:17:14 CET 2013 - mc@suse.de

- version 5.10.41.8-1
- always commit the update

-------------------------------------------------------------------
Thu Nov 22 15:42:02 CET 2012 - jrenner@suse.de

- version 5.10.41.7-1
- osad requires config.getServerURL()

-------------------------------------------------------------------
Mon Oct 15 12:20:56 CEST 2012 - mc@suse.de

- version 5.10.41.6-1
- recompile python files only on SUSE

-------------------------------------------------------------------
Fri Sep 28 16:35:57 CEST 2012 - mc@suse.de

- version 5.10.41.5-1
- Set owner/group of config-defaults dir consistently (bnc#776377)
- recompile python files (bnc#776356)

-------------------------------------------------------------------
Mon May 14 10:56:20 CEST 2012 - mc@suse.de

- version 5.10.41.4-1
- no use of /var/lock/subsys/ anymore

-------------------------------------------------------------------
Fri Apr 27 16:55:29 CEST 2012 - mc@suse.de

- version 5.10.41.3-1
- rotate osa-dispatcher logfiles at 10M

-------------------------------------------------------------------
Fri Apr 20 15:41:28 CEST 2012 - mc@suse.de

- version 5.10.41.2-1
- prevent 'notifying clients' starvation
- Make osa-dispatcher use the hostname in the rhn.conf if present

-------------------------------------------------------------------
Thu Mar 22 16:14:48 CET 2012 - mc@suse.de

- osa-dispatcher: rotate logfiles as user www 
  (bnc#681984) CVE-2011-1550

-------------------------------------------------------------------
Wed Mar 21 17:14:46 CET 2012 - mc@suse.de

- version 5.10.41.1-1
- Bumping package version

-------------------------------------------------------------------
Fri Feb  3 15:44:17 CET 2012 - ug@suse.de

- changed permissions of /etc/rhn

-------------------------------------------------------------------
Tue Sep 13 11:47:54 CEST 2011 - mc@suse.de

- enable and start osad during installation (FATE#312379)

-------------------------------------------------------------------
Fri Aug 26 13:03:20 CEST 2011 - mc@suse.de

- start osa-dispatcher after oracle DB

-------------------------------------------------------------------
Thu Aug 11 15:11:07 CEST 2011 - iartarisi@suse.cz

- fix some imports after moving modules out of spacewalk.common

-------------------------------------------------------------------
Mon Jul  4 17:18:58 CEST 2011 - mc@suse.de

- require python-hashlib for SLE10

-------------------------------------------------------------------
Wed Jun 15 14:35:25 CEST 2011 - mc@suse.de

- ensure presence subscription works with standard
  jabberd setup.(bnc#695946)

-------------------------------------------------------------------
Mon Feb  7 15:31:07 CET 2011 - mc@suse.de

- add rc links (bnc#669894) 

-------------------------------------------------------------------
Wed Feb  2 17:27:07 CET 2011 - mc@suse.de

- fix build on RH4 

-------------------------------------------------------------------
Sun Jan 30 15:27:23 CET 2011 - mc@suse.de

- backport upstrem fixes

-------------------------------------------------------------------
Sat Jan 22 12:38:52 CET 2011 - mc@suse.de

- fix macros

-------------------------------------------------------------------
Thu Sep 16 08:28:50 CEST 2010 - mantel@suse.de

- fix missing prog.init.SUSE

-------------------------------------------------------------------
Wed Sep 15 13:11:04 CEST 2010 - mantel@suse.de

- cleanup specfile: remove excessive *init.SUSE Source statement

-------------------------------------------------------------------
Wed Sep 15 12:13:40 CEST 2010 - mantel@suse.de

- fix post-build-checks

-------------------------------------------------------------------
Tue Sep 14 17:31:29 CEST 2010 - mantel@suse.de

- Initial release of osad

-------------------------------------------------------------------
0707010000000D000081B40000000000000000000000015E282E5600005731000000000000000000000000000000000000001700000000mgr-osad/mgr-osad.spec#
# spec file for package mgr-osad
#
# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
# Copyright (c) 2008-2018 Red Hat, Inc.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.

# Please submit bugfixes or comments via https://bugs.opensuse.org/
#


# Old name and version+1 before switching to mgr-osad
%define oldname osad
%define oldversion 5.11.103
%global rhnroot /usr/share/rhn
%global rhnconf /etc/sysconfig/rhn
%global client_caps_dir /etc/sysconfig/rhn/clientCaps.d
%{!?fedora: %global sbinpath /sbin}%{?fedora: %global sbinpath %{_sbindir}}

%if 0%{?suse_version}
%global apache_group www
%global apache_user wwwrun
%global include_selinux_package 0
%else
%global apache_group apache
%global apache_user apache
%global include_selinux_package 1
%endif

%if 0%{?fedora} || 0%{?suse_version} > 1320 || 0%{?rhel} >= 8
%global build_py3   1
%global default_py3 1
%endif

%if ( 0%{?fedora} && 0%{?fedora} < 28 ) || ( 0%{?rhel} && 0%{?rhel} < 8 ) || 0%{?suse_version}
%global build_py2   1
%endif

%define pythonX %{?default_py3: python3}%{!?default_py3: python2}

Name:           mgr-osad
Summary:        Open Source Architecture Daemon
License:        GPL-2.0-only
Group:          System Environment/Daemons
Version:        4.1.2
Provides:       %{oldname} = %{oldversion}
Obsoletes:      %{oldname} < %{oldversion}
Release:        1%{?dist}
Url:            https://github.com/uyuni-project/uyuni
Source0:        https://github.com/spacewalkproject/spacewalk/archive/%{name}-%{version}.tar.gz
Source1:        %{name}-rpmlintrc
BuildRoot:      %{_tmppath}/%{name}-%{version}-build
%if 0%{?fedora} || 0%{?rhel} || 0%{?suse_version} >= 1210
BuildArch:      noarch
%endif
%if 0%{?fedora} > 26
BuildRequires:  perl-interpreter
%else
BuildRequires:  perl
%endif
Requires:       %{pythonX}-%{name} = %{version}-%{release}
Conflicts:      mgr-osa-dispatcher < %{version}-%{release}
Conflicts:      mgr-osa-dispatcher > %{version}-%{release}
BuildRequires:  uyuni-base-common
Requires(pre):  uyuni-base-common
%if 0%{?suse_version} >= 1210
BuildRequires:  systemd
%{?systemd_requires}
%endif
%if 0%{?suse_version}
# provides chkconfig on SUSE
Requires(post): aaa_base
Requires(preun): aaa_base
# to make chkconfig test work during build
BuildRequires:  sysconfig
BuildRequires:  syslog
%if 0%{?suse_version} < 1210
Requires:       %fillup_prereq
Requires:       %insserv_prereq
%endif
%else
%if 0%{?fedora} || 0%{?rhel} >= 7
Requires(post): chkconfig
Requires(preun): chkconfig
Requires(post): systemd-sysv
Requires(preun): systemd-sysv
Requires(post): systemd-units
Requires(preun): systemd-units
BuildRequires:  systemd-units
%else
Requires(post): chkconfig
Requires(preun): chkconfig
# This is for /sbin/service
Requires(preun): initscripts
%endif
%endif

%description
OSAD agent receives commands over jabber protocol from Spacewalk Server and
commands are instantly executed.

This package effectively replaces the behavior of rhnsd/rhn_check that
only poll the Spacewalk Server from time to time.

%if 0%{?build_py2}
%package -n python2-%{name}
Summary:        Open Source Architecture Daemon
Group:          System Environment/Daemons
Provides:       python-%{name} = %{oldversion}
Obsoletes:      python-%{name} < %{oldversion}
Provides:       python2-%{oldname} = %{oldversion}
Obsoletes:      python2-%{oldname} < %{oldversion}
Requires:       %{name} = %{version}-%{release}
Requires:       python
Requires:       python-jabberpy
Requires:       python2-mgr-osa-common = %{version}
Requires:       python2-rhn-client-tools >= 2.8.4
Requires:       rhnlib >= 2.8.3
Requires:       python2-uyuni-common-libs
%if 0%{?rhel} && 0%{?rhel} <= 5
Requires:       python-hashlib
%endif
BuildRequires:  python-devel

%description -n python2-%{name}
Python 2 specific files for %{name}
%endif

%if 0%{?build_py3}
%package -n python3-%{name}
Summary:        Open Source Architecture Daemon
Group:          System Environment/Daemons
Provides:       python3-%{oldname} = %{oldversion}
Obsoletes:      python3-%{oldname} < %{oldversion}
Requires:       %{name} = %{version}-%{release}
Requires:       python3
Requires:       python3-jabberpy
Requires:       python3-mgr-osa-common = %{version}
Requires:       python3-rhn-client-tools >= 2.8.4
Requires:       python3-rhnlib >= 2.8.3
Requires:       python3-uyuni-common-libs
BuildRequires:  python3-devel

%description -n python3-%{name}
Python 3 specific files for %{name}
%endif

%package -n python2-mgr-osa-common
Summary:        OSA common files
Group:          System Environment/Daemons
Requires:       python-jabberpy
Conflicts:      %{name} < %{version}-%{release}
Conflicts:      %{name} > %{version}-%{release}
Obsoletes:      osa-common < %{oldversion}
Provides:       osa-common = %{oldversion}
Obsoletes:      python2-osa-common < %{oldversion}
Provides:       python2-osa-common = %{oldversion}

%description -n python2-mgr-osa-common
Python 2 common files needed by mgr-osad and mgr-osa-dispatcher

%if 0%{?build_py3}
%package -n python3-mgr-osa-common
Summary:        OSA common files
Group:          System Environment/Daemons
Requires:       python3-jabberpy
Conflicts:      %{name} < %{version}-%{release}
Conflicts:      %{name} > %{version}-%{release}
Obsoletes:      osa-common < %{oldversion}
Provides:       osa-common = %{oldversion}
Obsoletes:      python3-osa-common < %{oldversion}
Provides:       python3-osa-common = %{oldversion}

%description -n python3-mgr-osa-common
Python 3 common files needed by mgr-osad and mgr-osa-dispatcher
%endif

%package -n mgr-osa-dispatcher
Summary:        OSA dispatcher
Group:          System Environment/Daemons
Obsoletes:      osa-dispatcher < %{oldversion}
Provides:       osa-dispatcher = %{oldversion}
Requires:       lsof
Requires:       %{pythonX}-mgr-osa-dispatcher = %{version}-%{release}
Requires:       spacewalk-backend-server >= 1.2.32
Conflicts:      %{name} < %{version}-%{release}
Conflicts:      %{name} > %{version}-%{release}
%if 0%{?suse_version} >= 1210
%{?systemd_requires}
%endif
%if 0%{?suse_version}
# provides chkconfig on SUSE
Requires(post): aaa_base
Requires(preun): aaa_base
%else
Requires(post): chkconfig
Requires(preun): chkconfig
# This is for /sbin/service
Requires(preun): initscripts
%endif

%description -n mgr-osa-dispatcher
OSA dispatcher is supposed to run on the Spacewalk server. It gets information
from the Spacewalk server that some command needs to be execute on the client;
that message is transported via jabber protocol to OSAD agent on the clients.

%package -n python2-mgr-osa-dispatcher
Summary:        OSA dispatcher
Group:          System Environment/Daemons
Obsoletes:      python2-osa-dispatcher < %{oldversion}
Provides:       python2-osa-dispatcher = %{oldversion}
%if 0%{?fedora} >= 28
BuildRequires:  python2-devel
Requires:       python2
%else
BuildRequires:  python-devel
Requires:       python
%endif
Requires:       python-jabberpy
Requires:       python2-mgr-osa-common = %{version}-%{release}

%description -n python2-mgr-osa-dispatcher
Python 2 specific files for osa-dispatcher.

%if 0%{?build_py3}
%package -n python3-mgr-osa-dispatcher
Summary:        OSA dispatcher
Group:          System Environment/Daemons
Obsoletes:      python3-osa-dispatcher < %{oldversion}
Provides:       python3-osa-dispatcher = %{oldversion}
BuildRequires:  python3-devel
Requires:       python3
Requires:       python3-jabberpy
Requires:       python3-mgr-osa-common = %{version}-%{release}

%description -n python3-mgr-osa-dispatcher
Python 3 specific files for osa-dispatcher.
%endif

%if 0%{?include_selinux_package}
%package -n mgr-osa-dispatcher-selinux
%global selinux_variants mls strict targeted
%global selinux_policyver %(sed -e 's,.*selinux-policy-\\([^/]*\\)/.*,\\1,' /usr/share/selinux/devel/policyhelp 2> /dev/null)
%global POLICYCOREUTILSVER 1.33.12-1

%global moduletype apps
%global modulename osa-dispatcher

Summary:        SELinux policy module supporting osa-dispatcher
Group:          System Environment/Base
Obsoletes:      osa-dispatcher-selinux < %{oldversion}
Provides:       osa-dispatcher-selinux = %{oldversion}
BuildRequires:  checkpolicy
BuildRequires:  hardlink
BuildRequires:  policycoreutils >= %{POLICYCOREUTILSVER}
BuildRequires:  selinux-policy-devel
Requires:       spacewalk-selinux

%if "%{selinux_policyver}" != ""
Requires:       selinux-policy >= %{selinux_policyver}
%endif
%if 0%{?rhel} == 5
Requires:       selinux-policy >= 2.4.6-114
%endif
Requires(post): /usr/sbin/semodule, %{sbinpath}/restorecon, /usr/sbin/selinuxenabled, /usr/sbin/semanage
Requires(postun): /usr/sbin/semodule, %{sbinpath}/restorecon, /usr/sbin/semanage, spacewalk-selinux
Requires:       mgr-osa-dispatcher

%description -n mgr-osa-dispatcher-selinux
SELinux policy module supporting osa-dispatcher.
%endif

%prep
%setup -q
%if 0%{?suse_version}
cp prog.init.SUSE prog.init
%endif
%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} > 5)
sed -i 's@^#!/usr/bin/python$@#!/usr/bin/python -s@' invocation.py
%endif

%build
make -f Makefile.osad all PYTHONPATH=%{python_sitelib}

%if 0%{?include_selinux_package}
%{__perl} -i -pe 'BEGIN { $VER = join ".", grep /^\d+$/, split /\./, "%{version}.%{release}"; } s!\@\@VERSION\@\@!$VER!g;' osa-dispatcher-selinux/%{modulename}.te
%if 0%{?fedora} || 0%{?rhel} >= 7
cat osa-dispatcher-selinux/%{modulename}.te.fedora17 >> osa-dispatcher-selinux/%{modulename}.te
%endif
%if 0%{?fedora} >= 26
cat osa-dispatcher-selinux/%{modulename}.te.fedora26 >> osa-dispatcher-selinux/%{modulename}.te
%endif
for selinuxvariant in %{selinux_variants}
do
    make -C osa-dispatcher-selinux NAME=${selinuxvariant} -f /usr/share/selinux/devel/Makefile
    mv osa-dispatcher-selinux/%{modulename}.pp osa-dispatcher-selinux/%{modulename}.pp.${selinuxvariant}
    make -C osa-dispatcher-selinux NAME=${selinuxvariant} -f /usr/share/selinux/devel/Makefile clean
done
%endif

%install
install -d $RPM_BUILD_ROOT%{rhnroot}
make -f Makefile.osad install PREFIX=$RPM_BUILD_ROOT ROOT=%{rhnroot} INITDIR=%{_initrddir} \
        PYTHONPATH=%{python_sitelib} PYTHONVERSION=%{python_version}
%if 0%{?build_py3}
make -f Makefile.osad install PREFIX=$RPM_BUILD_ROOT ROOT=%{rhnroot} INITDIR=%{_initrddir} \
        PYTHONPATH=%{python3_sitelib} PYTHONVERSION=%{python3_version}
sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|' $RPM_BUILD_ROOT/usr/sbin/osad-%{python3_version}
sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|' $RPM_BUILD_ROOT/usr/sbin/osa-dispatcher-%{python3_version}
%endif

%define default_suffix %{?default_py3:-%{python3_version}}%{!?default_py3:-%{python_version}}
ln -s osad%{default_suffix} $RPM_BUILD_ROOT/usr/sbin/osad
# osa-dispatcher is python2 even on Fedora
ln -s osa-dispatcher%{default_suffix} $RPM_BUILD_ROOT/usr/sbin/osa-dispatcher

mkdir -p %{buildroot}%{_var}/log/rhn
touch %{buildroot}%{_var}/log/osad
touch %{buildroot}%{_var}/log/rhn/osa-dispatcher.log

%if 0%{?fedora} || 0%{?rhel} > 6
sed -i 's/#LOGROTATE-3.8#//' $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/osa-dispatcher
%endif

%if 0%{?fedora} || 0%{?suse_version} >= 1210 || 0%{?rhel} >= 7
rm $RPM_BUILD_ROOT/%{_initrddir}/osad
rm $RPM_BUILD_ROOT/%{_initrddir}/osa-dispatcher
mkdir -p $RPM_BUILD_ROOT/%{_unitdir}
install -m 0644 osad.service $RPM_BUILD_ROOT/%{_unitdir}/
install -m 0644 osa-dispatcher.service $RPM_BUILD_ROOT/%{_unitdir}/
%endif

%if 0%{?include_selinux_package}
for selinuxvariant in %{selinux_variants}
  do
    install -d %{buildroot}%{_datadir}/selinux/${selinuxvariant}
    install -p -m 644 osa-dispatcher-selinux/%{modulename}.pp.${selinuxvariant} \
           %{buildroot}%{_datadir}/selinux/${selinuxvariant}/%{modulename}.pp
  done

# Install SELinux interfaces
install -d %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype}
install -p -m 644 osa-dispatcher-selinux/%{modulename}.if \
  %{buildroot}%{_datadir}/selinux/devel/include/%{moduletype}/%{modulename}.if

# Hardlink identical policy module packages together
/usr/sbin/hardlink -cv %{buildroot}%{_datadir}/selinux

# Install osa-dispatcher-selinux-enable which will be called in %%post
install -d %{buildroot}%{_sbindir}
install -p -m 755 osa-dispatcher-selinux/osa-dispatcher-selinux-enable %{buildroot}%{_sbindir}/osa-dispatcher-selinux-enable
%endif

%if ! 0%{?build_py2}
rm -rf $RPM_BUILD_ROOT/%{python_sitelib}/osad/osad*
rm -f $RPM_BUILD_ROOT/usr/sbin/osad-%{python_version}
%endif

mkdir -p %{buildroot}%{_var}/log/rhn
touch %{buildroot}%{_var}/log/osad
touch %{buildroot}%{_var}/log/rhn/osa-dispatcher.log

%if 0%{?suse_version}
%py_compile -O %{buildroot}/%{python_sitelib}
%if 0%{?build_py3}
%py3_compile -O %{buildroot}/%{python3_sitelib}
%endif
%endif

%if 0%{?suse_version}
# add rclinks
%if 0%{?suse_version} < 1210
ln -sf ../../etc/init.d/osad %{buildroot}%{_sbindir}/rcosad
ln -sf ../../etc/init.d/osa-dispatcher %{buildroot}%{_sbindir}/rcosa-dispatcher
%else
ln -s %{_sbindir}/service %{buildroot}%{_sbindir}/rcosad
ln -s %{_sbindir}/service %{buildroot}%{_sbindir}/rcosa-dispatcher
%endif
%endif

%{!?systemd_post: %global systemd_post() if [ $1 -eq 1 ] ; then /usr/bin/systemctl enable %%{?*} >/dev/null 2>&1 || : ; fi; }
%{!?systemd_preun: %global systemd_preun() if [ $1 -eq 0 ] ; then /usr/bin/systemctl --no-reload disable %%{?*} > /dev/null 2>&1 || : ; /usr/bin/systemctl stop %%{?*} >/dev/null 2>&1 || : ; fi; }
%{!?systemd_postun_with_restart: %global systemd_postun_with_restart() /usr/bin/systemctl daemon-reload >/dev/null 2>&1 || : ; if [ $1 -ge 1 ] ; then /usr/bin/systemctl try-restart %%{?*} >/dev/null 2>&1 || : ; fi; }

%post
ARG=$1
%if 0%{?suse_version} >= 1210
%service_add_post osad.service
if [ $ARG -eq 1 ] ; then
    # executed only in case of install
    /usr/bin/systemctl enable osad.service >/dev/null 2>&1
    /usr/bin/systemctl start osad.service ||:
fi
%else
if [ -f %{_sysconfdir}/init.d/osad ]; then
    /sbin/chkconfig --add osad ||:
fi
if [ -f %{_unitdir}/osad.service ]; then
    %systemd_post osad.service
    if [ "$1" = "2" ]; then
        # upgrade from old init.d
        if [ -L /etc/rc2.d/S97osad ]; then
            /usr/bin/systemctl enable osad.service >/dev/null 2>&1
        fi
        rm -f /etc/rc?.d/[SK]??osad
    fi
fi

# Fix the /var/log/osad permission BZ 836984
if [ -f %{_var}/log/osad ]; then
    /bin/chmod 600 %{_var}/log/osad
fi
if [ $ARG -eq 1 ] ; then
  # executed only in case of install
  /sbin/service osad start ||:
fi
%endif

%preun
%if 0%{?suse_version} >= 1210
%service_del_preun osad.service
%else
if [ $1 = 0 ]; then
    %if 0%{?fedora} || 0%{?rhel} >= 7
    %systemd_preun osad.service
    %else
    /sbin/service osad stop > /dev/null 2>&1
    /sbin/chkconfig --del osad
    %endif
fi
%endif

%postun
%if 0%{?fedora} || 0%{?rhel} >= 7
%systemd_postun osad.service
%else
%if 0%{?suse_version} >= 1210
%service_del_postun -n osad.service
%endif
%endif

%posttrans
if [ -x /usr/bin/systemctl ]; then
    (
        test "$YAST_IS_RUNNING" = instsys && exit 0
        test -e /var/lib/systemd/migrated/enable-osad && /usr/bin/systemctl enable osad.service >/dev/null 2>&1
        rm -f /var/lib/systemd/migrated/enable-osad 2> /dev/null
        test -e /var/lib/systemd/migrated/activate-osad && /usr/bin/systemctl start osad.service >/dev/null 2>&1
        rm -f /var/lib/systemd/migrated/activate-osad 2> /dev/null
        test -f /etc/sysconfig/services -a \
             -z "$DISABLE_RESTART_ON_UPDATE" && . /etc/sysconfig/services
        test "$DISABLE_RESTART_ON_UPDATE" = yes -o \
             "$DISABLE_RESTART_ON_UPDATE" = 1 && exit 0
        /usr/bin/systemctl try-restart osad.service
    ) || :
fi

%pre
%if 0%{?suse_version} >= 1210
%service_add_pre osad.service
%endif
if [ -x /usr/bin/systemctl ]; then
  (
    [ -d /var/lib/systemd/migrated ] || mkdir -p /var/lib/systemd/migrated || :
    /usr/bin/systemctl is-enabled osad.service >/dev/null 2>&1 && touch /var/lib/systemd/migrated/enable-osad
    /usr/bin/systemctl is-active osad.service >/dev/null 2>&1 && touch /var/lib/systemd/migrated/activate-osad
  ) ||:
fi

%if 0%{?suse_version} >= 1210
%pre -n mgr-osa-dispatcher
%service_add_pre osa-dispatcher.service

%postun -n mgr-osa-dispatcher
%service_del_postun osa-dispatcher.service

%endif

%post -n mgr-osa-dispatcher
%if 0%{?suse_version} >= 1210
%service_add_post osa-dispatcher.service
%else
if [ -f %{_sysconfdir}/init.d/osa-dispatcher ]; then
    /sbin/chkconfig --add osa-dispatcher ||:
fi
if [ -f %{_unitdir}/osa-dispatcher.service ]; then
    %systemd_post osa-dispatcher.service
    if [ "$1" = "2" ]; then
        # upgrade from old init.d
        if [ -L /etc/rc2.d/S86osa-dispatcher ]; then
            /usr/bin/systemctl enable osa-dispatcher.service >/dev/null 2>&1
        fi
        rm -f /etc/rc?.d/[SK]??osa-dispatcher
    fi
fi
%endif

%preun -n mgr-osa-dispatcher
%if 0%{?suse_version} >= 1210
%service_del_preun osa-dispatcher.service
%else
if [ $1 = 0 ]; then
    %if 0%{?fedora} || 0%{?rhel} >= 7
    %systemd_preun osa-dispatcher.service
    %else
    /sbin/service osa-dispatcher stop > /dev/null 2>&1
    /sbin/chkconfig --del osa-dispatcher
    %endif
fi
%endif

%if 0%{?include_selinux_package}
%post -n mgr-osa-dispatcher-selinux
if /usr/sbin/selinuxenabled ; then
   %{_sbindir}/osa-dispatcher-selinux-enable
fi

%posttrans -n mgr-osa-dispatcher-selinux
#this may be safely remove when BZ 505066 is fixed
if /usr/sbin/selinuxenabled ; then
  rpm -ql osa-dispatcher | xargs -n 1 /sbin/restorecon -rvi {}
  /sbin/restorecon -vvi /var/log/rhn/osa-dispatcher.log
fi

%postun -n mgr-osa-dispatcher-selinux
# Clean up after package removal
if [ $1 -eq 0 ]; then
  for selinuxvariant in %{selinux_variants}
    do
      /usr/sbin/semanage module -s ${selinuxvariant} -l > /dev/null 2>&1 \
        && /usr/sbin/semodule -s ${selinuxvariant} -r %{modulename} || :
    done
fi

rpm -ql osa-dispatcher | xargs -n 1 /sbin/restorecon -rvi {}
/sbin/restorecon -vvi /var/log/rhn/osa-dispatcher.log
%endif

%files
%defattr(-,root,root)
%{_sbindir}/osad
%config(noreplace) %{_sysconfdir}/sysconfig/rhn/osad.conf
%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sysconfig/rhn/osad-auth.conf
%config(noreplace) %{client_caps_dir}/*
%if 0%{?fedora} || 0%{?suse_version} >= 1210 || 0%{?rhel} >= 7
%{_unitdir}/osad.service
%else
%attr(755,root,root) %{_initrddir}/osad
%endif
%doc LICENSE
%config(noreplace) %attr(644,root,root) %{_sysconfdir}/logrotate.d/osad
%ghost %attr(600,root,root) %{_var}/log/osad
%if 0%{?suse_version}
%{_sbindir}/rcosad
# provide directories not owned by any package during build
%dir %{_sysconfdir}/sysconfig/rhn
%dir %{_sysconfdir}/sysconfig/rhn/clientCaps.d
%endif

%if 0%{?build_py2}
%files -n python2-%{name}
%defattr(-,root,root)
%attr(755,root,root) %{_sbindir}/osad-%{python_version}
%dir %{python_sitelib}/osad
%{python_sitelib}/osad/osad.py*
%{python_sitelib}/osad/osad_client.py*
%{python_sitelib}/osad/osad_config.py*
%endif

%if 0%{?build_py3}
%files -n python3-%{name}
%defattr(-,root,root)
%attr(755,root,root) %{_sbindir}/osad-%{python3_version}
%dir %{python3_sitelib}/osad
%{python3_sitelib}/osad/osad.py*
%{python3_sitelib}/osad/osad_client.py*
%{python3_sitelib}/osad/osad_config.py*
%dir %{python3_sitelib}/osad/__pycache__
%{python3_sitelib}/osad/__pycache__/osad.*
%{python3_sitelib}/osad/__pycache__/osad_client.*
%{python3_sitelib}/osad/__pycache__/osad_config.*
%endif

%files -n mgr-osa-dispatcher
%defattr(0644,root,root,0755)
%{_sbindir}/osa-dispatcher
%config(noreplace) %{_sysconfdir}/sysconfig/osa-dispatcher
%config(noreplace) %{_sysconfdir}/logrotate.d/osa-dispatcher
%{rhnroot}/config-defaults/rhn_osa-dispatcher.conf
%dir %{_sysconfdir}/rhn/tns_admin
%dir %{_sysconfdir}/rhn/tns_admin/osa-dispatcher
%config(noreplace) %{_sysconfdir}/rhn/tns_admin/osa-dispatcher/sqlnet.ora
%if 0%{?fedora} || 0%{?suse_version} >= 1210 || 0%{?rhel} >= 7
%{_unitdir}/osa-dispatcher.service
%else
%attr(755,root,root) %{_initrddir}/osa-dispatcher
%endif
%attr(770,root,%{apache_group}) %dir %{_var}/log/rhn/oracle
%attr(770,root,root) %dir %{_var}/log/rhn/oracle/osa-dispatcher
%doc LICENSE
%ghost %attr(640,%{apache_user},root) %{_var}/log/rhn/osa-dispatcher.log
%if 0%{?suse_version}
%{_sbindir}/rcosa-dispatcher
%dir %{rhnroot}
%dir %{_sysconfdir}/rhn/tns_admin
%attr(770,root,%{apache_group}) %dir %{_var}/log/rhn
%endif

%files -n python2-mgr-osa-dispatcher
%defattr(-,root,root)
%attr(755,root,root) %{_sbindir}/osa-dispatcher-%{python_version}
%dir %{python_sitelib}/osad
%{python_sitelib}/osad/osa_dispatcher.py*
%{python_sitelib}/osad/dispatcher_client.py*

%if 0%{?build_py3}
%files -n python3-mgr-osa-dispatcher
%defattr(-,root,root)
%attr(755,root,root) %{_sbindir}/osa-dispatcher-%{python3_version}
%dir %{python3_sitelib}/osad
%{python3_sitelib}/osad/osa_dispatcher.py*
%{python3_sitelib}/osad/dispatcher_client.py*
%{python3_sitelib}/osad/__pycache__/osa_dispatcher.*
%{python3_sitelib}/osad/__pycache__/dispatcher_client.*
%endif

%files -n python2-mgr-osa-common
%defattr(-,root,root)
%{python_sitelib}/osad/__init__.py*
%{python_sitelib}/osad/jabber_lib.py*
%{python_sitelib}/osad/rhn_log.py*

%if 0%{?build_py3}
%files -n python3-mgr-osa-common
%defattr(-,root,root)
%{python3_sitelib}/osad/__init__.py*
%{python3_sitelib}/osad/jabber_lib.py*
%{python3_sitelib}/osad/rhn_log.py*
%{python3_sitelib}/osad/__pycache__/__init__.*
%{python3_sitelib}/osad/__pycache__/jabber_lib.*
%{python3_sitelib}/osad/__pycache__/rhn_log.*
%endif

%if 0%{?include_selinux_package}
%files -n mgr-osa-dispatcher-selinux
%defattr(-,root,root)
%doc osa-dispatcher-selinux/%{modulename}.fc
%doc osa-dispatcher-selinux/%{modulename}.if
%doc osa-dispatcher-selinux/%{modulename}.te
%{_datadir}/selinux/*/%{modulename}.pp
%{_datadir}/selinux/devel/include/%{moduletype}/%{modulename}.if
%doc LICENSE
%attr(0755,root,root) %{_sbindir}/osa-dispatcher-selinux-enable
%endif

%changelog
0707010000000E000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000002000000000mgr-osad/osa-dispatcher-selinux0707010000000F000081B40000000000000000000000015E282E56000002F4000000000000000000000000000000000000003E00000000mgr-osad/osa-dispatcher-selinux/osa-dispatcher-selinux-enable#!/bin/bash

# make sure osa_dispatcher_upstream_notif_server_port_t has been removed
/usr/sbin/semanage port -ln \
  | awk -F'[ ,]+' '/osa_dispatcher_upstream_notif_server_port_t +tcp / {for (n=3;n<=NF;n++) {print  $n}}' \
  | while read port ; do
      /usr/sbin/semanage port -d -t osa_dispatcher_upstream_notif_server_port_t -p tcp $port || :
    done

# Install SELinux policy modules
for selinuxvariant in mls strict targeted
  do
    /usr/sbin/semanage module -l -S ${selinuxvariant} > /dev/null 2>&1 \
      && /usr/sbin/semodule -s ${selinuxvariant} -i \
        /usr/share/selinux/${selinuxvariant}/osa-dispatcher.pp || :
  done

rpm -ql osa-dispatcher | xargs -n 1 /sbin/restorecon -rvi {}
/sbin/restorecon -vvi /var/log/rhn/osa-dispatcher.log

07070100000010000081B40000000000000000000000015E282E5600000164000000000000000000000000000000000000003200000000mgr-osad/osa-dispatcher-selinux/osa-dispatcher.fc/usr/sbin/osa-dispatcher(-\d.\d)?	gen_context(system_u:object_r:osa_dispatcher_exec_t,s0)
/var/log/rhn/osa-dispatcher\.log	gen_context(system_u:object_r:osa_dispatcher_log_t,s0)
/var/log/rhn/oracle/osa-dispatcher	gen_context(system_u:object_r:osa_dispatcher_log_t,s0)
/var/run/osa-dispatcher\.pid	gen_context(system_u:object_r:osa_dispatcher_var_run_t,s0)
07070100000011000081B40000000000000000000000015E282E5600000021000000000000000000000000000000000000003200000000mgr-osad/osa-dispatcher-selinux/osa-dispatcher.if## This file is empty (for now).
07070100000012000081B40000000000000000000000015E282E5600000BF4000000000000000000000000000000000000003200000000mgr-osad/osa-dispatcher-selinux/osa-dispatcher.tepolicy_module(osa-dispatcher,@@VERSION@@)

require {
	type var_log_t;
	type spacewalk_log_t;
	type httpd_sys_content_t;
	type jabber_client_port_t;
	type inaddr_any_node_t;
	type postgresql_port_t;
}

type osa_dispatcher_t;
type osa_dispatcher_exec_t;
init_daemon_domain(osa_dispatcher_t,osa_dispatcher_exec_t)
allow init_t osa_dispatcher_exec_t:lnk_file read;
allow osa_dispatcher_t osa_dispatcher_exec_t:lnk_file { getattr read };

type osa_dispatcher_log_t;
logging_log_file(osa_dispatcher_log_t)

type osa_dispatcher_var_run_t;
files_pid_file(osa_dispatcher_var_run_t)
files_pid_filetrans(osa_dispatcher_t,osa_dispatcher_var_run_t,file)
create_files_pattern(osa_dispatcher_t,var_run_t,osa_dispatcher_var_run_t)
write_files_pattern(osa_dispatcher_t,var_run_t,osa_dispatcher_var_run_t)

files_read_etc_files(osa_dispatcher_t)
libs_use_ld_so(osa_dispatcher_t)
libs_use_shared_libs(osa_dispatcher_t)

miscfiles_read_localization(osa_dispatcher_t)
corecmd_exec_bin(osa_dispatcher_t)

kernel_read_system_state(osa_dispatcher_t)

files_read_usr_files(osa_dispatcher_t)

dev_read_urand(osa_dispatcher_t)

files_search_var(osa_dispatcher_t)
allow osa_dispatcher_t var_log_t:dir search_dir_perms;
allow osa_dispatcher_t spacewalk_log_t:dir search_dir_perms;
# We cannot use logging_log_filetrans here because /var/log/rhn has
# type spacewalk_log_t.
filetrans_pattern(osa_dispatcher_t,spacewalk_log_t,osa_dispatcher_log_t,{ file dir })
manage_files_pattern(osa_dispatcher_t,osa_dispatcher_log_t,osa_dispatcher_log_t)

init_rw_utmp(osa_dispatcher_t)

read_files_pattern(osa_dispatcher_t,httpd_sys_content_t,httpd_sys_content_t)

allow osa_dispatcher_t self:fifo_file rw_fifo_file_perms;

gen_require(`
	type lo_node_t;
')
allow osa_dispatcher_t lo_node_t:udp_socket node_bind;
corenet_udp_bind_all_nodes(osa_dispatcher_t)
corenet_udp_bind_generic_node(osa_dispatcher_t)

sysnet_dns_name_resolve(osa_dispatcher_t)

allow osa_dispatcher_t jabber_client_port_t:tcp_socket name_connect;
allow osa_dispatcher_t postgresql_port_t:tcp_socket name_connect;

corenet_all_recvfrom_unlabeled(osa_dispatcher_t)
corenet_all_recvfrom_netlabel(osa_dispatcher_t)

allow osa_dispatcher_t self:process { setpgid setsched getsched };
allow osa_dispatcher_t self:capability { sys_nice chown fowner sys_tty_config };

allow osa_dispatcher_t inaddr_any_node_t:tcp_socket node_bind;
allow osa_dispatcher_t osa_dispatcher_t:tcp_socket listen;

files_list_tmp(osa_dispatcher_t)

optional_policy(`
	tunable_policy(`allow_ypbind',`
		nis_use_ypbind_uncond(osa_dispatcher_t)
	')
')

optional_policy(`
	gen_require(`
		type oracle_port_t;
	')
allow osa_dispatcher_t oracle_port_t:tcp_socket name_connect;
')

postgresql_stream_connect(osa_dispatcher_t)

kernel_read_kernel_sysctls(osa_dispatcher_t)
dev_list_sysfs(osa_dispatcher_t)
dev_read_sysfs(osa_dispatcher_t)

require {
	class netlink_route_socket { write getattr read bind create nlmsg_read };
}

allow osa_dispatcher_t self:netlink_route_socket { write getattr read bind create nlmsg_read };

allow osa_dispatcher_t self:process execmem;
07070100000013000081B40000000000000000000000015E282E5600000023000000000000000000000000000000000000003B00000000mgr-osad/osa-dispatcher-selinux/osa-dispatcher.te.fedora17auth_read_passwd(osa_dispatcher_t)
07070100000014000081B40000000000000000000000015E282E560000009B000000000000000000000000000000000000003B00000000mgr-osad/osa-dispatcher-selinux/osa-dispatcher.te.fedora26sssd_read_public_files(osa_dispatcher_t)
sssd_run_stream_connect(osa_dispatcher_t)
sssd_search_lib(osa_dispatcher_t)
sssd_stream_connect(osa_dispatcher_t)
07070100000015000081B40000000000000000000000015E282E56000001AF000000000000000000000000000000000000002000000000mgr-osad/osa-dispatcher.service[Unit]
Description=OSA Dispatcher daemon
After=syslog.target network.target jabberd.service
BindsTo=jabberd.service
Requires=spacewalk-wait-for-jabberd.service

[Service]
Type=forking
EnvironmentFile=-/etc/sysconfig/osa-dispatcher
PIDFile=/var/run/osa-dispatcher.pid
ExecStart=/usr/sbin/osa-dispatcher --pid-file /var/run/osa-dispatcher.pid
ExecStartPre=/bin/rm -f /var/run/osa-dispatcher.pid

[Install]
WantedBy=multi-user.target
07070100000016000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000001300000000mgr-osad/osad-conf07070100000017000081B40000000000000000000000015E282E56000001AD000000000000000000000000000000000000001C00000000mgr-osad/osad-conf/Makefile# Makefile for installation of the RHN server configuration files
#

# what is the backend top dir
TOP	= ..

INSTALL_FILES	= $(wildcard *.conf)
INSTALL_DEST	= /etc/sysconfig/rhn

CAPS            = osad
CAPS_DIR        = /etc/sysconfig/rhn/clientCaps.d
EXTRA_DIRS      = $(CAPS_DIR)

include $(TOP)/Makefile.defs

install :: install-caps

install-caps :: $(CAPS) $(PREFIX)$(CAPS_DIR)
	$(INSTALL_DATA) $(CAPS) $(PREFIX)$(CAPS_DIR)
07070100000018000081B40000000000000000000000015E282E5600000023000000000000000000000000000000000000001800000000mgr-osad/osad-conf/osadosad.ping(1)=1
osad.rhn_check(1)=1
07070100000019000081B40000000000000000000000015E282E5600000028000000000000000000000000000000000000002200000000mgr-osad/osad-conf/osad-auth.conf# Automatically generated. Do not edit!
0707010000001A000081B40000000000000000000000015E282E56000006F9000000000000000000000000000000000000001D00000000mgr-osad/osad-conf/osad.conf[osad]

# don't change this
systemid = /etc/sysconfig/rhn/systemid

# increase for debugging output
debug_level = 0

# don't change this... used in substitutions below.
# if you get rid of the '%(server_handler)s' bits below,
# the *MUST* be replaced with this value...
server_handler = /XMLRPC

# Protocol to talk upstream
proto = https

# to use a server other than what up2date is configured to use,
# do something along the lines of:
# server_url = https://some.example.com%(server_handler)s
# server_url = http://another.example.net:8080%(server_handler)s
# server_url = https://yet.another.example.org:8081/XMLRPC
server_url = %(proto)s://%(server_name)s%(server_handler)s

# the following fields are inherited from up2date's configuration,
# but are overridable in this file

# enableProxy = 1
# enableProxyAuth = 1
# httpProxy = some.proxy.example.com:3030
# proxyUser = proxy_user_name
# proxyPassword = proxy_password

# Use a different certificate from what up2date is using
# This should point to the satellite certificate for
# server_name
osa_ssl_cert =

logfile = /var/log/osad

max_time_drift = 120

run_rhn_check = 1

# Default command to run when asked by the dispatcher
rhn_check_command = /usr/sbin/rhn_check

# By default we only use the first jabber server.
# Turn the enable_failover option to 1 if you want the connections
# to try Satellite's jabberd if Proxy's is not available.
enable_failover = 0

# Enable kernel keepalive timer on the osad client side socket
# in case the satellite/proxy side socket is closed without osad realising it
# After 'tcp_keepalive_timeout' seconds the kernel will probe the connection
# After 'tcp_keepalive_count' unsuccessful probes, the kernel will close the connection
tcp_keepalive_timeout = 1800
tcp_keepalive_count = 3
0707010000001B000081B40000000000000000000000015E282E5600000106000000000000000000000000000000000000001600000000mgr-osad/osad.service[Unit]
Description=OSAD daemon
After=syslog.target network.target

[Service]
Type=forking
KillMode=process
EnvironmentFile=-/etc/sysconfig/osad
PIDFile=/var/run/osad.pid
ExecStart=/usr/sbin/osad --pid-file /var/run/osad.pid

[Install]
WantedBy=multi-user.target
0707010000001C000081FD0000000000000000000000015E282E5600000711000000000000000000000000000000000000001300000000mgr-osad/prog.init#!/bin/sh
#
# @@PROG@@     This shell script starts the @@PROG@@ daemon
#
# chkconfig: - 86 04
# description: @@PROG@@ is a daemon used by the Spacewalk
#
### BEGIN INIT INFO
# Provides: @@PROG@@
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: This shell script starts the @@PROG@@ daemon
# Description:       @@PROG@@ is a daemon used by the Spacewalk
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Source prog-specific configuration
if [ -f /etc/sysconfig/@@PROG@@ ]; then
	. /etc/sysconfig/@@PROG@@
fi

# Check that networking is up.
[ "${NETWORKING}" = "no" ] && exit 0

RETVAL=0
prog="@@PROG@@"
script="/usr/sbin/@@PROG@@"

[ -f $script ] || exit 0

start() {
    # Start daemon
    echo -n "Starting $prog: "
    if [ "$prog" == "osa-dispatcher" ]; then
        /usr/sbin/spacewalk-startup-helper wait-for-jabberd
    fi
    daemon $script --pid-file /var/run/$prog.pid
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
    return $RETVAL
}


stop() {
    # Stop daemon
    echo -n "Shutting down $prog: "
    killproc $prog
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
    return $RETVAL
}

# See how we were called.
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart|reload)
        stop
        start
        ;;
    status)
        status "$script"
        RETVAL=$?
        ;;
    condrestart)
        if status "$script" >/dev/null; then
            stop
            start
        fi
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|reload|status|condrestart}"
        exit 1
esac

exit $RETVAL

0707010000001D000081FD0000000000000000000000015E282E560000062F000000000000000000000000000000000000001800000000mgr-osad/prog.init.SUSE#!/bin/sh
#
# @@PROG@@     This shell script starts the @@PROG@@ daemon
#
# chkconfig: - 81 04
# description: @@PROG@@ is a daemon used by the Spacewalk
#
### BEGIN INIT INFO
# Provides: @@PROG@@
# Required-Start: $syslog $local_fs $network $remote_fs
# Required-Stop: $syslog $local_fs $network $remote_fs
# Should-Start: oracle-xe oracle jabberd postgresql
# Should-Stop:  oracle-xe oracle jabberd postgresql
# Default-Start:  3 5
# Default-Stop: 0 1 6
# Short-Description: This shell script starts the @@PROG@@ daemon
# Description:       @@PROG@@ is a daemon used by the Spacewalk
### END INIT INFO

# Source function library.
. /etc/rc.status

prog="@@PROG@@"
script="/usr/sbin/@@PROG@@"
PIDFILE="/var/run/@@PROG@@.pid"

[ -f $script ] || exit 0

start() {
    # Start daemon
    echo -n "Starting $prog: "
    startproc -p $PIDFILE $script --pid-file="$PIDFILE"
    rc_status -v
}


stop() {
    # Stop daemon
    echo -n "Shutting down $prog: "
    killproc -p $PIDFILE $script
    rc_status -v
}

# See how we were called.
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart|reload)
        stop
        start
        ;;
    status)
        echo -n "Checking for service $prog: "
        checkproc -p $PIDFILE $script
        rc_status -v
        ;;
    condrestart|try-restart)
        $0 status
        if test $? = 0; then
            $0 restart
        else
            rc_reset
        fi
        rc_status
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|reload|status|condrestart}"
        exit 1
esac

rc_exit
0707010000001E000081B40000000000000000000000015E282E5600001396000000000000000000000000000000000000001200000000mgr-osad/pylintrc# mgr-osad package pylint configuration

[MASTER]

# Profiled execution.
profile=no

# Pickle collected data for later comparisons.
persistent=no


[MESSAGES CONTROL]

# Disable the message(s) with the given id(s).


disable=I0011,
	C0302,
	C0111,
	R0801,
	R0902,
	R0903,
	R0904,
	R0912,
	R0913,
	R0914,
	R0915,
	R0921,
	R0922,
	W0142,
	W0403,
	W0603,
	C1001,
	W0121,
	useless-else-on-loop,
	bad-whitespace,
	unpacking-non-sequence,
	superfluous-parens,
	cyclic-import,
	redefined-variable-type,
	no-else-return,

        # Uyuni disabled
	E0203,
	E0611,
	E1101,
	E1102

# list of disabled messages:
#I0011: 62: Locally disabling R0201
#C0302:  1: Too many lines in module (2425)
#C0111:  1: Missing docstring
#R0902: 19:RequestedChannels: Too many instance attributes (9/7)
#R0903:  Too few public methods
#R0904: 26:Transport: Too many public methods (22/20)
#R0912:171:set_slots_from_cert: Too many branches (59/20)
#R0913:101:GETServer.__init__: Too many arguments (11/10)
#R0914:171:set_slots_from_cert: Too many local variables (38/20)
#R0915:171:set_slots_from_cert: Too many statements (169/50)
#W0142:228:MPM_Package.write: Used * or ** magic
#W0403: 28: Relative import 'rhnLog', should be 'backend.common.rhnLog'
#W0603: 72:initLOG: Using the global statement
# for pylint-1.0 we also disable
#C1001: 46, 0: Old-style class defined. (old-style-class)
#W0121: 33,16: Use raise ErrorClass(args) instead of raise ErrorClass, args. (old-raise-syntax)
#W:243, 8: Else clause on loop without a break statement (useless-else-on-loop)
# pylint-1.1 checks
#C:334, 0: No space allowed after bracket (bad-whitespace)
#W:162, 8: Attempting to unpack a non-sequence defined at line 6 of (unpacking-non-sequence)
#C: 37, 0: Unnecessary parens after 'not' keyword (superfluous-parens)
#C:301, 0: Unnecessary parens after 'if' keyword (superfluous-parens)

[REPORTS]

# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=parseable

# Include message's id in output
include-ids=yes

# Tells whether to display a full report or only the messages
reports=yes

# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"

[VARIABLES]

# A regular expression matching names used for dummy variables (i.e. not used).
dummy-variables-rgx=_|dummy


[BASIC]

# Regular expression which should only match correct module names
#module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
module-rgx=([a-zA-Z_][a-zA-Z0-9_]+)$

# Regular expression which should only match correct module level names
const-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$

# Regular expression which should only match correct class names
class-rgx=[a-zA-Z_][a-zA-Z0-9_]+$

# Regular expression which should only match correct function names
function-rgx=[a-z_][a-zA-Z0-9_]{,42}$

# Regular expression which should only match correct method names
method-rgx=[a-z_][a-zA-Z0-9_]{,42}$

# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-zA-Z0-9_]{,30}$

# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-zA-Z0-9_]{,30}$

# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-zA-Z0-9_]{,30}$

# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$

# Regular expression which should only match correct class sttribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,42}|(__.*__))$

# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_

# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata

# List of builtins function names that should not be used, separated by a comma
bad-functions=apply,input


[DESIGN]

# Maximum number of arguments for function / method
max-args=10

# Maximum number of locals for function / method body
max-locals=20

# Maximum number of return / yield for function / method body
max-returns=6

# Maximum number of branch for function / method body
max-branchs=20

# Maximum number of statements in function / method body
max-statements=50

# Maximum number of parents for a class (see R0901).
max-parents=7

# Maximum number of attributes for a class (see R0902).
max-attributes=7

# Minimum number of public methods for a class (see R0903).
min-public-methods=1

# Maximum number of public methods for a class (see R0904).
max-public-methods=20


[CLASSES]


[FORMAT]

# Maximum number of characters on a single line.
max-line-length=120

# Maximum number of lines in a module
max-module-lines=1000

# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string='    '


[MISCELLANEOUS]

# List of note tags to take in consideration, separated by a comma.
notes=
0707010000001F000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000001200000000mgr-osad/rhn-conf07070100000020000081B40000000000000000000000015E282E56000000DC000000000000000000000000000000000000001B00000000mgr-osad/rhn-conf/Makefile# Makefile for installation of the RHN server configuration files
#

# what is the backend top dir
TOP	= ..

INSTALL_FILES	= $(wildcard *.conf)
INSTALL_DEST	= /usr/share/rhn/config-defaults

include $(TOP)/Makefile.defs
07070100000021000081B40000000000000000000000015E282E5600000164000000000000000000000000000000000000002A00000000mgr-osad/rhn-conf/rhn_osa-dispatcher.conf# Default log file
log_file        = /var/log/rhn/osa-dispatcher.log
# SSL cert
osa_ssl_cert  = /usr/share/rhn/RHNS-OSA-CERT
# Jabber server to connect to
jabber_server   = jabberserver.example.org:5234

poll_interval = 5

# number of systems that can run actions until
# osad-dispatcher stops notifying more clients.
# default: 100
notify_threshold = 100
07070100000022000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000000D00000000mgr-osad/src07070100000023000081B40000000000000000000000015E282E560000010E000000000000000000000000000000000000001600000000mgr-osad/src/Makefile# Makefile for spacewalk backend
#

# what is the backend top dir
TOP     = ..

# Specific stuff
SUBDIR		= osad
FILES           = __init__ dispatcher_client jabber_lib osa_dispatcher \
                  osad_client osad osad_config rhn_log

include $(TOP)/Makefile.defs
07070100000024000081B40000000000000000000000015E282E5600000266000000000000000000000000000000000000001900000000mgr-osad/src/__init__.py#
# Copyright (c) 2008--2013 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

07070100000025000081B40000000000000000000000015E282E5600001E80000000000000000000000000000000000000002200000000mgr-osad/src/dispatcher_client.py#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import time
import sys
from spacewalk.common.rhnLog import log_debug
from spacewalk.server import rhnSQL

try: # python 2
    import jabber_lib
except ImportError: # python 3
    from osad import jabber_lib

class Client(jabber_lib.JabberClient):
    def __init__(self, *args, **kwargs):
        jabber_lib.JabberClient.__init__(self, *args, **kwargs)
        self.username = None
        self.resource = None
        #self.DEBUG = jabber_lib.my_debug

    def start(self, username, password, resource):
        # XXX find a better name for this function
        log_debug(2)
        self.auth(username, password, resource)
        log_debug(3, "Authenticated")
        self.username = username
        self.resource = resource
        self.jid = "%s@%s/%s" % (self.username, self._host, self.resource)

        self.username = username
        self.resource = resource

    def _add_jid_resource(self, jid, resource):
        if not isinstance(jid, jabber_lib.jabber.JID) or jid.resource:
            return jid
        return jabber_lib.jabber.JID(str(jid) + '/' + resource)

    def _fix_jid(self, jid):
        return self._add_jid_resource(jid, 'osad')

    def _check_signature(self, stanza, actions=None):
        # Do we have this client in the table?
        jid = stanza.getFrom()
        if jid is None:
            log_debug(3, 'no from')
            return None
        jid = str(self._fix_jid(jid))
        # Look for a <x> child that has our namespace
        xes = stanza.getTags('x')
        for x in xes:
            if x.getNamespace() != jabber_lib.NS_RHN_SIGNED:
                continue
            break
        else: #for
            log_debug(1, "No signature node found in stanza")
            return None
        # We now have our signature node
        x_client_id = x.getAttr('client-id')

        row = lookup_client_by_name(x_client_id)
        if not row:
            log_debug(3, 'no client found', x_client_id)
            if self.debug_level > 5:
                raise Exception(1)
            return None
        shared_key = row['shared_key']
        timestamp = x.getAttr('timestap')
        serial = x.getAttr('serial')
        action = x.getAttr('action')

        if actions and action not in actions:
            log_debug(1, "action %s not allowed" % action)
            return None

        attrs = {
            'client-id'     : x_client_id,
            'timestamp'     : x.getAttr('timestamp'),
            'serial'        : x.getAttr('serial'),
            'action'        : x.getAttr('action'),
            'jid'           : jid,
        }
        signing_comps = ['client-id', 'timestamp', 'serial', 'action', 'jid']
        args = [shared_key, self.jid]
        for sc in signing_comps:
            args.append(attrs[sc])

        log_debug(4, "Signature args", args)
        signature = jabber_lib.sign(*args)
        x_signature = x.getAttr('signature')
        if signature != x_signature:
            log_debug(1, "Signatures do not match", signature, x_signature)
            if self.debug_level > 5:
                raise Exception(1)
            return None
        # Happy joy
        return x

    def _create_signature(self, jid, action):
        row = lookup_client_by_jid(jid)
        if not row:
            log_debug(3, 'no client found for jid', jid)
            if self.debug_level > 5:
                raise Exception(1)
            return None
        full_jid = row['jabber_id']
        shared_key = row['shared_key']
        attrs = {
            'timestamp'     : int(time.time()),
            'serial'        : self.get_unique_id(),
            'action'        : action,
            'jid'           : self.jid,
        }
        signing_comps = ['timestamp', 'serial', 'action', 'jid']
        args = [shared_key, full_jid]
        for sc in signing_comps:
            args.append(attrs[sc])

        log_debug(4, "Signature args", args)
        attrs['signature'] = jabber_lib.sign(*args)

        x = jabber_lib.jabber.xmlstream.Node('x')
        x.setNamespace(jabber_lib.NS_RHN_SIGNED)
        for k, v in attrs.items():
            x.putAttr(k, v)
        return x

    def _message_callback(self, client, stanza):
        log_debug(4)
        assert stanza.getName() == 'message'

        # Actions we know how to react to
        actions = [
            jabber_lib.NS_RHN_MESSAGE_RESPONSE_CHECKIN,
            jabber_lib.NS_RHN_MESSAGE_RESPONSE_PING,
        ]
        sig = self._check_signature_from_message(stanza, actions)
        if not sig:
            return

        self.update_client_message_received(stanza.getFrom())

        action = sig.getAttr('action')
        if action == jabber_lib.NS_RHN_MESSAGE_RESPONSE_PING:
            log_debug(1, 'Ping response')
            # XXX
            return

    def ping_clients(self, clients):
        for client in clients:
            jid = client['jabber_id']
            if jid is None:
                continue
            self.send_message(jid, jabber_lib.NS_RHN_MESSAGE_REQUEST_PING)

    def set_jid_available(self, jid):
        jabber_lib.JabberClient.set_jid_available(self, jid)
        self._set_state(jid, self._get_push_state_id('online'))

    def set_jid_unavailable(self, jid):
        jabber_lib.JabberClient.set_jid_unavailable(self, jid)
        self._set_state(jid, self._get_push_state_id('offline'))

    _query_set_state = rhnSQL.Statement("""
        update rhnPushClient
           set state_id = :state_id,
               last_ping_time = NULL,
               next_action_time = NULL
         where jabber_id = :jid
    """)
    def _set_state(self, jid, state_id):
        h = rhnSQL.prepare(self._query_set_state)
        h.execute(state_id=state_id, jid=str(jid))
        rhnSQL.commit()

    def _get_push_state_id(self, state):
        t = rhnSQL.Table('rhnPushClientState', 'label')
        row = t[state]
        assert row is not None
        return row['id']

    _query_update_client_message_received = rhnSQL.Statement("""
        update rhnPushClient
           set state_id = :state_id,
               last_message_time = current_timestamp,
               last_ping_time = NULL,
               next_action_time = NULL
         where jabber_id = :jid
    """)
    def update_client_message_received(self, jid):
        jid = str(jid)
        state_id = self._get_push_state_id('online')
        h = rhnSQL.prepare(self._query_update_client_message_received)
        ret = h.execute(jid=jid, state_id=state_id)
        rhnSQL.commit()

class InvalidClientError(Exception):
    pass

def lookup_client_by_name(client_name):
    client_name = str(client_name)
    t = rhnSQL.Table('rhnPushClient', 'name')
    row = t[client_name]
    if row is None:
        raise InvalidClientError(client_name)
    return row

def lookup_client_by_jid(jid):
    if not isinstance(jid, jabber_lib.jabber.JID):
        jid = jabber_lib.jabber.JID(jid)

    if not jid.getResource():
        # add the resource so we can find the guy in our table
        jid.setResource('osad')

    jid = str(jid)
    t = rhnSQL.Table('rhnPushClient', 'jabber_id')
    row = t[jid]
    if row is None:
        raise InvalidClientError(jid)
    return row
07070100000026000081B40000000000000000000000015E282E560000CFB4000000000000000000000000000000000000001B00000000mgr-osad/src/jabber_lib.py#
# Copyright (c) 2008--2017 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import os
import hashlib
import sys
import time
import select
import socket
import random
import fnmatch
from optparse import OptionParser, Option
import traceback
from rhn import SSL

try: # python 2
    from rhn_log import log_debug, log_error
except ImportError: # python 3
    from osad.rhn_log import log_debug, log_error

from uyuni.common.usix import raise_with_tb
from rhn.i18n import bstr

import warnings
try:
    warnings.filterwarnings("ignore", category=DeprecationWarning)
    import jabber
finally:
    warnings.resetwarnings()

NS_RHN = "http://jabber.rhn.redhat.com/jabber"
NS_RHN_SIGNED = "%s/signed" % NS_RHN
NS_RHN_PRESENCE_SUBSCRIBE = "%s/presence/subscribe" % NS_RHN
NS_RHN_PRESENCE_SUBSCRIBED = "%s/presence/subscribed" % NS_RHN
NS_RHN_PRESENCE_UNSUBSCRIBE = "%s/presence/unsubscribe" % NS_RHN
NS_RHN_MESSAGE_REQUEST_CHECKIN = "%s/message/request/checkin" % NS_RHN
NS_RHN_MESSAGE_RESPONSE_CHECKIN = "%s/message/response/checkin" % NS_RHN
NS_RHN_MESSAGE_REQUEST_PING = "%s/message/request/ping" % NS_RHN
NS_RHN_MESSAGE_RESPONSE_PING = "%s/message/response/ping" % NS_RHN

NS_STARTTLS = 'urn:ietf:params:xml:ns:xmpp-tls'
NS_STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas"

# The class that starts everything
class Runner(object):
    option_parser = OptionParser
    option = Option

    client_factory = None

    # How often will we try to reconnect. We want this randomized, so not all
    # clients hit the server at the same time
    _min_sleep = 60
    _max_sleep = 90

    def __init__(self):
        self.options_table = [
            self.option("-v", "--verbose",       action="count",
                help="Increase verbosity"),
            self.option('-N', "--nodetach",      action="store_true",
                help="Suppress backgrounding and detachment of the process"),
            self.option('--pid-file',            action="store",
                help="Write to this PID file"),
            self.option('--logfile',             action="store",
                help="Write log information to this file"),
        ]

        self.ssl_cert = None
        self.debug_level = 0
        self._jabber_servers = []
        self._connected_jabber_server = None
        self._username = None
        self._password = None
        self._resource = None
        self._in_background = 0
        self._use_proxy = 0

    def process_cli_options(self):
        "Process command line options"
        self._parser = self.option_parser(option_list=self.options_table)
        self.options, self.args = self._parser.parse_args()

    def main(self):
        """Method that starts up everything
        - processes command line options
        - big loop to reconnect if necessary
            - read config
            - setup config
            - setup jabber connection
            - process requests
        """
        self.process_cli_options()
        force_setup = 0
        no_fork = None
        while 1:
            # First time around?
            if no_fork is None:
                # Yes, we may be forking
                no_fork=0
            else:
                # Been here before, no need to fork anymore
                no_fork=1

            try:
                config = self.read_config()
                if force_setup:
                    log_debug(2,"###Forcing setup")
                    self.setup_config(config, 1)
                    force_setup = 0
                else:
                    self.setup_config(config)
                c = self.setup_connection(no_fork=no_fork)
                self.fix_connection(c)
                self.process_forever(c)
            except KeyboardInterrupt:
                try:
                    c.disconnect()
                except:
                    pass
                sys.exit(0)
            except SystemExit:
                raise
            except RestartRequested:
                e = sys.exc_info()[1]
                log_error("Restart requested", e)
                if not self.is_in_background():
                    self.push_to_background()
                continue
            except NeedRestart:
                e = sys.exc_info()[1]
                log_debug(3, "Need Restart")
                force_setup = 1
                continue
            except JabberConnectionError:
                time_to_sleep = random.randint(self._min_sleep, self._max_sleep)
                log_debug(0, "Unable to connect to jabber servers, sleeping"
                    " %s seconds" % time_to_sleep)
                if not self.is_in_background():
                    self.push_to_background()
                try:
                    time.sleep(time_to_sleep)
                except KeyboardInterrupt:
                    sys.exit(0)
            except InvalidCertError:
                e = sys.exc_info()[1]
                log_error("Invalid Cert Error:")
                raise
            except:
                # Print traceback
                log_error("Error caught:")
                log_error(extract_traceback())
                time_to_sleep = random.randint(self._min_sleep, self._max_sleep)
                log_debug(3, "Sleeping", time_to_sleep, "seconds")
                if not self.is_in_background():
                    self.push_to_background()
                try:
                    time.sleep(time_to_sleep)
                except KeyboardInterrupt:
                    sys.exit(0)

    def fix_connection(self, client):
        "After setting up the connection, do whatever else is necessary"
        return client

    def preprocess_once(self, client):
        return client

    def process_forever(self, client):
        """Big loop to process requests
        """
        log_debug(1)
        self.preprocess_once(client)
        while 1:
            try:
                self.process_once(client)
                # random sleep so we don't kill CPU performance, bz 222988
                time_to_sleep = random.randint(6, 10)
                time.sleep(time_to_sleep)
            except KeyboardInterrupt:
                # CTRL+C
                client.disconnect()
                sys.exit(0)
            except:
                # XXX to be refined later
                raise

    def process_once(self, client):
        "To be overridden in a client class"
        raise NotImplementedError

    def setup_config(self, config):
        pass

    def read_config(self):
        return {}

    def is_in_background(self):
        return self._in_background

    def push_to_background(self):
        if not self.options.nodetach:
            # Detach and push to the background
            push_to_background()
            self._in_background = 1

        pid_file = self.options.pid_file
        if pid_file:
            try:
                os.unlink(pid_file)
            except OSError:
                e = sys.exc_info()[1]
                if e.errno != 2:
                    raise
            try:
                # Make sure we don't create the file world-writable (#162619)
                fd = os.open(pid_file, os.O_WRONLY| os.O_APPEND | os.O_CREAT, int("0644", 8))
                os.write(fd, bstr("%d" % os.getpid()))
                os.write(fd, bstr("\n"))
                os.close(fd)
            except OSError:
                pass

    def check_cert(self, cert):
        return check_cert(cert)

    def print_message(self, js, e):
        log_debug(1, e)
        log_debug(1, "Could not connect to jabber server", js)

    def setup_connection(self, no_fork=0):
        """
        - initializes a Jabber connection (by instantiating a Jabber client)
        - if necessary, pushes to background
        - authentication and resource binding (by calling start())

        Possible causes for this function to return None:
        - jabber server is not started
        - jabber server is started but did not initialize SSL just yet

        This function will kill the process with exit code 1 if the SSL
        handshake failed (an indication of a mismatching CA cert). We do this
        so starting the program as a daemon to fail if this happens. Of
        course, if the server is down and the CA cert is bad, then the daemon
        will start but will silently fail afterwards; the error log should
        have a traceback though.
        """
        for js in self._jabber_servers:

            log_debug(3, "Connecting to", js)

            try:
                c = self._get_jabber_client(js)
                log_debug(1, "Connected to jabber server", js)
                self._connected_jabber_server = js
                break
            except SSLHandshakeError:
                # Error doing the handshake - this is a permanent error
                sys.exit(1)
            except socket.error:
                e = sys.exc_info()[1]
                self.print_message(js, "socket error")
                log_error(extract_traceback())
                continue
            except JabberError:
                e = sys.exc_info()[1]
                self.print_message(js, "JabberError")
                log_error(extract_traceback())
                continue
            except SSLError:
                e = sys.exc_info()[1]
                self.print_message(js, "SSLError")
                log_error(extract_traceback())
                continue
        else:
            # Ran out of Jabber servers to try
            # Could not connect to any servers
            log_debug(1, "Could not connect to any jabber server")
            # Make sure we push to background at this point, we don't want the
            # service to block at startup
            if not no_fork:
                self.push_to_background()
            raise JabberConnectionError

        # If we got to this point, we have a connection set up
        if not no_fork:
            self.push_to_background()

        # Autentication and resource binding
        c.start(username=self._username, password=self._password,
            resource=self._resource)

        # Register callbacks
        c.custom_handler.register_callback(c._presence_callback, 'presence')
        c.custom_handler.register_callback(c._message_callback, 'message')
        c.custom_handler.register_callback(self._error_callback, 'error')
        return c

    def _get_jabber_client(self, jabber_server):
        """Returns a connected Jabber client, or raises an exception if it was
        unable to connect"""
        log_debug(3)
        arr = jabber_server.split(':', 1)
        jabber_server = arr[0]
        cf = self.read_config()

        jabberpy_proxy_dict = None
        if self._use_proxy and 'proxy_url' in cf:
            jabberpy_proxy_dict = {'host': cf['proxy_url'].split(':')[0],
                                   'port': int(cf['proxy_url'].split(':')[1])}
            if cf['enable_proxy_auth']:
                jabberpy_proxy_dict['user'] = cf['proxy_user'];
                jabberpy_proxy_dict['password'] = cf['proxy_password'];

        if len(arr) == 2:
            jabber_port = int(arr[1])
            log_debug(2, "Connecting to", jabber_server, jabber_port)
            c = self.client_factory(jabber_server, jabber_port, proxy=jabberpy_proxy_dict)
        else:
            log_debug(2, "Connecting to", jabber_server)
            c = self.client_factory(jabber_server, proxy=jabberpy_proxy_dict)

        c.debug_level = self.debug_level
        c.add_trusted_cert(self.ssl_cert)
        c.connect()
        return c

    def _error_callback(self, client, stanza):
        """Logs error stanza messages for diagnostic purposes"""
        log_error("Received an error stanza: ", stanza)
        for kid in stanza.kids:
            if kid.getName() == "conflict":
                log_error("Received an conflict. Restarting with new credentials.")
                raise NeedRestart

class InvalidCertError(SSL.SSL.Error):
    def __str__(self):
        return " ".join(self.args)
    __repr__ = __str__

def check_cert(cert_path):
    if cert_path is None:
        raise InvalidCertError("Cannot pass None as a certificate path")
    try:
        c = open(cert_path)
        cert = c.read()
        c.close()
    except IOError:
        raise_with_tb(InvalidCertError("Unable to read file", cert_path), sys.exc_info()[2])
    try:
        x509 = SSL.crypto.load_certificate(SSL.crypto.FILETYPE_PEM, cert)
    except SSL.crypto.Error:
        raise_with_tb(InvalidCertError("Unable to open certificate", cert_path), sys.exc_info()[2])
    log_debug(4, "Loading cert", x509.get_subject())
    if x509.has_expired():
        raise InvalidCertError("Expired certificate", cert_path)

def sign(secret_key, *values):
    h = hashlib.new('sha1', bstr(secret_key)).hexdigest()
    for v in values:
        h = hashlib.new('sha1', bstr("%s%s" % (h, v))).hexdigest()
    return h

# getAttr is braindead, rewrite it
class JabberProtocolNode(jabber.Protocol):
    def getAttr(self, key):
        return self.attrs.get(key, None)

class JabberIqNode(jabber.Iq, JabberProtocolNode):
    getAttr = JabberProtocolNode.getAttr

class JabberMessageNode(jabber.Message, JabberProtocolNode):
    getAttr = JabberProtocolNode.getAttr

class JabberPresenceNode(jabber.Presence, JabberProtocolNode):
    getAttr = JabberProtocolNode.getAttr

class Handlers:
    def __init__(self):
        log_debug(3)
        self._handlers = {}


    def dispatch(self, client, stanza):
        log_debug(5, stanza)

        self.cleanup_expired_callbacks()

        callbacks = self._get_callbacks(stanza)
        if not callbacks:
            log_debug(4, "Unhandled stanza", stanza)
            return
        for callback in callbacks:
            log_debug(6, "Calling callback", callback, stanza)
            callback(client, stanza)

    def _get_callbacks(self, stanza):
        log_debug(5, stanza)
        stanza_name = stanza.getName()
        if stanza_name not in self._handlers:
            return []
        stanza_id = stanza.getID()
        stanza_ns = stanza.getNamespace()
        result = {}
        (h_idns, h_id, h_ns, l_def) = self._handlers[stanza_name]

        if stanza_id is not None and stanza_ns:
            cbs = h_idns.get((stanza_id, stanza_ns), [])
            self._get_callbacks_from_list(cbs, result)
        if stanza_id is not None:
            cbs = h_id.get(stanza_id, [])
            self._get_callbacks_from_list(cbs, result)
        if stanza_ns:
            cbs = h_ns.get(stanza_ns, [])
            self._get_callbacks_from_list(cbs, result)
        self._get_callbacks_from_list(l_def, result)
        return list(result.keys())

    def _get_callbacks_from_list(self, l, result_hash):
        for ent in l:
            (callback, expiry, usage_count) = ent[:3]
            if usage_count is None or usage_count >= 1:
                result_hash[callback] = None
                if usage_count is None:
                    # We're done here
                    continue

            usage_count = usage_count - 1
            if usage_count <= 0:
                # Expired
                l.remove(ent)
                continue
            # Update the usage count
            ent[2] = usage_count - 1

    def register_callback(self, callback, stanza_name, stanza_id=None,
            stanza_ns=None, timeout=None, usage_count=None):
        log_debug(3, callback, stanza_name, stanza_id, stanza_ns, timeout,
            usage_count)
        if timeout:
            expiry = time.time() + timeout
        else:
            expiry = None
        callback_entry = [callback, expiry, usage_count]
        h_idns, h_id, h_ns, l_def = self._get_from_hash(self._handlers,
            stanza_name, default_value=({}, {}, {}, []))
        # h_id is for all the callbacks we should call for a particular stanza
        # id; h_ns is for namespaces
        if stanza_id is not None and stanza_ns:
            l = self._get_from_hash(h_idns, (stanza_id, stanza_ns), [])
            l.append(callback_entry)
            return

        if stanza_id is not None:
            l = self._get_from_hash(h_id, stanza_id, [])
            l.append(callback_entry)
            return

        if stanza_ns:
            l = self._get_from_hash(h_ns, stanza_ns, [])
            l.append(callback_entry)
            return

        # Default callback
        l_def.append(callback_entry)

    def _get_from_hash(self, h, key, default_value):
        if key in h:
            val = h[key]
        else:
            val = h[key] = default_value
        return val

    def cleanup_expired_callbacks(self):
        log_debug(5)
        now = time.time()
        for stanza_name, vals in self._handlers.items():
            h_idns, h_id, h_ns, l_def = vals
            for h in (h_idns, h_id, h_ns):
                self._expire_callbacks_hash(h, now)
            self._expire_callbacks_list(l_def, now)

    def _expire_callbacks_hash(self, h, now):
        log_debug(6, now)
        for key, vals in h.items():
            self._expire_callbacks_list(vals, now)

    def _expire_callbacks_list(self, vals, now):
        log_debug(7, vals, now)
        for val in vals:
            (callback, expiry, usage_count) = val
            if not expiry:
                continue
            if now <= expiry:
                # Fresh
                continue
            # Callback is stale
            vals.remove(val)

def my_debug(*args):
    print("Debugging:", args)

class RestartRequested(Exception):
    pass

class JabberError(Exception):
    pass

class NeedRestart(Exception):
    pass

class TimeoutError(JabberError):
    pass

class SSLError(Exception):
    "Raised when a lower-level SSL error is caught"
    pass

class SSLHandshakeError(SSLError):
    "Raised when the SSL handshake failed"
    pass

class SSLDisabledError(SSLError):
    "Raised if the server does not support SSL"
    pass

class JabberConnectionError(Exception):
    "Raised when we were unable to make a jabber connection"
    pass

class JabberQualifiedError(JabberError):
    def __init__(self, errcode, err, *args):
        self.errcode = errcode
        self.err = err
        JabberError.__init__(self, *args)

    def __repr__(self):
        return "<%s instance at %s; errcode=%s; err=%s>" % (
            self.__class__.__name__, id(self), self.errcode, self.err)

    __str__ = __repr__

class JabberClient(jabber.Client, object):
    _seq = 0
    BLOCK_SIZE = jabber.xmlstream.BLOCK_SIZE

    def __init__(self, *args, **kwargs):
        log_debug(1)
        jabber.Client.__init__(self, *args, **kwargs)
        self.jid = None
        # Lots of magic to add the nodes into a queue
        self._incoming_node_queue = []

        self.debug_level = 0
        self.trusted_certs = []

        self.registerProtocol('unknown', JabberProtocolNode)
        self.registerProtocol('iq', JabberIqNode)
        self.registerProtocol('message', JabberMessageNode)
        self.registerProtocol('presence', JabberPresenceNode)
        self.registerProtocol('error', JabberProtocolNode)

        self.registerHandler('iq', self._expectedIqHandler, system=True)
        self.registerHandler('iq', self._IqRegisterResult, 'result',
            jabber.NS_REGISTER, system=True)

        h = Handlers()
        self.custom_handler = h
        self.registerHandler('presence', h.dispatch)
        self.registerHandler('iq', h.dispatch)
        self.registerHandler('message', h.dispatch)
        self.registerHandler('error', h.dispatch)

        self._non_ssl_sock = None
        self._roster = Roster()

        self._uniq_client_string = generate_random_string(6)

    def add_trusted_cert(self, trusted_cert):
        check_cert(trusted_cert)
        self.trusted_certs.append(trusted_cert)

    def connect(self):
        log_debug(2)
        if not self.trusted_certs:
            raise SSLVerifyError("No trusted certs added")

        # Use our own dispatcher - we need to be able to read one stanza at
        # the time
        self.dispatch = self._auth_dispatch

        log_debug(5, "Attempting to connect")

        for retry in range(0,3):
            try:
                jabber.Client.connect(self)
            except socket.error:
                e = sys.exc_info()[1]
                log_error("Error connecting to jabber server: %s. "
                      "See https://access.redhat.com/solutions/327903 for more information. " % e)
                raise socket.error(e)

            log_debug(5, "Connected")

            # From the XMPP Core Internet Draft:
            # server advertises <features><starttls /></features>
            # client sends back <starttls />
            # server responds with <proceed />

            # Wait for a stanza
            stanza = self.get_one_stanza()

            log_debug(5, "Expecting features stanza, got:", stanza)
            if stanza.getName() != 'features':
                log_debug(1, "Server did not return a <features /> stanza,"
                             " reconnecting")
                self.disconnect()
                time.sleep(1)
            else:
                break
        else:
            log_error("Not able to reconnect - "
                "See https://access.redhat.com/solutions/45332 for possible solutions.\n")
            raise SSLDisabledError

        starttls_node = stanza.getTag('starttls')
        log_debug(5, "starttls node", starttls_node)
        if starttls_node is None:
            log_error("Server does not support TLS - <starttls /> "
                "not in <features /> stanza")
            self.disconnect()
            raise SSLDisabledError

        # Initiate the TLS stream
        self.write("<starttls xmlns='%s' />" % NS_STARTTLS)

        stanza = self.get_one_stanza()
        log_debug(5, "Expecting proceed stanza, got:", stanza)
        if stanza.getName() != 'proceed':
            log_error("Server broke TLS negotiation - <proceed /> not sent")
            self.disconnect()
            raise SSLDisabledError

        log_debug(4, "Preparing for TLS handshake")
        ssl = SSLSocket(self._sock, trusted_certs=self.trusted_certs)
        ssl.ssl_verify_callback = self.ssl_verify_callback
        ssl.init_ssl()
        # Explicitly perform the SSL handshake
        try:
            ssl.do_handshake()
            self.verify_peer(ssl)
        except SSL.SSL.Error:
            # Error in the SSL handshake - most likely mismatching CA cert
            log_error("Traceback caught:")
            log_error(extract_traceback())
            raise_with_tb(SSLHandshakeError(), sys.exc_info()[2])

        # Re-init the parsers
        jabber.xmlstream.Stream.connect(self)

        # Now replace the socket with the ssl object's connection
        self._non_ssl_sock = self._sock
        self._sock = ssl._connection

        # jabber.py has copies of _read, _write, _reader - those have to
        # be re-initialized as well
        self._setupComms()

        # Send the header again
        self.send(self._header_string())

        # Read the server's open stream tag
        self.process()

        stanza = self.get_one_stanza()

        if stanza.getName() != 'features':
            self.disconnect()
            raise Exception("Server did not pass any features?")

        # Now replace the dispatcher
        self.dispatch = self._orig_dispatch
        log_debug(5, "connect returning")

    def disconnect(self):
        try:
            jabber.Client.disconnect(self)
        except SSL.SSL.Error:
            pass

    def _setupComms(self):
        # We pretty much only support TCP connections
        self._read = self._sock.recv
        if hasattr(self._sock, 'sendall'):
            self._write = self._sock.sendall
        else:
            self._write = Sendall(self._sock).sendall
        self._reader = self._sock

    def ssl_verify_callback(self, conn, cert, errnum, depth, ok):
        log_debug(4, "Called", errnum, depth, ok)
        if not ok:
            log_error("SSL certificate verification failed")
            self.write("</stream:stream>")
            conn.close()
            self._sock.close()
            return ok

        return ok

    def verify_peer(self, ssl):
        cert = ssl.get_peer_certificate()
        if cert is None:
            raise SSLVerifyError("Unable to retrieve peer cert")

        subject = cert.get_subject()
        if not hasattr(subject, 'CN'):
            raise SSLVerifyError("Certificate has no Common Name")

        common_name = subject.CN

        # Add a trailing . since foo.example.com. is equal to foo.example.com
        # This also catches non-FQDNs
        if common_name[-1] != '.':
            common_name = common_name + '.'
        hdot = self._host
        if hdot[-1] != '.':
            hdot = hdot + '.'

        if common_name != hdot and not fnmatch.fnmatchcase(hdot, common_name):
            raise SSLVerifyError("Mismatch: peer name: %s; common name: %s" %
                (self._host, common_name))

    def retrieve_roster(self):
        """Request the roster. Will register the roster callback,
        but the call will wait for the roster to be properly populated"""
        # Register the roster callback
        self.custom_handler.register_callback(self._roster_callback, 'iq')
        iq_node_id = 'iq-request-%s' % self.get_unique_id()
        iq_node = JabberIqNode(type="get")
        iq_node.setQuery(jabber.NS_ROSTER)
        iq_node.setID(iq_node_id)
        self.send(iq_node)

        stanza = None
        # Wait for an IQ stanza with the same ID as the one we sent
        while 1:
            stanza = self.get_one_stanza()
            node_id = stanza.getAttr('id')
            if node_id == iq_node_id:
                # This is the response
                break
        # We now have the roster populated

        # All entries of type "from" and ask="subscribe" should be answered to
        for k, v in self._roster.get_subscribed_from().items():
            if 'ask' in v and v['ask'] == 'subscribe':
                self.send_presence(k, type="subscribed")
            else:
                # Ask for a subscription
                self.send_presence(k, type="subscribe")

    def _roster_callback(self, client, stanza):
        log_debug(3, "Updating the roster", stanza)

        # Extract the <query> node
        qnode = stanza.getTag('query')
        if qnode is None or qnode.getNamespace() != jabber.NS_ROSTER:
            # No query
            log_debug(5, "Query node not found, skipping")
            return

        # This gets called any time a roster event is received
        node_type = stanza.getAttr('type')
        if node_type not in ('result', 'set'):
            log_debug(5, "Not a result or a set, skipping")
            return

        # Now extract the <item> nodes
        for node in qnode.getTags('item'):
            self._roster.add_item(node)

    def cancel_subscription(self, jids):
        if not jids:
            return

        qnode = JabberProtocolNode("query")
        qnode.setNamespace(jabber.NS_ROSTER)

        for jid in jids:
            attrs = {
                'jid'           : jid,
                'subscription'  : 'remove',
            }
            inode = JabberProtocolNode("item", attrs=attrs)
            qnode.insertNode(inode)

        node = JabberIqNode(type="set")
        remove_iq_id = "remove-%s" % self.get_unique_id()
        node.setID(remove_iq_id)
        node.insertNode(qnode)

        self.send(node)

    def get_one_stanza(self, timeout=None):
        """Returns one stanza (or None if timeout is set)"""
        if timeout:
            start = time.time()
        while not self._incoming_node_queue:
            if timeout:
                now = time.time()
                if now >= start + timeout:
                    # Timed out
                    log_debug(4, "timed out", now, start, timeout)
                    return None
                tm = start + timeout - now
            else:
                tm = None
            # No nodes in the queue, read some data
            self.process(timeout=tm)

        # Now we have nodes in the queue
        node = self._incoming_node_queue[0]
        del self._incoming_node_queue[0]
        return node

    def _build_stanza(self, stanza):
        """Builds one stanza according to the handlers we have registered via
        registerHandler or registerProtocol"""
        name = stanza.getName()
        if name not in self.handlers:
            name = 'unknown'
        # XXX This is weird - why is jabbberpy using type which is a type?
        stanza = self.handlers[name][type](node=stanza)
        return stanza

    def _orig_dispatch(self, stanza):
        log_debug(6, stanza)
        if self.debug_level > 5:
            # Even more verbosity
            sys.stderr.write("<-- ")
            sys.stderr.write(str(stanza))
            sys.stderr.write("\n\n")
        # Even though Client.dispatch does build a stanza properly, we have to
        # do it ourselves too since dispatch doesn't return the modified
        # stanza, so it was always of type Node (i.e. the top-level class)
        stanza = self._build_stanza(stanza)
        jabber.Client.dispatch(self, stanza)
        self._incoming_node_queue.append(stanza)

    def _auth_dispatch(self, stanza):
        log_debug(6, stanza)
        if self.debug_level > 5:
            # Even more verbosity
            sys.stderr.write("<-- ")
            sys.stderr.write(str(stanza))
            sys.stderr.write("\n\n")
        # Create the stanza of the proper type
        stanza = self._build_stanza(stanza)
        self._incoming_node_queue.append(stanza)

    def auth(self, username, password, resource, register=1):
        """Try to authenticate the username with the specified password
        If the authentication fails, try to register the user.
        If that fails as well, then JabberQualifiedError is raised
        """
        log_debug(2, username, password, resource, register)
        auth_iq_id = "auth-get-%s" % self.get_unique_id()
        auth_get_iq = jabber.Iq(type='get')
        auth_get_iq.setID(auth_iq_id)
        q = auth_get_iq.setQuery(jabber.NS_AUTH)
        q.insertTag('username').insertData(username)
        self.send(auth_get_iq)
        log_debug(4, "Sending auth request", auth_get_iq)

        try:
            auth_response = self.waitForResponse(auth_iq_id, timeout=60)
        except JabberQualifiedError:
            e = sys.exc_info()[1]
            if not register:
                raise
            if e.errcode == '401':
                # Need to register the user if possible
                log_debug(4, "Need to register")
                self.register(username, password)
                return self.auth(username, password, resource, register=0)

            raise

        log_debug(4, "Auth response", auth_response)
        auth_ret_query = auth_response.getTag('query')
        auth_set_id = "auth-set-%s" % self.get_unique_id()
        auth_set_iq = jabber.Iq(type="set")
        auth_set_iq.setID(auth_set_id)

        q = auth_set_iq.setQuery(jabber.NS_AUTH)
        q.insertTag('username').insertData(username)
        q.insertTag('resource').insertData(resource)

        if auth_ret_query.getTag('token'):
            token = auth_ret_query.getTag('token').getData()
            seq = auth_ret_query.getTag('sequence').getData()

            h = hashlib.new('sha1', hashlib.new('sha1', password).hexdigest() + token).hexdigest()
            for i in range(int(seq)):
                h = hashlib.new('sha1', h).hexdigest()
            q.insertTag('hash').insertData(h)
        elif auth_ret_query.getTag('digest'):
            digest = q.insertTag('digest')
            digest.insertData(hashlib.new('sha1',
                bstr(self.getIncomingID() + password)).hexdigest() )
        else:
            q.insertTag('password').insertData(password)

        log_debug(4, "Sending auth info", auth_set_iq)
        try:
            self.SendAndWaitForResponse(auth_set_iq)
        except JabberQualifiedError:
            e = sys.exc_info()[1]
            if e.errcode == '401':
                # Need to reserve the user if possible
                log_debug(4, "Need to register")
                return self.register(username, password)
            raise

        log_debug(4, "Authenticated")
        return True

    def send(self, stanza):
        if self.debug_level > 5:
            sys.stderr.write("--> ")
            sys.stderr.write(str(stanza))
            sys.stderr.write("\n\n")
        return jabber.Client.send(self, stanza)


    def subscribe_to_presence(self, jids):
        """Subscribe to these nodes' presence
        The subscription in jabber works like this:

        Contact 1    State Contact 1     State Contact 2   Contact 2
        ----------+-------------------+------------------+----------
        subscribe  -> [ none + ask ]
                                         [ from ]       <- subscribed
                      [ to ]
                                         [ from + ask ] <- subscribe
        subscribed -> [ both ]
                                         [ both ]
        ----------+-------------------+------------------+----------

        Enclosed in square brackets is the state when the communication took
        place.
        """
        subscribed_to = self._roster.get_subscribed_to()
        log_debug(4, "Subscribed to", subscribed_to)
        subscribed_both = self._roster.get_subscribed_both()
        log_debug(4, "Subscribed both", subscribed_both)
        subscribed_none = self._roster.get_subscribed_none()
        log_debug(4, "Subscribed none", subscribed_none)
        subscribed_from = self._roster.get_subscribed_from()
        log_debug(4, "Subscribed from", subscribed_from)
        for full_jid in jids:
            jid = self._strip_resource(full_jid)
            jid = str(jid)
            if jid in subscribed_both:
                log_debug(4, "Already subscribed to the presence of node", jid)
                continue
            # If to or from subscription for this node, we still send the
            # subscription request, but we shouldn't drop the subscription, so
            # we take the jid out of the respective hash
            if jid in subscribed_to:
                log_debug(4, "Subscribed to")
                continue
            if jid in subscribed_none:
                ent = subscribed_none[jid]
                if 'ask' in ent and ent['ask'] == 'subscribe':
                    log_debug(4, "Subscribed none + ask=subscribe")
                    # We already asked for a subscription
                    continue
            if jid in subscribed_from:
                ent = subscribed_from[jid]
                if 'ask' in ent and ent['ask'] == 'subscribe':
                    log_debug(4, "Subscribed from + ask=subscribe")
                    # We already asked for a subscription
                    continue

            # Make sure we update the roster ourselves, to avoid sending
            # presence subscriptions twice
            # At this point we should only have 2 cases left: either from or
            # none.
            if jid in self._roster._subscribed_from:
                subscription = "from"
                hashd = self._roster._subscribed_from
            else:
                subscription = "none"
                hashd = self._roster._subscribed_none

            hashd[jid] = {
                'jid'           : jid,
                'subscription'  : subscription,
                'ask'           : 'subscribe',
            }

            # subscribe this node to the jid's presence
            log_debug(4, jid)
            stripped_jid = self._strip_resource(jid)
            presence_node = JabberPresenceNode(to=stripped_jid, type="subscribe")
            presence_node.setID("presence-%s" % self.get_unique_id())
            sig = self._create_signature(jid, NS_RHN_PRESENCE_SUBSCRIBE)
            if sig:
                presence_node.insertNode(sig)
            log_debug(5, "Sending presence subscription request", presence_node)
            self.send(presence_node)

        # XXX Here we should clean up everybody that is no longer online, but
        # this is more difficult

    def send_presence(self, jid=None, type=None, xid=None):
        log_debug(3, jid, type)
        if jid is None:
            node = JabberPresenceNode()
        else:
            node = JabberPresenceNode(to=jid)

        if type:
            node.setType(type)

        if xid:
            node.setID(xid)

        self.send(node)

    def fileno(self):
        return self._reader.fileno()

    def read(self):
        received = ''
        while 1:
            rfds, wfds, exfds = select.select([self.fileno()], [], [], 0)
            if not rfds:
                # No input
                break
            buff = self._read(self.BLOCK_SIZE)
            if not buff:
                break
            received = received + buff
        if not received:
            # EOF reached
            self.disconnected(self)
        return received

    def process_loop_hook(self):
        pass

    def process(self, timeout=None):
        log_debug(3, timeout)
        self._incoming_node_queue = []
        fileno = self.fileno()
        # Wait for a node or until we hit the timeout
        start = time.time()
        while 1:
            now = time.time()
            if timeout:
                if now >= start + timeout:
                    # Timed out
                    return 0
                tm = start + timeout - now
            else:
                tm = None

            self.process_loop_hook()
            # tm is the number of seconds we have to wait (or None)
            log_debug(5, "before select(); timeout", tm)
            rfds, wfds, exfds = select.select([fileno], [], [], tm)
            log_debug(5, "select() returned")
            if not rfds:
                # Timed out
                return 0
            # Try to read as much data as possible
            if hasattr(self._sock, 'pending'):
                # This is on the SSL case - select() will use the native
                # socket's file descriptor. SSL may decode more data than we
                # are willing to read - so just read what's available
                log_debug(5, "Reading %s bytes from ssl socket" % self.BLOCK_SIZE)
                try:
                    data = self._read(self.BLOCK_SIZE)
                except SSL.SSL.SysCallError:
                    e = sys.exc_info()[1]
                    log_debug(5, "Closing socket")
                    self._non_ssl_sock.close()
                    raise_with_tb(SSLError("OpenSSL error; will retry", str(e)), sys.exc_info()[2])
                log_debug(5, "Read %s bytes" % len(data))
                if not data:
                    raise JabberError("Premature EOF")
                self._parser.Parse(data)
                pending = self._sock.pending()
                if pending:
                    # More bytes to read from the SSL socket
                    data = self._read(pending)
                    self._parser.Parse(data)
            else:
                # Normal socket - select will figure out correctly if the read
                # will block
                data = self._read(self.BLOCK_SIZE)
                if not data:
                    raise JabberError("Premature EOF")
                self._parser.Parse(data)

            # We may not have read enough data to be able to produce a node
            if not self._incoming_node_queue:
                # Go back and read some more
                if timeout:
                    # Trying to wait some more before giving up in this call
                    continue
                # No reason to block again, return into the higher-level
                # select()
                return 0
            return len(self._incoming_node_queue)
        return 0


    def register(self, username, password):
        log_debug(2, username, password)
        self.requestRegInfo()
        d = self.getRegInfo()
        if 'username' in d:
            self.setRegInfo('username', username)
        if 'password' in d:
            self.setRegInfo('password', password)
        try:
            self.sendRegInfo()
        except JabberQualifiedError:
            e = sys.exc_info()[1]
            if e.errcode == '409':
                # Need to register the user if possible
                log_error("Invalid password")
                self.disconnect()
                sys.exit(0)
            raise
        return True

    def _waitForResponse(self, ID, timeout=jabber.timeout):
        log_debug(5, ID, timeout)
        # jabberpy's function waits when it shouldn't so have to rebuild it
        ID = jabber.ustr(ID)

        self.lastErr = ''
        self.lastErrCode = 0

        if timeout is not None:
            abort_time = time.time() + timeout
            self.DEBUG("waiting with timeout:%s for %s" % (timeout, ID),
                jabber.DBG_NODE_IQ)
        else:
            self.DEBUG("waiting for %s" % ID, jabber.DBG_NODE_IQ)

        while 1:
            if timeout is None:
                tmout = None
            else:
                tmout = abort_time - time.time()
                if tmout <= 0:
                    # Timed out
                    break
            log_debug(5, "before get_one_stanza")
            stanza = self.get_one_stanza(tmout)
            log_debug(5, "after get_one_stanza")
            if not stanza:
                # get_one_stanza should only return None for a timeout
                assert timeout is not None
                break

            error_code = stanza.getErrorCode()
            if error_code:
                # Error
                self.lastErr = stanza.getError()
                self.lastErrCode = error_code
                return None

            # Is it the proper stanza ID?
            tid = jabber.ustr(stanza.getID())
            if ID == tid:
                # This is the node
                return stanza

            # Keep looking for stanzas until we time out (if a timeout was
            # passed)

        # Timed out
        self.lastErr = "Timeout"
        return None

    def waitForResponse(self, ID, timeout=jabber.timeout):
        result = self._waitForResponse(ID, timeout=timeout)
        if result is not None:
            return result
        if self.lastErr == 'Timeout':
            raise TimeoutError()

        if self.lastErrCode:
            raise JabberQualifiedError(self.lastErrCode, self.lastErr)

        raise JabberError("Unknown error", self.lastErr)


    def get_unique_id(self):
        seq = self._seq
        JabberClient._seq = seq + 1
        return "%s-%s" % (self._uniq_client_string, seq)

    def disconnectHandler(self, conn):
        pass

    # Need to add the version tothe XML stream
    def _header_string(self):
        self.DEBUG("jabber_lib.JabberClient.header: sending initial header",
            jabber.DBG_INIT)
        templ = "<?xml version='1.0' encoding='UTF-8'?><stream:stream %s>"
        attrs = {
            'to'            : self._host,
            'xmlns'         : self._namespace,
            'xmlns:stream'  : "http://etherx.jabber.org/streams",
            'version'   : '1.0',
        }
        if self._outgoingID:
            attrs['id'] = self._outgoingID
        # XXX Add more custom attributes here
        addition = []
        for k, v in attrs.items():
            addition.append("%s='%s'" % (k, v))
        addition = " ".join(addition)
        return templ % addition

    def header(self):
        header = self._header_string()
        self.send(header)
        self.process(jabber.timeout)

    def _fix_jid(self, jid):
        return jid

    def _presence_callback(self, client, stanza):
        """
        If the roster is enabled, presence stanzas with type="subscribed"
        should never be received - the server will initiate a roster push
        instead
        """
        jid = stanza.getFrom()
        presence_type = stanza.getType()
        log_debug(3, self.jid, jid, presence_type)
        stanza_id = stanza.getID()

        assert(stanza.getName() == 'presence')

        # We may not get the full JID here
        if presence_type is None or presence_type == 'subscribed':
            log_debug(4, "Node is available", jid, presence_type)
            self.set_jid_available(jid)

            # Now subscribe this node to the other node's presence, just in
            # case
            self.subscribe_to_presence([jid])
            return

        if presence_type in ('unsubscribed', 'unavailable'):
            log_debug(4, "Node is unavailable", jid, presence_type)
            self.set_jid_unavailable(jid)
            return

        if presence_type == 'subscribe':
            # XXX misa 20051111: don't check signatures for presence anymore,
            # the fact they expire makes them unreliable

            #sig = self._check_signature(stanza)
            #if not sig:
            #    print("KKKKKK", stanza)
            #    log_debug(1, "Invalid signature", jid)
            #    return

            log_debug(4, "Subscription request approved", jid)
            self.send_presence(jid, type="subscribed", xid=stanza_id)
            # Now subscribe this node to the other node's presence
            self.subscribe_to_presence([jid])
            return
        if presence_type == 'probe':
            log_debug(4, "Presence probe", jid)
            self.send(JabberPresenceNode(to=jid))

    def _check_signature(self, stanza, actions=None):
        return 1

    def _strip_resource(self, jid):
        return strip_resource(jid)

    def _create_signature(self, jid, action):
        return None

    def send_message(self, jid, action):
        node = JabberMessageNode(to=jid, type='normal')
        sig = self._create_signature(jid, action)
        if sig:
            node.insertNode(sig)
        self.send(node)

    def jid_available(self, jid):
        return self._roster.jid_available(jid)

    def set_jid_available(self, jid):
        return self._roster.set_available(jid)

    def set_jid_unavailable(self, jid):
        return self._roster.set_unavailable(jid)

    def match_stanza_tags(self, stanza, tag_name, namespace=None):
        """Get the matching (child) tags of this stanza, possibly with the
        specified namespace"""
        tags = stanza.getTags(tag_name)
        if not tags:
            return []
        if namespace is None:
            # Nothing more to look for
            return tags
        return [x for x in tags if x.getNamespace() == namespace]

    def _check_signature_from_message(self, stanza, actions):
        log_debug(4, stanza)
        assert stanza.getName() == 'message'

        message_from = stanza.getFrom()
        message_type = stanza.getType()
        if message_type == 'error':
            log_debug(1, 'Received error from %s: %s' % (message_from, stanza))
            return None

        if message_type != 'normal':
            log_debug(1, 'Unsupported message type %s ignored' % message_type)
            return None

        x_delayed_nodes = self.match_stanza_tags(stanza, 'x',
            namespace=jabber.NS_DELAY)
        if x_delayed_nodes:
            log_debug(1, 'Ignoring delayed stanza')
            return None

        sig = self._check_signature(stanza, actions=actions)
        if not sig:
            if self.debug_level > 5:
                raise Exception(1)
            log_debug(1, "Mismatching signatures")
            return None

        return sig


class SSLSocket(SSL.SSLSocket):
    pass

class SSLVerifyError(SSL.SSL.Error):
    pass

def generate_random_string(length=20):
    if not length:
        return ''
    random_bytes = 16
    length = int(length)
    s = hashlib.new('sha1')
    s.update(bstr("%.8f" % time.time()))
    s.update(bstr("%s" % os.getpid()))
    devrandom = open('/dev/urandom', "rb")
    result = []
    cur_length = 0
    while 1:
        s.update(bstr(devrandom.read(random_bytes)))
        buf = s.hexdigest()
        result.append(buf)
        cur_length = cur_length + len(buf)
        if cur_length >= length:
            break

    devrandom.close()

    return ''.join(result)[:length].lower()


def push_to_background():
    log_debug(3, "Pushing process into background")
    # Push this process into background
    pid = os.fork()
    if pid > 0:
        # Terminate parent process
        os._exit(0)

    # Child process becomes a process group leader (and detaches from
    # terminal)
    os.setpgrp()

    # Change working directory
    os.chdir('/')

    # Set umask
    #7/7/05 wregglej 162619 set the umask to 0 so the remote scripts can run
    os.umask(0)

    #redirect stdin, stdout, and stderr.
    for f in sys.stdout, sys.stderr:
        f.flush()

    #files we want stdin,stdout and stderr to point to.
    si = open("/dev/null", 'r')
    so = open("/dev/null", 'ab+')
    se = open("/dev/null", 'ab+', 0)

    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())


    # close file descriptors
    # from subprocess import MAXFD
    #for i in range(3, MAXFD):
    #    try:
    #        os.close(i)
    #    except:
    #        pass


class Roster:
    def __init__(self):
        self._subscribed_to = {}
        self._subscribed_from = {}
        self._subscribed_both = {}
        self._subscribed_none = {}
        self._available_nodes = {}

    def add_item(self, item):
        subscr = item.getAttr('subscription')
        jid = item.getAttr('jid')
        jid = strip_resource(jid)
        jid = str(jid)
        entry = {
            'jid'           : jid,
            'subscription'  : subscr,
        }
        ask = item.getAttr('ask')
        if ask:
            entry['ask'] = ask

        actions = ['to', 'from', 'both', 'none']
        if subscr in actions:
            for a in actions:
                d = getattr(self, '_subscribed_' + a)
                if subscr == a:
                    # Set it
                    d[jid] = entry
                elif jid in d:
                    # Remove it
                    del d[jid]

    def get_subscribed_from(self):
        return self._subscribed_from.copy()

    def get_subscribed_to(self):
        return self._subscribed_to.copy()

    def get_subscribed_both(self):
        return self._subscribed_both.copy()

    def get_subscribed_none(self):
        return self._subscribed_none.copy()

    def get_subscribed_to_jids(self):
        ret = self._subscribed_to.copy()
        ret.update(self._subscribed_both)
        return ret

    def get_subscribed_from_jids(self):
        ret = self._subscribed_from.copy()
        ret.update(self._subscribed_both)
        return ret

    def get_available_nodes(self):
        return self._available_nodes.copy()

    def set_available(self, jid):
        jid = str(jid)
        self._available_nodes[jid] = 1

    def set_unavailable(self, jid):
        jid = str(jid)
        if jid in self._available_nodes:
            del self._available_nodes[jid]

    def jid_available(self, jid):
        return jid in self._available_nodes

    def clear(self):
        self._subscribed_to.clear()
        self._subscribed_from.clear()
        self._subscribed_both.clear()
        self._subscribed_none.clear()

    def __repr__(self):
        return "Roster:\n\tto: %s\n\tfrom: %s\n\tboth: %s\n\tnone: %s" % (
            self._subscribed_to.keys(),
            self._subscribed_from.keys(),
            self._subscribed_both.keys(),
            self._subscribed_none.keys(),
        )


def strip_resource(jid):
    # One doesn't subscribe to a specific resource
    if not isinstance(jid, jabber.JID):
        jid = jabber.JID(jid)
    return jid.getStripped()

def extract_traceback():
    return traceback.format_exc(None)

class Sendall:
    """This class exists here because python 1.5.2 does not support a
    sendall() method for sockets"""
    def __init__(self, sock):
        self.sock = sock

    def sendall(self, data, flags=0):
        to_send = len(data)
        if not to_send:
            # No data
            return 0
        bytes_sent = 0
        while 1:
            ret = self.sock.send(data[bytes_sent:], flags)
            if bytes_sent + ret == to_send:
                # We're done
                break
            bytes_sent = bytes_sent + ret
        return to_send
07070100000027000081B40000000000000000000000015E282E5600003EB8000000000000000000000000000000000000001F00000000mgr-osad/src/osa_dispatcher.py#
# Copyright (c) 2008--2017 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import sys
import select
import socket
import string
try: # python 3
    import socketserver
except ImportError: # python 2
    import SocketServer
from random import choice
from rhn.connections import idn_ascii_to_puny
from spacewalk.common.rhnLog import initLOG, log_debug, log_error
from spacewalk.common.rhnConfig import initCFG, CFG
from spacewalk.server import rhnSQL

try: # python 3
    from osad import jabber_lib, dispatcher_client
except ImportError: # python 2
    import jabber_lib, dispatcher_client

# Override the log functions
jabber_lib.log_debug = log_debug
jabber_lib.log_error = log_error

def main():
    return Runner().main()

class Runner(jabber_lib.Runner):
    client_factory = dispatcher_client.Client

    # We want the dispatcher to check in quite often in case the jabberd
    # connection drops
    _min_sleep = 10
    _max_sleep = 10

    def __init__(self):
        jabber_lib.Runner.__init__(self)
        initCFG("osa-dispatcher")
        self._notifier = Notifier()
        self._poll_interval = None
        self._next_poll_interval = None
        # Cache states
        self._state_ids = {}

    def read_config(self):
        ret = {
            'jabber_server' : CFG.jabber_server,
        }
        return ret

    _query_get_dispatcher_password = """
    select id, password
      from rhnPushDispatcher
     where jabber_id like :jabber_id
    """

    _update_dispatcher_password = """
    update rhnPushDispatcher
       set password = :password_in
     where id = :id_in
    """

    def get_dispatcher_password(self, username):
        h = rhnSQL.prepare(self._query_get_dispatcher_password)
        h.execute(jabber_id = username + "%")
        ret = h.fetchall_dict()

        if ret and len(ret) == 1:
            if ret[0]['password']:
                return ret[0]['password']
            else:
                # Upgrade Spacewalk 1.5 -> 1.6: the dispatcher row exists,
                # we just need to generate and save the password.
                self._password = self.create_dispatcher_password(32)
                u = rhnSQL.prepare(self._update_dispatcher_password)
                u.execute(password_in = self._password, id_in = ret[0]['id'])
                return self._password
        else:
            return None

    def create_dispatcher_password(self, length):
        chars = string.ascii_letters + string.digits
        return "".join(choice(chars) for x in range(length))

    def setup_config(self, config, force=0):
        # Figure out the log level
        debug_level = self.options.verbose
        if debug_level is None:
            debug_level = CFG.debug
        self.debug_level = debug_level
        logfile = self.options.logfile
        if logfile is None or logfile == '':
            logfile = CFG.log_file
        initLOG(level=debug_level, log_file=logfile)

        # Get the ssl cert
        ssl_cert = CFG.osa_ssl_cert
        try:
            self.check_cert(ssl_cert)
        except jabber_lib.InvalidCertError:
            e = sys.exc_info()[1]
            log_error("Invalid SSL certificate:", e)
            return 1

        self.ssl_cert = ssl_cert

        rhnSQL.initDB()

        self._username = 'rhn-dispatcher-sat'
        self._password = self.get_dispatcher_password(self._username)
        if not self._password:
            self._password = self.create_dispatcher_password(32)
        self._resource = 'superclient'
        js = config.get('jabber_server')
        self._jabber_servers = [ idn_ascii_to_puny(js) ]

    def fix_connection(self, c):
        "After setting up the connection, do whatever else is necessary"
        self._notifier.set_jabber_connection(c)

        self._poll_interval = CFG.poll_interval
        self._next_poll_interval = self._poll_interval

        if self._jabber_servers and self._jabber_servers[0]:
            hostname = self._jabber_servers[0]
        else:
            hostname = socket.gethostname()

        self._register_dispatcher(c.jid, hostname)

        c.retrieve_roster()
        log_debug(4, "Subscribed to",   c._roster.get_subscribed_to())
        log_debug(4, "Subscribed from", c._roster.get_subscribed_from())
        log_debug(4, "Subscribed both", c._roster.get_subscribed_both())

        client_jids = self._get_client_jids()
        client_jids = [x[0] for x in client_jids]

        # self-healing no longer works correctly since we blow away jabberd's
        # db on restart. Instead try to resubscribe jabberd to active jids manually.
        c.subscribe_to_presence(client_jids)

        # Unsubscribe the dispatcher from any client jid that no longer exists
        self.cleanup_roster(c, client_jids)

        c.send_presence()
        return c

    def cleanup_roster(self, client, active_jids):
        roster = client._roster
        active_stripped_jids = {}
        for jid in active_jids:
            stripped_jid = jabber_lib.strip_resource(jid)
            stripped_jid = str(stripped_jid)
            active_stripped_jids[stripped_jid] = None

        roster_jids = roster.get_subscribed_to()
        roster_jids.update(roster.get_subscribed_from())
        roster_jids.update(roster.get_subscribed_both())

        to_remove = []
        for jid in roster_jids.keys():
            stripped_jid = jabber_lib.strip_resource(jid)
            stripped_jid = str(stripped_jid)
            if stripped_jid not in active_stripped_jids:
                to_remove.append(stripped_jid)

        client.cancel_subscription(to_remove)

    def process_once(self, client):
        log_debug(3)
        # First, clean up the nodes that have been pinged and have not
        # responded
        client.retrieve_roster()
        self.reap_pinged_clients()
        need_pinging = self._fetch_clients_to_be_pinged()
        log_debug(4, "Clients to be pinged:", need_pinging)
        if need_pinging:
            client.ping_clients(need_pinging)
        npi = self._next_poll_interval

        rfds, wfds, efds = select.select([client], [client], [], npi)
        # Reset the next poll interval
        npi = self._next_poll_interval = self._poll_interval
        if client in rfds:
            log_debug(5, "before process")
            client.process(timeout=None)
            log_debug(5, "after process")
        if wfds:
            # Timeout
            log_debug(5,"Notifying jabber nodes")
            self._notifier.notify_jabber_nodes()
        else:
            log_debug(5,"Not notifying jabber nodes")


    _query_reap_pinged_clients = rhnSQL.Statement("""
        update rhnPushClient
           set state_id = :offline_id
         where state_id = :online_id
           and last_ping_time is not null
           and current_timestamp > next_action_time
    """)
    def reap_pinged_clients(self):
        # Get the online and offline ids
        online_id = self._get_push_state_id('online')
        offline_id = self._get_push_state_id('offline')

        h = rhnSQL.prepare(self._query_reap_pinged_clients)
        ret = h.execute(online_id=online_id, offline_id=offline_id)
        if ret:
            # We have changed something
            rhnSQL.commit()

    _query_fetch_clients_to_be_pinged = rhnSQL.Statement("""
        select id, name, shared_key, jabber_id
          from rhnPushClient
         where state_id = :online_id
           and last_ping_time is not null
           and next_action_time is null
           and jabber_id is not null
    """)
    _query_update_clients_to_be_pinged = rhnSQL.Statement("""
        update rhnPushClient
           set next_action_time = current_timestamp + numtodsinterval(:delta, 'second')
         where id = :client_id
    """)
    def _fetch_clients_to_be_pinged(self):
        online_id = self._get_push_state_id('online')
        h = rhnSQL.prepare(self._query_fetch_clients_to_be_pinged)
        h.execute(online_id=online_id)
        clients = h.fetchall_dict() or []
        rhnSQL.commit()
        if not clients:
            # Nothing to do
            return

        # XXX Need config option
        delta = 20

        client_ids = [x['id'] for x in clients]
        deltas = [ delta ] * len(client_ids)
        h = rhnSQL.prepare(self._query_update_clients_to_be_pinged)
        h.executemany(client_id=client_ids, delta=deltas)
        rhnSQL.commit()
        return clients

    def _get_push_state_id(self, state):
        if state in self._state_ids:
            return self._state_ids[state]

        t = rhnSQL.Table('rhnPushClientState', 'label')
        row = t[state]
        assert row is not None
        self._state_ids[state] = row['id']
        return row['id']


    _query_update_register_dispatcher = rhnSQL.Statement("""
            update rhnPushDispatcher
               set last_checkin = current_timestamp,
                   hostname = :hostname_in
             where jabber_id = :jabber_id_in
    """)
    _query_insert_register_dispatcher = rhnSQL.Statement("""
                insert into rhnPushDispatcher
                       (id, jabber_id, last_checkin, hostname, password)
                values (sequence_nextval('rhn_pushdispatch_id_seq'), :jabber_id_in, current_timestamp,
                       :hostname_in, :password_in)
    """)

    def _register_dispatcher(self, jabber_id, hostname):
        h = rhnSQL.prepare(self._query_update_register_dispatcher)
        rowcount = h.execute(jabber_id_in=jabber_id, hostname_in=hostname, password_in=self._password)
        if not rowcount:
            h = rhnSQL.prepare(self._query_insert_register_dispatcher)
            h.execute(jabber_id_in=jabber_id, hostname_in=hostname, password_in=self._password)
        rhnSQL.commit()

    _query_get_client_jids = rhnSQL.Statement("""
        select jabber_id, TO_CHAR(modified, 'YYYY-MM-DD HH24:MI:SS') modified
          from rhnPushClient
         where jabber_id is not null
    """)
    def _get_client_jids(self):
        h = rhnSQL.prepare(self._query_get_client_jids)
        h.execute()
        ret = []
        while 1:
            row = h.fetchone_dict()
            if not row:
                break
            # Save the modified time too - we don't want to mark as offline
            # clients that just checked in
            ret.append((row['jabber_id'], row['modified']))
        return ret


class Notifier:
    def __init__(self):
        self._next_poll_interval = None
        self._notify_threshold = CFG.get('notify_threshold')

    def get_next_poll_interval(self):
        return self._next_poll_interval

    def set_jabber_connection(self, jabber_connection):
        self.jabber_connection = jabber_connection

    def get_running_clients(self):
        log_debug(3)
        # only with contact_method = default. SSH Push has its own value
        h = rhnSQL.prepare(self._query_get_running_clients)
        h.execute()
        row = h.fetchone_dict() or {}
        return int(row.get("clients", 0))

    def notify_jabber_nodes(self):
        log_debug(3)
        running_clients = self.get_running_clients()
        free_slots = 0
        if self._notify_threshold:
             free_slots = self._notify_threshold - running_clients
        log_debug(4, "notify_threshold: %s running_clients: %s free_slots: %s" %
                (self._notify_threshold, running_clients, free_slots))

        h = rhnSQL.prepare(self._query_get_pending_clients)
        h.execute()
        self._next_poll_interval = None
        notified = []

        while 1:
            if self._notify_threshold and free_slots <= 0:
                # End of loop
                log_debug(4, "max running clients reached; stop notifying")
                break

            row = h.fetchone_dict()
            if not row:
                # End of loop
                break

            delta = row['delta']
            if delta > 0:
                # Set the next poll interval to something large if it was not
                # previously set before; this way min() will pick up this
                # delta, but we don't have to special-case the first delta we
                # find
                npi = self._next_poll_interval or 86400
                self._next_poll_interval = min(delta, npi)
                log_debug(4, "Next poll interval", delta)
                continue

            jabber_id = row['jabber_id']
            if jabber_id is None:
                # Not even online
                continue
            server_id = row['server_id']
            if server_id and reboot_in_progress(server_id):
                # don't call when a reboot is in progress
                continue

            if not self.jabber_connection.jid_available(jabber_id):
                log_debug(4, "Node %s not available for notifications" %
                    jabber_id)
                # iterate further, in case there are other clients that
                # CAN be notified.
                continue

            log_debug(4, "Notifying", jabber_id, row['server_id'])
            self.jabber_connection.send_message(jabber_id,
                jabber_lib.NS_RHN_MESSAGE_REQUEST_CHECKIN)
            if jabber_id not in notified:
                free_slots -= 1
                notified.append(jabber_id)
        rhnSQL.commit()

    # We need to drive this query by rhnPushClient since it's substantially
    # smaller than rhnAction
    # order by status, earliest_action, server_id to get
    # "Queued" first with earliest_action first. If multiple clients have the
    # same values, finally order by server_id to get a defined order
    # important for notify_threshold
    _query_get_pending_clients = rhnSQL.Statement("""
        select a.id, sa.server_id, pc.jabber_id,
               date_diff_in_days(current_timestamp, earliest_action) * 86400 delta
          from
               rhnServerAction sa,
               rhnAction a,
               rhnPushClient pc
         where pc.server_id = sa.server_id
           and sa.action_id = a.id
           and sa.status in (0, 1) -- Queued or picked up
           and not exists (
               -- This is like saying 'this action has no
               -- prerequisite or has a prerequisite that has completed
               -- (status = 2)
               select 1
                 from rhnServerAction sap
                where sap.server_id = sa.server_id
                  and sap.action_id = a.prerequisite
                  and sap.status != 2
            )
         order by sa.status, earliest_action, sa.server_id
    """)

    _query_get_running_clients = rhnSQL.Statement("""
        select count(distinct sa.server_id) clients
          from rhnServerAction sa
          join rhnServer s ON sa.server_id = s.id
          join suseServerContactMethod sscm ON sscm.id = s.contact_method_id
         where sscm.label = 'default'
           and sa.status = 1 -- picked up
    """)

def reboot_in_progress(server_id):
    """check for a reboot action for this server in status Picked Up"""
    h = rhnSQL.prepare("""
        select 1
          from rhnServerAction sa
          join rhnAction a on sa.action_id = a.id
          join rhnActionType at on a.action_type = at.id
         where sa.server_id = :server_id
           and at.label = 'reboot.reboot'
           and sa.status = 1 -- Picked Up
    """)
    h.execute(server_id = server_id)
    ret = h.fetchone_dict() or None
    if ret:
        return True
    return False


if __name__ == '__main__':
    sys.exit(main() or 0)
07070100000028000081B40000000000000000000000015E282E5600004234000000000000000000000000000000000000001500000000mgr-osad/src/osad.py#
# Copyright (c) 2008--2017 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import re
import sys
import time
from uyuni.common.usix import ListType
from rhn import rpclib
import random
import socket

from up2date_client.config import initUp2dateConfig
from up2date_client import config

try: # python 3
    PY3 = sys.version_info.major >= 3
except AttributeError: # python 2
    PY3 = False

if PY3:
    import urllib.parse as urlparse
    from osad.rhn_log import set_debug_level, log_debug, die, set_logfile
    from osad import jabber_lib, osad_config, osad_client
else:
    import urlparse
    from rhn_log import set_debug_level, log_debug, die, set_logfile
    import jabber_lib
    import osad_config
    import osad_client

def main():
    return Runner().main()

class Runner(jabber_lib.Runner):
    client_factory = osad_client.Client

    # How often will we try to reconnect. We want this randomized, so not all
    # clients hit the server at the same time
    _min_sleep = 60
    _max_sleep = 120

    def __init__(self):
        jabber_lib.Runner.__init__(self)
        self._up2date_config = None
        self._config = None
        self._xmlrpc_server = None
        self._systemid_file = None
        self._time_drift = 0
        self.options_table.extend([
            self.option('--cfg',                action="store",
                help="Use this configuration file for defaults"),
            self.option('--jabber-server',      action="store",
                help="Primary jabber server to connect to"),
        ])
        self._config_options = {}
        # Counter for the number of config setups we had
        self._config_setup_counter = 0
        # How often to re-setup the config (i.e. make xmlrpc requests to get
        # the config from the server)
        self._config_setup_interval = random.randint(50, 100)
        self._use_proxy = 1

    def setup_config(self, config, force=0):
        # We don't want to slam the server with lots of XMLRPC requests at the
        # same time, especially if jabberd goes down - in that case all
        # clients are slamming the server at the same time
        try:
            if (self._config_setup_counter % self._config_setup_interval == 0) or \
               force:
                # This will catch the first pass too
                self._setup_config(config, force)
            else:
                log_debug(4, "Skipping config setup; counter=%s; interval=%s" %
                    (self._config_setup_counter, self._config_setup_interval))
        except:
            self._config_setup_counter = 0
            raise

        # Update the counter for the next time
        self._config_setup_counter = self._config_setup_counter + 1

    def _setup_config(self, config, force=0):
        logfile = self.options.logfile
        if logfile is None or logfile == '':
            logfile = config['logfile']

        debug_level = self.options.verbose
        if debug_level is None:
            dl = config['debug_level']
            if dl is not None:
                debug_level = int(dl)
            else:
                dl = 0

        set_logfile(logfile)
        self.debug_level = debug_level
        set_debug_level(debug_level)

        self._tcp_keepalive_timeout = config['tcp_keepalive_timeout']
        self._tcp_keepalive_count = config['tcp_keepalive_count']

        log_debug(3, "Updating configuration")

        client_ssl_cert = config['ssl_ca_cert']
        osa_ssl_cert = config['osa_ssl_cert'] or client_ssl_cert
        if osa_ssl_cert is None:
            die("No SSL cert supplied")

        self.ssl_cert = osa_ssl_cert

        auth_info = self.read_auth_info(force)

        self._username = auth_info['username']
        self._password = auth_info['password']
        self._resource = auth_info['resource']

        server_url = config.get('server_url')

        self._jabber_servers = []
        if self.options.jabber_server:
            self._jabber_servers.append(self.options.jabber_server)

        if type(server_url) == type([]):
            for su in server_url:
                a_su = self._parse_url(su)[1]
                self._jabber_servers.append(a_su)
        else:
            upstream_jabber_server = self._parse_url(server_url)[1]
            if upstream_jabber_server not in self._jabber_servers:
                self._jabber_servers.append(upstream_jabber_server)

        if 'enable_failover' not in config or config['enable_failover'] != '1':
            self._jabber_servers = [self._jabber_servers[0]]

        # Load the config
        self._config_options.clear()
        self._config_options.update(config)
        # No reason to expose these at the Client level - but if we have to,
        # uncommment some of the values below
        self._config_options.update({
        #    'jabber-servers'    : self._jabber_servers,
        #    'dispatchers'       : self._dispatchers,
        #    'client_name'       : self._client_name,
        #    'shared_key'        : self._shared_key,
        })


    def _parse_url(self, url, scheme="http"):
        sch, netloc, path, params, query, fragment = urlparse.urlparse(url)
        if not netloc:
            # No schema - trying to patch it up ourselves?
            url = scheme + "://" + url
            sch, netloc, path, params, query, fragment = urlparse.urlparse(url)
        return sch, netloc, path, params, query, fragment

    def fix_connection(self, c):
        "After setting up the connection, do whatever else is necessary"

        # Setup XMLRPC server
        xmlrpc_params = self.build_rpclib_params(self._config_options)

        # Looking for a server we connected to jabberd on
        server_urls = self._config_options['server_url']
        for url in server_urls:
            if self._connected_jabber_server in url:
                xmlrpc_params['uri'] = url
                break

        server = rpclib.Server(**xmlrpc_params)
        self._xmlrpc_server = server

        client_ssl_cert = self._config_options['ssl_ca_cert']
        osa_ssl_cert = self._config_options['osa_ssl_cert'] or client_ssl_cert
        if osa_ssl_cert:
            server.add_trusted_cert(osa_ssl_cert)

        server.registration.welcome_message()

        server_capabilities = get_server_capability(server)
        if 'registration.register_osad' not in server_capabilities:
            raise Exception("Server does not support OSAD registration")

        self._systemid_file = self._config_options['systemid']
        self._systemid = open(self._systemid_file).read()

        current_timestamp = int(time.time())
        ret = server.registration.register_osad(self._systemid,
                                                {'client-timestamp': current_timestamp})

        #Bugzilla: 142067
        #If the server doesn't have push support. 'ret' won't have anything in it.
        if len(ret.keys()) < 1:
            raise jabber_lib.JabberConnectionError

        js = ret.get('jabber-server')
        if js not in self._jabber_servers:
            self._jabber_servers.append(js)


        server_timestamp = ret.get('server-timestamp')
        # Compute the time drift between the client and the server
        self._time_drift = server_timestamp - current_timestamp
        log_debug(2, "Time drift", self._time_drift)

        self._dispatchers = ret.get('dispatchers')
        self._client_name = ret.get('client-name')
        self._shared_key = ret.get('shared-key')
        log_debug(2, "Client name", self._client_name)
        log_debug(2, "Shared key", self._shared_key)


        c.set_config_options(self._config_options)
        c.client_id = self._client_name
        c.shared_key = self._shared_key
        c.time_drift = self._time_drift
        c._sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
        c._sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, self._tcp_keepalive_timeout)
        c._sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, self._tcp_keepalive_count)

        # Update the jabber ID
        systemid = open(self._systemid_file).read()
        args = {
            'jabber-id' : str(c.jid),
        }
        ret = self._xmlrpc_server.registration.register_osad_jid(systemid,
            args)

        c.set_dispatchers(self._dispatchers)

        c.subscribe_to_presence(self._dispatchers)
        # Signal presence to the jabber server
        c.send_presence()
        return c

    def preprocess_once(self, client):
        # BZ 1410781
        # If the system just started following a reboot event,
        # we need to run rhn_check in order to let the server
        # know the reboot is complete, otherwise it won't send
        # any further events to us.
        #
        # not needed for SUSE systems - we have update-status-service
        # to do this.
        #
        #super(Runner, self).preprocess_once(client)
        #client.run_rhn_check_async()
        return client

    def process_once(self, client):

        # Re-read the systemid file.  If it's changed from the
        # previous version re-setup the config.  This will create a new
        # key on the satellite server tied to this new system id.
        # This change prevents having to restart osad after a system
        # re-registration.
        systemid = open(self._systemid_file).read()
        if systemid != self._systemid:
            log_debug(4, "System re-registration detected. systemid file has changed.")
            config = self.read_config()
            raise jabber_lib.NeedRestart

        # make sure that dispatchers are not stuck in state [none + ask] or [from + ask]
        # for too long. This can happen, for example, if a "subscribe" presence stanza
        # gets lost - in that case re-send it
        client.unstick_contacts(self._dispatchers)

        # if rhn_check is running or the last one failed, check more often
        if (client._rhn_check_process is None) and (client._rhn_check_fail_count < 1):
            client.process(timeout=180)
        else:
            client.process(timeout=5)

    def read_config(self):
        ret = {}
        # Read from the global config first
        config_file = self.options.cfg
        self._config = osad_config.init('osad', config_file=config_file)
        config_keys = ['debug_level', 'osa_ssl_cert', 'logfile', 'run_rhn_check',
            'rhn_check_command', 'enable_failover']
        for key in config_keys:
            ret[key] = osad_config.get(key)

        try:
            server_url = osad_config.get('server_url')
        except osad_config.InterpolationError:
            e = sys.exc_info()[1]
            server_url = config.getServerlURL()
        else:
            if not server_url:
                server_url = config.getServerlURL()
            else:
                def convert_url(s):
                    s = s.strip()
                    if hasattr(config, 'convert_url_to_puny'):
                       s = config.convert_url_to_puny(s)
                    elif hasattr(config, 'convert_url_to_pune'):
                       s = config.convert_url_to_pune(s)
                    return s

                server_url = [convert_url(i) for i in server_url.split(';')]

        # Remove empty URLs
        for url in server_url:
            if not url:
                server_url.remove(url)

        # Real unusual case if there is no server URL both in up2date and osad config files
        if not server_url:
            die("Missing server URL in config file")

        ret['server_url'] = server_url

        #8/23/05 wregglej 165775 added the run_rhn_check option.
        run_rhn_check = osad_config.get('run_rhn_check')
        if run_rhn_check is None:
            log_debug(3, "Forcing run_rhn_check")
            run_rhn_check = 1
        ret['run_rhn_check'] = int(run_rhn_check)

        ret['tcp_keepalive_timeout'] = int(osad_config.get('tcp_keepalive_timeout', defval=1800))
        ret['tcp_keepalive_count'] = int(osad_config.get('tcp_keepalive_count', defval=3))

        systemid = osad_config.get('systemid')
        if systemid is None:
            systemid = self.get_up2date_config()['systemIdPath']
        ret['systemid'] = systemid

        enable_proxy = self._config.get_option('enableProxy')
        if enable_proxy is None:
            enable_proxy = self.get_up2date_config()['enableProxy']

        if enable_proxy:
            ret['enable_proxy'] = 1

            ret['proxy_url'] = self._config.get_option('httpProxy')
            if ret['proxy_url'] is None:
                ret['proxy_url'] = str(config.getProxySetting())

            ret['enable_proxy_auth'] = 0
            enable_proxy_auth = self._config.get_option('enableProxyAuth')
            if enable_proxy_auth is None:
                enable_proxy_auth = self.get_up2date_config()['enableProxyAuth']

            if enable_proxy_auth:
                ret['enable_proxy_auth'] = 1
                proxy_user = self._config.get_option('proxyUser')
                if proxy_user is None:
                    proxy_user = self.get_up2date_config()['proxyUser']
                ret['proxy_user'] = proxy_user

                proxy_password = self._config.get_option('proxyPassword')
                if proxy_password is None:
                    proxy_password = self.get_up2date_config()['proxyPassword']
                ret['proxy_password'] = proxy_password

        if not server_url:
            die("Unable to retrieve server URL")

        # SSL cert for Jabber's TLS, it can potentially be different than the
        # client's
        osa_ssl_cert = self._config.get_option('osa_ssl_cert')
        # The up2date ssl cert - we get it from up2daate's config file
        client_ca_cert = self.get_up2date_config()['sslCACert']
        if isinstance(client_ca_cert, ListType):
            if client_ca_cert:
                client_ca_cert = client_ca_cert[0]
            else:
                client_ca_cert = None
        if osa_ssl_cert is None:
            # No setting, use up2date's
            osa_ssl_cert = client_ca_cert

        if client_ca_cert is not None:
            ret['ssl_ca_cert'] = client_ca_cert
        if osa_ssl_cert is not None:
            ret['osa_ssl_cert'] = osa_ssl_cert

        return ret

    def get_up2date_config(self):
        if self._up2date_config is None:
            self._up2date_config = initUp2dateConfig()
        return self._up2date_config

    def build_rpclib_params(self, config):
        ret = {}
        kmap = {
            'server_url'        : 'uri',
            'proxy_user'        : 'username',
            'proxy_password'    : 'password',
            'proxy_url'         : 'proxy',
        }
        for k, v in kmap.items():
            if k in config:
                val = config[k]
                if val is not None:
                    ret[v] = val
        return ret

    def read_auth_info(self, force):
        # generate some defaults
        resource = 'osad'
        username = 'osad-%s' % jabber_lib.generate_random_string(10)
        password = jabber_lib.generate_random_string(20)

        # Get the path to the auth info file - may be None
        auth_info_file = self._config.get_option('auth_file')
        auth_info = osad_config.get_auth_info(auth_info_file, 'osad-auth', force,
            username=username, password=password, resource=resource)
        return auth_info

def get_server_capability(s):
    headers = s.get_response_headers()
    if headers is None:
        # No request done yet
        return {}
    if PY3:
        cap_headers = ["X-RHN-Server-Capability: %s" % val for val in headers.get_all("X-RHN-Server-Capability")]
    else:
        cap_headers = headers.getallmatchingheaders("X-RHN-Server-Capability")

    if not cap_headers:
        return {}
    regexp = re.compile(
            r"^(?P<name>[^(]*)\((?P<version>[^)]*)\)\s*=\s*(?P<value>.*)$")
    vals = {}
    for h in cap_headers:
        arr = h.split(':', 1)
        assert len(arr) == 2
        val = arr[1].strip()
        if not val:
            continue

        mo = regexp.match(val)
        if not mo:
            # XXX Just ignoring it, for now
            continue
        vdict = mo.groupdict()
        for k, v in vdict.items():
            vdict[k] = v.strip()

        vals[vdict['name']] = vdict
    return vals

if __name__ == '__main__':
    sys.exit(main() or 0)
07070100000029000081B40000000000000000000000015E282E560000280F000000000000000000000000000000000000001C00000000mgr-osad/src/osad_client.py#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import os
import time
import sys
from subprocess import Popen

try: # python 3
    PY3 = sys.version_info.major >= 3
except AttributeError: # python 2
    PY3 = False

if PY3:
    from osad.rhn_log import log_debug
    from osad import jabber_lib
else:
    from rhn_log import log_debug
    import jabber_lib

class Client(jabber_lib.JabberClient):

    RHN_CHECK_CMD = '/usr/sbin/rhn_check'

    def __init__(self, *args, **kwargs):
        jabber_lib.JabberClient.__init__(self, *args, **kwargs)
        self.username = None
        self.resource = None
        self.client_id = None
        self.shared_key = None
        self.debug_level = 0
        self.time_drift = 0
        self._dispatchers = []
        self._config = {}
        self._rhn_check_process = None
        self._rhn_check_fail_count = 0
        self._stuck_subscription_timestamp = time.time()

    def set_config_options(self, config):
        self._config = config

    def set_debug_level(self, debug_level):
        self.debug_level = debug_level

    def set_dispatchers(self, dispatchers):
        self._dispatchers = dispatchers

    def start(self, username, password, resource):
        log_debug(3, username, password, resource)
        # XXX find a better name for this function
        self.auth(username, password, resource)
        self.username = username
        self.resource = resource
        self.jid = "%s@%s/%s" % (self.username, self._host, self.resource)

        # Retrieve roster
        self.retrieve_roster()

    def _create_signature(self, jid, action):
        log_debug(4, jid, action)
        attrs = {
            'client-id'     : self.client_id,
            'timestamp'     : int(time.time()),
            'serial'        : self.get_unique_id(),
            'action'        : action,
            'jid'           : self.jid,
        }
        signing_comps = ['client-id', 'timestamp', 'serial', 'action', 'jid']
        args = [self.shared_key, jid]
        for sc in signing_comps:
            args.append(attrs[sc])

        log_debug(4, "Signature args", args)
        attrs['signature'] = jabber_lib.sign(*args)

        x = jabber_lib.jabber.xmlstream.Node('x')
        x.setNamespace(jabber_lib.NS_RHN_SIGNED)
        for k, v in attrs.items():
            x.putAttr(k, v)
        return x

    def _lookup_dispatcher(self, jid):
        # presence may not send a resource in the JID
        if not isinstance(jid, jabber_lib.jabber.JID) or jid.resource:
            return str(jid)
        jid = str(jid)
        jid_len = len(jid)
        for d in self._dispatchers:
            if d[:jid_len] != jid:
                continue
            assert len(d) > jid_len
            if d[jid_len] == '/':
                # This is it
                return d
        return None

    def _fix_jid(self, jid):
        return self._lookup_dispatcher(jid)

    def _check_signature(self, stanza, actions=None):
        # Do we have this client in the table?
        jid = stanza.getFrom()
        if jid is None:
            log_debug(3, 'no from')
            return None
        # Look for a <x> child that has our namespace
        xes = stanza.getTags('x')
        for x in xes:
            if x.getNamespace() != jabber_lib.NS_RHN_SIGNED:
                continue
            break
        else: #for
            log_debug(1, "No signature node found in stanza")
            return None

        timestamp = x.getAttr('timestamp')
        try:
            timestamp = int(timestamp)
        except ValueError:
            log_debug(1, "Invalid message timestamp", timestamp)
            return None
        now = time.time()

        current_drift = timestamp - now
        # Allow for a 120 seconds drift
        max_drift = 120
        abs_drift = abs(current_drift - self.time_drift)
        if abs_drift > max_drift:
            log_debug(1, "Dropping message, drift is too big", abs_drift)

        action = x.getAttr('action')

        if actions and action not in actions:
            log_debug(1, "action %s not allowed" % action)
            return None

        # We need the fully qualified JID here too
        full_jid = x.getAttr('jid')
        if not full_jid:
            log_debug(3, "Full JID not found in signature stanza")
            return None

        attrs = {
            'timestamp'     : x.getAttr('timestamp'),
            'serial'        : x.getAttr('serial'),
            'action'        : x.getAttr('action'),
            'jid'           : full_jid,
        }
        signing_comps = ['timestamp', 'serial', 'action', 'jid']
        args = [self.shared_key, self.jid]
        for sc in signing_comps:
            args.append(attrs[sc])

        log_debug(4, "Signature args", args)
        signature = jabber_lib.sign(*args)
        x_signature = x.getAttr('signature')
        if signature != x_signature:
            log_debug(1, "Signatures do not match", signature, x_signature)
            return None
        # Happy joy
        return x

    def _message_callback(self, client, stanza):
        log_debug(4)
        assert stanza.getName() == 'message'

        # Actions we know how to react to
        actions = [
            jabber_lib.NS_RHN_MESSAGE_REQUEST_CHECKIN,
            jabber_lib.NS_RHN_MESSAGE_REQUEST_PING,
        ]
        sig = self._check_signature_from_message(stanza, actions)
        if not sig:
            return

        action = sig.getAttr('action')
        if action == jabber_lib.NS_RHN_MESSAGE_REQUEST_PING:
            log_debug(1, 'Ping request')
            self.send_message(stanza.getFrom(),
                jabber_lib.NS_RHN_MESSAGE_RESPONSE_PING)
            return

        # Send confirmation
        self.send_message(stanza.getFrom(),
            jabber_lib.NS_RHN_MESSAGE_RESPONSE_CHECKIN)

        # Checkin
        run_check = self._config.get('run_rhn_check')
        log_debug(3, "run_rhn_check:", run_check)

        if not self._config.get('run_rhn_check'):
            log_debug(0, "Pretend that command just ran")
        else:
            self.run_rhn_check_async()

    def process_loop_hook(self):
        # if rhn_check process exists, check it last
        # status
        if self._rhn_check_process is not None:
            retcode = self._rhn_check_process.poll()
            if retcode is not None:
                log_debug(3, "rhn_check exited with status %d" % retcode)
                if retcode != 0:
                    self._rhn_check_fail_count += 1
                else:
                    self._rhn_check_fail_count = 0
                self._rhn_check_process = None
            else:
                log_debug(3, "rhn_check is still running...")
        else:
            # rhn_check is not running but last one failed
            # we force a check even if the server does not
            # contact us. The idea is to exhaust the number of
            # times we can pick up the action until the server fails
            # it.
            if self._rhn_check_fail_count > 0:
                log_debug(3, "rhn_check failed last time, " \
                          "force retry (fail count %d)" % self._rhn_check_fail_count)
                self.run_rhn_check_async()

    def run_rhn_check_async(self):
        """Runs rhn_check and keeps a handle that it is monitored
        during the event loop
        """
        command = self._config.get('rhn_check_command')
        # rhn_check now checks for multiple instances,
        # lets use that directly
        if command is None:
            args = [self.RHN_CHECK_CMD]
        else:
            # XXX should find a better way to get the list of args
            args = command.split()

        # if rhn_check process already exists
        if self._rhn_check_process is not None:
            retcode = self._rhn_check_process.poll()
            if retcode is None:
                log_debug(3, "rhn_check is still running, not running again...")
                return

        if self._rhn_check_fail_count > 0:
            log_debug(3, "rhn_check failed last time (fail count %d)" % self._rhn_check_fail_count)

        log_debug(3, "About to execute:", args)
        oldumask = os.umask(int("0077", 8))
        os.umask(oldumask | int("0022", 8))
        self._rhn_check_process = Popen(args)
        os.umask(oldumask)
        log_debug(0, "executed %s with pid %d" % (args[0], self._rhn_check_process.pid))

    def unstick_contacts(self, jids):
        """If we are waiting for 'subscribed' presence stanzas for too long, ask again"""
        if time.time() - self._stuck_subscription_timestamp > 60:
            for jid in jids:
                stripped_jid = self._strip_resource(jid)
                if self.needs_unsticking(stripped_jid):
                    presence_node = jabber_lib.JabberPresenceNode(to=stripped_jid, type="subscribe")
                    presence_node.setID("presence-%s" % self.get_unique_id())
                    log_debug(4, "Re-sending presence subscription request", presence_node)
                    self.send(presence_node)
            self._stuck_subscription_timestamp = time.time()

    def needs_unsticking(self, jid):
        """Returns True if jid is in state [none + ask] or [from + ask]"""
        contact = None

        subscribed_none = self._roster.get_subscribed_none()
        if jid in subscribed_none:
            contact = subscribed_none[jid]

        subscribed_from = self._roster.get_subscribed_from()
        if jid in subscribed_from:
            contact = subscribed_from[jid]

        if contact is not None:
            return 'ask' in contact and contact['ask'] == 'subscribe'

        return False
0707010000002A000081B40000000000000000000000015E282E5600000FE0000000000000000000000000000000000000001C00000000mgr-osad/src/osad_config.py#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import os
import sys

try: # python 3
    import configparser as ConfigParser
except ImportError: # python 2
    import ConfigParser

InterpolationError = ConfigParser.InterpolationError


class ClientConfigParser(ConfigParser.ConfigParser):
    _instance = None
    _global_config_file = "/etc/sysconfig/rhn/osad.conf"

    def __init__(self, section, defaults=None):
        """defaults is either None, or a dictionary of default values which can be overridden"""
        ConfigParser.ConfigParser.__init__(self, defaults)
        self.section = section
        self.overrides = {}

    def read_config_files(self, overrides={}):
        self.overrides.clear()
        self.overrides.update(overrides)

        try:
            self.read(self._get_config_files())
        except ConfigParser.MissingSectionHeaderError:
            e = sys.exc_info()[1]
            print("Config error: line %s, file %s: %s" % (e.lineno,
                e.filename, e))
            sys.exit(1)

    def _get_config_files(self):
        return [
            self._global_config_file,
        ]

    def get_option(self, option, defval=None):
        try:
            return self.get(self.section, option, vars=self.overrides)
        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
            e = sys.exc_info()[1]
            pass

        defaults = self.defaults()

        if option in defaults:
            return defaults[option]

        return defval

    def has_key(self, option):
        return self.has_option(self.section, option)

    def keys(self):
        return self.options(self.section)

    def __getitem__(self, item):
        return self.get_option(item)

class UserPassConfigParser(ClientConfigParser):
    _global_config_file = "/etc/sysconfig/rhn/osad-auth.conf"

def init(section, defaults=None, config_file=None, **overrides):
    cp = ClientConfigParser._instance = ClientConfigParser(section, defaults)
    # Allow for the config file to be changed, if necessary
    if config_file is not None:
        cp._global_config_file = config_file
    cp.read_config_files(overrides)
    return cp

def get(var, defval=None):
    return _get_config().get_option(var, defval=defval)

def _get_config():
    if ClientConfigParser._instance is None:
        raise ValueError("Configuration not initialized")
    return ClientConfigParser._instance

def instance():
    return _get_config()

def keys():
    return _get_config().keys()

def get_auth_info(auth_file, section, force, **defaults):
    _modified = 0
    c = UserPassConfigParser(section)
    if auth_file is not None:
        c._global_config_file = auth_file
    c.read_config_files()
    if not c.has_section(section):
        _modified = 1
        c.add_section(section)
    for k, v in defaults.items():
        if not c.has_option(section, k) or force:
            c.set(section, k, v)
            _modified = 1
    if _modified:
        fd = os.open(c._global_config_file,
            os.O_CREAT | os.O_TRUNC | os.O_WRONLY, int("0600", 8))
        f = os.fdopen(fd, "w")
        f.write("# Automatically generated. Do not edit!\n\n")
        c.write(f)
        f.close()
    return c

def main():
    init('osad')
    print("server_url: %s" % get("server_url"))

    auth_file = "/tmp/osad-auth.conf"
    section = "osad-auth"
    c = get_auth_info(auth_file, section, username="aaa", password="bbb")
    print(c.keys())

if __name__ == '__main__':
    main()
0707010000002B000081B40000000000000000000000015E282E5600000A24000000000000000000000000000000000000001800000000mgr-osad/src/rhn_log.py#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import os
import sys
import time
import traceback
from rhn.i18n import bstr

class Logger:
    debug_level = 1
    logfile = "/var/log/osad"

    def set_logfile( self, logfile ):
        Logger.logfile = logfile

    def log_debug(self, debug_level, *args):
        if debug_level <= self.debug_level:
            info_out =  (
                time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())),
                self.get_caller(),
                " ".join([str(x) for x in args])
            )

            outstring = "%s %s: %s\n" % info_out
            sys.stdout.write( outstring )

            if not Logger.logfile is None:
                try:
                    fd = os.open(Logger.logfile, os.O_APPEND | os.O_RDWR | os.O_CREAT, int("0600", 8))
                    os.write(fd, bstr(outstring))
                    os.close(fd)
                except IOError:
                    raise

    def set_debug_level(self, debug_level):
        Logger.debug_level = debug_level

    def get_caller(self, caller_offset=4):
        tbStack = traceback.extract_stack()
        callid = len(tbStack) - caller_offset
        module = tbStack[callid]
        module_file = os.path.basename(module[0])
        module_file = module_file.split('.', 1)[0]
        return "%s.%s" % (module_file, module[2])

    def log_error(self, *args):
        self.log_debug(0, *args)
        line = [str(x) for x in args]
        sys.stderr.write(" ".join(line))
        sys.stderr.write("\n")

    def die(self, error_code, *args):
        self.log_error(args)
        sys.exit(error_code)

def set_logfile(*args):
    return Logger().set_logfile(*args)

def set_debug_level(*args):
    return Logger().set_debug_level(*args)

def get_debug_level():
    return Logger().debug_level

def log_debug(*args):
    return Logger().log_debug(*args)

def log_error(*args):
    return Logger().log_error(*args)

def die(error_code, *args):
    Logger().log_error(*args)
    sys.exit(error_code)
0707010000002C000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000001300000000mgr-osad/sysconfig0707010000002D000081B40000000000000000000000015E282E56000000C6000000000000000000000000000000000000001C00000000mgr-osad/sysconfig/Makefile# Makefile for installation of the RHN server configuration files

# what is the backend top dir
TOP	= ..

INSTALL_FILES	= osa-dispatcher
INSTALL_DEST	= /etc/sysconfig

include $(TOP)/Makefile.defs
0707010000002E000081B40000000000000000000000015E282E560000003F000000000000000000000000000000000000002200000000mgr-osad/sysconfig/osa-dispatcher
TNS_ADMIN=/etc/rhn/tns_admin/osa-dispatcher
export TNS_ADMIN

0707010000002F000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000000E00000000mgr-osad/test07070100000030000081B40000000000000000000000015E282E56000009BC000000000000000000000000000000000000001F00000000mgr-osad/test/simple-client.py#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import sys
import jabber_lib

import test_lib

class SimpleClientClient(test_lib.SimpleClient):
    pass

class SimpleClientRunner(test_lib.SimpleRunner):
    client_factory = SimpleClientClient
    _resource = 'client'

    def __init__(self, *args, **kwargs):
        test_lib.SimpleRunner.__init__(self, *args, **kwargs)

        self.options_table.extend([
            self.option('--dispatcher',           action="store",
                help="Dispatcher"),
            self.option('--exit',                 action="store_true",
                help="Exit after registering"),
            ])

    def setup_config(self, config):
        test_lib.SimpleRunner.setup_config(self, config)
        self.dispatcher = self.options.dispatcher
        self._should_exit = self.options.exit
        if not self.dispatcher:
            print("Missing dispatcher")
            sys.exit(0)

    def fix_connection(self, client):
        # First, retrieve the roster
        client.retrieve_roster()

        # If not subscribed already, subscribe to the dispatcher
        dest = str(jabber_lib.strip_resource(self.dispatcher))
        client.subscribe_to_presence([dest])
        client._roster._subscribed_to[dest] = {
            'jid'           : dest,
            'subscription'  : "to",
        }
        client.send_presence()

    def process_once(self, client):
        ret = test_lib.SimpleRunner.process_once(self, client)
        if not self._should_exit:
            return ret

        # Wait for a presence subscription request
        djid = str(jabber_lib.strip_resource(self.dispatcher))

        if djid in client._roster.get_subscribed_both():
            client.disconnect()
            sys.exit(0)

        return ret

def main():

    d = SimpleClientRunner('username1', 'password1', "client")
    d.main()

if __name__ == '__main__':
    sys.exit(main() or 0)

07070100000031000081B40000000000000000000000015E282E560000043F000000000000000000000000000000000000002300000000mgr-osad/test/simple-dispatcher.py#
# Copyright (c) 2008--2013 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import sys

import test_lib

class SimpleDispatcherClient(test_lib.SimpleClient):
    pass

class SimpleDispatcherRunner(test_lib.SimpleRunner):
    client_factory = SimpleDispatcherClient
    _resource = 'DISPATCHER'

    def fix_connection(self, client):
        client.retrieve_roster()
        client.send_presence()

def main():

    d = SimpleDispatcherRunner('username1', 'password1', "DISPATCHER")
    d.main()

if __name__ == '__main__':
    sys.exit(main() or 0)
07070100000032000081B40000000000000000000000015E282E5600000CB5000000000000000000000000000000000000001A00000000mgr-osad/test/test_lib.py#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

import sys
import jabber_lib
import time
import rhn_log

class SimpleClient(jabber_lib.JabberClient):
    def start(self, username, password, resource):
        t0 = time.time()
        self.auth(username, password, resource)
        t1 = time.time()
        print("TIMING: auth: %.3f" % (t1 - t0))
        self.username = username
        self.resource = resource
        self.jid = "%s@%s/%s" % (self.username, self._host, self.resource)
        rhn_log.log_debug(0, "Authenticated", self.jid)

    def _message_callback(self, client, stanza):
        pass

class SimpleRunner(jabber_lib.Runner):

    def __init__(self, username, password, resource):
        jabber_lib.Runner.__init__(self)
        self._username = username
        self._password = password
        self._resource = resource
        self.log_file = '/tmp/simple-dispatcher.log'

        self.options_table.extend([
            self.option('--jabberd',              action="append",
                help="Use this jabber server"),
            self.option('--trusted-cert',         action="store",
                help="Use this trusted CA cert"),
            self.option('--username',             action="store",
                help="Username"),
            self.option('--password',             action="store",
                help="Password"),
            self.option('--resource',             action="store",
                help="Resource"),
            ])


    def set_log_file(self, log_file):
        self.log_file = log_file

    def set_trusted_cert(self, trusted_cert):
        self.ssl_cert = trusted_cert

    def read_config(self):
        return {
            'logfile'       : self.log_file,
        }

    def setup_config(self, config):
        self.options.nodetach = 1
        self.debug_level = self.options.verbose
        rhn_log.set_logfile(config.get('logfile') or "/dev/null")

        self._username = self.options.username
        self._password = self.options.password
        if not self._username:
            print("Missing username")
            sys.exit(0)
        if not self._password:
            print("Missing password")
            sys.exit(0)
        if self.options.resource:
            self._resource = self.options.resource

        if not self.options.jabberd:
            print("Missing jabber servers")
            sys.exit(0)

        if not self.options.trusted_cert:
            print("Missing trusted cert")
            sys.exit(0)
        self.ssl_cert = self.options.trusted_cert

        self._jabber_servers.extend(self.options.jabberd)

    def process_once(self, client):
        client.process(timeout=None)
07070100000033000041FD0000000000000000000000015E282E5600000000000000000000000000000000000000000000002200000000mgr-osad/tns-admin-osa-dispatcher07070100000034000081B40000000000000000000000015E282E56000000E5000000000000000000000000000000000000002B00000000mgr-osad/tns-admin-osa-dispatcher/Makefile# Makefile for installation of the RHN server configuration files

# what is the backend top dir
TOP	= ..

INSTALL_FILES	= sqlnet.ora
INSTALL_LINKS	=
INSTALL_DEST	= /etc/rhn/tns_admin/osa-dispatcher

include $(TOP)/Makefile.defs
07070100000035000081B40000000000000000000000015E282E5600000053000000000000000000000000000000000000002D00000000mgr-osad/tns-admin-osa-dispatcher/sqlnet.ora
log_directory_client = /var/log/rhn/oracle/osa-dispatcher
diag_adr_enabled = off

07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
openSUSE Build Service is sponsored by