Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP2:GA
uyuni-common-libs.34325
uyuni-common-libs-git-0.4d1842e.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File uyuni-common-libs-git-0.4d1842e.obscpio of Package uyuni-common-libs.34325
07070100000000000041FD0000000000000000000000036667115D00000000000000000000000000000000000000000000001200000000uyuni-common-libs07070100000001000081B40000000000000000000000016667115D000046AC000000000000000000000000000000000000001A00000000uyuni-common-libs/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. 07070100000002000081B40000000000000000000000016667115D00000280000000000000000000000000000000000000002700000000uyuni-common-libs/Makefile.common-libs# Makefile for the common libs directory # SPACEWALK_FILES = __init__ CODE_DIRS = common PYLINT_DIRS = common CONF_DIRS = # We look for config files in "well known" locations (rhn-conf, # httpd-conf, logrotate) EXTRA_DIRS = all :: all-code all-conf %-code : Makefile.common-libs @$(foreach d,$(CODE_DIRS), $(MAKE) -C $(d) $* || exit 1; ) %-conf : Makefile.common-libs @$(foreach d,$(CONF_DIRS), $(MAKE) -C $(d) $* || exit 1; ) # now include some Macros include Makefile.defs install :: install-code install-conf clean :: clean-code clean-conf test :: mkdir -p $(CURDIR)/reports cd ..; $(PYTHON_BIN) -m pytest test/unit/uyuni/ 07070100000003000081B40000000000000000000000016667115D000006F6000000000000000000000000000000000000002000000000uyuni-common-libs/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 ifeq ("$(PYTHON_BIN)", "") PYTHON_BIN = "python" endif SPACEWALK_ROOT ?= $(shell $(PYTHON_BIN) -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")/uyuni export SPACEWALK_ROOT PREFIX ?= export PREFIX # Compilation stuff CC = gcc PYTHON_INCLUDE = -I/usr/include/python$(PythonVersion) CFLAGS = -Wall -O2 -fomit-frame-pointer $(PYTHON_INCLUDE) -fPIC SOFLAGS = -shared -fPIC # Installation stuff INSTALL = /usr/bin/install -c --verbose INSTALL_BIN = $(INSTALL) -m 755 INSTALL_DATA = $(INSTALL) -m 644 INSTALL_DIR = $(INSTALL) -m 755 -d # This is for the subdir part PYFILES = $(addsuffix .py,$(FILES)) SPACEWALK_PYFILES = $(addsuffix .py,$(SPACEWALK_FILES)) SPACEWALK_DEST ?= $(SPACEWALK_ROOT)/$(SUBDIR) # what do we need to install and where INSTALL_FILES += $(PYFILES) INSTALL_DEST ?= $(ROOT)/$(SUBDIR) SPACEWALK_INSTALL_FILES += $(SPACEWALK_PYFILES) DIRS += $(addprefix $(PREFIX), \ $(sort $(EXTRA_DIRS)) $(INSTALL_DEST) $(SPACEWALK_ROOT) $(SPACEWALK_DEST)) all :: $(INSTALL_FILES) install :: all $(DIRS) $(INSTALL_FILES) $(SPACEWALK_INSTALL_FILES) @$(foreach f,$(INSTALL_FILES), \ $(INSTALL_DATA) $(f) $(PREFIX)$(INSTALL_DEST)/$(f) ; ) @$(foreach f,$(SPACEWALK_INSTALL_FILES), \ $(INSTALL_DATA) $(f) $(PREFIX)$(SPACEWALK_DEST)/$(f) ; ) $(DIRS): $(INSTALL_DIR) $@ clean :: @rm -fv *~ *.pyc *.pyo .??*~ @rm -fv .\#* @rm -fv core # useful macro descend-subdirs = @$(foreach d,$(SUBDIRS), $(MAKE) -C $(d) $@ || exit 1; ) # subdirs are treated at the end all install clean:: $(SUBDIRS) $(descend-subdirs) 07070100000004000081B40000000000000000000000016667115D000001A4000000000000000000000000000000000000001E00000000uyuni-common-libs/__init__.py# # Copyright (c) 2019 SUSE LLC # # 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. # 07070100000005000041FD0000000000000000000000026667115D00000000000000000000000000000000000000000000001900000000uyuni-common-libs/common07070100000006000081B40000000000000000000000016667115D000002F2000000000000000000000000000000000000002200000000uyuni-common-libs/common/Makefile# Makefile for spacewalk backend # # what is the backend top dir TOP = .. # Specific stuff SUBDIR = common SPACEWALK_FILES = __init__ \ checksum \ cli \ context_managers \ fileutils \ rhnLib \ rhn_deb \ rhn_mpm \ rhn_pkg \ rhn_rpm \ timezone_utils \ usix SCRIPTS = # check if we can build man pages DOCBOOK = $(wildcard /usr/bin/docbook2man) SGMLS = $(wildcard *.sgml) MANS = $(patsubst %.sgml,%.8,$(SGMLS)) MANDIR ?= /usr/man EXTRA_DIRS = include $(TOP)/Makefile.defs # install scripts ifneq ($(DOCBOOK),) # install man pages all :: $(MANS) install :: $(MANS) $(PREFIX)/$(MANDIR) $(INSTALL_DATA) $(MANS) $(PREFIX)/$(MANDIR)/man8 endif %.8 : %.sgml $(DOCBOOK) $< clean :: @rm -fv $(MANS) manpage.* 07070100000007000081B40000000000000000000000016667115D000001D2000000000000000000000000000000000000002500000000uyuni-common-libs/common/__init__.py# # Copyright (c) 2019 SUSE LLC # # 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. # # Initialization file for the common module # 07070100000008000081B40000000000000000000000016667115D00000C86000000000000000000000000000000000000002500000000uyuni-common-libs/common/checksum.py# pylint: disable=missing-module-docstring # # Copyright (c) 2009--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 try: import hashlib import inspect hashlib_has_usedforsecurity = ( "usedforsecurity" in inspect.getargspec(hashlib.new)[0] ) except ImportError: import md5 import sha # pylint: disable=F0401 # pylint can't find Crypto.Hash here, but it is present on older systems. from Crypto.Hash import SHA256 as sha256 hashlib_has_usedforsecurity = False # pylint: disable-next=invalid-name class hashlib(object): @staticmethod def new(checksum): if checksum == "md5": return md5.new() elif checksum == "sha1": return sha.new() elif checksum == "sha256": return sha256.new() else: raise ValueError("Incompatible checksum type") # pylint: disable-next=invalid-name def getHashlibInstance(hash_type, used_for_security): """Get an instance of a hashlib object.""" if hashlib_has_usedforsecurity: return hashlib.new(hash_type, usedforsecurity=used_for_security) else: return hashlib.new(hash_type) # pylint: disable-next=invalid-name def getFileChecksum( hashtype, filename=None, fd=None, file_obj=None, buffer_size=None, used_for_security=False, ): """Compute a file's checksum Used by rotateFile() """ # python's md5 lib sucks # there's no way to directly import a file. if buffer_size is None: buffer_size = 65536 if hashtype == "sha": hashtype = "sha1" if filename is None and fd is None and file_obj is None: raise ValueError("no file specified") if file_obj: f = file_obj elif fd is not None: f = os.fdopen(os.dup(fd), "rb") else: f = open(filename, "rb") # Rewind it f.seek(0, 0) m = getHashlibInstance(hashtype, used_for_security) while 1: try: buf = f.read(buffer_size) except: # pylint: disable=W0702,W0703 # No need to know exact root cause of the exception. # Will produce checksum other than expected for such case. break if not buf: break m.update(buf) # cleanup time if file_obj is not None: file_obj.seek(0, 0) else: f.close() return m.hexdigest() # pylint: disable-next=invalid-name def getStringChecksum(hashtype, s): """compute checksum of an arbitrary string""" h = getHashlibInstance(hashtype, False) h.update(s) return h.hexdigest() 07070100000009000081B40000000000000000000000016667115D00000A37000000000000000000000000000000000000002000000000uyuni-common-libs/common/cli.py# pylint: disable=missing-module-docstring # # Copyright (c) 2012--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 import io try: # python 2 import xmlrpclib except ImportError: # python3 import xmlrpc.client as xmlrpclib # pylint: disable=F0401 # pylint: disable-next=invalid-name def getUsernamePassword(cmdlineUsername, cmdlinePassword): """ Returns a username and password (either by returning the ones passed as args, or the user's input """ if cmdlineUsername and cmdlinePassword: return cmdlineUsername, cmdlinePassword username = cmdlineUsername password = cmdlinePassword # Read the username, if not already specified tty = io.TextIOWrapper(open("/dev/tty", "r+b", buffering=0)) while not username: tty.write("SUSE Manager username: ") try: username = tty.readline() except KeyboardInterrupt: tty.write("\n") sys.exit(0) if username is None: # EOF tty.write("\n") sys.exit(0) username = username.strip() if username: break # Now read the password while not password: try: password = getpass.getpass("SUSE Manager password: ") except KeyboardInterrupt: tty.write("\n") sys.exit(0) tty.close() return username, password def xmlrpc_login(client, username, password, verbose=0): """ Authenticate Session call """ if verbose: print("...logging in to server...") try: sessionkey = client.auth.login(username, password) except xmlrpclib.Fault: e = sys.exc_info()[1] # pylint: disable-next=consider-using-f-string sys.stderr.write("Error: %s\n" % e.faultString) sys.exit(-1) return sessionkey def xmlrpc_logout(client, session_key, verbose=0): """ End Authentication call """ if verbose: print("...logging out of server...") client.auth.logout(session_key) 0707010000000A000081B40000000000000000000000016667115D000001D3000000000000000000000000000000000000002D00000000uyuni-common-libs/common/context_managers.py"""Collection of context managers for Uyuni.""" # This module always had a dependency on spacewalk.common.rhnConfig, which is wrong. # Everything in uyuni.common should work on clients as well. When we add other context # managers (that are available on clients), this import should be guarded. # Re-export to allow users to keep their import on uyuni.common.context_managers # pylint: disable-next=unused-import from spacewalk.common.rhnConfig import cfg_component 0707010000000B000081B40000000000000000000000016667115D00004147000000000000000000000000000000000000002600000000uyuni-common-libs/common/fileutils.py""" Utility library for working with files, paths, and more. Part of uyuni.common and available on Uyuni servers and its clients. """ # # 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 codecs import os import sys import bz2 import gzip import pwd import grp import shutil import subprocess import select import stat import tempfile import io from uyuni.common.checksum import getFileChecksum from uyuni.common.usix import ListType, TupleType, MaxInt try: import lzma HAS_LZMA = True except ImportError: HAS_LZMA = False # pylint: disable-next=invalid-name def cleanupAbsPath(path): """take ~taw/../some/path/$MOUNT_POINT/blah and make it sensible. Path returned is absolute. NOTE: python 2.2 fixes a number of bugs with this and eliminates the need for os.path.expanduser """ if path is None: return None return os.path.abspath(os.path.expanduser(os.path.expandvars(path))) # pylint: disable-next=invalid-name def cleanupNormPath(path, dotYN=0): """take ~taw/../some/path/$MOUNT_POINT/blah and make it sensible. Returned path may be relative. NOTE: python 2.2 fixes a number of bugs with this and eliminates the need for os.path.expanduser """ if path is None: return None path = os.path.normpath(os.path.expanduser(os.path.expandvars(path))) if dotYN and not (path and path[0] == "/"): dirs = path.split("/") if dirs[:1] not in (["."], [".."]): dirs = ["."] + dirs path = "/".join(dirs) return path # pylint: disable-next=invalid-name def rotateFile(filepath, depth=5, suffix=".", verbosity=0): """backup/rotate a file depth (-1==no limit) refers to num. of backups (rotations) to keep. Behavior: (1) x.txt (current) x.txt.1 (old) x.txt.2 (older) x.txt.3 (oldest) (2) all file stats preserved. Doesn't blow away original file. (3) if x.txt and x.txt.1 are identical (size or checksum), None is returned """ # check argument sanity (should really be down outside of this function) if not filepath or not isinstance(filepath, type("")): # pylint: disable-next=consider-using-f-string raise ValueError("filepath '%s' is not a valid arguement" % filepath) if not isinstance(depth, type(0)) or depth < -1 or depth > MaxInt - 1 or depth == 0: # pylint: disable-next=consider-using-f-string raise ValueError("depth must fall within range " "[-1, 1...%s]" % (MaxInt - 1)) # force verbosity to be a numeric value verbosity = verbosity or 0 if not isinstance(verbosity, type(0)) or verbosity < -1 or verbosity > MaxInt - 1: # pylint: disable-next=consider-using-f-string raise ValueError("invalid verbosity value: %s" % (verbosity)) filepath = cleanupAbsPath(filepath) if not os.path.isfile(filepath): # pylint: disable-next=consider-using-f-string raise ValueError("filepath '%s' does not lead to a file" % filepath) # pylint: disable-next=invalid-name pathNSuffix = filepath + suffix # pylint: disable-next=invalid-name pathNSuffix1 = pathNSuffix + "1" if verbosity > 1: # pylint: disable-next=consider-using-f-string sys.stderr.write("Working dir: %s\n" % os.path.dirname(pathNSuffix)) # is there anything to do? (existence, then size, then checksum) checksum_type = "sha1" if ( os.path.exists(pathNSuffix1) and os.path.isfile(pathNSuffix1) and os.stat(filepath)[6] == os.stat(pathNSuffix1)[6] and getFileChecksum(checksum_type, filepath) == getFileChecksum(checksum_type, pathNSuffix1) ): # nothing to do if verbosity: sys.stderr.write( # pylint: disable-next=consider-using-f-string "File '%s' is identical to its rotation. " "Nothing to do.\n" % os.path.basename(filepath) ) return None # find last in series (of rotations): last = 0 # pylint: disable-next=consider-using-f-string while os.path.exists("%s%d" % (pathNSuffix, last + 1)): last = last + 1 # percolate renames: for i in range(last, 0, -1): # pylint: disable-next=consider-using-f-string os.rename("%s%d" % (pathNSuffix, i), "%s%d" % (pathNSuffix, i + 1)) if verbosity > 1: filename = os.path.basename(pathNSuffix) sys.stderr.write( # pylint: disable-next=consider-using-f-string "Moving file: %s%d --> %s%d\n" % (filename, i, filename, i + 1) ) # blow away excess rotations: if depth != -1: last = last + 1 for i in range(depth + 1, last + 1): # pylint: disable-next=consider-using-f-string path = "%s%d" % (pathNSuffix, i) os.unlink(path) if verbosity: # pylint: disable-next=consider-using-f-string sys.stderr.write("Rotated out: '%s'\n" % (os.path.basename(path))) # do the actual rotation shutil.copy2(filepath, pathNSuffix1) if os.path.exists(pathNSuffix1) and verbosity: sys.stderr.write( # pylint: disable-next=consider-using-f-string "Backup made: '%s' --> '%s'\n" % (os.path.basename(filepath), os.path.basename(pathNSuffix1)) ) # return the full filepath of the backed up file return pathNSuffix1 # pylint: disable-next=invalid-name def rhn_popen(cmd, progressCallback=None, bufferSize=16384, outputLog=None): """popen-like function, that accepts execvp-style arguments too (i.e. an array of params, thus making shell escaping unnecessary) cmd can be either a string (like "ls -l /dev"), or an array of arguments ["ls", "-l", "/dev"] Returns the command's error code, a stream with stdout's contents and a stream with stderr's contents progressCallback --> progress bar twiddler outputLog --> optional log file file object write method """ cmd_is_list = isinstance(cmd, (ListType, TupleType)) if cmd_is_list: cmd = list(map(str, cmd)) # pylint: disable=E1101 c = subprocess.Popen( cmd, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=(not cmd_is_list), ) # We don't write to the child process c.stdin.close() # Create two temporary streams to hold the info from stdout and stderr child_out = tempfile.TemporaryFile(prefix="/tmp/my-popen-", mode="r+b") child_err = tempfile.TemporaryFile(prefix="/tmp/my-popen-", mode="r+b") # Map the input file descriptor with the temporary (output) one fd_mappings = [(c.stdout, child_out), (c.stderr, child_err)] exitcode = None count = 1 while 1: # Is the child process done? status = c.poll() if status is not None: if status >= 0: # Save the exit code, we still have to read from the pipes exitcode = status else: # Some signal sent to this process if outputLog is not None: # pylint: disable-next=consider-using-f-string outputLog("rhn_popen: Signal %s received\n" % (-status)) exitcode = status break fd_set = [x[0] for x in fd_mappings] readfds = select.select(fd_set, [], [])[0] for in_fd, out_fd in fd_mappings: if in_fd in readfds: # There was activity on this file descriptor output = os.read(in_fd.fileno(), bufferSize) if output: # show progress if progressCallback: count = count + len(output) progressCallback(count) if outputLog is not None: outputLog(output) # write to the output buffer(s) out_fd.write(output) out_fd.flush() if exitcode is not None: # Child process is done break for f_in, f_out in fd_mappings: f_in.close() f_out.seek(0, 0) return exitcode, child_out, child_err def makedirs(path, mode=int("0755", 8), user=None, group=None): """Creates all required directories on a path and changes its owner and group :param path: path to create :type path: str :param mode: mode for the created directories :type mode: int :param user: desired owner :type user: str :param group: desired group :type group:str :returns: None """ dirs_to_create = [] dirname = path uid, gid = getUidGid(user, group) if uid is None: # pylint: disable-next=consider-using-f-string raise OSError("*** ERROR: user %s doesn't exist. Cannot create path." % user) if gid is None: # pylint: disable-next=consider-using-f-string raise OSError("*** ERROR: group %s doesn't exist. Cannot create path." % group) while 1: if os.path.isdir(dirname): # We're done with this step break # We have to create this directory dirs_to_create.append(dirname) dirname, last = os.path.split(dirname) if not last: # We reached the top directory break # Now create the directories while dirs_to_create: dirname = dirs_to_create.pop() try: os.mkdir(dirname, mode) except OSError: e = sys.exc_info()[1] if e.errno != 17: # File exists raise # Ignore the error try: os.chown(dirname, uid, gid) except OSError: # Changing permissions failed; ignore the error # pylint: disable-next=consider-using-f-string sys.stderr.write("Changing owner for %s failed\n" % dirname) class GecosCache: "Cache getpwnam() and getgrnam() calls" # pylint: disable-next=invalid-name __shared_data = {} def __init__(self): self.__dict__ = self.__shared_data if not list(self.__shared_data.keys()): # Not initialized self._users = {} self._groups = {} def getuid(self, name): "Return the UID of the user by name" if name in self._users: return self._users[name] try: uid = pwd.getpwnam(name)[2] except KeyError: return None self._users[name] = uid return uid def getgid(self, name): "Return the GID of the group by name" if name in self._groups: return self._groups[name] try: gid = grp.getgrnam(name)[2] except KeyError: return None self._groups[name] = gid return gid def reset(self): self.__shared_data.clear() # pylint: disable-next=unnecessary-dunder-call self.__init__() # pylint: disable-next=invalid-name def getUidGid(user=None, group=None): """Returns uid and gid of given user name and group name :param user: The name of the user :type user: str :param group: The name of the group :type group: str :returns: a tuple with the uid and the gid :rtype: tuple """ gc = GecosCache() if user: uid = gc.getuid(user) else: uid = os.getuid() if group: gid = gc.getgid(group) else: gid = os.getgid() return uid, gid # Duplicated in client/tools/rhncfg/config_common/file_utils.py to remove dependency # requirement. If making changes make them there too. FILETYPE2CHAR = { "file": "-", "directory": "d", "symlink": "l", "chardev": "c", "blockdev": "b", } # Duplicated in client/tools/rhncfg/config_common/file_utils.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 in client/tools/rhncfg/config_common/file_utils.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 in client/tools/rhncfg/config_common/file_utils.py to remove dependency # requirement. If making changes make them there too. # pylint: disable-next=invalid-name def f_date(dbiDate): # pylint: disable-next=consider-using-f-string return "%04d-%02d-%02d %02d:%02d:%02d" % ( dbiDate.year, dbiDate.month, dbiDate.day, dbiDate.hour, dbiDate.minute, dbiDate.second, ) # pylint: disable-next=invalid-name class payload: """this class implements simple file like object usable for reading payload from rpm, mpm, etc. it skips first 'skip' bytes of header """ def __init__(self, filename, skip=0): # pylint: disable-next=unspecified-encoding self.fileobj = open(filename, "r") self.skip = skip self.seek(0) def seek(self, offset, whence=0): if whence == 0: offset += self.skip return self.fileobj.seek(offset, whence) def tell(self): return self.fileobj.tell() - self.skip @staticmethod def truncate(size=-1): # pylint: disable=W0613 raise AttributeError("'Payload' object do not implement this method") @staticmethod # pylint: disable-next=invalid-name def write(_s): raise AttributeError("'Payload' object do not implement this method") @staticmethod # pylint: disable-next=invalid-name def writelines(_seq): raise AttributeError("'Payload' object do not implement this method") def __getattr__(self, x): return getattr(self.fileobj, x) def decompress_open(filename): file_obj = None if filename.endswith(".gz"): file_obj = gzip.open(filename, "rb") elif filename.endswith(".bz2"): file_obj = bz2.BZ2File(filename, "rb") elif filename.endswith(".xz"): if HAS_LZMA: file_obj = lzma.LZMAFile(filename, "rb") else: file_obj = subprocess.Popen( ["xz", "-d", "-k", filename], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, ).stdout elif filename.endswith(".zck"): file_obj = subprocess.Popen( ["unzck", "-c", filename], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL ).stdout elif filename.endswith(".zst"): file_obj = subprocess.Popen( ["zstd", "-d", "-c", filename], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, ).stdout else: file_obj = codecs.open(filename, "r", encoding="utf8") if filename.endswith((".gz", ".bz2", ".xz", ".zck")): return io.TextIOWrapper(file_obj, encoding="utf8") return file_obj 0707010000000C000081B40000000000000000000000016667115D00001ED2000000000000000000000000000000000000002300000000uyuni-common-libs/common/rhnLib.py# pylint: disable=missing-module-docstring,invalid-name # # 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 re import time from datetime import tzinfo, timedelta try: # python 2 import urlparse except ImportError: # python3 import urllib.parse as urlparse # pylint: disable=F0401,E0611 from uyuni.common import usix def setHeaderValue(mp_table, name, values): """ Function that correctly sets headers in an Apache-like table The values may be a string (which are set as for a dictionary), or an array. """ # mp_table is an Apache mp_table (like headers_in or headers_out) # Sets the header name to the values if isinstance(values, (usix.ListType, usix.TupleType)): for v in values: mp_table.add(name, str(v)) else: mp_table[name] = str(values) rfc822_days = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") rfc822_mons = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ) def rfc822time(arg): """ Return time as a string formatted such as: 'Wed, 23 Jun 2001 23:08:35 GMT'. We must not use locale-specific functions such as strftime here because the RFC explicitly requires C-locale only formatting. To satisfy this requirement, we declare our own days and months here and do the formatting manually. This function accepts a single argument. If it is a List or Tuple type, it is assumed to be of the form struct_time, as specified in the Python time module reference. If the argument is a float, it is expected to be the number of seconds from the epoch. NOTE: In all cases, the argument is assumed to be in local time. It will be translated to GMT in the return value. """ if isinstance(arg, (usix.ListType, usix.TupleType)): # Convert to float. arg = time.mktime(tuple(arg)) # Now, the arg must be a float. ( tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, # pylint: disable-next=unused-variable _tm_yday_, # pylint: disable-next=unused-variable _tm_isdst_, ) = time.gmtime(arg) # pylint: disable-next=consider-using-f-string return "%s, %02d %s %04d %02d:%02d:%02d %s" % ( rfc822_days[tm_wday], tm_mday, rfc822_mons[tm_mon - 1], tm_year, tm_hour, tm_min, tm_sec, "GMT", ) def timestamp(s): """ Converts the string in format YYYYMMDDHHMISS to seconds from the epoch """ if isinstance(s, (usix.IntType, usix.FloatType)): # Presumably already a timestamp return s if len(s) == 14: format_string = "%Y%m%d%H%M%S" elif len(s) == 19: format_string = "%Y-%m-%d %H:%M:%S" else: # pylint: disable-next=consider-using-f-string raise TypeError("String '%s' is not a YYYYMMDDHHMISS" % s) # Get the current DST setting timeval = list(time.strptime(s, format_string)) # No daylight information available timeval[8] = -1 return time.mktime(tuple(timeval)) def checkValue(val, *args): """A type/value checker Check value against the list of acceptable values / types """ for a in args: if isinstance(a, usix.TypeType): # Is val of type a? if isinstance(val, a): return 1 else: # This is an actual value we allow if val == a: return 1 return 0 def parseUrl(url): """urlparse is more complicated than what we need. We make the assumption that the URL has real URL information. NOTE: http/https ONLY for right now. The normal behavior of urlparse: - if no {http[s],file}:// then the string is considered everything that normally follows the URL, e.g. /XMLRPC - if {http[s],file}:// exists, anything between that and the next / is the URL. The behavior of *this* function: - if no {http[s],file}:// then the string is simply assumed to be a URL without the {http[s],file}:// attached. The parsed info is reparsed as one would think it would be: - returns: (addressing scheme, network location, path, parameters, query, fragment identifier). NOTE: netloc (or network location) can be HOSTNAME:PORT """ schemes = ("http", "https") if url is None: return None parsed = list(urlparse.urlparse(url)) if not parsed[0] or parsed[0] not in schemes: url = "https://" + url parsed = list(urlparse.urlparse(url)) parsed[0] = "" return tuple(parsed) def hash_object_id(object_id, factor): """Given an object id (assumed to be <label>-<number>), returns the last few digits for the number. For instance, (812345, 3) should return 345""" # Grab the digits after - num_id = object_id.split("-")[-1] # get last 'factor' numbers num_id = num_id[-factor:] return num_id.rjust(factor, "0") # reg exp for splitting rpm package names. re_rpmName = re.compile("^(.*)-([^-]*)-([^-]*)$") def parseRPMName(pkgName): """IN: Package string in, n-n-n-v.v.v-r.r_r, format. OUT: Four strings (in a tuple): name, epoch, version, release. """ reg = re_rpmName.match(pkgName) if reg is None: return None, None, None, None n, v, r = reg.group(1, 2, 3) e = None ind = r.find(":") if ind >= 0: # epoch found e = r[ind + 1 :] r = r[0:ind] return str(n), e, str(v), str(r) def parseDEBName(pkgName): """IN: Package string in, n-n_v.v-v.v-r.r, format. OUT: Four strings (in a tuple): name, epoch, version, release. """ if pkgName.find("_") == -1: return None, None, None, None e = None n, version = pkgName.split("_") if version.find(":") != -1: e, version = version.split(":") version_tmpArr = version.split("-") v = "-".join(version_tmpArr[:-1]) r = version_tmpArr[-1] return str(n), e, str(v), str(r) def isSUSE(): """Return true if this is a SUSE system, otherwise false""" if not os.path.exists("/etc/os-release"): return False cpe_name = "" try: # pylint: disable-next=unspecified-encoding lines = open("/etc/os-release", "r").readlines() for line in lines: # Skip empty and comment-only lines if re.match(r"[ \t]*(#|$)", line): continue # now split it into keys and values. We allow for max one # split/cut (the first one) (key, val) = [c.strip() for c in line.split("=", 1)] if key == "CPE_NAME": cpe_name = val break except (IOError, OSError): pass if "cpe:/o:opensuse:" in cpe_name or "cpe:/o:suse:" in cpe_name: return True return False class UTC(tzinfo): """Used for creating offset-aware datetime objects in Python 2.""" # pylint: disable=W0613 def utcoffset(self, dt): return timedelta(0) def tzname(self, dt): return "UTC" def dst(self, dt): return timedelta(0) utc = UTC() 0707010000000D000081B40000000000000000000000016667115D00001853000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_deb.py# pylint: disable=missing-module-docstring # # Copyright (c) 2010--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. # # # Meta-package manager # # Author: Lukas Durfina <lukas.durfina@gmail.com> import sys import tempfile from debian import debfile from debian.deb822 import Deb822 from uyuni.common.usix import raise_with_tb from uyuni.common import checksum from uyuni.common.rhn_pkg import A_Package, InvalidPackageError # bare-except and broad-except # pylint: disable=W0702,W0703 DEB_CHECKSUM_TYPE = "sha256" # FIXME: this should be a configuration option # pylint: disable-next=missing-class-docstring,invalid-name class deb_Header: # this is a workaround for issue in python-debian # https://www.mail-archive.com/pkg-python-debian-maint@alioth-lists.debian.net/msg00598.html # after the issue is fixed, remove this function def get_file(self, control, fname): if fname.startswith("./"): fname = fname[2:] elif fname.startswith("/"): fname = fname[1:] try: fobj = control.tgz().extractfile(fname) except KeyError: # pylint: disable-next=raise-missing-from raise debfile.DebError("control.tar.* not found inside package") if fobj is None: raise debfile.DebError("control.tar.* not found inside package") return fobj # pylint: disable-next=pointless-string-statement "Wrapper class for an deb header - we need to store a flag is_source" def __init__(self, stream): self.packaging = "deb" self.signatures = [] self.is_source = 0 self.deb = None try: self.deb = debfile.DebFile(stream.name) except Exception: e = sys.exc_info()[1] raise_with_tb(InvalidPackageError(e), sys.exc_info()[2]) try: # Fill info about package try: debcontrol = self.deb.debcontrol() except debfile.DebError: # this is a workaround for issue in python-debian # https://www.mail-archive.com/pkg-python-debian-maint@alioth-lists.debian.net/msg00598.html debcontrol = Deb822(self.get_file(self.deb.control, "control")) self.hdr = { "name": debcontrol.get_as_string("Package"), "arch": debcontrol.get_as_string("Architecture") + "-deb", "summary": debcontrol.get_as_string("Description").splitlines()[0], "epoch": "", "version": 0, "release": 0, "description": debcontrol.get_as_string("Description"), } for hdr_k, deb_k in [ ("requires", "Depends"), ("provides", "Provides"), ("conflicts", "Conflicts"), ("obsoletes", "Replaces"), ("recommends", "Recommends"), ("suggests", "Suggests"), ("breaks", "Breaks"), ("predepends", "Pre-Depends"), ("payload_size", "Installed-Size"), ("maintainer", "Maintainer"), ]: if deb_k in debcontrol: self.hdr[hdr_k] = debcontrol.get_as_string(deb_k) for k in list(debcontrol.keys()): if k not in self.hdr: self.hdr[k] = debcontrol.get_as_string(k) version = debcontrol.get_as_string("Version") if version.find(":") != -1: self.hdr["epoch"], version = version.split(":") self.hdr["version"] = version if version.find("-") != -1: # pylint: disable-next=invalid-name version_tmpArr = version.split("-") self.hdr["version"] = "-".join(version_tmpArr[:-1]) self.hdr["release"] = version_tmpArr[-1] else: self.hdr["version"] = version self.hdr["release"] = "X" except Exception: e = sys.exc_info()[1] raise_with_tb(InvalidPackageError(e), sys.exc_info()[2]) @staticmethod def checksum_type(): return DEB_CHECKSUM_TYPE @staticmethod def is_signed(): return 0 def __getitem__(self, name): return self.hdr.get(str(name)) def __setitem__(self, name, item): self.hdr[name] = item def __delitem__(self, name): del self.hdr[name] def __getattr__(self, name): return getattr(self.hdr, name) def __len__(self): return len(self.hdr) # pylint: disable-next=missing-class-docstring,invalid-name class DEB_Package(A_Package): def __init__(self, input_stream=None): A_Package.__init__(self, input_stream) self.header_data = tempfile.NamedTemporaryFile() self.checksum_type = DEB_CHECKSUM_TYPE def read_header(self): self._stream_copy(self.input_stream, self.header_data) self.header_end = self.header_data.tell() try: self.header_data.seek(0, 0) self.header = deb_Header(self.header_data) except: e = sys.exc_info()[1] raise_with_tb(InvalidPackageError(e), sys.exc_info()[2]) def save_payload(self, output_stream): c_hash = checksum.getHashlibInstance(self.checksum_type, False) if output_stream: output_start = output_stream.tell() self._stream_copy(self.header_data, output_stream, c_hash) self.checksum = c_hash.hexdigest() if output_stream: self.payload_stream = output_stream self.payload_size = output_stream.tell() - output_start 0707010000000E000081B40000000000000000000000016667115D0000269F000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_mpm.py# pylint: disable=missing-module-docstring # # 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 gzip try: # python 2 import cStringIO except ImportError: # python3 import io as cStringIO import tempfile try: # python 2 import xmlrpclib except ImportError: # python3 import xmlrpc.client as xmlrpclib # pylint: disable=F0401 import struct import sys from uyuni.common import fileutils from uyuni.common.usix import ListType, TupleType from uyuni.common.usix import raise_with_tb from uyuni.common import checksum from uyuni.common.rhn_pkg import A_Package, InvalidPackageError # bare-except and broad-except # pylint: disable=W0702,W0703 MPM_CHECKSUM_TYPE = "md5" # FIXME: this should be a configuration option # pylint: disable-next=invalid-name def labelCompare(l1, l2): try: # pylint: disable-next=import-outside-toplevel from uyuni.common import rhn_rpm except ImportError: # rhn_rpm not avalable; return a dummy comparison function return -1 return rhn_rpm.labelCompare(l1, l2) def get_package_header(filename=None, file_obj=None, fd=None): return load(filename=filename, file_obj=file_obj, fd=fd)[0] def load(filename=None, file_obj=None, fd=None): """Loads an MPM and returns its header and its payload""" if filename is None and file_obj is None and fd is None: raise ValueError("No parameters passed") if filename is not None: # pylint: disable-next=unspecified-encoding f = open(filename) elif file_obj is not None: f = file_obj else: # fd is not None f = os.fdopen(os.dup(fd), "r") f.seek(0, 0) p = MPM_Package() try: p.load(f) except InvalidPackageError: e = sys.exc_info()[1] try: return load_rpm(f) except InvalidPackageError: raise_with_tb(e, sys.exc_info()[2]) except: raise_with_tb(e, sys.exc_info()[2]) return p.header, p.payload_stream def load_rpm(stream): # Hmm, maybe an rpm try: # pylint: disable-next=import-outside-toplevel from uyuni.common import rhn_rpm except ImportError: raise_with_tb(InvalidPackageError, sys.exc_info()[2]) # Dup the file descriptor, we don't want it to get closed before we read # the payload newfd = os.dup(stream.fileno()) stream = os.fdopen(newfd, "r") stream.flush() stream.seek(0, 0) try: header = rhn_rpm.get_package_header(file_obj=stream) except InvalidPackageError: e = sys.exc_info()[1] raise_with_tb(InvalidPackageError(*e.args), sys.exc_info()[2]) except rhn_rpm.error: e = sys.exc_info()[1] raise_with_tb(InvalidPackageError(e), sys.exc_info()[2]) except: raise_with_tb(InvalidPackageError, sys.exc_info()[2]) stream.seek(0, 0) return header, stream # pylint: disable-next=invalid-name class MPM_Header: "Wrapper class for an mpm header - we need to store a flag is_source" def __init__(self, hdr): self.hdr = hdr self.is_source = hdr.get("is_source") self.packaging = "mpm" self.signatures = [] def __getitem__(self, name): return self.hdr.get(name) def __setitem__(self, name, item): self.hdr[name] = item def __delitem__(self, name): del self.hdr[name] def __getattr__(self, name): return getattr(self.hdr, name) def __len__(self): return len(self.hdr) @staticmethod def is_signed(): return 0 @staticmethod def checksum_type(): return MPM_CHECKSUM_TYPE @staticmethod def unload(): return None MPM_HEADER_COMPRESSED_GZIP = 1 MPM_PAYLOAD_COMPRESSED_GZIP = 1 # pylint: disable-next=missing-class-docstring,invalid-name class MPM_Package(A_Package): # pylint: disable=R0902 _lead_format = "!16sB3s4L92s" _magic = "mpmpackage012345" def __init__(self, input_stream=None): A_Package.__init__(self, input_stream) self.header_flags = MPM_HEADER_COMPRESSED_GZIP self.header_size = 0 self.payload_flags = 0 assert len(self._magic) == 16 self._buffer_size = 16384 self.file_size = 0 def read_header(self): arr = self._read_lead(self.input_stream) magic = arr[0] if magic != self._magic: raise InvalidPackageError() header_len, payload_len = int(arr[5]), int(arr[6]) self.header_flags, self.payload_flags = arr[3], arr[4] self.file_size = 128 + header_len + payload_len header_data = self._read_bytes(self.input_stream, header_len) self._read_header(header_data, self.header_flags) self.checksum_type = self.header.checksum_type() def _read_lead(self, stream): # Lead has the following format: # 16 bytes magic # 1 bytes version # 3 bytes unused # 4 bytes header flags # 4 bytes payload flags # 4 bytes header length # 4 bytes payload length # 92 bytes padding to 128 bytes lead = self._read_bytes(stream, 128) if len(lead) != 128: raise InvalidPackageError() arr = struct.unpack(self._lead_format, lead) return arr def load(self, input_stream): # Clean up # pylint: disable-next=unnecessary-dunder-call self.__init__() self.input_stream = input_stream # Read the header self.read_header() payload_stream = fileutils.payload(input_stream.name, input_stream.tell()) input_stream.seek(self.file_size) if self.file_size != input_stream.tell(): raise InvalidPackageError() self._read_payload(payload_stream, self.payload_flags) def _read_header(self, header_data, header_flags): if header_flags & MPM_HEADER_COMPRESSED_GZIP: t = cStringIO.StringIO(header_data) g = gzip.GzipFile(None, "r", 0, t) header_data = g.read() g.close() t.close() try: # pylint: disable-next=invalid-name,unused-variable params, _x = xmlrpclib.loads(header_data) # pylint: disable-next=try-except-raise except: # XXX raise self.header = MPM_Header(params[0]) def _read_payload(self, payload_stream, payload_flags): payload_stream.seek(0, 0) if payload_flags & MPM_PAYLOAD_COMPRESSED_GZIP: g = gzip.GzipFile(None, "r", 0, payload_stream) t = tempfile.TemporaryFile() self._stream_copy(g, t) g.close() payload_stream = t self.payload_stream = payload_stream def write(self, output_stream): if self.header is None: # pylint: disable-next=broad-exception-raised raise Exception() output_stream.seek(128, 0) self._encode_header(output_stream) self._encode_payload(output_stream) # pylint: disable=E0012,W1401 # now we know header and payload size so rewind back and write lead lead_arr = ( self._magic, 1, "\0" * 3, self.header_flags, self.payload_flags, self.header_size, self.payload_size, "\0" * 92, ) # lead lead = struct.pack(self._lead_format, *lead_arr) output_stream.seek(0, 0) output_stream.write(lead) output_stream.seek(0, 2) def _encode_header(self, stream): assert self.header is not None data = xmlrpclib.dumps((_replace_null(self.header),)) start = stream.tell() if self.header_flags & MPM_HEADER_COMPRESSED_GZIP: f = gzip.GzipFile(None, "wb", 9, stream) f.write(data) f.close() else: stream.write(data) stream.flush() self.header_size = stream.tell() - start def _encode_payload(self, stream, c_hash=None): assert self.payload_stream is not None if stream: start = stream.tell() if stream and self.payload_flags & MPM_PAYLOAD_COMPRESSED_GZIP: f = gzip.GzipFile(None, "wb", 9, stream) self._stream_copy(self.payload_stream, f, c_hash) f.close() else: self._stream_copy(self.payload_stream, stream, c_hash) if stream: self.payload_size = stream.tell() - start def save_payload(self, output_stream): self.payload_stream = self.input_stream c_hash = checksum.getHashlibInstance(self.header.checksum_type(), False) self._encode_payload(output_stream, c_hash) self.checksum = c_hash.hexdigest() if output_stream: self.payload_stream = output_stream def _replace_null(obj): if obj is None: return "" if isinstance(obj, ListType): return list(map(_replace_null, obj)) if isinstance(obj, TupleType): return tuple(_replace_null(list(obj))) if hasattr(obj, "items"): obj_dict = {} for k, v in list(obj.items()): obj_dict[_replace_null(k)] = _replace_null(v) return obj_dict return obj 0707010000000F000081B40000000000000000000000016667115D00001318000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_pkg.py# pylint: disable=missing-module-docstring # # 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 rhn.stringutils import bstr from uyuni.common import checksum def get_package_header(filename=None, file_obj=None, fd=None): # pylint: disable=E1103 if filename is not None: stream = open(filename, mode="rb") need_close = True elif file_obj is not None: stream = file_obj else: stream = os.fdopen(os.dup(fd), "r") need_close = True if stream.name.endswith(".deb"): packaging = "deb" elif stream.name.endswith(".rpm"): packaging = "rpm" else: packaging = "mpm" a_pkg = package_from_stream(stream, packaging) a_pkg.read_header() if need_close: stream.close() return a_pkg.header def package_from_stream(stream, packaging): if packaging == "deb": # pylint: disable-next=import-outside-toplevel from uyuni.common import rhn_deb a_pkg = rhn_deb.DEB_Package(stream) elif packaging == "rpm": # pylint: disable-next=import-outside-toplevel from uyuni.common import rhn_rpm a_pkg = rhn_rpm.RPM_Package(stream) elif packaging == "mpm": # pylint: disable-next=import-outside-toplevel from uyuni.common import rhn_mpm a_pkg = rhn_mpm.MPM_Package(stream) else: a_pkg = None return a_pkg def package_from_filename(filename): if filename.endswith(".deb"): packaging = "deb" elif filename.endswith(".rpm") or filename.endswith(".hdr"): packaging = "rpm" else: packaging = "mpm" stream = open(filename, mode="rb") return package_from_stream(stream, packaging) BUFFER_SIZE = 8388608 DEFAULT_CHECKSUM_TYPE = "md5" # pylint: disable-next=invalid-name class A_Package: """virtual class that implements shared methods for RPM/MPM/DEB package object""" # pylint: disable=R0902 def __init__(self, input_stream=None): self.header = None self.header_start = 0 self.header_end = 0 self.input_stream = input_stream self.checksum_type = DEFAULT_CHECKSUM_TYPE self.checksum = None self.payload_stream = None self.payload_size = None def read_header(self): """reads header from self.input_file""" pass def save_payload(self, output_stream): """saves payload to output_stream""" c_hash = checksum.getHashlibInstance(self.checksum_type, False) if output_stream: output_start = output_stream.tell() self._stream_copy(self.input_stream, output_stream, c_hash) self.checksum = c_hash.hexdigest() if output_stream: self.payload_stream = output_stream self.payload_size = output_stream.tell() - output_start def payload_checksum(self): # just read and compute checksum start = self.input_stream.tell() self.save_payload(None) self.payload_size = self.input_stream.tell() - start + self.header_end self.payload_stream = self.input_stream def set_checksum_type(self, ctype=None): """set ctype as checksum type. If ctype is None; reset to default""" if self.checksum_type == ctype: return # pylint: disable-next=singleton-comparison if ctype == None and DEFAULT_CHECKSUM_TYPE != self.checksum_type: self.checksum_type = DEFAULT_CHECKSUM_TYPE self.payload_checksum() elif self.checksum_type != ctype: self.checksum_type = ctype self.payload_checksum() @staticmethod def _stream_copy(source, dest, c_hash=None): """copies data from the source stream to the destination stream""" while True: buf = source.read(BUFFER_SIZE) if not buf: break if dest: dest.write(buf) if c_hash: c_hash.update(buf) @staticmethod def _read_bytes(stream, amt): ret = bstr("") while amt: buf = stream.read(min(amt, BUFFER_SIZE)) if not buf: return ret ret = ret + buf amt = amt - len(buf) return ret class InvalidPackageError(Exception): pass 07070100000010000081B40000000000000000000000016667115D0000405E000000000000000000000000000000000000002400000000uyuni-common-libs/common/rhn_rpm.py# pylint: disable=missing-module-docstring # # 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 struct import tempfile import functools import rpm from uyuni.common.usix import raise_with_tb from uyuni.common.usix import next as usix_next from uyuni.common import checksum from uyuni.common.rhn_pkg import A_Package, InvalidPackageError from rhn.stringutils import sstr # bare-except and broad-except # pylint: disable=W0702,W0703 if not hasattr(tempfile, "SpooledTemporaryFile"): # RHEL5 tempfile.SpooledTemporaryFile = tempfile.NamedTemporaryFile # pylint: disable=E1101 # Expose a bunch of useful constants from rpm error = rpm.error sym, val = None, None for sym, val in list(rpm.__dict__.items()): if sym[:3] == "RPM": # A constant, probably - import it into our namespace globals()[sym] = val del sym, val # need this for rpm-pyhon < 4.6 (e.g. on RHEL5) rpm.RPMTAG_FILEDIGESTALGO = 5011 rpm.RPMTAG_MODULARITYLABEL = 5096 # these values are taken from /usr/include/rpm/rpmpgp.h # PGPHASHALGO_MD5 = 1, /*!< MD5 */ # PGPHASHALGO_SHA1 = 2, /*!< SHA1 */ # PGPHASHALGO_RIPEMD160 = 3, /*!< RIPEMD160 */ # PGPHASHALGO_MD2 = 5, /*!< MD2 */ # PGPHASHALGO_TIGER192 = 6, /*!< TIGER192 */ # PGPHASHALGO_HAVAL_5_160 = 7, /*!< HAVAL-5-160 */ # PGPHASHALGO_SHA256 = 8, /*!< SHA256 */ # PGPHASHALGO_SHA384 = 9, /*!< SHA384 */ # PGPHASHALGO_SHA512 = 10, /*!< SHA512 */ PGPHASHALGO = { 1: "md5", 2: "sha1", 3: "ripemd160", 5: "md2", 6: "tiger192", 7: "haval-5-160", 8: "sha256", 9: "sha384", 10: "sha512", } # pylint: disable-next=invalid-name class RPM_Header: "Wrapper class for an rpm header - we need to store a flag is_source" def __init__(self, hdr, is_source=None): self.hdr = hdr self.is_source = is_source self.packaging = "rpm" self.signatures = [] self._extract_signatures() def __getitem__(self, name): item = self.hdr[name] if isinstance(item, bytes): item = sstr(item) elif isinstance(item, list): item = [sstr(i) if isinstance(i, bytes) else i for i in item] return item def __contains__(self, name): return True if name in self.hdr else False def __setitem__(self, name, item): self.hdr[name] = item def __delitem__(self, name): del self.hdr[name] def __getattr__(self, name): item = getattr(self.hdr, name) if isinstance(item, bytes): item = sstr(item) elif isinstance(item, list): item = [sstr(i) if isinstance(i, bytes) else i for i in item] return item def __len__(self): return len(self.hdr) def __nonzero__(self): return bool(self.hdr) __bool__ = __nonzero__ def get(self, name, default=None): return self.hdr[name] if name in self.hdr else default def modularity_label(self): """ Get modularity label tag. Returns string of modularity label or None if tag is not there. Fix: Some distributions use the DISTTAG (1155) tag instead of MODULARITYLABEL (5096) to label modular packages in the format: `module(n:s:v:c:a)`. We need to check this tag as a fallback (bsc#1192487). """ mtag = None if rpm.RPMTAG_MODULARITYLABEL in self.hdr.keys(): mtag = self.hdr[rpm.RPMTAG_MODULARITYLABEL] elif rpm.RPMTAG_DISTTAG in self.hdr.keys() and self.hdr[ rpm.RPMTAG_DISTTAG ].startswith(b"module("): # Strip away 'module(...)' wrap mtag = self.hdr[rpm.RPMTAG_DISTTAG][7:-1] return mtag def checksum_type(self): if ( self.hdr[rpm.RPMTAG_FILEDIGESTALGO] and self.hdr[rpm.RPMTAG_FILEDIGESTALGO] in PGPHASHALGO ): checksum_type = PGPHASHALGO[self.hdr[rpm.RPMTAG_FILEDIGESTALGO]] else: checksum_type = "md5" return checksum_type def is_signed(self): dsaheader = rsaheader = 0 if hasattr(rpm, "RPMTAG_DSAHEADER"): dsaheader = self.hdr["dsaheader"] if hasattr(rpm, "RPMTAG_RSAHEADER"): rsaheader = self.hdr["rsaheader"] if self.hdr["siggpg"] or self.hdr["sigpgp"] or dsaheader or rsaheader: return 1 return 0 def _extract_signatures(self): header_tags = [ [rpm.RPMTAG_DSAHEADER, "dsa"], [rpm.RPMTAG_RSAHEADER, "rsa"], [rpm.RPMTAG_SIGGPG, "gpg"], [rpm.RPMTAG_SIGPGP, "pgp"], ] for ht, sig_type in header_tags: ret = self.hdr[ht] if not ret: continue ret_len = len(ret) if ret_len < 17: continue # Get the key id - hopefully we get it right elif ret_len <= 65: # V3 DSA signature key_id = ret[9:17] elif ret_len <= 72: # V4 DSA signature key_id = ret[18:26] elif ret_len <= 280: # V3 RSA/8 signature key_id = ret[10:18] elif ret_len <= 287: # V4 RSA/SHA1 signature key_id = ret[19:27] elif ret_len <= 310: # V4 RSA/SHA512 signature key_id = ret[24:32] elif ret_len <= 536: # V3 RSA/SHA256 signature key_id = ret[10:18] elif ret_len <= 566: # V4 RSA/SHA384 signature key_id = ret[24:32] else: # ret_len > 566 # V4 RSA/SHA signature key_id = ret[19:27] key_id_len = len(key_id) # pylint: disable-next=consider-using-f-string fmt = "%dB" % key_id_len t = struct.unpack(fmt, key_id) fmt = "%02x" * key_id_len key_id = fmt % t self.signatures.append( { "signature_type": sig_type, "key_id": key_id, "signature": ret, } ) # pylint: disable-next=missing-class-docstring,invalid-name class RPM_Package(A_Package): # pylint: disable=R0902 def __init__(self, input_stream=None): A_Package.__init__(self, input_stream) self.header = None self.header_data = tempfile.SpooledTemporaryFile() self.header_start = None self.header_end = None self.checksum_type = None self.checksum = None self.payload_stream = None self.payload_size = None def read_header(self): self._get_header_byte_range() try: self.header = get_package_header(file_obj=self.header_data) except InvalidPackageError: e = sys.exc_info()[1] raise_with_tb(InvalidPackageError(*e.args), sys.exc_info()[2]) except error: e = sys.exc_info()[1] raise_with_tb(InvalidPackageError(e), sys.exc_info()[2]) except: raise_with_tb(InvalidPackageError, sys.exc_info()[2]) self.checksum_type = self.header.checksum_type() def _get_header_byte_range(self): """ Return the start and end bytes of the rpm header object. Raw header data are then stored in self.header_data. For details of the rpm file format, see: http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html """ lead_size = 96 struct_lead_size = 16 # Move past the rpm lead buf = self._read_bytes(self.input_stream, lead_size) self.header_data.write(buf) buf = self._read_bytes(self.input_stream, struct_lead_size) self.header_data.write(buf) sig_size = self._get_header_struct_size(buf) # Now we can find the start of the actual header. self.header_start = lead_size + sig_size buf = self._read_bytes(self.input_stream, sig_size - struct_lead_size) self.header_data.write(buf) buf = self._read_bytes(self.input_stream, struct_lead_size) self.header_data.write(buf) header_size = self._get_header_struct_size(buf) self.header_end = self.header_start + header_size buf = self._read_bytes(self.input_stream, header_size - struct_lead_size) self.header_data.write(buf) @staticmethod def _get_header_struct_size(struct_lead): """ Compute the size in bytes of the rpm header struct starting at the current position in package_file. """ try: # Read the number of index entries header_index = struct_lead[8:12] (header_index_value,) = struct.unpack(">I", header_index) # Read the the size of the header data store header_store = struct_lead[12:16] (header_store_value,) = struct.unpack(">I", header_store) except: # pylint: disable-next=raise-missing-from raise InvalidPackageError # The total size of the header. Each index entry is 16 bytes long. header_size = 8 + 4 + 4 + header_index_value * 16 + header_store_value # Headers end on an 8-byte boundary. Round out the extra data. round_out = header_size % 8 if round_out != 0: header_size = header_size + (8 - round_out) return header_size def save_payload(self, output_stream): c_hash = checksum.getHashlibInstance(self.checksum_type, False) if output_stream: output_start = output_stream.tell() self.header_data.seek(0, 0) self._stream_copy(self.header_data, output_stream, c_hash) self._stream_copy(self.input_stream, output_stream, c_hash) self.checksum = c_hash.hexdigest() self.header_data.close() if output_stream: self.payload_stream = output_stream self.payload_size = output_stream.tell() - output_start def get_header_byte_range(package_file): """ Return the start and end bytes of the rpm header object. For details of the rpm file format, see: http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html """ lead_size = 96 # Move past the rpm lead package_file.seek(lead_size) sig_size = get_header_struct_size(package_file) # Now we can find the start of the actual header. header_start = lead_size + sig_size package_file.seek(header_start) header_size = get_header_struct_size(package_file) header_end = header_start + header_size return (header_start, header_end) def get_header_struct_size(package_file): """ Compute the size in bytes of the rpm header struct starting at the current position in package_file. """ # Move past the header preamble package_file.seek(8, 1) try: # Read the number of index entries header_index = package_file.read(4) (header_index_value,) = struct.unpack(">I", header_index) # Read the the size of the header data store header_store = package_file.read(4) (header_store_value,) = struct.unpack(">I", header_store) except: # pylint: disable-next=raise-missing-from raise InvalidPackageError # The total size of the header. Each index entry is 16 bytes long. header_size = 8 + 4 + 4 + header_index_value * 16 + header_store_value # Headers end on an 8-byte boundary. Round out the extra data. round_out = header_size % 8 if round_out != 0: header_size = header_size + (8 - round_out) return header_size SHARED_TS = None def get_package_header(filename=None, file_obj=None, fd=None): """Loads the package header from a file / stream / file descriptor Raises rpm.error if an error is found, or InvalidPacageError if package is busted """ global SHARED_TS # XXX Deal with exceptions better if filename is None and file_obj is None and fd is None: raise ValueError("No parameters passed") if filename is not None: # pylint: disable-next=unspecified-encoding f = open(filename, "r") elif file_obj is not None: f = file_obj f.seek(0, 0) else: # fd is not None f = None if f is None: os.lseek(fd, 0, 0) file_desc = fd else: file_desc = f.fileno() # don't try to use rpm.readHeaderFromFD() here, it brokes signatures # see commit message if not SHARED_TS: SHARED_TS = rpm.ts() SHARED_TS.setVSFlags(-1) rpm.addMacro("_dbpath", "/var/cache/rhn/rhnpush-rpmdb") try: hdr = SHARED_TS.hdrFromFdno(file_desc) rpm.delMacro("_dbpath") except: rpm.delMacro("_dbpath") raise if hdr is None: raise InvalidPackageError is_source = hdr[rpm.RPMTAG_SOURCEPACKAGE] return RPM_Header(hdr, is_source) # pylint: disable-next=missing-class-docstring class MatchIterator: def __init__(self, tag_name=None, value=None): # Query by name, by default if not tag_name: tag_name = "name" # rpm 4.1 or later self.ts = rpm.TransactionSet() self.ts.setVSFlags(8) m_args = (tag_name,) if value: m_args += (value,) # pylint: disable=E1101 self.mi = self.ts.dbMatch(*m_args) def pattern(self, tag_name, mode, pattern): self.mi.pattern(tag_name, mode, pattern) def next(self): try: hdr = usix_next(self.mi) except StopIteration: hdr = None if hdr is None: return None is_source = hdr[rpm.RPMTAG_SOURCEPACKAGE] return RPM_Header(hdr, is_source) # pylint: disable-next=invalid-name def headerLoad(data): hdr = rpm.headerLoad(data) is_source = hdr[rpm.RPMTAG_SOURCEPACKAGE] return RPM_Header(hdr, is_source) # pylint: disable-next=invalid-name def labelCompare(t1, t2): return rpm.labelCompare(t1, t2) def nvre_compare(t1, t2): def build_evr(p): evr = [p[3], p[1], p[2]] evr = list(map(str, evr)) if evr[0] == "": evr[0] = None return evr if t1[0] != t2[0]: raise ValueError("You should only compare packages with the same name") evr1, evr2 = (build_evr(t1), build_evr(t2)) return rpm.labelCompare(evr1, evr2) # pylint: disable-next=invalid-name def hdrLabelCompare(hdr1, hdr2): """take two RPMs or headers and compare them for order""" if hdr1["name"] == hdr2["name"]: hdr1 = [hdr1["epoch"] or None, hdr1["version"], hdr1["release"]] hdr2 = [hdr2["epoch"] or None, hdr2["version"], hdr2["release"]] if hdr1[0]: hdr1[0] = str(hdr1[0]) if hdr2[0]: hdr2[0] = str(hdr2[0]) return rpm.labelCompare(hdr1, hdr2) elif hdr1["name"] < hdr2["name"]: return -1 return 1 # pylint: disable-next=invalid-name def sortRPMs(rpms): """Sorts a list of RPM files. They *must* exist.""" assert isinstance(rpms, type([])) # Build a list of (header, rpm) helper = [(get_package_header(x), x) for x in rpms] # Sort the list using the headers as a comparison # pylint: disable-next=unnecessary-lambda-assignment sort_cmp = lambda x, y: hdrLabelCompare(x[0], y[0]) try: helper.sort(sort_cmp) except TypeError: helper.sort(key=functools.cmp_to_key(sort_cmp)) # Extract the rpm names now return [x[1] for x in helper] # pylint: disable-next=invalid-name def getInstalledHeader(rpmName): """quieries the RPM DB for a header matching rpmName.""" matchiter = MatchIterator("name") matchiter.pattern("name", rpm.RPMMIRE_STRCMP, rpmName) return matchiter.next() if __name__ == "__main__": pass 07070100000011000081B40000000000000000000000016667115D0000052C000000000000000000000000000000000000002B00000000uyuni-common-libs/common/timezone_utils.py""" Copyright (C) 2017 Oracle and/or its affiliates. All rights reserved. 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, version 2 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. Utility to get system UTC offset and format as needed by DBs. """ import time def get_utc_offset(): """Return the UTC offset, allowing for DST.""" is_dst = time.daylight and time.localtime().tm_isdst > 0 utc_offset = -time.timezone if is_dst: utc_offset = -time.altzone mins = divmod(utc_offset, 60)[0] hours, mins = divmod(mins, 60) # pylint: disable-next=consider-using-f-string return "{0:+03d}:{1:02d}".format(hours, mins) if __name__ == "__main__": # pylint: disable-next=consider-using-f-string print("UTC offset (allowing for DST if in effect): %s" % get_utc_offset()) 07070100000012000081B40000000000000000000000016667115D000009A1000000000000000000000000000000000000002100000000uyuni-common-libs/common/usix.py# # Copyright (c) 2013--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. # """ Usix is a library to bring compatibility between Python2 and Python3. """ # pylint: disable=E1101 import sys import types PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 # Common data types # Common data types if PY3: BufferType = bytes UnicodeType = bytes StringType = str DictType = dict IntType = int LongType = int ListType = list ClassType = type FloatType = float TupleType = tuple TypeType = type InstanceType = object else: BufferType = types.BufferType UnicodeType = unicode # pylint: disable=E0602 StringType = types.StringType DictType = types.DictType IntType = types.IntType LongType = types.LongType ListType = types.ListType ClassType = types.BufferType FloatType = types.FloatType TupleType = types.TupleType TypeType = types.TypeType InstanceType = types.InstanceType # Common limits if PY3: # pylint: disable-next=invalid-name MaxInt = sys.maxsize else: MaxInt = sys.maxint # Common methods # raise exception with traceback # pylint: disable=W0122 if PY3: def raise_with_tb(value, tb=None): """ Re-raise an exception with the traceback. """ try: if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value finally: value = None tb = None else: exec( """ def raise_with_tb(value, tb=None): try: raise value, None, tb finally: tb = None """ ) # code from original 'six' module # added for compatibility with Python 2.4 try: advance_iterator = next except NameError: def advance_iterator(it): """ Iterator invocation. """ return it.next() next = advance_iterator # pylint: disable=W0622 07070100000013000081B40000000000000000000000016667115D00001412000000000000000000000000000000000000002C00000000uyuni-common-libs/uyuni-common-libs.changes------------------------------------------------------------------- Mon Jun 10 16:43:55 CEST 2024 - marina.latini@suse.com - version 5.0.4-0 * Move server-only functions out of uyuni.common (bsc#1222325) ------------------------------------------------------------------- Fri May 03 14:26:54 CEST 2024 - marina.latini@suse.com - version 5.0.3-0 * Add support for package signature type V4 RSA/SHA384 * Add support for package signature type V4 RSA/SHA512 (bsc#1221465) ------------------------------------------------------------------- Mon Jan 29 11:55:55 CET 2024 - rosuna@suse.com - version 5.0.2-1 * Apply Black and Pylint to enforce Python style ------------------------------------------------------------------- Tue Jan 16 08:20:02 CET 2024 - jgonzalez@suse.com - version 5.0.1-1 * Bump version to 5.0.0 ------------------------------------------------------------------- Mon Sep 18 14:43:06 CEST 2023 - rosuna@suse.com - version 4.4.4-1 * Workaround for python3-debian bug about collecting control file (bsc#1211525, bsc#1208692) * Accept missing rhn.conf file * Use context manager for apache users in fileutils.py. ------------------------------------------------------------------- Wed Apr 19 12:55:12 CEST 2023 - marina.latini@suse.com - version 4.4.3-1 * Ensure installation of make for building. * Use versioned Python during packaging. ------------------------------------------------------------------- Wed Dec 14 14:13:10 CET 2022 - jgonzalez@suse.com - version 4.4.2-1 * unify user notification code on java side ------------------------------------------------------------------- Wed Sep 28 11:05:47 CEST 2022 - jgonzalez@suse.com - version 4.4.1-1 * Do not allow creating path if nonexistent user or group in fileutils. ------------------------------------------------------------------- Wed Jul 06 16:24:17 CEST 2022 - jgonzalez@suse.com - version 4.3.5-1 * Fix reposync issue about 'rpm.hdr' object has no attribute 'get' ------------------------------------------------------------------- Tue Apr 19 12:14:45 CEST 2022 - jgonzalez@suse.com - version 4.3.4-1 * implement more decompression algorithms for reposync (bsc#1196704) ------------------------------------------------------------------- Fri Mar 11 15:46:23 CET 2022 - jgonzalez@suse.com - version 4.3.3-1 * Reorganize python files ------------------------------------------------------------------- Tue Jan 18 14:09:04 CET 2022 - jgonzalez@suse.com - version 4.3.2-1 * Read modularity data from DISTTAG tag as fallback (bsc#1192487) * Add decompression of zck files to fileutils * require python macros for building ------------------------------------------------------------------- Mon Aug 09 11:12:24 CEST 2021 - jgonzalez@suse.com - version 4.3.1-1 - Handle broken RPM packages to prevent exceptions causing fails on repository synchronization (bsc#1186650) ------------------------------------------------------------------- Fri May 28 09:11:32 CEST 2021 - jgonzalez@suse.com - version 4.2.4-1 - Maintainer field in debian packages are only recommended (bsc#1186508) ------------------------------------------------------------------- Wed Jan 27 13:17:30 CET 2021 - jgonzalez@suse.com - version 4.2.3-1 - Section in Debian packages in now treated as optional (bsc#1179555) ------------------------------------------------------------------- Wed Nov 25 12:37:09 CET 2020 - jgonzalez@suse.com - version 4.2.2-1 - Cleaning up unused Python 2 build leftovers. - Disabled debug package build. ------------------------------------------------------------------- Fri Sep 18 12:32:52 CEST 2020 - jgonzalez@suse.com - version 4.2.1-1 - Fix issues importing RPM packages with long RPM headers (bsc#1174965) - Update package version to 4.2.0 ------------------------------------------------------------------- Wed May 20 11:08:34 CEST 2020 - jgonzalez@suse.com - version 4.1.5-1 - uyuni-common-libs obsoletes python3-spacewalk-usix and python3-spacewalk-backend-libs (bsc#1170684) - reposync speedup fixes ------------------------------------------------------------------- Mon Apr 13 09:38:18 CEST 2020 - jgonzalez@suse.com - version 4.1.4-1 - implement notification utility - Do not break when syncing Oracle 7 yum channel (bsc#1158463) ------------------------------------------------------------------- Thu Mar 19 12:18:16 CET 2020 - jgonzalez@suse.com - version 4.1.3-1 - Add MODULARITYLABEL tag support ------------------------------------------------------------------- Wed Jan 22 12:26:04 CET 2020 - jgonzalez@suse.com - version 4.1.2-1 - remove conflicts to spacewalk-backend-libs packages ------------------------------------------------------------------- Wed Nov 27 17:12:01 CET 2019 - jgonzalez@suse.com - version 4.1.1-1 - Fix building for CentOS6/RHEL6/Expanded Support 6 - add conflicts to spacewalk-backend-libs and spacewalk-usix to remove them on update ------------------------------------------------------------------- Fri Nov 1 11:20:21 UTC 2019 - Michael Calmer <mc@suse.com> - initial release - replaces spacewalk-backend-libs and spacewalk-usix 07070100000014000081B40000000000000000000000016667115D00001014000000000000000000000000000000000000002900000000uyuni-common-libs/uyuni-common-libs.spec# # spec file for package uyuni-common-libs # # Copyright (c) 2024 SUSE LLC # # 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/ # %global debug_package %{nil} %define __python /usr/bin/python2 %if 0%{?fedora} || 0%{?suse_version} >= 1500 || 0%{?rhel} >= 8 %{!?python3_sitelib: %global python3_sitelib %(%{__python3} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %global python3root %{python3_sitelib}/uyuni %global build_py3 1 %endif %if ( 0%{?rhel} && 0%{?rhel} < 8 ) || 0%{?suse_version} %global build_py2 1 %endif %define pythonX %{?build_py3:python3}%{!?build_py3:python} %if 0%{?suse_version} >= 1500 %global python_prefix python3 %else %if 0%{?fedora} >= 28 || 0%{?rhel} >= 8 %global python_prefix python2 %else %global python_prefix python %endif %endif %{!?python2_sitelib: %global python2_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %global python2root %{python2_sitelib}/uyuni Name: uyuni-common-libs Summary: Uyuni server and client libs License: GPL-2.0-only Group: Development/Languages/Python Version: 5.0.4 Release: 0 URL: https://github.com/uyuni-project/uyuni Source0: %{name}-%{version}.tar.gz BuildRequires: fdupes BuildRequires: make BuildRoot: %{_tmppath}/%{name}-%{version}-build %description Uyuni server and client libs %if 0%{?build_py2} %package -n python2-%{name} Summary: Uyuni server and client tools libraries for python2 Group: Development/Languages/Python Requires: python %if 0%{?suse_version} BuildRequires: python-devel %else BuildRequires: python2-devel %endif %if 0%{?suse_version} || 0%{?rhel} >= 8 Recommends: zchunk Recommends: zstd %endif %description -n python2-%{name} Python 2 libraries required by both Uyuni server and client tools. %endif %if 0%{?build_py3} %package -n python3-%{name} Summary: Uyuni server and client tools libraries for python3 Group: Development/Languages/Python BuildRequires: python3-devel BuildRequires: python3-rpm-macros Conflicts: %{name} < 1.7.0 %if 0%{?suse_version} Requires: python3-base %else Requires: python3-libs %endif %if 0%{?suse_version} || 0%{?rhel} >= 8 Recommends: zchunk Recommends: zstd %endif Obsoletes: python3-spacewalk-backend-libs Obsoletes: python3-spacewalk-usix %description -n python3-%{name} Python 3 libraries required by both Uyuni server and client tools. %endif %prep %setup -q %build make -f Makefile.common-libs all PYTHON_BIN=%{pythonX} %install make -f Makefile.common-libs install PREFIX=$RPM_BUILD_ROOT \ MANDIR=%{_mandir} PYTHON_BIN=%{pythonX} %if 0%{?build_py3} install -d $RPM_BUILD_ROOT%{python2root}/common cp $RPM_BUILD_ROOT%{python3root}/__init__.py \ $RPM_BUILD_ROOT%{python2root}/ cp $RPM_BUILD_ROOT%{python3root}/common/*.py \ $RPM_BUILD_ROOT%{python2root}/common %endif %if 0%{?suse_version} %if 0%{?build_py2} %py_compile -O %{buildroot}/%{python2root} %fdupes %{buildroot}/%{python2root} %endif %if 0%{?build_py3} %py3_compile -O %{buildroot}/%{python3root} %fdupes %{buildroot}/%{python3root} %endif %endif %if !(0%{?build_py2}) rm -Rf $RPM_BUILD_ROOT%{python2root} %endif %if 0%{?build_py2} %files -n python2-%{name} %defattr(-,root,root) %{!?_licensedir:%global license %doc} %license LICENSE %{python2root} %endif %if 0%{?build_py3} %files -n python3-%{name} %defattr(-,root,root) %license LICENSE %{python3root} %endif %changelog 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor