File mgr-cfg-git-0.27b2044.obscpio of Package mgr-cfg.12115

07070100000000000041FD0000000000000000000000015D403FC800000000000000000000000000000000000000000000000800000000mgr-cfg07070100000001000081B40000000000000000000000015D403FC8000046AC000000000000000000000000000000000000001000000000mgr-cfg/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.
07070100000002000081B40000000000000000000000015D403FC800000022000000000000000000000000000000000000001100000000mgr-cfg/Makefileinclude ../../../rel-eng/Makefile
07070100000003000081B40000000000000000000000015D403FC800000943000000000000000000000000000000000000001600000000mgr-cfg/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

BINDIR		?= /usr/bin
CONFDIR         ?= /etc/sysconfig/rhn

# 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) $(PYCFILES)
INSTALL_DEST	?= $(ROOT)/$(SUBDIR)

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

LN            ?= ln -sfv

all :: $(INSTALL_FILES)

install :: all $(DIRS) $(INSTALL_FILES)
	@$(foreach f,$(INSTALL_FILES), \
		$(INSTALL_DATA) $(f) $(PREFIX)$(INSTALL_DEST)/$(f) ; )

$(DIRS):
	$(INSTALL_DIR) $@

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

%.8 : %.sgml
	[ -f /usr/bin/docbook2man ] && /usr/bin/docbook2man $< || true

# 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)
PYTHONVERSION   ?= 2
PYCHECKER       = pychecker
PYCHECKEROPTS   = --maxreturns 10 --maxbranches 15

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

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

make_rel	= $(shell a="$1"; b=; while [ "$$a" != '/' ]; do \
		    a=$$(dirname $$a); \
		    if [ -z "$$b" ]; then b=".."; else b="$$b/.."; fi; \
		    done; echo $$b)

install-bin: $(PREFIX)$(BINDIR) $(addsuffix .py,$(EXEC_FILES))
	@$(foreach f,$(EXEC_FILES), \
	    chmod 755 $(PREFIX)$(INSTALL_DEST)/$(f).py; \
	    $(LN) $(call make_rel,$(BINDIR))$(INSTALL_DEST)/$(f).py $(PREFIX)$(BINDIR)/$(f)-$(PYTHONVERSION); )

install-conf: $(PREFIX)$(CONFDIR) $(CONFIG_FILES)
	@$(foreach f,$(CONFIG_FILES), \
		$(INSTALL_DATA) $(f) $(PREFIX)$(CONFDIR)/$(f) ; )
07070100000004000081B40000000000000000000000015D403FC800000343000000000000000000000000000000000000001800000000mgr-cfg/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-cfg; 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
07070100000005000081B40000000000000000000000015D403FC8000000C0000000000000000000000000000000000000001800000000mgr-cfg/Makefile.rhncfg# Makefile for spacewalk backend
#

# what is the backend top dir
TOP     = .

# Specific stuff
SUBDIRS		= actions config_common config_client config_management


include $(TOP)/Makefile.defs
07070100000006000041FD0000000000000000000000015D403FC800000000000000000000000000000000000000000000001000000000mgr-cfg/actions07070100000007000081B40000000000000000000000015D403FC80000042E000000000000000000000000000000000000001900000000mgr-cfg/actions/Makefile# Makefile for spacewalk backend
#

# what is the backend top dir
TOP     = ..

# Specific stuff
SUBDIR		= rhn/actions
EXEC_FILES      = rhn-actions-control
FILES           = $(EXEC_FILES) configfiles script \
                  Modes ModeController ModeControllerCreator
CAPS		= configfiles script
CAPS_DIR	= /etc/sysconfig/rhn/clientCaps.d

#man page stuff
DOCBOOK = $(wildcard /usr/bin/docbook2man)
                                                                                                                                                                                   
SGMLS   = $(wildcard *.sgml)
MANS    = $(patsubst %.sgml,%.8,$(SGMLS))
MANDIR  ?= /usr/man


EXTRA_DIRS	= $(MANDIR)/man8 $(BINDIR) $(CAPS_DIR)

include $(TOP)/Makefile.defs

install :: install-bin install-caps

install-caps :: $(CAPS) $(PREFIX)$(CAPS_DIR)
	$(INSTALL_DATA) $(CAPS) $(PREFIX)$(CAPS_DIR)

ifneq ($(DOCBOOK),)
# install man pages
all :: $(MANS)
install :: $(MANS) $(PREFIX)$(MANDIR)
	$(INSTALL_DATA) $(MANS) $(PREFIX)$(MANDIR)/man8
endif

clean ::
	@rm -fv $(MANS) manpage.*

07070100000008000081B40000000000000000000000015D403FC800001496000000000000000000000000000000000000002200000000mgr-cfg/actions/ModeController.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

try:
    PY3 = sys.version_info.major >= 3
    raw_input = input
except AttributeError:
    PY3 = False


class ModeMissingException(Exception):
    pass


#Handles operations on a group of Modes.
class ModeController:
    def __init__(self, force=None):
        self.mode_list = {} #Indexed on the name of the mode
        self.force = False

    #Enable the mode.
    def on(self, mode_name):
        if mode_name in self.mode_list:
            self.mode_list[mode_name].on()
        else:
            raise ModeMissingException()

    #Disable the mode
    def off(self, mode_name):
        if mode_name in self.mode_list:
            self.mode_list[mode_name].off()
        else:
            raise ModeMissingException()

    #Turn on all of the modes.
    def all_on(self):
        for m in self.mode_list.keys():
            self.mode_list[m].on()

        if self.mode_list['all'].is_on():
            self.mode_list['all'].off()

    #Turn off all of the modes.
    def all_off(self):
        for m in self.mode_list.keys():
            self.mode_list[m].off()

    #Check to see if the mode is on.
    def is_on(self, mode_name):
        if mode_name in self.mode_list:
            return self.mode_list[mode_name].is_on()
        else:
            return 0

    #Check to see if the mode is off.
    def is_off(self, mode_name):
        if mode_name in self.mode_list:
            return self.mode_list[mode_name].is_off()
        else:
            return 0

    #Add another mode to the batch.
    def add_mode(self, mode_obj):
        mode_name = mode_obj.get_name()

        if not mode_name in self.mode_list:
            self.mode_list[mode_name] = mode_obj

    #Remove a mode from the batch.
    def del_mode(self, mode_obj):
        mode_name = mode_obj.get_name()
        if mode_name in self.mode_list:
            del self.mode_list[mode_name]

    #set the value of force
    def set_force(self, force):
        self.force = force

class ConfigFilesModeController(ModeController):
    def __init__(self):
        ModeController.__init__(self)

    def is_on(self, mode_name):
        if ModeController.is_on(self, 'all'):
            return 1
        else:
            return ModeController.is_on(self, mode_name)

    def is_off(self, mode_name):
        if ModeController.is_off(self, 'all'):
            return 1
        else:
            return ModeController.is_off(self, mode_name)

    #the possible presence of the 'all' file confuses things a little.
    #If the user enables something while the 'all' file is present, nothing should be added to the configfiles dir.
    def on(self, mode_name):
        if mode_name != 'all':
            if self.is_off('all'):
                ModeController.on(self, mode_name)

                #Go through each of the modes and see if they're on. If they're all on, then place the 'all' file in there.
                all_modes_on = 1
                for m in self.mode_list.keys():
                    if m != 'all' and self.mode_list[m].is_off():
                        all_modes_on = 0
                if all_modes_on:
                    self.all_on()
        else:
            self.all_on()

    #If the 'all' file is present and the user disables a mode, then the all file must be removed, and all modes other than
    #the specified mode need to be turned on.
    def off(self, mode_name):
        if mode_name != 'all':
            if self.is_on('all'):
                if not self.force:
                    ask_before_continuing("All modes are currently enabled. Continue? (y or n):")

                #Turn off all modes
                self.all_off()

                #Manually flip on all of the modes except 'all'.
                for m in self.mode_list.keys():
                    if m != 'all':
                        self.mode_list[m].on()

            #Turn off the specified mode. Calls off() at the Mode level, not at the Controller level.
            self.mode_list[mode_name].off()
        else:
            self.all_off()


    #This is a little different when the 'all' file is used.
    #There shouldn't be any other files in the directory when 'all' is used.
    def all_on(self):
        #Get rid of all of the files.
        self.all_off()
        #Turn on the 'all' mode.
        self.mode_list['all'].on()

def ask_before_continuing(question=None):
    if question is None:
        the_question = "Continue? (y or n):"
    else:
        the_question = question

    answer = '-1'

    while answer != 'y' and answer != 'n':
        answer = raw_input(the_question)
        answer = answer.lower()[0]

    if answer == 'n':
        sys.exit(0)

07070100000009000081B40000000000000000000000015D403FC8000009E7000000000000000000000000000000000000002900000000mgr-cfg/actions/ModeControllerCreator.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 ModeController
import Modes

class ModeControllerCreator:
    #if mode_list isn't set in the constructor, the populate_list is going to have to be called before create_controller.
    def __init__(self, mode_list=None):
        self.mode_list = mode_list or []

        #A reference to a class obj. This is the type of controller that will be returned by create_controller.
        self.controller_class = ModeController.ModeController

    #Sets the class that the controller will be instantiated as. The constructor for the class shouldn't have
    #to take any parameters.
    def set_controller_class(self, controller_class):
        self.controller_class = controller_class

    #Populates self.mode_list with concrete implementations of Modes.
    def populate_list(self, mode_list):
        self.mode_list = mode_list

    #using the Modes in self.mode_list, create, populate, and return a ModeController
    def create_controller(self):
        controller = self.controller_class()

        for m in self.mode_list:
            controller.add_mode(m)

        return controller

def get_controller_creator():
    if sys.platform.find('sunos') > -1:
        mode_list = [Modes.SolarisDeployMode(), Modes.SolarisDiffMode(), Modes.SolarisUploadMode(), Modes.SolarisMTimeUploadMode(), Modes.SolarisAllMode()]
    else:
        mode_list = [Modes.DeployMode(), Modes.DiffMode(), Modes.UploadMode(), Modes.MTimeUploadMode(), Modes.AllMode()]

    controller = ModeControllerCreator(mode_list=mode_list)
    controller.set_controller_class(ModeController.ConfigFilesModeController)
    return controller

def get_run_controller_creator():
    if sys.platform.find('sunos') > -1:
        mode_list = [Modes.SolarisRunMode(), Modes.SolarisRunAllMode()]
    else:
        mode_list = [Modes.RunMode(), Modes.RunAllMode()]

    controller = ModeControllerCreator(mode_list=mode_list)
    return controller
0707010000000A000081B40000000000000000000000015D403FC8000017BA000000000000000000000000000000000000001900000000mgr-cfg/actions/Modes.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

#Base class for all modes.
class BaseMode:
    def __init__(self):
        self.state = False
        self.name = ""

    def on(self):
        self.state = True

    def off(self):
        self.state = False

    def is_on(self):
        if self.state:
            return True
        else:
            return False

    def is_off(self):
        if not self.state:
            return True
        else:
            return False

    def set_name(self, name):
        self.name = name

    def get_name(self):
        return self.name

#Contains the directory and file manipulation stuff
class PathHandler:
    def __init__(self):
        self.rhn_root = "/etc/sysconfig/rhn/allowed-actions/configfiles"

    #Set the rhn_root variable.
    def set_rhn_root(self, rhn_root):
        self.rhn_root = rhn_root

    #Creates the self.rhn_root directories if they don't already exist. This allows subclasses to implement modes in different locations.
    def _create_rhnconfig_path(self):
        if not os.path.exists(self.rhn_root):
            os.makedirs(self.rhn_root, int('0770', 8))

    #Create the file if it doesn't already exist.
    def add_file(self, filename):
        self._create_rhnconfig_path()
        if not self.check_for_file(filename):
            try:
                f = open(os.path.join(self.rhn_root, filename), "w")
                f.close()
            except Exception:
                raise

    #remove the file if it's present.
    def remove_file(self, filename):
        self._create_rhnconfig_path()
        if self.check_for_file(filename):
            try:
                os.remove(os.path.join(self.rhn_root, filename))
            except Exception:
                raise

    #Returns True if filename exists in /etc/sysconfig/rhn/allowed-actions/configfiles
    def check_for_file(self, filename):
        self._create_rhnconfig_path()
        return os.path.exists(os.path.join(self.rhn_root, filename))


#Stuff that's common to the Mode subclasses.
class ConfigFilesBaseMode(BaseMode):
    def __init__(self):
        BaseMode.__init__(self)
        self.ph = PathHandler()
        self.name = None       #Must be set in subclass

    def on(self):
        self.ph.add_file(self.name)
        self.state = True

    def off(self):
        self.ph.remove_file(self.name)
        self.state = False

    #Could probably just check the value of state...
    def is_on(self):
        return self.ph.check_for_file(self.name)

    def is_off(self):
        if self.ph.check_for_file(self.name):
            return False
        elif not self.ph.check_for_file(self.name):
            return True

class RunMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "run"
        self.ph.set_rhn_root("/etc/sysconfig/rhn/allowed-actions/script")

class RunAllMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "all"
        self.ph.set_rhn_root("/etc/sysconfig/rhn/allowed-actions/script")

class AllMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "all"

class DeployMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "deploy"

class DiffMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "diff"

class UploadMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "upload"

class MTimeUploadMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "mtime_upload"

#Solaris Specific Modes
class SolarisRunMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "run"
        self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/script")

class SolarisAllRunMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "all"
        self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/script")

class SolarisAllMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "all"
        self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")

class SolarisDeployMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "deploy"
        self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")

class SolarisDiffMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "diff"
        self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")

class SolarisUploadMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "upload"
        self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")

class SolarisMTimeUploadMode(ConfigFilesBaseMode):
    def __init__(self):
        ConfigFilesBaseMode.__init__(self)
        self.name = "mtime_upload"
        self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")


0707010000000B000081B40000000000000000000000015D403FC800000266000000000000000000000000000000000000001C00000000mgr-cfg/actions/__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.
#

0707010000000C000081B40000000000000000000000015D403FC800000081000000000000000000000000000000000000001C00000000mgr-cfg/actions/configfilesconfigfiles.mtime_upload(1)=1
configfiles.upload(1)=1
configfiles.deploy(1)=1
configfiles.diff(1)=1
configfiles.base64_enc(1)=1

0707010000000D000081FD0000000000000000000000015D403FC8000047C5000000000000000000000000000000000000001F00000000mgr-cfg/actions/configfiles.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 config_common import local_config, file_utils, utils, cfg_exceptions
from config_common.rhn_log import set_debug_level, get_debug_level, set_logfile, log_to_file
from config_common.transactions import DeployTransaction, FailedRollback

from config_client import rpc_cli_repository

from up2date_client import config

from spacewalk.common.usix import StringType, UnicodeType


# this is a list of the methods that get exported by a module
__rhnexport__ = [
    'mtime_upload',
    'upload',
    'deploy',
    'verify',
    'diff',
]


# action version we understand
ACTION_VERSION = 2

# when we do this within up2date, should just have something like
# __rhn_require_local_permission__ that flags the methods requiring the touched
# file
_permission_root_dir = '/etc/sysconfig/rhn/allowed-actions'
def _local_permission_check(action_type):
    # action_type ala configfiles.deploy
    atype_structure = action_type.split('.')

    for i in range(len(atype_structure)):
        all_structure = atype_structure[:i]
        all_structure.append('all')

        if os.path.exists(os.path.join(_permission_root_dir, "script/all")) and action_type == "script.run":
            os.rename(os.path.join(_permission_root_dir, "script/all"),os.path.join(_permission_root_dir, "script/run"))

        potential_all_path = os.path.join(*all_structure)
        if os.path.exists(os.path.join(_permission_root_dir, potential_all_path)):
            return 1

    action_path = os.path.join(*atype_structure)
    return os.path.exists(os.path.join(_permission_root_dir, action_path))

def _perm_error(action_type):
    return (42, "Local permission not set for action type %s" % action_type, {})


def _visit_dir(params, dirname, names):

    matches = params['matches']
    info = params['info']
    ignore_dirs = params['ignore']
    now = params['now']

    i = 0
    while i < len(names):
        full_path = os.path.join(dirname, names[i])
        is_dir = os.path.isdir(full_path)

        if is_dir:
            if full_path in ignore_dirs:
                # don't consider the entire subtree on subsequent runs of
                # visit
                del names[i]
            else:
                i = i + 1
            # since we can have multiple search paths hitting the same subdir,
            # filter 'em out after the first pass
            ignore_dirs[full_path] = None
            continue

        if not os.path.exists(full_path):
            i = i + 1
            continue

        mtime = os.path.getmtime(full_path)

        # do it via delta...
        if (now - mtime) <= (info['now'] - info['start_date']):
            if info['end_date']:
                if (now - mtime) >= (info['now'] - info['end_date']):
                    matches.append(full_path)
            else:
                matches.append(full_path)

        i = i + 1


def format_result(result, files):
    files_too_large = result.get('files_too_large') or []
    quota_failed = result.get('failed_due_to_quota') or []
    missing_files = result.get('missing_files') or []

    extras = { 'attempted_paths' : files }

    if missing_files:
        extras['missing_files'] = missing_files
    if files_too_large:
        extras['files_too_large'] = files_too_large
    if quota_failed:
        extras['quota_failed'] = quota_failed

    num_files = len(files)
    num_uploaded = num_files - (len(missing_files) + len(files_too_large) + len(quota_failed))

    if num_uploaded == num_files:
        return 0, "All files successfully uploaded", extras
    else:
        return -1, "Some files failed to upload", extras


##     foo = {'ignore': ['\n', '/home/bretm/rhn/build/', '/home/bretm/rhn/sql'],
##            'info': {'now': 1071722611.0, 'import_contents': 'N', 'start_date': 1071290580.0, 'end_date': ''},
##            'search': ['/home/bretm/'],
##            }
def mtime_upload(action_id, params, cache_only=None):
    if cache_only:
        return (0, "no-ops for caching", {})

    action_type = 'configfiles.mtime_upload'
    if not _local_permission_check(action_type):
        log_to_file(0, "permissions error: " + str(action_type))
        return _perm_error(action_type)

    _init()

    file_matches = []
    now = time.time()
    upload_contents = None
    ignore_dirs = {'/proc':None, '/dev':None}

    if params['info']['import_contents'] == 'Y':
        upload_contents = 1

    for to_ignore in params['ignore']:
        ignore_dirs[utils.normalize_path(to_ignore)] = 1

    for search_path in params['search']:
        for dirname, dirs, names in os.walk(utils.normalize_path(search_path)):
             _visit_dir({
                'matches' : file_matches,
                'info' : params['info'],
                'ignore' : ignore_dirs,
                'now' : now,
                }, dirname, names)

    if not file_matches:
        return 0, "No files found", {}

    r = rpc_cli_repository.ClientRepository()
    result = r.put_files(action_id, file_matches, upload_contents=upload_contents)

    formatted_result = format_result(result, file_matches)
    log_to_file(0, formatted_result)
    return formatted_result


def upload(action_id, params, cache_only=None):
    if cache_only:
        return (0, "no-ops for caching", {})

    action_type = 'configfiles.upload'
    if not _local_permission_check(action_type):
        log_to_file(0, "permissions error: " + str(action_type))
        return _perm_error(action_type)

    _init()

    files = params or []

    r = rpc_cli_repository.ClientRepository()
    result = r.put_files(action_id, files)

    formatted_result = format_result(result, files)
    log_to_file(0, formatted_result)

    return formatted_result


def deploy(params, topdir=None, cache_only=None):
    if cache_only:
        return (0, "no-ops for caching", {})

    action_type = 'configfiles.deploy'
    if not _local_permission_check(action_type):
        log_to_file(0, "permissions error: " + str(action_type))
        return _perm_error(action_type)

    _init()
    files = params.get('files') or []
    dep_trans = DeployTransaction(transaction_root=topdir, auto_rollback=0)

    for file in files:
        dep_trans.add(file)

    try:
        dep_trans.deploy()
    #5/3/05 wregglej - 135415 Adding stuff for missing user info
    except cfg_exceptions.UserNotFound:
            e = sys.exc_info()[1]
            try:
                dep_trans.rollback()
            except FailedRollback:
                log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ))
                return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {})
            #5/3/05 wregglej - 136415 Adding some more exceptions to handle
            except cfg_exceptions.UserNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ))
                return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
            #5/5/05 wregglej - 136415 Adding exception handling for unknown group,
            except cfg_exceptions.GroupNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
                return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
            else:
                log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ))
                return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {})
    except cfg_exceptions.GroupNotFound:
            e = sys.exc_info()[1]
            try:
                dep_trans.rollback()
            except FailedRollback:
                log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ))
                return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {})
            #5/3/05 wregglej - 136415 Adding some more exceptions to handle
            except cfg_exceptions.UserNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ) )
                return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
            #5/5/05 wregglej - 136415 Adding exception handling for unknown group,
            except cfg_exceptions.GroupNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
                return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
            else:
                log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (e[0], ))
                return (51, "Failed deployment and rollback, group '%s' could not be found" % (e[0], ), {})
    except cfg_exceptions.FileEntryIsDirectory:
            e = sys.exc_info()[1]
            try:
                dep_trans.rollback()
            except FailedRollback:
                log_to_file(0, "Failed deployment and rollback, %s already exists as a directory" % (e[0], ))
                return (44, "Failed deployment and rollback, %s already exists as a directory" % (e[0], ), {})
            #5/3/05 wregglej - 136415 Adding some more exceptions to handle
            except cfg_exceptions.UserNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ))
                return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
            #5/5/05 wregglej - 136415 Adding exception handling for unknown group,
            except cfg_exceptions.GroupNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
                return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
            else:
                log_to_file(0, "Failed deployment, %s already exists as a directory" % (e[0], ))
                return (45, "Failed deployment, %s already exists as a directory" % (e[0], ), {})
    except cfg_exceptions.DirectoryEntryIsFile:
            e = sys.exc_info()[1]
            try:
                dep_trans.rollback()
            except FailedRollback:
                log_to_file(0, "Failed deployment and rollback, %s already exists as a file" % (e[0], ))
                return (46, "Failed deployment and rollback, %s already exists as a file" % (e[0], ), {})
            #5/3/05 wregglej - 136415 Adding exceptions for missing user
            except cfg_exceptions.UserNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ))
                return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
            #5/5/05 wregglej - 136415 Adding exception handling for unknown group,
            except cfg_exceptions.GroupNotFound:
                f = sys.exc_info()[1]
                log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
                return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
            else:
                log_to_file(0, "Failed deployment, %s already exists as a file" % (e[0], ))
                return (47, "Failed deployment, %s already exists as a file" % (e[0], ), {})

    except Exception:
        e = sys.exc_info()[1]
        print(e)
        try:
            dep_trans.rollback()
        except FailedRollback:
            e2 = sys.exc_info()[1]
            log_to_file(0, "Failed deployment, failed rollback:  %s" % e2)
            return (48, "Failed deployment, failed rollback:  %s" % e2, {})
        #5/3/05 wregglej - 135415 Add exception handling for missing user.
        except cfg_exceptions.UserNotFound:
            f = sys.exc_info()[1]
            log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0]))
            return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0]), {})
        #5/5/05 wregglej - 136415 Adding exception handling for unknown group,
        except cfg_exceptions.GroupNotFound:
            f = sys.exc_info()[1]
            log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
            return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
        else:
            log_to_file(0, "Failed deployment, rolled back:  %s" % e)
            return (49, "Failed deployment, rolled back:  %s" % e, {})

    extras = {}
    log_to_file(0, "Files successfully deployed: %s %s" % (format_file_string(files, create_key_list()), str(extras)))
    return 0, "Files successfully deployed", extras


def diff(params, cache_only=None):
    def is_utf8(in_string):
        """Returns true if input is a valid UTF-8 string, False otherwise."""
        if isinstance(in_string, UnicodeType):
            return True
        elif isinstance(in_string, StringType):
            try:
                in_string.decode('utf-8')
                return True
            except UnicodeDecodeError:
                return False
        return False

    if cache_only:
        return (0, "no-ops for caching", {})

    action_type = 'configfiles.diff'
    if not _local_permission_check(action_type):
        log_to_file(0, "permissions error: " + str(action_type))
        return _perm_error(action_type)

    _init()
    files = params.get('files') or []
    fp = file_utils.FileProcessor()
    missing_files = []
    diffs = {}
    exists = hasattr(os.path, 'lexists') and os.path.lexists or os.path.exists
    for file in files:
        path = file['path']
        if not exists(path):
            missing_files.append(path)
            continue

        diff = fp.diff(file)
        if diff:
            diffs[path] = diff

    extras = {}
    if missing_files:
        extras['missing_files'] = missing_files

    if diffs:
        for file in diffs.keys():
            if not is_utf8(diffs[file]):
                diffs[file] = "%s: binary files differ" % file
        extras['diffs'] = diffs

    log_to_file(0, "Files successfully compared: %s %s" % (format_file_string(files, create_key_list()), str(extras)))
    return 0, "Files successfully compared", extras

verify = diff

#The format_file_string and create_key_list functions can be used together to create a string
#containing information about the files in file_list. Use sparingly.

#file_list is a list of dictionaries containing file information.
#keylist is a list of strings containing the keys of the information in file_list that you wish to print out.
def format_file_string(file_list, keylist):
    outstr = ""
    for afile in file_list:
        outstr
        for key in keylist:
            formatstr = "\n%s: %s"
            if key in afile:
                outstr = outstr + formatstr % (key, afile[key])
        outstr = outstr + "\n"
    return outstr

#Returns a list of strings. Each string is a key in the dictionary containing file information.
#The number of keys returned corresponds to the debug_level. The higher the debug_level, the longer the
#list of keys.
def create_key_list():
    #The list of keys. The order of the keys determines what debug_level they will be returned in.
    #For example, at debug level 0 only the path and revision will be included. At level 1, the path, revision,
    #config_channel, and filemode keys should be included.
    key_list = [
                    'path',
                    'revision',
                    'config_channel',
                    'filemode',
                    'filetype',
                    'encoding',
                    'username',
                    'groupname',
                    'delim_start',
                    'delim_end',
                    'md5sum',
                    'checksum_type',
                    'checksum',
                    'verify_contents',
                    'file_contents',
                ]
    #This dictionary associates each debug level (the key) with the index into key_list (the value) at which
    #we should stop including keys in the returned list.
    debug_levels = {
                        0 : 2,
                        1 : 4,
                        2 : 6,
                        3 : 8,
                        4 : 10,
                        5 : 15,
                   }
    curr_debug = get_debug_level()
    if curr_debug > 5:
        curr_debug = 5
    if curr_debug < 0:
        curr_debug = 0
    if not curr_debug in debug_levels.keys():
        curr_debug = 0
    return key_list[:debug_levels[curr_debug]]

def _init():
    cfg = config.initUp2dateConfig()
    cfg_dict = dict(cfg.items())
    server_url = config.getServerlURL()
    cfg_dict['proto'], cfg_dict['server_name'] = utils.parse_url(server_url[0], scheme="https")[:2]
    if len(server_url) > 1:
        cfg_dict['server_list'] = server_url
    local_config.init('rhncfg-client', defaults=cfg_dict)
    set_debug_level(int(local_config.get('debug_level') or 0))
    set_logfile("/var/log/rhncfg-actions")
0707010000000E000081FD0000000000000000000000015D403FC80000115A000000000000000000000000000000000000002700000000mgr-cfg/actions/rhn-actions-control.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 sys
import ModeControllerCreator
from optparse import Option, OptionParser


def main():
    optionsTable = [
        Option('--enable-deploy',       action='store_true',      help='Allow rhncfg-client to deploy files.', default=0),
        Option('--enable-diff',         action='store_true',      help='Allow rhncfg-client to diff files.', default=0),
        Option('--enable-upload',       action='store_true',      help='Allow rhncfg-client to upload files.', default=0),
        Option('--enable-mtime-upload', action='store_true',      help='Allow rhncfg-client to upload mtime.', default=0),
        Option('--enable-run',          action='store_true',      help='Allow rhncfg-client the ability to execute remote scripts.', default=0),
        Option('--enable-all',          action='store_true',      help='Allow rhncfg-client to do everything.', default=0),
        Option('--disable-deploy',      action='store_true',      help='Disable deployment.', default=0),
        Option('--disable-diff',        action='store_true',      help='Disable diff.', default=0),
        Option('--disable-upload',      action='store_true',      help='Disable upload.', default=0),
        Option('--disable-mtime-upload',action='store_true',      help='Disable mtime upload.', default=0),
        Option('--disable-run',         action='store_true',      help='Disable remote script execution.', default=0),
        Option('--disable-all',         action='store_true',      help='Disable all options.', default=0),
        Option('-f', '--force',         action='store_true',      help='Force the operation without confirmation', default=0),
        Option('--report',              action='store_true',      help='Report the status of the mode settings (enabled or disabled)', default=0),
    ]

    parser = OptionParser(option_list=optionsTable)
    (options, args) = parser.parse_args()

    creator = ModeControllerCreator.get_controller_creator()
    controller = creator.create_controller()
    controller.set_force(options.force)

    runcreator = ModeControllerCreator.get_run_controller_creator()
    runcontroller = runcreator.create_controller()
    runcontroller.set_force(options.force)

    if options.enable_deploy:
        controller.on('deploy')

    if options.enable_diff:
        controller.on('diff')

    if options.enable_upload:
        controller.on('upload')

    if options.enable_mtime_upload:
        controller.on('mtime_upload')

    if options.enable_all:
        controller.all_on()
        runcontroller.all_on()

    if options.enable_run:
        runcontroller.on('run')
        runcontroller.off('all')

    if options.disable_deploy:
        controller.off('deploy')

    if options.disable_diff:
        controller.off('diff')

    if options.disable_upload:
        controller.off('upload')

    if options.disable_mtime_upload:
        controller.off('mtime_upload')

    if options.disable_all:
        controller.all_off()
        runcontroller.all_off()

    if options.disable_run:
        runcontroller.off('run')
        runcontroller.off('all')

    if options.report:
        mode_list = ['deploy', 'diff', 'upload', 'mtime_upload']

        for m in mode_list:
            rstring = "%s is %s"
            status = "disabled"
            if controller.is_on(m):
                status = "enabled"
            print(rstring % (m, status))

        status = "disabled"
        if runcontroller.is_on('all'):
            runcontroller.off('all')
            runcontroller.on('run')

        if runcontroller.is_on('run'):
            status = "enabled"
        print(rstring % ('run', status))


if __name__ == "__main__":
    try:
        sys.exit(main() or 0)
    except KeyboardInterrupt:
        sys.stderr.write("user interrupted\n")
        sys.exit(0)

0707010000000F000081B40000000000000000000000015D403FC800000FA1000000000000000000000000000000000000002900000000mgr-cfg/actions/rhn-actions-control.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY RHNACTIONSCONTROL "Spacewalk Actions Control" >

]>
<refentry>

<RefMeta>
<RefEntryTitle>rhn-actions-control</RefEntryTitle><manvolnum>8</manvolnum>
<RefMiscInfo>Version 4.0</RefMiscInfo>
</RefMeta>

<RefNameDiv>
<RefName><command>rhn-actions-control</command></RefName>
<RefPurpose>
Controls the enabling and disabling of scheduled actions.
</RefPurpose>
</RefNameDiv>

<RefSynopsisDiv>
<Synopsis>
    <cmdsynopsis>
        <command>rhn-actions-control</command>
        <arg>options <replaceable>...</replaceable></arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--enable-deploy | --disable-deploy</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--enable-diff | --disable-diff</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--enable-upload | --disable-upload</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--enable-mtime-upload | --disable-mtime-upload</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--enable-run | --disable-run</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--enable-all | --disable-all</arg>
    </cmdsynopsis>
    <cmdsynopsis>
        <arg>--report</arg>
    </cmdsynopsis>
</Synopsis>
</RefSynopsisDiv>

<RefSect1><Title>Description</Title>

    <para>
        The &RHNACTIONSCONTROL; (<emphasis>rhn-actions-control</emphasis>)
        allows the user to enable and disable different types of scheduled
        actions.  These various modes are enabled/disabled by placing/removing
        files in the <emphasis>/etc/sysconfig/rhn/allowed-actions/</emphasis>
        directory. Due to the default permissions on the
        <emphasis>/etc/sysconfig/rhn/</emphasis> directory, &RHNACTIONSCONTROL;
        will most likely have to be run by someone with root access.
    </para>

</RefSect1>

<RefSect1><Title>Options</Title>
<variablelist>
    <varlistentry>
        <term>--enable-deploy, --disable-deploy</term>
        <listitem>
            <para>Enable/disable the deploy scheduled action. I.e., allow
                installation of configuration files from the central repository
                on the system. Without this functionality enabled, the
                configuration management interface provides no real value for
                this system.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--enable-diff, --disable-diff</term>
        <listitem>
            <para>Enable/disable the diff scheduled action.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--enable-upload, --disable-upload</term>
        <listitem>
            <para>Enable/disable the upload scheduled action.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--enable-mtime-upload, --disable-mtime-upload</term>
        <listitem>
            <para>Enable/disable the mtime-upload scheduled action.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--enable-run, --disable-run</term>
        <listitem>
            <para>Enable/disable the ability to run remote scripts.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--enable-all, --disable-all</term>
        <listitem>
            <para>Enable/disable all capabilities.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-f, --force</term>
        <listitem>
            <para>Force an operation without confirmation.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--report</term>
        <listitem>
            <para>Report the status of the mode settings (enabled or disabled).</para>
        </listitem>
    </varlistentry>
</variablelist>
</RefSect1>

<RefSect1><Title>Authors</Title>
<simplelist>
    <member>John Wregglesworth <email>wregglej@redhat.com</email></member>
    <member>Todd Warner <email>taw@redhat.com</email> (man page only)</member>
</simplelist>
</RefSect1>
</RefEntry>
07070100000010000081B40000000000000000000000015D403FC800000010000000000000000000000000000000000000001700000000mgr-cfg/actions/scriptscript.run(1)=1
07070100000011000081B40000000000000000000000015D403FC8000022BD000000000000000000000000000000000000001A00000000mgr-cfg/actions/script.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 sys
import pwd
import grp
import time
import select
import signal
import tempfile
import base64

try:
    MAXFD = os.sysconf("SC_OPEN_MAX")
except:
    MAXFD = 256


# this is ugly, hopefully it will be natively supported in up2date
from rhn.actions.configfiles import _local_permission_check, _perm_error
from config_common import local_config
from config_common.rhn_log import set_logfile, log_to_file

from up2date_client import config


# this is a list of the methods that get exported by a module
__rhnexport__ = [
    'run',
    ]

# action version we understand
ACTION_VERSION = 2


def _create_script_file(script, uid=None, gid=None):

    storageDir = tempfile.gettempdir()
    script_path = os.path.join(storageDir, 'rhn-remote-script')

    # Loop a couple of times to try to get rid of race conditions
    for i in range(2):
        try:
            fd = os.open(script_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, int("0700", 8))
            # If this succeeds, break out the loop
            break
        except OSError:
            e = sys.exc_info()[1]
            if e.errno != 17: # File exists
                raise
            # File does exist, try to remove it
            try:
                os.unlink(script_path)
            except OSError:
                e = sys.exc_info()[1]
                if e.errno != 2: # No such file or directory
                    raise
    else:
        # Tried a couple of times, failed; bail out raising the latest error
        raise
    sf = os.fdopen(fd, 'wb')
    sf.write(script.encode("utf-8"))
    sf.close()

    if uid and gid:
        os.chown(script_path, uid, gid)

    return script_path

# Make sure the dir-path to a file exists
def _create_path(fpath):
    d = os.path.dirname(fpath)
    if d and not os.path.exists(d):
        os.makedirs(d, int("0700", 8))
    return os.path.exists(d)

def run(action_id, params, cache_only=None):

    cfg = config.initUp2dateConfig()
    local_config.init('rhncfg-client', defaults=dict(cfg.items()))

    tempfile.tempdir = local_config.get('script_tmp_dir')

    logfile_name = local_config.get('script_log_file')
    log_output = local_config.get('script_log_file_enable')

    if log_output:
        # If we're going to log, make sure we can create the logfile
        _create_path(logfile_name)

    if cache_only:
        return (0, "no-ops for caching", {})

    action_type = 'script.run'
    if not _local_permission_check(action_type):
        return _perm_error(action_type)


    extras = {'output':''}
    script = params.get('script')
    if not script:
        return (1, "No script to execute", {})

    username = params.get('username')
    groupname = params.get('groupname')

    if not username:
        return (1, "No username given to execute script as", {})

    if not groupname:
        return (1, "No groupname given to execute script as", {})

    timeout = params.get('timeout')

    if timeout:
        try:
            timeout = int(timeout)
        except ValueError:
            return (1, "Invalid timeout value", {})
    else:
        timeout = None

    db_now = params.get('now')
    if not db_now:
        return (1, "'now' argument missing", {})
    db_now = time.mktime(time.strptime(db_now, "%Y-%m-%d %H:%M:%S"))

    now = time.time()
    process_start = None
    process_end = None

    child_pid = None

    # determine uid/ugid for script ownership, uid also used for setuid...
    try:
        user_record = pwd.getpwnam(username)
    except KeyError:
        return 1, "No such user %s" % username, extras

    uid = user_record[2]
    ugid = user_record[3]


    # create the script on disk
    try:
        script_path = _create_script_file(script, uid=uid, gid=ugid)
    except OSError:
        e = sys.exc_info()[1]
        return 1, "Problem creating script file:  %s" % e, extras

    # determine gid to run script as
    try:
        group_record = grp.getgrnam(groupname)
    except KeyError:
        return 1, "No such group %s" % groupname, extras

    run_as_gid = group_record[2]


    # create some pipes to communicate w/ the child process
    (pipe_read, pipe_write) = os.pipe()

    process_start = time.time()
    child_pid = os.fork()

    if not child_pid:
        # Parent doesn't write to child, so close that part
        os.close(pipe_read)

        # Redirect both stdout and stderr to the pipe
        os.dup2(pipe_write, sys.stdout.fileno())
        os.dup2(pipe_write, sys.stderr.fileno())

        # Close unnecessary file descriptors (including pipe since it's duped)
        for i in range(3, MAXFD):
            try:
                os.close(i)
            except:
                pass

        # all scripts initial working directory will be /
        # puts burden on script writer to ensure cwd is correct within the
        # script
        os.chdir('/')

        # the child process gets the desired uid/gid
        os.setgid(run_as_gid)
        groups=[g.gr_gid for g in grp.getgrall() if username in g.gr_mem or username in g.gr_name]
        os.setgroups(groups)
        os.setuid(uid)

        # give this its own process group (which happens to be equal to its
        # pid)
        os.setpgrp()

        clean_env = {"PATH": "/sbin:/bin:/usr/sbin:/usr/bin", "TERM": "xterm"}
        # Finally, exec the script
        try:
            os.umask(int("022", 8))
            os.execve(script_path, [script_path, ], clean_env)
        finally:
            # This code can be reached only when script_path can not be
            # executed as otherwise execv never returns.
            # (The umask syscall always succeeds.)
            os._exit(1)

    # Parent doesn't write to child, so close that part
    os.close(pipe_write)

    output = None
    timed_out = None

    out_stream = open('/var/lib/up2date/action.%s' % str(action_id), 'ab+', 0)

    while 1:
        select_wait = None

        if timeout:
            elapsed = time.time() - process_start

            if elapsed >= timeout:
                timed_out = 1
                # Send TERM to all processes in the child's process group
                # Send KILL after that, just to make sure the child died
                os.kill(-child_pid, signal.SIGTERM)
                time.sleep(2)
                os.kill(-child_pid, signal.SIGKILL)
                break

            select_wait = timeout - elapsed

        try:
            input_fds, output_fds, error_fds = select.select([pipe_read], [], [], select_wait)
        except select.error:
            return 255, "Termination signal occurred during execution.", {}

        if error_fds:
            # when would this happen?
            os.close(pipe_read)
            return 1, "Fatal exceptional case", extras

        if not (pipe_read in input_fds):
            # Read timed out, should be caught in the next loop
            continue

        output = os.read(pipe_read, 4096)
        if not output:
            # End of file from the child
            break

        out_stream.write(output)

    os.close(pipe_read)

    # wait for the child to complete
    (somepid, exit_status) = os.waitpid(child_pid, 0)
    process_end = time.time()

    # Copy the output from the temporary file
    out_stream.seek(0, 0)
    extras['output'] = out_stream.read()
    out_stream.close()

    # Log script-output locally, unless we're asked not to
    if log_output :
        set_logfile(logfile_name)
        log_to_file(0, extras['output'])

    # since output can contain chars that won't make xmlrpc very happy,
    # base64 encode it...
    extras['base64enc'] = 1
    extras['output'] = base64.encodestring(extras['output'])

    extras['return_code'] = exit_status

    # calculate start and end times in db's timespace
    extras['process_start'] = db_now + (process_start - now)
    extras['process_end'] = db_now + (process_end - now)

    for key in ('process_start', 'process_end'):
        extras[key] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(extras[key]))

    # clean up the script
    os.unlink(script_path)

    if timed_out:
        return 1, "Script killed, timeout of %s seconds exceeded" % timeout, extras

    if exit_status == 0:
        return 0, "Script executed", extras

    return 1, "Script failed", extras
07070100000012000041FD0000000000000000000000015D403FC800000000000000000000000000000000000000000000001600000000mgr-cfg/config_client07070100000013000081B40000000000000000000000015D403FC800000386000000000000000000000000000000000000001F00000000mgr-cfg/config_client/Makefile# Makefile for spacewalk backend
#

# what is the backend top dir
TOP     = ..

# Specific stuff
SUBDIR		= config_client
EXEC_FILES	= rhncfg-client
FILES           = $(EXEC_FILES) __init__ handler_base \
                  rhncfgcli_channels \
                  rhncfgcli_diff rhncfgcli_get \
                  rhncfgcli_list \
                  rhncfgcli_elist \
                  rhncfgcli_verify rpc_cli_repository
CONFIG_FILES    = rhncfg-client.conf

# man pages
DOCBOOK = $(wildcard /usr/bin/docbook2man)
SGMLS	= $(wildcard *.sgml)
MANS	= $(patsubst %.sgml,%.8,$(SGMLS))
MANDIR	?= /usr/man

EXTRA_DIRS	= $(BINDIR) $(CONFDIR) $(MANDIR)/man8

include $(TOP)/Makefile.defs

ifneq ($(DOCBOOK),)
# install man pages
all	:: $(MANS)
install :: $(MANS) $(PREFIX)/$(MANDIR)
	$(INSTALL_DATA) $(MANS) $(PREFIX)/$(MANDIR)/man8
clean   ::
	@rm -fv $(MANS) manpage.*
endif

install	:: install-bin install-conf

07070100000014000081B40000000000000000000000015D403FC800000266000000000000000000000000000000000000002200000000mgr-cfg/config_client/__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.
#

07070100000015000081B40000000000000000000000015D403FC8000008D4000000000000000000000000000000000000002800000000mgr-cfg/config_client/cli_repository.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

from config_common import cfg_exceptions, repository

# this is a bit odd; right now, we instantiate a regular
# repository.Repository object since it does a lot of local disk
# access for us.  In the future, though, it is unlikely Repository and
# ClientRepository classes/APIs will actually share this in common
# since Repository mostly requires user perms and ClientRepository
# mostly requires server perms

class ClientRepository:
    def __init__(self):
        self.server_repository = repository.Repository()
        tmp_channels = os.environ.get("RHNCFG_CHANNELS") or "all"

        # listed in order of losers first, ie, entry 2 overrides entry
        # 1, etc
        self.config_channels = tmp_channels.split(":")
        self.cfg_files = {}

    def list_files(self):
        # iterate over channels, accumulating hash of what files
        # come from where; subsequent entries override previous ones,
        # so the final hash is the result we seek

        if self.cfg_files:
            return self.cfg_files

        self.cfg_files = {}
        for ns in self.config_channels:
            for file in self.server_repository.list_files(ns):
                self.cfg_files[file] = [ ns, file ]

        return self.cfg_files

    def get_file(self, file):
        if not self.cfg_files:
            raise "never did a list_files"

        if file not in self.cfg_files:
            raise cfg_exceptions.ConfigNotManaged(file)

        return self.server_repository.get_file(self.cfg_files[file][0], self.cfg_files[file][1])

    def list_config_channels(self):
        return self.config_channels
07070100000016000081B40000000000000000000000015D403FC800000AEB000000000000000000000000000000000000002600000000mgr-cfg/config_client/handler_base.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 os.path

from config_common import handler_base, utils
from config_common.rhn_log import log_debug

class HandlerBase(handler_base.HandlerBase):
    def get_dest_file(self, file):
        target_dir = os.sep
        return utils.normalize_path(target_dir + os.sep + file)

    # Returns a list of valid files
    def get_valid_files(self):
        files_hash = {}
        for file in self.repository.list_files():
            files_hash[file[1]] = None

        if not self.args:
            # No file specified; use all of them
            files = list(files_hash.keys())
            files.sort()
            return files

        files = []
        for file in self.args:
            #5/9/05 wregglej - 151197 make sure that trailing /'s aren't passed through for directories.
            if os.path.isdir(file):
                if file[-1] == "/":
                    file = file[0:-1]

            if file not in files_hash:
                print("Not found on server: %s" % file)
                continue
            files.append(file)
        files.sort()
        return files

    # Main function to be run
    def run(self):
        log_debug(2)
        for file in self.get_valid_files():
            (src, file_info, dirs_created) = self.repository.get_file_info(file)

            ftype = file_info.get('filetype')

            if not src:
                continue

            dst = self.get_dest_file(file)

            self._process_file(src, dst, file, ftype, file_info)

    # To be overridden with specific actions in subclasses
    def _process_file(self, *args):
        pass

class TopdirHandlerBase(HandlerBase):
    _options_table = [
        HandlerBase._option_class(
            '--topdir',     action="store",
            help="Make all file operations relative to this directory.",
        ),
        HandlerBase._option_class(
            '--exclude',    action="append",
            help="Excludes a file from being deployed with 'get'. May be used multiple times.",
        ),
    ]

    def get_dest_file(self, file):
        target_dir = self.options.topdir or os.sep
        return utils.normalize_path(target_dir + os.sep + file)
07070100000017000081B40000000000000000000000015D403FC8000005A6000000000000000000000000000000000000002900000000mgr-cfg/config_client/rhncfg-client.conf[rhncfg-client]

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


# increase for debugging output
debug_level = 0

# Default directory for remote command temporary script file.
# If not set it will default to None and it will fallback to python's default tempdir search algorithm
script_tmp_dir = /var/spool/rhn

# 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 = /CONFIG-MANAGEMENT


# don't change this, period.
repository_type = rpc_cli_repository


# 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/CONFIG-MANAGEMENT
server_url = https://%(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
# sslCACert = /usr/share/rhn/RHNS-CA-CERT

# control of remote-script-logging
# Should we store output of a RemoteCommand locally? 1 = yes, 0 = no
script_log_file_enable = 0
# Where will we write that output?
script_log_file = /var/log/rhn/rhncfg-action-output.log
07070100000018000081B40000000000000000000000015D403FC8000004F1000000000000000000000000000000000000002700000000mgr-cfg/config_client/rhncfg-client.py#!/usr/bin/python
#
# Copyright (c) 2008--2012 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, os

from config_common.rhn_main import BaseMain

class Main(BaseMain):
    modes = ['diff', 'get', 'list', 'elist', 'channels', 'verify']
    repository_class_name = 'ClientRepository'
    plugins_dir = 'config_client'
    config_section = 'rhncfg-client'
    mode_prefix = 'rhncfgcli'


if __name__ == '__main__':
    # quick check to see if you are a super-user.
    if os.getuid() != 0:
        sys.stderr.write("ERROR: must be root to execute!" + "\n")
        sys.exit(1)
    try:
        sys.exit(Main().main() or 0)
    except KeyboardInterrupt:
        sys.stderr.write("user interrupted\n")
        sys.exit(0)

07070100000019000081B40000000000000000000000015D403FC800001408000000000000000000000000000000000000002900000000mgr-cfg/config_client/rhncfg-client.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY RHN "Spacewalk" >
<!ENTITY RHNCFGC "rhncfg-client" >

]>
<refentry>

<RefMeta>
<RefEntryTitle>&RHNCFGC;</RefEntryTitle><manvolnum>8</manvolnum>
<RefMiscInfo>Version 4.0</RefMiscInfo>
</RefMeta>

<RefNameDiv>
<RefName><command>&RHNCFGC;</command></RefName>
<RefPurpose>
  a tool used for system configuration querying and deployment.
</RefPurpose>
</RefNameDiv>

<RefSynopsisDiv>
<Synopsis>
    <cmdsynopsis>
        <command>&RHNCFGC;</command>
        <arg>mode </arg>
        <arg>options <replaceable>...</replaceable></arg>
        <arg>arguments <replaceable>...</replaceable></arg>
    </cmdsynopsis>
</Synopsis>
</RefSynopsisDiv>

<RefSect1><Title>Description</Title>
<para>
    The &RHN; Configuration Client (<emphasis>&RHNCFGC;</emphasis>) is used to query a
    system's configuration makeup and deployment status as managed by &RHN;.
</para>
<para>
    This tool enables a &RHN; client to <emphasis>list</emphasis> available
    configuration files, fetch (or <emphasis>get</emphasis>) those
    configuration files, <emphasis>view</emphasis> configuration channels, and
    finally, <emphasis>diff</emphasis> and <emphasis>verify</emphasis> current
    configuration of the system in comparison to the system's status according
    to &RHN;.
</para>
<para>
  <simplelist>
    <member>This tool utilizes a "cvs style" command syntax: command mode options</member>
    <member>For example: &RHNCFGC; diff --topdir /home/test/blah/</member>
  </simplelist>
</para>
</RefSect1>

<RefSect1><Title>mode &amp; options</Title>
<variablelist>
    <varlistentry>
        <term>list</term>
        <listitem>
            <para>
              List the configuration files for the machine and the labels
              of the config channels containing them. Used alone.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>elist</term>
        <listitem>
            <para>
              List the configuration files for the machine and the labels
              of the config channels containing them which provides an
              ls-esque style output. Used alone.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>get <replaceable>CONFIG_FILEPATH</replaceable></term>
        <listitem>
            <para>
              Download/deploy the most relevant configuration file for the
              machine. For example: &RHNCFGC; get /etc/foo.conf
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>channels</term>
        <listitem>
            <para>
              View the labels and names of the config channels that apply to
              the system. Used alone.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>diff</term>
        <listitem>
            <para>
              Obtain a difference between the config files deployed on the
              system and those for it stored by &RHN;.
            </para>
            <para>
              May be used with the --topdir <replaceable>ROOT_PATH</replaceable>
              option. E.g., &RHNCFGC; diff --topdir /home/test/foo/
            </para>
	    <para>
              If a file is readable only by root then the differences are not 
	      displayed by default. The option -d or --diff-secure-files 
	      forces the differences to be displayed.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>verify</term>
        <listitem>
            <para>
              Quick verification to see if files are different than those
              associated with it on &RHN;.
            </para>
            <para>
              All files are displayed unless called with the -o or --only
              option, in which case only files with differences are displayed.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-h, --help</term>
        <listitem>
            <para>Display the help screen with a list of options.</para>
        </listitem>
    </varlistentry>
</variablelist>
</RefSect1>

<RefSect1><Title>Examples/Summary</Title>
<simplelist>
        <member><command>&RHNCFGC; list</command></member>
        <member><command>&RHNCFGC; elist</command></member>
        <member><command>&RHNCFGC; get /etc/foo.conf</command></member>
        <member><command>&RHNCFGC; channels</command></member>
        <member><command>&RHNCFGC; diff</command></member>
        <member><command>&RHNCFGC; diff --topdir /tmp/configroot/</command></member>
        <member><command>&RHNCFGC; verify</command></member>
</simplelist>
</RefSect1>

<RefSect1><Title>See Also</Title>
<simplelist>
    <member>rhncfg-manager (8)</member>
</simplelist>
</RefSect1>

<RefSect1><Title>Authors</Title>
<simplelist>
    <member>Mihai Ibanescu <email>misa@redhat.com</email></member>
    <member>Bret McMillan <email>bretm@redhat.com</email></member>
    <member>Todd Warner <email>taw@redhat.com</email> (man page only)</member>
</simplelist>
</RefSect1>
</RefEntry>
0707010000001A000081B40000000000000000000000015D403FC800000497000000000000000000000000000000000000002500000000mgr-cfg/config_client/rhncfgcli.conf[rhncfgcli]

# Should we display the diff of a file that is only readable by root? By
# default we do not, uncomment this line to change that.
#display_diff = True

# 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 = /CONFIG-MANAGEMENT


# don't change this, period.
repository_type = rpc_cli_repository


# 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/CONFIG-MANAGEMENT
server_url = https://%(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
# sslCACert = /usr/share/rhn/RHNS-CA-CERT

0707010000001B000081B40000000000000000000000015D403FC8000004E1000000000000000000000000000000000000002C00000000mgr-cfg/config_client/rhncfgcli_channels.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.
#

from config_common.rhn_log import log_debug

import handler_base

class Handler(handler_base.HandlerBase):
    def run(self):
        log_debug(2)
        r = self.repository

        config_channels = r.load_config_channels()
        if not config_channels:
            print("(no config channels associated with this system)")
            return
        print("Config channels:")
        templ = "%-32s%s"
        label = "Label"
        name = "Name"
        print(templ % (label, name))
        print(templ % ('-' * len(label), '-' * len(name)))
        for c in config_channels:
            print(templ % (c['label'], c['name']))
0707010000001C000081B40000000000000000000000015D403FC800000C5E000000000000000000000000000000000000002800000000mgr-cfg/config_client/rhncfgcli_diff.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 os

import handler_base
from config_common.file_utils import diff
from config_common.local_config import get as get_config

class Handler(handler_base.HandlerBase):
    _options_table = [
        handler_base.HandlerBase._option_class(
            '-d', '--diff-secure-files', dest='display_diff',
            action="store_true", default=False,
            help="Force diff to display the diff for secure files.",
        )
    ]
    _usage_options = handler_base.HandlerBase._usage_options + " [ files ... ]"
    def _process_file(self, *args):
        src, dst= args [:2]
        type = args[3]
        file_info = args[4]

        if type == 'symlink':
            if not os.path.exists(dst):
                print("Symbolic link '%s' is missing" % dst)
                return

            if not os.path.islink(dst):
                print("Path '%s' is not a symbolic link" % dst)
                return

            #dst is a symlink, so just tell the user we're skipping the entry
            srclink = os.readlink(src)
            destlink = os.readlink(dst)
            if srclink != destlink:
                print("Symbolic links differ. Channel: '%s' -> '%s'   System: '%s' -> '%s' " % (dst,srclink, dst, destlink))
        elif type == 'file':
            if 'is_binary' in file_info and file_info['is_binary'] == 'Y':
                src_content = dst_content = None
                content_differs = False
                src_file = open(src, 'rb')
                src_content = src_file.read()
                src_file.close()
                if os.access(dst, os.R_OK):
                    dst_file = open(dst, 'rb')
                    dst_content = dst_file.read()
                    dst_file.close()
                else:
                    print("File %s that is not readable. Re-deployment of configuration file is recommended." % dst)
                    return
                if len(src_content) != len(dst_content):
                    content_differs = True
                else:
                    for i in range(len(src_content)):
                        if src_content[i] != dst_content[i]:
                            content_differs = True
                            break
                if content_differs:
                    sys.stdout.write("Binary file %s differs.\n" % (dst))
            else:
                sys.stdout.write(''.join(diff(src, dst, srcname=dst, dstname=dst,
                    display_diff=
                    (self.options.display_diff or get_config('display_diff')))))
0707010000001D000081FD0000000000000000000000015D403FC800000714000000000000000000000000000000000000002900000000mgr-cfg/config_client/rhncfgcli_elist.pyfrom config_common.rhn_log import log_debug, die
from config_common.file_utils import ostr_to_sym
import handler_base, base64
import sys
from rhn.i18n import bstr


class Handler(handler_base.HandlerBase):
    def run(self):
        log_debug(2)
        r = self.repository
        files = r.list_files()

        if not files:
            die(1, "No managed files.")

        label = "Config Channel"
        maxlen = max([len(s[0]) for s in files])
        maxlen = max(maxlen, len(label)) + 2
        print("%-10s %8s %-8s %10s %+3s    %*s    %s" % ('Mode', 'Owner', 'Group', 'Size', 'Rev', maxlen, label, "File"))

        arg_files = []
        if len(sys.argv) > 2:
            arg_files = sys.argv[2:len(sys.argv)]

        for file in files:

            if len(arg_files) and not file[1] in arg_files:
                continue

            # Get the file info
            finfo = r.get_file_info(file[1])[1]
            # Get the file length
            if finfo['encoding'] == 'base64':
                fsize = len(base64.decodestring(bstr(finfo['file_contents'])))
            else:
                # * indicates raw 'unencoded' size
                fsize = '*' + str(len(finfo['file_contents']))

            if finfo['filetype'] == 'symlink':
                permstr = ostr_to_sym('777', finfo['filetype'])
                dest = "%s -> %s" % (file[1], finfo['symlink'])
                fsize = str(len(finfo['symlink']))
                finfo['username'] = 'root'
                finfo['groupname'] = 'root'
            else:
                permstr = ostr_to_sym(finfo['filemode'], finfo['filetype']) or ''
                dest = file[1]
            print("%10s %8s %-8s %10s %+3s    %*s    %s" % (permstr, finfo['username'], finfo['groupname'], fsize, finfo['revision'], maxlen, file[0], dest))

0707010000001E000081B40000000000000000000000015D403FC8000003E6000000000000000000000000000000000000002700000000mgr-cfg/config_client/rhncfgcli_get.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 handler_base
from config_common.deploy import deploy_files

class Handler(handler_base.TopdirHandlerBase):
    _usage_options = handler_base.HandlerBase._usage_options + " [ files ... ]"
    def run(self):
        deploy_files(self.options.topdir,
                     self.repository,
                     self.get_valid_files(),
                     self.options.exclude)



0707010000001F000081B40000000000000000000000015D403FC8000006F6000000000000000000000000000000000000002800000000mgr-cfg/config_client/rhncfgcli_list.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.
#

from config_common.rhn_log import log_debug, die

import handler_base
import sys

class Handler(handler_base.HandlerBase):
    def run(self):
        log_debug(2)
        r = self.repository

        files = r.list_files()

        if not files:
            die(1, "No managed files.")

        label = "Config Channel"
        maxlen = max([len(s[0]) for s in files])
        maxlen = max(maxlen, len(label)) + 2

        print("DoFoS %*s   %s" % (maxlen, label, "File"))
        arg_files = []
        if len(sys.argv) > 2:
            arg_files = sys.argv[2:len(sys.argv)]

        for file in files:

            if len(arg_files) and not file[1] in arg_files:
                continue

            # checking to see if the filetype is in the 'file' entry,
            # and if it is and that type is '1', it is a file
            if (len(file) < 3) or file[2] == 1:
                print("F %*s     %s" % (maxlen, file[0], file[1]))
            elif file[2] == 2 :
                # the filetype is a directory
                print("D %*s     %s" % (maxlen, file[0], file[1]))
            else:
                print("S %*s     %s" % (maxlen, file[0], file[1]))
07070100000020000081B40000000000000000000000015D403FC8000031E2000000000000000000000000000000000000002A00000000mgr-cfg/config_client/rhncfgcli_verify.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.
#

from config_common import utils
from config_common.rhn_log import log_debug

import handler_base
import os
import stat
import pwd, grp
try:
    from selinux import lgetfilecon
except:
    # on rhel4 we do not support selinux
    def lgetfilecon(path):
        return [0, '']

class Handler(handler_base.HandlerBase):
    _usage_options = handler_base.HandlerBase._usage_options + " [ files ... ]"
    _options_table = [
        handler_base.HandlerBase._option_class(
            '--verbose',
            "-v",
            action="count",
            help="Increase the amount of output detail.",
        ),
        handler_base.HandlerBase._option_class(
            '--only',
            "-o",
            action="count",
            help="Only show files that differ.",
        ),
    ]

    # Main function to be run
    def run(self):
        log_debug(2)
        ret = []

        #Labels for column headers
        status_label = "STATUS"
        owner_status = "OWNER"
        group_status = "GROUP"
        mode_status = "MODE"
        selinux_status = "SELINUX"
        file_status = "FILE"

        status_help = "(channel:local)"

        maxlenarr = {
            'status' : len(status_label),
            'owner' : max(len(owner_status), len(status_help)),
            'group' : max(len(group_status), len(status_help)),
            'mode' : max(len(mode_status), len(status_help)),
            'selinux' : max(len(selinux_status), len(status_help)),
        }

        #Iterate throught the files and process them. The src file is the file as it is in the config channel,
        #the dst file is the file as it is in the filesystem.
        for file in self.get_valid_files():
            (src, file_info, dirs_created) = self.repository.get_file_info(file)

            ftype = file_info.get('filetype')

            if not src:
                continue

            dst = self.get_dest_file(file)

            #Added file_info parameter, which contains information needed to look for differences in the owner, group, and mode.
            ret_dict = self._process_file(src, dst, file, ftype, file_info)

            if self.options.verbose:
                #Get the max of the return values for this file, which is used to determine the length of each field in the output.
                #Don't include the 'file' value, because it gets displayed last in each row and will throw off the size of the other fields.
                maxlenarr['status'] = max(maxlenarr['status'], len(ret_dict['status']))
                maxlenarr['owner'] = max(maxlenarr['owner'], len(ret_dict['owner']))
                maxlenarr['group'] = max(maxlenarr['group'], len(ret_dict['group']))
                maxlenarr['mode'] = max(maxlenarr['mode'], len(ret_dict['mode']))
                if len(ret_dict['selinux']) > 0:
                    (src, dst) = ret_dict['selinux'].split('|')
                    maxlenarr['selinux'] = max(maxlenarr['selinux'], len(src), len(dst))

            #Place the return values into a list so we can iterate through them later when we want to print them out.
            ret.append(ret_dict)

        if self.options.verbose:
            formatstr = "%-*s"      #format string for the fields where the length matters.
            formatstr_nolimit = "%-s"   #format string for the fields where the length of the field doesn't matter. Namely, the file field.

            #The overall format of the output.
            outstring = "%(status)s  %(owner)s  %(group)s  %(mode)s  %(selinux)s  %(file)s"

            #Print out the column labels.
            print(outstring % {
                                    "status"       :       formatstr % (maxlenarr['status'], status_label),
                                    "owner"        :       formatstr % (maxlenarr['owner'], owner_status),
                                    "group"        :       formatstr % (maxlenarr['group'], group_status),
                                    "mode"         :       formatstr % (maxlenarr['mode'], mode_status),
                                    "selinux"      :       formatstr % (maxlenarr['selinux'], selinux_status),
                                    "file"         :       formatstr_nolimit % (file_status),
                              })

            print(outstring % {
                                    "status"       :       formatstr % (maxlenarr['status'], ""),
                                    "owner"        :       formatstr % (maxlenarr['owner'], status_help),
                                    "group"        :       formatstr % (maxlenarr['group'], status_help),
                                    "mode"         :       formatstr % (maxlenarr['mode'], status_help),
                                    "selinux"      :       formatstr % (maxlenarr['selinux'], status_help),
                                    "file"         :       ""
                              })

            #Go through each of the dictionaries returned by self._process_file(), format their values, and print out the result.
            for fdict in ret:
                src_selinux = dst_selinux = ""
                if len(fdict['selinux']) > 0:
                    (src_selinux, dst_selinux) = fdict['selinux'].split('|')

                if self.options.only:
                    sum = 0
                    for key in fdict.keys():
                        if key != 'file':
                            sum += len(fdict[key])
                    if sum == 0:
                        continue

                print(outstring % {
                                    "status"       :       formatstr % (maxlenarr['status'], fdict['status']),
                                    "owner"        :       formatstr % (maxlenarr['owner'], fdict['owner']),
                                    "group"        :       formatstr % (maxlenarr['group'], fdict['group']),
                                    "mode"         :       formatstr % (maxlenarr['mode'], fdict['mode']),
                                    "selinux"      :       formatstr % (maxlenarr['selinux'], src_selinux),
                                    "file"         :       formatstr_nolimit % (fdict['file']),
                                  })
                if len(dst_selinux) > 0:
                    print(outstring % {
                                    "status"       :       formatstr % (maxlenarr['status'], ""),
                                    "owner"        :       formatstr % (maxlenarr['owner'], ""),
                                    "group"        :       formatstr % (maxlenarr['group'], ""),
                                    "mode"         :       formatstr % (maxlenarr['mode'], ""),
                                    "selinux"      :       formatstr % (maxlenarr['selinux'], dst_selinux),
                                    "file"         :       "",
                                      })
        #Not verbose, so give the simple output for each file...
        else:
            outstring = "%*s %s"
            maxlen = max([0] + [len(x['status']) for x in ret]) + 1
            for fdict in ret:
                if self.options.only and len(fdict['status']) == 0:
                    continue
                print(outstring % (maxlen, fdict['status'], fdict['file']))

    def _process_file(self, *args):
        owner_report = "%s:%s"
        group_report = "%s:%s"
        perm_report = "%s:%s"
        selinux_report = "%s|%s"

        src, dst, file, type, info = args[:5]
        owner_status = ""
        group_status = ""
        perm_status = ""
        selinux_status = ""

        status = []
        stat_err = 0
        #Stat the destination file
        try:
            dst_stat = os.lstat(dst)
        except:
            stat_err = 1
        if type != 'symlink':
            src_user = info['username']
            if not stat_err:
                #check for owner differences
                dst_uid = dst_stat[stat.ST_UID]
                try:
                    dst_user = pwd.getpwuid(dst_uid)[0]
                except KeyError:
                    # Orphan UID with no name,return unknown
                    dst_user = "unknown(UID %d)" % (dst_uid,)
            else:
                dst_user = "missing"

            #owner_status gets displayed with the verbose option.
            if src_user == dst_user:
                owner_status = ""
            else:
                owner_status = owner_report % (src_user, dst_user)
                status.append('user')

            src_group = info['groupname']
            if not stat_err:
                #check for group differences
                dst_gid = dst_stat[stat.ST_GID]
                try:
                    dst_group = grp.getgrgid(dst_gid)[0]
                except KeyError:
                    # Orphan GID with no name,return unknown
                    dst_group = "unknown(GID %d)" % (dst_gid,)
            else:
                dst_group = "missing"

            #group_status gets displayed with the verbose option.
            if src_group == dst_group:
                group_status = ""
            else:
                group_status = group_report % (src_group, dst_group)
                status.append('group')

            #check for permissions differences
            src_perm = str(info['filemode'])
            if not stat_err:
                #The mode returned by stat is decimal, but won't match the value in file_info unless it's octal.
                #Unfortunately, the mode in file_info looks like the octal value of the mode, except it's in decimal.
                #The solution I came up with is to convert them both into strings, rip off the leading '0' from the
                #mode returned by stat, use the resulting strings. It sucks, but it seems to work (for now).
                dst_perm = str(oct(stat.S_IMODE(dst_stat[stat.ST_MODE])))
            else:
                dst_perm = "missing"

            #rip off the leading '0' from the mode returned by stat()
            if dst_perm[0] == '0':
                dst_perm = dst_perm[1:]

            #perm_status gets displayed with the verbose option.
            if src_perm == dst_perm:
                perm_status = ""
            else:
                perm_status = perm_report % (src_perm, dst_perm)
                status.append('mode')

        # compare selinux contexts
        if 'selinux_ctx' in info:
            src_selinux = info['selinux_ctx']
            if src_selinux:
                if not stat_err:
                    try:
                        dst_selinux = lgetfilecon(dst)[1]
                    except OSError:
                        dst_selinux = ""
                    if dst_selinux == None:
                        dst_selinux = ""
                else:
                    dst_selinux = "missing"

                if src_selinux == dst_selinux:
                    selinux_status = ""
                else:
                    selinux_status = selinux_report % (src_selinux, dst_selinux)
                    status.append('selinux')

        #figure out the ultimate value of status.
        if stat_err:
            status = ["missing"]
        elif type == 'symlink':
            if not os.path.islink(file):
                status = ["missing"]
            elif os.readlink(file) != info['symlink']:
                status.append('target-link-modified')
        elif type == 'directory':
            if not os.path.isdir(file):
                status = ["missing"]

        elif not os.access(dst, os.R_OK):
            status = ["missing"]

        else:
            src_sum = utils.sha256_file(src)
            dst_sum = utils.sha256_file(dst)
            if src_sum != dst_sum:
                status.append('modified')

        return {
                    "status"            :   ','.join(status),
                    "owner"             :   owner_status,
                    "group"             :   group_status,
                    "mode"              :   perm_status,
                    "selinux"           :   selinux_status,
                    "file"              :   file,
               }

07070100000021000081B40000000000000000000000015D403FC80000182D000000000000000000000000000000000000002C00000000mgr-cfg/config_client/rpc_cli_repository.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 sys

try: # python2
    import xmlrpclib
except ImportError: # python3
    import xmlrpc.client as xmlrpclib

from config_common import local_config, cfg_exceptions, file_utils, \
    repository
from config_common.rhn_log import log_debug
from spacewalk.common.usix import raise_with_tb

import traceback

class ClientRepository(repository.RPC_Repository):

    default_systemid = "/etc/sysconfig/rhn/systemid"

    # bug #170825,169203: reusing the base class's default value for setup_network
    def __init__(self, setup_network=1):
        repository.RPC_Repository.__init__(self, setup_network)

        systemid_file = local_config.get("systemid") or self.default_systemid
        try:
            f = open(systemid_file, "r")
        except IOError:
            e = sys.exc_info()[1]
            sys.stderr.write("Cannot open %s: %s\n" % (systemid_file, e))
            sys.exit(1)

        self.system_id = f.read()
        f.close()

        log_debug(4, 'system id', self.system_id)

        self.files_to_delete = []

    def rpc_call(self, method_name, *params):
        try:
            result = repository.RPC_Repository.rpc_call(self, method_name, *params)
        except xmlrpclib.Fault:
            e = sys.exc_info()[1]
            if e.faultCode == -9:
                # System not subscribed
                raise_with_tb(cfg_exceptions.AuthenticationError(
                    "Invalid digital server certificate%s" % e.faultString), sys.exc_info()[2])
            raise
        return result

    def load_config_channels(self):
        log_debug(4)
        self.config_channels = self.rpc_call(
            'config.client.list_config_channels', self.system_id)
        return self.config_channels

    def list_files(self):
        log_debug(4)
        return self.rpc_call('config.client.list_files', self.system_id)

    def get_file_info(self, file, auto_delete=1, dest_directory=None):
        log_debug(4, file)
        result = self.rpc_call('config.client.get_file', self.system_id, file)

        if 'missing' in result:
            return None

        dirs_created = None

        # Older servers will not return directories; if filetype is missing,
        # assume file
        if result.get('filetype') == 'directory':
            if dest_directory:
                result['path'] = dest_directory.rstrip(os.path.sep) + result['path']
            if os.path.isfile(result['path']):
                raise cfg_exceptions.DirectoryEntryIsFile(result['path'])
            else:
                auto_delete = 0
                temp_file = result['path']
        else:
            f = file_utils.FileProcessor()
            temp_file, dirs_created = f.process(result, directory=dest_directory)

        if auto_delete:
            self.files_to_delete.append(temp_file)

        return temp_file, result, dirs_created

    def put_files(self, action_id, files, upload_contents=1):
        """Inserts a set of files into the repo, as a result of a scheduled
        action"""
        log_debug(4)
        missing_files = []
        files_too_large = []
        failed_due_to_quota = []

        max_file_size = self.get_maximum_file_size()

        for file in files:
            try:
                params = self._make_file_info(file, local_path=None,
                    load_contents=upload_contents)
            except cfg_exceptions.RepositoryLocalFileError:
                missing_files.append(file)
                continue

            if upload_contents and (params['size'] > max_file_size):
                files_too_large.append(file)
                continue

            try:
                self.rpc_call('config.client.upload_file',
                    self.system_id, action_id, params)
            except xmlrpclib.Fault:
                e = sys.exc_info()[1]
                fault_code, fault_string = e.faultCode, e.faultString
                # deal with particular faults
                if fault_code == -4003:
                    # File too large
                    files_too_large.append(file)
                elif fault_code == -4014:
                    # Ran out of org quota space
                    failed_due_to_quota.append(file)
                else:
                    raise_with_tb(cfg_exceptions.RepositoryFilePushError(fault_code,
                        fault_string), sys.exc_info()[2])
            except Exception:
                traceback.print_exc()
                raise

        result = {}
        # If there are files too large to be pushed, result will have a key
        # `file_too_large'
        if len(files_too_large) > 0:
            result['files_too_large'] = files_too_large

        if len(failed_due_to_quota) > 0:
            result['failed_due_to_quota'] = failed_due_to_quota

        if len(missing_files) > 0:
            result['missing_files'] = missing_files

        return result


    def list_config_channels(self):
        log_debug(4)
        return self.config_channels

    def _get_default_delimiters(self):
        "retrieves the default delimiters from the server"
        log_debug(4)
        result = self.rpc_call('config.client.get_default_delimiters',
            self.system_id)
        return result.get('delim_start'), result.get('delim_end')

    def _get_maximum_file_size(self):
        log_debug(4)
        result = self.rpc_call('config.client.get_maximum_file_size',
            self.system_id)
        return result

    def cleanup(self):
        log_debug(4)
        for file in self.files_to_delete:
            os.unlink(file)
07070100000022000041FD0000000000000000000000015D403FC800000000000000000000000000000000000000000000001600000000mgr-cfg/config_common07070100000023000081B40000000000000000000000015D403FC80000016B000000000000000000000000000000000000001F00000000mgr-cfg/config_common/Makefile# Makefile for spacewalk backend
#

# what is the backend top dir
TOP     = ..

# Specific stuff
SUBDIR		= config_common
FILES           = __init__ cfg_exceptions \
                  file_utils deploy \
                  handler_base local_config \
		  repository rhn_log rhn_main \
                  utils transactions rpc_wrapper


include $(TOP)/Makefile.defs
07070100000024000081B40000000000000000000000015D403FC800000266000000000000000000000000000000000000002200000000mgr-cfg/config_common/__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.
#

07070100000025000081B40000000000000000000000015D403FC8000006FE000000000000000000000000000000000000002800000000mgr-cfg/config_common/cfg_exceptions.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.
#

class InvalidSession(Exception):
    pass

class AuthenticationError(Exception):
    pass

class ConfigChannelNotInRepo(Exception):
    pass

class ConfigChannelAlreadyExistsError(Exception):
    pass

class ConfigChannelNotEmptyError(Exception):
    pass

class ConfigNotManaged(Exception):
    pass

class ConfigurationError(Exception):
    pass

class BinaryFileDiffError(Exception):
    pass

class RepositoryFileError(Exception):
    pass

class RepositoryLocalFileError(Exception):
    pass

class RepositoryFileMissingError(Exception):
    pass

class RepositoryFilePushError(RepositoryFileError):
    pass

class ConfigFileTooLargeError(RepositoryFilePushError):
    pass

class QuotaExceeded(RepositoryFilePushError):
    pass

class RepositoryFileExistsError(RepositoryFilePushError):
    "Attempted to add a file that already exists"
    pass

class RepositoryFileVersionMismatchError(RepositoryFilePushError):
    "File upload failed because the version changed underneath"
    pass

class FileEntryIsDirectory(Exception):
    pass

class DirectoryEntryIsFile(Exception):
    pass

class UserNotFound(Exception):
    pass

class GroupNotFound(Exception):
    pass
07070100000026000081B40000000000000000000000015D403FC800000EA0000000000000000000000000000000000000002000000000mgr-cfg/config_common/deploy.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

from config_common.transactions import DeployTransaction, FailedRollback
from config_common import file_utils
from config_common import cfg_exceptions

def deploy_msg_callback(path):
    print("Deploying %s" % path)

def deploy_files(topdir, repository, files, excludes = None, config_channel = None):
    topdir = topdir or os.sep
    if not excludes:
        excludes = []
    dep_trans = DeployTransaction(transaction_root=topdir)
    dep_trans.deploy_callback(deploy_msg_callback)

    for path in files:
        if path in excludes:
            print("Excluding %s" % path)
        else:
            try:
                if config_channel:
                    args = (config_channel, path)
                else:
                    args = (path, )
                kwargs = {'auto_delete': 0, 'dest_directory': topdir}
                finfo = repository.get_file_info(*args, **kwargs)
            except cfg_exceptions.DirectoryEntryIsFile:
                e = sys.exc_info()[1]
                print("Error: unable to deploy directory %s, as it is already a file on disk" % e[0])
                continue

            if finfo is None:
                # File disappeared since we called the function
                continue

            (processed_path, file_info, dirs_created) = finfo
            try:
                dep_trans.add_preprocessed(path, processed_path, file_info, dirs_created)
            except cfg_exceptions.UserNotFound:
                e = sys.exc_info()[1]
                print("Error: unable to deploy file %s, information on user '%s' could not be found." % (path,e[0]))
                continue
            except cfg_exceptions.GroupNotFound:
                e = sys.exc_info()[1]
                print("Error: unable to deploy file %s, information on group '%s' could not be found." % (path, e[0]))
                continue

    try:
        dep_trans.deploy()
    #5/3/05 wregglej - 136415 added missing user exception stuff.
    except cfg_exceptions.UserNotFound:
        e = sys.exc_info()[1]
        try_rollback(dep_trans, "Error unable to deploy file, information on user '%s' could not be found" % e[0])
    except cfg_exceptions.GroupNotFound:
        e = sys.exc_info()[1]
        try_rollback(dep_trans, "Error: unable to deploy file, information on group '%s' could not be found" % e[0])
    except cfg_exceptions.FileEntryIsDirectory:
        e = sys.exc_info()[1]
        try_rollback(dep_trans, "Error: unable to deploy file %s, as it is already a directory on disk" % e[0])
    except cfg_exceptions.DirectoryEntryIsFile:
        e = sys.exc_info()[1]
        try_rollback(dep_trans, "Error: unable to deploy directory %s, as it is already a file on disk" % e[0])
    except Exception:
        try:
            try_rollback(dep_trans, "Deploy failed, rollback successful")
        except:
            print("Failed rollback")
            raise

def try_rollback(dep_trans, msg):
    try:
        dep_trans.rollback()
    except (FailedRollback,
            cfg_exceptions.UserNotFound,
            cfg_exceptions.GroupNotFound):
        pass
    print(msg)
07070100000027000081B40000000000000000000000015D403FC8000033F6000000000000000000000000000000000000002400000000mgr-cfg/config_common/file_utils.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 sys
import stat
import time
import tempfile
import base64
import difflib
import pwd
import grp
try:
    from selinux import lgetfilecon
except:
    # on rhel4 we do not support selinux
    def lgetfilecon(path):
        return [0, '']

from config_common import utils
from config_common.local_config import get as get_config
from rhn.i18n import bstr, sstr

decodestring = base64.decodestring
if hasattr(base64, 'decodebytes'):
    decodestring = base64.decodebytes

class FileProcessor:
    file_struct_fields = {
        'file_contents'     : None,
        'delim_start'       : None,
        'delim_end'         : None,
    }
    def __init__(self):
        pass

    def process(self, file_struct, directory=None, strict_ownership=1):
        # Older servers will not return directories; if filetype is missing,
        # assume file

        if file_struct.get('filetype') == 'directory':
            if directory is None:
                directory = ""
            return None, utils.mkdir_p(directory + file_struct['path'])

        if directory:
            directory += os.path.split(file_struct['path'])[0]
        if file_struct.get('filetype') == 'symlink':
            if 'symlink' not in file_struct:
                raise Exception("Missing key symlink")

            (fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory)
            os.close(fd)
            os.unlink(fullpath)
            os.symlink(file_struct['symlink'], fullpath)
            return fullpath, dirs_created

        for k in self.file_struct_fields.keys():
            if k not in file_struct:
                # XXX
                raise Exception("Missing key %s" % k)

        encoding = ''

        if 'encoding' in file_struct:
            encoding = file_struct['encoding']

        contents = file_struct['file_contents']

        if contents and (encoding == 'base64'):
            contents = decodestring(bstr(contents))

        delim_start = file_struct['delim_start']
        delim_end = file_struct['delim_end']

        if ('checksum' in file_struct
                and 'checksum_type' in file_struct
                and 'verify_contents' in file_struct
                and file_struct['verify_contents']):
            if file_struct['checksum'] != utils.getContentChecksum(
                    file_struct['checksum_type'], contents):
                raise Exception("Corrupt file received: Content checksums do not match!")
        elif ('md5sum' in file_struct and 'verify_contents' in file_struct
                and file_struct['verify_contents']):
            if file_struct['md5sum'] != utils.getContentChecksum(
                    'md5', contents):
                raise Exception("Corrupt file received: Content checksums do not match!")
        elif ('verify_contents' in file_struct
                and file_struct['verify_contents']):
            raise Exception("Corrupt file received: missing checksum information!")


        (fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory)

        try:
            os.write(fd, bstr(contents))
        except Exception:
            raise
        finally:
            os.close(fd)

        # try to set mtime and ctime of the file to
        # the last modified time on the server
        if 'modified' in file_struct:
            try:
                modified = xmlrpc_time(file_struct['modified'].value)
                epoch_time = time.mktime(modified)
                os.utime(fullpath, (epoch_time, epoch_time))
            except (ValueError, AttributeError):
                # we can't parse modified time
                pass

        return fullpath, dirs_created


    def diff(self, file_struct):
        self._validate_struct(file_struct)

        temp_file, temp_dirs = self.process(file_struct)
        path = file_struct['path']
        sectx_result = ''
        owner_result = ''
        group_result = ''
        perm_result = ''
        result = ''

        stat_err = 0

        try:
            cur_stat = os.lstat(path)
        except:
            stat_err = 1

        if file_struct['filetype'] != 'symlink':
            if not stat_err:
                 #check for owner differences
                 cur_uid = cur_stat[stat.ST_UID]
                 try:
                     cur_user = pwd.getpwuid(cur_uid)[0]
                 except KeyError:
                     #Orphan UID with no name,return unknown
                     cur_user = "unknown(UID %d)" % (cur_uid,)
            else:
                 cur_user = "missing"

            if cur_user == file_struct['username']:
                 owner_result = ""

            else:
                 owner_result = "User name differ: actual: [%s], expected: [%s]\n" % (cur_user, file_struct['username'])

            if not stat_err:
                #check for group differences
                cur_gid = cur_stat[stat.ST_GID]
                try:
                    cur_group = grp.getgrgid(cur_gid)[0]
                except KeyError:
                    #Orphan GID with no name,return unknown
                    cur_group = "unknown(GID %d)" % (cur_gid,)
            else:
                cur_group = "missing"

            if cur_group == file_struct['groupname']:
                group_result = ""
            else:
                group_result = "Group name differ: actual: [%s], expected: [%s]\n" % (cur_group, file_struct['groupname'])

            #check for permissions differences
            if not stat_err:
                cur_perm = format(stat.S_IMODE(cur_stat[stat.ST_MODE]), 'o')
            else:
                cur_perm = "missing"

            #rip off the leading '0' from the mode returned by stat()
            if cur_perm[0] == '0':
                cur_perm = cur_perm[1:]

            #perm_status gets displayed with the verbose option.
            if cur_perm == str(file_struct['filemode']):
                perm_result = ""
            else:
                perm_result = "File mode differ: actual: [%s], expected: [%s]\n" % (cur_perm, file_struct['filemode'])

        try:
            cur_sectx = lgetfilecon(path)[1]
        except OSError: # workarounding BZ 690238
            cur_sectx = None

        if cur_sectx == None:
            cur_sectx = ''

        if 'selinux_ctx' in file_struct and file_struct['selinux_ctx']:
            if cur_sectx != file_struct['selinux_ctx']:
                sectx_result = "SELinux contexts differ:  actual: [%s], expected: [%s]\n" % (cur_sectx, file_struct['selinux_ctx'])

        if file_struct['filetype'] == 'directory':
            if os.path.isdir(file_struct['path']):
                result = ''
            else:
                result = "Deployed directory is no longer a directory!"
        elif file_struct['filetype'] == 'symlink':
            try:
                curlink = os.readlink(path)
                newlink = os.readlink(temp_file)
                if curlink == newlink:
                    result = ''
                else:
                    result = "Link targets differ for [%s]: actual: [%s], expected: [%s]\n" % (path, curlink, newlink)
            except OSError:
                e = sys.exc_info()[1]
                if e.errno == 22:
                    result = "Deployed symlink is no longer a symlink!"
                else:
                    raise e
        else:
            result = ''.join(diff(temp_file, path, display_diff=get_config('display_diff'),
                is_binary=False if file_struct['is_binary'] == 'N' else True))

        if temp_file:
            os.unlink(temp_file)
        return owner_result + group_result + perm_result + sectx_result + result

    def _validate_struct(self, file_struct):
        for k in self.file_struct_fields.keys():
            if k not in file_struct:
                # XXX
                raise Exception("Missing key %s" % k)


def diff(src, dst, srcname=None, dstname=None, display_diff=False, is_binary=False):
    def f_content(path, name, is_binary):
        statinfo = None
        if os.access(path, os.R_OK):
            f = open(path, ('r' if int(sys.version[0]) == 3 else 'U') + ('b' if is_binary else ''))
            content = [sstr(i) for i in f.readlines()]
            f.close()
            statinfo = os.stat(path)
            f_time = time.ctime(statinfo.st_mtime)
            if not is_binary and content and content[-1] and content[-1][-1] != "\n":
                content[-1] += "\n"
        else:
            content = []
            f_time = time.ctime(0)
        if not name:
            name = path
        if is_binary:
            return (str(content), name, f_time, statinfo)
        return (content, name, f_time, statinfo)

    (src_content, src_name, src_time, src_stat) = f_content(src, srcname, is_binary)
    (dst_content, dst_name, dst_time, dst_stat) = f_content(dst, dstname, is_binary)

    diff_u = difflib.unified_diff(src_content, dst_content,
                                  src_name, dst_name,
                                  src_time, dst_time)

    ret_list = list(diff_u)
    # don't return the diff if the file is not readable by everyone
    # for security reasons.
    if (len(ret_list) > 0 # if differences exist
            and not display_diff # and we have not explicitly decided to display
            and (dst_stat == None # file is not there or not readable to root
                or (dst_stat.st_uid == 0 # file is owned by root
                    and not dst_stat.st_mode & stat.S_IROTH))): # not read-all
        ret_list = [
                "Differences exist in a file %s that is not readable by all. " % dst,
                "Re-deployment of configuration file is recommended."]
    return ret_list



def maketemp(prefix=None, directory=None):
    """Creates a temporary file (guaranteed to be new), using the
       specified prefix.

       Returns the filename and a file descriptor
    """
    if not directory:
        directory = tempfile.gettempdir()

    dirs_created = None
    if not os.path.exists(directory):
        dirs_created = utils.mkdir_p(directory)

    if not prefix:
    # Create the file in /tmp by default
        prefix = 'rhncfg-tempfile'

    file_prefix = "%s-%s-" % (prefix, os.getpid())
    (fd, filename) = tempfile.mkstemp(prefix=file_prefix, dir=directory)

    return filename, dirs_created, fd


# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
FILETYPE2CHAR = {
    'file'      : '-',
    'directory' : 'd',
    'symlink'   : 'l',
    'chardev'   : 'c',
    'blockdev'  : 'b',
}

# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
def _ifelse(cond, thenval, elseval):
    if cond:
        return thenval
    else:
        return elseval

# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
def ostr_to_sym(octstr, ftype):
    """ Convert filemode in octets (like '644') to string like "ls -l" ("-rwxrw-rw-")
        ftype is one of: file, directory, symlink, chardev, blockdev.
    """
    mode = int(str(octstr), 8)

    symstr = FILETYPE2CHAR.get(ftype, '?')

    symstr += _ifelse(mode & stat.S_IRUSR, 'r', '-')
    symstr += _ifelse(mode & stat.S_IWUSR, 'w', '-')
    symstr += _ifelse(mode & stat.S_IXUSR,
                      _ifelse(mode & stat.S_ISUID, 's', 'x'),
                      _ifelse(mode & stat.S_ISUID, 'S', '-'))
    symstr += _ifelse(mode & stat.S_IRGRP, 'r', '-')
    symstr += _ifelse(mode & stat.S_IWGRP, 'w', '-')
    symstr += _ifelse(mode & stat.S_IXGRP,
                      _ifelse(mode & stat.S_ISGID, 's', 'x'),
                      _ifelse(mode & stat.S_ISGID, 'S', '-'))
    symstr += _ifelse(mode & stat.S_IROTH, 'r', '-')
    symstr += _ifelse(mode & stat.S_IWOTH, 'w', '-')
    symstr += _ifelse(mode & stat.S_IXOTH,
                      _ifelse(mode & stat.S_ISVTX, 't', 'x'),
                      _ifelse(mode & stat.S_ISVTX, 'T', '-'))
    return symstr

# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
def f_date(dbiDate):
    return "%04d-%02d-%02d %02d:%02d:%02d" % (dbiDate.year, dbiDate.month,
        dbiDate.day, dbiDate.hour, dbiDate.minute, dbiDate.second)

def xmlrpc_time(xtime):
    if xtime[8] == 'T':
        # oracle backend: 20130304T23:19:17
        timefmt='%Y%m%dT%H:%M:%S'
    else:
        # postresql backend format: 2014-02-28 18:47:31.506953+01:00
        timefmt='%Y-%m-%d %H:%M:%S'
        xtime = xtime[:19]

    return time.strptime(xtime, timefmt)
07070100000028000081B40000000000000000000000015D403FC800000C3D000000000000000000000000000000000000002600000000mgr-cfg/config_common/handler_base.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 getpass
from optparse import OptionParser, Option

from config_common import rhn_log
from config_common import cfg_exceptions
from config_common import local_config
from rhn.i18n import bstr

class HandlerBase:
    _options_table = []
    _option_parser_class = OptionParser
    _usage_options = "[options]"
    _option_class = Option
    def __init__(self, args, repository, mode=None, exec_name=None):
        self.repository = repository
        self.set_mode(mode)
        self.set_exec_name(exec_name)
        self.options, self.args = self._parse_args(args)

    def set_mode(self, mode):
        self.mode = mode

    def set_exec_name(self, exec_name):
        self.exec_name = exec_name

    def _prog(self):
        return "%s %s" % (sys.argv[0], self.mode or "<unknown>")

    def _parse_args(self, args):
        # Parses the arguments and returns a tuple (options, args)
        usage = " ".join(["%prog", self.mode, self._usage_options])
        self._parser = self._option_parser_class(
            option_list=self._options_table,
            usage=usage)
        return self._parser.parse_args(args)

    def usage(self):
        return self._parser.print_help()

    def authenticate(self, username=None, password=None):
        # entry point for repository authentication

        try:
            self.repository.login()
        except cfg_exceptions.InvalidSession:
            if not username :
                username=local_config.get('username')
            if not password :
                password=local_config.get('password')

            if not password :
                (username, password) = self.get_auth_info(username)
            try:
                self.repository.login(username=username, password=password)
            except cfg_exceptions.InvalidSession:
                e = sys.exc_info()[1]
                rhn_log.die(1, "Session error: %s\n" % e)

    def get_auth_info(self, username=None):
        if username is None:
            username = self._read_username()

        password = getpass.getpass()

        return (username, password)

    def _read_username(self):
        tty = open("/dev/tty", "rb+", buffering=0)
        tty.write(bstr("SUSE Manager username: "))
        try:
            username = tty.readline()
        except KeyboardInterrupt:
            tty.write(bstr("\n"))
            sys.exit(0)
        if username is None:
            # EOF
            tty.write(bstr("\n"))
            sys.exit(0)
        return username.strip()

07070100000029000081B40000000000000000000000015D403FC80000117C000000000000000000000000000000000000002600000000mgr-cfg/config_common/local_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

# python2
try:
    import ConfigParser
except: #python3
    import configparser as ConfigParser


from config_common import utils

class rhncfgConfigParser(ConfigParser.ConfigParser):
    _local_config_file_name = '.rhncfgrc'
    _instance = None

    def __init__(self, section, defaults=None, config_file_override=None):
        """defaults is either None, or a dictionary of default values which can be overridden"""
        if defaults:
            for (k, v) in defaults.items():
              if type(v) == int:
                    defaults[k] = str(v)
        ConfigParser.ConfigParser.__init__(self, defaults)
        self.section = section
        self.overrides = {}
        self.mydefaults = self.defaults()
        self.config_file_override = config_file_override

    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):
        if sys.platform.find('sunos') > -1:
            default_config_file = "/opt/redhat/rhn/solaris/etc/sysconfig/rhn/%s.conf" % self.section
        else:
            default_config_file = "/etc/sysconfig/rhn/%s.conf" % self.section

        config_file_list = [
            default_config_file,
            os.path.join(utils.get_home_dir(), self._local_config_file_name),
            self._local_config_file_name,
        ]

        if self.config_file_override:
            config_file_list.append(self.config_file_override)

        return config_file_list

    def get_option(self, option):
        #6/29/05 wregglej 152388
        # server_list is always in the defaults, never in the rhncfg config file. It's formed when there
        # are more than one server in up2date's serverURL setting.
        if option == 'server_list':
            if 'server_list' in self.mydefaults:
                if type(self.mydefaults['server_list']) is type([]):
                    return self.mydefaults['server_list']

        try:
            ret = self.get(self.section, option, vars=self.overrides)

            #5/25/05 wregglej - 158694
            #Move the cast to an int to here from the up2date_config_parser, that way the stuff that needs to get interpolated
            #gets interpolated, the stuff that should be an int ends up and int, and the stuff that's neither doesn't get
            #messed with.
            try:
                if type(ret) != type([]):
                    ret = int(ret)
            except ValueError:
                pass
            return ret
        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
            pass

        defaults = self.defaults()

        return defaults.get(option)

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

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

def init(section, defaults=None, config_file_override=None, **overrides):
    cp = rhncfgConfigParser._instance = rhncfgConfigParser(section, defaults, config_file_override=config_file_override)
    cp.read_config_files(overrides)

def get(var):
    return _get_config().get_option(var)

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

def instance():
    return _get_config()

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

def main():
    init('rhncfgcli')
    print("repository: %s" % get("repository"))
    print("useGPG: %s" % get("useGPG"))
    print("serverURL: %s" % get("serverURL"))

if __name__ == '__main__':
    main()
0707010000002A000081B40000000000000000000000015D403FC800003D32000000000000000000000000000000000000002400000000mgr-cfg/config_common/repository.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 re
import os
import pwd
import grp
import sys
import stat
import base64
from config_common import cfg_exceptions
from config_common import local_config
from config_common import utils
from config_common.rhn_log import log_debug
from rhn import rpclib
Output = rpclib.transports.Output

try: # python2
    PY3 = False
    import xmlrpclib
except ImportError: # python3
    PY3 = True
    import xmlrpc.client as xmlrpclib
    basestring = (str, bytes)

from spacewalk.common.usix import raise_with_tb

try:
    from selinux import lgetfilecon
except:
    # on rhel4 we do not support selinux
    def lgetfilecon(path):
        return [0, '']

#6/29/05 rpc_wrapper implements the failover logic.
from config_common import rpc_wrapper
rpclib = rpc_wrapper


def deci_to_octal(number):
    """convert a normal decimal int to another int representing the octal value"""
    # python 2.4/2.6: oct(420) -> '0644'
    # python 3.4: oct(420) -> '0o644'
    return int(oct(number).replace("o", ""))


class Repository:
    _uid_cache = {}
    _gid_cache = {}
    _local_config = local_config

    def __init__(self):
        self.default_delimiters = None
        self.maximum_file_size = None

    # Helpers

    # Unless overridden in a subclass, per-file delimiters are the same as the
    # global delimiters
    def get_file_delimiters(self, file):
        "returns the default delimiters for this file"
        return self.get_default_delimiters()

    def get_default_delimiters(self):
        "returns the default delimiters"
        if self.default_delimiters is None:
            self.default_delimiters = self._get_default_delimiters()
        return self.default_delimiters

    def _get_default_delimiters(self):
        raise NotImplementedError

    def get_maximum_file_size(self):
        "returns the maximum file size"
        if self.maximum_file_size is None:
            self.maximum_file_size = self._get_maximum_file_size()
        return self.maximum_file_size

    def _get_maximum_file_size(self):
        "To be overwritten in subclasses"
        return 1024

    def make_stat_info(self, path, file_stat):
        # Returns the stat information as required by the API
        ret = {}
        fields = {
            'mode'      : stat.ST_MODE,
            'user'      : stat.ST_UID,
            'group'     : stat.ST_GID,
            'size'      : stat.ST_SIZE,
            'mtime'     : stat.ST_MTIME,
            'ctime'     : stat.ST_CTIME,
        }
        for label, st in fields.items():
            ret[label] = file_stat[st]

        # server expects things like 644, 700, etc.
        ret['mode'] = deci_to_octal(ret['mode'] & int('07777', 8))

        #print ret['size']
        #if ret['size'] > self.get_maximum_file_size():
        #    die(4, "File %s exceeds the maximum file size (%s)" %
        #        (path, ret['size']))

        uid = ret['user']
        gid = ret['group']

        pw_name = self._uid_cache.get(uid)
        if not pw_name:
            try:
                pw_name = pwd.getpwuid(uid)[0]
            except KeyError:
                print("Error looking up user id %s" % (uid, ))

        if pw_name:
            ret['user'] = pw_name
            self._uid_cache[uid] = pw_name

        gr_name = self._gid_cache.get(gid)
        if not gr_name:
            try:
                gr_name = grp.getgrgid(gid)[0]
            except KeyError:
                print("Error looking up group id %s" % (gid, ))

        if gr_name:
            ret['group'] = gr_name
            self._gid_cache[gid] = gr_name

        # if selinux is disabled or on RHEL4 we do not send the selinux_ctx
        # flag at all - see bug 644985 - SELinux context cleared from
        # RHEL4 rhncfg-client
        try:
            selinux_ctx = lgetfilecon(path)[1]
        except OSError:
            selinux_ctx = ''
        if selinux_ctx == None:
            selinux_ctx = ''

        ret['selinux_ctx'] = selinux_ctx

        return ret

    def _make_file_info(self, remote_path, local_path=None, delim_start=None,
            delim_end=None, load_contents=1):
        if not local_path:
            # Safe enough to assume local path is the same as the remote one
            local_path = remote_path

        try:
            file_stat = os.lstat(local_path)
        except OSError:
            e = sys.exc_info()[1]
            raise_with_tb(cfg_exceptions.RepositoryLocalFileError(
                "Error lstat()-ing local file: %s" % e), sys.exc_info()[2])

        # Dlimiters
        if delim_start or delim_end:
            if not (delim_start and delim_end):
                # If only one delimiter is provided, assume the delimiters are
                # the same, whatever that is (or is nice)
                delim_start = delim_end = (delim_start or delim_end)
        else:
            # Use the default
            delim_start, delim_end = self.get_file_delimiters(remote_path)

        params = {
            'path'          : remote_path,
            'delim_start'   : delim_start,
            'delim_end'     : delim_end,
        }

        file_contents = None
        if os.path.islink(local_path):
            params['config_file_type_id'] = 3
            params['symlink'] = os.readlink(local_path)
            load_contents = 0
        elif os.path.isdir(local_path):
            params['config_file_type_id'] = 2
            load_contents = 0
        else:
            params['config_file_type_id'] = 1

        if load_contents:
            try:
                file_contents = open(local_path, "rb").read()
            except IOError:
                e = sys.exc_info()[1]
                raise_with_tb(cfg_exceptions.RepositoryLocalFileError(
                    "Error opening local file: %s" % e), sys.exc_info()[2])

            self._add_content(file_contents, params)

        params.update(self.make_stat_info(local_path, file_stat))
        return params

    def _add_content(self, file_contents, params):
        """Add the file contents to the params hash"""

        params['enc64'] = 1
        params['file_contents'] = base64.encodestring(file_contents)

    def login(self, username=None, password=None):
        pass


class RPC_Repository(Repository):

    def __init__(self, setup_network=1):
        Repository.__init__(self)
        # all this so needs to be in a seperate rhnConfig library,
        # shared by up2date, rhncfg*, etc.
        #
        # But, I digress.

        self.__server_url = self._local_config.get('server_url')

        # 6/29/05 wregglej 152388
        # server_list contains the list of servers to failover to.
        self.__server_list = self._local_config.get('server_list')

        # 6/29/05 wregglej 152388
        # Grab server_handler, which is different for rhncfg-client and rhncfg-manager
        # and is needed when failover occurs. During a failover, when the server object is
        # being set up to use a new satellite, the server_handler is added to the address so
        # the tool communicates with the correct xmlrpc handler.
        handler = self._local_config.get('server_handler')
        cap_handler = re.sub('[^/]+$', 'XMLRPC', handler)

        if not self.__server_url:
            raise cfg_exceptions.ConfigurationError(
                "Missing entry 'server_url' in the config files\n" \
                "Try running as root, or configure server_url as described in the configuration file"
                )

        log_debug(3, "server url", self.__server_url)
        self.__proxy_user = None
        self.__proxy_password = None
        self.__proxy_host = None

        self.__enable_proxy = self._local_config.get('enableProxy')
        self.__enable_proxy_auth = self._local_config.get('enableProxyAuth')

        if self.__enable_proxy:
            self.__proxy_host = self._local_config.get('httpProxy')

            if self.__enable_proxy_auth:
                self.__proxy_user = self._local_config.get('proxyUser')
                self.__proxy_password = self._local_config.get('proxyPassword')

        ca = self._local_config.get('sslCACert')
        if isinstance(ca, basestring):
            ca = [ca]

        ca_certs = ca or ["/usr/share/rhn/RHNS-CA-CERT"]

        # not sure if we need this or not...
        lang = None
        for env in 'LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG':
            if env in os.environ:
                if not os.environ[env]:
                    # sometimes unset
                    continue
                lang = os.environ[env].split(':')[0]
                lang = lang.split('.')[0]
                break

        if setup_network:
            # Fetch server capabilities - we need the /XMLRPC handler
            #t = list(utils.parse_url(self.__server_url))
            #t[2] = '/XMLRPC'
            #x_server_url = utils.unparse_url(t)

            # 6/29/05 wregglej 152388
            # Fetching the server capabilities involves using the /XMLRPC handler. It's
            # the only place that I know of that does that. The server_url and server_list
            # both need to have /XMLRPC on the ends, which is what _patch_uris() does by default.
            x_server_url = self._patch_uris(self.__server_url, cap_handler)
            if self.__server_list != None:
                x_server_list = self._patch_uris(self.__server_list, cap_handler)
            else:
                x_server_list = None

            x_server = rpclib.Server(x_server_url,
                proxy=self.__proxy_host,
                username=self.__proxy_user,
                password=self.__proxy_password,
                server_list=x_server_list,
                rpc_handler="/XMLRPC")

            # Make a call to a function that can export the server's capabilities
            # without setting any state on the server side
            try:
                x_server.registration.welcome_message()
            except xmlrpclib.Fault:
                e = sys.exc_info()[1]
                sys.stderr.write("XML-RPC error while talking to %s:\n %s\n" % (self.__server_url, e))
                sys.exit(2)

            self._server_capabilities = get_server_capability(x_server)
            del x_server

        # 6/29/05 wregglej 152388
        # From here on out all communication should take place through the xmlrpc handler
        # that's appropriate for the tool being used. For rhncfg-client that's /CONFIG-MANAGEMENT.
        # For rhncfg-manager that's /CONFIG-MANAGEMENT-TOOL. No, I don't know the reasoning behind that.
        # First we need to patch the uris in server_list, to use the correct handler.
        self.__server_url = self._patch_uris(self.__server_url, handler)
        if self.__server_list != None:
            self.__server_list = self._patch_uris(self.__server_list, handler)
        else:
            self.__server_list = None

        self.server = rpclib.Server(self.__server_url,
                                    proxy=self.__proxy_host,
                                    username=self.__proxy_user,
                                    password=self.__proxy_password,
                                    server_list=self.__server_list,
                                    rpc_handler=handler)

        self._set_capabilities()
        self.server.set_transport_flags(
            transfer=Output.TRANSFER_BINARY,
            encoding=Output.ENCODE_GZIP
        )

        if lang:
            self.server.setlang(lang)

        for ca_cert in ca_certs:
            if not os.access(ca_cert, os.R_OK):
                raise cfg_exceptions.ConfigurationError("Can not find CA file: %s" % ca_cert)

            log_debug(3, "ca cert", ca_cert)
            # force the validation of the SSL cert
            self.server.add_trusted_cert(ca_cert)

    # 6/29/05 wregglej 152388
    # Places handler at the end of the uri.
    # uris can be either a uri string or a list of uri strings.
    def _patch_uris(self, uris, handler="/XMLRPC"):
        #Handles patching the uris when they're in a list.
        if type(uris) == type([]):
            ret = []
            for i in range(len(uris)):
                t = list(utils.parse_url(uris[i]))
                t[2] = handler
                ret.append(utils.unparse_url(t))
        #Handles patching the uri when it's a string.
        else:
            t = list(utils.parse_url(uris))
            t[2] = handler
            ret = utils.unparse_url(t)
        return ret

    def _set_capabilities(self):
        # list of client capabilities
        capabilities = {
            'configfiles.base64_enc' : {'version' : 1, 'value' : 1},
            'rhncfg.dirs_enabled'    : {'version' : 1, 'value' : 1},
        }
        for name, hashval in capabilities.items():
            cap = "%s(%s)=%s" % (name, hashval['version'], hashval['value'])
            self.server.add_header("X-RHN-Client-Capability", cap)

    def rpc_call(self, method_name, *params):
        method = getattr(self.server, method_name)
        try:
            result = method(*params)
        except xmlrpclib.ProtocolError:
            e = sys.exc_info()[1]
            sys.stderr.write("XML-RPC call error: %s\n" % e)
            sys.exit(1)
        except xmlrpclib.Fault:
            # Re-raise them
            raise
        except Exception:
            e = sys.exc_info()[1]
            sys.stderr.write("XML-RPC error while talking to %s: %s\n" % (
                self.__server_url, e))
            sys.exit(2)

        return result

    def _get_maximum_file_size(self):
        return self.rpc_call('config.max_upload_fsize')

    def _add_content(self, file_contents, params):
        """Add the file contents to the params hash"""

        # check for the rhncfg.content.base64_decode capability and encode the
        # data if the server is capable of descoding it
        if 'rhncfg.content.base64_decode' in self._server_capabilities:
            params['enc64'] = 1
            params['file_contents'] = base64.encodestring(file_contents)
        else:
            params['file_contents'] = file_contents

        return params

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
0707010000002B000081B40000000000000000000000015D403FC800000EC9000000000000000000000000000000000000002100000000mgr-cfg/config_common/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 stat
import sys
import time
import traceback

class Logger:
    debug_level = 1
    logfile = None

    def log_debug(self, debug_level, *args):
        if debug_level <= self.debug_level:
            outstr = "%s %s: %s\n" % (
                time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())),
                self.get_caller(),
                " ".join([str(it) for it in args]))
            sys.stdout.write(outstr)

            if not self.logfile is None:
                self.write_to_logfile(outstr)

    def log_to_file(self, debug_level, *args):
        if debug_level <= self.debug_level:
            outstr = "%s %s: %s\n" % (
                time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())),
                self.get_caller(),
                " ".join([str(it) for it in args]))
            if not self.logfile is None:
                self.write_to_logfile(outstr)


    def write_to_logfile(self, logstr):
        if os.access(self.logfile, os.F_OK|os.R_OK|os.W_OK):
            logname = open(self.logfile, "a")
            logname.write(logstr)
            logname.close()
        else:
            #pkilambi: bug#179367: check permissions before writing.
            #non-root users will not have permissions to create the file

            # Set to root-RW-only if we have to create the file
            mode = stat.S_IRUSR | stat.S_IWUSR  # octal 0o600
            try:
                # Need to use os.open to insure initial permissions
                fd = os.open(self.logfile, os.O_WRONLY | os.O_CREAT, mode)
                logname = os.fdopen(fd, 'w')
                logname.write(logstr)
                logname.close()
            except:
                print("does not have permissions to create file  %s" % (self.logfile))
                sys.exit(1)

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

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

    def get_debug_level(self):
        return Logger.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):
        outstr = " ".join([str(it) for it in args])
        sys.stderr.write(outstr)
        sys.stderr.write("\n")
        if not self.logfile is None:
            self.write_to_logfile(outstr)

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

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

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

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

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

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

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

def die(error_code, *args):
    Logger().log_error(*args)
    sys.exit(error_code)
0707010000002C000081B40000000000000000000000015D403FC80000208E000000000000000000000000000000000000002200000000mgr-cfg/config_common/rhn_main.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

from config_common import local_config
from config_common import rhn_log
from config_common import utils
from config_common import cfg_exceptions

try:
    # python3
    from urllib.parse import urlsplit
    from configparser import InterpolationError
except ImportError: #python2
    from urlparse import urlsplit
    from ConfigParser import InterpolationError

try:
    from socket import gaierror
except:
    from socket import error
    gaierror = error

from up2date_client import config

class BaseMain:
    modes = []
    repository_class_name = "Repository"
    plugins_dir = 'config_common'
    mode_prefix = None
    config_section = None

    def usage(self, exit_code):
        print("Usage: %s MODE [ --config config_file ] [ --server-name name ] [ params ]" % sys.argv[0])
        print("Valid modes are:")
        for mode in self.modes:
            print("\t%s" % mode)
        sys.exit(exit_code)

    def main(self):
        args = []

        show_help = None
        debug_level = 3
        mode = None

        dict_name_opt={'--server-name': None,'--password': None,'--username': None, '--config': None,}
        for index in range(1,len(sys.argv)):
            arg=sys.argv[index]
            param = [x for x in dict_name_opt.items() if x[1] == 0]
            if param:
                if arg.startswith('-') or arg in self.modes:
                  # not perfect, but at least a little bit better
                  print("Option %s requires an argument" % dict_name_opt[param[0][0]])
                  return 1
                dict_name_opt[param[0][0]] = arg
                continue

            if arg in ('--help', '-h'):
                show_help = 1
                continue

            param = [s for s in dict_name_opt.keys() if arg.startswith(s)]
            if param:
                rarg = arg[len(param[0]):]
                if not rarg:
                    dict_name_opt[param[0]] = 0
                    if index == len(sys.argv) - 1:
                       print("Option %s requires an argument" % param[0])
                       return 1
                    continue
                if rarg[0] == '=':
                   if len(rarg) == 1:
                      print("Option %s requires an argument" % param[0])
                      return 1

                   dict_name_opt[param[0]] = rarg[1:]
                   continue
                print("Unknown option %s" % arg)
                return 1

            if mode is None:
                # This should be the mode
                mode = arg
                if mode == '':
                    # Bad
                    self.usage(1)

                if mode[0] == '-':
                    # Mode can't be an option
                    self.usage(1)

                if mode not in self.modes:
                    print("Unknown mode %s" % mode)
                    self.usage(1)

                continue

            args.append(arg)

        server_name = dict_name_opt['--server-name']
        password = dict_name_opt['--password']
        username = dict_name_opt['--username']
        config_file_override = dict_name_opt['--config']

        if config_file_override and not os.path.isfile(config_file_override):
            rhn_log.die(1, "Config file %s does not exist.", config_file_override)

        rhn_log.set_debug_level(debug_level)

        if mode is None:
            # No args were specified
            self.usage(0)

        execname = os.path.basename(sys.argv[0])
        # Class names cannot have dot in them, so strip the extension
        execname = execname.split('.', 1)[0]

        mode_module = mode.replace('-', '_')
        module_name = "%s_%s" % (self.mode_prefix, mode_module)
        full_module_name = "%s.%s" % (self.plugins_dir, module_name)

        try:
            module = __import__(full_module_name)
        except ImportError:
            e = sys.exc_info()[1]
            rhn_log.die(1, "Unable to load plugin for mode '%s': %s" % (mode, e))

        module = getattr(module, module_name)

        if show_help:
            # Display the per-module help
            handler = module.Handler(args, None, mode=mode, exec_name=execname)
            handler.usage()
            return 0

        cfg = config.initUp2dateConfig()
        up2date_cfg = dict(cfg.items())

        if server_name:
            local_config.init(self.config_section, defaults=up2date_cfg, config_file_override=config_file_override, server_url="https://" + server_name)
        else:
            local_config.init(self.config_section, defaults=up2date_cfg, config_file_override=config_file_override)

            try:
                server_name = local_config.get('server_url')
            except InterpolationError:
                e = sys.exc_info()[1]
                if e.option == 'server_url':
                    server_name = config.getServerlURL()
                    up2date_cfg['proto'] = urlsplit(server_name[0])[0]
                    if up2date_cfg['proto'] == '':
                        up2date_cfg['proto'] = 'https'
                        up2date_cfg['server_list'] = [urlsplit(x)[2] for x in server_name]
                    else:
                        up2date_cfg['server_list'] = [urlsplit(x)[1] for x in server_name]
                    server_name = (up2date_cfg['server_list'])[0]
                    local_config.init(self.config_section, defaults=up2date_cfg, config_file_override=config_file_override,  server_name=server_name)

        print("Using server name %s" % server_name)

        # set the debug level through the config
        rhn_log.set_debug_level(int(local_config.get('debug_level') or 0))
        rhn_log.set_logfile(local_config.get('logfile') or "/var/log/rhncfg")

        # Multi-level import - __import__("foo.bar") does not return the bar
        # module, but the foo module with bar loaded
        # XXX Error checking
        repo_class = local_config.get('repository_type')
        if repo_class is None:
            rhn_log.die(1, "repository_type not set (missing configuration file?)")

        repo_module_name = "%s.%s" % (self.plugins_dir, repo_class)
        try:
            repo_module = __import__(repo_module_name)
        except ImportError:
            e = sys.exc_info()[1]
            rhn_log.die(1, "Unable to load repository module:  %s" % e)

        try:
            repo_module = getattr(repo_module, repo_class)
        except AttributeError:
            rhn_log.die(1, "Malformed repository module")

        try:
            repo = getattr(repo_module, self.repository_class_name)()
        except AttributeError:
            rhn_log.die(1, "Missing repository class")
        except InterpolationError:
            e = sys.exc_info()[1]
            if e.option == 'server_url':
                #pkilambi: bug#179367# backtic is replaced with single quote
                rhn_log.die(1, "Missing 'server_url' configuration variable; please refer to the config file")
            raise
        except cfg_exceptions.ConfigurationError:
            e = sys.exc_info()[1]
            rhn_log.die(e)
        except gaierror:
            e = sys.exc_info()[1]
            print("Socket Error: %s" % (e.args[1],))
            sys.exit(1)

        handler = module.Handler(args, repo, mode=mode, exec_name=execname)
        try:
            try:
                handler.authenticate(username, password)
                handler.run()
            except cfg_exceptions.AuthenticationError:
                e = sys.exc_info()[1]
                rhn_log.die(1, "Authentication failed: %s" % e)
            except Exception:
                raise
        finally:
            repo.cleanup()
        return 0
0707010000002D000081B40000000000000000000000015D403FC80000231C000000000000000000000000000000000000002500000000mgr-cfg/config_common/rpc_wrapper.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
from rhn import rpclib
from spacewalk.common.usix import raise_with_tb

try:
    from socket import error, sslerror, herror, gaierror, timeout
except ImportError:
    from socket import error
    sslerror = error
    herror = error
    gaierror = error
    timeout = error

try: # python2
    import xmlrpclib
    import urllib
except ImportError: # python3
    import xmlrpc.client as xmlrpclib
    import urllib.parse as urllib


#This is raised when the failover stuff has gone through every server in the server list
#and the error is still occurring.
class NoMoreServers(Exception):
    pass


#This was supposed to be a wrapper that contained a reference to a rpclib.Server object and delegated calls to it,
#but it turns out that I needed to override _request() to make sure that all
#communication errors are caught and handled here, so I changed it to be a subclass of rpclib.Server.
#The problem that spurred that was when a xmlrpc function was called on an object that was an
#attribute of the server class it wasn't passing through the _request that I had written here,
#it was going directly to rpclib.Server's _request() and missing all of the failover logic that I had added.
#The short version is that I needed to make sure this class was in the inheritance hierarchy.
class Server(rpclib.Server):
    def __init__(self, uri, transport=None, encoding=None, verbose=0,
        proxy=None, username=None, password=None, refreshCallback=None,
        progressCallback=None, server_list=None, rpc_handler=None):
        self.list_of_uris = None    #Contains all of the possible uris.
        self.current_index = 0      #index of the uri that we're currently using.

        #If server_list is None, then no failover systems were listed in the up2date config and
        #we need to use the one that was put together in the rhncfg-* config, which was passed in as
        #uri.
        if server_list is None:
            if type(uri) == type([]):
                self.list_of_uris = uri
            else:
                self.list_of_uris = [uri]
        else:
            #If the server_url passed in is the same as the first element of the server_list, then
            #that means all of the server info came from the up2date config (or is the same as the up2date config)
            #and we should use the server_list.
            #If they don't match then we should use the server_url passed in as uri, because it's a specific setting from
            #the rhncfg-*.conf file.
            if uri == server_list[0]:
                self.list_of_uris = server_list
            else:
                self.list_of_uris = [uri]

        self.rpc_handler = rpc_handler

        #Grabs the initial uri that we're going to use.
        init_uri = self._get_uri()


        #self.rpc_args = {
        #                    'transport'             :       transport,
        #                    'encoding'              :       encoding,
        #                    'verbose'               :       verbose,
        #                    'proxy'                 :       proxy,
        #                    'username'              :       username,
        #                    'password'              :       password,
        #                    'refreshCallback'       :       refreshCallback,
        #                    'progressCallback'      :       progressCallback,

        #                 }
        #Set up the rpclib.Server stuff with the first uri.
        rpclib.Server.__init__(self, init_uri, transport=transport, encoding=encoding, verbose=verbose,\
                                      proxy=proxy, username=username, password=password, refreshCallback=refreshCallback,\
                                      progressCallback=progressCallback)

    #Return the uri that we should be using.
    def _get_uri(self):
        return self.list_of_uris[self.current_index]

    #Returns the list of uris that could be used.
    def get_uri_list(self):
        return self.list_of_uris

    #This is called when we failover. It re-inits the server object to use the new uri. Most of this was cribbed from
    #alikins' wrapper that does a similar thing for up2date.
    def init_server(self, myuri):
        #Borrowed the following from rpcServer.py
        #rpclib.Server.__init__(self, uri, transport=self.rpc_args['transport'], encoding=self.rpc_args['encoding'], verbose=self.rpc_args['verbose'],\
        #                              proxy=self.rpc_args['proxy'], username=self.rpc_args['username'],\
        #                              password=self.rpc_args['password'], refreshCallback=self.rpc_args['refreshCallback'],\
        #                              progressCallback=self.rpc_args['progressCallback'])
        self._uri = myuri
        typ, uri = urllib.splittype(self._uri)
        typ = typ.lower()
        if typ not in ("http", "https"):
            raise InvalidRedirectionError(
                "Redirected to unsupported protocol %s" % typ)

        self._host, self._handler = urllib.splithost(uri)
        self._orig_handler = self._handler
        self._type = typ
        if not self._handler:
            self._handler = self.rpc_handler
        self._allow_redirect = 1
        del self._transport
        self._transport = self.default_transport(typ, self._proxy,
                                             self._username, self._password)
        self.set_progress_callback(self._progressCallback)
        self.set_refresh_callback(self._refreshCallback)
        self.set_buffer_size(self._bufferSize)
        self.setlang(self._lang)

        if self._trusted_cert_files != [] and \
            hasattr(self._transport, "add_trusted_cert"):
            for certfile in self._trusted_cert_files:
                self._transport.add_trusted_cert(certfile)

    #This is the logic for switching to a new server resides.
    def _failover(self):
        #The print statements are from alikins rpcServer.py.
        msg = "An error occurred talking to %s:\n" % self._get_uri()
        msg = msg + "%s\n%s\n" % (sys.exc_info()[0], sys.exc_info()[1])

        #Increments the index to point to the next server in self.list_of_uris
        self.current_index = self.current_index + 1

        #Make sure we don't try to go past the end of the list.
        if self.current_index > (len(self.list_of_uris) - 1):
            raise NoMoreServers()
        else:
            failover_uri = self._get_uri()  #Grab the uri of the new server to use.
        msg = msg + "Trying the next serverURL: %s\n" % failover_uri

        print(msg)

        #Set up rpclib.Server to use the new uri.
        self.init_server(failover_uri)

    #This is where the magic happens. function is a function reference, arglist is the list of arguements
    #that get passed to the function and kwargs is the list of named arguments that get passed into the function.
    #I used apply() here so it will work with Python 1.5, which doesn't have the extended call syntax.
    def _call_function(self, function, arglist, kwargs={}):
        succeed = 0
        while succeed == 0:
            try:
                ret = function(*arglist, **kwargs)
            except rpclib.InvalidRedirectionError:
                raise
            except xmlrpclib.Fault:
                e = sys.exc_info()[1]
                save_traceback = sys.exc_info()[2]
                try:
                    self._failover()
                except NoMoreServers:
                    f = sys.exc_info()[1]
                    raise_with_tb(e, save_traceback)  #Don't raise the NoMoreServers error, raise the error that triggered the failover.
                continue
            except (error, sslerror, herror, gaierror, timeout):
                e = sys.exc_info()[1]
                save_traceback = sys.exc_info()[2]
                try:
                    self._failover()
                except NoMoreServers:
                    raise_with_tb(e, save_traceback)
                continue
            succeed = 1 #If we get here then the function call eventually succeeded and we don't need to try again.
        return ret

    def _request(self, methodname, params):
        return self._call_function(rpclib.Server._request, (self, methodname, params))

    def __getattr__(self, name):
        return rpclib.xmlrpclib._Method(self._request, name)
0707010000002E000081B40000000000000000000000015D403FC80000436C000000000000000000000000000000000000002600000000mgr-cfg/config_common/transactions.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 shutil
import pwd
import grp
import sys
import errno
import shutil

from config_common import file_utils, utils, cfg_exceptions
from config_common.rhn_log import log_debug
from spacewalk.common.usix import raise_with_tb

class TargetNotFile(Exception): pass
class DuplicateDeployment(Exception): pass
class FailedRollback(Exception): pass

try:
    from selinux import lsetfilecon
except:
    def lsetfilecon(temp_file_path, sectx):
        # Do nothing RHEL 4
        return 0

BACKUP_PREFIX = '/var/lib/rhncfg/backups'
BACKUP_EXTENSION = '.rhn-cfg-backup'

class DeployTransaction:

    def __init__(self, transaction_root=None, auto_rollback=0):
        # rollback transaction immediately upon failure?
        self.auto_rollback = auto_rollback
        # prepend all given paths
        self.transaction_root = transaction_root

        self.files = []
        self.dirs  = []
        self.symlinks = []
        self.new_dirs = []
        self.backup_by_path = {}
        self.newtemp_by_path = {}
        self.changed_dir_info = {}

        self.deployment_cb = None


    def _generate_backup_path(self, path):
        return "%s%s%s" % (BACKUP_PREFIX, path, BACKUP_EXTENSION)


    def _rename_to_backup(self, path):
        """renames a file to it's new backup name"""
        # ensure we haven't attempted to back this file up before
        # (protect against odd logic coming from the server)
        if path in self.backup_by_path:
            raise DuplicateDeployment("Error: attempted to backup %s twice" % path)


        new_path = None

        if os.path.exists(path):
            # race
            if os.path.isfile(path) or os.path.islink(path):
                new_path = self._generate_backup_path(path)
                log_debug(6, "renaming %s to backup %s ..." % (path, new_path))
                # os.renames will fail if the path and the new_path are on different partitions
                # need to make sure to handle it if we catch a 'OSError: [Errno 18] Invalid cross-device link'
                try:
                    log_debug(9, "trying to use os.renames")
                    oumask = os.umask(int('022', 8))
                    os.renames(path, new_path)
                    os.umask(oumask)
                except OSError:
                    e = sys.exc_info()[1]
                    if e.errno == 18:
                        log_debug(9, "os.renames failed, using shutil functions")
                        path_dir, path_file = os.path.split(path)
                        new_path_dir, new_path_file = os.path.split(new_path)
                        if os.path.isdir(new_path_dir):
                            if os.path.islink(path):
                                log_debug(9, "copying symlink %s to %s"% (path,new_path_dir))
                                linkto = os.readlink(path)
                                if os.path.lexists(new_path):
                                    log_debug(9, "backup %s exists, removing it"% (new_path))
                                    os.unlink(new_path)
                                os.symlink(linkto,new_path)
                            else:
                                log_debug(9, "backup directory %s exists, copying %s to it" % (new_path_dir, new_path_file))
                                if os.path.lexists(new_path):
                                    log_debug(9, "backup %s exists, removing it"% (new_path))
                                    os.unlink(new_path)
                                shutil.copy(path, new_path)
                        else:
                            log_debug(9, "backup directory does not exist, creating the tree now")
                            shutil.copytree(path_dir, new_path_dir, symlinks=0)
                            shutil.copy(path, new_path)
                    else:
                        raise
                self.backup_by_path[path] = new_path
                log_debug(9, "backed up to %s" % new_path)
            else:
                raise TargetNotFile("Error: %s is not a valid file, cannot create backup copy" % path)
        return new_path


    def deploy_callback(self, cb):
        self.deployment_cb = cb

    def _chown_chmod_chcon(self, temp_file_path, dest_path, file_info, strict_ownership=1):
        if file_info['filetype'] != 'symlink':
            uid = file_info.get('uid')
            if uid is None:
                if 'username' in file_info:
                    # determine uid

                    try:
                        user_record = pwd.getpwnam(file_info['username'])
                        uid = user_record[2]
                    except Exception:
                        e = sys.exc_info()[1]
                        #Check if username is an int
                        try:
                            uid = int(file_info['username'])
                        except ValueError:
                            raise_with_tb(cfg_exceptions.UserNotFound(file_info['username']), sys.exc_info()[2])
                else:
                    #default to root (3.2 sats)
                    uid = 0

            gid = file_info.get('gid')
            if gid is None:
                if 'groupname' in file_info:
                    # determine gid
                    try:
                        group_record = grp.getgrnam(file_info['groupname'])
                        gid = group_record[2]
                    except Exception:
                        e = sys.exc_info()[1]
                        try:
                            gid = int(file_info['groupname'])
                        except ValueError:
                            raise_with_tb(cfg_exceptions.GroupNotFound(file_info['groupname']), sys.exc_info()[2])

                else:
                    #default to root (3.2 sats)
                    gid = 0

        try:
            if file_info['filetype'] != 'symlink':
                os.chown(temp_file_path, uid, gid)

                mode = '600'
                if 'filemode' in file_info:
                    if file_info['filemode'] is "":
                        mode='000'
                    else:
                        mode = file_info['filemode']

                mode = int(str(mode), 8)
                os.chmod(temp_file_path, mode)

            if 'selinux_ctx' in file_info:
                sectx = file_info.get('selinux_ctx')
                if sectx is not None and sectx is not "":
                    log_debug(1, "selinux context: " + sectx);
                    try:
                        if lsetfilecon(temp_file_path, sectx) < 0:
                            raise Exception("failed to set selinux context on %s" % dest_path)
                    except OSError:
                        e = sys.exc_info()[1]
                        raise_with_tb(Exception("failed to set selinux context on %s" % dest_path, e), sys.exc_info()[2])

        except OSError:
            e = sys.exc_info()[1]
            if e.errno == errno.EPERM and not strict_ownership:
                sys.stderr.write("cannonical file ownership and permissions lost on %s\n" % dest_path)
            else:
                raise



    def _normalize_path_to_root(self, path):
        if self.transaction_root:
            path = utils.normalize_path(self.transaction_root + os.sep + path)
        return path

    def add_preprocessed(self, dest_path, processed_file_path, file_info, dirs_created, strict_ownership=1):
        """preprocess the file if needed, and add the entry to the correct list"""
        dest_path = self._normalize_path_to_root(dest_path)
        log_debug(3, "preprocessing entry")

        # If we get any dirs that were created by mkdir_p, add them here
        if dirs_created:
            self.new_dirs.extend(dirs_created)

        # If the file is a directory, don't do all the file related work
        # Older servers will not return directories; if filetype is missing,
        # assume file
        if file_info.get('filetype') == 'directory':
            self.dirs.append(file_info)
        else:
            if "dest_path" in self.newtemp_by_path:
                raise DuplicateDeployment("Error:  %s already added to transaction" % dest_path)
            self.newtemp_by_path[dest_path] = processed_file_path
            self._chown_chmod_chcon(processed_file_path, dest_path, file_info, strict_ownership=strict_ownership)

    def add(self, file_info):
        """add a file to the deploy transaction"""
        for k in file_utils.FileProcessor.file_struct_fields:
            if k not in file_info:
                raise Exception("needed key %s mising from file structure" % k)

        file_info['path'] = self._normalize_path_to_root(file_info['path'])

        # Older servers will not return directories; if filetype is missing,
        # assume file
        if file_info.get('filetype') == 'directory':
            self.dirs.append(file_info)
        elif file_info.get('filetype') == 'symlink':
            self.files.append(file_info)
        else:
            self.files.append(file_info)


    def rollback(self):
        """revert the transaction"""
        log_debug(3, "rolling back")

        # restore old file from backup asap
        for path in self.backup_by_path.keys():
            log_debug(6, "restoring %s from %s ..." % (path, self.backup_by_path[path]))
            # os.rename will fail if the backup file and the old file are on different partitions
            # need to make sure to handle it if we catch a 'OSError: [Errno 18] Invalid cross-device link'
            try:
                os.rename(self.backup_by_path[path], path)
            except OSError:
                e = sys.exc_info()[1]
                if e.errno == 18:
                    log_debug(9, "os.rename failed, using shutil.copy")
                    shutil.copy(self.backup_by_path[path], path)
                else:
                    raise
            log_debug(9, "%s restored" % path)

        # remove the temp files that we created
        for tmp_file_path in self.newtemp_by_path.values():
            log_debug(6, "removing tmp file %s ..." % tmp_file_path)
            os.unlink(tmp_file_path)
            log_debug(9, "tmp file removed")

        #revert the owner/perms of any directories that we changed
        for d, val in self.changed_dir_info.items():
            log_debug(6, "reverting owner and perms of %s" % d)
            self._chown_chmod_chcon(d, d, val)
            log_debug(9, "directory reverted")

        #remove any directories created by either mkdir_p or in the deploy
        self.new_dirs.reverse()
        for i in range(len(self.new_dirs)):
            remove_dir = self.new_dirs[i]
            log_debug(6, "removing directory %s that was created during transaction ..." % remove_dir)
            if os.path.islink(remove_dir) == True:
                os.remove(remove_dir)
            else:
                os.rmdir(remove_dir)
            log_debug(9, "directory removed")

        log_debug(3, "rollback successful")

    def deploy(self):
        """attempt deployment; will rollback if auto_rollback is set"""
        fp = file_utils.FileProcessor()

        log_debug(3, "deploying transaction")

        for dep_file in self.files:
            if dep_file['filetype'] == 'symlink':
                self.symlinks.append(dep_file)

        # 0. handle any dirs we need to create first
        #    a) if the dir exists, then just change the mode and owners,
        #       else create it and then make sure the mode and owners are correct.
        #    b) if there are files, then continue
        # 1. write new version (tmp)
        #    a)  if anything breaks, remove all tmp versions and error out
        # 2. rename old version to backup
        #    a)  if anything breaks, rename all backed up files to original name,
        #        then do 1-a.
        # 3. rename tmp to target name
        #    a)  if anything breaks, remove all deployed files, then do 2-a.
        #
        # (yes, this leaves the backup version on disk...)

        try:

            # 0.
            if self.dirs:
                for directory in self.dirs:
                    dirname = self._normalize_path_to_root(directory['path'])
                    dirmode = directory['filemode']
                    if os.path.isfile(dirname):
                        raise cfg_exceptions.DirectoryEntryIsFile(dirname)
                    if os.path.isdir(dirname):
                        s = os.stat(dirname)
                        entry = { 'filemode': "%o" % (s[0] & int('07777', 8)),
                                  'uid': s[4],
                                  'gid': s[5],
                                  'filetype': 'directory',
                                }
                        self.changed_dir_info[dirname] = entry
                        log_debug(3, "directory found, chowning and chmoding to %s as needed: %s" % (dirmode, dirname))
                        self._chown_chmod_chcon(dirname, dirname, directory)
                    else:
                        log_debug(3, "directory not found, creating: %s" % dirname)
                        dirs_created = utils.mkdir_p(dirname, None, self.symlinks, self.files)
                        self.new_dirs.extend(dirs_created)
                        self._chown_chmod_chcon(dirname, dirname, directory)
                    if self.deployment_cb:
                        self.deployment_cb(dirname)

            log_debug(6, "changed_dir_info: %s" % self.changed_dir_info)
            log_debug(4, "new_dirs: ", self.new_dirs)


            if not self.newtemp_by_path and not self.files:
                log_debug(4, "directory creation complete, no files found to create")
                return
            else:
                log_debug(4, "done with directory creation, moving on to files")

            # 1.
            for dep_file in self.files:
                path = dep_file['path']

                log_debug(6, "writing new version of %s to tmp file ..." % path)
                # make any directories needed...
                #
                # TODO:  it'd be nice if this had a hook for letting me know
                # which ones are created... then i could clean created
                # dirs on rollback
                (directory, filename) = os.path.split(path)
                if os.path.isdir(path) and not os.path.islink(path):
                    raise cfg_exceptions.FileEntryIsDirectory(path)
                if not os.path.exists(directory):# and os.path.isdir(directory):
                    log_debug(7, "creating directories for %s ..." % directory)
                    dirs_created = utils.mkdir_p(directory, None, self.symlinks, self.files)
                    self.new_dirs.extend(dirs_created)
                    log_debug(7, "directories created and added to list for rollback")

                # write the new contents to a tmp file, and store the path of the
                # new tmp file by it's eventual target path
                self.newtemp_by_path[path], temp_new_dirs = fp.process(dep_file, os.path.sep)
                self.new_dirs.extend(temp_new_dirs or [])

                # properly chown and chmod it
                self._chown_chmod_chcon(self.newtemp_by_path[path], path, dep_file)
                log_debug(9, "tempfile written:  %s" % self.newtemp_by_path[path])


            #paths = map(lambda x: x['path'], self.files)
            paths = list(self.newtemp_by_path.keys())

            # 2.
            for path in paths:
                if os.path.isdir(path) and not os.path.islink(path):
                    raise cfg_exceptions.FileEntryIsDirectory(path)
                else:
                    self._rename_to_backup(path)
                    if path in self.backup_by_path:
                        log_debug(9, "backup file %s written" % self.backup_by_path[path])

            # 3.
            paths.sort(key = lambda s: s.count(os.path.sep))
            for path in paths:
                if self.deployment_cb:
                    self.deployment_cb(path)
                log_debug(6, "deploying %s ..." % path)
                os.rename(self.newtemp_by_path[path], path)
                # race
                del self.newtemp_by_path[path]
                log_debug(9, "new version of %s deployed" % path)

            log_debug(3, "deploy transaction successful")

        except Exception:
            #log_debug(1, traceback.format_exception_only(SyntaxError, e))
            #traceback.print_exc()
            if self.auto_rollback:
                self.rollback()
            raise
0707010000002F000081B40000000000000000000000015D403FC8000016FB000000000000000000000000000000000000001F00000000mgr-cfg/config_common/utils.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
# guaranteed to exist even on RHEL 5 because we now require python-hashlib
import hashlib
import re
import shutil
import pwd
import sys

try: # python2
    import urlparse
except ImportError: # python3
    import urllib.parse as urlparse

import inspect
from config_common.rhn_log import log_debug
from rhn.i18n import bstr

hashlib_has_usedforsecurity = False

if not hasattr(inspect, 'getfullargspec'):
    inspect.getfullargspec = inspect.getargspec
if 'usedforsecurity' in inspect.getfullargspec(hashlib.new)[0]:
    hashlib_has_usedforsecurity = True

_normpath_re = re.compile("^(%s)+" % os.sep)
def normalize_path(path):
    """
    os.path.normpath does not remove path separator duplicates at the
    beginning of the path
    """
    return _normpath_re.sub(os.sep, os.path.normpath(path))

def join_path(*args):
    return normalize_path(os.sep.join(args))

def path_full_split(path):
    """
    Given a path, it fully splits it into constituent path
    components (as opposed to os.path.split which splits it into
    trailing component and preceeding path
    """

    path = normalize_path(path)
    splitpath = []
    while 1:
        path, current = os.path.split(path)
        if current == '':
            if path:
                # Absolute path
                splitpath.append(os.sep)
            break
        splitpath.append(current)

    splitpath.reverse()
    return splitpath

def copyfile_p(src, dst):
    """
    Simple util function, copies src path to dst path, making
    directories as necessary. File permissions are not preserved.
    """

    directory = os.path.split(dst)[0]
    try:
        os.makedirs(directory)
    except OSError:
        e = sys.exc_info()[1]
        if e.errno != 17:
            # not File exists
            raise

    if os.path.isdir(src):
        if not os.path.exists(dst):
            os.mkdir(dst)
    elif os.path.islink(src):
        exists = hasattr(os.path, "lexists") and os.path.lexists or os.path.exists
        if exists(dst):
            os.remove(dst)
        os.symlink(os.readlink(src), dst)
    else:
        shutil.copyfile(src, dst)

def mkdir_p(path, mode=None, symlinks=None, allfiles=None):
    """
    Similar to 'mkdir -p' -- makes all directories necessary to ensure
    the 'path' is a directory, and return the list of directories that were
    made as a result
    """
    if mode is None:
        mode = int('0700', 8)
    dirs_created = []

    components = path_full_split(path)
    for i in range(1,len(components)):
        d = os.path.join(*components[:i+1])
        if symlinks:
            for symlink in symlinks:
                if symlink['path'] == d:
                    # create symlink and remove it from symlink list
                    os.symlink(symlink['symlink'], symlink['path'])
                    symlinks.remove(symlink)
                    allfiles.remove(symlink)
                    dirs_created.append(symlink)
                    continue
        log_debug(8, "testing", d)
        try:
            os.mkdir(d, mode)
            dirs_created.append(d)
            log_debug(8, "created", d)
        except OSError:
            e = sys.exc_info()[1]
            if e.errno != 17:
                raise
            else:
                log_debug(8, "already exists", d)

    log_debug(6, "dirs_created:", dirs_created)

    return dirs_created

def rmdir_p(path, stoppath):
    """
    if rmdir had a -p option, this would be it.  remove dir and up
    until empty dir is hit, or stoppath is reached

    path and stoppath have to be absolute paths
    """

    # First normalize both paths
    stoppath = normalize_path(os.sep + stoppath)
    path = normalize_path(os.sep + path)

    # stoppath has to be a prefix of path
    if path[:len(stoppath)] != stoppath:
        raise OSError("Could not remove %s: %s is not a prefix" % (
            path, stoppath))

    while 1:
        if stoppath == path:
            # We're done
            break

        # Try to remove the directory
        try:
            os.rmdir(path)
        except OSError:
            # Either the directory is full, or we don't have permissions; stop
            break

        path, current = os.path.split(path)
        if current == '':
            # We're done - reached the root
            break

#returns slashstring with any trailing slash removed
def rm_trailing_slash(slashstring):
    if slashstring[-1] == "/":
        slashstring = slashstring[0:-1]
    return slashstring

def getContentChecksum(checksum_type, contents):
    if hashlib_has_usedforsecurity:
        engine = hashlib.new(checksum_type, usedforsecurity=False)
    else:
        engine = hashlib.new(checksum_type)
    engine.update(bstr(contents))
    return engine.hexdigest()

def sha256_file(filename):
    engine = hashlib.new('sha256')

    fh = open(filename, "rb")
    while 1:
        buf = fh.read(4096)
        if not buf:
            break

        engine.update(bstr(buf))

    return engine.hexdigest()

def parse_url(server_url, scheme="https"):
    return urlparse.urlparse(server_url, scheme=scheme)

def unparse_url(url_tuple):
    return urlparse.urlunparse(url_tuple)

def get_home_dir():
    uid = os.getuid()
    ent = pwd.getpwuid(uid)
    return ent[5]
07070100000030000041FD0000000000000000000000015D403FC800000000000000000000000000000000000000000000001A00000000mgr-cfg/config_management07070100000031000081B40000000000000000000000015D403FC8000004AE000000000000000000000000000000000000002300000000mgr-cfg/config_management/Makefile# Makefile for spacewalk backend
#

# what is the backend top dir
TOP     = ..

# Specific stuff
SUBDIR		= config_management
EXEC_FILES	= rhncfg-manager
FILES           = $(EXEC_FILES) __init__ \
                  rhncfg_add \
                  rhncfg_create_channel \
                  rhncfg_diff \
                  rhncfg_diff_revisions \
                  rhncfg_download_channel \
                  rhncfg_get \
                  rhncfg_list \
                  rhncfg_list_channels \
                  rhncfg_remove \
                  rhncfg_remove_channel \
                  rhncfg_revisions \
                  rhncfg_update \
                  rhncfg_upload_channel \
                  rpc_repository
CONFIG_FILES    = rhncfg-manager.conf

# man pages
DOCBOOK = $(wildcard /usr/bin/docbook2man)
SGMLS	= $(wildcard *.sgml)
MANS	= $(patsubst %.sgml,%.8,$(SGMLS))
MANDIR	?= /usr/man

EXTRA_DIRS	= $(BINDIR) $(CONFDIR) $(MANDIR)/man8

include $(TOP)/Makefile.defs

ifneq ($(DOCBOOK),)
# install man pages
all	:: $(MANS)
install :: $(MANS) $(PREFIX)/$(MANDIR)
	$(INSTALL_DATA) $(MANS) $(PREFIX)/$(MANDIR)/man8
clean   ::
	@rm -fv $(MANS) manpage.*
endif

install	:: install-bin install-conf
07070100000032000081B40000000000000000000000015D403FC800000266000000000000000000000000000000000000002600000000mgr-cfg/config_management/__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.
#

07070100000033000081B40000000000000000000000015D403FC800000518000000000000000000000000000000000000002E00000000mgr-cfg/config_management/rhncfg-manager.conf[rhncfg-manager]

# 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 = /CONFIG-MANAGEMENT-TOOL


# don't change this, period.
repository_type = rpc_repository


# 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/CONFIG-MANAGEMENT-TOOL
# 
# You may also edit this value in a ~/.rhncfgrc file override file,
# which will allow you to run the rhncfg-manager program as non-root
server_url = https://%(server_name)s%(server_handler)s


# for your convenience, you may put your RHN username in here
# to avoid being asked for it every time you run rhncfg-manager

# username = your_rhn_username_here


# the following fields are inherited from up2date's configuration,
# but are overridable in this file, or in a ~/.rhncfgrc file

# enableProxy = 1
# enableProxyAuth = 1
# httpProxy = some.proxy.example.com:3030
# proxyUser = proxy_user_name
# proxyPassword = proxy_password
# sslCACert = /usr/share/rhn/RHNS-CA-CERT

07070100000034000081B40000000000000000000000015D403FC800000502000000000000000000000000000000000000002C00000000mgr-cfg/config_management/rhncfg-manager.py#!/usr/bin/python
#
# 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

from config_common.rhn_main import BaseMain

class Main(BaseMain):
    modes = [
        'add',
        'create-channel',
        'diff',
        'diff-revisions',
        'download-channel',
        'get',
        'list',
        'list-channels',
        'remove',
        'remove-channel',
        'revisions',
        'update',
        'upload-channel',
    ]
    plugins_dir = 'config_management'
    config_section = 'rhncfg-manager'
    mode_prefix = 'rhncfg'

if __name__ == '__main__':
    try:
        sys.exit(Main().main() or 0)
    except KeyboardInterrupt:
        sys.stderr.write("user interrupted\n")
        sys.exit(0)

07070100000035000081B40000000000000000000000015D403FC8000033D1000000000000000000000000000000000000002E00000000mgr-cfg/config_management/rhncfg-manager.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY RHN "Spacewalk" >
<!ENTITY HOSTED " or Hosted" >
<!ENTITY RHNCFGM "rhncfg-manager" >

]>
<refentry>

<RefMeta>
<RefEntryTitle>&RHNCFGM;</RefEntryTitle><manvolnum>8</manvolnum>
<RefMiscInfo>Version 4.0</RefMiscInfo>
</RefMeta>

<RefNameDiv>
<RefName><command>&RHNCFGM;</command></RefName>
<RefPurpose>
  a tool for managing &RHN; central repository of configuration files
  and channels.
</RefPurpose>
</RefNameDiv>

<RefSynopsisDiv>
<Synopsis>
    <cmdsynopsis>
        <command>&RHNCFGM;</command>
        <arg>MODE</arg>
        <arg>--config <replaceable>config_file</replaceable></arg>
        <arg>--server-name <replaceable>name</replaceable></arg>
        <arg>--username <replaceable>user</replaceable></arg>
        <arg>--password <replaceable>pass</replaceable></arg>
        <arg>params</arg>
    </cmdsynopsis>
</Synopsis>
</RefSynopsisDiv>

<RefSect1><Title>Description</Title>
<para>
    The &RHN; Configuration Manager (<emphasis>&RHNCFGM;</emphasis>) is used to
    manage &RHN; configuration files and channels.
</para>
<para>
    This tool offers a command line alternative to the configuration management
    features found within the &RHN; web user interface&HOSTED;, as well
    as the ability to script some or all of the related maintenance.
</para>
<para>
    This tool is intended for use by the Config Administrator and therefore
    requires an &RHN; username and password that has the appropriate permission
    set. (The username and password may be specified in
    <filename>/etc/sysconfig/rhn/rhncfg-manager.conf</filename> or in
    the [rhncfg-manager] section of <filename>~/.rhncfgrc</filename>
    or the config file specified
    by <term>--config=<replaceable>config_file</replaceable></term>).
</para>
<para>
    Note that when run as root, &RHNCFGM; attempts to pull in needed
    configuration values from the Red Hat Update Agent (up2date). When run as
    something other than root, configuration changes may be needed within the
    <filename>~/.rhncfgrc</filename> file, or the config file
    specified
    by <term>--config=<replaceable>config_file</replaceable></term>. The
    session file is cached in
    <filename>~/.rhncfg-manager-session</filename> to prevent logging in for
    every command.
</para>
<para>
    The <filename>~/.rhncfgrc</filename> file overrides the values set in
    <filename>/etc/sysconfig/rhn/rhncfg-manager.conf</filename> which, by
    default, uses the values set in
    <filename>/etc/sysconfig/rhn/up2date</filename>. The <filename>~/.rhncfgrc</filename>
    file may itself be overridden by the command line
    option <term>--config=<replaceable>config_file</replaceable></term>
    Please review
    <filename>rhncfg-manager.conf</filename> for further details.
</para>
<para>
    The &RHN; Configuration Manager offers these primary modes:
    <emphasis>add</emphasis>, <emphasis>create-channel</emphasis>,
    <emphasis>diff</emphasis>, <emphasis>diff-revisions</emphasis>,
    <emphasis>download-channel</emphasis>, <emphasis>get</emphasis>,
    <emphasis>list</emphasis>, <emphasis>list-channels</emphasis>,
    <emphasis>remove</emphasis>, <emphasis>remove-channel</emphasis>,
    <emphasis>revisions</emphasis>, <emphasis>update</emphasis>,
    <emphasis>upload-channel</emphasis>.
</para>
<para>
    Context sensitive help is available for each "mode", e.g.,
    &RHNCFGM; diff-revisions --help
</para>
<para>
  <simplelist>
    <member>This tool utilizes a "cvs style" command syntax: command mode options</member>
    <member>For example: &RHNCFGM; diff-revisions --help</member>
  </simplelist>
</para>
</RefSect1>

<RefSect1><Title>modes</Title>
<variablelist>
    <varlistentry>
        <term>add [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
        <listitem>
            <para>
              Add a file to a config channel.
            </para>
            <para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- upload file(s) in this config channel</para>
            <para>-d<replaceable>DEST_FILE</replaceable>, --dest-file=<replaceable>DEST_FILE</replaceable> -- upload the file as this path</para>
            <para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
            <para>--delim-start=<replaceable>DELIM_START</replaceable> -- start delimiter for variable interpolation</para>
            <para>--delim-end=<replaceable>DELIM_END</replaceable> -- end delimiter for variable interpolation</para>
            <para>--selinux-context=<replaceable>SELINUX_CONTEXT</replaceable> -- overwrite the SELinux context label to this string</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>create-channel <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL ...</replaceable> ]</term>
        <listitem>
            <para>
              Create a config channel for your organization.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>diff [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
        <listitem>
            <para>
              Differentiating between latest configuration files in a channel
              and whats on disk.
            </para>
            <para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- get file(s) from this config channel</para>
            <para>-r<replaceable>REVISION</replaceable>, --revision=<replaceable>REVISION</replaceable> -- use this revision</para>
            <para>-d<replaceable>DEST_FILE</replaceable>, --dest-file=<replaceable>DEST_FILE</replaceable> -- upload the file as this path</para>
            <para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>diff-revisions [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable></term>
        <listitem>
            <para>
              Compare different versions of a file across channels and
              revisions. See documentation for further details.
            </para>
            <para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- use this config channel</para>
            <para>-r<replaceable>REVISION</replaceable>, --revision=<replaceable>REVISION</replaceable> -- use this revision</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>download-channel (-t|--topdir=)<replaceable>TOP_LEVEL_DIRECTORY</replaceable> [ <replaceable>OPTIONS</replaceable> ] <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL</replaceable> ... ]</term>
        <listitem>
            <para>
              Download all the configuration files in a channel to disk.
            </para>
            <para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- directory all the file paths are relative to</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>get [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
        <listitem>
            <para>
              Get the contents of a particular configuration file. Outputs
              to stdout.
            </para>
            <para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- get file(s) from this config channel</para>
            <para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
            <para>-r<replaceable>REVISION</replaceable>, --revision=<replaceable>REVISION</replaceable> -- get this file revision</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>list <replaceable>CHANNEL_LABEL</replaceable></term>
        <listitem>
            <para>
              List all configuration files in a channel.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>list-channels</term>
        <listitem>
            <para>
              List all configuration channels.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>remove [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
        <listitem>
            <para>
              Remove a configuration file from a channel. May be prompted for
              a username and password.
            </para>
            <para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- remove file(s) from this config channel</para>
            <para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>remove-channel <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL</replaceable> ... ]</term>
        <listitem>
            <para>
              Remove a configuration channel. May be prompted for
              a username and password.
            </para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>revisions [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
        <listitem>
            <para>
              Determine the number of configuration file revisions.
            </para>
            <para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- use this config channel</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>update [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
        <listitem>
            <para>
              Create a new revision of a file in a channel (or add the first
              revisions to that channel if none existed before for the given
              path).
            </para>
            <para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- upload file(s) in this config channel</para>
            <para>-d<replaceable>DEST_FILE</replaceable>, --dest-file=<replaceable>DEST_FILE</replaceable> -- upload the file as this path</para>
            <para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
            <para>--delim-start=<replaceable>DELIM_START</replaceable> -- start delimiter for variable interpolation</para>
            <para>--delim-end=<replaceable>DELIM_END</replaceable> -- end delimiter for variable interpolation</para>
            <para>--selinux-context=<replaceable>SELINUX_CONTEXT</replaceable> -- overwrite the SELinux context label to this string</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>upload-channel [ <replaceable>OPTIONS</replaceable> ] <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL</replaceable> ... ]</term>
        <listitem>
            <para>
              Upload multiple configuration files (a tree) to a channel from
              disk.
            </para>
            <para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- directory all file paths are relative to</para>
        </listitem>
    </varlistentry>
</variablelist>
</RefSect1>
<RefSect1><Title>others</Title>
<variablelist>
    <varlistentry>
        <term>--server-name</term>
        <listitem>
            <para>&RHN; server name.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
    <term>--username</term>
        <listitem>
            <para>&RHN; user name.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>--password</term>
        <listitem>
            <para>&RHN; password for the specified user.</para>
        </listitem>
    </varlistentry>
    <varlistentry>
        <term>-h, --help</term>
        <listitem>
            <para>Display the help screen with a list of options.</para>
            <para>Use context sensitive help as well with the diffent "modes".
                  E.g., &RHNCFGM; upload-channel --help</para>
        </listitem>
    </varlistentry>
</variablelist>
</RefSect1>

<RefSect1><Title>See Also</Title>
<simplelist>
    <member>rhncfg-client (8)</member>
</simplelist>
</RefSect1>

<RefSect1><Title>Authors</Title>
<simplelist>
    <member>Mihai Ibanescu <email>misa@redhat.com</email></member>
    <member>Bret McMillan <email>bretm@redhat.com</email></member>
    <member>Todd Warner <email>taw@redhat.com</email> (man page only)</member>
    <member>Laurence Rochfort <email>laurence.rochfort@oracle.com</email></member>
</simplelist>
</RefSect1>
</RefEntry>
07070100000036000081B40000000000000000000000015D403FC80000050F000000000000000000000000000000000000002600000000mgr-cfg/config_management/rhncfg.conf[rhncfg]

# 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 = /CONFIG-MANAGEMENT-TOOL


# don't change this, period.
repository_type = rpc_repository


# 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/CONFIG-MANAGEMENT-TOOL
# 
# You may also edit this value in a ~/.rhncfgrc file override file,
# which will allow you to run the rhncfg-manager program as non-root
server_url = https://%(server_name)s%(server_handler)s


# for your convenience, you may put your RHN username in here
# to avoid being asked for it every time you run rhncfg-manager

# username = your_rhn_username_here


# the following fields are inherited from up2date's configuration,
# but are overridable in this file, or in a ~/.rhncfgrc file

# enableProxy = 1
# enableProxyAuth = 1
# httpProxy = some.proxy.example.com:3030
# proxyUser = proxy_user_name
# proxyPassword = proxy_password
# sslCACert = /usr/share/rhn/RHNS-CA-CERT
07070100000037000081B40000000000000000000000015D403FC8000015D8000000000000000000000000000000000000002800000000mgr-cfg/config_management/rhncfg_add.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
from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, die, log_error

class Handler(handler_base.HandlerBase):
    _usage_options = "[options] file [ file ... ]"

    is_first_revision=1

    _options_table = handler_base.HandlerBase._options_table + [
        handler_base.HandlerBase._option_class(
            '-c', '--channel',      action="store",
             help="Upload files in this config channel",
         ),
        handler_base.HandlerBase._option_class(
            '-d', '--dest-file',    action="store",
             help="Upload the file as this path",
         ),
        handler_base.HandlerBase._option_class(
            '-t', '--topdir',       action="store",
             help="Make all files relative to this string",
         ),
        handler_base.HandlerBase._option_class(
            '--delim-start',        action="store",
             help="Start delimiter for variable interpolation",
         ),
        handler_base.HandlerBase._option_class(
            '--delim-end',          action="store",
             help="End delimiter for variable interpolation",
         ),
        handler_base.HandlerBase._option_class(
            '-i', '--ignore-missing',       action="store_true",
             help="Ignore missing local files",
         ),
        handler_base.HandlerBase._option_class(
            '--selinux-context',       action="store",
             help="Overwrite the SELinux context",
         ),
    ]

    def run(self):
        log_debug(2)

        if self.options.dest_file and self.options.topdir:
            die(6, "Error: conflicting options --dest-file and --topdir")

        if len(self.args) == 0:
            die(0, "No files supplied (use --help for help)")

        channel = self.options.channel

        if not channel:
            die(6, "Config channel not specified")

        r = self.repository
        if not r.config_channel_exists(channel):
            die(6, "Error: config channel %s does not exist" % channel)

        files = [utils.normalize_path(x) for x in self.args]
        files_to_push = []
        if self.options.dest_file:
            if len(files) != 1:
                die(7, "--dest-file accepts a single file")
            if not (self.options.dest_file[0] == os.sep):
                die(7, "--dest-file argument must begin with " + os.sep)
            files_to_push.append((files[0], self.options.dest_file))
        elif self.options.topdir:
            if not os.path.isdir(self.options.topdir):
                die(8, "--topdir specified, but `%s' not a directory" %
                    self.options.topdir)

            #5/11/05 wregglej - 141790 remove the trailing slash from topdir
            self.options.topdir = utils.rm_trailing_slash(self.options.topdir)

            for f in files:
                if not f.startswith(self.options.topdir):
                    die(8, "--topdir %s specified, but file `%s' doesn't comply"
                        % (self.options.topdir, f))
                files_to_push.append((f, f[len(self.options.topdir):]))
        else:
            for f in files:
                #if a file is given w/o a full path, then use the abspath of the
                #file as name of the file to be uploaded into the channel
                if not (f[0] == os.sep):
                    files_to_push.append((f, os.path.abspath(f)))
                else:
                    files_to_push.append((f, f))

        for (local_file, remote_file) in files_to_push:
            if not os.path.exists(local_file):
                if self.options.ignore_missing:
                    files_to_push.remove((local_file,remote_file))
                    print("Local file %s does not exist. Ignoring file..." %(local_file))
                else:
                    die(9, "No such file `%s'" % local_file)

        print("Pushing to channel %s:" % (channel, ))

        delim_start = self.options.delim_start
        delim_end = self.options.delim_end

        selinux_ctx = None
        if type(self.options.selinux_context) != None:
            selinux_ctx = self.options.selinux_context

        for (local_file, remote_file) in files_to_push:
            try:
                r.put_file(channel, remote_file, local_file,
                    is_first_revision=self.is_first_revision,
                    delim_start=delim_start,
                    delim_end=delim_end,
                    selinux_ctx=selinux_ctx)
            except cfg_exceptions.RepositoryFileExistsError:
                e = sys.exc_info()[1]
                log_error("Error: %s is already in channel %s" %
                          (remote_file, channel))
            except cfg_exceptions.RepositoryFilePushError:
                e = sys.exc_info()[1]
                log_error("Error pushing file:  %s" % e)
            else:
                print("Local file %s -> remote file %s" % (local_file, remote_file))
07070100000038000081B40000000000000000000000015D403FC80000052A000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_create_channel.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.
#

from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die

class Handler(handler_base.HandlerBase):
    _usage_options = "config_channel [ config_channel ... ]"
    def run(self):
        log_debug(2)
        r = self.repository

        if len(self.args) == 0:
            die(5, "No config channels specified")

        for channel in self.args:
            print("Creating config channel %s" % channel)
            try:
                r.create_config_channel(channel)
            except cfg_exceptions.ConfigChannelAlreadyExistsError:
                die("Config channel %s already exists" % channel)
            print("Config channel %s created" % channel)
07070100000039000081B40000000000000000000000015D403FC800002048000000000000000000000000000000000000002900000000mgr-cfg/config_management/rhncfg_diff.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 base64
from datetime import datetime
import difflib
import os
import sys

from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, die
from config_common.file_utils import f_date, ostr_to_sym
from spacewalk.common.usix import next
from rhn.i18n import bstr, sstr


class Handler(handler_base.HandlerBase):
    _usage_options = "[options] file [ file ... ]"
    _options_table = [
        handler_base.HandlerBase._option_class(
            '-c', '--channel',    action="store",
             help="Get file(s) from this config channel",
         ),
        handler_base.HandlerBase._option_class(
            '-r', '--revision',     action="store",
             help="Use this revision",
         ),
        handler_base.HandlerBase._option_class(
            '-d', '--dest-file',    action="store",
             help="Remote file to compare to",
         ),
        handler_base.HandlerBase._option_class(
            '-t', '--topdir',       action="store",
             help="Directory to which all file paths are relative",
         ),
    ]
    def run(self):
        log_debug(2)

        if self.options.dest_file and self.options.topdir:
            die(6, "Error: conflicting options --dest-file and --topdir")

        if len(self.args) == 0:
            die(0, "No files supplied (use --help for help)")

        channel = self.options.channel

        if not channel:
            die(6, "Config channel not specified")

        r = self.repository
        if not r.config_channel_exists(channel):
            die(6, "Error: config channel %s does not exist" % channel)

        topdir = self.options.topdir
        revision = self.options.revision

        files_to_diff = []

        files = [utils.normalize_path(x) for x in self.args]
        files_count = len(files)

        if files_count != 1 and revision is not None:
            die(8, "--revision can only be used with a single file")

        if self.options.dest_file:
            if files_count != 1:
                die(7, "--dest-file accepts a single file")

            files_to_diff.append((files[0], self.options.dest_file))

        elif topdir:
            if not os.path.isdir(topdir):
                die(8, "--topdir specified, but `%s' not a directory" %
                    topdir)

            #5/11/04 wregglej - 141790 remove trailing slash in topdir, if present.
            topdir = utils.rm_trailing_slash(topdir)

            for f in files:
                if not f.startswith(topdir):
                    die(8, "--topdir %s specified, but file `%s' doesn't comply"
                        % (topdir, f))
                if os.path.isdir(f) and not os.path.islink(f):
                    die(8, "Cannot diff %s; it is a directory" % f)
                files_to_diff.append((f, f[len(topdir):]))
        else:
            for f in files:
                if os.path.isdir(f) and not os.path.islink(f):
                    die(8, "Cannot diff %s; it is a directory" % f)
                files_to_diff.append((f, f))

        for (local_file, remote_file) in files_to_diff:
            sys.stdout.write(
                self.diff_file(channel, remote_file, local_file, revision))

    def __attributes_differ(self, fsrc, fdst):
        """ Returns true if acl, ownership, type or selinux context differ.
            fsrc is config file retrieved from xmlrpc, fdst is output of make_stat_info()
        """
        return (fsrc['filemode'] != fdst['mode']) or \
               (fsrc['username'] != fdst['user']) or (fsrc['groupname'] != fdst['group']) or \
               (fsrc['selinux_ctx'] != fdst['selinux_ctx'])

    def diff_file(self, channel, path, local_file, revision):
        r = self.repository
        try:
            info = r.get_raw_file_info(channel, path, revision)
            if 'encoding' in info and info['file_contents']:
                if info['encoding'] == 'base64':
                    info['file_contents'] = base64.decodestring(bstr(info['file_contents']))
                else:
                    die(9, 'Error: unknown encoding %s' % info['encoding'])
        except cfg_exceptions.RepositoryFileMissingError:
            die(2, "Error: no such file %s (revision %s) in config channel %s"
                % (path, revision, channel))
        if os.path.islink(local_file) and info['filetype'] != 'symlink' :
            die(8, "Cannot diff %s; the file on the system is a symbolic link while the file in the channel is not. " % local_file)
        if  info['filetype'] == 'symlink' and not os.path.islink(local_file) :
            die(8, "Cannot diff %s; the file on the system is not a symbolic link while the file in the channel is. " % local_file)
        if info['filetype'] == 'symlink':
            src_link = info['symlink']
            dest_link = os.readlink(local_file)
            if src_link != os.readlink(local_file):
                return "Symbolic links differ. Channel: '%s' -> '%s'   System: '%s' -> '%s' \n " % (path,src_link, path, dest_link)
            return ""

        response_output = ""
        content_differs = False
        if 'is_binary' in info and info['is_binary'] == 'Y':
            from_content = info['file_contents']
            to_file = open(local_file, 'rb')
            to_content = to_file.read()
            to_file.close()
            if len(from_content) != len(to_content):
                content_differs = True
            else:
                for i in range(len(from_content)):
                    if from_content[i] != to_content[i]:
                         content_differs = True
                         break
            if content_differs:
                response_output = "Binary file content differs\n"
        else:
            fromlines = sstr(info['file_contents']).splitlines(1)
            tofile = open(local_file, 'r')
            tolines = tofile.readlines()
            tofile.close()
            diff_output = difflib.unified_diff(fromlines, tolines, info['path'], local_file)
            first_row = second_row = ''
            try:
                first_row = next(diff_output)
                # if content was same, exception thrown so following
                # lines don't execute
                content_differs = True
                second_row = next(diff_output)
                response_output = ''.join(list(diff_output))
            except StopIteration:
                pass

        file_stat = os.lstat(local_file)
        local_info = r.make_stat_info(local_file, file_stat)
        # rhel4 do not support selinux
        if not 'selinux_ctx' in local_info:
            local_info['selinux_ctx'] = ''
        if 'selinux_ctx' not in info:
            info['selinux_ctx'] = ''
        if not content_differs and not self.__attributes_differ(info, local_info):
            return ""
        else:
            template = "%s %s\t%s\tattributes: %s %s %s %s\tconfig channel: %s\trevision: %s"
            if 'modified' not in info:
                info['modified'] = ''
            first_row = template % ('---', path, str(info['modified']), ostr_to_sym(info['filemode'], info['filetype']),
                        info['username'], info['groupname'], info['selinux_ctx'], channel,
                        info['revision'],
            )
            second_row = template % ('+++', local_file, f_date(datetime.fromtimestamp(local_info['mtime'])), ostr_to_sym(local_info['mode'], 'file'),
                        local_info['user'], local_info['group'], local_info['selinux_ctx'], 'local file', None
            )
        return first_row + '\n' + second_row + '\n' + response_output
0707010000003A000081B40000000000000000000000015D403FC800000ACD000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_diff_revisions.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

from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die

class Handler(handler_base.HandlerBase):
    _usage_options = "[options] file"
    _options_table = [
        handler_base.HandlerBase._option_class(
            '-c', '--channel',    action="append",
             help="Use this config channel",
         ),
        handler_base.HandlerBase._option_class(
            '-r', '--revision',     action="append",
             help="Use this revision",
         ),
    ]
    def run(self):
        log_debug(2)
        r = self.repository

        if len(self.args) != 1:
            die(3, "One file needs to be specified")

        path = self.args[0]

        channel_dst = None
        ns_count = len(self.options.channel or [])
        if ns_count == 0:
            die(3, "At least one config channel has to be specified")

        channel_src = self.options.channel[0]
        if ns_count > 2:
            die(3, "At most two config channels can be specified")

        if not r.config_channel_exists(channel_src):
            die(4, "Source config channel %s does not exist" % channel_src)

        if ns_count == 2:
            channel_dst = self.options.channel[1]

            if not r.config_channel_exists(channel_dst):
                die(4, "Config channel %s does not exist" % channel_dst)

        revision_dst = None
        rev_count = len(self.options.revision or [])
        if rev_count == 0:
            die(3, "At least one revision has to be specified")

        revision_src = self.options.revision[0]
        if rev_count > 2:
            die(3, "At most two revisions can be specified")

        if rev_count == 2:
            revision_dst = self.options.revision[1]

        try:
            result = r.diff_file_revisions(path, channel_src,
                revision_src, channel_dst, revision_dst)
        except cfg_exceptions.RepositoryFileMissingError:
            e = sys.exc_info()[1]
            die(2, e[0])
        except cfg_exceptions.BinaryFileDiffError:
            e = sys.exc_info()[1]
            die(3, e[0])

        sys.stdout.write(result)
0707010000003B000081B40000000000000000000000015D403FC800000734000000000000000000000000000000000000003500000000mgr-cfg/config_management/rhncfg_download_channel.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 os

from config_common import handler_base, utils
from config_common.deploy import deploy_files
from config_common.rhn_log import log_debug, die

class Handler(handler_base.HandlerBase):
    _usage_options = "(-t|--topdir=)<top-level-directory> [options] config_channel [ config_channel ... ]"

    _options_table = handler_base.HandlerBase._options_table + [
        handler_base.HandlerBase._option_class(
            '-t', '--topdir',       action="store",
             help="Directory all the file paths are relative to. This option must be set.",
         ),
    ]

    def run(self):
        log_debug(2)
        r = self.repository

        if not self.args:
            die(6, "No config channels specified")

        topdir = self.options.topdir
        if not topdir:
            die(7, "--topdir not specified")

        if not os.path.isdir(topdir):
            die(8, "--topdir specified, but `%s' not a directory" %
                topdir)

        for ns in self.args:
            if not r.config_channel_exists(ns):
                die(6, "Error: config channel %s does not exist" % ns)

            deploy_files(utils.join_path(topdir, ns), r, r.list_files(ns),
                         config_channel=ns)
0707010000003C000081B40000000000000000000000015D403FC8000011EE000000000000000000000000000000000000002800000000mgr-cfg/config_management/rhncfg_get.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 tempfile

from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, die
from config_common.transactions import DeployTransaction, FailedRollback
from spacewalk.common.usix import raise_with_tb


def deploying_mesg_callback(path):
    print("Deploying %s" % path)


class Handler(handler_base.HandlerBase):
    _usage_options = "[options] file [ file ... ]"

    _options_table = handler_base.HandlerBase._options_table + [
        handler_base.HandlerBase._option_class(
            '-c', '--channel',      action="store",
             help="Get file(s) from this config channel",
         ),
        handler_base.HandlerBase._option_class(
            '-t', '--topdir',       action="store",
             help="Make all files relative to this string",
         ),
        handler_base.HandlerBase._option_class(
            '-r', '--revision',    action="store",
             help="Get this file revision",
         ),
    ]

    def run(self):
        log_debug(2)
        r = self.repository

        channel = self.options.channel
        if not channel:
            die(6, "Config channel not specified")

        topdir = self.options.topdir
        if topdir:
            if not os.path.isdir(self.options.topdir):
                die(8, "--topdir specified, but `%s' not a directory" %
                    self.options.topdir)

        if not self.args:
            die(7, "No files specified")

        revision = self.options.revision
        if revision:
            if len(self.args) > 1:
                die(9, "--revision specified with multiple files")

        dep_trans = None

        if topdir:
            dep_trans = DeployTransaction(transaction_root=topdir)
            dep_trans.deploy_callback(deploying_mesg_callback)

        for f in self.args:
            try:
                directory = topdir or tempfile.gettempdir()

                #5/11/05 wregglej - 157066 dirs_created is returned from get_file_info.
                (temp_file, info, dirs_created) = r.get_file_info(channel, f, revision=revision,
                                                    auto_delete=0, dest_directory=directory)

            except cfg_exceptions.RepositoryFileMissingError:
                if revision is not None:
                    die(2, "Error: file %s (revision %s) not in config "
                        "channel %s" % (f, revision, channel))
                else:
                    die(2, "Error: file %s not in config channel %s" %
                        (f, channel))

            if topdir:
                #5/11/05 wregglej - 157066 dirs_created now gets passed into add_preprocessed.
                dep_trans.add_preprocessed(f, temp_file, info, dirs_created, strict_ownership=0)
                continue
            elif info.get('filetype') == 'symlink':
                print("%s -> %s" % (info['path'], info['symlink']))
                continue
            elif info.get('filetype') == 'directory':
                print("%s is a directory entry, nothing to get" % info['path'])
                continue
            else:
                print(open(temp_file).read())
                os.unlink(temp_file)

        if topdir:
            try:
                dep_trans.deploy()
            except Exception:
                try:
                    dep_trans.rollback()
                except FailedRollback:
                    raise_with_tb(FailedRollback("FAILED ROLLBACK:  "), sys.exc_info()[2])
                #5/3/05 wregglej - 136415 Added exception stuff for missing user info.
                except cfg_exceptions.UserNotFound:
                    raise
                #5/5/05 wregglej - 136415 Added exception handling for unknown group.
                except cfg_exceptions.GroupNotFound:
                    raise
                else:
                    raise_with_tb(Exception("Deploy failed, rollback successful:  "), sys.exc_info()[2])


0707010000003D000081B40000000000000000000000015D403FC8000004C2000000000000000000000000000000000000002900000000mgr-cfg/config_management/rhncfg_list.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.
#

from config_common import handler_base
from config_common.rhn_log import log_debug, die

class Handler(handler_base.HandlerBase):
    _usage_options = "config_channel"
    def run(self):
        log_debug(2)
        r = self.repository

        if len(self.args) != 1:
            die(5, "Exactly one argument required")

        channel = self.args[0]

        print("Files in config channel '%s':" % channel)

        files = r.list_files(channel)
        if not files:
            print("(no files in config channel)")
        else:
            for file in files:
                print("  %s" % file)
0707010000003E000081B40000000000000000000000015D403FC800000404000000000000000000000000000000000000003200000000mgr-cfg/config_management/rhncfg_list_channels.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.
#

from config_common import handler_base
from config_common.rhn_log import log_debug, die

class Handler(handler_base.HandlerBase):
    _usage_options = ""
    def run(self):
        log_debug(2)
        r = self.repository

        if len(self.args):
            die(5, "No arguments required")

        print("Available config channels:")
        for ns in r.list_config_channels():
            print("  %s" % ns)
0707010000003F000081B40000000000000000000000015D403FC800000B71000000000000000000000000000000000000002B00000000mgr-cfg/config_management/rhncfg_remove.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
from config_common import handler_base, utils
from config_common.rhn_log import log_debug, die

try: # python2
    import xmlrpclib
except ImportError: # python3
    import xmlrpc.client as xmlrpclib

class Handler(handler_base.HandlerBase):
    _usage_options = "[options] file [ file ... ]"

    _options_table = handler_base.HandlerBase._options_table + [
        handler_base.HandlerBase._option_class(
            '-c', '--channel',      action="store",
             help="Remove files from this config channel",
         ),
        handler_base.HandlerBase._option_class(
            '-t', '--topdir',       action="store",
             help="Make all files relative to this string",
         ),
    ]

    def run(self):
        log_debug(2)
        r = self.repository

        if len(self.args) == 0:
            die(0, "No files supplied (use --help for help)")

        channel = self.options.channel

        if not channel:
            die(6, "Config channel not specified")

        r = self.repository
        if not r.config_channel_exists(channel):
            die(6, "Error: config channel %s does not exist" % channel)

        files = [utils.normalize_path(x) for x in self.args]

        files_to_remove = []
        if self.options.topdir:
            if not os.path.isdir(self.options.topdir):
                die(8, "--topdir specified, but `%s' not a directory" %
                    self.options.topdir)
            for f in files:
                if not f.startswith(self.options.topdir):
                    die(8, "--topdir %s specified, but file `%s' doesn't comply"
                        % (self.options.topdir, f))
                files_to_remove.append((f, f[len(self.options.topdir):]))
        else:
            for f in files:
                files_to_remove.append((f, f))

        print("Removing from config channel %s" % channel)
        for (local_file, remote_file) in files_to_remove:
            try:
                r.remove_file(channel, remote_file)
            except xmlrpclib.Fault:
                e = sys.exc_info()[1]
                if e.faultCode == -4011:
                    print("%s does not exist" % remote_file)
                    continue
                raise
            else:
                print("%s removed" % remote_file)
07070100000040000081B40000000000000000000000015D403FC8000005EB000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_remove_channel.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.
#

from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die

class Handler(handler_base.HandlerBase):
    _usage_options = "config_channel [ config_channel ... ]"
    def run(self):
        log_debug(2)
        r = self.repository

        if len(self.args) == 0:
            die(5, "No config channels specified")

        for config_channel in self.args:
            print("Removing config channel %s" % config_channel)
            try:
                r.remove_config_channel(config_channel)
            except cfg_exceptions.ConfigChannelNotInRepo:
                die("Config channel %s does not exist" % config_channel)
            except cfg_exceptions.ConfigChannelNotEmptyError:
                die("Could not remove non-empty config channel %s" %
                    config_channel)
            print("Config channel %s removed" % config_channel)
07070100000041000081B40000000000000000000000015D403FC800000720000000000000000000000000000000000000002E00000000mgr-cfg/config_management/rhncfg_revisions.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.
#

from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die

class Handler(handler_base.HandlerBase):
    _usage_options = "[options] file [ file ... ]"

    _options_table = handler_base.HandlerBase._options_table + [
        handler_base.HandlerBase._option_class(
            '-c', '--channel',      action="store",
             help="Use this config channel",
         ),
    ]
    def run(self):
        log_debug(2)
        r = self.repository

        channel = self.options.channel
        if not channel:
            die(6, "Config channel not specified")

        if not self.args:
            die(7, "No files specified")

        print("Analyzing files in config channel %s" % channel)
        for f in self.args:
            if not r.has_file(channel, f):
                die(8, "Config channel %s does not contain file %s" %
                    (channel, f))
            try:
                revisions = r.get_file_revisions(channel, f)
            except cfg_exceptions.RepositoryFileMissingError:
                print("%s: not in config channel" % f)
                continue
            print("%s: %s" % (f, " ".join([str(x) for x in revisions])))
07070100000042000081B40000000000000000000000015D403FC8000002B4000000000000000000000000000000000000002B00000000mgr-cfg/config_management/rhncfg_update.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 rhncfg_add

class Handler(rhncfg_add.Handler):
    is_first_revision=0
07070100000043000081B40000000000000000000000015D403FC8000014CF000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_upload_channel.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

from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, log_error, die

class Handler(handler_base.HandlerBase):
    _usage_options = "[options] [ config_channel ... ]"

    _options_table = handler_base.HandlerBase._options_table + [
        handler_base.HandlerBase._option_class(
            '-t', '--topdir',
            action="store",
            default="./",
             help="Directory all the file paths are relative to",
         ),
        handler_base.HandlerBase._option_class(
            '-c', '--channel',
            action='store',
            default=None,
            help="List of channels the config info will be uploaded into. Channels delimited by ','.\nExample: --channel=foo,bar,baz",
        ),
        handler_base.HandlerBase._option_class(
            '', '--disable-selinux',
            action='store_true',
            default=None,
            help="Don't upload SELinux contexts",
        ),
    ]

    def run(self):
        log_debug(2)
        #5/12/05 wregglej - 149034 changed r into a instance variable
        self.r = self.repository

        topdir = self.options.topdir
        if not topdir:
            die(7, "--topdir not specified")

        if not os.path.isdir(topdir):
            die(8, "--topdir specified, but `%s' not a directory" %
                topdir)

        topdir = utils.normalize_path(topdir)

        #5/12/05 wregglej - 149034 allowing the channel name and the directory name to vary independently.
        if not self.options.channel is None:
            #Get the list of channels with leading and trailing whitespace removed.
            channels = [x.strip() for x in self.options.channel.split(',') if x]

            #Get the list of directories to upload. At this point it's the list of arguments.
            dirs = self.args
        elif not self.args:
            #If we get to this point, then --channel wasn't used and nothing was included as arguments.
            #Assumes that the directories in topdir are the ones we want to upload, and since no channels were
            #specified that each directory is it's own channel.
            channels = os.listdir(topdir)
            dirs = None
            print("No config channels specified, using %s" % channels)
        else:
            #At this point, --channel wasn't used but there was something included as an argument.
            #The name of the channel is assumed to be the same as the name of the directory.
            channels = self.args
            dirs = None

        #If dirs isn't None, then each directory needs to be uploaded into each channel.
        if dirs:
            for channel in channels:
                for directory in dirs:
                    self.upload_config_channel(topdir, channel, directory)
        #If dirs is None, then each channel is it's own channel.
        else:
            for channel in channels:
                self.upload_config_channel(topdir, channel, channel)

    def upload_config_channel(self, topdir, channel, directory_name):
        if not self.r.config_channel_exists(channel):
            die(6, "Error: config channel %s does not exist" % channel)

        if self.options.disable_selinux:
            selinux_ctx = ''
        else:
            selinux_ctx = None

        print("Using config channel %s" % channel)

        channel_dir = utils.join_path(topdir, directory_name)

        if not os.path.exists(channel_dir):
            die(6, "Error: channel directory %s does not exist" % channel_dir)

        flist = list_files_recursive(channel_dir)

        for (dirname, filenames) in flist:
            assert dirname.startswith(channel_dir)
            remote_dirname = dirname[len(channel_dir):]

            for f in filenames:
                local_file = utils.join_path(dirname, f)
                remote_file = utils.join_path(remote_dirname, f)

                print("Uploading %s from %s" % (remote_file, local_file))
                try:
                    self.r.put_file(channel, remote_file, local_file, is_first_revision=0,
                                    selinux_ctx = selinux_ctx)
                except cfg_exceptions.RepositoryFilePushError:
                    e = sys.exc_info()[1]
                    log_error(e)


def is_file_or_link(dirname, basename):
    return os.path.isfile(os.path.join(dirname, basename)) or \
                        os.path.islink(os.path.join(dirname, basename))

def list_files_recursive(d):
    file_list = []
    for dirname, dirs, names in os.walk(d):
        file_list.append((dirname, filter(lambda x, d=dirname: is_file_or_link(d, x),
            names)))
    return file_list
07070100000044000081B40000000000000000000000015D403FC800003038000000000000000000000000000000000000002C00000000mgr-cfg/config_management/rpc_repository.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 tempfile
from spacewalk.common.usix import raise_with_tb

try: # python2
    import xmlrpclib
except ImportError: # python3
    import xmlrpc.client as xmlrpclib


from config_common import cfg_exceptions, repository, utils, file_utils
from config_common.rhn_log import log_debug

class Repository(repository.RPC_Repository):
    _session_file = '.rhncfg-manager-session'
    def __init__(self, setup_network=1):
        log_debug(2)
        repository.RPC_Repository.__init__(self, setup_network)

        self.files_to_delete = []

        self.username = None
        self.password = None

        self.session = None

    def login(self, username=None, password=None):

        self._get_session()

        if not self.session and not (username and password):
            raise cfg_exceptions.InvalidSession()

        if self.session and not username:
            test = self.rpc_call('config.test_session',
                {'session' : self.session})
            if not test:
                raise cfg_exceptions.InvalidSession('Session is either expired or invalid')

        else:
            self.username = username
            self.password = password

            try:
                self.session = self.rpc_call('config.rhn_login', {
                    'username' : self.username,
                    'password' : self.password,
                    })
            except xmlrpclib.Fault:
                e = sys.exc_info()[1]
                fault_code, fault_string = e.faultCode, e.faultString
                if fault_code == -2:
                    raise_with_tb(cfg_exceptions.AuthenticationError(
                        "Invalid username or incorrect password"), sys.exc_info()[2])
                raise_with_tb(cfg_exceptions.InvalidSession(fault_code, fault_string), sys.exc_info()[2])

            self._save_session()

        if not self.session:
            raise cfg_exceptions.InvalidSession

        self.assert_repo_health()


    def cleanup(self):
        log_debug(4)
        for file in self.files_to_delete:
            if not os.path.isdir(file):
                os.unlink(file)

    def get_raw_file_info(self, config_channel, repopath, revision=None):
        """ given a namepath, return the raw data  passed by the server """
        log_debug(5)
        params =  {
            'session'           : self.session,
            'config_channel'    : config_channel,
            'path'              : repopath,
        }
        if revision is not None:
            params['revision'] = revision
        try:
            result = self.rpc_call('config.management.get_file', params)
        except xmlrpclib.Fault:
            e = sys.exc_info()[1]
            if e.faultCode == -4011:
                # File not present
                raise_with_tb(cfg_exceptions.RepositoryFileMissingError(config_channel,
                    repopath), sys.exc_info()[2])
            raise
        return result

    def get_file_info(self, config_channel, repopath, revision=None, auto_delete=1, dest_directory=tempfile.gettempdir()):
        """
        given a namepath, return the filename and the rest of the info passed
        by the server
        """
        result = self.get_raw_file_info(config_channel, repopath, revision)

        fp = file_utils.FileProcessor()
        fullpath, dirs_created = fp.process(result, directory=dest_directory, strict_ownership=0)

        if auto_delete:
            self.files_to_delete.append(fullpath)

        del result['file_contents']

        return fullpath, result, dirs_created


    def has_file(self, config_channel, repopath):
        params =  {
            'session'           : self.session,
            'config_channel'    : config_channel,
            'path'              : repopath,
        }
        return self.rpc_call('config.management.has_file', params)

    def remove_file(self, config_channel, repopath):
        """ remove a given file from the repo """
        log_debug(4)
        params =  {
            'session'           : self.session,
            'config_channel'    : config_channel,
            'path'              : repopath,
        }
        return self.rpc_call('config.management.remove_file', params)

    def put_file(self, config_channel, repopath, localfile=None,
            is_first_revision=None, old_revision=None, delim_start=None,
            delim_end=None, selinux_ctx=None):
        """
        Insert a given file into the repo, overwriting if necessary.
        localfile defaults to the repopath
        """
        log_debug(4)

        params = self._make_file_info(repopath, localfile,
            delim_start=delim_start, delim_end=delim_end)

        max_file_size = self.get_maximum_file_size()

        if params['size'] > max_file_size:
            error_msg = "%s too large (%s bytes, %s bytes max allowed)"
            raise cfg_exceptions.ConfigFileTooLargeError(error_msg % (localfile, params['size'], max_file_size))

        if selinux_ctx is not None:
            params.update({
                'selinux_ctx'   : selinux_ctx,
            })

        params.update({
            'session'           : self.session,
            'config_channel'    : config_channel,
        })
        if is_first_revision:
            params['is_first_revision'] = 1
        elif old_revision:
            params['old_revision'] = int(old_revision)

        try:
            result = self.rpc_call('config.management.put_file', params)

        except xmlrpclib.Fault:
            e = sys.exc_info()[1]
            fault_code, fault_string = e.faultCode, e.faultString

            if is_first_revision and fault_code == -4013:
                raise_with_tb(cfg_exceptions.RepositoryFileExistsError(fault_string), sys.exc_info()[2])

            if old_revision and fault_code == -4012:
                raise_with_tb(cfg_exceptions.RepositoryFileVersionMismatchError(fault_string), sys.exc_info()[2])

            if fault_code == -4003:
                raise_with_tb(cfg_exceptions.ConfigFileTooLargeError(fault_string), sys.exc_info()[2])

            if fault_code == -4014:
                raise_with_tb(cfg_exceptions.QuotaExceeded(fault_string), sys.exc_info()[2])

            raise_with_tb(cfg_exceptions.RepositoryFilePushError(fault_code, fault_string), sys.exc_info()[2])

        return result


    def config_channel_exists(self, config_channel):
        log_debug(4, config_channel)
        return (config_channel in self.list_config_channels())

    def list_files(self, config_channel, repopath = None, recurse = 1):
        """
        list files in a repo, recursing if requested;
        repopath is not used yet
        """
        log_debug(4)
        files = self.rpc_call('config.management.list_files',
            {'session' : self.session, 'config_channel' : config_channel})

        return [p['path'] for p in files]

    def get_file_revisions(self, config_channel, repopath):
        """
        Fetch the file's revisions
        """
        log_debug(4)
        params =  {
            'session'           : self.session,
            'config_channel'    : config_channel,
            'path'              : repopath,
        }
        try:
            revisions = self.rpc_call('config.management.list_file_revisions',
                params)
        except xmlrpclib.Fault:
            e = sys.exc_info()[1]
            if e.faultCode == -4011:
                # File not present
                raise_with_tb(cfg_exceptions.RepositoryFileMissingError(
                    config_channel, repopath), sys.exc_info()[2])
            raise
        return revisions

    def list_config_channels(self):
        "List config channels"
        log_debug(4)
        if hasattr(self, 'config_channels'):
            return self.config_channels

        self.config_channels = self.rpc_call(
            'config.management.list_config_channels', {'session' : self.session}
        ) or []

        return self.config_channels

    def create_config_channel(self, config_channel):
        "creates a configuration channel"
        log_debug(4, config_channel)
        try:
            return self.rpc_call('config.management.create_config_channel',
                {'session' : self.session, 'config_channel' : config_channel})
        except xmlrpclib.Fault:
            e = sys.exc_info()[1]
            if e.faultCode == -4010:
                raise_with_tb(cfg_exceptions.ConfigChannelAlreadyExistsError(config_channel), sys.exc_info()[2])
            raise

    def remove_config_channel(self, config_channel):
        "Removes a configuration channel"
        log_debug(4, config_channel)
        try:
            return self.rpc_call('config.management.remove_config_channel',
                {'session' : self.session, 'config_channel' : config_channel})
        except xmlrpclib.Fault:
            e = sys.exc_info()[1]
            if e.faultCode == -4009:
                raise_with_tb(cfg_exceptions.ConfigChannelNotInRepo(config_channel), sys.exc_info()[2])
            if e.faultCode == -4005:
                raise_with_tb(cfg_exceptions.ConfigChannelNotEmptyError(config_channel), sys.exc_info()[2])
            raise

    def _get_default_delimiters(self):
        "retrieves the default delimiters from the server"
        log_debug(4)
        result = self.rpc_call('config.management.get_default_delimiters',
            {'session'   : self.session})
        return result.get('delim_start'), result.get('delim_end')

    def _get_maximum_file_size(self):
        "get the maximum file size from the server"
        log_debug(4)
        result = self.rpc_call('config.management.get_maximum_file_size',
            {'session'   : self.session})
        return result

    def assert_repo_health(self):
        log_debug(4)
        pass

    def diff_file_revisions(self, path, config_channel_src, revision_src,
            config_channel_dst, revision_dst):
        log_debug(4)
        params = {
            'session'           : self.session,
            'path'              : path,
            'config_channel_src': config_channel_src,
            'revision_src'      : revision_src,
        }
        if config_channel_dst is not None:
            params['config_channel_dst'] = config_channel_dst
        if revision_dst is not None:
            params['revision_dst'] = revision_dst
        try:
            ret = self.rpc_call('config.management.diff', params)
        except xmlrpclib.Fault:
            e = sys.exc_info()[1]
            if e.faultCode == -4011:
                # File not present
                raise_with_tb(cfg_exceptions.RepositoryFileMissingError(e.faultString), sys.exc_info()[2])
            if e.faultCode == -4004:
                # Binary file requested
                raise_with_tb(cfg_exceptions.BinaryFileDiffError(e.faultString), sys.exc_info()[2])
            raise
        return ret

    def _get_session(self):
        session_path = self._get_session_path()

        try:
            fh = open(session_path, 'r')
            self.session = fh.read()
            fh.close()
        except IOError:
            # session file not there...
            self.session = None

        return self.session

    def _save_session(self):
        if not self.session:
            self._remove_session()

        session_path = self._get_session_path()

        fh = open(session_path, "w+")
        fh.write(self.session)
        fh.close()

    def _remove_session(self):
        p = self._get_session_path()
        try:
            os.unlink(p)
        except OSError:
            pass

    def _get_session_path(self):
        return os.path.join(utils.get_home_dir(), self._session_file)

07070100000045000081B40000000000000000000000015D403FC80000003C000000000000000000000000000000000000001A00000000mgr-cfg/mgr-cfg-rpmlintrcaddFilter("suse-filelist-forbidden-sysconfig .*/sysconfig")
07070100000046000081B40000000000000000000000015D403FC800003328000000000000000000000000000000000000001800000000mgr-cfg/mgr-cfg.changes-------------------------------------------------------------------
Tue Jul 30 14:39:43 CEST 2019 - jgonzalez@suse.com

- version 4.0.9-1
- Ensure bytes type when using hashlib to avoid traceback (bsc#1138822)

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

- version 4.0.8-1
- Obsolete newer versions of rhncfg

-------------------------------------------------------------------
Wed May 15 14:58:23 CEST 2019 - jgonzalez@suse.com

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

-------------------------------------------------------------------
Mon Apr 22 14:40:08 CEST 2019 - jgonzalez@suse.com

- version 4.0.6-1
- Fix build for non SUSE/openSUSE distributions

-------------------------------------------------------------------
Mon Apr 22 12:00:20 CEST 2019 - jgonzalez@suse.com

- version 4.0.5-1
- create client tools compat links also for non-SUSE systems
  (bsc#1131408)
- Add makefile and configuration for pylint

-------------------------------------------------------------------
Wed Feb 27 12:57:48 CET 2019 - jgonzalez@suse.com

- version 4.0.4-1
- Sync with Spacewalk
- make filemod more readable
- 1665858 - diff expects string not bytes

-------------------------------------------------------------------
Mon Dec 17 14:31:58 CET 2018 - jgonzalez@suse.com

- version 4.0.3-1
- Fix python2 compilation on openSUSE

-------------------------------------------------------------------
Fri Oct 26 09:52:13 CEST 2018 - jgonzalez@suse.com

- version 4.0.2-1
- use rpm for debian packaging

-------------------------------------------------------------------
Fri Aug 10 16:00:11 CEST 2018 - jgonzalez@suse.com

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

-------------------------------------------------------------------
Wed May 23 08:59:19 CEST 2018 - jgonzalez@suse.com

- version 5.10.122.2-1
- format the file mode in unified way(bsc#1093529)

-------------------------------------------------------------------
Mon Mar 26 08:36:31 CEST 2018 - jgonzalez@suse.com

- version 5.10.122.1-1
- Sync with upstream (bsc#1083294)
- Build python2 packages on SUSE systems

-------------------------------------------------------------------
Mon Mar 05 08:39:27 CET 2018 - jgonzalez@suse.com

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

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

- version 5.10.121.1-1
- Sync with upstream

-------------------------------------------------------------------
Fri Feb 02 11:54:33 CET 2018 - jgonzalez@suse.com

- version 5.10.119.2-1
- Improve webui for comparing files (bsc#1076201)

-------------------------------------------------------------------
Wed Jan 17 11:10:05 CET 2018 - jgonzalez@suse.com

- version 5.10.119.1-1
- Fix update mechanism when updating the updateservice (bsc#1073619)
- Add --config option to rhncfg-manager and rhncfg-client
- Add better handling of interrupted system calls

-------------------------------------------------------------------
Fri Nov 10 16:22:55 CET 2017 - mc@suse.de

- version 5.10.117.1-1
- rhncfg: add missing dirs to filelist

-------------------------------------------------------------------
Thu Oct 26 17:14:23 CEST 2017 - mc@suse.de

- version 5.10.116.1-1
- move files into proper python2/python3 subpackages
- store output in the action file so partial output can arrive to server
- print different message if file does not exist
- print a name of file which does not exist during diff
- tell user which file differs
- add password config option to rhncfg-manager
- execute remote commands in clean environment

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

- version 5.10.103.2-1
- Add password config option to rhncfg-manager.
- require rhnlib version with i18n module (bsc#1038483)

-------------------------------------------------------------------
Wed May 03 16:43:52 CEST 2017 - michele.bologna@suse.com

- version 5.10.103.1-1

-------------------------------------------------------------------
Fri Mar 31 10:00:10 CEST 2017 - mc@suse.de

- version 5.10.102.2-1
- fix missing import in rhncfg

-------------------------------------------------------------------
Tue Mar 07 16:01:22 CET 2017 - mc@suse.de

- version 5.10.102.1-1
- Updated links to github in spec files
- Symlink target overwritten when the symlink is replaced by a file
  managed by rhncfg-client
- require spacewalk-usix indead of spacewalk-backend-usix

-------------------------------------------------------------------
Wed Jan 11 15:26:05 CET 2017 - michele.bologna@suse.com

- version 5.10.100.1-1
- Version bump to 5.10.100

-------------------------------------------------------------------
Mon Mar 21 16:40:54 CET 2016 - mc@suse.de

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

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

- version 5.10.88.1-1
- fixing removing of temporary files during transaction rollback for
  rhncfg-manager.
- fixing removing directories which rhncfg-manager didn't create.

-------------------------------------------------------------------
Mon Nov 30 11:16:55 CET 2015 - mc@suse.de

- version 5.10.87.1-1
- python 2.4 compatibility
- remove temporary files when exception occurs

-------------------------------------------------------------------
Wed Oct 07 15:51:28 CEST 2015 - mc@suse.de

- version 5.10.85.1-1
- make rhncfg support sha256 and use it by default
- fix for assigning all groups user belongs to running process
- show server modified time with rhncfg-client diff

-------------------------------------------------------------------
Tue Mar 31 14:51:34 CEST 2015 - mc@suse.de

- version 5.10.65.11-1
- fix config diff - Avoid addition of None and str (bsc#920897)

-------------------------------------------------------------------
Tue Feb 03 12:03:24 CET 2015 - mc@suse.de

- version 5.10.65.10-1
- Normalize path sooner
- Fix directory creation
- Getting rid of Tabs and trailing spaces

-------------------------------------------------------------------
Thu Dec 04 13:22:40 CET 2014 - mc@suse.de

- version 5.10.65.9-1
- fix compare config files by checking permissions on the
  correct file (bsc#900498)

-------------------------------------------------------------------
Fri Nov 07 13:25:00 CET 2014 - mc@suse.de

- version 5.10.65.8-1
- fix errror in rhncfg if selinux off

-------------------------------------------------------------------
Fri Sep 12 16:06:20 CEST 2014 - mc@suse.de

- version 5.10.65.7-1
- validate the content of config files before deploying

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

- sanitize release line in specfile

-------------------------------------------------------------------
Mon Sep 01 17:06:59 CEST 2014 - mc@suse.de

- version 5.10.65.6-1
- Avoid traceback with a configfiles upload action with no selinux context
- make webui config dir diff work
- ensure webui config file diff looks at owner and permissions
- make sure webui doesn't say there are diffs if there aren't

-------------------------------------------------------------------
Tue Jun 17 10:15:27 CEST 2014 - jrenner@suse.de

- version 5.10.65.5-1
- list/elist: allow to specify list of files

-------------------------------------------------------------------
Tue May 06 15:23:18 CEST 2014 - mc@suse.de

- version 5.10.65.4-1
- fix for assigning all groups user belongs to running process

-------------------------------------------------------------------
Thu Apr 10 17:29:31 CEST 2014 - mc@suse.de

- version 5.10.65.3-1
- fix uncaught exception in config deploy (bnc#871549)

-------------------------------------------------------------------
Thu Apr 10 17:29:01 CEST 2014 - mc@suse.de

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

-------------------------------------------------------------------
Thu Mar 27 10:07:15 CET 2014 - fcastelli@suse.com

- version 5.10.65.2-1
- show server modified time with rhncfg-client diff

-------------------------------------------------------------------
Mon Dec 09 16:17:50 CET 2013 - mc@suse.de

- version 5.10.65.1-1
- switch to 2.1

-------------------------------------------------------------------
Wed Aug 21 15:46:06 CEST 2013 - mc@suse.de

- version 5.10.27.12-1
- rhncfg-* --server-name now overwrites rhncfg-*.conf
- unicode support for Remote Command scripts

-------------------------------------------------------------------
Wed Jun 12 13:41:14 CEST 2013 - mc@suse.de

- version 5.10.27.11-1
- make diffs initiated from another spacewalk server obey
  display_diff config option

-------------------------------------------------------------------
Mon Apr 22 15:54:45 CEST 2013 - mc@suse.de

- version 5.10.27.10-1
- simplify rhncfg API (bnc#815460)

-------------------------------------------------------------------
Fri Feb 08 11:06:39 CET 2013 - mc@suse.de

- version 5.10.27.9-1
- Fix Web UI config diff, which always shows 'binary files differ'
- make rhncfg diff output configurable

-------------------------------------------------------------------
Mon Oct 15 12:21:31 CEST 2012 - mc@suse.de

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

-------------------------------------------------------------------
Fri Sep 28 15:57:46 CEST 2012 - mc@suse.de

- version 5.10.27.7-1
- precompile python code to prevent verification issues (bnc#776356)
- If there's symlink in file deployment path it will be created

-------------------------------------------------------------------
Mon Jul 16 15:16:08 CEST 2012 - ug@suse.de

- version 5.10.27.6-1
- check symlink not target file existence
- value of selinux context is important

-------------------------------------------------------------------
Thu Jun 21 11:16:28 CEST 2012 - jrenner@suse.de

- version 5.10.27.5-1

-------------------------------------------------------------------
Sun Jun 17 18:07:24 CEST 2012 - mc@suse.de

- fixed insecure permissions used for /var/log/rhncfg-actions file
  CVE-2012-2679 (bnc#766148) 

-------------------------------------------------------------------
Thu May 31 10:52:19 CEST 2012 - mc@suse.de

- version 5.10.27.4-1
- fixed command synopsis

-------------------------------------------------------------------
Mon May 14 10:49:40 CEST 2012 - mc@suse.de

- version 5.10.27.3-1
- honor rhncfg-specific settings

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

- version 5.10.27.2-1
- accept server name without protocol

-------------------------------------------------------------------
Wed Mar 21 18:00:07 CET 2012 - mc@suse.de

- version 5.10.27.1-1
- Bumping package version

-------------------------------------------------------------------
Thu Nov  3 19:25:06 CET 2011 - ma@suse.de

- Adapt dependencies to renamed packages (bnc#722052)

-------------------------------------------------------------------
Thu Sep 15 17:02:28 CEST 2011 - mc@suse.de

- do not require selinux on SLES10

-------------------------------------------------------------------
Mon May 16 15:00:51 CEST 2011 - iartarisi@suse.cz

- fix staticmethod Syntax Error on systems with python < 2.4 (e.g. RHEL4) (bnc#694028)

-------------------------------------------------------------------
Mon May 16 09:14:56 CEST 2011 - iartarisi@suse.cz

- fix usage documentation messages for topdir and dest-file (bnc#684390)

-------------------------------------------------------------------
Fri Mar 18 13:00:20 CET 2011 - iartarisi@suse.cz

- pulled a few commits from the master branch to fix bnc#679716 and a
  more general problem we had with uploading config files without a
  SELinux context

-------------------------------------------------------------------
Thu Mar 17 15:35:32 CET 2011 - mc@suse.de

- debrand rhncfg-manager (bnc#678489)

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

- backport upstrem fixes 

-------------------------------------------------------------------
Mon Jan 17 13:44:41 CET 2011 - mantel@suse.de

- add mgr-* symlinks (BNC #660789)

-------------------------------------------------------------------
Wed Nov  3 09:49:28 CET 2010 - mantel@suse.de

- fix %dir in filelist

-------------------------------------------------------------------
Tue Nov  2 17:30:40 CET 2010 - mantel@suse.de

- don't ignore post-build-checks

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

- fix post-build-checks

-------------------------------------------------------------------
Tue Sep 14 17:47:16 CEST 2010 - mantel@suse.de

- Initial release of rhncfg

-------------------------------------------------------------------
07070100000047000081B40000000000000000000000015D403FC800003BF2000000000000000000000000000000000000001500000000mgr-cfg/mgr-cfg.spec#
# spec file for package mgr-cfg
#
# 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/
#


# package renaming fun :(
%define rhn_client_tools spacewalk-client-tools
%define rhn_setup	 spacewalk-client-setup
%define rhn_check	 spacewalk-check
%define rhnsd		 spacewalksd
# Old name and version+1 before renaming to mgr-cfg
%define oldname          rhncfg
%define oldversion       5.10.123
#
%global rhnroot %{_datadir}/rhn
%global rhnconf %{_sysconfdir}/sysconfig/rhn
%global client_caps_dir %{rhnconf}/clientCaps.d

%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} || 0%{?ubuntu} || 0%{?debian}
%global build_py2   1
%endif

# ------------------------------- Python macros for debian ----------------------------------------
%{!?__python2:%global __python2 /usr/bin/python2}
%{!?__python3:%global __python3 /usr/bin/python3}

%if %{undefined python2_version}
%global python2_version %(%{__python2} -Esc "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))")
%endif

%if %{undefined python3_version}
%global python3_version %(%{__python3} -Ic "import sys; sys.stdout.write(sys.version[:3])")
%endif

%if %{undefined python2_sitelib}
%global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
%endif

%if %{undefined python3_sitelib}
%global python3_sitelib %(%{__python3} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
%endif
# --------------------------- End Python macros for debian ----------------------------------------

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

%if %{_vendor} == "debbuild"
# Bash constructs in scriptlets don't play nice with Debian's default shell, dash
%global _buildshell /bin/bash
%endif

Name:           mgr-cfg
Version:        4.0.9
Provides:       %{oldname} = %{oldversion}
Obsoletes:      %{oldname} < %{oldversion}
Release:        1%{?dist}
Summary:        Spacewalk Configuration Client Libraries
License:        GPL-2.0-only
%if %{_vendor} == "debbuild"
Group:      admin
Packager:   Uyuni Project <uyuni-devel@opensuse.org>
%else
Group:          Applications/System
%endif
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
BuildRequires:  docbook-utils
Requires:       %{pythonX}-%{name} = %{version}-%{release}

%if %{_vendor} != "debbuild"
%if 0%{?suse_version}
# provide rhn directories and no selinux on suse
BuildRequires:  spacewalk-client-tools
%if %{suse_version} >= 1110
# Only on SLES11
Requires:       python-selinux
%endif
%else
Requires:       libselinux-python
%endif
%endif

%if %{_vendor} == "debbuild"
%if 0%{?build_py2}
Requires: python-selinux
%endif
%if 0%{?build_py3}
Requires: python3-selinux
%endif
%endif


%description
The base libraries and functions needed by all mgr-cfg-* packages.

%if 0%{?build_py2}
%package -n python2-%{name}
Summary:        Spacewalk Configuration Client Libraries
Group:          Applications/System
Provides:       python-%{name} = %{oldversion}
Obsoletes:      python-%{name} < %{oldversion}
Provides:       python-%{oldname} = %{oldversion}
Obsoletes:      python-%{oldname} < %{oldversion}
Requires:       %{name} = %{version}-%{release}
Requires:       python
Requires:       python2-rhn-client-tools >= 2.8.4
Requires:       rhnlib >= 2.8.3
Requires:       spacewalk-usix
%if 0%{?rhel} && 0%{?rhel} <= 5
Requires:       python-hashlib
%endif
BuildRequires:  python

%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif

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

%if 0%{?build_py3}
%package -n python3-%{name}
Summary:        Spacewalk Configuration Client Libraries
Group:          Applications/System
Requires:       %{name} = %{version}-%{release}
Provides:       python3-%{oldname} = %{oldversion}
Obsoletes:      python3-%{oldname} < %{oldversion}
Requires:       python3
Requires:       python3-rhn-client-tools >= 2.8.4
Requires:       python3-rhnlib >= 2.8.3
Requires:       python3-spacewalk-usix
BuildRequires:  python3
%if %{_vendor} != "debbuild"
BuildRequires:  python3-rpm-macros
%endif
%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif

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

%package client
Summary:        Spacewalk Configuration Client
Group:          Applications/System
Provides:       %{oldname}-client = %{oldversion}
Obsoletes:      %{oldname}-client < %{oldversion}
Requires:       %{name} = %{version}-%{release}
Requires:       %{pythonX}-%{name}-client = %{version}-%{release}

%description client
A command line interface to the client features of the Spacewalk Configuration
Management system.

%if 0%{?build_py2}
%package -n python2-%{name}-client
Summary:        Spacewalk Configuration Client
Group:          Applications/System
Provides:       python-%{name}-client = %{oldversion}
Obsoletes:      python-%{name}-client < %{oldversion}
Provides:       python-%{oldname}-client = %{oldversion}
Obsoletes:      python-%{oldname}-client < %{oldversion}
Requires:       %{name}-client = %{version}-%{release}
%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif

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

%if 0%{?build_py3}
%package -n python3-%{name}-client
Summary:        Spacewalk Configuration Client
Group:          Applications/System
Provides:       python3-%{oldname}-client = %{oldversion}
Obsoletes:      python3-%{oldname}-client < %{oldversion}
Requires:       %{name}-client = %{version}-%{release}
%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif

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

%package management
Summary:        Spacewalk Configuration Management Client
Group:          Applications/System
Provides:       %{oldname}-management = %{oldversion}
Obsoletes:      %{oldname}-management < %{oldversion}
Requires:       %{name} = %{version}-%{release}
Requires:       %{pythonX}-%{name}-management = %{version}-%{release}

%description management
A command line interface used to manage Spacewalk configuration.

%if 0%{?build_py2}
%package -n python2-%{name}-management
Summary:        Spacewalk Configuration Management Client
Group:          Applications/System
Provides:       python-%{name}-management = %{oldversion}
Obsoletes:      python-%{name}-management < %{oldversion}
Provides:       python-%{oldname}-management = %{oldversion}
Obsoletes:      python-%{oldname}-management < %{oldversion}
Requires:       %{name}-management = %{version}-%{release}
%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif

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

%if 0%{?build_py3}
%package -n python3-%{name}-management
Summary:        Spacewalk Configuration Management Client
Group:          Applications/System
Provides:       python3-%{oldname}-management = %{oldversion}
Obsoletes:      python3-%{oldname}-management < %{oldversion}
Requires:       %{name}-management = %{version}-%{release}
%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif

%description -n python3-%{name}-management
Python 2 specific files for python3-%{name}-management.
%endif

%package actions
Summary:        Spacewalk Configuration Client Actions
Group:          Applications/System
Provides:       %{oldname}-actions = %{oldversion}
Obsoletes:      %{oldname}-actions < %{oldversion}
Requires:       %{name} = %{version}-%{release}
Requires:       %{pythonX}-%{name}-actions = %{version}-%{release}

%description actions
The code required to run configuration actions scheduled via Spacewalk.

%if 0%{?build_py2}
%package -n python2-%{name}-actions
Summary:        Spacewalk Configuration Client Actions
Group:          Applications/System
Provides:       python-%{name}-actions = %{oldversion}
Obsoletes:      python-%{name}-actions < %{oldversion}
Provides:       python-%{oldname}-actions = %{oldversion}
Obsoletes:      python-%{oldname}-actions < %{oldversion}
Requires:       %{name}-actions = %{version}-%{release}
Requires:       python2-%{name}-client
%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif

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

%if 0%{?build_py3}
%package -n python3-%{name}-actions
Summary:        Spacewalk Configuration Client Actions
Group:          Applications/System
Provides:       python3-%{oldname}-actions = %{oldversion}
Obsoletes:      python3-%{oldname}-actions < %{oldversion}
Requires:       %{name}-actions = %{version}-%{release}
Requires:       python3-%{name}-client
%if %{_vendor} == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif

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

%prep
%setup -q

%build
make -f Makefile.rhncfg all

%install
install -d $RPM_BUILD_ROOT/%{python_sitelib}
%if 0%{?build_py2}
make -f Makefile.rhncfg install PREFIX=$RPM_BUILD_ROOT ROOT=%{python_sitelib} \
    MANDIR=%{_mandir} PYTHONVERSION=%{python_version}
%endif
%if 0%{?build_py3}
    install -d $RPM_BUILD_ROOT/%{python3_sitelib}
    sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|' config_*/*.py actions/*.py
    make -f Makefile.rhncfg install PREFIX=$RPM_BUILD_ROOT ROOT=%{python3_sitelib} \
        MANDIR=%{_mandir} PYTHONVERSION=%{python3_version}
%endif
mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/spool/rhn
mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/log
touch $RPM_BUILD_ROOT/%{_localstatedir}/log/rhncfg-actions

# create links to default script version
%define default_suffix %{?default_py3:-%{python3_version}}%{!?default_py3:-%{python_version}}
for i in \
    /usr/bin/rhncfg-client \
    /usr/bin/rhncfg-manager \
    /usr/bin/rhn-actions-control \
; do
    ln -s $(basename "$i")%{default_suffix} "$RPM_BUILD_ROOT$i"
done

ln -s rhncfg-manager $RPM_BUILD_ROOT/%{_bindir}/mgrcfg-manager
ln -s rhncfg-client $RPM_BUILD_ROOT/%{_bindir}/mgrcfg-client
ln -s rhn-actions-control $RPM_BUILD_ROOT/%{_bindir}/mgr-actions-control

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

%post
if [ -f %{_localstatedir}/log/rhncfg-actions ]
then 
chown root %{_localstatedir}/log/rhncfg-actions
chmod 600 %{_localstatedir}/log/rhncfg-actions
fi

%if %{_vendor} == "debbuild"
# Debian requires:
# post: Do bytecompilation after install
# preun: Remove any *.py[co] files

%if 0%{?build_py2}
%post -n python2-%{name}
pycompile python2-%{name} -V -3.0

%preun -n python2-%{name}
pyclean -p python2-%{name}

%post -n python2-%{name}-client
pycompile python2-%{name}-client -V -3.0

%preun -n python2-%{name}-client
pyclean -p python2-%{name}-client

%post -n python2-%{name}-management
pycompile python2-%{name}-management -V -3.0

%preun -n python2-%{name}-management
pyclean -p python2-%{name}-management

%post -n python2-%{name}-actions
pycompile python2-%{name}-actions -V -3.0

%preun -n python2-%{name}-actions
pyclean -p python2-%{name}-actions
%endif

%if 0%{?build_py3}
%post -n python3-%{name}
py3compile python3-%{name} -V -4.0

%preun -n python3-%{name}
py3clean -p python3-%{name}

%post -n python3-%{name}-client
py3compile python3-%{name}-client -V -4.0

%preun -n python3-%{name}-client
py3clean -p python3-%{name}-client

%post -n python3-%{name}-management
py3compile python3-%{name}-management -V -4.0

%preun -n python3-%{name}-management
py3clean -p python3-%{name}-management

%post -n python3-%{name}-actions
py3compile python3-%{name}-actions -V -4.0

%preun -n python3-%{name}-actions
py3clean -p python3-%{name}-actions
%endif
%endif


%files
%defattr(-,root,root,-)
%dir %{_localstatedir}/spool/rhn
%doc LICENSE

%if 0%{?build_py2}
%files -n python2-%{name}
%defattr(-,root,root,-)
%{python_sitelib}/config_common
%endif

%if 0%{?build_py3}
%files -n python3-%{name}
%defattr(-,root,root,-)
%{python3_sitelib}/config_common
%endif

%files client
%defattr(-,root,root,-)
%{_bindir}/rhncfg-client
%{_bindir}/mgrcfg-client
%attr(644,root,root) %config(noreplace) %{rhnconf}/rhncfg-client.conf
%{_mandir}/man8/rhncfg-client.8*

%if 0%{?build_py2}
%files -n python2-%{name}-client
%defattr(-,root,root,-)
%{python_sitelib}/config_client
%{_bindir}/rhncfg-client-%{python_version}
%endif

%if 0%{?build_py3}
%files -n python3-%{name}-client
%defattr(-,root,root,-)
%{python3_sitelib}/config_client
%{_bindir}/rhncfg-client-%{python3_version}
%endif

%files management
%defattr(-,root,root,-)
%{_bindir}/mgrcfg-manager
%{_bindir}/rhncfg-manager
%attr(644,root,root) %config(noreplace) %{rhnconf}/rhncfg-manager.conf
%{_mandir}/man8/rhncfg-manager.8*

%if 0%{?build_py2}
%files -n python2-%{name}-management
%defattr(-,root,root,-)
%{python_sitelib}/config_management
%{_bindir}/rhncfg-manager-%{python_version}
%endif

%if 0%{?build_py3}
%files -n python3-%{name}-management
%defattr(-,root,root,-)
%{python3_sitelib}/config_management
%{_bindir}/rhncfg-manager-%{python3_version}
%endif

%files actions
%defattr(-,root,root,-)
%{_bindir}/mgr-actions-control
%{_bindir}/rhn-actions-control
%config(noreplace) %{client_caps_dir}/*
%{_mandir}/man8/rhn-actions-control.8*
%ghost %attr(600,root,root) %{_localstatedir}/log/rhncfg-actions

%if 0%{?build_py2}
%files -n python2-%{name}-actions
%defattr(-,root,root,-)
%dir %{python_sitelib}/rhn
%{python_sitelib}/rhn/actions
%{_bindir}/rhn-actions-control-%{python_version}
%if 0%{?suse_version}
%dir %{python_sitelib}/rhn
%endif
%endif

%if 0%{?build_py3}
%files -n python3-%{name}-actions
%defattr(-,root,root,-)
%dir %{python3_sitelib}/rhn
%{python3_sitelib}/rhn/actions
%{_bindir}/rhn-actions-control-%{python3_version}
%if 0%{?suse_version}
%dir %{python3_sitelib}/rhn
%endif
%endif

%changelog
07070100000048000081B40000000000000000000000015D403FC800001395000000000000000000000000000000000000001100000000mgr-cfg/pylintrc# mgr-cfg 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=
07070100000049000041FD0000000000000000000000015D403FC800000000000000000000000000000000000000000000001000000000mgr-cfg/solaris0707010000004A000081FD0000000000000000000000015D403FC80000073B000000000000000000000000000000000000002100000000mgr-cfg/solaris/build-solaris.sh#!/usr/bin/bash

# clean up junk...
make clean
rm Prototype

rm -rf /tmp/RHATrncfg*
rm -rf /test-bretm/build-target

# where you want stuff to land
DEST_PREFIX=/opt/redhat/rhn/solaris
export DEST_PREFIX

# think of this as the buildroot, should be some temp dir
BUILD_ROOT=/test-bretm/build-target
export BUILD_ROOT

# PREFIX used by the makefiles...
PREFIX="$BUILD_ROOT$DEST_PREFIX"
export PREFIX

INSTALL='/usr/ucb/install -c'
export INSTALL

LN='ln -sf'
export LN

rm -rf temp_src

mkdir temp_src
cp * temp_src/
cp -R actions temp_src/
cp -R config_* temp_src/
cd temp_src

perl -pi -e "s|/usr/bin/diff|$DEST_PREFIX/bin/diff|" \
    ./config_client/rhncfgcli_diff.py \
    ./config_common/file_utils.py \
    ./config_management/rhncfg_diff.py

perl -pi -e "s|/usr/share/rhn|$DEST_PREFIX/usr/share/rhn|" \
    ./config_client/* \
    ./config_management/*

perl -pi -e "s|/etc/sysconfig/rhn/allowed-actions|$DEST_PREFIX/etc/sysconfig/rhn/allowed-actions|" \
    ./actions/configfiles.py

perl -pi -e "s|/etc/sysconfig/rhn/systemid|$DEST_PREFIX/etc/sysconfig/rhn/systemid|" \
    ./config_client/*

perl -pi -e "s|/etc/sysconfig/rhn/up2date|$DEST_PREFIX/etc/sysconfig/rhn/up2date|" \
    ./config_common/up2date_config_parser.py

perl -pi -e "s|os.path.join\(os.sep|os.path.join\('$DEST_PREFIX'|" \
    ./config_common/local_config.py

perl -pi -e "s|^#!/usr/bin/python|#!$DEST_PREFIX/bin/python|" \
    ./config_client/rhncfg-client.py \
    ./config_management/rhncfg-manager.py

make
make install


echo "i pkginfo" > Prototype
#echo "i postinstall" >> Prototype
#echo "i checkinstall" >> Prototype

find $PREFIX -print | pkgproto | sed "s#$PREFIX#$DEST_PREFIX#g" >> Prototype

pkgmk -o -r $BUILD_ROOT -d /tmp -f Prototype

cd /tmp

# tar -cf - RHATrncfg | gzip -9 -c > RHATrncfg-1.0.sparc.pkg.tar.gz
pkgtrans -s /tmp RHATrncfg-1.0.pkg RHATrncfg
0707010000004B000081B40000000000000000000000015D403FC8000000A6000000000000000000000000000000000000001800000000mgr-cfg/solaris/pkginfoPKG="RHATrncfg"
NAME="Spacewalk configuration client"
VERSION="1.0"
ARCH="sparc"
CLASSES="none"
CATEGORY="utility"
VENDOR="RHAT"
EMAIL="bretm@redhat.com"
BASEDIR="/"
0707010000004C000041FD0000000000000000000000015D403FC800000000000000000000000000000000000000000000000D00000000mgr-cfg/test0707010000004D000081B40000000000000000000000015D403FC8000009D9000000000000000000000000000000000000002300000000mgr-cfg/test/test_action_deploy.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 fnmatch

from actions import configfiles
from config_common import local_config, rhn_log

local_config.init("rhncfg")
rhn_log.set_debug_level(local_config.get('debug'))

def test(msg, test):
    ok_str = 'NOT OK'
    if test:
        ok_str = 'ok'

    print("%s: %s" % (msg, ok_str))

def stray_files(path):
    (directory, filename) = os.path.split(path)
    for path in os.listdir(directory):
        if fnmatch.fnmatch(path, "%s*" % filename):
            return 1
    return 0

files = { 'files' : [
    {
        'path'          : "/etc/googah",
        'namespace'     : "foo1",
        'file_contents' : "This is 1 and this is 2\nAnd this is 3",
        'md5sum'        : "0abcabcabcabc",
        'delim_start'   : "[|",
        'delim_end'     : "|]",
        },
    {
        'path'          : "/var/tmp/ggg",
        'namespace'     : "foo1",
        'file_contents' : "That is 1 and this is 2\nAnd this is 3",
        'md5sum'        : "0abcabcabcabc",
        'delim_start'   : "[|",
        'delim_end'     : "|]",
        },
    ]}

try:
    for file in files['files']:
        os.unlink(file['path'])

except OSError:
    pass

test("testing a clean new deployment",
     configfiles.deploy(files)[0] == 0)


file = files['files'][0]['path']
os.system("chattr +i %s" % file)
test("testing deployment over read-only file (no files deployed, remove backups)",
     configfiles.deploy(files)[0] == 43)
os.system("chattr -i %s" % file)


test("testing redeployment w/ backups",
     configfiles.deploy(files)[0] == 0)


file = files['files'][1]['path']
os.system("chattr +i %s" % file)
test("testing deployment over read-only file (partial deployment, restore from then remove backups)",
     configfiles.deploy(files)[0] == 43 and os.path.exists(file))
os.system("chattr -i %s" % file)


test("testing redeployment w/ backups (again)",
     configfiles.deploy(files)[0] == 0)

0707010000004E000081B40000000000000000000000015D403FC8000005D8000000000000000000000000000000000000002100000000mgr-cfg/test/test_action_diff.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.
#

from actions import configfiles

print(configfiles.diff([
    {
        'path'          : "/etc/googah",
        'namespace'     : "foo1",
        'file_contents' : "This is 1 and this is 2\nAnd this is 3",
        'md5sum'        : "0abcabcabcabc",
        'delim_start'   : "[|",
        'delim_end'     : "|]",
    },
    {
        'path'          : "/var/tmp/ggg",
        'namespace'     : "foo1",
        'file_contents' : "That is 1 and this is 2\nAnd this is 3",
        'md5sum'        : "0abcabcabcabc",
        'delim_start'   : "[|",
        'delim_end'     : "|]",
    },
    {
        'path'          : "/etc/issue",
        'namespace'     : "foo1",
        'file_contents' : "Red Hat Linux release 7.3 Evaluation (Valhalla)\nKernel \\r on an \\m",
        'md5sum'        : "0abcabcabcabc",
        'delim_start'   : "[|",
        'delim_end'     : "|]",
    },
]))
0707010000004F000081B40000000000000000000000015D403FC800000392000000000000000000000000000000000000002300000000mgr-cfg/test/test_action_script.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.
#

from actions import script

print(script.run(1, {
    'username'  : 'misa',
    'groupname' : 'misa',
    'now'       : '2004-02-20 09:00:00',
    'script'    : """\
#!/usr/bin/python

import sys
import time
sys.stdout.write("1\\n")
sys.stderr.write("2\\n")
time.sleep(10)
""",
}))
07070100000050000081B40000000000000000000000015D403FC8000002EE000000000000000000000000000000000000002300000000mgr-cfg/test/test_action_upload.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.
#

from actions import configfiles
from config_common import local_config

local_config.init('rhncfgcli')
print(configfiles.upload("foo"))
07070100000051000081B40000000000000000000000015D403FC8000003D0000000000000000000000000000000000000001E00000000mgr-cfg/test/test_maketemp.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
from config_common import file_utils

filepath, fd = file_utils.maketemp("/tmp/my-file-", mode=int("0700", 8))

buf = "0123456789" * 100

print("Writing", len(buf), "to", filepath)
os.write(fd, buf)
assert(len(buf) == f.tell())

os.close(fd)
os.unlink(filepath)


filepath, fd = file_utils.maketemp()
os.close(fd)
os.unlink(filepath)
07070100000052000081B40000000000000000000000015D403FC8000006E3000000000000000000000000000000000000001C00000000mgr-cfg/test/test_upload.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.
#

from rhn import rpclib

#systemid_file = '/home/devel/misa/playpen/systemid-devel'
systemid_file = '/home/devel/misa/cvs/rhn/test/backend/checks/systemid-farm06'
server = "coyote.devel.redhat.com"
#server = "rhnxml.back-webdev.redhat.com"

s = rpclib.Server("http://%s/CONFIG-MANAGEMENT" % server)

files = [
    {
       'path'           : '/etc/motd',
       'file_contents'  : 'This system will not work today\nCause I like it so\n',
       'delim_start'    : '{|',
       'delim_end'      : '|}',
       'file_stat'      : {
            'size'  : 1234,
            'mode'  : int("0755", 8),
            'user'  : 'misa',
            'group' : 'misa',
       },
    },
    {
       'path'           : '/etc/voodoo',
       'file_contents'  : 'If you read this file, your computer will reboot',
       'delim_start'    : '{|',
       'delim_end'      : '|}',
       'file_stat'      : {
            'size'  : 1234,
            'mode'  : int("0755", 8),
            'user'  : 'misa',
            'group' : 'misa',
       },
    },
]


systemid = open(systemid_file).read()

s.config.client.upload_files(systemid, 11921200, files)
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
openSUSE Build Service is sponsored by