pax_global_header00006660000000000000000000000064151162750550014521gustar00rootroot0000000000000052 comment=58a917a6c5e24e9e8a01976c17d2eee06249b9b6 rpm-software-management-rpm-3c1f23f/000077500000000000000000000000001511627505500174665ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/.dockerignore000066400000000000000000000000101511627505500221310ustar00rootroot00000000000000/_build rpm-software-management-rpm-3c1f23f/.editorconfig000066400000000000000000000004151511627505500221430ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true # XXX: Too common in existing source # trim_trailing_whitespace = true [*.{c,h,h.in,pc,pc.in}] indent_size = 4 indent_style = tab tab_width = 8 [*.md] indent_size = 4 indent_style = space rpm-software-management-rpm-3c1f23f/.github/000077500000000000000000000000001511627505500210265ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/.github/CODEOWNERS000066400000000000000000000001161511627505500224170ustar00rootroot00000000000000# By default, everything goes to rpm-team * @rpm-software-management/rpm-team rpm-software-management-rpm-3c1f23f/.github/ISSUE_TEMPLATE/000077500000000000000000000000001511627505500232115ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000013271511627505500257060ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Start condition e.g. installed packages 2. Command (line) executed 3. Error encountered Please link or attach the packages or spec files involved. **Expected behavior** A clear and concise description of what you expected to happen. **Output** If applicable, add copy of the output on the command line or a screenshots to help explain your problem. **Environment** - OS / Distribution: [e.g. Fedora 42] - Version [e.g. rpm-4.19.0] **Additional context** Add any other context about the problem here. rpm-software-management-rpm-3c1f23f/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000020041511627505500267320ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: RFE assignees: '' --- Generally requests for new features should be opened as a [Discussion](https://github.com/rpm-software-management/rpm/discussions) first. There feedback from the community can be collected and possible solutions can be discussed. Consider maintaining a design outline in the top post and update it as the discussion progresses. After a plan of action has solidified an issue for actually implementing it can be created. **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is or what you are trying to do and can't (easily). **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. rpm-software-management-rpm-3c1f23f/.github/workflows/000077500000000000000000000000001511627505500230635ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/.github/workflows/artifact.yml000066400000000000000000000025521511627505500254070ustar00rootroot00000000000000# Builds & attaches release artifacts to a newly published release. name: Release artifacts on: release: types: [published] permissions: # Permit the modification of releases contents: write jobs: # Generates dist tarball(s) and a checksum file to go with. # # A full build is needed here since we currently bundle some build artifacts # with the tarball (man pages, HTML manuals and .po files) so make use of the # test image as it already contains all the build dependencies. # dist: runs-on: ubuntu-24.04 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG_NAME: ${{ github.event.release.tag_name }} DIST_EXT: tar.bz2 steps: - name: Checkout release tag uses: actions/checkout@v4 - name: Build image run: | podman build --target base --tag rpm -f tests/Dockerfile . - name: Create dist tarball(s) run: | mkdir _build podman run -v $PWD:/srv:z --workdir /srv/_build --rm rpm sh -c \ "cmake -DWITH_DOXYGEN=ON .. && make dist" - name: Create checksum file working-directory: _build run: | sha512sum --tag *.$DIST_EXT > CHECKSUM cat CHECKSUM - name: Upload dist tarball(s) & checksum file working-directory: _build run: | gh release upload $TAG_NAME *.$DIST_EXT CHECKSUM rpm-software-management-rpm-3c1f23f/.github/workflows/gh-pages.yml000066400000000000000000000025351511627505500253060ustar00rootroot00000000000000# Builds & deploys a Jekyll site to GitHub Pages. name: GitHub Pages deployment on: push: branches: [master] paths: - 'docs/**' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: # Build job build: runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4 - name: Build image run: | podman build --target base --tag rpm -f tests/Dockerfile . - name: Generate Jekyll pages run: | mkdir _build podman run -v $PWD:/srv:z --workdir /srv/_build --rm rpm sh -c \ "cmake -DWITH_DOXYGEN=ON .. && make pages" - name: Setup Pages uses: actions/configure-pages@v5 - name: Build with Jekyll uses: actions/jekyll-build-pages@v1 with: source: ./_build/site destination: ./_site - name: Upload artifact uses: actions/upload-pages-artifact@v3 # Deployment job deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 rpm-software-management-rpm-3c1f23f/.github/workflows/linux.yml000066400000000000000000000015241511627505500247470ustar00rootroot00000000000000name: Linux CI on: push: paths-ignore: &docfiles - '**.md' - 'COPYING' - 'CREDITS' - 'INSTALL' - 'README' - 'docs/**' pull_request: paths-ignore: *docfiles jobs: build_and_test: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v3 - name: Build the Fedora testing environment run: ./mktree.oci build working-directory: ./tests - name: Fix kernel mmap rnd bits # High entropy setting in GH runner images >= 20240310.1.0 # causes ASAN blowing up here and there: # https://github.com/actions/runner-images/issues/9491 run: sudo sysctl vm.mmap_rnd_bits=28 - name: Run the test suite run: ./mktree.oci check --interactive=false -j$(nproc) working-directory: ./tests env: MKTREE_ENGINE: docker rpm-software-management-rpm-3c1f23f/.github/workflows/scdoc.yml000066400000000000000000000010401511627505500246740ustar00rootroot00000000000000name: Man page linter on: push: paths: &manpages - 'docs/man/**' pull_request: paths: *manpages jobs: scdoc_lint: runs-on: ubuntu-24.04 container: fedora:43 steps: - uses: actions/checkout@v3 - name: Install dependencies run: dnf install -y scdoc scd2html - name: Build man pages run: | for file in docs/man/*.scd; do [[ $file == *man-template* ]] && continue scdoc < $file > /dev/null scd2html < $file > /dev/null done rpm-software-management-rpm-3c1f23f/.gitignore000066400000000000000000000001251511627505500214540ustar00rootroot00000000000000.[^dockerignore]* !.tx !.gitignore !.mailmap *.*~ *.tmp /_build /tests/mktree.cache rpm-software-management-rpm-3c1f23f/.gitmodules000066400000000000000000000001541511627505500216430ustar00rootroot00000000000000[submodule "po"] path = po url = https://github.com/rpm-software-management/rpm-l10n.git branch = master rpm-software-management-rpm-3c1f23f/.mailmap000066400000000000000000000063441511627505500211160ustar00rootroot00000000000000 # More recent contributors with multiple addresses Alec Leamas Alec Leamas Alexandr D. Kanevskiy Alexander Kanevskiy Alexey Tourbin Alexey Tourbin Andreas Scherer Andreas Scherer Fionnuala Gunter fin@linux.vnet.ibm.com Fionnuala Gunter fin@linux.vnet.ibm.com Igor Raits Igor Gnatenko Jerome Quelin Jérôme Quelin Jindrich Novy Jindrich Novy Lubos Kardos Lubos Kardos Lubos Kardos Lubos Kardos Neal Gompa Neal Gompa (ニール・ゴンパ) Panu Matilainen pmatilai@dhcp115.koti.laiskiainen.org Panu Matilainen Panu Matilainen Paul Nasrat pauln@enki.eridu Paul Nasrat pauln Per Øyvind Karlsen Per Øyvind Karlsen (proyvind) Ralf Corsépius packman@mccallum.corsepiu.local Stefan Berger Stefan Berger Thierry Vignaud thierry.vignaud@gmail.com Taylon Silmer Taylon Silmer Pavlina Varekova Pavlina # Past contributors (pre-2007) # The email addresses here try to represent the time of contributions. Alex deVries Arkadiusz Miskiewicz Chip Turner cturner Erik Troan ewt Christian Gafton Jeff Johnson jbj James Olin Oden Michael K. Johnson Marc Ewing Michael Fulbright Matt Wilson Gustavo Niemeyer niemeyer Bill Nottingham Randy J. Ray Elliot Lee # Map unknown entries to one Unknown Author unknown author # Three commits from "djb" in 1998 Unknown Author djb # Mystery "otto" fixed a missing space in the rpm.8 manual in 1998 Unknown Author otto # This "root" would've been ewt or marc, or both Unknown Author root rpm-software-management-rpm-3c1f23f/CMakeLists.txt000066400000000000000000000400541511627505500222310ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.18) project(rpm VERSION 6.0.1 DESCRIPTION "The RPM Package Manager" HOMEPAGE_URL "http://rpm.org" LANGUAGES C CXX ) # user configurable stuff option(ENABLE_CUTF8 "Enable C.UTF-8 as default locale" ON) option(ENABLE_NLS "Enable native language support" ON) option(ENABLE_OPENMP "Enable OpenMP threading support" ON) option(ENABLE_PYTHON "Enable Python bindings" ON) option(ENABLE_PLUGINS "Enable plugin support" ON) option(ENABLE_WERROR "Stop build on warnings" OFF) option(ENABLE_SQLITE "Enable sqlite rpmdb support" ON) option(ENABLE_NDB "Enable ndb rpmdb support" ON) option(ENABLE_BDB_RO "Enable read-only Berkeley DB rpmdb support (EXPERIMENTAL)" OFF) option(ENABLE_TESTSUITE "Enable test-suite" OFF) option(ENABLE_ASAN "Enable address-sanitizer" OFF) option(ENABLE_UBSAN "Enable undefined behavior-sanitizer" OFF) option(WITH_CAP "Build with capability support" ON) option(WITH_ACL "Build with ACL support" ON) option(WITH_SELINUX "Build with SELinux support" ON) option(WITH_DBUS "Build with DBUS support" ON) option(WITH_AUDIT "Build with audit support" ON) option(WITH_FSVERITY "Build with fsverity support" OFF) option(WITH_IMAEVM "Build with IMA support" OFF) option(WITH_FAPOLICYD "Build with fapolicyd support" ON) option(WITH_SEQUOIA "Build with Sequoia OpenPGP support" ON) option(WITH_OPENSSL "Use openssl instead of libgcrypt for internal crypto" OFF) option(WITH_READLINE "Build with readline support" ON) option(WITH_BZIP2 "Build with bzip2 support" ON) option(WITH_ICONV "Build with iconv support" ON) option(WITH_ZSTD "Build with zstd support" ON) option(WITH_LIBDW "Build with libdw support" ON) option(WITH_LIBELF "Build with libelf support" ON) option(WITH_LIBLZMA "Build with liblzma support" ON) option(WITH_DOXYGEN "Build API docs with doxygen" OFF) set(RPM_CONFIGDIR "${CMAKE_INSTALL_PREFIX}/lib/rpm" CACHE PATH "rpm home") set(RPM_MACROSDIR "${RPM_CONFIGDIR}/macros.d") set(RPM_VENDOR "vendor" CACHE STRING "rpm vendor string") # Emulate libtool versioning. Before a public release: # - increment micro version whenever there are code changes # - increment minor version whenever there are added interfaces # - increment major version whenever there are removed interfaces # - incrementing a more significant version segment resets changes to # any less significant segments set(RPM_SOVERSION 10) set(RPM_LIBVERSION ${RPM_SOVERSION}.7.0) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # extensions needed for typeof() hacks during transition set(CMAKE_CXX_EXTENSIONS ON) set(CMAKE_SHARED_LIBRARY_PREFIX "") set(CMAKE_SHARED_MODULE_PREFIX "") set(CMAKE_POSITION_INDEPENDENT_CODE ON) include(GNUInstallDirs) include(CheckSymbolExists) add_compile_definitions(_GNU_SOURCE) add_definitions(-D_FILE_OFFSET_BITS=64) # Find a (system) utility in canonical FHS paths, excluding $PATH. Useful for # when the path is to be used at RPM runtime, such as in the macro files. The # MYPATH environment variable can be used to augment these paths. function(findutil UTIL TRY) list(GET TRY 0 util) find_program(${UTIL} NAMES ${TRY} PATHS ENV MYPATH PATHS /usr/local/bin /usr/bin /bin PATHS /usr/local/sbin /usr/sbin /sbin NO_DEFAULT_PATH ) if (NOT ${UTIL}) list(GET TRY 0 util) message(DEBUG "${util} not found, assuming /usr/bin/${util}") set(${UTIL} /usr/bin/${util} PARENT_SCOPE) endif() mark_as_advanced(${UTIL}) endfunction() function(makemacros) set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix "\${prefix}") set(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}") set(sbindir "\${exec_prefix}/${CMAKE_INSTALL_SBINDIR}") set(libexecdir "\${exec_prefix}/${CMAKE_INSTALL_LIBEXECDIR}") set(datarootdir "\${prefix}/${CMAKE_INSTALL_DATAROOTDIR}") set(datadir "\${datarootdir}") set(sysconfdir "${CMAKE_INSTALL_FULL_SYSCONFDIR}") set(sharedstatedir "${CMAKE_INSTALL_FULL_SHAREDSTATEDIR}") set(localstatedir "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}") set(libdir "\${prefix}/=LIB=") set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") set(oldincludedir "${CMAKE_INSTALL_FULL_OLDINCLUDEDIR}") set(infodir "\${prefix}/${CMAKE_INSTALL_INFODIR}") set(mandir "\${prefix}/${CMAKE_INSTALL_MANDIR}") set(rundir /run) pkg_get_variable(sysusersdir systemd sysusersdir) if (NOT sysusersdir) set(sysusersdir /usr/lib/sysusers.d) endif() findutil(__7ZIP "7za;7z") findutil(__BZIP2 bzip2) findutil(__CAT cat) findutil(__CHMOD chmod) findutil(__CHOWN chown) findutil(__CP cp) findutil(__CURL curl) findutil(__FILE file) findutil(__GPG gpg) findutil(__GREP grep) findutil(__GZIP gzip) findutil(__ID id) findutil(__CC cc) findutil(__LN ln) findutil(__INSTALL install) findutil(__LRZIP lrzip) findutil(__LZIP lzip) findutil(__XZ xz) findutil(__MAKE make) findutil(__MKDIR mkdir) findutil(__MV mv) findutil(__PATCH patch) findutil(__RM rm) findutil(__SED sed) findutil(__TAR tar) findutil(__UNZIP unzip) findutil(__ZSTD zstd) findutil(__GEM gem) findutil(__GIT git) findutil(__HG hg) findutil(__BZR bzr) findutil(__QUILT quilt) findutil(__LD ld) findutil(__OBJDUMP objdump) findutil(__STRIP strip) findutil(__SYSTEMD_SYSUSERS systemd-sysusers) findutil(__FIND_DEBUGINFO find-debuginfo) findutil(__AWK awk) findutil(__AR ar) findutil(__AS as) findutil(__CPP cpp) findutil(__CXX c++) findutil(__SQ sq) list(GET db_backends 0 DB_BACKEND) set(host_cpu ${CMAKE_HOST_SYSTEM_PROCESSOR}) string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} host_os) set(host_vendor ${RPM_VENDOR}) set(host ${host_cpu}-${host_vendor}-${host_os}) set(RPMCANONVENDOR ${host_vendor}) set(RPMCANONOS ${host_os}) set(RPMCANONGNU -gnu) configure_file(platform.in platform @ONLY) configure_file(rpmrc.in rpmrc @ONLY) configure_file(macros.in macros @ONLY) configure_file(rpmpopt.in rpmpopt-${PROJECT_VERSION} @ONLY) configure_file(rpm.pc.in rpm.pc @ONLY) install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E env pkglibdir=${RPM_CONFIGDIR} ${CMAKE_SOURCE_DIR}/installplatform rpmrc platform macros ${RPMCANONVENDOR} ${RPMCANONOS} ${RPMCANONGNU} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})" ) endfunction() include(CheckLibraryExists) include(CheckFunctionExists) include(CheckIncludeFile) include(CheckCCompilerFlag) include(CheckVariableExists) set(OPTFUNCS stpcpy stpncpy putenv mempcpy fdatasync lutimes mergesort getauxval setprogname __progname syncfs sched_getaffinity unshare secure_getenv __secure_getenv mremap strchrnul ) set(REQFUNCS mkstemp getcwd basename dirname realpath setenv unsetenv regcomp utimes getline localtime_r statvfs getaddrinfo openat mkdirat fstatat linkat symlinkat mkfifoat mknodat unlinkat renameat utimensat fchmodat fchownat stpcpy stpncpy ) find_package(PkgConfig REQUIRED) find_package(Lua 5.2 REQUIRED) find_package(ZLIB REQUIRED) if (WITH_BZIP2) find_package(BZip2 REQUIRED) endif() if (WITH_ICONV) find_package(Iconv REQUIRED) endif() pkg_check_modules(POPT REQUIRED IMPORTED_TARGET popt) if (WITH_READLINE) pkg_check_modules(READLINE REQUIRED IMPORTED_TARGET readline) endif() if (WITH_ZSTD) pkg_check_modules(ZSTD REQUIRED IMPORTED_TARGET libzstd>=1.3.8) endif() if (WITH_LIBELF) pkg_check_modules(LIBELF REQUIRED IMPORTED_TARGET libelf) endif() if (WITH_LIBDW) pkg_check_modules(LIBDW REQUIRED IMPORTED_TARGET libdw) endif() if (WITH_LIBLZMA) pkg_check_modules(LIBLZMA REQUIRED IMPORTED_TARGET liblzma>=5.2.0) endif() pkg_check_modules(LIBARCHIVE REQUIRED IMPORTED_TARGET libarchive) # Lua module does not ship an IMPORTED target, define our own add_library(LUA::LUA INTERFACE IMPORTED) set_target_properties(LUA::LUA PROPERTIES INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}") # file >= 5.39 ships a pkg-config, may move to that later add_library(MAGIC::MAGIC UNKNOWN IMPORTED) find_library(MAGIC_LIBRARY NAMES magic REQUIRED) find_path(MAGIC_INCLUDE_DIR NAMES magic.h REQUIRED) mark_as_advanced(MAGIC_LIBRARY) mark_as_advanced(MAGIC_INCLUDE_DIR) set_target_properties(MAGIC::MAGIC PROPERTIES IMPORTED_LOCATION "${MAGIC_LIBRARY}") target_include_directories(MAGIC::MAGIC INTERFACE "${MAGIC_INCLUDE_DIR}") if (ENABLE_OPENMP) find_package(OpenMP 4.5 REQUIRED) endif() if (ENABLE_NLS) find_package(Intl REQUIRED) check_variable_exists(_nl_msg_cat_cntr HAVE_NL_MSG_CAT_CNTR) endif() if (ENABLE_SQLITE) pkg_check_modules(SQLITE REQUIRED IMPORTED_TARGET sqlite3>=3.22) list(APPEND db_backends sqlite) endif() if (ENABLE_NDB) list(APPEND db_backends ndb) endif() if (ENABLE_BDB_RO) list(APPEND db_backends bdb_ro) endif() list(APPEND db_backends dummy) if (ENABLE_PYTHON) find_package(Python3 3.10 COMPONENTS Interpreter Development REQUIRED) endif() if (WITH_CAP) pkg_check_modules(LIBCAP REQUIRED IMPORTED_TARGET libcap) endif() if (WITH_ACL) pkg_check_modules(LIBACL REQUIRED IMPORTED_TARGET libacl) endif() if (WITH_AUDIT) pkg_check_modules(AUDIT REQUIRED IMPORTED_TARGET audit) endif() if (WITH_SELINUX) pkg_check_modules(SELINUX REQUIRED IMPORTED_TARGET libselinux) endif() if (WITH_FSVERITY) pkg_check_modules(FSVERITY REQUIRED IMPORTED_TARGET libfsverity) endif() if (WITH_IMAEVM) list(APPEND REQFUNCS lsetxattr) check_library_exists(imaevm imaevm_signhash "" HAVE_IMAEVM_SIGNHASH) add_library(IMA::IMA UNKNOWN IMPORTED) find_path(IMA_INCLUDE_DIR NAMES imaevm.h REQUIRED) find_library(IMA_LIBRARY NAMES imaevm REQUIRED) mark_as_advanced(IMA_INCLUDE_DIR) mark_as_advanced(IMA_LIBRARY) set_target_properties(IMA::IMA PROPERTIES IMPORTED_LOCATION "${IMA_LIBRARY}") target_include_directories(IMA::IMA INTERFACE "${IMA_INCLUDE_DIR}") endif() find_program(AWK gawk mawk nawk awk REQUIRED) mark_as_advanced(AWK) find_program(FIND_DEBUGINFO find-debuginfo) mark_as_advanced(FIND_DEBUGINFO) find_program(PODMAN podman) mark_as_advanced(PODMAN) find_program(SCDOC NAMES scdoc REQUIRED) mark_as_advanced(SCDOC) find_program(SCD2HTML NAMES scd2html) mark_as_advanced(SCD2HTML) function(chkdef func req) string(TOUPPER ${func} FUNC) set(HAVENAME HAVE_${FUNC}) check_function_exists(${func} ${HAVENAME}) if (${req} AND NOT ${HAVENAME}) message(FATAL_ERROR "required function ${func} not found") endif() endfunction() foreach(f ${OPTFUNCS}) chkdef(${f} FALSE) endforeach() foreach(f ${REQFUNCS}) chkdef(${f} TRUE) endforeach() function(chkhdr inc req) string(MAKE_C_IDENTIFIER ${inc} ID) string(TOUPPER ${ID} INC) set(HAVENAME HAVE_${INC}) check_include_file(${inc} ${HAVENAME}) if (${req} AND NOT ${HAVENAME}) message(FATAL_ERROR "required include ${inc} not found") endif() endfunction() set(OPTINCS unistd.h limits.h getopt.h sys/utsname.h sys/systemcfg.h sys/param.h sys/auxv.h ) foreach(f ${OPTINCS}) chkhdr(${f} FALSE) endforeach() function(id0name var file) execute_process(COMMAND ${AWK} -F: "$3==0 {print $1;exit}" ${file} OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE name) if ("${name}" STREQUAL "") set(name root) endif() set(${var} ${name} PARENT_SCOPE) endfunction() id0name(UID_0_USER /etc/passwd) id0name(GID_0_GROUP /etc/group) # map module/package findings to config.h if (BZIP2_FOUND) set(HAVE_BZLIB_H 1) endif() if (LIBLZMA_FOUND) set(HAVE_LZMA_H 1) endif() if (Iconv_FOUND) set(HAVE_ICONV 1) endif() if (ZSTD_FOUND) set(HAVE_ZSTD 1) endif() if (READLINE_FOUND) set(HAVE_READLINE 1) endif() if (LIBELF_FOUND) set(HAVE_LIBELF 1) endif() if (LIBDW_FOUND) set(HAVE_LIBDW 1) check_library_exists(dw dwelf_elf_begin "" HAVE_DWELF_ELF_BEGIN) endif() check_symbol_exists(GLOB_ONLYDIR "glob.h" HAVE_GLOB_ONLYDIR) check_symbol_exists(major "sys/sysmacros.h" MAJOR_IN_SYSMACROS) if (NOT MAJOR_IN_SYSMACROS) check_symbol_exists(major "sys/mkdev.h" MAJOR_IN_MKDEV) endif() if (ENABLE_CUTF8) set(C_LOCALE "C.UTF-8") else() set(C_LOCALE "C") endif() message(INFO " using ${C_LOCALE} as default locale") configure_file(config.h.in config.h) add_compile_definitions(HAVE_CONFIG_H) add_compile_options(-Wall -Wpointer-arith -Wempty-body -Wformat-security) if (ENABLE_WERROR) add_compile_options(-Werror) endif() set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-prototypes -Wstrict-prototypes") if (ENABLE_ASAN) add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) endif() if (ENABLE_UBSAN) add_compile_options(-fsanitize=undefined) add_link_options(-fsanitize=undefined) endif() if (ENABLE_ASAN OR ENABLE_UBSAN) add_compile_options(-fno-omit-frame-pointer) endif() # try to ensure some compiler sanity and hardening options where supported foreach (flag -fno-strict-overflow -fno-delete-null-pointer-checks -fhardened) check_c_compiler_flag(${flag} compiler-supports${flag}) if (compiler-supports${flag}) add_compile_options(${flag}) endif() endforeach() # generated sources include_directories(${CMAKE_BINARY_DIR}) # global private includes include_directories(${CMAKE_SOURCE_DIR}/misc) # public headers include_directories(${CMAKE_SOURCE_DIR}/include) add_subdirectory(docs) add_subdirectory(include/rpm) add_subdirectory(misc) add_subdirectory(rpmio) add_subdirectory(lib) add_subdirectory(build) add_subdirectory(sign) add_subdirectory(tools) if (EXISTS ${CMAKE_SOURCE_DIR}/po/rpm.pot) add_subdirectory(po) endif() if (ENABLE_PYTHON) add_subdirectory(python) endif() set(RPM_PLUGINDIR ${CMAKE_INSTALL_FULL_LIBDIR}/rpm-plugins CACHE PATH "rpm plugin directory") if (ENABLE_PLUGINS) add_subdirectory(plugins) endif() add_subdirectory(fileattrs) add_subdirectory(scripts) makemacros() foreach(f macros rpmrc rpmpopt-${PROJECT_VERSION}) install(FILES ${CMAKE_BINARY_DIR}/${f} DESTINATION ${RPM_CONFIGDIR}) endforeach() if (ENABLE_TESTSUITE) add_subdirectory(tests) endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rpm.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) install(DIRECTORY DESTINATION ${RPM_CONFIGDIR}/lua) install(DIRECTORY DESTINATION ${RPM_CONFIGDIR}/macros.d) install(DIRECTORY DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/rpm) install(FILES CONTRIBUTING.md COPYING CREDITS INSTALL README TYPE DOC) add_custom_target(ChangeLog WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND git log --no-merges --no-decorate --output=${CMAKE_BINARY_DIR}/ChangeLog ) add_custom_command(OUTPUT po/rpm.pot COMMAND git submodule update --init) function(add_tarball targetname namever treeish) set(distfmt tar) set(tarname ${namever}.${distfmt}) set(distname ${tarname}.bz2) add_custom_target(${distname} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} VERBATIM DEPENDS ChangeLog po/rpm.pot COMMAND git archive --format=${distfmt} --output=${CMAKE_BINARY_DIR}/${tarname} --prefix=${namever}/ --add-file=${CMAKE_BINARY_DIR}/ChangeLog ${treeish} COMMAND git submodule foreach --quiet "git archive --prefix=${namever}/$sm_path/ \ --output=${CMAKE_BINARY_DIR}/$sha1.tar HEAD \ && tar --concatenate \ --file=${CMAKE_BINARY_DIR}/${tarname} \ ${CMAKE_BINARY_DIR}/$sha1.tar \ && rm -f ${CMAKE_BINARY_DIR}/$sha1.tar" COMMAND bzip2 -f ${CMAKE_BINARY_DIR}/${tarname} ) add_custom_target(${targetname} DEPENDS ${distname}) endfunction() add_tarball(dist ${PROJECT_NAME}-${PROJECT_VERSION} HEAD) if (EXISTS ${CMAKE_SOURCE_DIR}/.git) execute_process(COMMAND git rev-list --count HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE gitcount OUTPUT_STRIP_TRAILING_WHITESPACE) add_tarball(snapshot ${PROJECT_NAME}-${PROJECT_VERSION}-git${gitcount} HEAD) endif() # Voodoo rites to make our libraries cmake find_package() importable include(CMakePackageConfigHelpers) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/rpm-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/rpm ) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/rpm-config-version.cmake COMPATIBILITY SameMinorVersion ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rpm-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/rpm-config-version.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/rpm ) export(TARGETS librpm librpmio librpmbuild librpmsign FILE rpm-targets.cmake NAMESPACE rpm:: ) install(EXPORT rpm-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${CMAKE_PROJECT_NAME} NAMESPACE rpm:: ) rpm-software-management-rpm-3c1f23f/CONTRIBUTING.md000066400000000000000000000177151511627505500217320ustar00rootroot00000000000000# Contributing to RPM RPM has a history that goes back over two decades and is used by many different distributions - some providing support for a decade or more. There are hundreds of thousands RPM packages in use out there. This makes changes to RPM difficult and they often require thorough consideration on the implications and on compatibility. So while we invite contributions from all sides - especially from the multitude of RPM based distributions - RPM by its very nature is not an easy project to get into. To be included in RPM, contributions need to * solve a generic problem, not just for one user's special use-case * needs to have a future - the project does not add support for legacy technologies * align with [RPM's philosophy](https://rpm-software-management.github.io/rpm/manual/philosophy.html) and design * be cleanly implemented, using the common style of RPM **When planning on any non-trivial amount of work on RPM, please check with us first to avoid situations where efforts goes wasted!** ## Submitting Patches Patches should be submitted via GitHub pull requests (PRs) or via `git send-email` to the rpm-maint@lists.rpm.org mailing list. Use your real name as the commit author (git `user.name` config option) in a format suitable for the CREDITS file. Patches from pseudonyms or anonymous submitters will not be accepted. Each PR should be a single atomic change, meaning that it should be merged or rejected as a unit. It is okay for a PR to contain multiple commits if later commits are dependent on earlier ones. If earlier commits do not depend on later ones, they (should/should not) be submitted separately. Pull requests should clearly state if the code is "ready for inclusion" or if further work is needed. Proof of concept or request for comment pull requests are fine but need to be labeled as such. Information about the change should go into the commit message. This includes not only what is changed, but also why. The commit message needs to be self-contained. While references to GitHub tickets or external bug trackers are welcome the important information needs to be in the commit message itself. The rationale behind the change is often *more* important than the accompanying code! When changing existing code, it's equally important to understand why the code is the way it is now. Always check for history when suggesting change of behavior. When using GitHub pull requests enable "Allow edits from maintainers" to allow changes to your branch. Often changes are needed and allowing them in the branch in your repository keeps the discussion and changes within one place. Avoid updating the branch of the pull request if not necessary. On every change notifications are sent out to all maintainers and everyone contributing to the pull request. Keep your changes local or in another branch and only push to the branch of the pull request when you are ready for further feedback. This is a project with limited resources, please do not flood us. Please ensure "make ci" passes locally before submitting a PR. A pull request with failing CI tests will generally not be looked at, so keep on eye on the checks until you get all green. ## Coding Style ### Indentation RPM code uses 8-space tabs. Indentation is 4 spaces, with each group of 8 or more leading spaces replaced by a tab. If in doubt, 'indent -kr' generally produces acceptable formatting. In Vim, this can be achieved with ```vim set shiftwidth=4 tabstop=8 softtabstop=4 ``` In Emacs use ``` (setq-default c-basic-offset 4 indent-tabs-mode t) ``` The Markdown documentation uses 4-space indents. An `.editorconfig` file is provided at the root of the repository with these settings defined. Any supporting editor should pick them up automatically. Editorconfig support is available (built-in or via the appropriate plugin/extension) for nearly all text editors. ### Comments Comments should be used sparingly and for subtle and non-obvious things only. One-liners are prefered for in-line comments, unless there is something really special or complicated. Longer comments can go into the doc strings of functions, but background information should generally go into the commit message instead. Always prefer self-explanatory code over comments! Many times well-named helper variables can make even complicated code read almost like plain English. Comments are always in `/* ... */`, never `//`. The two comment styles in rpm codebase are: `/* Short and sweet */` ``` /* * Multi-line comments should be formatted * like this. */ ``` ### Error handling Avoid multiple exit points from functions, these are an invitation to resource leaks, cause code duplication and require touching unrelated code when adding new features. As a ballpark rule: If you need to free resource in more than one place, think again, and if you need to free it in more than two places you're doing it wrong. For local variables, always initialize pointers on entry and free them on central exit point, using `goto exit` or `goto err` style jumps to get there from errors. Assuming failure and only setting to success just before the exit label often works well and is widely used in the codebase. RPM uses custom allocator functions in place of standard library malloc() and friends. These never return NULL (they abort the process on failure), so do not check for it. Also all "destructor" type functions in rpm accept NULL arguments, don't check for it separately. On the C++ side, don't catch `std::bad_alloc`, it should abort the process just like the C allocators do. Other specific exceptions such as those coming from the filesystem should be caught to avoid leaking them into C code which cannot handle it - or use the non-throwing versions to begin with. ### Miscellaneous While many details differ and lot of it does not apply at all, the [Linux kernel coding style document](https://www.kernel.org/doc/html/latest/process/coding-style.html) contains lots of excellent guidance on good C programming practices if you filter out what is kernel specific. ## API/ABI considerations Most of RPM functionality resides in public libraries, whose API and ABI need to be considered on in all changes. There are multiple levels of visibility in RPM: 1. Public API, which is described by our public C headers. If a symbol is not in a public header, it's not considered public regardless of its visibility. 2. Internal cross-library API, which is described by various non-public C headers but whose symbols are visible in our public ABI. 3. Internal per-library APIs, which are described by various non-public C headers and whose symbols are hidden from the ABI on platforms where supported (marked by `RPM_GNUC_INTERNAL`) 4. Statically scoped symbols in sources. Internals are generally free to change, but public API must be carefully preserved. Adding new public APIs is generally not a problem, removing and changing existing ones can only be done in major version bumps and even there should be done conservatively. People are annoyed when their code stops compiling. ABI compatibility in RPM is tracked with [libtool versioning](https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html) Newly added symbols in the public ABI must prefixed with `rpm` except to maintain consistency where another pre-existing prefix (eg `header`) is used for historical reasons. All public APIs must be documented using Doxygen annotation. ## Portability RPM aims to be portable within POSIX compliant systems, the exact version requirements are documented in INSTALL. However, the primary platform of RPM is Linux, and that is the only platform supported by the RPM main development team. Patches to support other OS'es within the POSIX version requirements are generally accepted though. ## Translations Translations should be submitted through [Fedora Weblate](https://translate.fedoraproject.org/projects/rpm/), the upstream project cannot review translations. ## AI Policy As a policy, the RPM project does not accept use of generative AI in contributions. rpm-software-management-rpm-3c1f23f/COPYING000066400000000000000000001262431511627505500205310ustar00rootroot00000000000000RPM is covered under two separate licenses. The entire code base may be distributed under the terms of the GNU General Public License (GPL), which appears immediately below. Alternatively, all of the source code in the lib and rpmio subdirectories of the RPM source code distribution as well as any code derived from that code may instead be distributed under the GNU Library General Public License (LGPL), at the choice of the distributor. The complete text of the LGPL appears at the bottom of this file. This alternative is provided to enable applications to be linked against the RPM library (commonly called librpm) without forcing such applications to be distributed under the GPL. Any questions regarding the licensing of RPM should be addressed to rpm-maint@lists.rpm.org --------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Moe Ghoul, 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. --------------------------------------------------------------------------- GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, 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 library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, 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 companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, see . Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Moe Ghoul, President of Vice That's all there is to it! rpm-software-management-rpm-3c1f23f/CREDITS000066400000000000000000000072451511627505500205160ustar00rootroot00000000000000Initial work on RPM was done primarily by: Marc Ewing Erik Troan Over the years, code, documentation, translations, ideas, porting help, tests etc have been contributed by: Tero Aho Cerul Alain A S Alam Matthew Almond Stuart Anderson Jun Aruga Ed Bailey Lumir Balhar Donnie Barnes Hela Basa Stefan Berger Eric W. Biederman Anders F Bjorklund Frederic Bonnard Nikita Borisov Pádraig Brady Filipe Brandenburger Tony Breeds Stepan Broz Till Bubeck Ross Burton Héctor Daniel Cabrera Davide Cavalca Vitaly Chikunov Yuri Chornoivan Michal Čihař Nick Clifton Archie Cobbs Ralf Corsépius Kit Cosper Mike Crowe Rafal Cygnarowski Jean Delvare Kristof Depraetere Alex de Vries Tom de Vries Johannes Dewender Michal Domonkos Piotr Drąg Boris Egorov Karl Eichwalder Peter Eisentraut Oğuz Ersen W. L. Estes Giulio Eulisse Christophe Fergeau Florian Festi Jakub Filak Brian Elliott Finley Fred Fish Fabrice Fontaine Nikola Forró Gleb Fotengauer-Malinovskiy David Fox Artur Frysiak Stephen Gallagher Narek Galstyan Rafael Garcia-Suarez Guillaume Gardet Alexey Gladkov Neal Gompa Guido Grazioli David Greaves Aron Griffis Fionnuala Gunter Anssi Hannula Johan Heikkilä Olaf Hering Miro Hrončok Richard Hughes Tom Hughes Omer I.S Thomas Jarosch Zbigniew Jędrzejewski-Szmek Jakub Jelinek Alan Jenkins Hongxu Jia Howard Johnson Jeff Johnson Michael K. Johnson Tadashi Jokagi Peter Jones Richard W.M. Jones Guillem Jover Joseph L. Hartmann, Jr Robbie Harwood Juha Kallioinen Alexander Kanavin Alexandr D. Kanevskiy Eugene Kanter Igor Kanyuka Lubos Kardos Per Øyvind Karlsen Jiri Kastner Peter Kjellerstedt Tomasz Kłoczko Phil Knirsch Michal Kochanowicz Kir Kolyshkin Steve Kowalik Ales Kozumplik Jan Kratochvil Ernestas Kulik Jan Kuparinen Toshio Kuratomi Tim Landscheidt Hermann Lauer Steve Lawrence Alec Leamas Dimitri John Ledkov Charles Lee Elliot Lee Robin Lee Simon Lees Dominique Leuenberger Dmitry V. Levin Markus Linnala Martin Liška Benedict Lofstedt Till Maas Daniel Mach David Malcolm Michal Marek Panu Matilainen Shogo Matsumoto Jared Mauch Roland McGrath Kyle McMartin Joshua Megerman Petr Menšík Gordon Messmer Nigel Metheringham Arkadiusz Miśkiewicz Tomas Mlcoch Horacio Rodriguez Montero Tim Mooney David Martínez Moreno Eric Mumpower Paul Nasrat José Lemos Neto Phu Hung Nguyen Gustavo Niemeyer Izumo Nobuse Mikhail Novosyolov Jindrich Novy Michael Nyquisk Demi Marie Obenour Stanislav Ochotnicky Ruediger Oertel Denis Ollier Vít Ondruch Tomas Orsava Ruediger Oertel Rakesh Pandit Jan Pazdziora Thomas Petazzoni Jan Pokorný Orion Poplawski Siddhesh Poyarekar Richard Purdie Trần Ngọc Quân Jerome Quelin Pavel Raiskup Igor Raits Elena Reshetova Athos Ribeiro Fábio Rodrigues Ribeiro Sebastian Riedel Lubomir Rintel Peter Robinson Bernhard Rosenkränzer Pavol Rusnak Kamil Rytarowski Mark Salter Steve Sanbeg Tyson Sawyer Andreas Scherer Michal Schmidt Michael Schroeder Andreas Schwab Christopher Seawood Marc Seeger Vladimir D. Seleznev Misha Shnurapet Kirill A. Shutemov Jan Silhan Taylon Silmer Mukund Sivaraman Ville Skyttä Jeff Smith Jes Sorensen Radovan Sroka Miroslav Suchý Johnie Stafford Tom Stellard Hajime Taira Bob Tanner Pascal Terjan Gary Thomas Morgan Thomas Jason Tibbitts Jeff Tickle Ricky Tigg Michal Toman Alexey Tourbin Tom Tromey Chip Turner Göran Uddeborg Vincent Untz Pavlina Varekova Nicolas Vigier Thierry Vignaud Denys Vlasenko Jonathan Wakely Colin Walters Xiaofeng Wang Brett T. Warden Florian Weimer Karsten Weiss Bernhard M. Wiedemann Mark Wielaard Mimi Zohar rpm-software-management-rpm-3c1f23f/INSTALL000066400000000000000000000350651511627505500205300ustar00rootroot00000000000000To build RPM you will need several other packages: -------------------------------------------------- A C++ compiler that supports C++ 20 standard. When the default c++ compiler does not support C++ 20 you can tell cmake to use a different compiler by setting CMAKE_CXX_COMPILER, eg. cmake -DCMAKE_CXX_COMPILER=g++-13 Note that using a C++ compiler such as g++ is needed, gcc may seem to work but will not compile the code correctly. The cmake build system, version 3.18 or later. It is available from https://cmake.org/ The popt library for option parsing, must be version 1.13 or later. It is available from http://ftp.rpm.org/popt/ The debugedit >= 0.3 tools for producing debuginfo sub-packages. It is available from https://sourceware.org/debugedit/ Lua >= 5.2 library + development environment. Note that only the library is needed at runtime, RPM never calls external Lua interpreter for anything. Lua is available from http://www.lua.org The zlib library for compression support. You might also need/want the unzip executable for java jar dependency analysis. All available from http://www.gzip.org/zlib/ The libmagic (aka file) library for file type detection (used by rpmbuild). The source for the file utility + library is available from ftp://ftp.astron.com/pub/file/ The libarchive library for archive access, available from https://www.libarchive.org/ The scdoc manual page generator, available from https://git.sr.ht/~sircmpwn/scdoc You will need a cryptographic library to support digests and an OpenPGP implementation to support signatures. rpm-sequoia (>= 1.9.0 required) is the most complete option, covering both, and also the default: https://github.com/rpm-software-management/rpm-sequoia Use of rpm-sequoia is strongly recommended. It is built on a full OpenPGP implementation in a memory-safe language, configurable policy and user-relevant error messages. For more information, see https://sequoia-pgp.org/ However, for bootstrapping purposes it may be desireable to avoid the Rust dependency from rpm-sequoia. When building with -DWITH_SEQUOIA=OFF, rpm is built with OpenPGP support disabled. That means, you cannot sign packages, verify signatures or import keys, but otherwise you can build (and install) packages normally. In this mode, libgcrypt is used for crypthographic hash calculations by default, but alternatively OpenSSL can be selected by specifying -DWITH_OPENSSL=ON. Finally, it's still possible to use rpm's own legacy OpenPGP parser, but it is considered insecure and it's use is strongly discouraged: https://github.com/rpm-software-management/rpmpgp_legacy libgcrypt library is available from https://www.gnupg.org/software/libgcrypt/ If using the OpenSSL library for encryption, it must be version 1.0.2 or later. Note: when compiling against OpenSSL, there is a possible license incompatibility. For more details on this, see https://people.gnome.org/~markmc/openssl-and-the-gpl.html Some Linux distributions have different legal interpretations of this possible incompatibility. It is recommended to consult with a lawyer before building RPM against OpenSSL. Fedora: https://fedoraproject.org/wiki/Licensing:FAQ#What.27s_the_deal_with_the_OpenSSL_license.3F Debian: https://lists.debian.org/debian-legal/2002/10/msg00113.html The OpenSSL crypto library is available from https://www.openssl.org/ RPM needs a database engine for normal operation. The main options are "ndb" and "sqlite", both enabled by default but can be disabled with -DENABLE_NDB=OFF and -DENABLE_SQLITE=OFF respetively. Additionally standalone support for read-only BDB databases is available as "bdb_ro" (-DENABLE_BDB_RO=ON) to aid with migration from BDB. The ndb and bdb_ro backends have no external dependencies. SQLite >= 3.22.0 is required for the sqlite database backend. SQLite is available from https://www.sqlite.org/ SELinux support is enabled by default but can be disabled with -DWITH_SELINUX=OFF. libselinux is is available from http://www.nsa.gov/selinux/ It may be desired to install bzip2, gzip, and xz/lzma so that RPM can use these formats. Gzip is necessary to build packages that contain compressed tar balls, these are quite common on the Internet. These are available from http://www.gzip.org http://www.bzip.org http://tukaani.org/xz/ Python bindings to RPM library are built by default, but it can be disabled with -DENABLE_PYTHON=OFF. You'll need to have Python >= 3.10 runtime and C API development environment installed. Python is available from: http://www.python.org/ POSIX.1e draft 15 file capabilities support is enabled by default, to disable use -DWITH_CAP=OFF. You'll also need recent libcap, available from: http://ftp.kernel.org/pub/linux/libs/security/linux-privs/libcap2/ POSIX 1003.1e draft 17 ACL verification support is enabled by default, to disable use -DWITH_ACL=OFF. You'll also need the ACL library, available from: ftp://oss.sgi.com/projects/xfs/cmd_tars/ For best results you should compile with GCC and GNU Make. Users have reported difficulty with other build tools (any patches to lift these dependencies are welcome). Both GCC and GNU Make available from http://www.gnu.org/ If National Language Support (NLS) is desired you will need gnu gettext (currently this is required to build rpm but we hope to lift this requirement soon), available from http://www.gnu.org/ By default, Rpm uses C.UTF-8 locale as it's default locale. If your environment does not support this, you can make rpm use the traditional C locale with -DENABLE_CUTF8=OFF. If you are going to hack the sources (or compile from source repository) you will need most of the GNU development tools including: autoconf, automake, gettext, libtool, makeinfo, perl, GNU m4, GNU tar available from http://www.gnu.org/ If you plan on using cryptographic signatures you will need a version of GPG, available from http://www.gnupg.org/ If you wish to build the API reference manual (in HTML format), use -DWITH_DOXYGEN=ON. This requires Doxygen which is available from https://github.com/doxygen/ OpenMP multithreading support is automatically enabled if your C compiler has support for OpenMP version 4.5 or higher (to disable, use -DENABLE_OPENMP=OFF option). For GCC, OpenMP 4.5 is fully supported since GCC 6.1, which is available from http://www.gnu.org/ If glibc is used, it needs to be of version 2.27 or newer. Older glibc versions have a longstanding bug where glob() does not return dangling symlinks as matches, which broadly affects rpmbuild. Rpm requires a POSIX.1-2008 level operating system. To compile RPM: -------------- mkdir _build cd _build cmake .. You can view the various cmake compile options with: cmake -L .. and fine tune the built components + features, eg: cmake -DENABLE_PYTHON=OFF .. If you have tools outside the regular FHS paths that you need rpm to find, you can augment the search path via MYPATH environment variable to cmake. Now build the system with: make and then install with: make install By default, rpm installs a series of default platforms based on the CPU architecture names in subdirectories called /usr/lib/platform/- This is enough for many distributions. However, some distributions may use more specific platform names that refer to particular computer systems, like SBCs or specific CPU tuning when compiling. Examples of such platform names are: "genericx86_64", "intel_skylake_64", "raspberrypi_armv7", "raspberrypi_armv8", etc. If the platform name is put into /etc/rpm/platform, then rpmbuild uses it and the only macros file rpmbuild looks for is /usr/lib/platform/`cat /etc/rpm/platform`-/macros If this file does not exist, many rpm macros will not have their expected values set and e.g. %configure will fail when trying to run rpmbuild. To allow creating the macros file for such a custom platform, the shell variables listed below must be set. If RPM_CUSTOM_ARCH is not set, the rest is ignored. export RPM_CUSTOM_ARCH=genericx86_64 export RPM_CUSTOM_ISANAME=x86 export RPM_CUSTOM_ISABITS=64 export RPM_CUSTOM_CANONARCH=x86_64 export RPM_CUSTOM_CANONCOLOR=0 # to use /usr/lib for %_libdir export RPM_CUSTOM_CANONCOLOR=3 # to use /usr/lib64 for %_libdir make install This also creates and installs the new platform file e.g. /usr/lib/platform/genericx86_64-linux/macros Rpm comes with an automated self-test suite. The test-suite requires podman (https://github.com/containers/podman/) or docker (https://github.com/docker/). It is disabled by default to avoid the dependencies but can be enabled with -DENABLE_TESTSUITE=ON. The test-suite can be executed with: make check Additionally, rpm supports executing the test-suite in a way that's similar to how the project CI is set up. You can do that with: make ci Finally, if you wish to prepare an rpm source tar ball, you should do make dist To package RPM: -------------- After RPM has been installed you can run rpm to build an rpm package. Edit the rpm.spec file to mirror any special steps you needed to follow to make rpm compile and change the specfile to match your taste. You will need to put the rpm source tar file into the SOURCES directory and we suggest putting the specfile in the SPECS directory, then run rpmbuild -ba rpm.spec. You will end up with two rpms which can be found in RPMS and SRPMS. If you are going to install rpm on machines with OS package managers other then rpm, you may choose to install the base rpm package via a cpio instead of a tar file. Instead of running "make tar" during the build process, as described above, use the base rpm packages to create a cpio. After the rpms have been created run rpm2cpio on the base rpm package, this will give you a cpio package which can then use to install rpm on a new system. rpm2cpio rpm-4.0-1.solaris2.6-sparc.rpm > rpm-4.0-1.solaris2.6-sparc.cpio Non Linux Configuration Issues: ------------------------------ OS dependencies: ---------------- Under RPM based Linux distributions all libraries (in fact all files distributed with the OS) are under RPM control and this section is not an issue. RPM will need to be informed of all the dependencies which were satisfied before RPM was installed. Typically this only refers to libraries that are installed by the OS, but may include other libraries and packages which are available at the time RPM is installed and will not under RPM control. Another common example of libraries which may need dependency provisions are precompiled libraries which are installed by the OS package manager during system build time. The list of dependencies you will wish to load into RPM will depend on exactly how you bootstrap RPM onto your system and what parts of the system you put into packages as well as on the specific OS you are using. The script vpkg-provides.sh can be used to generate a package which will satisfy the dependencies on your system. To run it you will need to create a specfile header for this empty package and run the progam with: --spec_header '/path/to/os-base-header.spec and if you wish to ensure that some directories are not traversed you can use the option: --ignore_dirs 'grep-E|pattern|of|paths|to|ignore By default the generated rpm will include a %verifyscript to verify checksum of all files traversed has not changed. This additional check can be suppressed with: --no_verify The result of running the script will be a specfile which will create a package continging all the dependencies found on the system. There will be one provides line for each depednecy. The package will contain none of the actual OS library files as it is assumed they are already on your system and managed by other means. Here is a example (truncated) of the provides lines used by one user of Digital Unix. (I have put several provides on the same line for brevity) provides: /bin/sh /usr/bin/ksh /usr/bin/csh provides: libc.so.osf.1 libm.so.osf.1 libcurses.so.xpg4 libdb.so.osf.1 provides: libX11.so libXaw.so.6.0 libXext.so libXm.so.motif1.2 libXmu.so provides: libdnet_stub.so.osf.1 libsecurity.so.osf.1 libpthread.so.osf.1 provides: libexc.so.osf.1 libmach.so.osf.1 libdps.so libdpstk.so The script vpkg-provides2.sh is underdevelopment as a more advanced version of vpkg-provides.sh which is aware of many different unix vendor packaging schemes. It will create one "dependency package" for each unix package your OS vendor installed. rpmfilename: ----------- If you plan on packaging for more then one OS you may want to edit /etc/macros or /usr/lib/rpm/macros and change the line which has rpmfilename to something which include both the %{_target_os} and %{_target_cpu}. This will cause the name of the generated rpm files to the operating system name as well as the architecture which the rpm runs under. The line to change looks like: %_rpmfilename %%{ARCH}/%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm you may wish to include both the %{_target_os} and %{_target_cpu} in the final base name, so that it's easier to distinguish between what package is appropriate for a particular arch-os-version combo. We suggest: %_rpmfilename %%{_target_platform/%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{_target_platform}.rpm There is no %{_target_os_version} tag, so if you need to also distinguish between RPMs for certain versions of the OS, you can hard-code the version in the rpmrc on the build machine, so that .rpm files are generated with the version as part of the filename. For example when one user builds RPMs for Digital Unix 4.0b and 4.0d, optimization is important and he will build one set of RPMs for the EV4 processor and another set for the EV56 processor. He specifies both the OS version (if it's important, as it is for a few packages) and the processor version by default by setting a special rpmfilename: on the particular build machine. The "rpmfilename: "tag on one machine (Digital Unix 4.0d, EV56 PWS 433) looks like: rpmfilename: %{_target_os}/4.0d/%{_target_cpu}/%{name}-%{version}-%{release}.%{_target_os}-%{_target_cpu}ev56.rpm For package `foo-1.1', at build time that would translate into: osf1/4.0d/alpha/foo-1.1-1.osf1-alphaev56.rpm The hyphen between the %{_target_cpu} and ev56 is left out for compatibility with GNU Config.guess and because `alphaev56' looks more "normal" to people with an alpha than alpha-ev56 for someone on an Intel Pentium Pro would want `i586pro' over `i586-pro', but it does make parsing this filename by other programs a bit more difficult. GPG --- To use the signing features of rpm, you will need to configure certain rpm macros in ~/.rpmmacros: %_gpg_name %_gpg_path %(echo $HOME)/.gnupg rpm-software-management-rpm-3c1f23f/README000066400000000000000000000011361511627505500203470ustar00rootroot00000000000000This is RPM, the RPM Package Manager. The latest releases are always available at: http://rpm.org/download.html Additional RPM documentation (papers, slides, HOWTOs) can also be found at the same site: http://rpm.org. http://rpm.org/community all rpm related mailing lists. RPM was originally written by: Erik Troan Marc Ewing See the CREDITS file for a list of folks who have helped us out tremendously. RPM is Copyright (c) 1998 by Red Hat Software, Inc., and may be distributed under the terms of the GPL and LGPL (see the file COPYING for details). rpm-software-management-rpm-3c1f23f/build/000077500000000000000000000000001511627505500205655ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/build/CMakeLists.txt000066400000000000000000000027471511627505500233370ustar00rootroot00000000000000add_library(librpmbuild SHARED) set_target_properties(librpmbuild PROPERTIES VERSION ${RPM_LIBVERSION} SOVERSION ${RPM_SOVERSION} ) target_sources(librpmbuild PRIVATE build.cc files.cc misc.cc pack.cc parseSimpleScript.cc parseChangelog.cc parseDescription.cc parseFiles.cc parsePreamble.cc parsePrep.cc parseReqs.cc parseScript.cc parseSpec.cc parseList.cc reqprov.cc rpmfc.cc spec.cc parsePolicies.cc policies.cc rpmbuild_internal.hh rpmbuild_misc.hh speclua.cc ) target_include_directories(librpmbuild PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/rpmio ${CMAKE_SOURCE_DIR}/lib ${Intl_INCLUDE_DIRS} PUBLIC $ $ ) target_link_libraries(librpmbuild PUBLIC librpmio librpm librpmsign) target_link_libraries(librpmbuild PRIVATE libmisc PkgConfig::POPT LUA::LUA MAGIC::MAGIC ${Intl_LIBRARIES} ) if (LIBDW_FOUND) target_link_libraries(librpmbuild PRIVATE PkgConfig::LIBDW) endif() if (LIBELF_FOUND) target_link_libraries(librpmbuild PRIVATE PkgConfig::LIBELF) endif() if (Iconv_FOUND) target_link_libraries(librpmbuild PRIVATE Iconv::Iconv) endif() if(WITH_CAP) target_link_libraries(librpmbuild PRIVATE PkgConfig::LIBCAP) endif() if(OpenMP_C_FOUND) target_link_libraries(librpmbuild PRIVATE OpenMP::OpenMP_C) endif() if (OpenMP_C_FOUND) target_link_libraries(librpmbuild PRIVATE OpenMP::OpenMP_CXX) endif() install(TARGETS librpmbuild EXPORT rpm-targets) rpm-software-management-rpm-3c1f23f/build/build.cc000066400000000000000000000402441511627505500221770ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/build.c * Top-level build dispatcher. */ #include "system.h" #include #include #include #include #include #include #ifdef ENABLE_OPENMP #include #endif #include #include #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "rpmmacro_internal.hh" #include "debug.h" using namespace rpm; static rpm_time_t getBuildTime(void) { rpm_time_t buildTime = 0; char *btMacro; char *srcdate; time_t epoch; char *endptr; char *timestr = NULL; btMacro = rpmExpand("%{?_buildtime}", NULL); if (*btMacro) { errno = 0; epoch = strtol(btMacro, &endptr, 10); if (btMacro == endptr || *endptr || errno != 0) rpmlog(RPMLOG_ERR, _("unable to parse _buildtime macro\n")); else buildTime = (uint32_t) epoch; } else if ((srcdate = getenv("SOURCE_DATE_EPOCH")) != NULL && rpmExpandNumeric("%{?use_source_date_epoch_as_buildtime}")) { errno = 0; epoch = strtol(srcdate, &endptr, 10); if (srcdate == endptr || *endptr || errno != 0) rpmlog(RPMLOG_ERR, _("unable to parse SOURCE_DATE_EPOCH\n")); else buildTime = (uint32_t) epoch; } else buildTime = (uint32_t) time(NULL); free(btMacro); rasprintf(×tr, "%u", buildTime); setenv("RPM_BUILD_TIME", timestr, 1); free(timestr); return buildTime; } static char * buildHost(void) { char* hostname; char *bhMacro; bhMacro = rpmExpand("%{?_buildhost}", NULL); if (strcmp(bhMacro, "") != 0) { rasprintf(&hostname, "%s", bhMacro); } else { hostname = (char *)rcalloc(NI_MAXHOST + 1, sizeof(*hostname)); if (gethostname(hostname, NI_MAXHOST) == 0) { struct addrinfo *ai, hints; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) { strcpy(hostname, ai->ai_canonname); freeaddrinfo(ai); } else { rpmlog(RPMLOG_WARNING, _("Could not canonicalize hostname: %s\n"), hostname); } } } free(bhMacro); return(hostname); } /** */ static int doRmSource(rpmSpec spec) { struct Source *p; Package pkg; int rc = 0; for (p = spec->sources; p != NULL; p = p->next) { if (! (p->flags & RPMBUILD_ISNO)) { rc = unlink(p->path); if (rc) goto exit; } } for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { for (p = pkg->icon; p != NULL; p = p->next) { if (! (p->flags & RPMBUILD_ISNO)) { rc = unlink(p->path); if (rc) goto exit; } } } exit: return rc; } /* * @todo Single use by %%doc in files.c prevents static. */ rpmRC doScript(rpmSpec spec, rpmBuildFlags what, const char *name, const char *sb, int test, StringBuf * sb_stdoutp) { char *scriptName = NULL; char * buildDir = rpmGenPath(spec->rootDir, "%{builddir}", ""); char * buildSubdir = rpmGetPath("%{?buildsubdir}", NULL); char * buildCmd = NULL; char * buildTemplate = NULL; char * buildPost = NULL; const char * mTemplate = NULL; const char * mCmd = NULL; const char * mPost = NULL; int argc = 0; const char **argv = NULL; FILE * fp = NULL; FD_t fd = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ switch (what) { case RPMBUILD_MKBUILDDIR: mTemplate = "%{__spec_builddir_template}"; mPost = "%{__spec_builddir_post}"; mCmd = "%{__spec_builddir_cmd}"; break; case RPMBUILD_PREP: mTemplate = "%{__spec_prep_template}"; mPost = "%{__spec_prep_post}"; mCmd = "%{__spec_prep_cmd}"; break; case RPMBUILD_BUILDREQUIRES: mTemplate = "%{__spec_buildrequires_template}"; mPost = "%{__spec_buildrequires_post}"; mCmd = "%{__spec_buildrequires_cmd}"; break; case RPMBUILD_CONF: mTemplate = "%{__spec_conf_template}"; mPost = "%{__spec_conf_post}"; mCmd = "%{__spec_conf_cmd}"; break; case RPMBUILD_BUILD: mTemplate = "%{__spec_build_template}"; mPost = "%{__spec_build_post}"; mCmd = "%{__spec_build_cmd}"; break; case RPMBUILD_INSTALL: mTemplate = "%{__spec_install_template}"; mPost = "%{__spec_install_post}"; mCmd = "%{__spec_install_cmd}"; break; case RPMBUILD_CHECK: mTemplate = "%{__spec_check_template}"; mPost = "%{__spec_check_post}"; mCmd = "%{__spec_check_cmd}"; break; case RPMBUILD_CLEAN: mTemplate = "%{__spec_clean_template}"; mPost = "%{__spec_clean_post}"; mCmd = "%{__spec_clean_cmd}"; break; case RPMBUILD_RMBUILD: mTemplate = "%{__spec_clean_template}"; mPost = "%{__spec_clean_post}"; mCmd = "%{__spec_clean_cmd}"; break; case RPMBUILD_STRINGBUF: default: mTemplate = "%{___build_template}"; mPost = "%{___build_post}"; mCmd = "%{___build_cmd}"; break; } if ((what != RPMBUILD_RMBUILD) && sb == NULL) { rc = RPMRC_OK; goto exit; } fd = rpmMkTempFile(spec->rootDir, &scriptName); if (Ferror(fd)) { rpmlog(RPMLOG_ERR, _("Unable to open temp file: %s\n"), Fstrerror(fd)); goto exit; } if ((fp = fdopen(Fileno(fd), "w")) == NULL) { rpmlog(RPMLOG_ERR, _("Unable to open stream: %s\n"), strerror(errno)); goto exit; } buildTemplate = rpmExpand(mTemplate, NULL); buildPost = rpmExpand(mPost, NULL); (void) fputs(buildTemplate, fp); if (what != RPMBUILD_MKBUILDDIR && what != RPMBUILD_PREP && what != RPMBUILD_RMBUILD && buildSubdir[0] != '\0') fprintf(fp, "cd '%s'\n", buildSubdir); if (what == RPMBUILD_RMBUILD) { char *fix = rpmExpand("%{_fixperms}", NULL); fprintf(fp, "test -d '%s' && %s '%s'\n", spec->buildDir, fix, spec->buildDir); fprintf(fp, "rm -rf '%s'\n", spec->buildDir); free(fix); } else if (sb != NULL) fprintf(fp, "%s", sb); (void) fputs(buildPost, fp); (void) fclose(fp); if (test) { rc = RPMRC_OK; goto exit; } if (buildDir && buildDir[0] != '/') { goto exit; } buildCmd = rpmExpand(mCmd, " ", scriptName, NULL); (void) poptParseArgvString(buildCmd, &argc, &argv); if (sb_stdoutp && *sb_stdoutp) *sb_stdoutp = freeStringBuf(*sb_stdoutp); rpmlog(RPMLOG_NOTICE, _("Executing(%s): %s\n"), name, buildCmd); if (rpmfcExec((ARGV_const_t)argv, NULL, sb_stdoutp, 1, buildSubdir)) { rpmlog(RPMLOG_ERR, _("Bad exit status from %s (%s)\n"), scriptName, name); goto exit; } rc = RPMRC_OK; exit: Fclose(fd); if (scriptName) { if (rc == RPMRC_OK && !rpmIsDebug()) (void) unlink(scriptName); free(scriptName); } free(argv); free(buildCmd); free(buildTemplate); free(buildPost); free(buildSubdir); free(buildDir); return rc; } static int doBuildRequires(rpmSpec spec, int test) { StringBuf sb_stdout = NULL; const char *buildrequires = rpmSpecGetSection(spec, RPMBUILD_BUILDREQUIRES); int outc; ARGV_t output = NULL; int rc = 1; /* assume failure */ if (!buildrequires) { rc = RPMRC_OK; goto exit; } if ((rc = doScript(spec, RPMBUILD_BUILDREQUIRES, "%generate_buildrequires", buildrequires, test, &sb_stdout))) goto exit; /* add results to requires of the srpm */ argvSplit(&output, getStringBuf(sb_stdout), "\n\r"); outc = argvCount(output); for (int i = 0; i < outc; i++) { if (parseRCPOT(spec, spec->sourcePackage, output[i], RPMTAG_REQUIRENAME, 0, RPMSENSE_FIND_REQUIRES, addReqProvPkg, NULL)) goto exit; } rpmdsPutToHeader( *packageDependencies(spec->sourcePackage, RPMTAG_REQUIRENAME), spec->sourcePackage->header); rc = RPMRC_MISSINGBUILDREQUIRES; exit: freeStringBuf(sb_stdout); argvFree(output); return rc; } static int doCheckBuildRequires(rpmts ts, rpmSpec spec, int test) { int rc = RPMRC_OK; rpmps ps = rpmSpecCheckDeps(ts, spec); if (ps) { rpmlog(RPMLOG_ERR, _("Failed build dependencies:\n")); rpmpsPrint(NULL, ps); rc = RPMRC_MISSINGBUILDREQUIRES; } rpmpsFree(ps); return rc; } static rpmRC doBuildDir(rpmSpec spec, int test, int inPlace, StringBuf *sbp) { auto [ ign, doDir ] = macros().expand({ "test -d '", spec->buildDir, "' && ", "%{_fixperms} '", spec->buildDir, "'\n", "%{__rm} -rf '", spec->buildDir, "'\n", "%{__mkdir_p} '", spec->buildDir, "'\n", "%{__mkdir_p} '%{specpartsdir}'\n", }); if (inPlace) { /* note that pwd needs to be from parse, not build time */ auto [ ign, buf ] = macros().expand("%{__ln} -s %(pwd) %{builddir}/%{buildsubdir}"); doDir += buf; } rpmRC rc = doScript(spec, RPMBUILD_MKBUILDDIR, "%mkbuilddir", doDir.c_str(), test, sbp); if (rc) { rpmlog(RPMLOG_ERR, _("failed to create package build directory %s: %s\n"), spec->buildDir, strerror(errno)); } return rc; } static int buildSpec(rpmts ts, BTA_t buildArgs, rpmSpec spec, int what) { int rc = RPMRC_OK; int missing_buildreqs = 0; int test = (what & RPMBUILD_NOBUILD); char *cookie = buildArgs->cookie ? xstrdup(buildArgs->cookie) : NULL; /* handle quiet mode by capturing the output into a sink buffer */ StringBuf sink = NULL; StringBuf *sbp = rpmIsVerbose() ? NULL : &sink; #ifdef ENABLE_OPENMP /* Set number of OMP threads centrally */ int prev_threads = omp_get_num_threads(); int nthreads = rpmExpandNumeric("%{?_smp_build_nthreads}"); int nthreads_max = rpmExpandNumeric("%{?_smp_nthreads_max}"); if (nthreads <= 0) nthreads = omp_get_max_threads(); if (nthreads_max > 0 && nthreads > nthreads_max) nthreads = nthreads_max; if (nthreads > 0) omp_set_num_threads(nthreads); #endif if (rpmExpandNumeric("%{?source_date_epoch_from_changelog}") && getenv("SOURCE_DATE_EPOCH") == NULL) { /* Use date of first (== latest) changelog entry */ Header h = spec->packages->header; struct rpmtd_s td; if (headerGet(h, RPMTAG_CHANGELOGTIME, &td, (HEADERGET_MINMEM|HEADERGET_RAW))) { char sdestr[22]; long long sdeint = rpmtdGetNumber(&td); if (sdeint % 86400 == 43200) /* date was rounded to 12:00 */ /* make sure it is in the past, so that clamping times works */ sdeint -= 43200; snprintf(sdestr, sizeof(sdestr), "%lli", sdeint); rpmlog(RPMLOG_NOTICE, _("setting %s=%s\n"), "SOURCE_DATE_EPOCH", sdestr); setenv("SOURCE_DATE_EPOCH", sdestr, 0); rpmtdFreeData(&td); } else { rpmlog(RPMLOG_WARNING, _("%%source_date_epoch_from_changelog is set, but " "%%changelog has no entries to take a date from\n")); } } spec->buildTime = getBuildTime(); spec->buildHost = buildHost(); /* Don't generate debug packages on noarch no matter what --target says */ const char *arch = headerGetString(spec->packages->header, RPMTAG_ARCH); if (rstreq(arch, "noarch")) rpmPushMacro(spec->macros, "_enable_debug_packages", NULL, "0", RMIL_SPEC); /* XXX TODO: rootDir is only relevant during build, eliminate from spec */ spec->rootDir = buildArgs->rootdir; if (!spec->recursing && spec->BACount) { int x; /* When iterating over BANames, do the source */ /* packaging on the first run, and skip RMSOURCE altogether */ if (spec->BASpecs != NULL) for (x = 0; x < spec->BACount; x++) { if ((rc = buildSpec(ts, buildArgs, spec->BASpecs[x], (what & ~RPMBUILD_RMSOURCE) | (x ? 0 : (what & RPMBUILD_PACKAGESOURCE))))) { goto exit; } } } else { int didBuild = (what & (RPMBUILD_PREP|RPMBUILD_CONF|RPMBUILD_BUILD|RPMBUILD_INSTALL)); int sourceOnly = ((what & RPMBUILD_PACKAGESOURCE) && !(what & (RPMBUILD_CONF|RPMBUILD_BUILD|RPMBUILD_INSTALL|RPMBUILD_PACKAGEBINARY))); int inPlace = (rpmExpandNumeric("%{?_build_in_place}") > 0); if (!rpmSpecGetSection(spec, RPMBUILD_BUILDREQUIRES) && sourceOnly) { /* don't run prep if not needed for source build */ /* with(out) dynamic build requires*/ what &= ~(RPMBUILD_PREP|RPMBUILD_MKBUILDDIR); } if (inPlace) { /* in-place builds counter-intuitively always have buildsubdir */ rpmPushMacro(spec->macros, "buildsubdir", NULL, "%{NAME}-%{VERSION}", RMIL_SPEC); if (didBuild) what |= RPMBUILD_MKBUILDDIR; what &= ~(RPMBUILD_PREP|RPMBUILD_RMBUILD); } /* If we don't create a build dir, we can't very well remove it */ if (!(what & RPMBUILD_MKBUILDDIR)) what &= ~(RPMBUILD_RMBUILD); if ((what & RPMBUILD_CHECKBUILDREQUIRES) && (rc = doCheckBuildRequires(ts, spec, test))) goto exit; if ((what & RPMBUILD_MKBUILDDIR) && (rc = doBuildDir(spec, test, inPlace, sbp))) goto exit; if ((what & RPMBUILD_PREP) && (rc = doScript(spec, RPMBUILD_PREP, "%prep", rpmSpecGetSection(spec, RPMBUILD_PREP), test, sbp))) goto exit; if (what & RPMBUILD_BUILDREQUIRES) rc = doBuildRequires(spec, test); if ((what & RPMBUILD_CHECKBUILDREQUIRES) && (rc == RPMRC_MISSINGBUILDREQUIRES)) rc = doCheckBuildRequires(ts, spec, test); if (rc == RPMRC_MISSINGBUILDREQUIRES) { if ((what & RPMBUILD_DUMPBUILDREQUIRES) && !(spec->flags & RPMSPEC_FORCE)) { /* Create buildreqs package */ char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR); free(spec->sourceRpmName); rasprintf(&spec->sourceRpmName, "%s.buildreqs.nosrc.rpm", nvr); free(nvr); /* free sources to not include them in the buildreqs package */ spec->sources = freeSources(spec->sources); spec->numSources = 0; missing_buildreqs = 1; what = RPMBUILD_PACKAGESOURCE; } else { rc = RPMRC_OK; } } else if (rc) { goto exit; } if ((what & RPMBUILD_CONF) && (rc = doScript(spec, RPMBUILD_BUILD, "%conf", rpmSpecGetSection(spec, RPMBUILD_CONF), test, sbp))) goto exit; if ((what & RPMBUILD_BUILD) && (rc = doScript(spec, RPMBUILD_BUILD, "%build", rpmSpecGetSection(spec, RPMBUILD_BUILD), test, sbp))) goto exit; if ((what & RPMBUILD_INSTALL) && (rc = doScript(spec, RPMBUILD_INSTALL, "%install", rpmSpecGetSection(spec, RPMBUILD_INSTALL), test, sbp))) goto exit; if (((what & RPMBUILD_INSTALL) || (what & RPMBUILD_PACKAGEBINARY) || (what & RPMBUILD_FILECHECK)) && (rc = parseGeneratedSpecs(spec))) goto exit; if ((what & RPMBUILD_CHECK) && (rc = doScript(spec, RPMBUILD_CHECK, "%check", rpmSpecGetSection(spec, RPMBUILD_CHECK), test, sbp))) goto exit; if ((what & RPMBUILD_PACKAGESOURCE) && (rc = processSourceFiles(spec, buildArgs->pkgFlags))) goto exit; if (((what & RPMBUILD_INSTALL) || (what & RPMBUILD_PACKAGEBINARY) || (what & RPMBUILD_FILECHECK)) && (rc = processBinaryFiles(spec, buildArgs->pkgFlags, what & RPMBUILD_INSTALL, test))) goto exit; if (((what & RPMBUILD_INSTALL) || (what & RPMBUILD_PACKAGEBINARY)) && (rc = processBinaryPolicies(spec, test))) goto exit; if (((what & RPMBUILD_PACKAGESOURCE) && !test) && (rc = packageSources(spec, &cookie))) goto exit; if (((what & RPMBUILD_PACKAGEBINARY) && !test) && (rc = packageBinaries(spec, cookie, (didBuild == 0)))) goto exit; if ((what & RPMBUILD_CLEAN) && (rc = doScript(spec, RPMBUILD_CLEAN, "%clean", rpmSpecGetSection(spec, RPMBUILD_CLEAN), test, sbp))) goto exit; if ((what & RPMBUILD_RMBUILD) && (rc = doScript(spec, RPMBUILD_RMBUILD, "rmbuild", NULL, test, sbp))) goto exit; } if (what & RPMBUILD_RMSOURCE) doRmSource(spec); if (what & RPMBUILD_RMSPEC) (void) unlink(spec->specFile); exit: #ifdef ENABLE_OPENMP omp_set_num_threads(prev_threads); #endif freeStringBuf(sink); free(cookie); spec->rootDir = NULL; /* Suppress summaries on --quiet */ if (rpmIsNormal()) { unsigned maskWarn = RPMLOG_MASK(RPMLOG_WARNING); unsigned maskErrs = RPMLOG_UPTO(RPMLOG_ERR); if (rpmlogGetNrecsByMask(maskWarn)) { rpmlog(RPMLOG_NOTICE, _("\nRPM build warnings:\n")); rpmlogPrintByMask(NULL, maskWarn); } if (rc != RPMRC_OK && rc != RPMRC_MISSINGBUILDREQUIRES) { if (rpmlogGetNrecsByMask(maskErrs)) { rpmlog(RPMLOG_NOTICE, _("\nRPM build errors:\n")); rpmlogPrintByMask(NULL, maskErrs); } } } if (missing_buildreqs && !rc) { rc = RPMRC_MISSINGBUILDREQUIRES; } if (rc == RPMRC_FAIL) { rc = 1; } return rc; } int rpmSpecBuild(rpmts ts, rpmSpec spec, BTA_t buildArgs) { /* buildSpec() can recurse with different buildAmount, pass it separately */ return buildSpec(ts, buildArgs, spec, buildArgs->buildAmount); } rpm-software-management-rpm-3c1f23f/build/files.cc000066400000000000000000002527171511627505500222140ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/files.c * The post-build, pre-packaging file tree walk to assemble the package * manifest. */ #include "system.h" #include #include #include #define MYALLPERMS 07777 #include #include #include #include #ifdef WITH_CAP #include #endif #ifdef HAVE_LIBDW #include #include #endif #include #include #include /* rpmDoDigest() */ #include #include #include "rpmio_internal.hh" /* XXX rpmioSlurp */ #include "rpmmacro_internal.hh" #include "rpmfts.h" #include "rpmfi_internal.hh" /* XXX fi->apath */ #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "debug.h" #include #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; } #define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;} #define SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;} /* the following defines must be in sync with the equally hardcoded paths from * scripts/find-debuginfo.sh */ #define BUILD_ID_DIR "/usr/lib/.build-id" #define DEBUG_SRC_DIR "/usr/src/debug" #define DEBUG_LIB_DIR "/usr/lib/debug" #define DEBUG_LIB_PREFIX "/usr/lib/debug/" #define DEBUG_ID_DIR "/usr/lib/debug/.build-id" #define DEBUG_DWZ_DIR "/usr/lib/debug/.dwz" /** */ enum specfFlags_e { SPECD_DEFFILEMODE = (1 << 0), SPECD_DEFDIRMODE = (1 << 1), SPECD_DEFUID = (1 << 2), SPECD_DEFGID = (1 << 3), SPECD_DEFVERIFY = (1 << 4), SPECD_FILEMODE = (1 << 8), SPECD_DIRMODE = (1 << 9), SPECD_UID = (1 << 10), SPECD_GID = (1 << 11), SPECD_VERIFY = (1 << 12) }; typedef rpmFlags specfFlags; /* internal %files parsing state attributes */ enum parseAttrs_e { RPMFILE_EXCLUDE = (1 << 16), /*!< from %%exclude */ RPMFILE_DOCDIR = (1 << 17), /*!< from %%docdir */ RPMFILE_DIR = (1 << 18), /*!< from %%dir */ RPMFILE_SPECIALDIR = (1 << 19), /*!< from special %%doc */ }; /* bits up to 15 (for now) reserved for exported rpmfileAttrs */ #define PARSEATTR_MASK 0x0000ffff /** */ struct FileListRec_s { struct stat fl_st; #define fl_dev fl_st.st_dev #define fl_ino fl_st.st_ino #define fl_mode fl_st.st_mode #define fl_nlink fl_st.st_nlink #define fl_rdev fl_st.st_rdev #define fl_size fl_st.st_size #define fl_mtime fl_st.st_mtime char *diskPath; /* get file from here */ char *cpioPath; /* filename in cpio archive */ rpmsid uname; rpmsid gname; unsigned flags; specfFlags specdFlags; /* which attributes have been explicitly specified. */ rpmVerifyFlags verifyFlags; char *langs; /* XXX locales separated with | */ char *caps; bool operator < (const FileListRec_s & other) const { return strcmp(cpioPath, other.cpioPath) < 0; } }; typedef struct FileListRec_s * FileListRec; /** */ typedef struct AttrRec_s { rpmsid ar_fmodestr; rpmsid ar_dmodestr; rpmsid ar_user; rpmsid ar_group; mode_t ar_fmode; mode_t ar_dmode; } * AttrRec; /* list of files */ static StringBuf check_fileList = NULL; typedef struct FileEntry_s { rpmfileAttrs attrFlags; specfFlags specdFlags; rpmVerifyFlags verifyFlags; struct AttrRec_s ar; ARGV_t langs; char *caps; /* these are only ever relevant for current entry */ unsigned devtype; unsigned devmajor; int devminor; int isDir; } * FileEntry; struct FileEntries_s { struct FileEntry_s defEntry; struct FileEntry_s curEntry; }; typedef struct specialDir_s { char * dirname; ARGV_t files; struct AttrRec_s ar; struct AttrRec_s def_ar; rpmFlags sdtype; std::vector entries; } * specialDir; using FileRecords = std::vector; /** * Package file tree walk data. */ typedef struct FileList_s { /* global filelist state */ char * buildRoot; size_t buildRootLen; int processingFailed; int haveCaps; int largeFiles; ARGV_t docDirs; rpmBuildPkgFlags pkgFlags; rpmstrPool pool; /* actual file records */ FileRecords files; /* active defaults */ struct FileEntry_s def; /* current file-entry state */ struct FileEntry_s cur; } * FileList; static void nullAttrRec(AttrRec ar) { memset(ar, 0, sizeof(*ar)); } static void dupAttrRec(const AttrRec oar, AttrRec nar) { if (oar == nar) return; *nar = *oar; /* struct assignment */ } /* Creates a default $defattr string. Can be used with argvAdd(). Caller owns the new string which needs to be freed when done. */ static char *mkattr(void) { char *s = NULL; rasprintf(&s, "%s(644,%s,%s,755)", "%defattr", UID_0_USER, GID_0_GROUP); return s; } static void copyFileEntry(FileEntry src, FileEntry dest) { /* Copying struct makes just shallow copy */ *dest = *src; /* Do also deep copying */ if (src->langs != NULL) { dest->langs = argvNew(); argvAppend(&dest->langs, src->langs); } if (src->caps != NULL) { dest->caps = xstrdup(src->caps); } } static void FileEntryFree(FileEntry entry) { argvFree(entry->langs); free(entry->caps); memset(entry, 0, sizeof(*entry)); } /** * strtokWithQuotes. * @param s string to tokenize * @param delim delimiter chars * @param[out] quotes 0 if unquoted, 1 if missing quote, 2 if quoted */ static char *strtokWithQuotes(char *s, const char *delim, int *quotes) { static char *olds = NULL; char *token; if (s == NULL) s = olds; if (s == NULL) return NULL; /* Skip leading delimiters */ s += strspn(s, delim); if (*s == '\0') return NULL; /* Leading quote escapes original delim until next quote */ *quotes = 0; if (*s == '"') { *quotes = 1; delim = "\""; s++; } /* Find the end of the token. */ token = s; while (!strchr(delim, *s)) { if (*s == '\\') s++; if (*s != '\0') s++; } /* Terminate it */ if (*s == '\0') { /* This token finishes the string */ olds = s; } else { /* Terminate the token and make olds point past it */ if (*s == '"' && *quotes) *quotes = 2; *s = '\0'; olds = s+1; } return token; } /** */ typedef const struct VFA { const char * attribute; int flag; } VFA_t; /** */ static VFA_t const verifyAttrs[] = { { "md5", RPMVERIFY_FILEDIGEST }, { "filedigest", RPMVERIFY_FILEDIGEST }, { "size", RPMVERIFY_FILESIZE }, { "link", RPMVERIFY_LINKTO }, { "user", RPMVERIFY_USER }, { "owner", RPMVERIFY_USER }, { "group", RPMVERIFY_GROUP }, { "mtime", RPMVERIFY_MTIME }, { "mode", RPMVERIFY_MODE }, { "rdev", RPMVERIFY_RDEV }, { "caps", RPMVERIFY_CAPS }, { NULL, 0 } }; static rpmFlags vfaMatch(VFA_t *attrs, const char *token, rpmFlags *flags) { VFA_t *vfa; for (vfa = attrs; vfa->attribute != NULL; vfa++) { if (rstreq(token, vfa->attribute)) { *flags |= vfa->flag; break; } } return vfa->flag; } /** * Parse %verify and %defverify from file manifest. * @param buf current spec file line * @param def parse for %defverify or %verify? * @param entry file entry data (current or default) * @return RPMRC_OK on success */ static rpmRC parseForVerify(char * buf, int def, FileEntry entry) { char *p, *pe, *q = NULL; const char *name = def ? "%defverify" : "%verify"; int negated = 0; rpmVerifyFlags verifyFlags = RPMVERIFY_NONE; rpmRC rc = RPMRC_FAIL; if ((p = strstr(buf, name)) == NULL) return RPMRC_OK; for (pe = p; (pe-p) < strlen(name); pe++) *pe = ' '; SKIPSPACE(pe); if (*pe != '(') { rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe); goto exit; } /* Bracket %*verify args */ *pe++ = ' '; for (p = pe; *pe && *pe != ')'; pe++) {}; if (*pe == '\0') { rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p); goto exit; } /* Localize. Erase parsed string */ q = (char *)xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); while (p <= pe) *p++ = ' '; for (p = q; *p != '\0'; p = pe) { SKIPWHITE(p); if (*p == '\0') break; pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; if (vfaMatch(verifyAttrs, p, &verifyFlags)) continue; if (rstreq(p, "not")) { negated ^= 1; } else { rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p); goto exit; } } entry->verifyFlags = negated ? ~(verifyFlags) : verifyFlags; entry->specdFlags |= SPECD_VERIFY; rc = RPMRC_OK; exit: free(q); return rc; } static int isAttrDefault(rpmstrPool pool, rpmsid arsid) { const char *ars = rpmstrPoolStr(pool, arsid); return (ars && ars[0] == '-' && ars[1] == '\0'); } /** * Parse %dev from file manifest. * @param buf current spec file line * @param cur current file entry data * @return RPMRC_OK on success */ static rpmRC parseForDev(char * buf, FileEntry cur) { const char * name; const char * errstr = NULL; char *p, *pe, *q = NULL; rpmRC rc = RPMRC_FAIL; /* assume error */ char *attr_parameters = NULL; if ((p = strstr(buf, (name = "%dev"))) == NULL) return RPMRC_OK; for (pe = p; (pe-p) < strlen(name); pe++) *pe = ' '; SKIPSPACE(pe); if (*pe != '(') { errstr = "'('"; goto exit; } /* Bracket %dev args */ *pe++ = ' '; for (p = pe; *pe && *pe != ')'; pe++) {}; if (*pe != ')') { errstr = "')'"; goto exit; } /* Localize. Erase parsed string */ q = (char *)xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); attr_parameters = (char *)xmalloc((pe-p) + 1); rstrlcpy(attr_parameters, p, (pe-p) + 1); while (p <= pe) *p++ = ' '; p = q; SKIPWHITE(p); pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; if (*p == 'b') cur->devtype = 'b'; else if (*p == 'c') cur->devtype = 'c'; else { errstr = "devtype"; goto exit; } p = pe; SKIPWHITE(p); pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0'; for (pe = p; *pe && risdigit(*pe); pe++) {} ; if (*pe == '\0') { cur->devmajor = atoi(p); if (!(cur->devmajor >= 0 && cur->devmajor < 256)) { errstr = "devmajor"; goto exit; } pe++; } else { errstr = "devmajor"; goto exit; } p = pe; SKIPWHITE(p); pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0'; for (pe = p; *pe && risdigit(*pe); pe++) {} ; if (*pe == '\0') { cur->devminor = atoi(p); if (!(cur->devminor >= 0 && cur->devminor < 256)) { errstr = "devminor"; goto exit; } } else { errstr = "devminor"; goto exit; } rc = RPMRC_OK; exit: if (rc) { rpmlog(RPMLOG_ERR, _("Missing %s in %s(%s)\n"), errstr, name, attr_parameters); } free(attr_parameters); free(q); return rc; } /** * Parse %attr and %defattr from file manifest. * @param pool string pool * @param buf current spec file line * @param def parse for %defattr or %attr? * @param entry file entry data (current / default) * @return 0 on success */ static rpmRC parseForAttr(rpmstrPool pool, char * buf, int def, FileEntry entry) { const char *name = def ? "%defattr" : "%attr"; char *p, *pe, *q = NULL; char *attr_parameters = NULL; int x; struct AttrRec_s arbuf; AttrRec ar = &arbuf; rpmRC rc = RPMRC_FAIL; if ((p = strstr(buf, name)) == NULL) return RPMRC_OK; for (pe = p; (pe-p) < strlen(name); pe++) *pe = ' '; SKIPSPACE(pe); if (*pe != '(') { rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe); goto exit; } /* Bracket %*attr args */ *pe++ = ' '; for (p = pe; *pe && *pe != ')'; pe++) {}; if (*pe == '\0') { rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p); goto exit; } if (def) { /* %defattr */ char *r = pe; r++; SKIPSPACE(r); if (*r != '\0') { rpmlog(RPMLOG_ERR, _("Non-white space follows %s(): %s\n"), name, r); goto exit; } } /* Localize. Erase parsed string */ q = (char *)xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); attr_parameters = (char *)xmalloc((pe-p) + 1); rstrlcpy(attr_parameters, p, (pe-p) + 1); while (p <= pe) *p++ = ' '; nullAttrRec(ar); p = q; SKIPWHITE(p); if (*p != '\0') { pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; ar->ar_fmodestr = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (*p != '\0') { pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; ar->ar_user = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (*p != '\0') { pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; ar->ar_group = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (*p != '\0' && def) { /* %defattr */ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; ar->ar_dmodestr = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') { rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, attr_parameters); goto exit; } /* Do a quick test on the mode argument and adjust for "-" */ if (ar->ar_fmodestr && !isAttrDefault(pool, ar->ar_fmodestr)) { unsigned int ui; x = sscanf(rpmstrPoolStr(pool, ar->ar_fmodestr), "%o", &ui); if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) { rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, attr_parameters); goto exit; } ar->ar_fmode = ui; } else { ar->ar_fmodestr = 0; } if (ar->ar_dmodestr && !isAttrDefault(pool, ar->ar_dmodestr)) { unsigned int ui; x = sscanf(rpmstrPoolStr(pool, ar->ar_dmodestr), "%o", &ui); if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) { rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, attr_parameters); goto exit; } ar->ar_dmode = ui; } else { ar->ar_dmodestr = 0; } if (!(ar->ar_user && !isAttrDefault(pool, ar->ar_user))) { ar->ar_user = 0; } if (!(ar->ar_group && !isAttrDefault(pool, ar->ar_group))) { ar->ar_group = 0; } dupAttrRec(ar, &(entry->ar)); /* XXX fix all this */ entry->specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE; rc = RPMRC_OK; exit: free(q); free(attr_parameters); return rc; } static VFA_t const configAttrs[] = { { "missingok", RPMFILE_MISSINGOK }, { "noreplace", RPMFILE_NOREPLACE }, { NULL, 0 } }; /** * Parse %config from file manifest. * @param buf current spec file line * @param cur current file entry data * @return RPMRC_OK on success */ static rpmRC parseForConfig(char * buf, FileEntry cur) { char *p, *pe, *q = NULL; const char *name; rpmRC rc = RPMRC_FAIL; if ((p = strstr(buf, (name = "%config"))) == NULL) return RPMRC_OK; cur->attrFlags |= RPMFILE_CONFIG; /* Erase "%config" token. */ for (pe = p; (pe-p) < strlen(name); pe++) *pe = ' '; SKIPSPACE(pe); if (*pe != '(') return RPMRC_OK; /* Bracket %config args */ *pe++ = ' '; for (p = pe; *pe && *pe != ')'; pe++) {}; if (*pe == '\0') { rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p); goto exit; } /* Localize. Erase parsed string. */ q = (char *)xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); while (p <= pe) *p++ = ' '; for (p = q; *p != '\0'; p = pe) { SKIPWHITE(p); if (*p == '\0') break; pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; if (!vfaMatch(configAttrs, p, &(cur->attrFlags))) { rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p); goto exit; } } rc = RPMRC_OK; exit: free(q); return rc; } static rpmRC addLang(ARGV_t *av, const char *lang, size_t n, const char *ent) { rpmRC rc = RPMRC_FAIL; char lbuf[n + 1]; rstrlcpy(lbuf, lang, sizeof(lbuf)); SKIPWHITE(ent); /* Sanity check locale length */ if (n < 1 || (n == 1 && *lang != 'C') || n >= 32) { rpmlog(RPMLOG_ERR, _("Unusual locale length: \"%s\" in %%lang(%s)\n"), lbuf, ent); goto exit; } /* Check for duplicate locales */ if (argvSearch(*av, lbuf, NULL)) { rpmlog(RPMLOG_WARNING, _("Duplicate locale %s in %%lang(%s)\n"), lbuf, ent); } else { argvAdd(av, lbuf); argvSort(*av, NULL); } rc = RPMRC_OK; exit: return rc; } /** * Parse %lang from file manifest. * @param buf current spec file line * @param cur current file entry data * @return RPMRC_OK on success */ static rpmRC parseForLang(char * buf, FileEntry cur) { char *p, *pe, *q = NULL; const char *name; rpmRC rc = RPMRC_FAIL; while ((p = strstr(buf, (name = "%lang"))) != NULL) { for (pe = p; (pe-p) < strlen(name); pe++) *pe = ' '; SKIPSPACE(pe); if (*pe != '(') { rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe); goto exit; } /* Bracket %lang args */ *pe = ' '; for (pe = p; *pe && *pe != ')'; pe++) {}; if (*pe == '\0') { rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p); goto exit; } /* Localize. Erase parsed string. */ q = (char *)xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); while (p <= pe) *p++ = ' '; /* Parse multiple arguments from %lang */ for (p = q; *p != '\0'; p = pe) { SKIPWHITE(p); pe = p; SKIPNONWHITE(pe); if (addLang(&(cur->langs), p, (pe-p), q)) goto exit; if (*pe == ',') pe++; /* skip , if present */ } q = _free(q); } rc = RPMRC_OK; exit: free(q); return rc; } /** * Parse %caps from file manifest. * @param buf current spec file line * @param cur current file entry data * @return RPMRC_OK on success */ static rpmRC parseForCaps(char * buf, FileEntry cur) { char *p, *pe, *q = NULL; const char *name; rpmRC rc = RPMRC_FAIL; if ((p = strstr(buf, (name = "%caps"))) == NULL) return RPMRC_OK; /* Erase "%caps" token. */ for (pe = p; (pe-p) < strlen(name); pe++) *pe = ' '; SKIPSPACE(pe); if (*pe != '(') return RPMRC_OK; /* Bracket %caps args */ *pe++ = ' '; for (p = pe; *pe && *pe != ')'; pe++) {}; if (*pe == '\0') { rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p); goto exit; } /* Localize. Erase parsed string. */ q = (char *)xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); while (p <= pe) *p++ = ' '; #ifdef WITH_CAP { char *captxt = NULL; cap_t fcaps = cap_from_text(q); if (fcaps == NULL) { rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q); goto exit; } /* run our string through cap_to_text() to get libcap presentation */ captxt = cap_to_text(fcaps, NULL); cur->caps = xstrdup(captxt); cap_free(captxt); cap_free(fcaps); } #else rpmlog(RPMLOG_ERR, _("File capability support not built in\n")); goto exit; #endif rc = RPMRC_OK; exit: free(q); return rc; } /** */ static VFA_t const virtualAttrs[] = { { "%dir", RPMFILE_DIR }, { "%docdir", RPMFILE_DOCDIR }, { "%doc", RPMFILE_DOC }, { "%ghost", RPMFILE_GHOST }, { "%exclude", RPMFILE_EXCLUDE }, { "%readme", RPMFILE_README }, { "%license", RPMFILE_LICENSE }, { "%pubkey", RPMFILE_PUBKEY }, { "%missingok", RPMFILE_MISSINGOK }, { "%artifact", RPMFILE_ARTIFACT }, { NULL, 0 } }; /** * Parse simple attributes (e.g. %dir) from file manifest. * @param buf current spec file line * @param cur current file entry data * @param[out] *fileNames file names * @return RPMRC_OK on success */ static rpmRC parseForSimple(char * buf, FileEntry cur, ARGV_t * fileNames) { char *s, *t, *end; const char *delim = " \t\n"; int quotes = 0; rpmRC res = RPMRC_OK; int allow_relative = (RPMFILE_PUBKEY|RPMFILE_DOC|RPMFILE_LICENSE); t = buf; while ((s = strtokWithQuotes(t, delim, "es)) != NULL) { t = NULL; end = s + strlen(s) - 1; /* Syntax checks */ if (quotes == 1) { rpmlog(RPMLOG_ERR, _("Missing quote: %s\n"), s); res = RPMRC_FAIL; break; } if (*end == '\n') { /* Newline escaping is not supported */ rpmlog(RPMLOG_ERR, _("Trailing backslash: %s\n"), s); res = RPMRC_FAIL; break; } /* Set flags for virtual file attributes */ if (vfaMatch(virtualAttrs, s, &(cur->attrFlags))) continue; /* normally paths need to be absolute */ if (*s != '/') { if (!(cur->attrFlags & allow_relative)) { rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s); res = RPMRC_FAIL; continue; } /* non-absolute %doc and %license paths are special */ if (cur->attrFlags & (RPMFILE_DOC | RPMFILE_LICENSE)) cur->attrFlags |= RPMFILE_SPECIALDIR; } if (!quotes) rpmUnescape(s, delim); else { rpmUnescape(s, "\""); s = rpmEscape(s, "?*[]{}"); } argvAdd(fileNames, s); if (quotes) free(s); } return res; } /** * Test if file is located in a %docdir. * @param docDirs doc dirs * @param fileName file path * @return 1 if doc file, 0 if not */ static int isDoc(ARGV_const_t docDirs, const char * fileName) { size_t k, l; k = strlen(fileName); for (ARGV_const_t dd = docDirs; *dd; dd++) { l = strlen(*dd); if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/') return 1; } return 0; } static int isLinkable(mode_t mode) { return (S_ISREG(mode) || S_ISLNK(mode)); } static int isHardLink(FileListRec flp, FileListRec tlp) { return ((isLinkable(flp->fl_mode) && isLinkable(tlp->fl_mode)) && ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) && (flp->fl_ino == tlp->fl_ino) && (flp->fl_dev == tlp->fl_dev)); } /** * Verify that file attributes scope over hardlinks correctly. * If partial hardlink sets are possible, then add tracking dependency. * @param files package file records * @return 1 if partial hardlink sets can exist, 0 otherwise. */ static int checkHardLinks(FileRecords & files) { for (int i = 0; i < files.size(); i++) { FileListRec ilp = &files[i]; if (!(isLinkable(ilp->fl_mode) && ilp->fl_nlink > 1)) continue; for (int j = i + 1; j < files.size(); j++) { FileListRec jlp = &files[j]; if (isHardLink(ilp, jlp)) { return 1; } } } return 0; } static int seenHardLink(FileRecords & files, FileListRec flp, rpm_ino_t *fileid) { for (FileListRec ilp = files.data(); ilp < flp; ilp++) { if (isHardLink(flp, ilp)) { *fileid = ilp - files.data(); return 1; } } return 0; } /** * Add file entries to header. * @todo Should directories have %doc/%config attributes? (#14531) * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead. * @param fl package file tree walk data * @param pkg (sub) package * @param isSrc pass 1 for source packages 0 otherwise */ static void genCpioListAndHeader(FileList fl, rpmSpec spec, Package pkg, int isSrc) { FileListRec flp; char buf[BUFSIZ]; int i, npaths = 0; int fail_on_dupes = rpmExpandNumeric("%{?_duplicate_files_terminate_build}") > 0; uint32_t defaultalgo = RPM_HASH_SHA256, digestalgo; rpm_loff_t totalFileSize = 0; Header h = pkg->header; /* just a shortcut */ int override_date = 0; time_t mtime_clamp = 0; char *srcdate = getenv("SOURCE_DATE_EPOCH"); char *mtime_policy_str = rpmExpand("%{?build_mtime_policy}", NULL); /* backward compatibility */ if (!*mtime_policy_str) { if (srcdate && rpmExpandNumeric("%{?clamp_mtime_to_source_date_epoch}")) { free(mtime_policy_str); mtime_policy_str = xstrdup("clamp_to_source_date_epoch"); rpmlog(RPMLOG_WARNING, _("%%clamp_mtime_to_source_date_epoch is deprecated, please use %%build_mtime_policy\n")); } } if (!strcmp(mtime_policy_str, "clamp_to_buildtime")) { mtime_clamp = spec->buildTime; override_date = 1; } else if (!strcmp(mtime_policy_str, "clamp_to_source_date_epoch")) { /* Limit the maximum date to SOURCE_DATE_EPOCH if defined * similar to the tar --clamp-mtime option * https://reproducible-builds.org/specs/source-date-epoch/ */ if (srcdate) { char *endptr; errno = 0; mtime_clamp = strtol(srcdate, &endptr, 10); if (srcdate == endptr || *endptr || errno != 0) { rpmlog(RPMLOG_ERR, _("unable to parse %s=%s\n"), "SOURCE_DATE_EPOCH", srcdate); fl->processingFailed = 1; } override_date = 1; } } else if (*mtime_policy_str) { rpmlog(RPMLOG_WARNING, _("Unknown mtime policy '%s'\n"), mtime_policy_str); } free(mtime_policy_str); /* * See if non-default file digest algorithm is requested. If not * specified, quietly assume default. Otherwise check if supported type. */ digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" : "%{_binary_filedigest_algorithm}"); if (digestalgo == 0) { digestalgo = defaultalgo; } if (rpmDigestLength(digestalgo) == 0) { rpmlog(RPMLOG_WARNING, _("Unknown file digest algorithm %u, falling back to %u\n"), digestalgo, defaultalgo); digestalgo = defaultalgo; } /* Adjust paths if needed */ if (!isSrc && pkg->removePostfixes) { for (auto const & fl : fl->files) { char * cpiopath = fl.cpioPath; char * cpiopath_orig = xstrdup(cpiopath); for (ARGV_const_t postfix_p = pkg->removePostfixes; *postfix_p; postfix_p++) { int len = strlen(*postfix_p); int plen = strlen(cpiopath); if (len <= plen && !strncmp(cpiopath+plen-len, *postfix_p, len)) { cpiopath[plen-len] = '\0'; if (plen-len > 0 && cpiopath[plen-len-1] == '/') { cpiopath[plen-len-1] = '\0'; } } } if (strcmp(cpiopath_orig, cpiopath)) pkg->fileRenameMap.insert({cpiopath, cpiopath_orig}); _free(cpiopath_orig); } } /* Sort the big list */ std::sort(fl->files.begin(), fl->files.end()); pkg->dpaths = (char **)xmalloc((fl->files.size() + 1) * sizeof(*pkg->dpaths)); /* Generate the header. */ for (i = 0, flp = fl->files.data(); i < fl->files.size(); i++, flp++) { rpm_ino_t fileid = flp - fl->files.data(); /* Merge duplicate entries. */ while (i < (fl->files.size() - 1) && rstreq(flp->cpioPath, flp[1].cpioPath)) { /* Two entries for the same file found, merge the entries. */ /* Note that an %exclude is a duplication of a file reference */ /* file flags */ flp[1].flags |= flp->flags; if (!(flp[1].flags & RPMFILE_EXCLUDE)) { int lvl = RPMLOG_WARNING; if (fail_on_dupes) { lvl = RPMLOG_ERR; fl->processingFailed = 1; } rpmlog(lvl, _("File listed twice: %s\n"), flp->cpioPath); } /* file mode */ if (S_ISDIR(flp->fl_mode)) { if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) < (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE))) flp[1].fl_mode = flp->fl_mode; } else { if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) < (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE))) flp[1].fl_mode = flp->fl_mode; } /* uid */ if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) < (flp->specdFlags & (SPECD_UID | SPECD_DEFUID))) { flp[1].uname = flp->uname; } /* gid */ if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) < (flp->specdFlags & (SPECD_GID | SPECD_DEFGID))) { flp[1].gname = flp->gname; } /* verify flags */ if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) < (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY))) flp[1].verifyFlags = flp->verifyFlags; /* XXX to-do: language */ flp++; i++; } /* Skip files that were marked with %exclude. */ if (flp->flags & RPMFILE_EXCLUDE) { argvAdd(&pkg->fileExcludeList, flp->cpioPath); continue; } /* Collect on-disk paths for archive creation */ pkg->dpaths[npaths++] = xstrdup(flp->diskPath); headerPutString(h, RPMTAG_OLDFILENAMES, flp->cpioPath); headerPutString(h, RPMTAG_FILEUSERNAME, rpmstrPoolStr(fl->pool, flp->uname)); headerPutString(h, RPMTAG_FILEGROUPNAME, rpmstrPoolStr(fl->pool, flp->gname)); /* Use 64bit filesizes always on v6, on older only if required. */ if (spec->rpmformat >= 6 || fl->largeFiles) { rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size; headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1); (void) rpmlibNeedsFeature(pkg, "LargeFiles", "4.12.0-1"); } else { rpm_off_t rsize32 = (rpm_off_t)flp->fl_size; headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1); } /* Excludes and dupes have been filtered out by now. */ if (isLinkable(flp->fl_mode)) { if (flp->fl_nlink == 1 || !seenHardLink(fl->files, flp, &fileid)) { totalFileSize += flp->fl_size; } } if (override_date && flp->fl_mtime > mtime_clamp) { flp->fl_mtime = mtime_clamp; } /* * For items whose size varies between systems, always explicitly * cast to the header type before inserting. * TODO: check and warn if header type overflows for each case. */ { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime; headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1); } { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode; headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1); } { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev; headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1); } /* * To allow rpmbuild to work on filesystems with 64bit inodes numbers, * remap them into 32bit integers based on filelist index, just * preserving semantics for determining hardlinks. * Start at 1 as inode zero as that could be considered as an error. * Since we flatten all the inodes to appear within a single fs, * we also need to flatten the devices. */ { rpm_ino_t rino = fileid + 1; rpm_dev_t rdev = flp->fl_dev ? 1 : 0; headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1); headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1); } headerPutString(h, RPMTAG_FILELANGS, flp->langs); if (fl->haveCaps) { headerPutString(h, RPMTAG_FILECAPS, flp->caps); } buf[0] = '\0'; if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST)) (void) rpmDoDigest(digestalgo, flp->diskPath, 1, (unsigned char *)buf); headerPutString(h, RPMTAG_FILEDIGESTS, buf); buf[0] = '\0'; if (S_ISLNK(flp->fl_mode)) { ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1); if (llen == -1) { rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"), flp->diskPath, strerror(errno)); fl->processingFailed = 1; } else { buf[llen] = '\0'; if (buf[0] == '/') { rpmlog(RPMLOG_WARNING, _("absolute symlink: %s -> %s\n"), flp->cpioPath, buf); } if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") && rstreqn(buf, fl->buildRoot, fl->buildRootLen)) { rpmlog(RPMLOG_ERR, _("Symlink points to BuildRoot: %s -> %s\n"), flp->cpioPath, buf); fl->processingFailed = 1; } } } headerPutString(h, RPMTAG_FILELINKTOS, buf); if (flp->flags & RPMFILE_GHOST) { flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_LINKTO | RPMVERIFY_MTIME); } headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1); if (!isSrc && isDoc(fl->docDirs, flp->cpioPath)) flp->flags |= RPMFILE_DOC; /* XXX Should directories have %doc/%config attributes? (#14531) */ if (S_ISDIR(flp->fl_mode)) flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC|RPMFILE_LICENSE); /* Strip internal parse data */ flp->flags &= PARSEATTR_MASK; headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1); } pkg->dpaths[npaths] = NULL; /* Use 64bit sizes always on v6, on older only if required. */ if (spec->rpmformat < 6 && totalFileSize < UINT32_MAX) { rpm_off_t totalsize = totalFileSize; headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1); } else { rpm_loff_t totalsize = totalFileSize; headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1); } if (digestalgo != RPM_HASH_MD5) { headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1); if (spec->rpmformat < 6) rpmlibNeedsFeature(pkg, "FileDigests", "4.6.0-1"); } if (fl->haveCaps) { rpmlibNeedsFeature(pkg, "FileCaps", "4.6.1-1"); } if (!isSrc && !rpmExpandNumeric("%{_noPayloadPrefix}")) { if (spec->rpmformat < 6) (void) rpmlibNeedsFeature(pkg, "PayloadFilesHavePrefix", "4.0-1"); } /* rpmfiNew() only groks compressed filelists */ headerConvert(h, HEADERCONV_COMPRESSFILELIST); pkg->cpioList = rpmfilesNew(NULL, h, RPMTAG_BASENAMES, 0); if (pkg->cpioList == NULL || rpmfilesFC(pkg->cpioList) != npaths) { fl->processingFailed = 1; } if (fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS) { /* Uncompress filelist if legacy format requested */ headerConvert(h, HEADERCONV_EXPANDFILELIST); } else { /* Binary packages with dirNames cannot be installed by legacy rpm. */ if (spec->rpmformat < 6) (void) rpmlibNeedsFeature(pkg, "CompressedFileNames", "3.0.4-1"); } } static void FileRecordsFree(FileRecords & files) { for (auto & rec : files) { free(rec.diskPath); free(rec.cpioPath); free(rec.langs); free(rec.caps); } files.clear(); } static void FileListFree(FileList fl) { FileEntryFree(&(fl->cur)); FileEntryFree(&(fl->def)); FileRecordsFree(fl->files); free(fl->buildRoot); argvFree(fl->docDirs); rpmstrPoolFree(fl->pool); } /* forward ref */ static rpmRC recurseDir(FileList fl, const char * diskPath); /* Hack up a stat structure for a %dev or non-existing %ghost */ static struct stat * fakeStat(FileEntry cur, struct stat * statp) { time_t now = time(NULL); if (cur->devtype) { statp->st_rdev = ((cur->devmajor & 0xff) << 8) | (cur->devminor & 0xff); statp->st_dev = statp->st_rdev; statp->st_mode = (cur->devtype == 'b' ? S_IFBLK : S_IFCHR); } else { /* non-existing %ghost file or directory */ statp->st_mode = cur->isDir ? S_IFDIR : S_IFREG; /* can't recurse into non-existing directory */ if (cur->isDir) cur->isDir = 1; } statp->st_mode |= (cur->ar.ar_fmode & 0777); statp->st_atime = now; statp->st_mtime = now; statp->st_ctime = now; statp->st_nlink = 1; return statp; } static int validFilename(const char *fn) { int rc = 1; /* char is signed but we're dealing with unsigned values here! */ for (const unsigned char *s = (const unsigned char *)fn; *s; s++) { /* Ban DEL and anything below space, UTF-8 is validated elsewhere */ if (*s == 0x7f || *s < 0x20) { rpmlog(RPMLOG_ERR, _("Illegal character (0x%x) in filename: %s\n"), *s, fn); rc = 0; break; } } return rc; } /** * Add a file to the package manifest. * @param fl package file tree walk data * @param diskPath path to file * @param statp file stat (possibly NULL) * @return RPMRC_OK on success */ static rpmRC addFile(FileList fl, const char * diskPath, struct stat * statp) { size_t plen = strlen(diskPath); char buf[plen + 1]; const char *cpioPath; struct stat statbuf; mode_t fileMode; const char *fileUname; const char *fileGname; rpmRC rc = RPMRC_FAIL; /* assume failure */ /* Strip trailing slash. The special case of '/' path is handled below. */ if (plen > 0 && diskPath[plen - 1] == '/') { diskPath = strcpy(buf, diskPath); buf[plen - 1] = '\0'; } cpioPath = diskPath; if (strncmp(diskPath, fl->buildRoot, fl->buildRootLen)) { rpmlog(RPMLOG_ERR, _("Path is outside buildroot: %s\n"), diskPath); goto exit; } if (!validFilename(diskPath)) goto exit; /* Path may have prepended buildRoot, so locate the original filename. */ /* * XXX There are 3 types of entry into addFile: * * From diskUrl statp * ===================================================== * processBinaryFile path NULL * processBinaryFile glob result path NULL * myftw path stat * */ if (fl->buildRoot && !rstreq(fl->buildRoot, "/")) cpioPath += fl->buildRootLen; /* XXX make sure '/' can be packaged also */ if (*cpioPath == '\0') cpioPath = "/"; /* * Unless recursing, we dont have stat() info at hand. Handle the * various cases, preserving historical behavior wrt %dev(): * - for %dev() entries we fake it up whether the file exists or not * - otherwise try to grab the data by lstat() * - %ghost entries might not exist, fake it up */ if (statp == NULL) { memset(&statbuf, 0, sizeof(statbuf)); if (fl->cur.devtype) { statp = fakeStat(&(fl->cur), &statbuf); } else if (lstat(diskPath, &statbuf) == 0) { statp = &statbuf; } else if (fl->cur.attrFlags & RPMFILE_GHOST) { statp = fakeStat(&(fl->cur), &statbuf); } else { int lvl = RPMLOG_ERR; int ignore = 0; const char *msg = fl->cur.isDir ? _("Directory not found: %s\n") : _("File not found: %s\n"); if (fl->cur.attrFlags & RPMFILE_EXCLUDE) ignore = 1; if (fl->cur.attrFlags & RPMFILE_DOC) { int strict_doc = rpmExpandNumeric("%{?_missing_doc_files_terminate_build}"); if (!strict_doc) ignore = 1; } if (ignore) { lvl = RPMLOG_WARNING; rc = RPMRC_OK; } rpmlog(lvl, msg, diskPath); goto exit; } } /* Error out when a non-directory is specified as one in spec */ if (fl->cur.isDir && (statp == &statbuf) && !S_ISDIR(statp->st_mode)) { rpmlog(RPMLOG_ERR, _("Not a directory: %s\n"), diskPath); goto exit; } /* Don't recurse into explicit %dir, don't double-recurse from fts */ if ((fl->cur.isDir != 1) && (statp == &statbuf) && S_ISDIR(statp->st_mode)) { return recurseDir(fl, diskPath); } fileMode = statp->st_mode; /* Explicit %attr() always wins */ if (fl->cur.ar.ar_fmodestr) { if (S_ISLNK(fileMode)) { rpmlog(RPMLOG_WARNING, "Explicit %%attr() mode not applicable to symlink: %s\n", diskPath); } else { fileMode &= S_IFMT; fileMode |= fl->cur.ar.ar_fmode; } } else { /* ...but %defattr() for directories and files is different */ if (S_ISDIR(fileMode)) { if (fl->def.ar.ar_dmodestr) { fileMode &= S_IFMT; fileMode |= fl->def.ar.ar_dmode; } } else if (!S_ISLNK(fileMode) && fl->def.ar.ar_fmodestr) { fileMode &= S_IFMT; fileMode |= fl->def.ar.ar_fmode; } } if (fl->cur.ar.ar_user) { fileUname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_user); } else if (fl->def.ar.ar_user) { fileUname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_user); } else { fileUname = UID_0_USER; } if (fl->cur.ar.ar_group) { fileGname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_group); } else if (fl->def.ar.ar_group) { fileGname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_group); } else { fileGname = GID_0_GROUP; } /* S_XXX macro must be consistent with type in find call at check-files script */ if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) { appendStringBuf(check_fileList, diskPath); appendStringBuf(check_fileList, "\n"); } /* Add to the file list */ fl->files.push_back({}); { FileListRec flp = &fl->files.back(); flp->fl_st = *statp; /* structure assignment */ flp->fl_mode = fileMode; if (S_ISDIR(fileMode)) flp->fl_size = 0; flp->cpioPath = xstrdup(cpioPath); flp->diskPath = xstrdup(diskPath); flp->uname = rpmstrPoolId(fl->pool, fileUname, 1); flp->gname = rpmstrPoolId(fl->pool, fileGname, 1); if (fl->cur.langs) { flp->langs = argvJoin(fl->cur.langs, "|"); } else { flp->langs = xstrdup(""); } if (fl->cur.caps) { flp->caps = xstrdup(fl->cur.caps); } else { flp->caps = xstrdup(""); } flp->flags = fl->cur.attrFlags; flp->specdFlags = fl->cur.specdFlags; flp->verifyFlags = fl->cur.verifyFlags; if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) { if (flp->fl_size >= UINT32_MAX) { fl->largeFiles = 1; } } } rc = RPMRC_OK; exit: if (rc != RPMRC_OK) fl->processingFailed = 1; return rc; } /** * Add directory (and all of its files) to the package manifest. * @param fl package file tree walk data * @param diskPath path to file * @return RPMRC_OK on success */ static rpmRC recurseDir(FileList fl, const char * diskPath) { char * ftsSet[2]; FTS * ftsp; FTSENT * fts; int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL); rpmRC rc = RPMRC_FAIL; ftsSet[0] = (char *) diskPath; ftsSet[1] = NULL; ftsp = Fts_open(ftsSet, myFtsOpts, NULL); while ((fts = Fts_read(ftsp)) != NULL) { switch (fts->fts_info) { case FTS_D: /* preorder directory */ case FTS_F: /* regular file */ case FTS_SL: /* symbolic link */ case FTS_SLNONE: /* symbolic link without target */ case FTS_DEFAULT: /* none of the above */ rc = addFile(fl, fts->fts_accpath, fts->fts_statp); break; case FTS_DOT: /* dot or dot-dot */ case FTS_DP: /* postorder directory */ rc = RPMRC_OK; break; case FTS_NS: /* stat(2) failed */ case FTS_DNR: /* unreadable directory */ case FTS_ERR: /* error; errno is set */ case FTS_DC: /* directory that causes cycles */ case FTS_NSOK: /* no stat(2) requested */ case FTS_INIT: /* initialized only */ case FTS_W: /* whiteout object */ default: rpmlog(RPMLOG_ERR, _("Can't read content of file: %s\n"), fts->fts_path); rc = RPMRC_FAIL; break; } if (rc) break; } (void) Fts_close(ftsp); return rc; } /** * Add a pubkey/icon to a binary package. * @param pkg * @param fl package file tree walk data * @param fileName path to file, relative is builddir, absolute buildroot. * @param tag tag to add * @return RPMRC_OK on success */ static rpmRC processMetadataFile(Package pkg, FileList fl, const char * fileName, rpmTagVal tag) { char * fn = NULL; char * apkt = NULL; uint8_t * pkt = NULL; ssize_t pktlen = 0; int absolute = 0; rpmRC rc = RPMRC_FAIL; int xx; if (*fileName == '/') { fn = rpmGenPath(fl->buildRoot, NULL, fileName); absolute = 1; } else fn = rpmGenPath("%{builddir}", "%{?buildsubdir}", fileName); switch (tag) { default: rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"), fn, tag); goto exit; break; case RPMTAG_PUBKEYS: { if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) { rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn); goto exit; } if (xx != PGPARMOR_PUBKEY) { rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn); goto exit; } apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen); break; } } if (!apkt) { rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn); goto exit; } headerPutString(pkg->header, tag, apkt); rc = RPMRC_OK; if (absolute) rc = addFile(fl, fn, NULL); exit: free(apkt); free(pkt); free(fn); if (rc) { fl->processingFailed = 1; rc = RPMRC_FAIL; } return rc; } /* add a file with possible virtual attributes to the file list */ static void argvAddAttr(ARGV_t *filesp, rpmfileAttrs attrs, const char *path) { char *line = NULL; for (VFA_t *vfa = virtualAttrs; vfa->attribute != NULL; vfa++) { if (vfa->flag & attrs) line = rstrscat(&line, vfa->attribute, " ", NULL); } line = rstrscat(&line, path, NULL); argvAdd(filesp, line); free(line); } #ifdef HAVE_LIBDW /* How build id links are generated. See macros.in for description. */ #define BUILD_IDS_NONE 0 #define BUILD_IDS_ALLDEBUG 1 #define BUILD_IDS_SEPARATE 2 #define BUILD_IDS_COMPAT 3 static int addNewIDSymlink(ARGV_t *files, char *targetpath, char *idlinkpath, int isDbg, int *dups) { const char *linkerr = _("failed symlink"); int rc = 0; int nr = 0; int exists = 0; char *origpath, *linkpath; if (isDbg) rasprintf(&linkpath, "%s.debug", idlinkpath); else linkpath = idlinkpath; origpath = linkpath; while (faccessat(AT_FDCWD, linkpath, F_OK, AT_SYMLINK_NOFOLLOW) == 0) { /* We don't care about finding dups for compat links, they are OK as is. Otherwise we will need to double check if existing link points to the correct target. */ if (dups == NULL) { exists = 1; break; } char ltarget[PATH_MAX]; ssize_t llen; /* In short-circuited builds the link might already exist */ if ((llen = readlink(linkpath, ltarget, sizeof(ltarget)-1)) != -1) { ltarget[llen] = '\0'; if (rstreq(ltarget, targetpath)) { exists = 1; break; } } if (nr > 0) free(linkpath); nr++; rasprintf(&linkpath, "%s.%d%s", idlinkpath, nr, isDbg ? ".debug" : ""); } if (!exists && symlink(targetpath, linkpath) < 0) { rc = 1; rpmlog(RPMLOG_ERR, "%s: %s -> %s: %m\n", linkerr, linkpath, targetpath); } else { argvAddAttr(files, RPMFILE_ARTIFACT, linkpath); } if (nr > 0) { /* Lets see why there are multiple build-ids. If the original targets are hard linked, then it is OK, otherwise warn something fishy is going on. Would be nice to call something like eu-elfcmp to see if they are really the same ELF file or not. */ struct stat st1, st2; if (stat (origpath, &st1) != 0) { rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"), origpath); } else if (stat (linkpath, &st2) != 0) { rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"), linkpath); } else if (!(S_ISREG(st1.st_mode) && S_ISREG(st2.st_mode) && st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink && st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev)) { char *rpath1 = realpath(origpath, NULL); char *rpath2 = realpath(linkpath, NULL); rpmlog(RPMLOG_WARNING, _("Duplicate build-ids %s and %s\n"), rpath1, rpath2); free(rpath1); free(rpath2); } } if (isDbg) free(origpath); if (nr > 0) free(linkpath); if (dups != NULL) *dups = nr; return rc; } static int haveModinfo(Elf *elf) { Elf_Scn * scn = NULL; size_t shstrndx; int have_modinfo = 0; const char *sname; if (elf_getshdrstrndx(elf, &shstrndx) == 0) { while ((scn = elf_nextscn(elf, scn)) != NULL) { GElf_Shdr shdr_mem, *shdr = gelf_getshdr(scn, &shdr_mem); if (shdr == NULL) continue; sname = elf_strptr(elf, shstrndx, shdr->sh_name); if (sname && rstreq(sname, ".modinfo")) { have_modinfo = 1; break; } } } return have_modinfo; } static int generateBuildIDs(FileList fl, ARGV_t *files) { int rc = 0; int i; FileListRec flp; std::vector ids; std::vector paths; /* How are we supposed to create the build-id links? */ char *build_id_links_macro = rpmExpand("%{?_build_id_links}", NULL); int build_id_links; if (*build_id_links_macro == '\0') { rpmlog(RPMLOG_WARNING, _("_build_id_links macro not set, assuming 'compat'\n")); build_id_links = BUILD_IDS_COMPAT; } else if (strcmp(build_id_links_macro, "none") == 0) { build_id_links = BUILD_IDS_NONE; } else if (strcmp(build_id_links_macro, "alldebug") == 0) { build_id_links = BUILD_IDS_ALLDEBUG; } else if (strcmp(build_id_links_macro, "separate") == 0) { build_id_links = BUILD_IDS_SEPARATE; } else if (strcmp(build_id_links_macro, "compat") == 0) { build_id_links = BUILD_IDS_COMPAT; } else { rc = 1; rpmlog(RPMLOG_ERR, _("_build_id_links macro set to unknown value '%s'\n"), build_id_links_macro); build_id_links = BUILD_IDS_NONE; } free(build_id_links_macro); if (build_id_links == BUILD_IDS_NONE || rc != 0) return rc; /* Historically we have only checked build_ids when __debug_package was defined. So don't terminate the build if __debug_package is unset, even when _missing_build_ids_terminate_build is. */ int terminate = (rpmExpandNumeric("%{?_missing_build_ids_terminate_build}") && rpmExpandNumeric("%{?__debug_package}")); /* Collect and check all build-ids for ELF files in this package. */ int needMain = 0; int needDbg = 0; for (i = 0, flp = fl->files.data(); i < fl->files.size(); i++, flp++) { struct stat sbuf; if (lstat(flp->diskPath, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) { /* We determine whether this is a main or debug ELF based on path. */ int isDbg = strncmp (flp->cpioPath, DEBUG_LIB_PREFIX, strlen (DEBUG_LIB_PREFIX)) == 0; /* For the main package files mimic what find-debuginfo.sh does. Only check build-ids for executable files. Debug files are always non-executable. */ if (!isDbg && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) continue; int fd = open (flp->diskPath, O_RDONLY); if (fd >= 0) { /* Only real ELF files, that are ET_EXEC, ET_DYN or kernel modules (ET_REL files with .modinfo section) should have build-ids. */ GElf_Ehdr ehdr; #ifdef HAVE_DWELF_ELF_BEGIN Elf *elf = dwelf_elf_begin(fd); #else Elf *elf = elf_begin (fd, ELF_C_READ, NULL); #endif if (elf != NULL && elf_kind(elf) == ELF_K_ELF && gelf_getehdr(elf, &ehdr) != NULL && (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN || (ehdr.e_type == ET_REL && haveModinfo(elf)))) { const void *build_id; ssize_t len = dwelf_elf_gnu_build_id (elf, &build_id); /* len == -1 means error. Zero means no build-id. We want at least a length of 2 so we have at least a xx/yy (hex) dir/file. But reasonable build-ids are between 16 bytes (md5 is 128 bits) and 64 bytes (largest sha3 is 512 bits), common is 20 bytes (sha1 is 160 bits). */ if (len >= 16 && len <= 64) { int addid = 0; if (isDbg) { needDbg = 1; addid = 1; } else if (build_id_links != BUILD_IDS_ALLDEBUG) { needMain = 1; addid = 1; } if (addid) { char *x = rpmhex((const uint8_t *)build_id, len); paths.push_back(flp->cpioPath); ids.push_back(x); free(x); } } else { if (len < 0) { rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, _("error reading build-id in %s: %s\n"), flp->diskPath, elf_errmsg (-1)); } else if (len == 0) { rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, _("Missing build-id in %s\n"), flp->diskPath); } else { rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, (len < 16 ? _("build-id found in %s too small\n") : _("build-id found in %s too large\n")), flp->diskPath); } if (terminate) rc = 1; } } elf_end (elf); close (fd); } } } /* Process and clean up all build-ids. */ if (ids.size() > 0) { const char *errdir = _("failed to create directory"); char *mainiddir = NULL; char *debugiddir = NULL; if (rc == 0) { char *attrstr; /* Add .build-id directories to hold the subdirs/symlinks. */ mainiddir = rpmGetPath(fl->buildRoot, BUILD_ID_DIR, NULL); debugiddir = rpmGetPath(fl->buildRoot, DEBUG_ID_DIR, NULL); /* Make sure to reset all file flags to defaults. */ attrstr = mkattr(); argvAdd(files, attrstr); free (attrstr); /* Supported, but questionable. */ if (needMain && needDbg) rpmlog(RPMLOG_WARNING, _("Mixing main ELF and debug files in package\n")); if (needMain) { if ((rc = rpmioMkpath(mainiddir, 0755, -1, -1)) != 0) { rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, mainiddir); } else { argvAddAttr(files, (uint32_t)RPMFILE_DIR|RPMFILE_ARTIFACT, mainiddir); } } if (rc == 0 && needDbg) { if ((rc = rpmioMkpath(debugiddir, 0755, -1, -1)) != 0) { rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, debugiddir); } else { argvAddAttr(files, (uint32_t)RPMFILE_DIR|RPMFILE_ARTIFACT, debugiddir); } } } /* In case we need ALLDEBUG links we might need the vra as tagged onto the .debug file name. */ char *vra = NULL; if (rc == 0 && needDbg && build_id_links == BUILD_IDS_ALLDEBUG) { int unique_debug_names = rpmExpandNumeric("%{?_unique_debug_names}"); if (unique_debug_names == 1) vra = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL); } /* Now add a subdir and symlink for each buildid found. */ for (i = 0; i < ids.size(); i++) { /* Don't add anything more when an error occurred. But do cleanup. */ if (rc == 0) { int isDbg = strncmp (paths[i].c_str(), DEBUG_LIB_PREFIX, strlen (DEBUG_LIB_PREFIX)) == 0; char *buildidsubdir; char subdir[4]; subdir[0] = '/'; subdir[1] = ids[i][0]; subdir[2] = ids[i][1]; subdir[3] = '\0'; if (isDbg) buildidsubdir = rpmGetPath(debugiddir, subdir, NULL); else buildidsubdir = rpmGetPath(mainiddir, subdir, NULL); /* We only need to create and add the subdir once. */ int addsubdir = access (buildidsubdir, F_OK) == -1; if (addsubdir && (rc = rpmioMkpath(buildidsubdir, 0755, -1, -1)) != 0) { rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, buildidsubdir); } else { if (addsubdir) argvAddAttr(files, (uint32_t)RPMFILE_DIR|RPMFILE_ARTIFACT, buildidsubdir); if (rc == 0) { const char *linkpattern, *targetpattern; char *linkpath, *targetpath; int dups = 0; if (isDbg) { linkpattern = "%s/%s"; targetpattern = "../../../../..%s"; } else { linkpattern = "%s/%s"; targetpattern = "../../../..%s"; } rasprintf(&linkpath, linkpattern, buildidsubdir, &ids[i][2]); rasprintf(&targetpath, targetpattern, paths[i].c_str()); rc = addNewIDSymlink(files, targetpath, linkpath, isDbg, &dups); /* We might want to have a link from the debug build_ids dir to the main one. We create it when we are creating compat links or doing an old style alldebug build-ids package. In the first case things are simple since we just link to the main build-id symlink. The second case is a bit tricky, since we cannot be 100% sure the file names in the main and debug package match. Currently they do, but when creating parallel installable debuginfo packages they might not (in that case we might have to also strip the nvr from the debug name). In general either method is discouraged since it might create dangling symlinks if the package versions get out of sync. */ if (rc == 0 && isDbg && build_id_links == BUILD_IDS_COMPAT) { /* buildidsubdir already points to the debug buildid. We just need to setup the symlink to the main one. There might be duplicate IDs, those are found by the addNewIDSymlink above. Target the last found duplicate, if any. */ free(linkpath); free(targetpath); if (dups == 0) { rasprintf(&linkpath, "%s/%s", buildidsubdir, &ids[i][2]); rasprintf(&targetpath, "../../../.build-id%s/%s", subdir, &ids[i][2]); } else { rasprintf(&linkpath, "%s/%s.%d", buildidsubdir, &ids[i][2], dups); rasprintf(&targetpath, "../../../.build-id%s/%s.%d", subdir, &ids[i][2], dups); } rc = addNewIDSymlink(files, targetpath, linkpath, 0, NULL); } if (rc == 0 && isDbg && build_id_links == BUILD_IDS_ALLDEBUG) { /* buildidsubdir already points to the debug buildid. We do have to figure out the main ELF file though (which is most likely not in this package). Guess we can find it by stripping the /usr/lib/debug path and .debug prefix. Which might not really be correct if there was a more involved transformation (for example for parallel installable debuginfo packages), but then we shouldn't be using ALLDEBUG in the first place. Also ignore things like .dwz multifiles which don't end in ".debug". */ int pathlen = paths[i].size(); int debuglen = strlen(".debug"); int prefixlen = strlen(DEBUG_LIB_DIR); int vralen = vra == NULL ? 0 : strlen(vra); if (pathlen > prefixlen + debuglen + vralen && strcmp ((paths[i].c_str() + pathlen - debuglen), ".debug") == 0) { free(linkpath); free(targetpath); char *targetstr = xstrdup (paths[i].c_str() + prefixlen); int targetlen = pathlen - prefixlen; int targetend = targetlen - debuglen - vralen; targetstr[targetend] = '\0'; rasprintf(&linkpath, "%s/%s", buildidsubdir, &ids[i][2]); rasprintf(&targetpath, "../../../../..%s", targetstr); rc = addNewIDSymlink(files, targetpath, linkpath, 0, &dups); free(targetstr); } } free(linkpath); free(targetpath); } } free(buildidsubdir); } } free(mainiddir); free(debugiddir); free(vra); } return rc; } #endif /** * Add a file to a binary package. * @param pkg * @param fl package file tree walk data * @param fileName file to add * @param doGlob expand file as a glob pattern * @return RPMRC_OK on success */ static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName, int doGlob) { std::string diskPath; rpmRC rc = RPMRC_OK; size_t fnlen = strlen(fileName); int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/'); ARGV_t argv = NULL; int argc = 0; int i; /* XXX differentiate other directories from explicit %dir */ if (trailing_slash && !fl->cur.isDir) fl->cur.isDir = -1; /* Check that file starts with leading "/" */ if (*fileName != '/') { rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName); rc = RPMRC_FAIL; goto exit; } /* Copy file name or glob pattern removing multiple "/" chars. */ /* * Note: join_path() returns a normalized path. That means * that the following pathologies should be weeded out: * //bin//sh * //usr//bin/ * /.././../usr/../bin//./sh */ diskPath = rpm::join_path({fl->buildRoot, fileName}, false); /* Arrange trailing slash on directories */ if (fl->cur.isDir) diskPath += '/'; if (fl->cur.devtype) doGlob = 0; if (!doGlob) { rc = addFile(fl, diskPath.c_str(), NULL); goto exit; } if (rpmGlobPath(diskPath.c_str(), RPMGLOB_NOCHECK, &argc, &argv)) goto exit; for (i = 0; i < argc; i++) rc = addFile(fl, argv[i], NULL); argvFree(argv); exit: if (rc) { fl->processingFailed = 1; rc = RPMRC_FAIL; } return rc; } int readManifest(rpmSpec spec, const char *path, const char *descr, int flags, ARGV_t *avp, StringBuf *sbp) { char *fn, buf[BUFSIZ]; FILE *fd = NULL; int lineno = 0; int nlines = -1; if (*path == '/') { fn = rpmGetPath(path, NULL); } else { fn = rpmGenPath("%{builddir}", "%{?buildsubdir}", path); } fd = fopen(fn, "r"); if (fd == NULL) { rpmlog(RPMLOG_ERR, _("Could not open %s file %s: %m\n"), descr, fn); goto exit; } rpmPushMacroFlags(spec->macros, "__file_name", NULL, fn, RMIL_SPEC, RPMMACRO_LITERAL); nlines = 0; while (fgets(buf, sizeof(buf), fd)) { char *expanded = NULL; lineno++; if ((flags & STRIP_COMMENTS) && handleComments(buf)) continue; if (specExpand(spec, lineno, buf, &expanded)) goto exit; if (avp) argvAdd(avp, expanded); if (sbp) appendStringBufAux(*sbp, expanded, (flags & STRIP_TRAILINGSPACE)); free(expanded); nlines++; } if (nlines == 0) { int emptyok = (flags & ALLOW_EMPTY); rpmlog(emptyok ? RPMLOG_WARNING : RPMLOG_ERR, _("Empty %s file %s\n"), descr, fn); if (!emptyok) nlines = -1; } exit: if (fd) { fclose(fd); rpmPopMacro(spec->macros, "__file_name"); } free(fn); return nlines; } static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path) { int nlines = 0; int flags = STRIP_COMMENTS | STRIP_TRAILINGSPACE; if (!rpmExpandNumeric("%{?_empty_manifest_terminate_build}")) flags |= ALLOW_EMPTY; /* XXX unmask %license while parsing files manifest*/ rpmPushMacroFlags(spec->macros, "license", NULL, "%license", RMIL_SPEC, RPMMACRO_LITERAL); nlines = readManifest(spec, path, "%files", flags, &(pkg->fileList), NULL); rpmPopMacro(NULL, "license"); return (nlines >= 0) ? RPMRC_OK : RPMRC_FAIL; } static char * getSpecialDocDir(Header h, rpmFlags sdtype) { const char *errstr = NULL; const char *dirtype = (sdtype == RPMFILE_DOC) ? "docdir" : "licensedir"; const char *fmt_default = "%{NAME}-%{VERSION}"; char *fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL); char *fmt = NULL; char *res = NULL; if (fmt_macro && strlen(fmt_macro) > 0) { fmt = headerFormat(h, fmt_macro, &errstr); if (errstr) { rpmlog(RPMLOG_WARNING, _("illegal _docdir_fmt %s: %s\n"), fmt_macro, errstr); } } if (fmt == NULL) fmt = headerFormat(h, fmt_default, &errstr); res = rpmGetPath("%{_", dirtype, "}/", fmt, NULL); free(fmt); free(fmt_macro); return res; } static specialDir specialDirNew(Header h, rpmFlags sdtype) { specialDir sd = new specialDir_s {}; sd->dirname = getSpecialDocDir(h, sdtype); sd->sdtype = sdtype; return sd; } static void addSpecialFile(specialDir sd, const char *path, FileEntry cur, FileEntry def) { argvAdd(&sd->files, path); sd->entries.push_back({}); auto & entry = sd->entries.back(); copyFileEntry(cur, &entry.curEntry); copyFileEntry(def, &entry.defEntry); } static specialDir specialDirFree(specialDir sd) { if (sd) { argvFree(sd->files); free(sd->dirname); for (auto & entry : sd->entries) { FileEntryFree(&entry.curEntry); FileEntryFree(&entry.defEntry); } delete sd; } return NULL; } static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl, specialDir sd, int install, int test) { const char *sdenv = (sd->sdtype == RPMFILE_DOC) ? "DOCDIR" : "LICENSEDIR"; const char *sdname = (sd->sdtype == RPMFILE_DOC) ? "%doc" : "%license"; char *mkdocdir = rpmExpand("%{__mkdir_p} $", sdenv, NULL); StringBuf docScript = newStringBuf(); int count = sd->entries.size(); char *basepath = rpmGenPath(spec->rootDir, "%{builddir}", "%{?buildsubdir}"); ARGV_t *files = (ARGV_t *)xmalloc(sizeof(*files) * count); int i, j; /* Glob and copy file entries from builddir to buildroot */ appendStringBuf(docScript, sdenv); appendStringBuf(docScript, "=$RPM_BUILD_ROOT"); appendLineStringBuf(docScript, sd->dirname); appendLineStringBuf(docScript, "export LC_ALL=" C_LOCALE); appendStringBuf(docScript, "export "); appendLineStringBuf(docScript, sdenv); appendLineStringBuf(docScript, mkdocdir); for (i = 0; i < count; i++) { std::string origfile = rpm::join_path({basepath, sd->files[i]}, false); ARGV_t argv = NULL; int argc = 0; if (rpmGlobPath(origfile.c_str(), RPMGLOB_NOCHECK, &argc, &argv) == 0) { for (j = 0; j < argc; j++) { appendStringBuf(docScript, "cp -pr '"); appendStringBuf(docScript, argv[j]); appendStringBuf(docScript, "' $"); appendStringBuf(docScript, sdenv); appendLineStringBuf(docScript, " ||:"); } } files[i] = argv; } free(basepath); if (install) { if (doScript(spec, RPMBUILD_STRINGBUF, sdname, getStringBuf(docScript), test, NULL)) { fl->processingFailed = 1; } } /* Add copied files in buildroot to file list */ for (i = 0; i < count; i++) { char *newfile; FileEntryFree(&fl->cur); FileEntryFree(&fl->def); copyFileEntry(&sd->entries[i].curEntry, &fl->cur); copyFileEntry(&sd->entries[i].defEntry, &fl->def); for (ARGV_const_t fn = files[i]; fn && *fn; fn++) { rasprintf(&newfile, "%s/%s", sd->dirname, basename(*fn)); processBinaryFile(pkg, fl, newfile, 0); free(newfile); } argvFree(files[i]); } free(files); FileEntryFree(&fl->cur); FileEntryFree(&fl->def); copyFileEntry(&sd->entries[0].defEntry, &fl->def); copyFileEntry(&sd->entries[0].curEntry, &fl->cur); fl->cur.isDir = 1; (void) processBinaryFile(pkg, fl, sd->dirname, 1); freeStringBuf(docScript); free(mkdocdir); } /* Resets the default settings for files in the package list. Used in processPackageFiles whenever a new set of files is added. */ static void resetPackageFilesDefaults (struct FileList_s *fl, rpmBuildPkgFlags pkgFlags) { struct AttrRec_s root_ar = { 0, 0, 0, 0, 0, 0 }; root_ar.ar_user = rpmstrPoolId(fl->pool, UID_0_USER, 1); root_ar.ar_group = rpmstrPoolId(fl->pool, GID_0_GROUP, 1); dupAttrRec(&root_ar, &fl->def.ar); /* XXX assume %defattr(-,root,root) */ fl->def.verifyFlags = RPMVERIFY_ALL; fl->pkgFlags = pkgFlags; } /* Adds the given fileList to the package. If fromSpecFileList is not zero then the specialDirs are also filled in and the files are sanitized through processBinaryFile(). Otherwise no special files are processed and the files are added directly through addFile(). */ static void addPackageFileList (struct FileList_s *fl, Package pkg, ARGV_t *fileList, specialDir *specialDoc, specialDir *specialLic, int fromSpecFileList) { ARGV_t fileNames = NULL; for (ARGV_const_t fp = *fileList; *fp != NULL; fp++) { char buf[strlen(*fp) + 1]; const char *s = *fp; SKIPSPACE(s); if (*s == '\0') continue; fileNames = argvFree(fileNames); rstrlcpy(buf, s, sizeof(buf)); /* Reset for a new line in %files */ FileEntryFree(&fl->cur); /* turn explicit flags into %def'd ones (gosh this is hacky...) */ fl->cur.specdFlags = ((unsigned)fl->def.specdFlags) >> 8; fl->cur.verifyFlags = fl->def.verifyFlags; if (parseForVerify(buf, 0, &fl->cur) || parseForVerify(buf, 1, &fl->def) || parseForAttr(fl->pool, buf, 0, &fl->cur) || parseForAttr(fl->pool, buf, 1, &fl->def) || parseForDev(buf, &fl->cur) || parseForConfig(buf, &fl->cur) || parseForLang(buf, &fl->cur) || parseForCaps(buf, &fl->cur) || parseForSimple(buf, &fl->cur, &fileNames)) { fl->processingFailed = 1; continue; } for (ARGV_const_t fn = fileNames; fn && *fn; fn++) { /* For file lists that don't come from a spec file list processing is easy. There are no special files and the file names don't need to be adjusted. */ if (!fromSpecFileList) { if (fl->cur.attrFlags & RPMFILE_SPECIALDIR || fl->cur.attrFlags & RPMFILE_DOCDIR || fl->cur.attrFlags & RPMFILE_PUBKEY) { rpmlog(RPMLOG_ERR, _("Special file in generated file list: %s\n"), *fn); fl->processingFailed = 1; continue; } if (fl->cur.attrFlags & RPMFILE_DIR) fl->cur.isDir = 1; addFile(fl, *fn, NULL); continue; } /* File list does come from the spec, try to detect special files and adjust the actual file names. */ if (fl->cur.attrFlags & RPMFILE_SPECIALDIR) { rpmFlags oattrs = (fl->cur.attrFlags & ~RPMFILE_SPECIALDIR); specialDir *sdp = NULL; if (oattrs == RPMFILE_DOC) { sdp = specialDoc; } else if (oattrs == RPMFILE_LICENSE) { sdp = specialLic; } if (sdp == NULL || **fn == '/') { rpmlog(RPMLOG_ERR, _("Can't mix special %s with other forms: %s\n"), (oattrs & RPMFILE_DOC) ? "%doc" : "%license", *fn); fl->processingFailed = 1; continue; } /* save attributes on first special doc/license for later use */ if (*sdp == NULL) { *sdp = specialDirNew(pkg->header, oattrs); } addSpecialFile(*sdp, *fn, &fl->cur, &fl->def); continue; } /* this is now an artificial limitation */ if (fn != fileNames) { rpmlog(RPMLOG_ERR, _("More than one file on a line: %s\n"),*fn); fl->processingFailed = 1; continue; } if (fl->cur.attrFlags & RPMFILE_DOCDIR) { argvAdd(&(fl->docDirs), *fn); } else if (fl->cur.attrFlags & RPMFILE_PUBKEY) { (void) processMetadataFile(pkg, fl, *fn, RPMTAG_PUBKEYS); } else { if (fl->cur.attrFlags & RPMFILE_DIR) fl->cur.isDir = 1; (void) processBinaryFile(pkg, fl, *fn, 1); } } if (fl->cur.caps) fl->haveCaps = 1; } argvFree(fileNames); } static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, Package pkg, int didInstall, int test) { struct FileList_s fl {}; specialDir specialDoc = NULL; specialDir specialLic = NULL; pkg->cpioList = NULL; for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) { if (readFilesManifest(spec, pkg, *fp)) return RPMRC_FAIL; } /* Init the file list structure */ fl.pool = rpmstrPoolLink(spec->pool); /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */ fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL); fl.buildRootLen = strlen(fl.buildRoot); resetPackageFilesDefaults (&fl, pkgFlags); { char *docs = rpmGetPath("%{?__docdir_path}", NULL); argvSplit(&fl.docDirs, docs, ":"); free(docs); } addPackageFileList (&fl, pkg, &pkg->fileList, &specialDoc, &specialLic, 1); /* Now process special docs and licenses if present */ if (specialDoc) processSpecialDir(spec, pkg, &fl, specialDoc, didInstall, test); if (specialLic) processSpecialDir(spec, pkg, &fl, specialLic, didInstall, test); if (fl.processingFailed) goto exit; #ifdef HAVE_LIBDW { /* Check build-ids and add build-ids links for files to package list. */ const char *arch = headerGetString(pkg->header, RPMTAG_ARCH); if (rpmExpandNumeric("%{?__debug_package}") && !rstreq(arch, "noarch")) { /* Go through the current package list and generate a files list. */ ARGV_t idFiles = NULL; if (generateBuildIDs (&fl, &idFiles) != 0) { rpmlog(RPMLOG_ERR, _("Generating build-id links failed\n")); fl.processingFailed = 1; argvFree(idFiles); goto exit; } if (idFiles != NULL) { resetPackageFilesDefaults (&fl, pkgFlags); addPackageFileList (&fl, pkg, &idFiles, NULL, NULL, 0); } argvFree(idFiles); if (fl.processingFailed) goto exit; } } #endif /* Verify that file attributes scope over hardlinks correctly. */ if (checkHardLinks(fl.files) && spec->rpmformat < 6) (void) rpmlibNeedsFeature(pkg, "PartialHardlinkSets", "4.0.4-1"); genCpioListAndHeader(&fl, spec, pkg, 0); exit: FileListFree(&fl); specialDirFree(specialDoc); specialDirFree(specialLic); return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK; } rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags) { struct Source *srcPtr; struct FileList_s fl {}; ARGV_t files = NULL; Package pkg; Package sourcePkg = spec->sourcePackage; static char *_srcdefattr; static int oneshot; if (!oneshot) { _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL); if (_srcdefattr && !*_srcdefattr) _srcdefattr = _free(_srcdefattr); oneshot = 1; } /* Construct the file list and source entries */ argvAdd(&files, spec->specFile); for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) { char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""), srcPtr->path, NULL); argvAdd(&files, sfn); free(sfn); } for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) { char * sfn; sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""), srcPtr->path, NULL); argvAdd(&files, sfn); free(sfn); } } sourcePkg->cpioList = NULL; /* Init the file list structure */ fl.pool = rpmstrPoolLink(spec->pool); if (_srcdefattr) { char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL); parseForAttr(fl.pool, a, 1, &fl.def); free(a); } fl.pkgFlags = pkgFlags; for (ARGV_const_t fp = files; *fp != NULL; fp++) { const char *diskPath = *fp; char *tmp; FileListRec flp; SKIPSPACE(diskPath); if (! *diskPath) continue; fl.files.push_back({}); flp = &fl.files.back(); /* The first source file is the spec file */ flp->flags = (fl.files.size() == 1) ? RPMFILE_SPECFILE : 0; /* files with leading ! are no source files */ if (*diskPath == '!') { flp->flags |= RPMFILE_GHOST; diskPath++; } tmp = xstrdup(diskPath); /* basename() might modify */ flp->diskPath = xstrdup(diskPath); flp->cpioPath = xstrdup(basename(tmp)); flp->verifyFlags = RPMVERIFY_ALL; free(tmp); if (stat(diskPath, &flp->fl_st)) { rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"), diskPath, strerror(errno)); fl.processingFailed = 1; } else { if (S_ISREG(flp->fl_mode) && flp->fl_size >= UINT32_MAX) fl.largeFiles = 1; } if (fl.def.ar.ar_fmodestr) { flp->fl_mode &= S_IFMT; flp->fl_mode |= fl.def.ar.ar_fmode; } if (fl.def.ar.ar_user) { flp->uname = fl.def.ar.ar_user; } if (! flp->uname) { flp->uname = rpmstrPoolId(fl.pool, UID_0_USER, 1); } if (fl.def.ar.ar_group) { flp->gname = fl.def.ar.ar_group; } if (! flp->gname) { flp->gname = rpmstrPoolId(fl.pool, GID_0_GROUP, 1); } flp->langs = xstrdup(""); } argvFree(files); if (! fl.processingFailed) { if (sourcePkg->header != NULL) { genCpioListAndHeader(&fl, spec, sourcePkg, 1); } } FileListFree(&fl); return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK; } /** * Check packaged file list against what's in the build root. * @param buildRoot path of build root * @param fileList packaged file list * @return -1 if skipped, 0 on OK, 1 on error */ static int checkFiles(const char *buildRoot, StringBuf fileList) { static const char * av_ckfile[] = { "%{?__check_files}", NULL }; StringBuf sb_stdout = NULL; int rc = -1; char * s = rpmExpand(av_ckfile[0], NULL); if (!(s && *s)) goto exit; rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s); rc = rpmfcExec((ARGV_const_t)av_ckfile, fileList, &sb_stdout, 0, buildRoot); if (rc < 0) goto exit; if (sb_stdout) { int _unpackaged_files_terminate_build = rpmExpandNumeric("%{?_unpackaged_files_terminate_build}"); const char * t = getStringBuf(sb_stdout); if ((*t != '\0') && (*t != '\n')) { rc = (_unpackaged_files_terminate_build) ? 1 : 0; rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING), _("Installed (but unpackaged) file(s) found:\n%s"), t); } } exit: freeStringBuf(sb_stdout); free(s); return rc; } static rpmTagVal copyTagsFromMainDebug[] = { RPMTAG_ARCH, RPMTAG_SUMMARY, RPMTAG_DESCRIPTION, RPMTAG_GROUP, /* see addTargets */ RPMTAG_OS, RPMTAG_PLATFORM, RPMTAG_OPTFLAGS, RPMTAG_SOURCERPM, 0 }; /* this is a hack: patch the summary and the description to include * the correct package name */ static void patchDebugPackageString(Package dbg, rpmTag tag, Package pkg, Package mainpkg) { const char *oldname, *newname, *old, *p; char *oldsubst = NULL, *newsubst = NULL; oldname = headerGetString(mainpkg->header, RPMTAG_NAME); newname = headerGetString(pkg->header, RPMTAG_NAME); rasprintf(&oldsubst, "package %s", oldname); rasprintf(&newsubst, "package %s", newname); old = headerGetString(dbg->header, tag); p = old ? strstr(old, oldsubst) : NULL; if (p) { char *newval = NULL; rasprintf(&newval, "%.*s%s%s", (int)(p - old), old, newsubst, p + strlen(oldsubst)); headerDel(dbg->header, tag); headerPutString(dbg->header, tag, newval); _free(newval); } _free(oldsubst); _free(newsubst); } /* Early prototype for use in filterDebuginfoPackage. */ static void addPackageDeps(Package from, Package to, enum rpmTag_e tag); /* create a new debuginfo subpackage for package pkg from the * main debuginfo package */ static Package cloneDebuginfoPackage(rpmSpec spec, Package pkg, Package maindbg) { Package dbg = NULL; char *dbgname = headerFormat(pkg->header, "%{name}-debuginfo", NULL); if (lookupPackage(spec, dbgname, PART_NAME|PART_QUIET, &dbg) == RPMRC_OK) { rpmlog(RPMLOG_WARNING, _("package %s already exists\n"), dbgname); goto exit; } dbg = newPackage(dbgname, spec->pool, &spec->packages); headerPutString(dbg->header, RPMTAG_NAME, dbgname); copyInheritedTags(dbg->header, pkg->header); headerDel(dbg->header, RPMTAG_GROUP); headerCopyTags(maindbg->header, dbg->header, copyTagsFromMainDebug); dbg->autoReq = maindbg->autoReq; dbg->autoProv = maindbg->autoProv; /* patch summary and description strings */ patchDebugPackageString(dbg, RPMTAG_SUMMARY, pkg, spec->packages); patchDebugPackageString(dbg, RPMTAG_DESCRIPTION, pkg, spec->packages); /* Add self-provides (normally done by addTargets) */ addPackageProvides(dbg); dbg->ds = rpmdsThis(dbg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL); exit: _free(dbgname); return dbg; } /* collect the debug files for package pkg and put them into * a (possibly new) debuginfo subpackage */ static void filterDebuginfoPackage(rpmSpec spec, Package pkg, Package maindbg, Package dbgsrc, const char *buildroot, const char *uniquearch) { rpmfi fi; ARGV_t files = NULL; ARGV_t dirs = NULL; int lastdiridx = -1, dirsadded; char *path = NULL, *p, *pmin; size_t buildrootlen = strlen(buildroot); /* ignore noarch subpackages */ if (rstreq(headerGetString(pkg->header, RPMTAG_ARCH), "noarch")) return; if (!uniquearch) uniquearch = ""; fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD); /* Check if the current package has files with debug info and add them to the file list */ fi = rpmfiInit(fi, 0); while (rpmfiNext(fi) >= 0) { const char *name = rpmfiFN(fi); int namel = strlen(name); /* strip trailing .debug like in find-debuginfo.sh */ if (namel > 6 && !strcmp(name + namel - 6, ".debug")) namel -= 6; /* fileRenameMap doesn't necessarily have to be initialized */ if (!pkg->fileRenameMap.empty()) { auto range = pkg->fileRenameMap.equal_range(name); if (range.first != range.second) { if (std::distance(range.first, range.second) > 1) rpmlog(RPMLOG_WARNING, _("%s was mapped to multiple filenames"), name); auto & it = range.first; name = it->second.c_str(); namel = it->second.size(); } } /* generate path */ rasprintf(&path, "%s%s%.*s%s.debug", buildroot, DEBUG_LIB_DIR, namel, name, uniquearch); /* If that file exists we have debug information for it */ if (access(path, F_OK) == 0) { /* Append the file list preamble */ if (!files) { char *attr = mkattr(); argvAdd(&files, attr); argvAddAttr(&files, RPMFILE_DIR, DEBUG_LIB_DIR); free(attr); } /* Add the files main debug-info file */ argvAdd(&files, path + buildrootlen); /* Add the dir(s) */ dirsadded = 0; pmin = path + buildrootlen + strlen(DEBUG_LIB_DIR); while ((p = strrchr(path + buildrootlen, '/')) != NULL && p > pmin) { *p = 0; if (lastdiridx >= 0 && !strcmp(dirs[lastdiridx], path + buildrootlen)) break; /* already added this one */ argvAdd(&dirs, path + buildrootlen); dirsadded++; } if (dirsadded) lastdiridx = argvCount(dirs) - dirsadded; /* remember longest dir */ } path = _free(path); } rpmfiFree(fi); /* Exclude debug files for files which were excluded in respective non-debug package */ for (ARGV_const_t excl = pkg->fileExcludeList; excl && *excl; excl++) { const char *name = *excl; /* generate path */ rasprintf(&path, "%s%s%s%s.debug", buildroot, DEBUG_LIB_DIR, name, uniquearch); /* Exclude only debuginfo files which actually exist */ if (access(path, F_OK) == 0) { char *line = NULL; rasprintf(&line, "%%exclude %s", path + buildrootlen); argvAdd(&files, line); _free(line); } path = _free(path); } /* add collected directories to file list */ if (dirs) { int i; argvSort(dirs, NULL); for (i = 0; dirs[i]; i++) { if (!i || strcmp(dirs[i], dirs[i - 1]) != 0) argvAddAttr(&files, RPMFILE_DIR, dirs[i]); } dirs = argvFree(dirs); } if (files) { /* we have collected some files. Now put them in a debuginfo * package. If this is not the main package, clone the main * debuginfo package */ if (pkg == spec->packages) maindbg->fileList = files; else { Package dbg = cloneDebuginfoPackage(spec, pkg, maindbg); dbg->fileList = files; /* Recommend the debugsource package (or the main debuginfo). */ addPackageDeps(dbg, dbgsrc ? dbgsrc : maindbg, RPMTAG_RECOMMENDNAME); } } } /* add the debug dwz files to package pkg. * return 1 if something was added, 0 otherwise. */ static int addDebugDwz(Package pkg, char *buildroot) { int ret = 0; char *path = NULL; struct stat sbuf; rasprintf(&path, "%s%s", buildroot, DEBUG_DWZ_DIR); if (lstat(path, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)) { if (!pkg->fileList) { char *attr = mkattr(); argvAdd(&pkg->fileList, attr); argvAddAttr(&pkg->fileList, (uint32_t)RPMFILE_DIR|RPMFILE_ARTIFACT, DEBUG_LIB_DIR); free(attr); } argvAddAttr(&pkg->fileList, RPMFILE_ARTIFACT, DEBUG_DWZ_DIR); ret = 1; } path = _free(path); return ret; } /* add the debug source files to package pkg. * return 1 if something was added, 0 otherwise. */ static int addDebugSrc(Package pkg, char *buildroot) { int ret = 0; char *path = NULL; DIR *d; struct dirent *de; /* not needed if we have an extra debugsource subpackage */ if (rpmExpandNumeric("%{?_debugsource_packages}")) return 0; rasprintf(&path, "%s%s", buildroot, DEBUG_SRC_DIR); d = opendir(path); path = _free(path); if (d) { while ((de = readdir(d)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; rasprintf(&path, "%s/%s", DEBUG_SRC_DIR, de->d_name); if (!pkg->fileList) { char *attr = mkattr(); argvAdd(&pkg->fileList, attr); free(attr); } argvAdd(&pkg->fileList, path); path = _free(path); ret = 1; } closedir(d); } return ret; } /* find the debugsource package, if it has been created. * We do this simply by searching for a package with the right name. */ static Package findDebugsourcePackage(rpmSpec spec) { Package pkg = NULL; if (lookupPackage(spec, "debugsource", PART_SUBNAME|PART_QUIET, &pkg)) return NULL; return pkg && pkg->fileList ? pkg : NULL; } /* find the main debuginfo package. We do this simply by * searching for a package with the right name. */ static Package findDebuginfoPackage(rpmSpec spec) { Package pkg = NULL; if (lookupPackage(spec, "debuginfo", PART_SUBNAME|PART_QUIET, &pkg)) return NULL; return pkg && pkg->fileList ? pkg : NULL; } /* add a dependency (e.g. RPMTAG_REQUIRENAME or RPMTAG_RECOMMENDNAME) for package "to" into package "from". */ static void addPackageDeps(Package from, Package to, enum rpmTag_e tag) { const char *name; char *evr, *isaprov; name = headerGetString(to->header, RPMTAG_NAME); evr = headerGetAsString(to->header, RPMTAG_EVR); isaprov = rpmExpand(name, "%{?_isa}", NULL); addReqProv(from, tag, isaprov, evr, RPMSENSE_EQUAL, 0); free(isaprov); free(evr); } rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, int didInstall, int test) { Package pkg; rpmRC rc = RPMRC_OK; char *buildroot; char *uniquearch = NULL; Package maindbg = NULL; /* the (existing) main debuginfo package */ Package deplink = NULL; /* create requires to this package */ /* The debugsource package, if it exists, that the debuginfo package(s) should Recommend. */ Package dbgsrcpkg = NULL; int processDebug = rpmExpandNumeric("%{?__debug_package}"); #ifdef HAVE_LIBDW elf_version (EV_CURRENT); #endif check_fileList = newStringBuf(); buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL); if (processDebug) dbgsrcpkg = findDebugsourcePackage(spec); if (processDebug && rpmExpandNumeric("%{?_debuginfo_subpackages}")) { maindbg = findDebuginfoPackage(spec); if (maindbg) { /* move debuginfo package to back */ if (maindbg->next) { Package *pp; /* dequeue */ for (pp = &spec->packages; *pp != maindbg; pp = &(*pp)->next) ; *pp = maindbg->next; maindbg->next = 0; /* enqueue at tail */ for (; *pp; pp = &(*pp)->next) ; *pp = maindbg; } /* delete unsplit file list, we will re-add files back later */ maindbg->fileFile = argvFree(maindbg->fileFile); maindbg->fileList = argvFree(maindbg->fileList); if (rpmExpandNumeric("%{?_unique_debug_names}")) uniquearch = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL); } } else if (dbgsrcpkg != NULL) { /* We have a debugsource package, but no debuginfo subpackages. The main debuginfo package should recommend the debugsource one. */ Package dbgpkg = findDebuginfoPackage(spec); if (dbgpkg) addPackageDeps(dbgpkg, dbgsrcpkg, RPMTAG_RECOMMENDNAME); } for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { char *nvr; const char *a; int header_color; int arch_color; if (pkg == maindbg) { /* if there is just one debuginfo package, we put our extra stuff * in it. Otherwise we put it in the main debug package */ Package extradbg = !maindbg->fileList && maindbg->next && !maindbg->next->next ? maindbg->next : maindbg; if (addDebugDwz(extradbg, buildroot)) deplink = extradbg; if (addDebugSrc(extradbg, buildroot)) deplink = extradbg; if (dbgsrcpkg != NULL) addPackageDeps(extradbg, dbgsrcpkg, RPMTAG_RECOMMENDNAME); maindbg = NULL; /* all normal packages processed */ } if (pkg->fileList == NULL) continue; nvr = headerGetAsString(pkg->header, RPMTAG_NVRA); rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr); free(nvr); if ((rc = processPackageFiles(spec, pkgFlags, pkg, didInstall, test)) != RPMRC_OK) goto exit; if (maindbg) filterDebuginfoPackage(spec, pkg, maindbg, dbgsrcpkg, buildroot, uniquearch); else if (deplink && pkg != deplink) addPackageDeps(pkg, deplink, RPMTAG_REQUIRENAME); if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK) goto exit; a = headerGetString(pkg->header, RPMTAG_ARCH); header_color = headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR); if (!rstreq(a, "noarch")) { arch_color = rpmGetArchColor(a); if (arch_color > 0 && header_color > 0 && !(arch_color & header_color)) { rpmlog(RPMLOG_WARNING, _("Binaries arch (%d) not matching the package arch (%d).\n"), header_color, arch_color); } } else if (header_color != 0) { int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}"); rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, _("Arch dependent binaries in noarch package\n")); if (terminate) { rc = RPMRC_FAIL; goto exit; } } } /* Now we have in fileList list of files from all packages. * We pass it to a script which does the work of finding missing * and duplicated files. */ if (checkFiles(spec->buildRoot, check_fileList) > 0) { rc = RPMRC_FAIL; } exit: check_fileList = freeStringBuf(check_fileList); _free(buildroot); _free(uniquearch); return rc; } rpm-software-management-rpm-3c1f23f/build/misc.cc000066400000000000000000000023011511627505500220230ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/misc.c */ #include "system.h" #include #include #include #include #include #include "rpmbuild_misc.hh" #include "debug.h" #define BUF_CHUNK 1024 struct StringBufRec { std::string buf; }; StringBuf newStringBuf(void) { return new StringBufRec {}; } StringBuf freeStringBuf(StringBuf sb) { delete sb; return NULL; } void stripTrailingBlanksStringBuf(StringBuf sb) { while (!sb->buf.empty() && risspace(sb->buf.back())) sb->buf.erase(sb->buf.size() - 1); } const char * getStringBuf(StringBuf sb) { return (sb != NULL) ? sb->buf.c_str() : NULL; } void appendStringBufAux(StringBuf sb, const std::string & s, int nl) { sb->buf += s; if (nl) sb->buf += '\n'; } int parseUnsignedNum(const char * line, uint32_t * res) { char * s1 = NULL; unsigned long rc; uint32_t result; if (line == NULL) return -1; while (isspace(*line)) line++; if (!isdigit(*line)) return -1; rc = strtoul(line, &s1, 10); if (*s1 || s1 == line || rc == ULONG_MAX || rc > UINT_MAX) return -1; result = (uint32_t)rc; if (res) *res = result; return 0; } rpm-software-management-rpm-3c1f23f/build/pack.cc000066400000000000000000000626271511627505500220270ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/pack.c * Assemble components of an RPM package. */ #include "system.h" #include #include #include #include #include #include /* RPMSIGTAG*, rpmReadPackageFile */ #include #include #include #include "rpmio_internal.hh" /* fdInitDigest, fdFiniDigest */ #include "signature.hh" #include "rpmlead.hh" #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "rpmmacro_internal.hh" #include "debug.h" static int rpmPackageFilesArchive(rpmfiles fi, int isSrc, FD_t cfd, ARGV_t dpaths, rpm_loff_t * archiveSize, char ** failedFile) { int rc = 0; rpmfi archive = rpmfiNewArchiveWriter(cfd, fi); while (!rc && (rc = rpmfiNext(archive)) >= 0) { /* Copy file into archive. */ FD_t rfd = NULL; const char *path = dpaths[rpmfiFX(archive)]; rfd = Fopen(path, "r.ufdio"); if (Ferror(rfd)) { rc = RPMERR_OPEN_FAILED; } else { rc = rpmfiArchiveWriteFile(archive, rfd); } if (rc && failedFile) *failedFile = xstrdup(path); if (rfd) { /* preserve any prior errno across close */ int myerrno = errno; Fclose(rfd); errno = myerrno; } } if (rc == RPMERR_ITER_END) rc = 0; /* Finish the payload stream */ if (!rc) rc = rpmfiArchiveClose(archive); if (archiveSize) *archiveSize = (rc == 0) ? rpmfiArchiveTell(archive) : 0; rpmfiFree(archive); return rc; } /** * @todo Create transaction set *much* earlier. */ static rpmRC cpio_doio(FD_t fdo, Package pkg, const char * fmodeMacro, rpm_loff_t *archiveSize, char ** pld, char ** pld512, char ** pld3) { char *failedFile = NULL; FD_t cfd; int fsmrc; (void) Fflush(fdo); cfd = Fdopen(fdDup(Fileno(fdo)), fmodeMacro); if (cfd == NULL) return RPMRC_FAIL; /* Calculate alternative (uncompressed) payload digest while writing */ fdInitDigestID(cfd, RPM_HASH_SHA256, RPMTAG_PAYLOADSHA256ALT, 0); fdInitDigestID(cfd, RPM_HASH_SHA512, RPMTAG_PAYLOADSHA512ALT, 0); fdInitDigestID(cfd, RPM_HASH_SHA3_256, RPMTAG_PAYLOADSHA3_256ALT, 0); fsmrc = rpmPackageFilesArchive(pkg->cpioList, headerIsSource(pkg->header), cfd, pkg->dpaths, archiveSize, &failedFile); fdFiniDigest(cfd, RPMTAG_PAYLOADSHA256ALT, (void **)pld, NULL, 1); fdFiniDigest(cfd, RPMTAG_PAYLOADSHA512ALT, (void **)pld512, NULL, 1); fdFiniDigest(cfd, RPMTAG_PAYLOADSHA3_256ALT, (void **)pld3, NULL, 1); if (fsmrc) { char *emsg = rpmfileStrerror(fsmrc); if (failedFile) rpmlog(RPMLOG_ERR, _("create archive failed on file %s: %s\n"), failedFile, emsg); else rpmlog(RPMLOG_ERR, _("create archive failed: %s\n"), emsg); free(emsg); } free(failedFile); Fclose(cfd); return (fsmrc == 0) ? RPMRC_OK : RPMRC_FAIL; } static rpmRC addFileToTag(rpmSpec spec, const char * file, Header h, rpmTagVal tag, int append) { StringBuf sb = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ int flags = ALLOW_EMPTY; /* no script file is not an error */ if (file == NULL) return RPMRC_OK; sb = newStringBuf(); #pragma omp critical { if (append) { const char *s = headerGetString(h, tag); if (s) { appendLineStringBuf(sb, s); headerDel(h, tag); } } if (readManifest(spec, file, rpmTagGetName(tag), flags, NULL, &sb) >= 0) { headerPutString(h, tag, getStringBuf(sb)); rc = RPMRC_OK; } } /* omp critical */ freeStringBuf(sb); return rc; } static rpmRC processScriptFiles(rpmSpec spec, Package pkg) { int addflags = 0; rpmRC rc = RPMRC_FAIL; Header h = pkg->header; std::vector*> tfa { &pkg->triggerFiles, &pkg->fileTriggerFiles, &pkg->transFileTriggerFiles }; rpmTagVal progTags[] = {RPMTAG_TRIGGERSCRIPTPROG, RPMTAG_FILETRIGGERSCRIPTPROG, RPMTAG_TRANSFILETRIGGERSCRIPTPROG}; rpmTagVal flagTags[] = {RPMTAG_TRIGGERSCRIPTFLAGS, RPMTAG_FILETRIGGERSCRIPTFLAGS, RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS}; rpmTagVal scriptTags[] = {RPMTAG_TRIGGERSCRIPTS, RPMTAG_FILETRIGGERSCRIPTS, RPMTAG_TRANSFILETRIGGERSCRIPTS}; rpmTagVal priorityTags[] = {0, RPMTAG_FILETRIGGERPRIORITIES, RPMTAG_TRANSFILETRIGGERPRIORITIES}; if (addFileToTag(spec, pkg->preInFile, h, RPMTAG_PREIN, 1) || addFileToTag(spec, pkg->preUnFile, h, RPMTAG_PREUN, 1) || addFileToTag(spec, pkg->preTransFile, h, RPMTAG_PRETRANS, 1) || addFileToTag(spec, pkg->postInFile, h, RPMTAG_POSTIN, 1) || addFileToTag(spec, pkg->postUnFile, h, RPMTAG_POSTUN, 1) || addFileToTag(spec, pkg->postTransFile, h, RPMTAG_POSTTRANS, 1) || addFileToTag(spec, pkg->preunTransFile, h, RPMTAG_PREUNTRANS, 1) || addFileToTag(spec, pkg->postunTransFile, h, RPMTAG_POSTUNTRANS, 1) || addFileToTag(spec, pkg->verifyFile, h, RPMTAG_VERIFYSCRIPT, 1)) { goto exit; } /* we need the index number for accessing the other associated arrays */ for (size_t i = 0; i < tfa.size(); ++i) { addflags = 0; /* if any trigger has flags, we need to add flags entry for all of them */ for (auto const & p : *tfa[i]) { if (p.flags) { addflags = 1; break; } } for (auto const & p : *tfa[i]) { headerPutString(h, progTags[i], p.prog); if (priorityTags[i]) { headerPutUint32(h, priorityTags[i], &p.priority, 1); } if (addflags) { headerPutUint32(h, flagTags[i], &p.flags, 1); } if (p.script) { headerPutString(h, scriptTags[i], p.script); } else if (p.fileName) { if (addFileToTag(spec, p.fileName, h, scriptTags[i], 0)) { goto exit; } } else { /* This is dumb. When the header supports NULL string */ /* this will go away. */ headerPutString(h, scriptTags[i], ""); } } } rc = RPMRC_OK; exit: return rc; } struct charInDepData { char c; int present; }; static rpmRC charInDepCb(void *cbdata, rpmrichParseType type, const char *n, int nl, const char *e, int el, rpmsenseFlags sense, rpmrichOp op, char **emsg) { struct charInDepData *data = (struct charInDepData *)cbdata; if (e && data && memchr(e, data->c, el)) data->present = 1; return RPMRC_OK; } static int haveCharInDep(Package pkg, char c) { struct charInDepData data = {c, 0}; for (int i = 0; i < PACKAGE_NUM_DEPS; i++) { rpmds ds = rpmdsInit(pkg->dependencies[i]); while (rpmdsNext(ds) >= 0) { if (rpmdsIsRich(ds)) { const char *depstr = rpmdsN(ds); rpmrichParse(&depstr, NULL, charInDepCb, &data); } else { const char *evr = rpmdsEVR(ds); if (strchr(evr, c)) data.present = 1; } if (data.present) return 1; } } return 0; } static int haveRichDep(Package pkg) { for (int i = 0; i < PACKAGE_NUM_DEPS; i++) { rpmds ds = rpmdsInit(pkg->dependencies[i]); rpmTagVal tagN = rpmdsTagN(ds); if (tagN != RPMTAG_REQUIRENAME && tagN != RPMTAG_RECOMMENDNAME && tagN != RPMTAG_SUGGESTNAME && tagN != RPMTAG_SUPPLEMENTNAME && tagN != RPMTAG_ENHANCENAME && tagN != RPMTAG_CONFLICTNAME) continue; while (rpmdsNext(ds) >= 0) { if (rpmdsIsRich(ds)) return 1; } } return 0; } static char *getIOFlags(Package pkg) { char *rpmio_flags; const char *s; /* Save payload information */ if (headerIsSource(pkg->header)) rpmio_flags = rpmExpand("%{?_source_payload}", NULL); else rpmio_flags = rpmExpand("%{?_binary_payload}", NULL); /* If not configured or bogus, fall back to gz */ if (rpmio_flags[0] != 'w') { free(rpmio_flags); rpmio_flags = xstrdup("w9.gzdio"); } s = strchr(rpmio_flags, '.'); if (s) { char *buf = NULL; const char *compr = NULL; headerPutString(pkg->header, RPMTAG_PAYLOADFORMAT, "cpio"); if (rstreq(s+1, "ufdio")) { compr = NULL; } else if (rstreq(s+1, "gzdio")) { compr = "gzip"; #ifdef HAVE_BZLIB_H } else if (rstreq(s+1, "bzdio")) { compr = "bzip2"; /* Add prereq on rpm version that understands bzip2 payloads */ (void) rpmlibNeedsFeature(pkg, "PayloadIsBzip2", "3.0.5-1"); #endif #ifdef HAVE_LZMA_H } else if (rstreq(s+1, "xzdio")) { compr = "xz"; (void) rpmlibNeedsFeature(pkg, "PayloadIsXz", "5.2-1"); } else if (rstreq(s+1, "lzdio")) { compr = "lzma"; (void) rpmlibNeedsFeature(pkg, "PayloadIsLzma", "4.4.6-1"); #endif #ifdef HAVE_ZSTD } else if (rstreq(s+1, "zstdio")) { compr = "zstd"; /* Add prereq on rpm version that understands zstd payloads */ (void) rpmlibNeedsFeature(pkg, "PayloadIsZstd", "5.4.18-1"); #endif } else { rpmlog(RPMLOG_ERR, _("Unknown payload compression: %s\n"), rpmio_flags); rpmio_flags = _free(rpmio_flags); goto exit; } if (compr) headerPutString(pkg->header, RPMTAG_PAYLOADCOMPRESSOR, compr); buf = xstrdup(rpmio_flags); buf[s - rpmio_flags] = '\0'; headerPutString(pkg->header, RPMTAG_PAYLOADFLAGS, buf+1); free(buf); } exit: return rpmio_flags; } static void finalizeDeps(Package pkg) { /* check if the package has a dependency with a '~' */ if (haveCharInDep(pkg, '~')) (void) rpmlibNeedsFeature(pkg, "TildeInVersions", "4.10.0-1"); /* check if the package has a dependency with a '^' */ if (haveCharInDep(pkg, '^')) (void) rpmlibNeedsFeature(pkg, "CaretInVersions", "4.15.0-1"); /* check if the package has a rich dependency */ if (haveRichDep(pkg)) (void) rpmlibNeedsFeature(pkg, "RichDependencies", "4.12.0-1"); /* All dependencies added finally, write them into the header */ for (int i = 0; i < PACKAGE_NUM_DEPS; i++) { /* Nuke any previously added dependencies from the header */ headerDel(pkg->header, rpmdsTagN(pkg->dependencies[i])); headerDel(pkg->header, rpmdsTagEVR(pkg->dependencies[i])); headerDel(pkg->header, rpmdsTagF(pkg->dependencies[i])); headerDel(pkg->header, rpmdsTagTi(pkg->dependencies[i])); /* ...and add again, now with automatic dependencies included */ rpmdsPutToHeader(pkg->dependencies[i], pkg->header); } } static void *nullDigest(int algo, int ascii) { void *d = NULL; DIGEST_CTX ctx = rpmDigestInit(algo, 0); rpmDigestFinal(ctx, &d, NULL, ascii); return d; } static rpmRC fdJump(FD_t fd, off_t offset) { if (Fseek(fd, offset, SEEK_SET) < 0) { rpmlog(RPMLOG_ERR, _("Could not seek in file %s: %s\n"), Fdescr(fd), Fstrerror(fd)); return RPMRC_FAIL; } return RPMRC_OK; } static rpmRC fdConsume(FD_t fd, off_t start, off_t nbytes) { size_t bufsiz = 32*BUFSIZ; unsigned char buf[bufsiz]; off_t left = nbytes; ssize_t nb; if (start && fdJump(fd, start)) return RPMRC_FAIL; while (left > 0) { nb = Fread(buf, 1, (left < bufsiz) ? left : bufsiz, fd); if (nb > 0) left -= nb; else break; }; if (left) { rpmlog(RPMLOG_ERR, _("Failed to read %jd bytes in file %s: %s\n"), (intmax_t) nbytes, Fdescr(fd), Fstrerror(fd)); } return (left == 0) ? RPMRC_OK : RPMRC_FAIL; } static rpmRC writeHdr(FD_t fd, Header pkgh) { /* Reallocate the header into one contiguous region for writing. */ Header h = headerReload(headerCopy(pkgh), RPMTAG_HEADERIMMUTABLE); rpmRC rc = RPMRC_FAIL; if (h == NULL) { rpmlog(RPMLOG_ERR,_("Unable to create immutable header region\n")); goto exit; } if (headerWrite(fd, h, HEADER_MAGIC_YES)) { rpmlog(RPMLOG_ERR, _("Unable to write header to %s: %s\n"), Fdescr(fd), Fstrerror(fd)); goto exit; } (void) Fflush(fd); rc = RPMRC_OK; exit: headerFree(h); return rc; } /* * This is more than just a little insane: * In order to write the signature, we need to know the size and * the size and digests of the header and payload, which are located * after the signature on disk. We also need a digest of the compressed * payload for the main header, and of course the payload is after the * header on disk. So we need to create placeholders for both the * signature and main header that exactly match the final sizes, calculate * the payload digest, then generate and write the real main header to * be able to FINALLY calculate the digests we need for the signature * header. In other words, we need to write things in the exact opposite * order to how the RPM format is laid on disk. */ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp, const char *fileName, char **cookie, rpm_time_t buildTime, const char* buildHost, uint32_t rpmformat) { FD_t fd = NULL; char * rpmio_flags = NULL; char * SHA1 = NULL; char * SHA256 = NULL; char * SHA3_256 = NULL; uint8_t * MD5 = NULL; char * pld = NULL; char * upld = NULL; char * pld512 = NULL; char * upld512 = NULL; char * pld3 = NULL; char * upld3 = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ rpm_loff_t archiveSize = 0; /* uncompressed */ rpm_loff_t payloadSize = 0; /* compressed */ off_t sigStart, hdrStart, payloadStart, payloadEnd; if (pkgidp) *pkgidp = NULL; rpmio_flags = getIOFlags(pkg); if (!rpmio_flags) goto exit; finalizeDeps(pkg); /* Create and add the cookie */ if (cookie) { free(*cookie); rasprintf(cookie, "%s %d", buildHost, buildTime); headerPutString(pkg->header, RPMTAG_COOKIE, *cookie); } if (rpmformat >= 6) { headerPutUint32(pkg->header, RPMTAG_RPMFORMAT, &rpmformat, 1); headerPutUint64(pkg->header, RPMTAG_PAYLOADSIZE, &payloadSize, 1); headerPutUint64(pkg->header, RPMTAG_PAYLOADSIZEALT, &archiveSize, 1); } else { uint32_t pld_algo = RPM_HASH_SHA256; headerPutUint32(pkg->header, RPMTAG_PAYLOADSHA256ALGO, &pld_algo, 1); } /* Create a dummy payload digests to get the header size right */ pld = (char *)nullDigest(RPM_HASH_SHA256, 1); headerPutString(pkg->header, RPMTAG_PAYLOADSHA256, pld); headerPutString(pkg->header, RPMTAG_PAYLOADSHA256ALT, pld); pld = _free(pld); if (rpmformat >= 6) { pld512 = (char *)nullDigest(RPM_HASH_SHA512, 1); pld3 = (char *)nullDigest(RPM_HASH_SHA3_256, 1); headerPutString(pkg->header, RPMTAG_PAYLOADSHA512, pld512); headerPutString(pkg->header, RPMTAG_PAYLOADSHA512ALT, pld512); headerPutString(pkg->header, RPMTAG_PAYLOADSHA3_256, pld3); headerPutString(pkg->header, RPMTAG_PAYLOADSHA3_256ALT, pld3); pld3 = _free(pld3); pld512 = _free(pld512); } /* Check for UTF-8 encoding of string tags, add encoding tag if all good */ if (checkForEncoding(pkg->header, 1)) goto exit; /* Open the output file */ fd = Fopen(fileName, "w+.ufdio"); if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOG_ERR, _("Could not open %s: %s\n"), fileName, Fstrerror(fd)); goto exit; } /* Write the lead section into the package. */ if (rpmLeadWrite(fd, pkg->header)) { rpmlog(RPMLOG_ERR, _("Unable to write package: %s\n"), Fstrerror(fd)); goto exit; } /* Save the position of signature section */ sigStart = Ftell(fd); /* Generate and write a placeholder signature header */ if (rpmformat < 6) { SHA1 = (char *)nullDigest(RPM_HASH_SHA1, 1); MD5 = (uint8_t *)nullDigest(RPM_HASH_MD5, 0); } else { SHA3_256 = (char *)nullDigest(RPM_HASH_SHA3_256, 1); } SHA256 = (char *)nullDigest(RPM_HASH_SHA256, 1); if (rpmGenerateSignature(SHA3_256, SHA256, SHA1, MD5, 0, 0, fd, rpmformat)) { goto exit; } SHA1 = _free(SHA1); SHA256 = _free(SHA256); SHA3_256 = _free(SHA3_256); MD5 = _free(MD5); /* Write a placeholder header. */ hdrStart = Ftell(fd); if (writeHdr(fd, pkg->header)) goto exit; /* Write payload section (cpio archive) */ payloadStart = Ftell(fd); if (cpio_doio(fd, pkg, rpmio_flags, &archiveSize, &upld, &upld512, &upld3)) goto exit; payloadEnd = Ftell(fd); payloadSize = payloadEnd - payloadStart; /* Delete dummies from the main header */ headerDel(pkg->header, RPMTAG_PAYLOADSHA256); headerDel(pkg->header, RPMTAG_PAYLOADSHA256ALT); if (rpmformat >= 6) { headerDel(pkg->header, RPMTAG_PAYLOADSIZE); headerDel(pkg->header, RPMTAG_PAYLOADSIZEALT); headerDel(pkg->header, RPMTAG_PAYLOADSHA512); headerDel(pkg->header, RPMTAG_PAYLOADSHA512ALT); headerDel(pkg->header, RPMTAG_PAYLOADSHA3_256); headerDel(pkg->header, RPMTAG_PAYLOADSHA3_256ALT); } /* Re-read payload to calculate compressed digest */ fdInitDigestID(fd, RPM_HASH_SHA256, RPMTAG_PAYLOADSHA256, 0); fdInitDigestID(fd, RPM_HASH_SHA512, RPMTAG_PAYLOADSHA512, 0); fdInitDigestID(fd, RPM_HASH_SHA3_256, RPMTAG_PAYLOADSHA3_256, 0); if (fdConsume(fd, payloadStart, payloadSize)) goto exit; fdFiniDigest(fd, RPMTAG_PAYLOADSHA256, (void **)&pld, NULL, 1); fdFiniDigest(fd, RPMTAG_PAYLOADSHA512, (void **)&pld512, NULL, 1); fdFiniDigest(fd, RPMTAG_PAYLOADSHA3_256, (void **)&pld3, NULL, 1); /* Insert the payload digests + size in main header */ headerPutString(pkg->header, RPMTAG_PAYLOADSHA256, pld); headerPutString(pkg->header, RPMTAG_PAYLOADSHA256ALT, upld); if (rpmformat >= 6) { headerPutString(pkg->header, RPMTAG_PAYLOADSHA512, pld512); headerPutString(pkg->header, RPMTAG_PAYLOADSHA512ALT, upld512); headerPutString(pkg->header, RPMTAG_PAYLOADSHA3_256, pld3); headerPutString(pkg->header, RPMTAG_PAYLOADSHA3_256ALT, upld3); headerPutUint64(pkg->header, RPMTAG_PAYLOADSIZE, &payloadSize, 1); headerPutUint64(pkg->header, RPMTAG_PAYLOADSIZEALT, &archiveSize, 1); } pld = _free(pld); pld512 = _free(pld512); pld3 = _free(pld3); /* Write the final header */ if (fdJump(fd, hdrStart)) goto exit; if (writeHdr(fd, pkg->header)) goto exit; /* Calculate the digests */ if (rpmformat < 6) { /* SHA1 and legacy MD5 on header + payload only in v4 */ fdInitDigestID(fd, RPM_HASH_MD5, RPMTAG_SIGMD5, 0); fdInitDigestID(fd, RPM_HASH_SHA1, RPMTAG_SHA1HEADER, 0); } else { fdInitDigestID(fd, RPM_HASH_SHA3_256, RPMTAG_SHA3_256HEADER, 0); } fdInitDigestID(fd, RPM_HASH_SHA256, RPMTAG_SHA256HEADER, 0); if (fdConsume(fd, hdrStart, payloadStart - hdrStart)) goto exit; fdFiniDigest(fd, RPMTAG_SHA1HEADER, (void **)&SHA1, NULL, 1); fdFiniDigest(fd, RPMTAG_SHA256HEADER, (void **)&SHA256, NULL, 1); fdFiniDigest(fd, RPMTAG_SHA3_256HEADER, (void **)&SHA3_256, NULL, 1); if (fdConsume(fd, 0, payloadSize)) goto exit; fdFiniDigest(fd, RPMTAG_SIGMD5, (void **)&MD5, NULL, 0); if (fdJump(fd, sigStart)) goto exit; /* Generate the signature. Now with right values */ if (rpmGenerateSignature(SHA3_256, SHA256, SHA1, MD5, payloadEnd - hdrStart, archiveSize, fd, rpmformat)) { goto exit; } rc = RPMRC_OK; exit: free(rpmio_flags); free(SHA1); free(SHA256); free(SHA3_256); free(upld); free(upld512); free(upld3); /* XXX Fish the pkgid out of the signature header. */ if (pkgidp != NULL) { if (MD5 != NULL) { *pkgidp = MD5; } } else { free(MD5); } Fclose(fd); if (rc == RPMRC_OK) rpmlog(RPMLOG_NOTICE, _("Wrote: %s\n"), fileName); else (void) unlink(fileName); return rc; } static const rpmTagVal copyTags[] = { RPMTAG_CHANGELOGTIME, RPMTAG_CHANGELOGNAME, RPMTAG_CHANGELOGTEXT, 0 }; static rpmRC checkPackages(char *pkgcheck) { int fail = rpmExpandNumeric("%{?_nonzero_exit_pkgcheck_terminate_build}"); int xx; rpmlog(RPMLOG_NOTICE, _("Executing \"%s\":\n"), pkgcheck); xx = system(pkgcheck); if (WEXITSTATUS(xx) == -1 || WEXITSTATUS(xx) == 127) { rpmlog(RPMLOG_ERR, _("Execution of \"%s\" failed.\n"), pkgcheck); if (fail) return RPMRC_NOTFOUND; } if (WEXITSTATUS(xx) != 0) { rpmlog(RPMLOG_ERR, _("Package check \"%s\" failed.\n"), pkgcheck); if (fail) return RPMRC_FAIL; } return RPMRC_OK; } static rpmRC checkPackageSet(Package pkgs) { char *pkglist = NULL; char *pkgcheck_set = NULL; rpmRC rc = RPMRC_OK; for (Package pkg = pkgs; pkg != NULL; pkg = pkg->next) { if (pkg->filename) rstrscat(&pkglist, pkg->filename, " ", NULL); } pkgcheck_set = rpmExpand("%{?_build_pkgcheck_set} ", pkglist, NULL); /* run only if _build_pkgcheck_set is defined */ if (pkgcheck_set[0] != ' ') rc = checkPackages(pkgcheck_set); free(pkgcheck_set); free(pkglist); return rc; } static rpmRC signPackage(const char *fn) { int rc = 0; /* fall merrily through if signer not defined */ auto [ ign, sign_id ] = rpm::macros().expand("%{?_openpgp_autosign_id}"); if (sign_id.empty() == false) { struct rpmSignArgs sa = { .keyid = const_cast(sign_id.c_str()), .hashalgo = {}, .signflags = {}, }; rc = rpmPkgSign(fn, &sa); rpmlog(RPMLOG_DEBUG, "signing %s with %s: %d\n", fn, sign_id.c_str(), rc); } return rc ? RPMRC_FAIL : RPMRC_OK; } /* watchout, argument is modified */ static rpmRC ensureDir(char *binRpm) { rpmRC rc = RPMRC_OK; char *binDir = strchr(binRpm, '/'); char *dn = NULL; if (binDir) { struct stat st; *binDir = '\0'; dn = rpmGetPath("%{_rpmdir}/", binRpm, NULL); if (stat(dn, &st) < 0) { switch (errno) { case ENOENT: if (mkdir(dn, 0755) == 0 || errno == EEXIST) break; default: rpmlog(RPMLOG_ERR,_("cannot create %s: %s\n"), dn, strerror(errno)); rc = RPMRC_FAIL; break; } } } free(dn); return rc; } static rpmRC getPkgFilename(Header h, char **filename) { const char *errorString; char *binFormat = rpmGetPath("%{_rpmfilename}", NULL); char *binRpm = headerFormat(h, binFormat, &errorString); rpmRC rc = RPMRC_FAIL; if (binRpm == NULL) { rpmlog(RPMLOG_ERR, _("Could not generate output " "filename for package %s: %s\n"), headerGetString(h, RPMTAG_NAME), errorString); } else { *filename = rpmGetPath("%{_rpmdir}/", binRpm, NULL); rc = ensureDir(binRpm); } free(binFormat); free(binRpm); return rc; } static rpmRC packageBinary(rpmSpec spec, Package pkg, const char *cookie, int cheating, char** filename) { rpmRC rc = RPMRC_OK; if (pkg->fileList == NULL) return rc; if ((rc = processScriptFiles(spec, pkg))) return rc; if (cookie) { headerPutString(pkg->header, RPMTAG_COOKIE, cookie); } /* Copy changelog from src rpm */ #pragma omp critical headerCopyTags(spec->sourcePackage->header, pkg->header, copyTags); headerPutString(pkg->header, RPMTAG_RPMVERSION, VERSION); headerPutString(pkg->header, RPMTAG_BUILDHOST, spec->buildHost); headerPutUint32(pkg->header, RPMTAG_BUILDTIME, &(spec->buildTime), 1); if (spec->sourcePkgId != NULL) { headerPutBin(pkg->header, RPMTAG_SOURCESIGMD5, spec->sourcePkgId,16); } if (cheating) { (void) rpmlibNeedsFeature(pkg, "ShortCircuited", "4.9.0-1"); } if ((rc = getPkgFilename(pkg->header, filename))) return rc; rc = writeRPM(pkg, NULL, *filename, NULL, spec->buildTime, spec->buildHost, spec->rpmformat); if (rc == RPMRC_OK) { /* Do check each written package if enabled */ char *pkgcheck = rpmExpand("%{?_build_pkgcheck} ", *filename, NULL); if (pkgcheck[0] != ' ') { rc = checkPackages(pkgcheck); } free(pkgcheck); } return rc; } static bool pkgSizeBigger(const Package & pkg1, const Package & pkg2) { uint64_t size1 = headerGetNumber(pkg1->header, RPMTAG_LONGSIZE); uint64_t size2 = headerGetNumber(pkg2->header, RPMTAG_LONGSIZE); return size1 > size2; } /* * Run binary creation in parallel, with task priority based on package size * (largest first) to help achieve an optimal load distribution. */ rpmRC packageBinaries(rpmSpec spec, const char *cookie, int cheating) { rpmRC rc = RPMRC_OK; std::vector tasks; for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) tasks.push_back(pkg); std::sort(tasks.begin(), tasks.end(), &pkgSizeBigger); int i = 0; #pragma omp parallel #pragma omp single for (auto & pkg : tasks) { #pragma omp task untied priority(i++) { pkg->rc = packageBinary(spec, pkg, cookie, cheating, &pkg->filename); rpmlog(RPMLOG_DEBUG, _("Finished binary package job, result %d, filename %s\n"), pkg->rc, pkg->filename); if (pkg->rc) { #pragma omp critical rc = pkg->rc; } } /* omp task */ if (rc) break; } /* Now check the package set if enabled */ if (rc == RPMRC_OK) rc = checkPackageSet(spec->packages); /* Finally, sign the packages. Signing is currently NOT thread-safe... */ if (rc == RPMRC_OK) { for (auto & pkg : tasks) { if (pkg->filename) rc = signPackage(pkg->filename); if (rc) break; } } return rc; } rpmRC packageSources(rpmSpec spec, char **cookie) { Package sourcePkg = spec->sourcePackage; rpmRC rc; /* Add some cruft */ headerPutString(sourcePkg->header, RPMTAG_RPMVERSION, VERSION); headerPutString(sourcePkg->header, RPMTAG_BUILDHOST, spec->buildHost); headerPutUint32(sourcePkg->header, RPMTAG_BUILDTIME, &(spec->buildTime), 1); /* Include spec in parsed and expanded form */ headerPutString(sourcePkg->header, RPMTAG_SPEC, rpmSpecGetSection(spec, RPMBUILD_NONE)); if (rpmSpecGetSection(spec, RPMBUILD_BUILDREQUIRES)) { (void) rpmlibNeedsFeature(sourcePkg, "DynamicBuildRequires", "4.15.0-1"); } /* XXX this should be %_srpmdir */ { sourcePkg->filename = rpmGetPath("%{_srcrpmdir}/", spec->sourceRpmName,NULL); char *pkgcheck = rpmExpand("%{?_build_pkgcheck_srpm} ", sourcePkg->filename, NULL); spec->sourcePkgId = NULL; rc = writeRPM(sourcePkg, &spec->sourcePkgId, sourcePkg->filename, cookie, spec->buildTime, spec->buildHost, spec->rpmformat); /* Do check SRPM package if enabled */ if (rc == RPMRC_OK && pkgcheck[0] != ' ') { rc = checkPackages(pkgcheck); } if (rc == RPMRC_OK) rc = signPackage(sourcePkg->filename); free(pkgcheck); } return rc; } rpm-software-management-rpm-3c1f23f/build/parseChangelog.cc000066400000000000000000000177361511627505500240340ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseChangelog.c * Parse %changelog section from spec file. */ #include "system.h" #include #include #include "rpmbuild_internal.hh" #include "debug.h" #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; } #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; } static void addChangelogEntry(Header h, time_t time, const char *name, const char *text) { rpm_time_t mytime = time; /* XXX convert to header representation */ headerPutUint32(h, RPMTAG_CHANGELOGTIME, &mytime, 1); headerPutString(h, RPMTAG_CHANGELOGNAME, name); headerPutString(h, RPMTAG_CHANGELOGTEXT, text); } static int sameDate(const struct tm *ot, const struct tm *nt) { return (ot->tm_year == nt->tm_year && ot->tm_mon == nt->tm_mon && ot->tm_mday == nt->tm_mday && ot->tm_wday == nt->tm_wday); } /** * Parse date string to seconds. * accepted date formats are "Mon Jun 6 2016" (original one) * and "Thu Oct 6 06:48:39 CEST 2016" (extended one) * @param datestr date string (e.g. 'Wed Jan 1 1997') * @param[out] secs secs since the unix epoch * @return 0 on success, -1 on error */ static int dateToTimet(const char * datestr, time_t * secs, int * date_words) { int rc = -1; /* assume failure */ struct tm time, ntime; const char * const * idx; char *p, *pe, *q, *date, *tz; char tz_name[10]; /* name of timezone (if extended format is used) */ static const char * const days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; static const char * const months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; static const char lengths[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; memset(&time, 0, sizeof(time)); memset(&ntime, 0, sizeof(ntime)); date = xstrdup(datestr); pe = date; /* day of week */ p = pe; SKIPSPACE(p); if (*p == '\0') goto exit; pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0'; for (idx = days; *idx && !rstreq(*idx, p); idx++) {}; if (*idx == NULL) goto exit; time.tm_wday = idx - days; /* month */ p = pe; SKIPSPACE(p); if (*p == '\0') goto exit; pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0'; for (idx = months; *idx && !rstreq(*idx, p); idx++) {}; if (*idx == NULL) goto exit; time.tm_mon = idx - months; /* day */ p = pe; SKIPSPACE(p); if (*p == '\0') goto exit; pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0'; time.tm_mday = strtol(p, &q, 10); if (!(q && *q == '\0')) goto exit; if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) goto exit; /* first part of year entry (original format) / time entry (extended format)*/ p = pe; SKIPSPACE(p); if (*p == '\0') goto exit; /* in the original format here is year record (e.g. 1999), * in the extended one here is time stamp (e.g. 10:22:30). * Choose the format */ if ((p[1]==':') || ((p[1]!='\0') && ((p[2]==':')))) { /* it can be extended format */ *date_words = 6; /* second part of time entry */ /* hours */ time.tm_hour = strtol(p, &q, 10); if ( (time.tm_hour < 0) || (time.tm_hour > 23) ) goto exit; if (*q!=':') goto exit; p = ++q; /* minutes */ time.tm_min = strtol(p, &q, 10); if ( (time.tm_min < 0) || (time.tm_min > 59) ) goto exit; if (*q != ':') goto exit; p = ++q; /* time - seconds */ time.tm_sec = strtol(p, &q, 10); if ( (time.tm_sec < 0) || (time.tm_sec > 59) ) goto exit; p = q; /* time zone name */ SKIPSPACE(p); if (*p == '\0') goto exit; pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0'; if (((int)(pe-p) + 1) > 9 ) goto exit; strncpy(tz_name, p, (int)(pe-p)); tz_name[(int)(pe-p)] = '\0'; /* first part of year entry */ p = pe; SKIPSPACE(p); if (*p == '\0') goto exit; } else { *date_words = 4; /* the original format */ /* make this noon so the day is always right (as we make this UTC) */ time.tm_hour = 12; } /* year - second part */ pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe = '\0'; time.tm_year = strtol(p, &q, 10); if (!(q && *q == '\0')) goto exit; if (time.tm_year < 1990 || time.tm_year >= 3000) goto exit; time.tm_year -= 1900; /* change time zone and compute calendar time representation */ tz = getenv("TZ"); if (tz) tz = xstrdup(tz); if (*date_words == 6) { /* changelog date is in read time zone */ setenv("TZ", tz_name, 1); } else { /* changelog date is always in UTC */ setenv("TZ", "UTC", 1); } ntime = time; /* struct assignment */ *secs = mktime(&ntime); unsetenv("TZ"); if (tz) { setenv("TZ", tz, 1); free(tz); } tzset(); if (*secs == -1) goto exit; /* XXX Turn this into a hard error in a release or two */ if (!sameDate(&time, &ntime)) rpmlog(RPMLOG_WARNING, _("bogus date in %%changelog: %s\n"), datestr); rc = 0; exit: free(date); return rc; } /** * Add %changelog section to header. * @param h header * @param sb changelog strings * @return RPMRC_OK on success */ static rpmRC addChangelog(Header h, ARGV_const_t sb) { rpmRC rc = RPMRC_FAIL; /* assume failure */ char *s, *sp; int i; time_t firstTime = 0; time_t lastTime = 0; time_t trimtime = rpmExpandNumeric("%{?_changelog_trimtime}"); time_t trimage = rpmExpandNumeric("%{?_changelog_trimage}"); char *date, *name, *text, *next; int date_words; /* number of words in date string */ /* Convert _changelog_trimtime to age for backwards compatibility */ if (trimtime && !trimage) { trimage = time(NULL) - trimtime; trimtime = 0; } s = sp = argvJoin(sb, ""); /* skip space */ SKIPSPACE(s); while (*s != '\0') { time_t time; if (*s != '*') { rpmlog(RPMLOG_ERR, _("%%changelog entries must start with *\n")); goto exit; } /* find end of line */ date = s; while (*s && *s != '\n') s++; if (! *s) { rpmlog(RPMLOG_ERR, _("incomplete %%changelog entry\n")); goto exit; } *s = '\0'; text = s + 1; /* 4 fields of date */ date++; s = date; SKIPSPACE(date); if (dateToTimet(date, &time, &date_words)) { rpmlog(RPMLOG_ERR, _("bad date in %%changelog: %s\n"), date); goto exit; } /* Changelog trimming is always relative to first entry */ if (!firstTime) { firstTime = time; if (trimage) trimtime = firstTime - trimage; } if (lastTime && lastTime < time) { rpmlog(RPMLOG_ERR, _("%%changelog not in descending chronological order\n")); goto exit; } for (i = 0; i < date_words; i++) { SKIPSPACE(s); SKIPNONSPACE(s); } lastTime = time; /* skip space to the name */ SKIPSPACE(s); if (! *s) { rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n")); goto exit; } /* name */ name = s; while (*s != '\0') s++; while (s > name && risspace(*s)) { *s-- = '\0'; } if (s == name) { rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n")); goto exit; } /* text */ SKIPSPACE(text); if (! *text) { rpmlog(RPMLOG_ERR, _("no description in %%changelog\n")); goto exit; } /* find the next leading '*' (or eos) */ s = text; do { s++; } while (*s && (*(s-1) != '\n' || *s != '*')); next = s; s--; /* backup to end of description */ while ((s > text) && risspace(*s)) { *s-- = '\0'; } if ( !trimtime || time >= trimtime ) { addChangelogEntry(h, time, name, text); } else break; s = next; } rc = RPMRC_OK; exit: free(sp); return rc; } int parseChangelog(rpmSpec spec) { int res = PART_ERROR; ARGV_t sb = NULL; if (headerIsEntry(spec->packages->header, RPMTAG_CHANGELOGTIME)) { rpmlog(RPMLOG_ERR, _("line %d: second %%changelog\n"), spec->lineNum); goto exit; } if ((res = parseLines(spec, STRIP_COMMENTS, &sb, NULL)) == PART_ERROR) goto exit; if (sb && addChangelog(spec->packages->header, sb)) { goto exit; } exit: argvFree(sb); return res; } rpm-software-management-rpm-3c1f23f/build/parseDescription.cc000066400000000000000000000040431511627505500244130ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseDescription.c * Parse %description section from spec file. */ #include "system.h" #include #include #include "rpmbuild_internal.hh" #include "debug.h" int parseDescription(rpmSpec spec) { int nextPart = PART_ERROR; /* assume error */ StringBuf sb = NULL; int flag = PART_SUBNAME; Package pkg; int rc, argc; int arg; const char **argv = NULL; char *name = NULL; char *lang = NULL; const char *descr = ""; poptContext optCon = NULL; struct poptOption optionsTable[] = { { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL}, { NULL, 'l', POPT_ARG_STRING, &lang, 'l', NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; if ((rc = poptParseArgvString(spec->line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("line %d: Error parsing %%description: %s\n"), spec->lineNum, poptStrerror(rc)); return PART_ERROR; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { if (arg == 'n') { flag = PART_NAME; } } if (arg < -1) { rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), spec->line); goto exit; } if (poptPeekArg(optCon)) { if (name == NULL) name = xstrdup(poptGetArg(optCon)); if (poptPeekArg(optCon)) { rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"), spec->lineNum, spec->line); goto exit; } } if (lookupPackage(spec, name, flag, &pkg)) goto exit; if ((nextPart = parseLines(spec, (STRIP_TRAILINGSPACE |STRIP_COMMENTS), NULL, &sb)) == PART_ERROR) { goto exit; } if (sb) { stripTrailingBlanksStringBuf(sb); descr = getStringBuf(sb); } if (addLangTag(spec, pkg->header, RPMTAG_DESCRIPTION, descr, lang ? lang : RPMBUILD_DEFAULT_LANG)) { nextPart = PART_ERROR; } exit: freeStringBuf(sb); free(lang); free(name); free(argv); poptFreeContext(optCon); return nextPart; } rpm-software-management-rpm-3c1f23f/build/parseFiles.cc000066400000000000000000000046521511627505500232000ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseFiles.c * Parse %files section from spec file. */ #include "system.h" #include #include #include "rpmbuild_internal.hh" #include "debug.h" int parseFiles(rpmSpec spec) { int res = PART_ERROR; Package pkg; int rc, argc; int arg; const char ** argv = NULL; char *name = NULL; int flag = PART_SUBNAME; poptContext optCon = NULL; struct poptOption optionsTable[] = { { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL}, { NULL, 'f', POPT_ARG_STRING, NULL, 'f', NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; /* XXX unmask %license while parsing %files */ rpmPushMacroFlags(spec->macros, "license", NULL, "%license", RMIL_SPEC, RPMMACRO_LITERAL); if ((rc = poptParseArgvString(spec->line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("line %d: Error parsing %%files: %s\n"), spec->lineNum, poptStrerror(rc)); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { if (arg == 'n') { flag = PART_NAME; } } if (arg < -1) { rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), spec->line); goto exit; } if (poptPeekArg(optCon)) { if (name == NULL) name = xstrdup(poptGetArg(optCon)); if (poptPeekArg(optCon)) { rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"), spec->lineNum, spec->line); goto exit; } } if (lookupPackage(spec, name, flag, &pkg)) goto exit; /* * This should be an error, but its surprisingly commonly abused for the * effect of multiple -f arguments in versions that dont support it. * Warn but preserve behavior, except for leaking memory. */ if (pkg->fileList != NULL) { rpmlog(RPMLOG_WARNING, _("line %d: multiple %%files for package '%s'\n"), spec->lineNum, rpmstrPoolStr(pkg->pool, pkg->name)); pkg->fileList = argvFree(pkg->fileList); } for (arg=1; argfileFile), file); free(file); } } pkg->fileList = argvNew(); res = parseLines(spec, STRIP_COMMENTS, &(pkg->fileList), NULL); exit: rpmPopMacro(NULL, "license"); free(argv); free(name); poptFreeContext(optCon); return res; } rpm-software-management-rpm-3c1f23f/build/parseList.cc000066400000000000000000000012541511627505500230440ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseBuildInstallClean.c * Parse %build/%install/%clean section from spec file. */ #include "system.h" #include #include "rpmbuild_internal.hh" #include "debug.h" int parseList(rpmSpec spec, const char *name, rpmTagVal tag) { int res = PART_ERROR; ARGV_t lst = NULL; /* There are no options to %patchlist and %sourcelist */ if ((res = parseLines(spec, (STRIP_TRAILINGSPACE | STRIP_COMMENTS), &lst, NULL)) == PART_ERROR) { goto exit; } for (ARGV_const_t l = lst; l && *l; l++) { if (rstreq(*l, "")) continue; addSource(spec, 0, *l, tag); } exit: argvFree(lst); return res; } rpm-software-management-rpm-3c1f23f/build/parsePolicies.cc000066400000000000000000000031761511627505500237050ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parsePolicies.c * Parse %policies section from spec file. */ #include "system.h" #include #include #include #include #include "rpmbuild_internal.hh" #include "debug.h" int parsePolicies(rpmSpec spec) { int res = PART_ERROR; Package pkg; int rc, argc; int arg; const char **argv = NULL; char *name = NULL; int flag = PART_SUBNAME; poptContext optCon = NULL; struct poptOption optionsTable[] = { {NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL}, {0, 0, 0, 0, 0, NULL, NULL} }; if ((rc = poptParseArgvString(spec->line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("line %d: Error parsing %%policies: %s\n"), spec->lineNum, poptStrerror(rc)); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { if (arg == 'n') { flag = PART_NAME; } } if (arg < -1) { rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), spec->line); goto exit; } if (poptPeekArg(optCon)) { if (name == NULL) name = xstrdup(poptGetArg(optCon)); if (poptPeekArg(optCon)) { rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"), spec->lineNum, spec->line); goto exit; } } if (lookupPackage(spec, name, flag, &pkg)) goto exit; res = parseLines(spec, (STRIP_TRAILINGSPACE | STRIP_COMMENTS), &(pkg->policyList), NULL); exit: free(argv); free(name); poptFreeContext(optCon); return res; } rpm-software-management-rpm-3c1f23f/build/parsePreamble.cc000066400000000000000000001041641511627505500236640ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parsePreamble.c * Parse tags in global section from spec file. */ #include "system.h" #include #include #include #include #include #include #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "debug.h" #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; } #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; } #define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;} #define SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;} /** */ static const rpmTagVal copyTagsDuringParse[] = { RPMTAG_EPOCH, RPMTAG_VERSION, RPMTAG_RELEASE, RPMTAG_LICENSE, RPMTAG_PACKAGER, RPMTAG_DISTRIBUTION, RPMTAG_DISTURL, RPMTAG_VENDOR, RPMTAG_ICON, RPMTAG_URL, RPMTAG_VCS, RPMTAG_CHANGELOGTIME, RPMTAG_CHANGELOGNAME, RPMTAG_CHANGELOGTEXT, RPMTAG_PREFIXES, RPMTAG_DISTTAG, RPMTAG_BUGURL, RPMTAG_TRANSLATIONURL, RPMTAG_UPSTREAMRELEASES, RPMTAG_GROUP, RPMTAG_MODULARITYLABEL, RPMTAG_SOURCENEVR, 0 }; /** */ static const rpmTagVal requiredTagsForBuild[] = { RPMTAG_NAME, RPMTAG_VERSION, RPMTAG_RELEASE, 0 }; /** */ static const rpmTagVal requiredTags[] = { RPMTAG_NAME, RPMTAG_VERSION, RPMTAG_RELEASE, RPMTAG_SUMMARY, RPMTAG_LICENSE, 0 }; /** */ static rpmRC addOrAppendListEntry(Header h, rpmTagVal tag, const char * line) { int xx; int argc; const char **argv; if ((xx = poptParseArgvString(line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("Error parsing tag field: %s\n"), poptStrerror(xx)); return RPMRC_FAIL; } if (argc) headerPutStringArray(h, tag, argv, argc); argv = _free(argv); return RPMRC_OK; } /* Parse a simple part line that only take -n or */ /* is returned in name as a pointer into a dynamic buffer */ /** */ static int parseSimplePart(const char *line, char **name, int *flag) { char *tok; char *linebuf = xstrdup(line); int rc; /* Throw away the first token (the %xxxx) */ (void)strtok(linebuf, " \t\n"); *name = NULL; if (!(tok = strtok(NULL, " \t\n"))) { rc = 1; goto exit; } if (rstreq(tok, "-n")) { if (!(tok = strtok(NULL, " \t\n"))) { rc = 1; goto exit; } *flag = PART_NAME; } else { *flag = PART_SUBNAME; } *name = xstrdup(tok); rc = strtok(NULL, " \t\n") ? 1 : 0; exit: free(linebuf); return rc; } /** */ static inline int parseYesNo(const char * s) { return ((!s || (s[0] == 'n' || s[0] == 'N' || s[0] == '0') || !rstrcasecmp(s, "false") || !rstrcasecmp(s, "off")) ? 0 : 1); } static struct Source *newSource(uint32_t num, const char *path, int flags) { struct Source *p = new Source {}; p->next = NULL; p->num = num; p->fullSource = xstrdup(path); p->flags = flags; p->source = strrchr(p->fullSource, '/'); if (p->source) { const char *buf = strrchr(p->source,'='); if (buf) p->source = buf; p->source++; } else { p->source = p->fullSource; } p->path = rpmGetPath("%{_sourcedir}/", p->source, NULL); return p; } struct Source *findSource(rpmSpec spec, uint32_t num, int flag) { struct Source *p; for (p = spec->sources; p != NULL; p = p->next) if ((num == p->num) && (p->flags & flag)) return p; return NULL; } static int parseNoSource(rpmSpec spec, const char * field, rpmTagVal tag) { const char *f, *fe; const char *name; int flag; uint32_t num; if (tag == RPMTAG_NOSOURCE) { flag = RPMBUILD_ISSOURCE; name = "source"; } else { flag = RPMBUILD_ISPATCH; name = "patch"; } fe = field; for (f = fe; *f != '\0'; f = fe) { struct Source *p; SKIPWHITE(f); if (*f == '\0') break; fe = f; SKIPNONWHITE(fe); if (*fe != '\0') fe++; if (parseUnsignedNum(f, &num)) { rpmlog(RPMLOG_ERR, _("line %d: Bad number: %s\n"), spec->lineNum, f); return RPMRC_FAIL; } if (! (p = findSource(spec, num, flag))) { rpmlog(RPMLOG_ERR, _("line %d: Bad no%s number: %u\n"), spec->lineNum, name, num); return RPMRC_FAIL; } p->flags |= RPMBUILD_ISNO; } return 0; } static int tryDownload(const struct Source *p) { int rc = 0; struct stat st; /* try to download source/patch if it's missing */ if (lstat(p->path, &st) != 0 && errno == ENOENT) { char *url = NULL; if (urlIsURL(p->fullSource) != URL_IS_UNKNOWN) { url = rstrdup(p->fullSource); } else { url = rpmExpand("%{_default_source_url}", NULL); rstrcat(&url, p->source); if (*url == '%') url = _free(url); } if (url) { rpmlog(RPMLOG_WARNING, _("Downloading %s to %s\n"), url, p->path); if (urlGetFile(url, p->path) != 0) { rpmlog(RPMLOG_ERR, _("Couldn't download %s\n"), p->fullSource); rc = -1; } } free(url); } return rc; } /* * Parse an option number of a tag, such as in sources and patches. * Return -1 on error, 0 if number present and 1 if no number found. */ static int parseTagNumber(const char *line, uint32_t *snum) { int rc = 0; char *l = xstrdup(line); char *fieldp = l; char *nump = l; /* We already know that a ':' exists, and that there */ /* are no spaces before it. */ /* This also now allows for spaces and tabs between */ /* the number and the ':' */ while ((*fieldp != ':') && (*fieldp != ' ') && (*fieldp != '\t')) { fieldp++; } *fieldp = '\0'; SKIPSPACE(nump); if (nump == NULL || *nump == '\0') { rc = 1; } else { rc = parseUnsignedNum(l, snum); } free(l); return rc; } int addSource(rpmSpec spec, int specline, const char *srcname, rpmTagVal tag) { struct Source *p; int flag = 0; int nonum = 1; /* assume autonumbering */ const char *name = NULL; char *buf = NULL; uint32_t num = 0; int *autonum = NULL; int nofetch = (spec->flags & RPMSPEC_FORCE) || rpmExpandNumeric("%{_disable_source_fetch}"); switch (tag) { case RPMTAG_SOURCE: flag = RPMBUILD_ISSOURCE; name = "source"; autonum = &spec->autonum_source; break; case RPMTAG_PATCH: flag = RPMBUILD_ISPATCH; name = "patch"; autonum = &spec->autonum_patch; break; default: return -1; break; } if (specline) { char * s = spec->line; SKIPSPACE(s); nonum = parseTagNumber(s + strlen(name), &num); if (nonum < 0) { rpmlog(RPMLOG_ERR, _("line %d: Bad %s number: %s\n"), spec->lineNum, name, spec->line); return RPMRC_FAIL; } } if (nonum > 0) { /* No number specified, use autonumbering */ (*autonum)++; num = *autonum; } else { /* Autonumbering continues from last specified number */ if ((int)num > *autonum) *autonum = num; } /* Check whether tags of the same number haven't already been defined */ if (findSource(spec, num, flag)) { rpmlog(RPMLOG_ERR, _("%s %d defined multiple times\n"), name, num); return RPMRC_FAIL; } /* Create the entry and link it at the end */ p = newSource(num, srcname, flag); if (!spec->sources) { spec->sources = p; } else { Source * s = spec->sources; while (s->next) s = s->next; s->next = p; } spec->numSources++; rasprintf(&buf, "%s%d", (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num); rpmPushMacro(spec->macros, buf, NULL, p->path, RMIL_SPEC); free(buf); rasprintf(&buf, "%sURL%d", (flag & RPMBUILD_ISPATCH) ? "PATCH" : "SOURCE", num); rpmPushMacro(spec->macros, buf, NULL, p->fullSource, RMIL_SPEC); free(buf); addLuaSource(p); if (!nofetch && tryDownload(p)) return RPMRC_FAIL; return 0; } typedef const struct tokenBits_s { const char * name; rpmsenseFlags bits; } * tokenBits; /** */ static struct tokenBits_s const installScriptBits[] = { { "interp", RPMSENSE_INTERP }, { "meta", RPMSENSE_META }, { "preun", RPMSENSE_SCRIPT_PREUN }, { "pre", RPMSENSE_SCRIPT_PRE }, { "postun", RPMSENSE_SCRIPT_POSTUN }, { "post", RPMSENSE_SCRIPT_POST }, { "rpmlib", RPMSENSE_RPMLIB }, { "verify", RPMSENSE_SCRIPT_VERIFY }, { "pretrans", RPMSENSE_PRETRANS }, { "posttrans", RPMSENSE_POSTTRANS }, { "preuntrans", RPMSENSE_PREUNTRANS }, { "postuntrans", RPMSENSE_POSTUNTRANS }, { NULL, 0 } }; /** */ static int parseBits(const char * s, const tokenBits tokbits, rpmsenseFlags * bp) { tokenBits tb = NULL; const char * se; rpmsenseFlags bits = RPMSENSE_ANY; int c = 0; int rc = RPMRC_OK; if (s) { for (;;) { while ((c = *s) && risspace(c)) s++; se = s; while ((c = *se) && risalpha(c)) se++; if (s == se) { if (c || tb) rc = RPMRC_FAIL; break; } for (tb = tokbits; tb->name; tb++) { if (tb->name != NULL && strlen(tb->name) == (se-s) && rstreqn(tb->name, s, (se-s))) break; } if (tb->name == NULL) { rc = RPMRC_FAIL; break; } bits |= tb->bits; while ((c = *se) && risspace(c)) se++; if (c != ',') { if (c) rc = RPMRC_FAIL; break; } s = ++se; } } *bp |= bits; return rc; } /** */ static inline char * findLastChar(char * s) { char *res = s; while (*s != '\0') { if (! risspace(*s)) res = s; s++; } return res; } /** */ int isMemberInEntry(Header h, const char *name, rpmTagVal tag) { struct rpmtd_s td; int found = 0; const char *str; if (!headerGet(h, tag, &td, HEADERGET_MINMEM)) return -1; while ((str = rpmtdNextString(&td))) { if (!rstrcasecmp(str, name)) { found = 1; break; } } rpmtdFreeData(&td); return found; } /** */ rpmRC checkForValidArchitectures(rpmSpec spec) { char *arch = rpmExpand("%{_target_cpu}", NULL); char *os = rpmExpand("%{_target_os}", NULL); rpmRC rc = RPMRC_FAIL; /* assume failure */ if (!strcmp(arch, "noarch")) { free(arch); arch = rpmExpand("%{_build_cpu}", NULL); } if (isMemberInEntry(spec->buildRestrictions, arch, RPMTAG_EXCLUDEARCH) == 1) { rpmlog(RPMLOG_ERR, _("Architecture is excluded: %s\n"), arch); goto exit; } if (isMemberInEntry(spec->buildRestrictions, arch, RPMTAG_EXCLUSIVEARCH) == 0) { rpmlog(RPMLOG_ERR, _("Architecture is not included: %s\n"), arch); goto exit; } if (isMemberInEntry(spec->buildRestrictions, os, RPMTAG_EXCLUDEOS) == 1) { rpmlog(RPMLOG_ERR, _("OS is excluded: %s\n"), os); goto exit; } if (isMemberInEntry(spec->buildRestrictions, os, RPMTAG_EXCLUSIVEOS) == 0) { rpmlog(RPMLOG_ERR, _("OS is not included: %s\n"), os); goto exit; } rc = RPMRC_OK; exit: free(arch); free(os); return rc; } /** * Check that required tags are present in header. * @param h header * @param NVR package name-version-release * @return RPMRC_OK if OK */ int checkForRequired(Header h) { int res = RPMRC_OK; const rpmTagVal * p; for (p = requiredTags; *p != 0; p++) { if (!headerIsEntry(h, *p)) { rpmlog(RPMLOG_ERR, _("%s field must be present in package: %s\n"), rpmTagGetName(*p), headerGetString(h, RPMTAG_NAME)); res = RPMRC_FAIL; } } return res; } /** * Check that required tags are present in header. * @param h header * @param NVR package name-version-release * @return RPMRC_OK if OK */ static int checkForRequiredForBuild(Header h) { int res = RPMRC_OK; const rpmTagVal * p; for (p = requiredTagsForBuild; *p != 0; p++) { if (!headerIsEntry(h, *p)) { rpmlog(RPMLOG_ERR, _("%s field must be present before build in package: %s\n"), rpmTagGetName(*p), headerGetString(h, RPMTAG_NAME)); res = RPMRC_FAIL; } } return res; } /** * Check that no duplicate tags are present in header. * @param h header * @param NVR package name-version-release * @return RPMRC_OK if OK */ int checkForDuplicates(Header h) { int res = RPMRC_OK; rpmTagVal tag, lastTag = RPMTAG_NOT_FOUND; HeaderIterator hi = headerInitIterator(h); while ((tag = headerNextTag(hi)) != RPMTAG_NOT_FOUND) { if (tag == lastTag) { rpmlog(RPMLOG_ERR, _("Duplicate %s entries in package: %s\n"), rpmTagGetName(tag), headerGetString(h, RPMTAG_NAME)); res = RPMRC_FAIL; } lastTag = tag; } headerFreeIterator(hi); return res; } /** */ static struct optionalTag { rpmTagVal ot_tag; const char * ot_mac; } const optionalTags[] = { { RPMTAG_VENDOR, "%{vendor}" }, { RPMTAG_PACKAGER, "%{packager}" }, { RPMTAG_DISTRIBUTION, "%{distribution}" }, { RPMTAG_DISTURL, "%{disturl}" }, { RPMTAG_DISTTAG, "%{disttag}" }, { RPMTAG_BUGURL, "%{bugurl}" }, { RPMTAG_TRANSLATIONURL, "%{translationurl}" }, { RPMTAG_UPSTREAMRELEASES, "%{upstreamreleases}" }, { RPMTAG_MODULARITYLABEL, "%{modularitylabel}"}, { 0, NULL } }; /** */ void fillOutMainPackage(Header h) { const struct optionalTag *ot; for (ot = optionalTags; ot->ot_mac != NULL; ot++) { if (!headerIsEntry(h, ot->ot_tag)) { char *val = rpmExpand(ot->ot_mac, NULL); if (val && *val != '%') { headerPutString(h, ot->ot_tag, val); } free(val); } } } /** */ void copyInheritedTags(Header h, Header fromh) { headerCopyTags(fromh, h, (rpmTagVal *)copyTagsDuringParse); } /** */ static rpmRC addIcon(Package pkg, const char * file) { struct Source *p = newSource(0, file, RPMBUILD_ISICON); char *fn = NULL; uint8_t *icon = NULL; FD_t fd = NULL; rpmRC rc = RPMRC_FAIL; /* assume failure */ off_t size; size_t nb, iconsize; p->next = pkg->icon; pkg->icon = p; /* XXX use rpmGenPath(rootdir, "%{_sourcedir}/", file) for icon path. */ fn = rpmGetPath("%{_sourcedir}/", file, NULL); fd = Fopen(fn, "r.ufdio"); if (fd == NULL) { rpmlog(RPMLOG_ERR, _("Unable to open icon %s: %s\n"), fn, Fstrerror(fd)); goto exit; } size = fdSize(fd); iconsize = (size >= 0 ? size : (8 * BUFSIZ)); if (iconsize == 0) { rc = RPMRC_OK; /* XXX Eh? */ goto exit; } icon = (uint8_t *)xmalloc(iconsize + 1); *icon = '\0'; nb = Fread(icon, sizeof(icon[0]), iconsize, fd); if (Ferror(fd) || (size >= 0 && nb != size)) { rpmlog(RPMLOG_ERR, _("Unable to read icon %s: %s\n"), fn, Fstrerror(fd)); goto exit; } if (rstreqn((char*)icon, "GIF", sizeof("GIF")-1)) { headerPutBin(pkg->header, RPMTAG_GIF, icon, iconsize); } else if (rstreqn((char*)icon, "/* XPM", sizeof("/* XPM")-1)) { headerPutBin(pkg->header, RPMTAG_XPM, icon, iconsize); } else { rpmlog(RPMLOG_ERR, _("Unknown icon type: %s\n"), file); goto exit; } rc = RPMRC_OK; exit: Fclose(fd); free(fn); free(icon); return rc; } #define SINGLE_TOKEN_ONLY \ if (multiToken) { \ rpmlog(RPMLOG_ERR, _("line %d: Tag takes single token only: %s\n"), \ spec->lineNum, spec->line); \ return RPMRC_FAIL; \ } static void specLog(rpmSpec spec, int lvl, const char *line, const char *msg) { if (spec) { rpmlog(lvl, _("line %d: %s in: %s\n"), spec->lineNum, msg, spec->line); } else { rpmlog(lvl, _("%s in: %s\n"), msg, line); } } /** * Check for inappropriate characters. All alphanums are considered sane. * @param spec spec (or NULL) * @param field string to check * @param allowedchars string of permitted characters * @return RPMRC_OK if OK */ rpmRC rpmCharCheck(rpmSpec spec, const char *field, const char *allowedchars, const char *allowedfirstchars) { const char *ch; char *err = NULL; rpmRC rc = RPMRC_OK; if (allowedfirstchars && !(risalnum(*field) || strchr(allowedfirstchars, *field))) { rasprintf(&err, _("Illegal char '%c' (0x%x)"), isprint(*field) ? *field : '?', *field); } for (ch=field; *ch; ch++) { if (ch==field && allowedfirstchars) continue; if (risalnum(*ch) || strchr(allowedchars, *ch)) continue; rasprintf(&err, _("Illegal char '%c' (0x%x)"), isprint(*ch) ? *ch : '?', *ch); } for (ch=field; *ch; ch++) { if (strchr("%{}", *ch)) { specLog(spec, RPMLOG_WARNING, field, _("Possible unexpanded macro")); break; } } if (err == NULL && strstr(field, "..") != NULL) { rasprintf(&err, _("Illegal sequence \"..\"")); } if (err) { specLog(spec, RPMLOG_ERR, field, err); free(err); rc = RPMRC_FAIL; } return rc; } static int haveLangTag(Header h, rpmTagVal tag, const char *lang) { int rc = 0; /* assume tag not present */ int langNum = -1; if (lang && *lang) { /* See if the language is in header i18n table */ struct rpmtd_s langtd; const char *s = NULL; headerGet(h, RPMTAG_HEADERI18NTABLE, &langtd, HEADERGET_MINMEM); while ((s = rpmtdNextString(&langtd)) != NULL) { if (rstreq(s, lang)) { langNum = rpmtdGetIndex(&langtd); break; } } rpmtdFreeData(&langtd); } else { /* C locale */ langNum = 0; } /* If locale is present, check the actual tag content */ if (langNum >= 0) { struct rpmtd_s td; headerGet(h, tag, &td, HEADERGET_MINMEM|HEADERGET_RAW); if (rpmtdSetIndex(&td, langNum) == langNum) { const char *s = rpmtdGetString(&td); /* non-empty string means a dupe */ if (s && *s) rc = 1; } rpmtdFreeData(&td); }; return rc; } int addLangTag(rpmSpec spec, Header h, rpmTagVal tag, const char *field, const char *lang) { int skip = 0; if (haveLangTag(h, tag, lang)) { /* Turn this into an error eventually */ rpmlog(RPMLOG_WARNING, _("line %d: second %s\n"), spec->lineNum, rpmTagGetName(tag)); } if (!*lang) { headerPutString(h, tag, field); } else { skip = ((spec->flags & RPMSPEC_NOLANG) && !rstreq(lang, RPMBUILD_DEFAULT_LANG)); if (skip) return 0; headerAddI18NString(h, tag, field, lang); } return 0; } static int addBuildOption(rpmSpec spec, const char *sect, const char *opt) { rpmRC rc = RPMRC_FAIL; if (*sect == '\0') sect = "conf"; const struct sectname_s *sc = getSection(sect, 0); if (sc) { argvAdd(&(spec->buildopts[sc->section]), opt); rc = RPMRC_OK; } return rc; } static rpmRC handlePreambleTag(rpmSpec spec, enum parseStages stage, Package pkg, rpmTagVal tag, const char *macro, const char *lang) { char * field = spec->line; char * end; int multiToken = 0; rpmsenseFlags tagflags = RPMSENSE_ANY; rpmRC rc = RPMRC_FAIL; if (field == NULL) /* XXX can't happen */ goto exit; /* Find the start of the "field" and strip trailing space */ while ((*field) && (*field != ':')) field++; if (*field != ':') { rpmlog(RPMLOG_ERR, _("line %d: Malformed tag: %s\n"), spec->lineNum, spec->line); goto exit; } field++; SKIPSPACE(field); if (!*field) { /* Empty field */ rpmlog(RPMLOG_ERR, _("line %d: Empty tag: %s\n"), spec->lineNum, spec->line); goto exit; } end = findLastChar(field); *(end+1) = '\0'; /* See if this is multi-token */ end = field; SKIPNONSPACE(end); if (*end != '\0') multiToken = 1; switch (tag) { case RPMTAG_BUILDSYSTEM: SINGLE_TOKEN_ONLY; if (checkBuildsystem(spec, field)) goto exit; break; case RPMTAG_BUILDOPTION: if (addBuildOption(spec, lang, field)) goto exit; break; case RPMTAG_NAME: SINGLE_TOKEN_ONLY; if (rpmCharCheck(spec, field, ALLOWED_CHARS_NAME, ALLOWED_FIRSTCHARS_NAME)) goto exit; headerPutString(pkg->header, tag, field); /* Main pkg name is unknown at the start, populate as soon as we can */ if (pkg == spec->packages) pkg->name = rpmstrPoolId(spec->pool, field, 1); break; case RPMTAG_VERSION: case RPMTAG_RELEASE: SINGLE_TOKEN_ONLY; if (rpmCharCheck(spec, field, ALLOWED_CHARS_VERREL, NULL)) goto exit; headerPutString(pkg->header, tag, field); break; case RPMTAG_URL: case RPMTAG_DISTTAG: case RPMTAG_BUGURL: case RPMTAG_MODULARITYLABEL: /* XXX TODO: validate format somehow */ case RPMTAG_VCS: SINGLE_TOKEN_ONLY; /* fallthrough */ case RPMTAG_TRANSLATIONURL: case RPMTAG_UPSTREAMRELEASES: headerPutString(pkg->header, tag, field); break; case RPMTAG_GROUP: case RPMTAG_SUMMARY: case RPMTAG_DISTRIBUTION: case RPMTAG_VENDOR: case RPMTAG_LICENSE: case RPMTAG_PACKAGER: if (addLangTag(spec, pkg->header, tag, field, lang)) goto exit; break; case RPMTAG_SOURCELICENSE: if (addLangTag(spec, spec->sourcePackage->header, RPMTAG_LICENSE, field, lang)) goto exit; break; case RPMTAG_BUILDROOT: /* just silently ignore BuildRoot */ break; case RPMTAG_PREFIXES: { struct rpmtd_s td; const char *str; if (addOrAppendListEntry(pkg->header, tag, field)) goto exit; headerGet(pkg->header, tag, &td, HEADERGET_MINMEM); while ((str = rpmtdNextString(&td))) { size_t len = strlen(str); if (len > 1 && str[len-1] == '/') { rpmlog(RPMLOG_ERR, _("line %d: Prefixes must not end with \"/\": %s\n"), spec->lineNum, spec->line); rpmtdFreeData(&td); goto exit; } } rpmtdFreeData(&td); break; } case RPMTAG_DOCDIR: SINGLE_TOKEN_ONLY; if (field[0] != '/') { rpmlog(RPMLOG_ERR, _("line %d: Docdir must begin with '/': %s\n"), spec->lineNum, spec->line); goto exit; } rpmPopMacro(NULL, "_docdir"); rpmPushMacro(NULL, "_docdir", NULL, field, RMIL_SPEC); break; case RPMTAG_EPOCH: { SINGLE_TOKEN_ONLY; uint32_t epoch; if (parseUnsignedNum(field, &epoch)) { rpmlog(RPMLOG_ERR, _("line %d: Epoch field must be an unsigned number: %s\n"), spec->lineNum, spec->line); goto exit; } headerPutUint32(pkg->header, tag, &epoch, 1); break; } case RPMTAG_AUTOREQPROV: pkg->autoReq = parseYesNo(field); pkg->autoProv = pkg->autoReq; break; case RPMTAG_AUTOREQ: pkg->autoReq = parseYesNo(field); break; case RPMTAG_AUTOPROV: pkg->autoProv = parseYesNo(field); break; case RPMTAG_SOURCE: case RPMTAG_PATCH: if (addSource(spec, 1, field, tag)) goto exit; break; case RPMTAG_ICON: SINGLE_TOKEN_ONLY; if (addIcon(pkg, field)) goto exit; spec->numSources++; break; case RPMTAG_NOSOURCE: case RPMTAG_NOPATCH: spec->noSource = 1; if (parseNoSource(spec, field, tag)) goto exit; break; case RPMTAG_ORDERNAME: case RPMTAG_REQUIRENAME: case RPMTAG_RECOMMENDNAME: case RPMTAG_SUGGESTNAME: case RPMTAG_SUPPLEMENTNAME: case RPMTAG_ENHANCENAME: if (parseBits(lang, installScriptBits, &tagflags)) { rpmlog(RPMLOG_ERR, _("line %d: Bad %s: qualifiers: %s\n"), spec->lineNum, rpmTagGetName(tag), spec->line); goto exit; } /* fallthrough */ case RPMTAG_PREREQ: case RPMTAG_CONFLICTNAME: case RPMTAG_OBSOLETENAME: case RPMTAG_PROVIDENAME: if (parseRCPOT(spec, pkg, field, tag, 0, tagflags, addReqProvPkg, NULL)) goto exit; break; case RPMTAG_BUILDPREREQ: case RPMTAG_BUILDREQUIRES: case RPMTAG_BUILDCONFLICTS: if (parseRCPOT(spec, spec->sourcePackage, field, tag, 0, tagflags, addReqProvPkg, NULL)) goto exit; break; case RPMTAG_EXCLUDEARCH: case RPMTAG_EXCLUSIVEARCH: case RPMTAG_EXCLUDEOS: case RPMTAG_EXCLUSIVEOS: if (addOrAppendListEntry(spec->buildRestrictions, tag, field)) goto exit; break; case RPMTAG_BUILDARCHS: { int BACount; const char **BANames = NULL; if (poptParseArgvString(field, &BACount, &BANames)) { rpmlog(RPMLOG_ERR, _("line %d: Bad BuildArchitecture format: %s\n"), spec->lineNum, spec->line); goto exit; } if (spec->packages == pkg) { if (spec->BANames) { rpmlog(RPMLOG_ERR, _("line %d: Duplicate BuildArch entry: %s\n"), spec->lineNum, spec->line); BANames = _free(BANames); goto exit; } if (stage == PARSE_GENERATED && (BACount != 1 || !rstreq(BANames[0], "noarch"))) { rpmlog(RPMLOG_ERR, _("line %d: Only noarch is allowed after the build: %s\n"), spec->lineNum, spec->line); BANames = _free(BANames); goto exit; } spec->BACount = BACount; spec->BANames = BANames; } else { if (BACount != 1 || !rstreq(BANames[0], "noarch")) { rpmlog(RPMLOG_ERR, _("line %d: Only noarch subpackages are supported: %s\n"), spec->lineNum, spec->line); BANames = _free(BANames); goto exit; } BANames = _free(BANames); headerPutString(pkg->header, RPMTAG_ARCH, "noarch"); } if (!BACount) spec->BANames = _free(spec->BANames); break; } case RPMTAG_REMOVEPATHPOSTFIXES: argvSplit(&pkg->removePostfixes, field, ":"); break; default: rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag); goto exit; } if (macro) { rpmPushMacro(spec->macros, macro, NULL, field, RMIL_SPEC); /* Add a separate uppercase macro for tags from the main package */ if (pkg == spec->packages) { char *m = xstrdup(macro); for (char *p = m; *p; ++p) *p = rtoupper(*p); rpmPushMacro(spec->macros, m, NULL, field, RMIL_SPEC); free(m); } } rc = RPMRC_OK; exit: return rc; } /* This table has to be in a peculiar order. If one tag is the */ /* same as another, plus a few letters, it must come first. */ /** */ typedef const struct PreambleRec_s { rpmTagVal tag; int type; int deprecated; int ismacro; int prebuildonly; size_t len; const char * token; } * PreambleRec; static struct PreambleRec_s const preambleList[] = { {RPMTAG_NAME, 0, 0, 1, 0, LEN_AND_STR("name")}, {RPMTAG_VERSION, 0, 0, 1, 0, LEN_AND_STR("version")}, {RPMTAG_RELEASE, 0, 0, 1, 0, LEN_AND_STR("release")}, {RPMTAG_EPOCH, 0, 0, 1, 0, LEN_AND_STR("epoch")}, {RPMTAG_SUMMARY, 1, 0, 1, 0, LEN_AND_STR("summary")}, {RPMTAG_LICENSE, 0, 0, 1, 0, LEN_AND_STR("license")}, {RPMTAG_SOURCELICENSE, 0, 0, 1, 0, LEN_AND_STR("sourcelicense")}, {RPMTAG_DISTRIBUTION, 0, 0, 1, 0, LEN_AND_STR("distribution")}, {RPMTAG_DISTURL, 0, 0, 1, 0, LEN_AND_STR("disturl")}, {RPMTAG_VENDOR, 0, 0, 1, 0, LEN_AND_STR("vendor")}, {RPMTAG_GROUP, 1, 0, 1, 0, LEN_AND_STR("group")}, {RPMTAG_PACKAGER, 0, 0, 1, 0, LEN_AND_STR("packager")}, {RPMTAG_URL, 0, 0, 1, 0, LEN_AND_STR("url")}, {RPMTAG_VCS, 0, 0, 1, 0, LEN_AND_STR("vcs")}, {RPMTAG_SOURCE, 0, 0, 0, 1, LEN_AND_STR("source")}, {RPMTAG_PATCH, 0, 0, 0, 1, LEN_AND_STR("patch")}, {RPMTAG_NOSOURCE, 0, 0, 0, 1, LEN_AND_STR("nosource")}, {RPMTAG_NOPATCH, 0, 0, 0, 1, LEN_AND_STR("nopatch")}, {RPMTAG_EXCLUDEARCH, 0, 0, 0, 1, LEN_AND_STR("excludearch")}, {RPMTAG_EXCLUSIVEARCH, 0, 0, 0, 1, LEN_AND_STR("exclusivearch")}, {RPMTAG_EXCLUDEOS, 0, 0, 0, 1, LEN_AND_STR("excludeos")}, {RPMTAG_EXCLUSIVEOS, 0, 0, 0, 1, LEN_AND_STR("exclusiveos")}, {RPMTAG_ICON, 0, 0, 0, 0, LEN_AND_STR("icon")}, {RPMTAG_PROVIDENAME, 0, 0, 0, 0, LEN_AND_STR("provides")}, {RPMTAG_REQUIRENAME, 2, 0, 0, 0, LEN_AND_STR("requires")}, {RPMTAG_RECOMMENDNAME, 2, 0, 0, 0, LEN_AND_STR("recommends")}, {RPMTAG_SUGGESTNAME, 2, 0, 0, 0, LEN_AND_STR("suggests")}, {RPMTAG_SUPPLEMENTNAME, 2, 0, 0, 0, LEN_AND_STR("supplements")}, {RPMTAG_ENHANCENAME, 2, 0, 0, 0, LEN_AND_STR("enhances")}, {RPMTAG_PREREQ, 2, 1, 0, 0, LEN_AND_STR("prereq")}, {RPMTAG_CONFLICTNAME, 0, 0, 0, 0, LEN_AND_STR("conflicts")}, {RPMTAG_OBSOLETENAME, 0, 0, 0, 0, LEN_AND_STR("obsoletes")}, {RPMTAG_PREFIXES, 0, 0, 1, 0, LEN_AND_STR("prefixes")}, {RPMTAG_PREFIXES, 0, 0, 1, 0, LEN_AND_STR("prefix")}, {RPMTAG_BUILDROOT, 0, 0, 0, 1, LEN_AND_STR("buildroot")}, {RPMTAG_BUILDARCHS, 0, 0, 0, 0, LEN_AND_STR("buildarchitectures")}, {RPMTAG_BUILDARCHS, 0, 0, 0, 0, LEN_AND_STR("buildarch")}, {RPMTAG_BUILDCONFLICTS, 0, 0, 0, 1, LEN_AND_STR("buildconflicts")}, {RPMTAG_BUILDOPTION, 2, 0, 0, 1, LEN_AND_STR("buildoption")}, {RPMTAG_BUILDPREREQ, 0, 1, 0, 1, LEN_AND_STR("buildprereq")}, {RPMTAG_BUILDREQUIRES, 0, 0, 0, 1, LEN_AND_STR("buildrequires")}, {RPMTAG_BUILDSYSTEM, 0, 0, 1, 1, LEN_AND_STR("buildsystem")}, {RPMTAG_AUTOREQPROV, 0, 0, 0, 0, LEN_AND_STR("autoreqprov")}, {RPMTAG_AUTOREQ, 0, 0, 0, 0, LEN_AND_STR("autoreq")}, {RPMTAG_AUTOPROV, 0, 0, 0, 0, LEN_AND_STR("autoprov")}, {RPMTAG_DOCDIR, 0, 0, 0, 0, LEN_AND_STR("docdir")}, {RPMTAG_DISTTAG, 0, 0, 1, 0, LEN_AND_STR("disttag")}, {RPMTAG_BUGURL, 0, 0, 1, 0, LEN_AND_STR("bugurl")}, {RPMTAG_TRANSLATIONURL, 0, 0, 1, 0, LEN_AND_STR("translationurl")}, {RPMTAG_UPSTREAMRELEASES, 0, 0, 1, 0, LEN_AND_STR("upstreamreleases")}, {RPMTAG_ORDERNAME, 2, 0, 0, 0, LEN_AND_STR("orderwithrequires")}, {RPMTAG_REMOVEPATHPOSTFIXES,0, 0, 1, 0, LEN_AND_STR("removepathpostfixes")}, {RPMTAG_MODULARITYLABEL, 0, 0, 1, 0, LEN_AND_STR("modularitylabel")}, {0, 0, 0, 0} }; /** */ static int findPreambleTag(rpmSpec spec, PreambleRec * pr, const char ** macro, char * lang) { PreambleRec p; char *s = spec->line; SKIPSPACE(s); for (p = preambleList; p->token != NULL; p++) { if (!(p->token && !rstrncasecmp(s, p->token, p->len))) continue; if (p->deprecated) { rpmlog(RPMLOG_WARNING, _("line %d: %s is deprecated: %s\n"), spec->lineNum, p->token, spec->line); } break; } if (p == NULL || p->token == NULL) return 1; s = s + p->len; SKIPSPACE(s); switch (p->type) { default: case 0: /* Unless this is a source or a patch, a ':' better be next */ if (p->tag != RPMTAG_SOURCE && p->tag != RPMTAG_PATCH) { if (*s != ':') return 1; } *lang = '\0'; break; case 1: /* Parse optional ( ). */ case 2: if (*s == ':') { /* Type 1 is multilang, 2 is qualifiers with no defaults */ strcpy(lang, (p->type == 1) ? RPMBUILD_DEFAULT_LANG : ""); break; } if (*s != '(') return 1; s++; SKIPSPACE(s); while (!risspace(*s) && *s != ')') *lang++ = *s++; *lang = '\0'; SKIPSPACE(s); if (*s != ')') return 1; s++; SKIPSPACE(s); if (*s != ':') return 1; break; } *macro = p->ismacro ? p->token : NULL; *pr = p; return 0; } int parsePreamble(rpmSpec spec, int initialPackage, enum parseStages stage) { int nextPart = PART_ERROR; int res = PART_ERROR; /* assume failure */ int rc; char *linep; int flag = 0; Package pkg; char *name = NULL; char *NVR = NULL; char lang[BUFSIZ]; if (! initialPackage) { /* There is one option to %package: or -n */ if (parseSimplePart(spec->line, &name, &flag)) { rpmlog(RPMLOG_ERR, _("Bad package specification: %s\n"), spec->line); goto exit; } if (rpmCharCheck(spec, name, ALLOWED_CHARS_NAME, flag == PART_SUBNAME ? NULL : ALLOWED_FIRSTCHARS_NAME)) goto exit; if (!lookupPackage(spec, name, flag, NULL)) goto exit; /* Construct the package */ if (flag == PART_SUBNAME) { rasprintf(&NVR, "%s-%s", headerGetString(spec->packages->header, RPMTAG_NAME), name); } else NVR = xstrdup(name); pkg = newPackage(NVR, spec->pool, &spec->packages); headerPutString(pkg->header, RPMTAG_NAME, NVR); } else if (spec->sourcePackage) { NVR = xstrdup("(main package)"); pkg = spec->packages; } else { NVR = xstrdup("(main package)"); pkg = newPackage(NULL, spec->pool, &spec->packages); spec->sourcePackage = newPackage(NULL, spec->pool, NULL); } if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) { nextPart = PART_NONE; } else if (rc < 0) { goto exit; } else { while (! (nextPart = isPart(spec->line))) { const char * macro; PreambleRec p; /* Skip blank lines */ linep = spec->line; SKIPSPACE(linep); if (*linep != '\0') { if (findPreambleTag(spec, &p, ¯o, lang)) { if (spec->lineNum == 1 && (unsigned char)(spec->line[0]) == 0xed && (unsigned char)(spec->line[1]) == 0xab && (unsigned char)(spec->line[2]) == 0xee && (unsigned char)(spec->line[3]) == 0xdb) { rpmlog(RPMLOG_ERR, _("Binary rpm package found. Expected spec file!\n")); goto exit; } rpmlog(RPMLOG_ERR, _("line %d: Unknown tag: %s\n"), spec->lineNum, spec->line); goto exit; } if (stage == PARSE_GENERATED && p->prebuildonly) { rpmlog(RPMLOG_ERR, _("line %d: Tag not allowed after build is done: %s\n"), spec->lineNum, spec->line); goto exit; } if (handlePreambleTag(spec, stage, pkg, p->tag, macro, lang)) { goto exit; } if (spec->BANames && !spec->recursing) { res = PART_BUILDARCHITECTURES; goto exit; } } if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) { nextPart = PART_NONE; break; } if (rc) { goto exit; } } } if (initialPackage) { if (checkForRequiredForBuild(pkg->header)) { goto exit; } if (!spec->buildDir) { /* Using release here causes a buildid no-recompute test to fail */ char *bn = rpmExpand("%{NAME}-%{VERSION}-build", NULL); /* Tilde and caret in paths are evil, convert to underscores */ for (char *t = bn; *t; t++) { if (*t == '^' || *t == '~') *t = '_'; } spec->buildDir = rpmExpand("%{_top_builddir}/", bn, NULL); free(bn); /* Override toplevel _builddir for backwards compatibility */ rpmPushMacroFlags(spec->macros, "_builddir", NULL, spec->buildDir, RMIL_SPEC, RPMMACRO_LITERAL); /* A user-oriented, unambiguous name for the thing */ rpmPushMacroFlags(spec->macros, "builddir", NULL, spec->buildDir, RMIL_SPEC, RPMMACRO_LITERAL); char *specparts = rpmGetPath(spec->buildDir, "/SPECPARTS", NULL); rpmPushMacroFlags(spec->macros, "specpartsdir", NULL, specparts, RMIL_SPEC, RPMMACRO_LITERAL); free(specparts); } if (!spec->buildRoot) { spec->buildRoot = rpmGetPath(spec->buildDir, "/BUILDROOT", NULL); } else { char *buildRoot = rpmGetPath(spec->buildRoot, NULL); free(spec->buildRoot); spec->buildRoot = buildRoot; } if (rstreq(spec->buildRoot, "") || rstreq(spec->buildRoot, "/")) { rpmlog(RPMLOG_ERR, _("Invalid buildroot: '%s'\n"), spec->buildRoot); goto exit; } rpmPushMacroFlags(spec->macros, "buildroot", NULL, spec->buildRoot, RMIL_SPEC, RPMMACRO_LITERAL); /* Silly compat thing, but many specs use %_buildrootdir */ rpmPushMacroFlags(spec->macros, "_buildrootdir", NULL, "%{dirname:%{buildroot}}", RMIL_GLOBAL, 0); } /* XXX Skip valid arch check if not building binary package */ if (!(spec->flags & RPMSPEC_ANYARCH) && checkForValidArchitectures(spec)) { goto exit; } /* if we get down here nextPart has been set to non-error */ res = nextPart; exit: free(name); free(NVR); return res; } rpm-software-management-rpm-3c1f23f/build/parsePrep.cc000066400000000000000000000251241511627505500230410ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parsePrep.c * Parse %prep section from spec file. */ #include "system.h" #include #include #include #include #include #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "rpmmacro_internal.hh" #include "debug.h" static void appendMb(rpmMacroBuf mb, const char *s, int nl) { rpmMacroBufAppendStr(mb, s); if (nl) rpmMacroBufAppend(mb, '\n'); } /** * Check if file can be determined non-compressed * @param urlfn file url * @return 1 if known uncompressed, 0 otherwise */ static int notCompressed(const char * fn) { rpmCompressedMagic compressed = COMPRESSED_OTHER; /* arbitrary non-zero */ struct stat sb; if (lstat(fn, &sb) == 0) rpmFileIsCompressed(fn, &compressed); return (compressed == COMPRESSED_NOT); } /** * Expand %patchN macro into %prep scriptlet. * @param spec build info * @param c patch index * @param strip patch level (i.e. patch -p argument) * @param db saved file suffix (i.e. patch --suffix argument) * @param reverse include -R? * @param removeEmpties include -E? * @param fuzz fuzz factor, fuzz<0 means no fuzz set * @param dir dir to change to (i.e. patch -d argument) * @param outfile send output to this file (i.e. patch -o argument) * @param setUtc include -Z? * @return expanded %patch macro (NULL on error) */ static char *doPatch(rpmSpec spec, uint32_t c, int strip, const char *db, int reverse, int removeEmpties, int fuzz, const char *dir, const char *outfile, int setUtc) { char *buf = NULL; char *arg_backup = NULL; char *arg_fuzz = NULL; char *arg_dir = NULL; char *arg_outfile = NULL; char *args = NULL; char *arg_patch_flags = rpmExpand("%{?_default_patch_flags}", NULL); struct Source *sp; char *patchcmd; if ((sp = findSource(spec, c, RPMBUILD_ISPATCH)) == NULL) { rpmlog(RPMLOG_ERR, _("No patch number %u\n"), c); goto exit; } if (db) { rasprintf(&arg_backup, "-b --suffix %s", db); } else arg_backup = xstrdup(""); if (dir) { rasprintf(&arg_dir, " -d %s", dir); } else arg_dir = xstrdup(""); if (outfile) { rasprintf(&arg_outfile, " -o %s", outfile); } else arg_outfile = xstrdup(""); if (fuzz >= 0) { rasprintf(&arg_fuzz, " --fuzz=%d", fuzz); } else arg_fuzz = xstrdup(""); rasprintf(&args, "%s -p%d %s%s%s%s%s%s%s", arg_patch_flags, strip, arg_backup, arg_fuzz, arg_dir, arg_outfile, reverse ? " -R" : "", removeEmpties ? " -E" : "", setUtc ? " -Z" : ""); /* Avoid the extra cost of fork and pipe for uncompressed patches */ if (notCompressed(sp->path)) { patchcmd = rpmExpand("%{__patch} ", args, " < ", sp->path, NULL); } else { patchcmd = rpmExpand("{ %{__rpmuncompress} ", sp->path, " || echo patch_fail ; } | " "%{__patch} ", args, NULL); } free(arg_fuzz); free(arg_outfile); free(arg_dir); free(arg_backup); free(args); rasprintf(&buf, "echo \"Patch #%u (%s):\"\n" "%s\n", c, sp->source, patchcmd); free(patchcmd); exit: free(arg_patch_flags); return buf; } /** * Expand %setup macro into %prep scriptlet. * @param spec build info * @param c source index * @param quietly should -vv be omitted from tar? * @return expanded %setup macro (NULL on error) */ static char *doUntar(rpmSpec spec, uint32_t c, int quietly, int autoPath) { char *buf = NULL; struct Source *sp; if ((sp = findSource(spec, c, RPMBUILD_ISSOURCE)) == NULL) { rpmlog(RPMLOG_ERR, _("No source number %u\n"), c); goto exit; } buf = rpmExpand("%{__rpmuncompress} -x ", quietly ? "" : "-v ", autoPath ? "-C %{buildsubdir} " : "", "%{shescape:", sp->path, "}", NULL); rstrcat(&buf, "\nSTATUS=$?\n" "if [ $STATUS -ne 0 ]; then\n" " exit $STATUS\n" "fi"); exit: return buf; } /** * Parse %setup macro. * @param spec build info * @param line current line from spec file * @return RPMRC_OK on success */ void doSetupMacro(rpmMacroBuf mb, rpmMacroEntry me, ARGV_t margs, size_t *parsed) { rpmSpec spec = (rpmSpec)rpmMacroEntryPriv(me); char *line = argvJoin(margs, " "); char *buf = NULL; StringBuf before = newStringBuf(); StringBuf after = newStringBuf(); poptContext optCon = NULL; int argc; const char ** argv = NULL; int arg; int xx; uint32_t num; int leaveDirs = 0, skipDefaultAction = 0; int createDir = 0, quietly = 0, autoPath = 0; char * dirName = NULL; struct poptOption optionsTable[] = { { NULL, 'a', POPT_ARG_STRING, NULL, 'a', NULL, NULL}, { NULL, 'b', POPT_ARG_STRING, NULL, 'b', NULL, NULL}, { NULL, 'c', 0, &createDir, 0, NULL, NULL}, { NULL, 'C', 0, &autoPath, 0, NULL, NULL}, { NULL, 'D', 0, &leaveDirs, 0, NULL, NULL}, { NULL, 'n', POPT_ARG_STRING, &dirName, 0, NULL, NULL}, { NULL, 'T', 0, &skipDefaultAction, 0, NULL, NULL}, { NULL, 'q', 0, &quietly, 0, NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; if (strstr(line+5, " -q")) quietly = 1; if ((xx = poptParseArgvString(line, &argc, &argv))) { rpmMacroBufErr(mb, 1, _("Error parsing %%setup: %s\n"), poptStrerror(xx)); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { char *optArg = poptGetOptArg(optCon); /* We only parse -a and -b here */ if (parseUnsignedNum(optArg, &num)) { rpmMacroBufErr(mb, 1, _("line %d: Bad arg to %%setup: %s\n"), spec->lineNum, (optArg ? optArg : "???")); goto exit; } { char *chptr = doUntar(spec, num, quietly, 0); if (chptr == NULL) goto exit; appendLineStringBuf((arg == 'a' ? after : before), chptr); free(chptr); } free(optArg); } if (arg < -1) { rpmMacroBufErr(mb, 1, _("line %d: Bad %%setup option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(arg)); goto exit; } if (dirName) { rpmPushMacro(spec->macros, "buildsubdir", NULL, dirName, RMIL_SPEC); } else { char * buildSubdir = NULL; rasprintf(&buildSubdir, "%s-%s", headerGetString(spec->packages->header, RPMTAG_NAME), headerGetString(spec->packages->header, RPMTAG_VERSION)); rpmPushMacro(spec->macros, "buildsubdir", NULL, buildSubdir, RMIL_SPEC); free(buildSubdir); } /* cd to the build dir */ { char * buildDir = rpmGenPath(spec->rootDir, "%{_builddir}", ""); rasprintf(&buf, "cd '%s'", buildDir); appendMb(mb, buf, 1); free(buf); free(buildDir); } /* delete any old sources */ if (!leaveDirs) { buf = rpmExpand("rm -rf '%{buildsubdir}'", NULL); appendMb(mb, buf, 1); free(buf); } appendMb(mb, getStringBuf(before), 0); /* if necessary, create and cd into the proper dir */ if (createDir) { buf = rpmExpand("%{__mkdir_p} '%{buildsubdir}'\n", "cd '%{buildsubdir}'", NULL); appendMb(mb, buf, 1); free(buf); } /* do the default action */ if (!skipDefaultAction) { char *chptr = doUntar(spec, 0, quietly, autoPath); if (!chptr) goto exit; appendMb(mb, chptr, 1); free(chptr); } if (!createDir) { buf = rpmExpand("cd '%{buildsubdir}'", NULL); appendMb(mb, buf, 1); free(buf); } appendMb(mb, getStringBuf(after), 0); /* Fix the permissions of the setup build tree */ { char *fix = rpmExpand("%{_fixperms} .", NULL); if (fix && *fix != '%') { appendMb(mb, fix, 1); } free(fix); } exit: freeStringBuf(before); freeStringBuf(after); poptFreeContext(optCon); free(dirName); free(argv); free(line); return; } /** * Parse %patch line. * This supports too many crazy syntaxes: * - %patchN is equal to %patch -P\ * - -P\ -P\... can be used to apply several patch on a single line * - Any trailing arguments are treated as patch numbers * - Any combination of the above * * @param spec build info * @param line current line from spec file * @return RPMRC_OK on success */ void doPatchMacro(rpmMacroBuf mb, rpmMacroEntry me, ARGV_t margs, size_t *parsed) { rpmSpec spec = (rpmSpec)rpmMacroEntryPriv(me); char *line = argvJoin(margs, " "); char *opt_b, *opt_d, *opt_o; int opt_p, opt_R, opt_E, opt_F, opt_Z; int argc, c; const char **argv = NULL; ARGV_t patch, patchnums = NULL; struct poptOption const patchOpts[] = { { NULL, 'P', POPT_ARG_STRING, NULL, 'P', NULL, NULL }, { NULL, 'p', POPT_ARG_INT, &opt_p, 'p', NULL, NULL }, { NULL, 'R', POPT_ARG_NONE, &opt_R, 'R', NULL, NULL }, { NULL, 'E', POPT_ARG_NONE, &opt_E, 'E', NULL, NULL }, { NULL, 'b', POPT_ARG_STRING, &opt_b, 'b', NULL, NULL }, { NULL, 'z', POPT_ARG_STRING, &opt_b, 'z', NULL, NULL }, { NULL, 'F', POPT_ARG_INT, &opt_F, 'F', NULL, NULL }, { NULL, 'd', POPT_ARG_STRING, &opt_d, 'd', NULL, NULL }, { NULL, 'o', POPT_ARG_STRING, &opt_o, 'o', NULL, NULL }, { NULL, 'Z', POPT_ARG_NONE, &opt_Z, 'Z', NULL, NULL}, { NULL, 0, 0, NULL, 0, NULL, NULL } }; poptContext optCon = NULL; opt_p = opt_R = opt_E = opt_Z = 0; opt_F = rpmExpandNumeric("%{_default_patch_fuzz}"); /* get default fuzz factor for %patch */ opt_b = opt_d = opt_o = NULL; poptParseArgvString(line, &argc, &argv); /* * Grab all -P numbers for later processing. Stored as strings * at this point so we only have to worry about conversion in one place. */ optCon = poptGetContext(NULL, argc, argv, patchOpts, 0); while ((c = poptGetNextOpt(optCon)) > 0) { switch (c) { case 'P': { char *arg = poptGetOptArg(optCon); if (arg) { argvAdd(&patchnums, arg); free(arg); } break; } default: break; } } if (c < -1) { rpmMacroBufErr(mb, 1, "%s: %s: %s\n", poptStrerror(c), poptBadOption(optCon, POPT_BADOPTION_NOALIAS), line); goto exit; } /* Any trailing arguments are treated as patch numbers */ argvAppend(&patchnums, (ARGV_const_t) poptGetArgs(optCon)); if (argvCount(patchnums) == 0) { rpmMacroBufErr(mb, 1, _("Patch number not specified: %s\n"), line); goto exit; } /* Convert to number, generate patch command and append to %prep script */ for (patch = patchnums; *patch; patch++) { uint32_t pnum; char *s; if (parseUnsignedNum(*patch, &pnum)) { rpmMacroBufErr(mb, 1, _("Invalid patch number %s: %s\n"), *patch, line); goto exit; } s = doPatch(spec, pnum, opt_p, opt_b, opt_R, opt_E, opt_F, opt_d, opt_o, opt_Z); if (s == NULL) { goto exit; } appendMb(mb, s, 1); free(s); } exit: argvFree(patchnums); free(opt_b); free(opt_d); free(opt_o); free(argv); free(line); poptFreeContext(optCon); return; } rpm-software-management-rpm-3c1f23f/build/parseReqs.cc000066400000000000000000000217551511627505500230530ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseReqs.c * Parse dependency tag from spec file or from auto-dependency generator. */ #include "system.h" #include #include #include #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "debug.h" #define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;} #define SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;} static rpmRC checkSep(const char *s, char c, char **emsg) { const char *sep = strchr(s, c); if (sep && strchr(sep + 1, c)) { rasprintf(emsg, "Invalid version (double separator '%c'): %s", c, s); return RPMRC_FAIL; } return RPMRC_OK; } static rpmRC checkEpoch(const char *s, char **emsg) { const char *si, *sep = strchr(s, ':'); if (!sep) return RPMRC_OK; for (si = s; si != sep; si++) { if (!risdigit(*si)) { rasprintf(emsg, "Invalid version (epoch must be unsigned integer): %s", s); return RPMRC_FAIL; } } return RPMRC_OK; } static rpmRC checkDep(rpmSpec spec, char *N, char *EVR, char **emsg) { /* * Tokens must begin with alphanumeric, _, or /, but we don't know * the spec's encoding so we only check what we can: plain ascii. */ if (isascii(N[0]) && !(risalnum(N[0]) || N[0] == '_' || N[0] == '/')) { rasprintf(emsg, _("Dependency tokens must begin with alpha-numeric, '_' or '/'")); return RPMRC_FAIL; } if (EVR) { if (N[0] == '/') { rasprintf(emsg, _("Versioned file name not permitted")); return RPMRC_FAIL; } if (rpmCharCheck(spec, EVR, ALLOWED_CHARS_EVR, NULL)) return RPMRC_FAIL; if (checkSep(EVR, '-', emsg) != RPMRC_OK || checkSep(EVR, ':', emsg) != RPMRC_OK || checkEpoch(EVR, emsg) != RPMRC_OK) { if (rpmExpandNumeric("%{?_wrong_version_format_terminate_build}")) return RPMRC_FAIL; } } return RPMRC_OK; } struct parseRCPOTRichData { rpmSpec spec; StringBuf sb; }; /* Callback for the rich dependency parser. We use this to do check for invalid * characters and to build a normailzed version of the dependency */ static rpmRC parseRCPOTRichCB(void *cbdata, rpmrichParseType type, const char *n, int nl, const char *e, int el, rpmsenseFlags sense, rpmrichOp op, char **emsg) { struct parseRCPOTRichData *data = (struct parseRCPOTRichData *)cbdata; StringBuf sb = data->sb; rpmRC rc = RPMRC_OK; if (type == RPMRICH_PARSE_ENTER) { appendStringBuf(sb, "("); } else if (type == RPMRICH_PARSE_LEAVE) { appendStringBuf(sb, ")"); } else if (type == RPMRICH_PARSE_SIMPLE) { char *N = (char *)xmalloc(nl + 1); char *EVR = NULL; rstrlcpy(N, n, nl + 1); appendStringBuf(sb, N); if (el) { char rel[6], *rp = rel; EVR = (char *)xmalloc(el + 1); rstrlcpy(EVR, e, el + 1); *rp++ = ' '; if (sense & RPMSENSE_LESS) *rp++ = '<'; if (sense & RPMSENSE_GREATER) *rp++ = '>'; if (sense & RPMSENSE_EQUAL) *rp++ = '='; *rp++ = ' '; *rp = 0; appendStringBuf(sb, rel); appendStringBuf(sb, EVR); } rc = checkDep(data->spec, N, EVR, emsg); _free(N); _free(EVR); } else if (type == RPMRICH_PARSE_OP) { appendStringBuf(sb, " "); appendStringBuf(sb, rpmrichOpStr(op)); appendStringBuf(sb, " "); } return rc; } rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN, int index, rpmsenseFlags tagflags, addReqProvFunction cb, void *cbdata) { const char *r, *re, *v, *ve; char *emsg = NULL; char * N = NULL, * EVR = NULL; rpmTagVal nametag = RPMTAG_NOT_FOUND; rpmsenseFlags Flags; rpmRC rc = RPMRC_FAIL; /* assume failure */ int allow_richdeps = 0; if (!cbdata) cbdata = pkg; switch (tagN) { default: case RPMTAG_REQUIRENAME: tagflags |= RPMSENSE_ANY; /* fall through */ case RPMTAG_RECOMMENDNAME: case RPMTAG_SUGGESTNAME: case RPMTAG_SUPPLEMENTNAME: case RPMTAG_ENHANCENAME: case RPMTAG_CONFLICTNAME: allow_richdeps = 1; /* fall through */ case RPMTAG_PROVIDENAME: case RPMTAG_OBSOLETENAME: case RPMTAG_ORDERNAME: nametag = tagN; break; case RPMTAG_PREREQ: /* XXX map legacy PreReq into Requires(pre,preun) */ nametag = RPMTAG_REQUIRENAME; tagflags |= (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_PREUN); allow_richdeps = 1; break; case RPMTAG_TRIGGERPREIN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERPREIN; break; case RPMTAG_TRIGGERIN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERIN; break; case RPMTAG_TRIGGERPOSTUN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERPOSTUN; break; case RPMTAG_TRIGGERUN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERUN; break; case RPMTAG_BUILDPREREQ: case RPMTAG_BUILDREQUIRES: nametag = RPMTAG_REQUIRENAME; tagflags |= RPMSENSE_ANY; allow_richdeps = 1; break; case RPMTAG_BUILDCONFLICTS: nametag = RPMTAG_CONFLICTNAME; allow_richdeps = 1; break; case RPMTAG_FILETRIGGERIN: nametag = RPMTAG_FILETRIGGERNAME; tagflags |= RPMSENSE_TRIGGERIN; break; case RPMTAG_FILETRIGGERUN: nametag = RPMTAG_FILETRIGGERNAME; tagflags |= RPMSENSE_TRIGGERUN; break; case RPMTAG_FILETRIGGERPOSTUN: nametag = RPMTAG_FILETRIGGERNAME; tagflags |= RPMSENSE_TRIGGERPOSTUN; break; case RPMTAG_TRANSFILETRIGGERIN: nametag = RPMTAG_TRANSFILETRIGGERNAME; tagflags |= RPMSENSE_TRIGGERIN; break; case RPMTAG_TRANSFILETRIGGERUN: nametag = RPMTAG_TRANSFILETRIGGERNAME; tagflags |= RPMSENSE_TRIGGERUN; break; case RPMTAG_TRANSFILETRIGGERPOSTUN: nametag = RPMTAG_TRANSFILETRIGGERNAME; tagflags |= RPMSENSE_TRIGGERPOSTUN; break; } for (r = field; *r != '\0'; r = re) { SKIPWHITE(r); if (*r == '\0') break; Flags = (tagflags & ~RPMSENSE_SENSEMASK); if (r[0] == '(') { struct parseRCPOTRichData data; if (!allow_richdeps) { rasprintf(&emsg, _("No rich dependencies allowed for this type")); goto exit; } data.spec = spec; data.sb = newStringBuf(); if (rpmrichParseForTag(&r, &emsg, parseRCPOTRichCB, &data, nametag) != RPMRC_OK) { freeStringBuf(data.sb); goto exit; } if (cb && cb(cbdata, nametag, getStringBuf(data.sb), NULL, Flags, index) != RPMRC_OK) { rasprintf(&emsg, _("invalid dependency")); freeStringBuf(data.sb); goto exit; } freeStringBuf(data.sb); re = r; continue; } re = r; SKIPNONWHITE(re); N = (char *)xmalloc((re-r) + 1); rstrlcpy(N, r, (re-r) + 1); /* Parse EVR */ EVR = NULL; v = re; SKIPWHITE(v); ve = v; SKIPNONWHITE(ve); re = v; /* ==> next token (if no EVR found) starts here */ /* Check for possible logical operator */ if (ve > v) { rpmsenseFlags sense = rpmParseDSFlags(v, ve - v); if (sense) { Flags |= sense; /* now parse EVR */ v = ve; SKIPWHITE(v); ve = v; SKIPNONWHITE(ve); if (*v == '\0' || ve == v) { rasprintf(&emsg, _("Version required")); goto exit; } EVR = (char *)xmalloc((ve-v) + 1); rstrlcpy(EVR, v, (ve-v) + 1); re = ve; /* ==> next token after EVR string starts here */ } } /* needs to be checked before checkDep() */ if (nametag == RPMTAG_FILETRIGGERNAME || nametag == RPMTAG_TRANSFILETRIGGERNAME) { if (N[0] != '/') { rasprintf(&emsg, _("file trigger conditions must begin with '/'")); goto exit; } } /* check that dependency is well-formed */ if (checkDep(spec, N, EVR, &emsg)) goto exit; if (nametag == RPMTAG_OBSOLETENAME) { if (rpmCharCheck(spec, N, ALLOWED_CHARS_NAME, ALLOWED_FIRSTCHARS_NAME)) { rasprintf(&emsg, _("Only package names are allowed in " "Obsoletes")); goto exit; } if (!EVR) { rasprintf(&emsg, _("It's not recommended to have " "unversioned Obsoletes")); } else if (Flags & RPMSENSE_GREATER) { rasprintf(&emsg, _("It's not recommended to use " "'>' in Obsoletes")); } } /* Deny more "normal" triggers fired by the same pakage. File triggers are ok */ if (nametag == RPMTAG_TRIGGERNAME) { rpmds *pdsp = packageDependencies(pkg, nametag); rpmds newds = rpmdsSingle(nametag, N, EVR, Flags); rpmdsInit(*pdsp); while (rpmdsNext(*pdsp) >= 0) { if (rpmdsCompare(*pdsp, newds) && (rpmdsFlags(*pdsp) & tagflags )) { rasprintf(&emsg, _("Trigger fired by the same package " "is already defined in spec file")); break; } } rpmdsFree(newds); if (emsg) goto exit; } if (cb && cb(cbdata, nametag, N, EVR, Flags, index) != RPMRC_OK) { rasprintf(&emsg, _("invalid dependency")); goto exit; } N = _free(N); EVR = _free(EVR); } rc = RPMRC_OK; exit: if (emsg) { int lvl = (rc == RPMRC_OK) ? RPMLOG_WARNING : RPMLOG_ERR; /* Automatic dependencies and triggers don't relate to spec lines */ if (tagflags & (RPMSENSE_FIND_REQUIRES|RPMSENSE_FIND_PROVIDES|RPMSENSE_TRIGGER)) { rpmlog(lvl, "%s: %s\n", emsg, r); } else { rpmlog(lvl, _("line %d: %s: %s\n"), spec->lineNum, emsg, spec->line); } free(emsg); } _free(N); _free(EVR); return rc; } rpm-software-management-rpm-3c1f23f/build/parseScript.cc000066400000000000000000000306111511627505500233740ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseScript.c * Parse install-time script section from spec file. */ #include "system.h" #include #include #include "rpmlua.hh" #include "rpmscript.hh" /* script flags */ #include "rpmtriggers.hh" /* default priority */ #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "debug.h" #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; } /** */ static int addTriggerIndex(Package pkg, const char *file, const char *script, const char *prog, rpmscriptFlags flags, rpmTagVal tag, uint32_t priority) { struct TriggerFileEntry *tfe; std::vector *tfp; if (tag == RPMTAG_FILETRIGGERSCRIPTS) { tfp = &pkg->fileTriggerFiles; } else if (tag == RPMTAG_TRANSFILETRIGGERSCRIPTS) { tfp = &pkg->transFileTriggerFiles; } else { tfp = &pkg->triggerFiles; } tfp->push_back({}); tfe = &tfp->back(); tfe->fileName = (file) ? xstrdup(file) : NULL; tfe->script = (script && *script != '\0') ? xstrdup(script) : NULL; tfe->prog = xstrdup(prog); tfe->flags = flags; tfe->index = tfp->size() - 1; tfe->priority = priority; return tfe->index; } /* %trigger is a strange combination of %pre and Requires: behavior */ /* We can handle it by parsing the args before "--" in parseScript. */ /* We then pass the remaining arguments to parseRCPOT, along with */ /* an index we just determined. */ int parseScript(rpmSpec spec, int parsePart) { /* There are a few options to scripts: */ /* */ /* -n */ /* -p */ /* -p " ..." */ /* -f */ const char *p = ""; const char **progArgv = NULL; int progArgc; const char *partname = NULL; rpmTagVal reqtag = 0; rpmTagVal tag = 0; rpmsenseFlags tagflags = 0; rpmTagVal progtag = 0; rpmTagVal flagtag = 0; rpmscriptFlags scriptFlags = 0; int flag = PART_SUBNAME; Package pkg; StringBuf sb = NULL; int index; char * reqargs = NULL; int nextPart, res = PART_ERROR; /* assume failure */ int rc, argc; int arg; const char **argv = NULL; poptContext optCon = NULL; char *name = NULL; char *prog = xstrdup("/bin/sh"); char *origprog = prog; char *file = NULL; int priority = RPMTRIGGER_DEFAULT_PRIORITY; struct poptOption optionsTable[] = { { NULL, 'p', POPT_ARG_STRING, &prog, 'p', NULL, NULL}, { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL}, { NULL, 'f', POPT_ARG_STRING, &file, 'f', NULL, NULL}, { NULL, 'e', POPT_BIT_SET, &scriptFlags, RPMSCRIPT_FLAG_EXPAND, NULL, NULL}, { NULL, 'q', POPT_BIT_SET, &scriptFlags, RPMSCRIPT_FLAG_QFORMAT, NULL, NULL}, { NULL, 'P', POPT_ARG_INT, &priority, 'P', NULL, NULL}, { 0, 0, 0, 0, 0, NULL, NULL} }; switch (parsePart) { case PART_PRE: tag = RPMTAG_PREIN; tagflags = RPMSENSE_SCRIPT_PRE; progtag = RPMTAG_PREINPROG; flagtag = RPMTAG_PREINFLAGS; partname = "%pre"; break; case PART_POST: tag = RPMTAG_POSTIN; tagflags = RPMSENSE_SCRIPT_POST; progtag = RPMTAG_POSTINPROG; flagtag = RPMTAG_POSTINFLAGS; partname = "%post"; break; case PART_PREUN: tag = RPMTAG_PREUN; tagflags = RPMSENSE_SCRIPT_PREUN; progtag = RPMTAG_PREUNPROG; flagtag = RPMTAG_PREUNFLAGS; partname = "%preun"; break; case PART_POSTUN: tag = RPMTAG_POSTUN; tagflags = RPMSENSE_SCRIPT_POSTUN; progtag = RPMTAG_POSTUNPROG; flagtag = RPMTAG_POSTUNFLAGS; partname = "%postun"; break; case PART_PRETRANS: tag = RPMTAG_PRETRANS; tagflags = RPMSENSE_PRETRANS; progtag = RPMTAG_PRETRANSPROG; flagtag = RPMTAG_PRETRANSFLAGS; partname = "%pretrans"; break; case PART_POSTTRANS: tag = RPMTAG_POSTTRANS; tagflags = RPMSENSE_POSTTRANS; progtag = RPMTAG_POSTTRANSPROG; flagtag = RPMTAG_POSTTRANSFLAGS; partname = "%posttrans"; break; case PART_PREUNTRANS: tag = RPMTAG_PREUNTRANS; tagflags = RPMSENSE_PREUNTRANS; progtag = RPMTAG_PREUNTRANSPROG; flagtag = RPMTAG_PREUNTRANSFLAGS; partname = "%preuntrans"; break; case PART_POSTUNTRANS: tag = RPMTAG_POSTUNTRANS; tagflags = RPMSENSE_POSTUNTRANS; progtag = RPMTAG_POSTUNTRANSPROG; flagtag = RPMTAG_POSTUNTRANSFLAGS; partname = "%postuntrans"; break; case PART_VERIFYSCRIPT: tag = RPMTAG_VERIFYSCRIPT; tagflags = RPMSENSE_SCRIPT_VERIFY; progtag = RPMTAG_VERIFYSCRIPTPROG; flagtag = RPMTAG_VERIFYSCRIPTFLAGS; partname = "%verifyscript"; break; case PART_TRIGGERPREIN: tag = RPMTAG_TRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRIGGERPREIN; progtag = RPMTAG_TRIGGERSCRIPTPROG; flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerprein"; break; case PART_TRIGGERIN: tag = RPMTAG_TRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRIGGERIN; progtag = RPMTAG_TRIGGERSCRIPTPROG; flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerin"; break; case PART_TRIGGERUN: tag = RPMTAG_TRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRIGGERUN; progtag = RPMTAG_TRIGGERSCRIPTPROG; flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerun"; break; case PART_TRIGGERPOSTUN: tag = RPMTAG_TRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRIGGERPOSTUN; progtag = RPMTAG_TRIGGERSCRIPTPROG; flagtag = RPMTAG_TRIGGERSCRIPTFLAGS; partname = "%triggerpostun"; break; case PART_FILETRIGGERIN: tag = RPMTAG_FILETRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_FILETRIGGERIN; progtag = RPMTAG_FILETRIGGERSCRIPTPROG; flagtag = RPMTAG_FILETRIGGERSCRIPTFLAGS; partname = "%filetriggerin"; break; case PART_FILETRIGGERUN: tag = RPMTAG_FILETRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_FILETRIGGERUN; progtag = RPMTAG_FILETRIGGERSCRIPTPROG; flagtag = RPMTAG_FILETRIGGERSCRIPTFLAGS; partname = "%filetriggerun"; break; case PART_FILETRIGGERPOSTUN: tag = RPMTAG_FILETRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_FILETRIGGERPOSTUN; progtag = RPMTAG_FILETRIGGERSCRIPTPROG; flagtag = RPMTAG_FILETRIGGERSCRIPTFLAGS; partname = "%filetriggerpostun"; break; case PART_TRANSFILETRIGGERIN: tag = RPMTAG_TRANSFILETRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRANSFILETRIGGERIN; progtag = RPMTAG_TRANSFILETRIGGERSCRIPTPROG; flagtag = RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS; partname = "%transfiletriggerin"; break; case PART_TRANSFILETRIGGERUN: tag = RPMTAG_TRANSFILETRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRANSFILETRIGGERUN; progtag = RPMTAG_TRANSFILETRIGGERSCRIPTPROG; flagtag = RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS; partname = "%transfiletriggerun"; break; case PART_TRANSFILETRIGGERPOSTUN: tag = RPMTAG_TRANSFILETRIGGERSCRIPTS; tagflags = 0; reqtag = RPMTAG_TRANSFILETRIGGERPOSTUN; progtag = RPMTAG_TRANSFILETRIGGERSCRIPTPROG; flagtag = RPMTAG_TRANSFILETRIGGERSCRIPTFLAGS; partname = "%transfiletriggerpostun"; break; } if (tag == RPMTAG_TRIGGERSCRIPTS || tag == RPMTAG_FILETRIGGERSCRIPTS || tag == RPMTAG_TRANSFILETRIGGERSCRIPTS) { /* break line into two at the -- separator */ char *sep, *s = spec->line; while ((s = strstr(s, "--")) != NULL) { s += 2; if (risblank(*(s-3)) && risblank(*s)) break; } if (s == NULL) { rpmlog(RPMLOG_ERR, _("line %d: triggers must have --: %s\n"), spec->lineNum, spec->line); goto exit; } sep = s; SKIPSPACE(s); if (*s == '\0') { rpmlog(RPMLOG_ERR, _("line %d: missing trigger condition: %s\n"), spec->lineNum, spec->line); goto exit; } *sep = '\0'; reqargs = xstrdup(s); } if ((rc = poptParseArgvString(spec->line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("line %d: Error parsing %s: %s\n"), spec->lineNum, partname, poptStrerror(rc)); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((arg = poptGetNextOpt(optCon)) > 0) { switch (arg) { case 'p': if (prog[0] == '<') { if (prog[strlen(prog)-1] != '>') { rpmlog(RPMLOG_ERR, _("line %d: internal script must end " "with \'>\': %s\n"), spec->lineNum, prog); goto exit; } } else if (prog[0] != '/') { rpmlog(RPMLOG_ERR, _("line %d: script program must begin " "with \'/\': %s\n"), spec->lineNum, prog); goto exit; } break; case 'n': flag = PART_NAME; break; case 'P': if (tag != RPMTAG_TRIGGERSCRIPTS && tag != RPMTAG_FILETRIGGERSCRIPTS && tag != RPMTAG_TRANSFILETRIGGERSCRIPTS) { rpmlog(RPMLOG_ERR, _("line %d: Priorities are allowed only for file " "triggers : %s\n"), spec->lineNum, prog); goto exit; } } } if (arg < -1) { rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"), spec->lineNum, poptBadOption(optCon, POPT_BADOPTION_NOALIAS), spec->line); goto exit; } if (poptPeekArg(optCon)) { if (name == NULL) name = xstrdup(poptGetArg(optCon)); if (poptPeekArg(optCon)) { rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"), spec->lineNum, spec->line); goto exit; } } if (lookupPackage(spec, name, flag, &pkg)) goto exit; if (tag != RPMTAG_TRIGGERSCRIPTS) { if (headerIsEntry(pkg->header, progtag)) { rpmlog(RPMLOG_ERR, _("line %d: Second %s\n"), spec->lineNum, partname); goto exit; } } if ((rc = poptParseArgvString(prog, &progArgc, &progArgv))) { rpmlog(RPMLOG_ERR, _("line %d: Error parsing %s: %s\n"), spec->lineNum, partname, poptStrerror(rc)); goto exit; } if ((nextPart = parseLines(spec, STRIP_NOTHING, NULL, &sb)) == PART_ERROR) goto exit; if (sb) { stripTrailingBlanksStringBuf(sb); p = getStringBuf(sb); } if (rstreq(progArgv[0], "")) { rpmlua lua = NULL; /* Global state. */ if (rpmluaCheckScript(lua, p, partname) != RPMRC_OK) { goto exit; } (void) rpmlibNeedsFeature(pkg, "BuiltinLuaScripts", "4.2.2-1"); } else if (progArgv[0][0] == '<') { rpmlog(RPMLOG_ERR, _("line %d: unsupported internal script: %s\n"), spec->lineNum, progArgv[0]); goto exit; } else { (void) addReqProv(pkg, RPMTAG_REQUIRENAME, progArgv[0], NULL, (tagflags | RPMSENSE_INTERP), 0); } if (scriptFlags) { rpmlibNeedsFeature(pkg, "ScriptletExpansion", "4.9.0-1"); } /* Trigger script insertion is always delayed in order to */ /* get the index right. */ if (tag == RPMTAG_TRIGGERSCRIPTS || tag == RPMTAG_FILETRIGGERSCRIPTS || tag == RPMTAG_TRANSFILETRIGGERSCRIPTS) { if (progArgc > 1) { rpmlog(RPMLOG_ERR, _("line %d: interpreter arguments not allowed in triggers: %s\n"), spec->lineNum, prog); goto exit; } /* Add file/index/prog triple to the trigger file list */ index = addTriggerIndex(pkg, file, p, progArgv[0], scriptFlags, tag, priority); /* Generate the trigger tags */ if (parseRCPOT(spec, pkg, reqargs, reqtag, index, tagflags, addReqProvPkg, NULL)) goto exit; } else { struct rpmtd_s td; /* * XXX Ancient rpm uses STRING, not STRING_ARRAY type here. Construct * the td manually and preserve legacy compat for now... */ rpmtdReset(&td); td.tag = progtag; td.count = progArgc; if (progArgc == 1) { td.data = (void *) *progArgv; td.type = RPM_STRING_TYPE; } else { (void) rpmlibNeedsFeature(pkg, "ScriptletInterpreterArgs", "4.0.3-1"); td.data = progArgv; td.type = RPM_STRING_ARRAY_TYPE; } headerPut(pkg->header, &td, HEADERPUT_DEFAULT); if (*p != '\0') { headerPutString(pkg->header, tag, p); } if (scriptFlags) { headerPutUint32(pkg->header, flagtag, &scriptFlags, 1); } if (file) { switch (parsePart) { case PART_PRE: pkg->preInFile = xstrdup(file); break; case PART_POST: pkg->postInFile = xstrdup(file); break; case PART_PREUN: pkg->preUnFile = xstrdup(file); break; case PART_POSTUN: pkg->postUnFile = xstrdup(file); break; case PART_PRETRANS: pkg->preTransFile = xstrdup(file); break; case PART_POSTTRANS: pkg->postTransFile = xstrdup(file); break; case PART_PREUNTRANS: pkg->preunTransFile = xstrdup(file); break; case PART_POSTUNTRANS: pkg->postunTransFile = xstrdup(file); break; case PART_VERIFYSCRIPT: pkg->verifyFile = xstrdup(file); break; } } } res = nextPart; exit: free(reqargs); freeStringBuf(sb); free(progArgv); if (origprog != prog) free(origprog); free(prog); free(name); free(file); free(argv); poptFreeContext(optCon); return res; } rpm-software-management-rpm-3c1f23f/build/parseSimpleScript.cc000066400000000000000000000034501511627505500245470ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseBuildInstallClean.c * Parse %build/%install/%clean section from spec file. */ #include "system.h" #include #include "rpmbuild_internal.hh" #include "debug.h" int parseSimpleScript(rpmSpec spec, const char * name, StringBuf *sbp, ARGV_t *avp, int *modep) { int res = PART_ERROR; poptContext optCon = NULL; int argc; const char **argv = NULL; StringBuf *target = sbp; StringBuf buf = NULL; int rc, mode = PARSE_NONE; struct poptOption optionsTable[] = { { NULL, 'a', POPT_BIT_SET, &mode, PARSE_APPEND, NULL, NULL }, { NULL, 'p', POPT_BIT_SET, &mode, PARSE_PREPEND, NULL, NULL }, { NULL, 0, 0, NULL, 0, NULL, NULL } }; if ((rc = poptParseArgvString(spec->line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("line %d: Error parsing script: %s: %s\n"), spec->lineNum, spec->line, poptStrerror(rc)); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while ((rc = poptGetNextOpt(optCon)) > 0) {}; if (rc < -1) { rpmlog(RPMLOG_ERR, _("line %d: Bad %s option %s: %s\n"), spec->lineNum, argv[0], poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(rc)); goto exit; } if (*sbp != NULL && mode == PARSE_NONE) { rpmlog(RPMLOG_ERR, _("line %d: second %s\n"), spec->lineNum, name); goto exit; } if (mode == (PARSE_APPEND|PARSE_PREPEND)) { rpmlog(RPMLOG_ERR, _("line %d: append and prepend specified: %s\n"), spec->lineNum, spec->line); goto exit; } if (mode) { buf = newStringBuf(); target = &buf; } res = parseLines(spec, STRIP_NOTHING, NULL, target); if (buf) { argvAdd(avp, getStringBuf(buf)); freeStringBuf(buf); } exit: if (modep) *modep = mode; free(argv); poptFreeContext(optCon); return res; } rpm-software-management-rpm-3c1f23f/build/parseSpec.cc000066400000000000000000001134721511627505500230310ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/parseSpec.c * Top level dispatcher for spec file parsing. */ #include "system.h" #include #ifdef HAVE_ICONV #include #endif #include #include /* RPM_MACHTABLE & related */ #include #include #include #include #include "rpmbuild_internal.hh" #include "rpmbuild_misc.hh" #include "debug.h" #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; } #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; } #define ISMACRO(s,m,len) (rstreqn((s), (m), len) && !risalpha((s)[len])) #define ISMACROWITHARG(s,m,len) (rstreqn((s), (m), len) && (risblank((s)[len]) || !(s)[len])) static rpmRC parseSpecParts(rpmSpec spec, const char *pattern, enum parseStages stage); typedef struct OpenFileInfo { char * fileName; FILE *fp; int lineNum; char *readBuf; size_t readBufLen; const char * readPtr; struct OpenFileInfo * next; } OFI_t; static const struct PartRec { int part; int prebuildonly; size_t len; const char * token; } partList[] = { { PART_PREAMBLE, 0, LEN_AND_STR("%package")}, { PART_PREP, 1, LEN_AND_STR("%prep")}, { PART_BUILDREQUIRES, 1, LEN_AND_STR("%generate_buildrequires")}, { PART_CONF, 1, LEN_AND_STR("%conf")}, { PART_BUILD, 1, LEN_AND_STR("%build")}, { PART_INSTALL, 1, LEN_AND_STR("%install")}, { PART_CHECK, 1, LEN_AND_STR("%check")}, { PART_CLEAN, 1, LEN_AND_STR("%clean")}, { PART_PREUN, 0, LEN_AND_STR("%preun")}, { PART_POSTUN, 0, LEN_AND_STR("%postun")}, { PART_PRETRANS, 0, LEN_AND_STR("%pretrans")}, { PART_POSTTRANS, 0, LEN_AND_STR("%posttrans")}, { PART_PREUNTRANS, 0, LEN_AND_STR("%preuntrans")}, { PART_POSTUNTRANS, 0, LEN_AND_STR("%postuntrans")}, { PART_PRE, 0, LEN_AND_STR("%pre")}, { PART_POST, 0, LEN_AND_STR("%post")}, { PART_FILES, 0, LEN_AND_STR("%files")}, { PART_CHANGELOG, 0, LEN_AND_STR("%changelog")}, { PART_DESCRIPTION, 0, LEN_AND_STR("%description")}, { PART_TRIGGERPOSTUN, 0, LEN_AND_STR("%triggerpostun")}, { PART_TRIGGERPREIN, 0, LEN_AND_STR("%triggerprein")}, { PART_TRIGGERUN, 0, LEN_AND_STR("%triggerun")}, { PART_TRIGGERIN, 0, LEN_AND_STR("%triggerin")}, { PART_TRIGGERIN, 0, LEN_AND_STR("%trigger")}, { PART_VERIFYSCRIPT, 0, LEN_AND_STR("%verifyscript")}, { PART_POLICIES, 0, LEN_AND_STR("%sepolicy")}, { PART_FILETRIGGERIN, 0, LEN_AND_STR("%filetriggerin")}, { PART_FILETRIGGERIN, 0, LEN_AND_STR("%filetrigger")}, { PART_FILETRIGGERUN, 0, LEN_AND_STR("%filetriggerun")}, { PART_FILETRIGGERPOSTUN, 0, LEN_AND_STR("%filetriggerpostun")}, { PART_TRANSFILETRIGGERIN, 0, LEN_AND_STR("%transfiletriggerin")}, { PART_TRANSFILETRIGGERIN, 0, LEN_AND_STR("%transfiletrigger")}, { PART_TRANSFILETRIGGERUN, 0, LEN_AND_STR("%transfiletriggerun")}, { PART_TRANSFILETRIGGERPOSTUN, 0, LEN_AND_STR("%transfiletriggerpostun")}, { PART_EMPTY, 0, LEN_AND_STR("%end")}, { PART_PATCHLIST, 1, LEN_AND_STR("%patchlist")}, { PART_SOURCELIST, 1, LEN_AND_STR("%sourcelist")}, {0, 0, 0, 0} }; int isPart(const char *line) { const struct PartRec *p; for (p = partList; p->token != NULL; p++) { char c; if (rstrncasecmp(line, p->token, p->len)) continue; c = *(line + p->len); if (c == '\0' || risspace(c)) break; } return (p->token ? p->part : PART_NONE); } static const struct PartRec * getPart(int part) { const struct PartRec *p; for (p = partList; p->token != NULL; p++) { if (p->part == part) { return p; } } return NULL; } /** */ static int matchTok(const char *token, const char *line) { const char *b, *be = line; size_t toklen = strlen(token); int rc = 0; while ( *(b = be) != '\0' ) { SKIPSPACE(b); be = b; SKIPNONSPACE(be); if (be == b) break; if (toklen != (be-b) || rstrncasecmp(token, b, (be-b))) continue; rc = 1; break; } return rc; } int handleComments(char *s) { SKIPSPACE(s); if (*s == '#') { *s = '\0'; return 1; } return 0; } static void ofilineMacro(rpmMacroBuf mb, rpmMacroEntry me, ARGV_t margs, size_t *parsed) { OFI_t *ofi = (OFI_t *)rpmMacroEntryPriv(me); if (ofi) { char lnobuf[16]; snprintf(lnobuf, sizeof(lnobuf), "%d", ofi->lineNum); rpmMacroBufAppendStr(mb, lnobuf); } } /* Push a file to spec's file stack, return the newly pushed entry */ static OFI_t * pushOFI(rpmSpec spec, const char *fn) { OFI_t *ofi = new OpenFileInfo {}; ofi->fp = NULL; ofi->fileName = xstrdup(fn); ofi->lineNum = 0; ofi->readBufLen = BUFSIZ; ofi->readBuf = (char *)xmalloc(ofi->readBufLen); ofi->readBuf[0] = '\0'; ofi->readPtr = NULL; ofi->next = spec->fileStack; rpmPushMacroFlags(spec->macros, "__file_name", NULL, fn, RMIL_SPEC, RPMMACRO_LITERAL); rpmPushMacroAux(spec->macros, "__file_lineno", NULL, ofilineMacro, ofi, -1, 0, 0); spec->fileStack = ofi; return spec->fileStack; } /* Pop from spec's file stack */ static OFI_t * popOFI(rpmSpec spec) { if (spec->fileStack) { OFI_t * ofi = spec->fileStack; spec->fileStack = ofi->next; if (ofi->fp) fclose(ofi->fp); free(ofi->fileName); free(ofi->readBuf); delete ofi; rpmPopMacro(spec->macros, "__file_name"); rpmPopMacro(spec->macros, "__file_lineno"); } return spec->fileStack; } static int restoreFirstChar(rpmSpec spec) { /* Restore 1st char in (possible) next line */ if (spec->nextline != NULL && spec->nextpeekc != '\0') { *spec->nextline = spec->nextpeekc; spec->nextpeekc = '\0'; return 1; } return 0; } static parsedSpecLine parseLineType(char *line) { parsedSpecLine condition; for (condition = lineTypes; condition->text != NULL; condition++) { if (condition->withArgs) { if (ISMACROWITHARG(line, condition->text, condition->textLen)) return condition; } else { if (ISMACRO(line, condition->text, condition->textLen)) return condition; } } return NULL; } int specExpand(rpmSpec spec, int lineno, const char *sbuf, char **obuf) { return (rpmExpandMacros(spec->macros, sbuf, obuf, 0) < 0); } static int expandMacrosInSpecBuf(rpmSpec spec, int strip) { char *lbuf = NULL; int isComment = 0; parsedSpecLine condition; OFI_t *ofi = spec->fileStack; lbuf = spec->lbuf; SKIPSPACE(lbuf); if (lbuf[0] == '#') isComment = 1; condition = parseLineType(lbuf); if ((condition) && (!condition->withArgs)) { const char *s = lbuf + condition->textLen; SKIPSPACE(s); if (s[0] && s[0] != '#') { rpmlog(RPMLOG_ERR, _("extra tokens at the end of %s directive in line %d: %s\n"), condition->text, spec->lineNum, lbuf); return 1; } } /* Don't expand macros after %elif (resp. %elifarch, %elifos) in a false branch */ if (condition && (condition->id & LINE_ELIFANY)) { if (!spec->readStack->readable) return 0; /* Don't expand macros (eg. %define) in false branch of %if clause */ } else { if (!spec->readStack->reading) return 0; } if (specExpand(spec, ofi->lineNum, spec->lbuf, &lbuf)) return 1; if (strip & STRIP_COMMENTS && isComment) { char *bufA = spec->lbuf; char *bufB = lbuf; while (*bufA != '\0' && *bufB != '\0') { if (*bufA == '%' && *(bufA + 1) == '%') bufA++; if (*bufA != *bufB) break; bufA++; bufB++; } if (*bufA != '\0' || *bufB != '\0') rpmlog(RPMLOG_WARNING, _("Macro expanded in comment on line %d: %s\n"), spec->lineNum, bufA); } free(spec->lbuf); spec->lbuf = lbuf; spec->lbufSize = strlen(spec->lbuf) + 1; return 0; } /* Return zero on success, 1 if we need to read more and -1 on errors. */ static int copyNextLineFromOFI(rpmSpec spec, OFI_t *ofi, int strip) { /* Expand next line from file into line buffer */ if (!(spec->nextline && *spec->nextline)) { int pc = 0, bc = 0, xc = 0, nc = 0; const char *from = ofi->readPtr; char ch = ' '; while (from && *from && ch != '\n') { ch = spec->lbuf[spec->lbufOff] = *from; spec->lbufOff++; from++; if (spec->lbufOff >= spec->lbufSize) { spec->lbufSize += BUFSIZ; spec->lbuf = xrealloc(spec->lbuf, spec->lbufSize); } } spec->lbuf[spec->lbufOff] = '\0'; ofi->readPtr = from; /* Check if we need another line before expanding the buffer. */ for (const char *p = spec->lbuf; *p; p++) { switch (*p) { case '\\': switch (*(p+1)) { case '\n': p++, nc = 1; break; case '\0': break; default: p++; break; } break; case '\n': nc = 0; break; case '%': switch (*(p+1)) { case '{': p++, bc++; break; case '(': p++, pc++; break; case '[': p++, xc++; break; case '%': p++; break; } break; case '{': if (bc > 0) bc++; break; case '}': if (bc > 0) bc--; break; case '(': if (pc > 0) pc++; break; case ')': if (pc > 0) pc--; break; case '[': if (xc > 0) xc++; break; case ']': if (xc > 0) xc--; break; } } /* If it doesn't, ask for one more line. */ if (pc || bc || xc || nc ) { spec->nextline = NULL; return 1; } spec->lbufOff = 0; if (expandMacrosInSpecBuf(spec, strip)) return -1; spec->nextline = spec->lbuf; } return 0; } static parsedSpecLine copyNextLineFinish(rpmSpec spec, int strip) { char *last; char ch; char *s; parsedSpecLine lineType; /* Find next line in expanded line buffer */ s = spec->line = last = spec->nextline; ch = ' '; /* skip space until '\n' or non space is reached */ while (*(s) && (*s != '\n') && risspace(*(s))) (s)++; lineType = parseLineType(s); while (*spec->nextline && ch != '\n') { /* for conditionals and %include trim trailing '\' */ if (lineType && (*spec->nextline == '\\') && (*spec->nextline+1) && (*(spec->nextline+1) == '\n')) { *spec->nextline = ' '; *(spec->nextline+1) = ' '; } ch = *spec->nextline++; if (!risspace(ch)) last = spec->nextline; } /* Save 1st char of next line in order to terminate current line. */ if (*spec->nextline != '\0') { spec->nextpeekc = *spec->nextline; *spec->nextline = '\0'; } if (strip & STRIP_COMMENTS) handleComments(spec->line); if (strip & STRIP_TRAILINGSPACE) *last = '\0'; return lineType; } static int readLineFromOFI(rpmSpec spec, OFI_t *ofi) { retry: /* Make sure the current file is open */ if (ofi->fp == NULL) { ofi->fp = fopen(ofi->fileName, "r"); if (ofi->fp == NULL) { rpmlog(RPMLOG_ERR, _("Unable to open %s: %s\n"), ofi->fileName, strerror(errno)); return PART_ERROR; } spec->lineNum = ofi->lineNum = 0; } /* Make sure we have something in the read buffer */ if (!(ofi->readPtr && *(ofi->readPtr))) { if (getline(&ofi->readBuf, &ofi->readBufLen, ofi->fp) <= 0) { /* EOF, remove this file from the stack */ ofi = popOFI(spec); /* only on last file do we signal EOF to caller */ if (ofi == NULL) return 1; /* otherwise, go back and try the read again. */ goto retry; } ofi->readPtr = ofi->readBuf; ofi->lineNum++; spec->lineNum = ofi->lineNum; } return 0; } #define ARGMATCH(s,token,match) \ do { \ char *os = s; \ char *exp = rpmExpand(token, NULL); \ while (*s && !risblank(*s)) s++; \ while (*s && risblank(*s)) s++; \ if (!*s) { \ rpmlog(RPMLOG_ERR, _("%s:%d: Argument expected for %s\n"), ofi->fileName, ofi->lineNum, os); \ free(exp); \ return PART_ERROR; \ } \ match = matchTok(exp, s); \ free(exp); \ } while (0) int readLine(rpmSpec spec, int strip) { char *s; int match = 0; struct ReadLevelEntry *rl; OFI_t *ofi = spec->fileStack; int rc; int startLine = 0; parsedSpecLine lineType; int prevType = spec->readStack->lastConditional->id; int checkCondition; if (!restoreFirstChar(spec)) { retry: if ((rc = readLineFromOFI(spec, ofi)) != 0) { if (spec->readStack->next) { rpmlog(RPMLOG_ERR, _("line %d: Unclosed %%if\n"), spec->readStack->lineNum); rc = PART_ERROR; } else if (startLine > 0) { rpmlog(RPMLOG_ERR, _("line %d: unclosed macro or bad line continuation\n"), startLine); rc = PART_ERROR; } return rc; } ofi = spec->fileStack; /* Copy next file line into the spec line buffer */ rc = copyNextLineFromOFI(spec, ofi, strip); if (rc > 0) { if (startLine == 0) startLine = spec->lineNum; goto retry; } else if (rc < 0) { return PART_ERROR; } } lineType = copyNextLineFinish(spec, strip); s = spec->line; SKIPSPACE(s); if (!lineType) goto after_classification; /* check ordering of the conditional */ if (lineType->isConditional && (prevType & lineType->wrongPrecursors)) { if (prevType == LINE_ENDIF) { rpmlog(RPMLOG_ERR,_("%s: line %d: %s with no %%if\n"), ofi->fileName, ofi->lineNum, lineType->text); } else { rpmlog(RPMLOG_ERR,_("%s: line %d: %s after %s\n"), ofi->fileName, ofi->lineNum, lineType->text, spec->readStack->lastConditional->text); } return PART_ERROR; } if (lineType->id & (LINE_IFARCH | LINE_ELIFARCH)) { ARGMATCH(s, "%{_target_cpu}", match); } else if (lineType->id == LINE_IFNARCH) { ARGMATCH(s, "%{_target_cpu}", match); match = !match; } else if (lineType->id & (LINE_IFOS | LINE_ELIFOS)) { ARGMATCH(s, "%{_target_os}", match); } else if (lineType->id == LINE_IFNOS) { ARGMATCH(s, "%{_target_os}", match); match = !match; } else if (lineType->id & (LINE_IF | LINE_ELIF)) { s += lineType->textLen; if (lineType->id == LINE_IF) checkCondition = spec->readStack->reading; else checkCondition = spec->readStack->readable; if (checkCondition) { match = rpmExprBoolFlags(s, 0); if (match < 0) { rpmlog(RPMLOG_ERR, _("%s:%d: bad %s condition: %s\n"), ofi->fileName, ofi->lineNum, lineType->text, s); return PART_ERROR; } } } else if (lineType->id == LINE_ELSE) { spec->readStack->lastConditional = lineType; spec->readStack->reading = spec->readStack->next->reading && spec->readStack->readable; spec->line[0] = '\0'; } else if (lineType->id == LINE_ENDIF) { rl = spec->readStack; spec->readStack = spec->readStack->next; delete rl; spec->line[0] = '\0'; } else if (spec->readStack->reading && (lineType->id == LINE_INCLUDE)) { char *fileName, *endFileName, *p; fileName = s+8; SKIPSPACE(fileName); endFileName = fileName; do { SKIPNONSPACE(endFileName); p = endFileName; SKIPSPACE(p); if (*p != '\0') endFileName = p; } while (*p != '\0'); if (*fileName == '\0') { rpmlog(RPMLOG_ERR, _("%s:%d: malformed %%include statement\n"), ofi->fileName, ofi->lineNum); return PART_ERROR; } *endFileName = '\0'; ofi = pushOFI(spec, fileName); goto retry; } if (lineType->id & LINE_IFANY) { rl = new ReadLevelEntry {}; rl->reading = spec->readStack->reading && match; rl->next = spec->readStack; rl->lineNum = ofi->lineNum; rl->readable = (!rl->reading) && (spec->readStack->reading); rl->lastConditional = lineType; spec->readStack = rl; spec->line[0] = '\0'; } else if (lineType->id & LINE_ELIFANY) { spec->readStack->reading = spec->readStack->readable && match; if (spec->readStack->reading) spec->readStack->readable = 0; spec->line[0] = '\0'; } after_classification: if (! spec->readStack->reading) { spec->line[0] = '\0'; } /* Collect parsed line */ if (spec->parsed == NULL) spec->parsed = newStringBuf(); appendStringBufAux(spec->parsed, spec->line,(strip & STRIP_TRAILINGSPACE)); /* FIX: spec->readStack->next should be dependent */ return 0; } int parseLines(rpmSpec spec, int strip, ARGV_t *avp, StringBuf *sbp) { int rc, nextPart = PART_ERROR; int nl = (strip & STRIP_TRAILINGSPACE); if ((rc = readLine(spec, strip)) > 0) { nextPart = PART_NONE; goto exit; } else if (rc < 0) { goto exit; } if (sbp && *sbp == NULL) *sbp = newStringBuf(); while (! (nextPart = isPart(spec->line))) { /* HACK: Emit a legible error on the obsolete %patchN form for now */ if (sbp == &(spec->sections[SECT_PREP])) { size_t slen = strlen(spec->line); if (slen >= 7 && risdigit(spec->line[6]) && rstreqn(spec->line, "%patch", 6)) { rpmlog(RPMLOG_ERR, _("%%patchN is obsolete, " "use %%patch N (or %%patch -P N): %s\n"), spec->line); nextPart = PART_ERROR; break; } } if (avp) argvAdd(avp, spec->line); if (sbp) appendStringBufAux(*sbp, spec->line, nl); if ((rc = readLine(spec, strip)) > 0) { nextPart = PART_NONE; break; } else if (rc < 0) { nextPart = PART_ERROR; break; } } exit: return nextPart; } void closeSpec(rpmSpec spec) { while (popOFI(spec)) {}; } static const rpmTagVal sourceTags[] = { RPMTAG_NAME, RPMTAG_VERSION, RPMTAG_RELEASE, RPMTAG_EPOCH, RPMTAG_SUMMARY, RPMTAG_DESCRIPTION, RPMTAG_PACKAGER, RPMTAG_DISTRIBUTION, RPMTAG_DISTURL, RPMTAG_DISTTAG, RPMTAG_VENDOR, RPMTAG_LICENSE, RPMTAG_GROUP, RPMTAG_OS, RPMTAG_ARCH, RPMTAG_CHANGELOGTIME, RPMTAG_CHANGELOGNAME, RPMTAG_CHANGELOGTEXT, RPMTAG_URL, RPMTAG_BUGURL, RPMTAG_TRANSLATIONURL, RPMTAG_UPSTREAMRELEASES, RPMTAG_HEADERI18NTABLE, RPMTAG_VCS, RPMTAG_MODULARITYLABEL, 0 }; static int initSourceHeader(rpmSpec spec) { Package sourcePkg = spec->sourcePackage; struct Source *srcPtr; int rc = 0; if (headerIsEntry(sourcePkg->header, RPMTAG_NAME)) return rc; char *os = rpmExpand("%{_target_os}", NULL); headerPutString(sourcePkg->header, RPMTAG_OS, os); free(os); /* Only specific tags are added to the source package header */ headerCopyTags(spec->packages->header, sourcePkg->header, sourceTags); /* Add the build restrictions */ for (int i=0; idependencies[i], sourcePkg->header); } { HeaderIterator hi = headerInitIterator(spec->buildRestrictions); struct rpmtd_s td; while (headerNext(hi, &td)) { if (rpmtdCount(&td) > 0) { (void) headerPut(sourcePkg->header, &td, HEADERPUT_DEFAULT); } rpmtdFreeData(&td); } headerFreeIterator(hi); } if (spec->BANames && spec->BACount > 0) { headerPutStringArray(sourcePkg->header, RPMTAG_BUILDARCHS, spec->BANames, spec->BACount); } /* Add tags for sources and patches */ for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) { char *fn = rpmExpand(srcPtr->source, NULL); if (srcPtr->flags & RPMBUILD_ISSOURCE) { headerPutString(sourcePkg->header, RPMTAG_SOURCE, fn); if (srcPtr->flags & RPMBUILD_ISNO) { headerPutUint32(sourcePkg->header, RPMTAG_NOSOURCE, &srcPtr->num, 1); } } if (srcPtr->flags & RPMBUILD_ISPATCH) { headerPutString(sourcePkg->header, RPMTAG_PATCH, fn); if (srcPtr->flags & RPMBUILD_ISNO) { headerPutUint32(sourcePkg->header, RPMTAG_NOPATCH, &srcPtr->num, 1); } } free(fn); } if (spec->sourceRpmName == NULL) { char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR); rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr, spec->noSource ? "no" : ""); free(nvr); } rc = checkForRequired(spec->sourcePackage->header); return rc; } static void finalizeSourceHeader(rpmSpec spec) { uint32_t one = 1; /* Only specific tags are added to the source package header */ headerPutUint32(spec->sourcePackage->header, RPMTAG_SOURCEPACKAGE, &one, 1); headerCopyTags(spec->packages->header, spec->sourcePackage->header, sourceTags); /* Provide all package NEVRs that would be built */ for (Package p = spec->packages; p != NULL; p = p->next) { if (p->fileList) { Header h = spec->sourcePackage->header; uint32_t dsflags = rpmdsFlags(p->ds); headerPutString(h, RPMTAG_PROVIDENAME, rpmdsN(p->ds)); headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &dsflags, 1); headerPutString(h, RPMTAG_PROVIDEVERSION, rpmdsEVR(p->ds)); } } } /* Add extra provides to package. */ void addPackageProvides(Package pkg) { const char *arch, *name; char *evr, *isaprov; rpmsenseFlags pflags = RPMSENSE_EQUAL; /* = provide */ name = headerGetString(pkg->header, RPMTAG_NAME); arch = headerGetString(pkg->header, RPMTAG_ARCH); evr = headerGetAsString(pkg->header, RPMTAG_EVR); addReqProv(pkg, RPMTAG_PROVIDENAME, name, evr, pflags, 0); /* * () = provide * FIXME: noarch needs special casing for now as BuildArch: noarch doesn't * cause reading in the noarch macros :-/ */ isaprov = rpmExpand(name, "%{?_isa}", NULL); if (!rstreq(arch, "noarch") && !rstreq(name, isaprov)) { addReqProv(pkg, RPMTAG_PROVIDENAME, isaprov, evr, pflags, 0); } free(isaprov); free(evr); } static void addArch(rpmSpec spec) { char *arch = rpmExpand("%{_target_cpu}", NULL); for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { /* noarch subpackages already have arch set here, leave it alone */ if (!headerIsEntry(pkg->header, RPMTAG_ARCH)) { headerPutString(pkg->header, RPMTAG_ARCH, arch); } } free(arch); } rpmRC checkForEncoding(Header h, int addtag) { rpmRC rc = RPMRC_OK; #ifdef HAVE_ICONV const char *encoding = "utf-8"; rpmTagVal tag; iconv_t ic; char *dest = NULL; size_t destlen = 0; int strict = rpmExpandNumeric("%{_invalid_encoding_terminates_build}"); HeaderIterator hi = headerInitIterator(h); ic = iconv_open(encoding, encoding); if (ic == (iconv_t) -1) { rpmlog(RPMLOG_WARNING, _("encoding %s not supported by system\n"), encoding); goto exit; } while ((tag = headerNextTag(hi)) != RPMTAG_NOT_FOUND) { struct rpmtd_s td; const char *src = NULL; if (rpmTagGetClass(tag) != RPM_STRING_CLASS) continue; headerGet(h, tag, &td, (HEADERGET_RAW|HEADERGET_MINMEM)); while ((src = rpmtdNextString(&td)) != NULL) { size_t srclen = strlen(src); size_t outlen, inlen = srclen; char *out, *in = (char *) src; if (destlen < srclen) { destlen = srclen * 2; dest = xrealloc(dest, destlen); } out = dest; outlen = destlen; /* reset conversion state */ iconv(ic, NULL, &inlen, &out, &outlen); if (iconv(ic, &in, &inlen, &out, &outlen) == (size_t) -1) { rpmlog(strict ? RPMLOG_ERR : RPMLOG_WARNING, _("Package %s: invalid %s encoding in %s: %s - %s\n"), headerGetString(h, RPMTAG_NAME), encoding, rpmTagGetName(tag), src, strerror(errno)); rc = RPMRC_FAIL; } } rpmtdFreeData(&td); } /* Stomp "known good utf" mark in header if requested */ if (rc == RPMRC_OK && addtag) headerPutString(h, RPMTAG_ENCODING, encoding); if (!strict) rc = RPMRC_OK; exit: if (ic != (iconv_t) -1) iconv_close(ic); headerFreeIterator(hi); free(dest); #endif /* HAVE_ICONV */ return rc; } static int parseEmpty(rpmSpec spec, int prevParsePart) { int res = PART_ERROR; int nextPart, rc; char *line; if (prevParsePart != PART_NONE) { line = spec->line + sizeof("%end") - 1; SKIPSPACE(line); if (line[0] != '\0') { rpmlog(RPMLOG_ERR, _("line %d: %%end doesn't take any arguments: %s\n"), spec->lineNum, spec->line); goto exit; } } if (prevParsePart == PART_EMPTY) { rpmlog(RPMLOG_ERR, _("line %d: %%end not expected here, no section to close: %s\n"), spec->lineNum, spec->line); goto exit; } if ((rc = readLine(spec, STRIP_TRAILINGSPACE|STRIP_COMMENTS)) > 0) { nextPart = PART_NONE; } else if (rc < 0) { goto exit; } else { while (! (nextPart = isPart(spec->line))) { line = spec->line; SKIPSPACE(line); if (line[0] != '\0') { rpmlog(RPMLOG_ERR, _("line %d doesn't belong to any section: %s\n"), spec->lineNum, spec->line); goto exit; } if ((rc = readLine(spec, STRIP_TRAILINGSPACE|STRIP_COMMENTS)) > 0) { nextPart = PART_NONE; break; } else if (rc < 0) { goto exit; } } } res = nextPart; exit: return res; } static struct sectname_s sectList[] = { { "prep", SECT_PREP, PART_PREP, 0 }, { "conf", SECT_CONF, PART_CONF, 0 }, { "generate_buildrequires", SECT_BUILDREQUIRES, PART_BUILDREQUIRES, 0 }, { "build", SECT_BUILD, PART_BUILD, 1 }, { "install", SECT_INSTALL, PART_INSTALL, 1 }, { "check", SECT_CHECK, PART_CHECK, 0 }, { "clean", SECT_CLEAN, PART_CLEAN, 0 }, { NULL, -1, -1, 0 } }; const struct sectname_s *getSection(const char *name, int part) { for (const struct sectname_s *sc = sectList; sc->name; sc++) { if (name && rstreq(name, sc->name)) return sc; if (part && part == sc->part) return sc; } return NULL; } int checkBuildsystem(rpmSpec spec, const char *name) { if (rpmCharCheck(spec, name, ALLOWED_CHARS_NAME, ALLOWED_FIRSTCHARS_NAME)) return -1; int rc = 0; int found = 0; char * mmn = NULL; for (struct sectname_s *sc = sectList; sc->name; sc++) { char *mn = rstrscat(NULL, "buildsystem_", name, "_", sc->name, NULL); if (!rpmMacroIsParametric(NULL, mn)) { if (sc->required) { if (!mmn) mmn = xstrdup(mn); rc = -1; } } else { found = 1; } free(mn); } if (!found) { rpmlog(RPMLOG_ERR, _("line %d: Unknown buildsystem: %s\n"), spec->lineNum, name); } else if (mmn) { rpmlog(RPMLOG_ERR, _("line %d: Required parametric macro %%%s not defined for buildsystem %s\n"), spec->lineNum, mmn, name); } free(mmn); return rc; } static rpmRC parseBuildsysSect(rpmSpec spec, const char *prefix, struct sectname_s *sc, FD_t fd, int *foundp) { rpmRC rc = RPMRC_OK; if (spec->sections[sc->section] == NULL) { char *mn = rstrscat(NULL, "buildsystem_", prefix, "_", sc->name, NULL); if (rpmMacroIsParametric(NULL, mn)) { char *args = NULL; if (spec->buildopts[sc->section]) { ARGV_t av = NULL; argvAdd(&av, "--"); argvAppend(&av, spec->buildopts[sc->section]); args = argvJoin(av, " "); argvFree(av); } char *buf = rstrscat(NULL, "%", sc->name, "\n", "%", mn, " ", args ? args : "", "\n", NULL); size_t blen = strlen(buf); if (Fwrite(buf, blen, 1, fd) < blen) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("failed to write buildsystem %s %%%s: %s\n"), prefix, sc->name, strerror(errno)); } free(buf); free(args); *foundp = 1; } free(mn); } return rc; } static rpmRC parseBuildsystem(rpmSpec spec) { rpmRC rc = RPMRC_OK; char *buildsystem = rpmExpand("%{?buildsystem}", NULL); if (*buildsystem) { char *path = NULL; FD_t fd = rpmMkTempFile(NULL, &path); if (fd == NULL) { rpmlog(RPMLOG_ERR, _("failed to create temp file for buildsystem: %s\n"), strerror(errno)); rc = RPMRC_FAIL; goto exit; } for (struct sectname_s *sc = sectList; !rc && sc->name; sc++) { int found = 0; rc = parseBuildsysSect(spec, buildsystem, sc, fd, &found); if (!rc && !found) rc = parseBuildsysSect(spec, "default", sc, fd, &found); } if (!rc) rc = parseSpecParts(spec, path, PARSE_BUILDSYS); if (!rc) unlink(path); Fclose(fd); free(path); } exit: free(buildsystem); return rc; } static rpmRC applyAppendPrepend(rpmSpec spec) { for (struct sectname_s *sc = sectList; sc->name; sc++) { ARGV_const_t sp = spec->sectionparts[sc->section]; if (sp) { int nparts = argvCount(sp); int *modes = argiData(spec->sectionops[sc->section]); StringBuf *sbp = &spec->sections[sc->section]; if (*sbp == NULL) *sbp = newStringBuf(); for (int i = 0; i < nparts; i++) { if (modes[i] == PARSE_APPEND) { appendStringBuf(*sbp, sp[i]); } else if (modes[i] == PARSE_PREPEND) { StringBuf nbuf = newStringBuf(); appendStringBuf(nbuf, sp[i]); appendStringBuf(nbuf, getStringBuf(*sbp)); freeStringBuf(*sbp); *sbp = nbuf; } } } } return RPMRC_OK; } static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags, const char *buildRoot, int recursing); /* is part allowed at this stage */ static int checkPart(int parsePart, enum parseStages stage) { if (stage == PARSE_GENERATED) { const struct PartRec *p = getPart(parsePart); if (p && p->prebuildonly ) { rpmlog(RPMLOG_ERR, _("Section %s is not allowed after build is done!\n"), p->token); return 1; } } return 0; } static int parseBuildScript(rpmSpec spec, int part) { const struct sectname_s *sc = getSection(NULL, part); if (sc == NULL) /* can't happen */ return -1; int mode = 0; int rc = parseSimpleScript(spec, sc->name, &spec->sections[sc->section], &spec->sectionparts[sc->section], &mode); if (mode) { int ix = argvCount(spec->sectionparts[sc->section]); argiAdd(&spec->sectionops[sc->section], ix-1, mode); } return rc; } static rpmRC parseSpecSection(rpmSpec *specptr, enum parseStages stage) { rpmSpec spec = *specptr; int parsePart = PART_PREAMBLE; int prevParsePart = PART_EMPTY; int storedParsePart; int initialPackage = 1; /* All the parse*() functions expect to have a line pre-read */ /* in the spec's line buffer. Except for parsePreamble(), */ /* which handles the initial entry into a spec file. */ while (parsePart != PART_NONE) { int goterror = 0; storedParsePart = parsePart; switch (parsePart) { case PART_ERROR: /* fallthrough */ default: goterror = 1; break; case PART_EMPTY: parsePart = parseEmpty(spec, prevParsePart); break; case PART_PREAMBLE: parsePart = parsePreamble(spec, initialPackage, stage); initialPackage = 0; break; case PART_PATCHLIST: parsePart = parseList(spec, "%patchlist", RPMTAG_PATCH); break; case PART_SOURCELIST: parsePart = parseList(spec, "%sourcelist", RPMTAG_SOURCE); break; case PART_PREP: rpmPushMacroAux(NULL, "setup", "-", doSetupMacro, spec, -1, 0, 0); rpmPushMacroAux(NULL, "patch", "-", doPatchMacro, spec, -1, 0, 0); parsePart = parseBuildScript(spec, parsePart); rpmPopMacro(NULL, "patch"); rpmPopMacro(NULL, "setup"); break; case PART_CONF: case PART_BUILDREQUIRES: case PART_BUILD: case PART_INSTALL: case PART_CHECK: case PART_CLEAN: parsePart = parseBuildScript(spec, parsePart); break; case PART_CHANGELOG: parsePart = parseChangelog(spec); break; case PART_DESCRIPTION: parsePart = parseDescription(spec); break; case PART_PRE: case PART_POST: case PART_PREUN: case PART_POSTUN: case PART_PRETRANS: case PART_POSTTRANS: case PART_PREUNTRANS: case PART_POSTUNTRANS: case PART_VERIFYSCRIPT: case PART_TRIGGERPREIN: case PART_TRIGGERIN: case PART_TRIGGERUN: case PART_TRIGGERPOSTUN: case PART_FILETRIGGERIN: case PART_FILETRIGGERUN: case PART_FILETRIGGERPOSTUN: case PART_TRANSFILETRIGGERIN: case PART_TRANSFILETRIGGERUN: case PART_TRANSFILETRIGGERPOSTUN: parsePart = parseScript(spec, parsePart); break; case PART_FILES: parsePart = parseFiles(spec); break; case PART_POLICIES: parsePart = parsePolicies(spec); break; case PART_NONE: /* XXX avoid gcc whining */ case PART_LAST: case PART_BUILDARCHITECTURES: break; } prevParsePart = storedParsePart; if (goterror || parsePart >= PART_LAST) { goto errxit; } if (checkPart(parsePart, stage)) { goto errxit; } if (parsePart == PART_BUILDARCHITECTURES) { int index; int x; closeSpec(spec); spec->BASpecs = (rpmSpec *)xcalloc(spec->BACount, sizeof(*spec->BASpecs)); index = 0; if (spec->BANames != NULL) for (x = 0; x < spec->BACount; x++) { /* Skip if not arch is not compatible. */ if (!rpmMachineScore(RPM_MACHTABLE_BUILDARCH, spec->BANames[x])) continue; rpmPushMacro(NULL, "_target_cpu", NULL, spec->BANames[x], RMIL_RPMRC); spec->BASpecs[index] = parseSpec(spec->specFile, spec->flags, spec->buildRoot, 1); if (spec->BASpecs[index] == NULL) { spec->BACount = index; goto errxit; } rpmPopMacro(NULL, "_target_cpu"); index++; } spec->BACount = index; if (! index) { rpmlog(RPMLOG_ERR, _("No compatible architectures found for build\n")); goto errxit; } /* * Return the 1st child's fully parsed Spec structure. * The restart of the parse when encountering BuildArch * causes problems for "rpm -q --specfile". This is * still a hack because there may be more than 1 arch * specified (unlikely but possible.) There's also the * further problem that the macro context, particularly * %{_target_cpu}, disagrees with the info in the header. */ if (spec->BACount >= 1) { rpmSpec nspec = spec->BASpecs[0]; spec->BASpecs = _free(spec->BASpecs); rpmSpecFree(spec); *specptr = spec = nspec; } goto exit; } } if (stage == PARSE_SPECFILE) { if (parseBuildsystem(spec)) goto errxit; if (applyAppendPrepend(spec)) goto errxit; } /* Add arch for each package */ addArch(spec); /* Check for encoding in each package unless disabled */ if (!(spec->flags & RPMSPEC_NOUTF8)) { int badenc = 0; for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { if (checkForEncoding(pkg->header, 0) != RPMRC_OK) { badenc = 1; } } if (badenc) goto errxit; } closeSpec(spec); exit: return RPMRC_OK; errxit: return RPMRC_FAIL; } static rpmSpec parseSpec(const char *specFile, rpmSpecFlags flags, const char *buildRoot, int recursing) { rpmSpec spec; /* Set up a new Spec structure with no packages. */ spec = newSpec(); spec->specFile = rpmGetPath(specFile, NULL); pushOFI(spec, spec->specFile); /* If explicit --buildroot was passed, grab hold of it */ if (buildRoot) spec->buildRoot = xstrdup(buildRoot); /* Grab top builddir on first entry as we'll override _builddir */ if (!rpmMacroIsDefined(spec->macros, "_top_builddir")) { char *top_builddir = rpmExpand("%{_builddir}", NULL); rpmPushMacroFlags(spec->macros, "_top_builddir", NULL, top_builddir, RMIL_GLOBAL, RPMMACRO_LITERAL); /* Undefine (!!) %_builddir so %global misuses fall through */ while (rpmMacroIsDefined(spec->macros, "_builddir")) rpmPopMacro(spec->macros, "_builddir"); free(top_builddir); } rpmPushMacro(NULL, "_docdir", NULL, "%{_defaultdocdir}", RMIL_SPEC); rpmPushMacro(NULL, "_licensedir", NULL, "%{_defaultlicensedir}", RMIL_SPEC); spec->recursing = recursing; spec->flags = flags; if (parseSpecSection(&spec, PARSE_SPECFILE) != RPMRC_OK) goto errxit; /* Assemble source header from parsed components */ if (initSourceHeader(spec)) goto errxit; return spec; errxit: spec = rpmSpecFree(spec); return NULL; } static rpmRC finalizeSpec(rpmSpec spec) { rpmRC rc = RPMRC_FAIL; char *platform = rpmExpand("%{_target_platform}", NULL); char *os = rpmExpand("%{_target_os}", NULL); char *optflags = rpmExpand("%{optflags}", NULL); fillOutMainPackage(spec->packages->header); /* Define group tag to something when group is undefined in main package*/ if (!headerIsEntry(spec->packages->header, RPMTAG_GROUP)) { headerPutString(spec->packages->header, RPMTAG_GROUP, "Unspecified"); } spec->rpmformat = rpmExpandNumeric("%{?_rpmformat}"); if (spec->rpmformat != 4 && spec->rpmformat != 6) { int default_rpmformat = 4; rpmlog(RPMLOG_WARNING, _("invalid rpm format %d requested, using %d\n"), spec->rpmformat, default_rpmformat); spec->rpmformat = 4; } if (spec->rpmformat >= 6) { char *nevr = headerGetAsString(spec->sourcePackage->header, RPMTAG_NEVR); headerPutString(spec->packages->header, RPMTAG_SOURCENEVR, nevr); free(nevr); } for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { headerPutString(pkg->header, RPMTAG_OS, os); headerPutString(pkg->header, RPMTAG_PLATFORM, platform); headerPutString(pkg->header, RPMTAG_OPTFLAGS, optflags); headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName); if (pkg != spec->packages) { copyInheritedTags(pkg->header, spec->packages->header); } /* Add manual dependencies early for rpmspec etc to look at */ addPackageProvides(pkg); for (int i=0; idependencies[i], pkg->header); } pkg->ds = rpmdsThis(pkg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL); if (checkForRequired(pkg->header)) { goto exit; } if (!headerIsEntry(pkg->header, RPMTAG_DESCRIPTION)) { rpmlog(RPMLOG_ERR, _("Package has no %%description: %s\n"), headerGetString(pkg->header, RPMTAG_NAME)); goto exit; } if (checkForDuplicates(pkg->header)) { goto exit; } } finalizeSourceHeader(spec); rc = RPMRC_OK; exit: free(platform); free(os); free(optflags); return rc; } rpmSpec rpmSpecParse(const char *specFile, rpmSpecFlags flags, const char *buildRoot) { rpmSpec spec = parseSpec(specFile, flags, buildRoot, 0); if (spec && !(flags & RPMSPEC_NOFINALIZE)) { finalizeSpec(spec); } return spec; } static rpmRC parseSpecParts(rpmSpec spec, const char *pattern, enum parseStages stage) { ARGV_t argv = NULL; int argc = 0; rpmRC rc = RPMRC_OK; /* rpmGlob returns files sorted */ if (rpmGlob(pattern, &argc, &argv) == 0) { for (int i = 0; i < argc; i++) { rpmlog(RPMLOG_INFO, "Reading %s\n", argv[i]); pushOFI(spec, argv[i]); snprintf(spec->fileStack->readBuf, spec->fileStack->readBufLen, "# Spec part read from %s\n\n", argv[i]); if (parseSpecSection(&spec, stage) != RPMRC_OK) { rpmlog(RPMLOG_ERR, "parsing failed\n"); rc = RPMRC_FAIL; break; } } argvFree(argv); } return rc; } rpmRC parseGeneratedSpecs(rpmSpec spec) { char * specPattern = rpmGenPath("%{specpartsdir}", NULL, "*.specpart"); rpmRC rc = parseSpecParts(spec, specPattern, PARSE_GENERATED); free(specPattern); if (!rc) { rc = finalizeSpec(spec); if (rc != RPMRC_OK) { rpmlog(RPMLOG_ERR, "parsing failed\n"); } } return rc; } rpm-software-management-rpm-3c1f23f/build/policies.cc000066400000000000000000000170041511627505500227050ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/policies.c * The post-build, packaging of policies */ #include "system.h" #include #include #include #include #include "rpmio_internal.hh" #include "rpmbuild_internal.hh" #include "debug.h" #ifdef WITH_SELINUX enum rpmpolFlags_e { RPMPOL_FLAG_NONE = 0, RPMPOL_FLAG_BASE = (1 << 0) }; typedef rpmFlags rpmpolFlags; #define RPMPOL_TYPE_DEFAULT "default" typedef struct ModuleRec_s { char *path; char *data; char *name; ARGV_t types; uint32_t flags; } *ModuleRec; static rpmRC writeModuleToHeader(ModuleRec mod, Package pkg) { rpmRC rc = RPMRC_FAIL; ARGV_t av; uint32_t count; struct rpmtd_s policies; struct rpmtd_s policynames; struct rpmtd_s policyflags; struct rpmtd_s policytypes; struct rpmtd_s policytypesidx; rpmtdReset(&policies); rpmtdReset(&policynames); rpmtdReset(&policyflags); rpmtdReset(&policytypes); rpmtdReset(&policytypesidx); if (!mod || !pkg) { goto exit; } /* check for duplicates */ if (headerIsEntry(pkg->header, RPMTAG_POLICYNAMES)) { int typeCount; const char *name; char *type; int i; int idx; headerGet(pkg->header, RPMTAG_POLICYNAMES, &policynames, HEADERGET_MINMEM); headerGet(pkg->header, RPMTAG_POLICYFLAGS, &policyflags, HEADERGET_ARGV | HEADERGET_MINMEM); headerGet(pkg->header, RPMTAG_POLICYTYPES, &policytypes, HEADERGET_ARGV | HEADERGET_MINMEM); headerGet(pkg->header, RPMTAG_POLICYTYPESINDEXES, &policytypesidx, HEADERGET_ARGV | HEADERGET_MINMEM); typeCount = rpmtdCount(&policytypes); while ((name = rpmtdNextString(&policynames))) { int overlappingtypes = 0; idx = rpmtdGetIndex(&policynames); for (i = 0; i < typeCount; i++) { if (((int *) policytypesidx.data)[i] != idx) { continue; } type = ((char **) policytypes.data)[i]; if (rstreq(type, RPMPOL_TYPE_DEFAULT) || argvSearch(mod->types, type, NULL) || argvSearch(mod->types, RPMPOL_TYPE_DEFAULT, NULL)) { overlappingtypes = 1; break; } } if (!overlappingtypes) { continue; } if (rstreq(mod->name, name)) { rpmlog(RPMLOG_ERR, _("Policy module '%s' duplicated with overlapping types\n"), name); goto exit; } if ((mod->flags && RPMPOL_FLAG_BASE) && (((int *) policyflags.data)[idx] & RPMPOL_FLAG_BASE)) { rpmlog(RPMLOG_ERR, _("Base modules '%s' and '%s' have overlapping types\n"), mod->name, name); goto exit; } } } if (headerIsEntry(pkg->header, RPMTAG_POLICIES)) { if (!headerGet(pkg->header, RPMTAG_POLICIES, &policies, HEADERGET_MINMEM)) { rpmlog(RPMLOG_ERR, _("Failed to get policies from header\n")); goto exit; } count = rpmtdCount(&policies); } else { count = 0; } /* save everything to the header */ headerPutString(pkg->header, RPMTAG_POLICIES, mod->data); headerPutString(pkg->header, RPMTAG_POLICYNAMES, mod->name); headerPutUint32(pkg->header, RPMTAG_POLICYFLAGS, &mod->flags, 1); for (av = mod->types; av && *av; av++) { headerPutString(pkg->header, RPMTAG_POLICYTYPES, *av); headerPutUint32(pkg->header, RPMTAG_POLICYTYPESINDEXES, &count, 1); } rc = RPMRC_OK; exit: rpmtdFreeData(&policies); rpmtdFreeData(&policynames); rpmtdFreeData(&policyflags); rpmtdFreeData(&policytypes); rpmtdFreeData(&policytypesidx); return rc; } static ModuleRec freeModule(ModuleRec mod) { if (mod) { _free(mod->path); _free(mod->data); _free(mod->name); argvFree(mod->types); delete mod; } return NULL; } static ModuleRec newModule(const char *path, const char *name, const char *types, uint32_t flags) { ModuleRec mod; uint8_t *raw = NULL; ssize_t rawlen = 0; const char *buildDir = "%{builddir}/%{?buildsubdir}/"; if (!path) { rpmlog(RPMLOG_ERR, _("%%semodule requires a file path\n")); return NULL; } mod = new ModuleRec_s {}; mod->path = rpmGenPath(buildDir, NULL, path); if ((rpmioSlurp(mod->path, &raw, &rawlen)) != 0 || raw == NULL) { rpmlog(RPMLOG_ERR, _("Failed to read policy file: %s\n"), mod->path); goto err; } mod->data = rpmBase64Encode(raw, rawlen, -1); if (!mod->data) { rpmlog(RPMLOG_ERR, _("Failed to encode policy file: %s\n"), mod->path); goto err; } if (name) { mod->name = xstrdup(name); } else { /* assume base name (minus extension) if name is not given */ char *tmp = xstrdup(mod->path); char *bname = basename(tmp); char *end = strchr(bname, '.'); if (end) *end = '\0'; if (strlen(bname) > 0) { mod->name = xstrdup(bname); } else { rpmlog(RPMLOG_ERR, _("Failed to determine a policy name: %s\n"), mod->path); _free(tmp); goto err; } _free(tmp); } if (types) { mod->types = argvSplitString(types, ",", ARGV_SKIPEMPTY); argvSort(mod->types, NULL); if (argvSearch(mod->types, RPMPOL_TYPE_DEFAULT, NULL) && argvCount(mod->types) > 1) { rpmlog(RPMLOG_WARNING, _("'%s' type given with other types in %%semodule %s. Compacting types to '%s'.\n"), RPMPOL_TYPE_DEFAULT, mod->path, RPMPOL_TYPE_DEFAULT); mod->types = argvFree(mod->types); argvAdd(&mod->types, RPMPOL_TYPE_DEFAULT); } } else { argvAdd(&mod->types, RPMPOL_TYPE_DEFAULT); } mod->flags = flags; return mod; err: freeModule(mod); return NULL; } static rpmRC processPolicies(rpmSpec spec, Package pkg, int test) { const char *path = NULL; char *name = NULL; char *types = NULL; uint32_t flags = 0; poptContext optCon = NULL; ModuleRec mod = NULL; rpmRC rc = RPMRC_FAIL; struct poptOption optionsTable[] = { {"name", 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL}, {"types", 't', POPT_ARG_STRING, &types, 't', NULL, NULL}, {"base", 'b', POPT_ARGFLAG_OR, &flags, RPMPOL_FLAG_BASE, NULL, NULL}, POPT_TABLEEND }; if (!spec || !pkg) { goto exit; } for (ARGV_const_t pol = pkg->policyList; *pol != NULL; pol++) { const char *line = *pol; const char **argv = NULL; int argc = 0; int err; if ((err = poptParseArgvString(line, &argc, &argv))) { rpmlog(RPMLOG_ERR, _("Error parsing %s: %s\n"), line, poptStrerror(err)); goto exit; } if (!rstreq(argv[0], "%semodule")) { rpmlog(RPMLOG_ERR, _("Expecting %%semodule tag: %s\n"), line); goto exit; } optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); while (poptGetNextOpt(optCon) > 0) { } path = poptGetArg(optCon); if (!path) { rpmlog(RPMLOG_ERR, _("Missing module path in line: %s\n"), line); goto exit; } if (poptPeekArg(optCon)) { rpmlog(RPMLOG_ERR, _("Too many arguments in line: %s\n"), line); goto exit; } mod = newModule(path, name, types, flags); if (!mod) { goto exit; } if (writeModuleToHeader(mod, pkg) != RPMRC_OK) { goto exit; } mod = freeModule(mod); name = _free(name); types = _free(types); optCon = poptFreeContext(optCon); } rc = RPMRC_OK; exit: freeModule(mod); free(name); free(types); poptFreeContext(optCon); return rc; } #endif rpmRC processBinaryPolicies(rpmSpec spec, int test) { rpmRC rc = RPMRC_OK; #ifdef WITH_SELINUX Package pkg; char *nvr; for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { if (pkg->policyList == NULL) { continue; } nvr = headerGetAsString(pkg->header, RPMTAG_NVRA); rpmlog(RPMLOG_NOTICE, _("Processing policies: %s\n"), nvr); free(nvr); if (processPolicies(spec, pkg, test) != RPMRC_OK) { rc = RPMRC_FAIL; break; } } #endif return rc; } rpm-software-management-rpm-3c1f23f/build/reqprov.cc000066400000000000000000000033111511627505500225700ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/reqprov.c * Add dependency tags to package header(s). */ #include "system.h" #include #include #include #include "rpmbuild_internal.hh" #include "debug.h" int addReqProv(Package pkg, rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags, uint32_t index) { rpmds newds, *dsp = NULL; dsp = packageDependencies(pkg, tagN); /* rpmlib() dependency sanity: * - Provides are permitted only for source packages * - Otherwise only requires * - Ensure sense bit */ if (rstreqn(N, "rpmlib(", sizeof("rpmlib(")-1)) { if (tagN != RPMTAG_REQUIRENAME && (tagN == RPMTAG_PROVIDENAME && !(Flags & RPMSENSE_RPMLIB))) return 1; Flags |= RPMSENSE_RPMLIB; } newds = rpmdsSinglePoolTix(pkg->pool, tagN, N, EVR, rpmSanitizeDSFlags(tagN, Flags), index); rpmdsMerge(dsp, newds); rpmdsFree(newds); return 0; } rpmRC addReqProvPkg(void *cbdata, rpmTagVal tagN, const char * N, const char *EVR, rpmsenseFlags Flags, int index) { Package pkg = (Package)cbdata; return addReqProv(pkg, tagN, N, EVR, Flags, index) ? RPMRC_FAIL : RPMRC_OK; } int rpmlibNeedsFeature(Package pkg, const char * feature, const char * featureEVR) { char *reqname = NULL; int flags = RPMSENSE_RPMLIB|RPMSENSE_LESS|RPMSENSE_EQUAL; int res; /* XXX HACK: avoid changing rpmlibNeedsFeature() for just one user */ if (rstreq(feature, "DynamicBuildRequires")) flags |= RPMSENSE_MISSINGOK; rasprintf(&reqname, "rpmlib(%s)", feature); res = addReqProv(pkg, RPMTAG_REQUIRENAME, reqname, featureEVR, flags, 0); free(reqname); return res; } rpm-software-management-rpm-3c1f23f/build/rpmbuild_internal.hh000066400000000000000000000466571511627505500246420ustar00rootroot00000000000000#ifndef _RPMBUILD_INTERNAL_H #define _RPMBUILD_INTERNAL_H #include #include #include #include #include #include #include "rpmbuild_misc.hh" #include "rpmlua.hh" using fileRenameHash = std::unordered_multimap; #define ALLOWED_CHARS_NAME ".-_+%{}" #define ALLOWED_FIRSTCHARS_NAME "_%" #define ALLOWED_CHARS_VERREL "._+%{}~^" #define ALLOWED_CHARS_EVR ALLOWED_CHARS_VERREL "-:" #define LEN_AND_STR(_tag) (sizeof(_tag)-1), (_tag) enum parseStages { PARSE_SPECFILE, PARSE_BUILDSYS, PARSE_GENERATED, }; enum sections_e { SECT_PREP = 0, SECT_CONF = 1, SECT_BUILDREQUIRES = 2, SECT_BUILD = 3, SECT_INSTALL = 4, SECT_CHECK = 5, SECT_CLEAN = 6, }; #define NR_SECT 7 enum parseOps_e { PARSE_NONE = 0, PARSE_PREPEND = (1 << 0), PARSE_APPEND = (1 << 1), }; struct sectname_s { const char *name; int section; int part; int required; }; struct TriggerFileEntry { int index; char * fileName; char * script; char * prog; uint32_t flags; struct TriggerFileEntry * next; uint32_t priority; }; typedef enum rpmParseLineType_e { LINE_DEFAULT = 0, LINE_IF = (1 << 0), LINE_IFARCH = (1 << 1), LINE_IFNARCH = (1 << 2), LINE_IFOS = (1 << 3), LINE_IFNOS = (1 << 4), LINE_ELSE = (1 << 5), LINE_ENDIF = (1 << 6), LINE_INCLUDE = (1 << 7), LINE_ELIF = (1 << 8), LINE_ELIFARCH = (1 << 9), LINE_ELIFOS = (1 << 10), } rpmParseLineType; typedef const struct parsedSpecLine_s { int id; size_t textLen; const char *text; int withArgs; int isConditional; int wrongPrecursors; } * parsedSpecLine; static struct parsedSpecLine_s const lineTypes[] = { { LINE_ENDIF, LEN_AND_STR("%endif") , 0, 1, LINE_ENDIF}, { LINE_ELSE, LEN_AND_STR("%else") , 0, 1, LINE_ENDIF | LINE_ELSE }, { LINE_IF, LEN_AND_STR("%if") , 1, 1, 0}, { LINE_IFARCH, LEN_AND_STR("%ifarch") , 1, 1, 0}, { LINE_IFNARCH, LEN_AND_STR("%ifnarch"), 1, 1, 0}, { LINE_IFOS, LEN_AND_STR("%ifos") , 1, 1, 0}, { LINE_IFNOS, LEN_AND_STR("%ifnos") , 1, 1, 0}, { LINE_INCLUDE, LEN_AND_STR("%include"), 1, 0, 0}, { LINE_ELIFARCH, LEN_AND_STR("%elifarch"), 1, 1, LINE_ENDIF | LINE_ELSE}, { LINE_ELIFOS, LEN_AND_STR("%elifos"), 1, 1, LINE_ENDIF | LINE_ELSE}, { LINE_ELIF, LEN_AND_STR("%elif") ,1, 1, LINE_ENDIF | LINE_ELSE}, { 0, 0, 0, 0, 0, 0 } }; #define LINE_IFANY (LINE_IF | LINE_IFARCH | LINE_IFNARCH | LINE_IFOS | LINE_IFNOS) #define LINE_ELIFANY (LINE_ELIF | LINE_ELIFOS | LINE_ELIFARCH) typedef struct ReadLevelEntry { int reading; int lineNum; int readable; parsedSpecLine lastConditional; struct ReadLevelEntry * next; } RLE_t; /** \ingroup rpmbuild */ struct Source { char * fullSource; const char * source; /* Pointer into fullSource */ char * path; /* On-disk path (including %_sourcedir) */ int flags; uint32_t num; struct Source * next; }; typedef struct Package_s * Package; /** \ingroup rpmbuild * The structure used to store values parsed from a spec file. */ struct rpmSpec_s { char * buildHost; rpm_time_t buildTime; unsigned int rpmformat; /* v4, v6? */ char * specFile; /*!< Name of the spec file. */ char * buildRoot; char * buildDir; const char * rootDir; struct OpenFileInfo * fileStack; char *lbuf; size_t lbufSize; size_t lbufOff; char nextpeekc; char * nextline; char * line; int lineNum; struct ReadLevelEntry * readStack; Header buildRestrictions; rpmSpec * BASpecs; const char ** BANames; int BACount; int recursing; /*!< parse is recursive? */ rpmSpecFlags flags; struct Source * sources; int numSources; int noSource; int autonum_patch; int autonum_source; char * sourceRpmName; unsigned char * sourcePkgId; Package sourcePackage; rpmMacroContext macros; rpmstrPool pool; StringBuf sections[NR_SECT]; /*!< spec sections (%prep etc) */ ARGV_t buildopts[NR_SECT]; /*!< per-section buildsystem options */ ARGV_t sectionparts[NR_SECT]; ARGI_t sectionops[NR_SECT]; StringBuf parsed; /*!< parsed spec contents */ Package packages; /*!< Package list. */ }; #define PACKAGE_NUM_DEPS 12 /** \ingroup rpmbuild * The structure used to store values for a package. */ struct Package_s { rpmsid name; rpmstrPool pool; Header header; rpmds ds; /*!< Requires: N = EVR */ rpmds dependencies[PACKAGE_NUM_DEPS]; rpmfiles cpioList; ARGV_t dpaths; struct Source * icon; int autoReq; int autoProv; char * preInFile; /*!< %pre scriptlet. */ char * postInFile; /*!< %post scriptlet. */ char * preUnFile; /*!< %preun scriptlet. */ char * postUnFile; /*!< %postun scriptlet. */ char * preTransFile; /*!< %pretrans scriptlet. */ char * postTransFile; /*!< %posttrans scriptlet. */ char * preunTransFile; /*!< %preuntrans scriptlet. */ char * postunTransFile; /*!< %postuntrans scriptlet. */ char * verifyFile; /*!< %verifyscript scriptlet. */ std::vector triggerFiles; std::vector fileTriggerFiles; std::vector transFileTriggerFiles; ARGV_t fileFile; ARGV_t fileList; /* If NULL, package will not be written */ ARGV_t fileExcludeList; ARGV_t removePostfixes; fileRenameHash fileRenameMap; ARGV_t policyList; char *filename; rpmRC rc; Package next; }; #define PART_SUBNAME 0 #define PART_NAME 1 #define PART_QUIET 2 /** \ingroup rpmbuild * rpmSpec file parser states. */ #define PART_BASE 0 typedef enum rpmParseState_e { PART_ERROR = -1, /*!< */ PART_NONE = 0+PART_BASE, /*!< */ /* leave room for RPMRC_NOTFOUND returns. */ PART_PREAMBLE = 11+PART_BASE, /*!< */ PART_PREP = 12+PART_BASE, /*!< */ PART_BUILD = 13+PART_BASE, /*!< */ PART_INSTALL = 14+PART_BASE, /*!< */ PART_CHECK = 15+PART_BASE, /*!< */ PART_CLEAN = 16+PART_BASE, /*!< */ PART_FILES = 17+PART_BASE, /*!< */ PART_PRE = 18+PART_BASE, /*!< */ PART_POST = 19+PART_BASE, /*!< */ PART_PREUN = 20+PART_BASE, /*!< */ PART_POSTUN = 21+PART_BASE, /*!< */ PART_PRETRANS = 22+PART_BASE, /*!< */ PART_POSTTRANS = 23+PART_BASE, /*!< */ PART_DESCRIPTION = 24+PART_BASE, /*!< */ PART_CHANGELOG = 25+PART_BASE, /*!< */ PART_TRIGGERIN = 26+PART_BASE, /*!< */ PART_TRIGGERUN = 27+PART_BASE, /*!< */ PART_VERIFYSCRIPT = 28+PART_BASE, /*!< */ PART_BUILDARCHITECTURES= 29+PART_BASE,/*!< */ PART_TRIGGERPOSTUN = 30+PART_BASE, /*!< */ PART_TRIGGERPREIN = 31+PART_BASE, /*!< */ PART_POLICIES = 32+PART_BASE, /*!< */ PART_FILETRIGGERIN = 33+PART_BASE, /*!< */ PART_FILETRIGGERUN = 34+PART_BASE, /*!< */ PART_FILETRIGGERPOSTUN = 35+PART_BASE, /*!< */ PART_TRANSFILETRIGGERIN = 36+PART_BASE, /*!< */ PART_TRANSFILETRIGGERUN = 37+PART_BASE, /*!< */ PART_TRANSFILETRIGGERPOSTUN = 38+PART_BASE, /*!< */ PART_EMPTY = 39+PART_BASE, /*!< */ PART_PATCHLIST = 40+PART_BASE, /*!< */ PART_SOURCELIST = 41+PART_BASE, /*!< */ PART_BUILDREQUIRES = 42+PART_BASE, /*!< */ PART_CONF = 43+PART_BASE, /*!< */ PART_PREUNTRANS = 44+PART_BASE, /*!< */ PART_POSTUNTRANS = 45+PART_BASE, /*!< */ PART_LAST = 46+PART_BASE /*!< */ } rpmParseState; #define STRIP_NOTHING 0 #define STRIP_TRAILINGSPACE (1 << 0) #define STRIP_COMMENTS (1 << 1) #define ALLOW_EMPTY (1 << 16) /** \ingroup rpmbuild * Create and initialize rpmSpec structure. * @return spec spec file control structure */ RPM_GNUC_INTERNAL rpmSpec newSpec(void); /** \ingroup rpmbuild * Stop reading from spec file, freeing resources. * @param spec spec file control structure */ RPM_GNUC_INTERNAL void closeSpec(rpmSpec spec); /** \ingroup rpmbuild * Read next line from spec file. * @param spec spec file control structure * @param strip truncate comments? * @return 0 on success, 1 on EOF, <0 on error */ RPM_GNUC_INTERNAL int readLine(rpmSpec spec, int strip); /** \ingroup rpmbuild * Read next line from spec file. * @param spec spec file control structure * @param strip truncate comments? * @param[out] avp pointer to argv (optional, alloced) * @param[out] sbp pointer to string buf (optional, alloced) * @return next spec part or PART_ERROR on error */ RPM_GNUC_INTERNAL int parseLines(rpmSpec spec, int strip, ARGV_t *avp, StringBuf *sbp); /** \ingroup rpmbuild * Destroy source component chain. * @param s source component chain * @return NULL always */ RPM_GNUC_INTERNAL struct Source * freeSources(struct Source * s); /** \ingroup rpmbuild * Check line for section separator, return next parser state. * @param line from spec file * @return next parser state */ RPM_GNUC_INTERNAL int isPart(const char * line) ; /** \ingroup rpmbuild * Parse %%build/%%install/%%clean section(s) of a spec file. * @param spec spec file control structure * @param parsePart current rpmParseState * @return >= 0 next rpmParseState, < 0 on error */ RPM_GNUC_INTERNAL int parseSimpleScript(rpmSpec spec, const char * name, StringBuf *sbp, ARGV_t *avp, int *modep); /** \ingroup rpmbuild * Parse %%changelog section of a spec file. * @param spec spec file control structure * @return >= 0 next rpmParseState, < 0 on error */ RPM_GNUC_INTERNAL int parseChangelog(rpmSpec spec); /** \ingroup rpmbuild * Parse %%description section of a spec file. * @param spec spec file control structure * @return >= 0 next rpmParseState, < 0 on error */ RPM_GNUC_INTERNAL int parseDescription(rpmSpec spec); /** \ingroup rpmbuild * Parse %%files section of a spec file. * @param spec spec file control structure * @return >= 0 next rpmParseState, < 0 on error */ RPM_GNUC_INTERNAL int parseFiles(rpmSpec spec); /** \ingroup rpmbuild * Parse %%sepolicy section of a spec file. * @param spec spec file control structure * @return >= 0 next rpmParseState, < 0 on error */ RPM_GNUC_INTERNAL int parsePolicies(rpmSpec spec); /** \ingroup rpmbuild * Parse tags from preamble of a spec file. * @param spec spec file control structure * @param initialPackage * @param stage * @return >= 0 next rpmParseState, < 0 on error */ RPM_GNUC_INTERNAL int parsePreamble(rpmSpec spec, int initialPackage, enum parseStages stage); /** \ingroup rpmbuild * Parse %%pre et al scriptlets from a spec file. * @param spec spec file control structure * @param parsePart current rpmParseState * @return >= 0 next rpmParseState, < 0 on error */ RPM_GNUC_INTERNAL int parseScript(rpmSpec spec, int parsePart); RPM_GNUC_INTERNAL int parseList(rpmSpec spec, const char *name, rpmTagVal stype); /** \ingroup rpmbuild * Check for inappropriate characters. All alphanums are considered sane. * @param spec spec * @param field string to check * @param allowedchars string of permitted characters * @return RPMRC_OK if OK */ RPM_GNUC_INTERNAL rpmRC rpmCharCheck(rpmSpec spec, const char *field, const char *allowedchars, const char *allowedcharsfirst); typedef rpmRC (*addReqProvFunction) (void *cbdata, rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags, int index); /** \ingroup rpmbuild * Parse dependency relations from spec file and/or autogenerated output buffer. * @param spec spec file control structure * @param pkg package control structure * @param field text to parse (e.g. "foo < 0:1.2-3, bar = 5:6.7") * @param tagN tag, identifies type of dependency * @param index (0 always) * @param tagflags dependency flags already known from context * @param cb Callback for adding dependency (nullable) * @param cbdata Callback data (@pkg if NULL) * @return RPMRC_OK on success, RPMRC_FAIL on failure */ RPM_GNUC_INTERNAL rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char * field, rpmTagVal tagN, int index, rpmsenseFlags tagflags, addReqProvFunction cb, void *cbdata); /** \ingroup rpmbuild * Parse spec piece generated during build * * @param spec spec file control structure * @return RPMRC_OK on success */ RPM_GNUC_INTERNAL rpmRC parseGeneratedSpecs(rpmSpec spec); /** \ingroup rpmbuild * Run a build script, assembled from spec file scriptlet section. * * @param spec spec file control structure * @param what type of script * @param name name of scriptlet section * @param sb lines that compose script body * @param test don't execute scripts or package if testing * @param sb_stdoutp StringBuf to catupre the stdout of the script or NULL * @return RPMRC_OK on success */ RPM_GNUC_INTERNAL rpmRC doScript(rpmSpec spec, rpmBuildFlags what, const char * name, const char * sb, int test, StringBuf * sb_stdoutp); /** \ingroup rpmbuild * Find sub-package control structure by name. * @param spec spec file control structure * @param name (sub-)package name * @param flag if PART_SUBNAME, then 1st package name is prepended * @param[out] pkg package control structure * @return 0 on success, 1 on failure */ RPM_GNUC_INTERNAL rpmRC lookupPackage(rpmSpec spec, const char * name, int flag, Package * pkg); /** \ingroup rpmbuild * Create and initialize package control structure. * @param name package name for sub-packages (or NULL) * @param pool string pool * @param pkglist package list pointer to append to (or NULL) * @return package control structure */ RPM_GNUC_INTERNAL Package newPackage(const char *name, rpmstrPool pool, Package * pkglist); /** \ingroup rpmbuild * Free a package control structure. * @param pkg package control structure */ RPM_GNUC_INTERNAL Package freePackage(Package pkg); /** \ingroup rpmbuild * Return rpmds containing the dependencies of a given type * @param pkg package * @param tag name tag denominating the dependency * @return pointer to dependency set */ RPM_GNUC_INTERNAL rpmds * packageDependencies(Package pkg, rpmTagVal tag); /** \ingroup rpmbuild * Post-build processing for binary package(s). * @param spec spec file control structure * @param pkgFlags bit(s) to control package generation * @param didInstall was %install executed? * @param test don't execute scripts or package if testing * @return 0 on success */ RPM_GNUC_INTERNAL rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, int didInstall, int test); /** \ingroup rpmfc * Generate package dependencies. * @param spec spec file control * @param pkg package control * @return RPMRC_OK on success */ RPM_GNUC_INTERNAL rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg); /** \ingroup rpmfc * Return helper output. * @param av helper argv (with possible macros) * @param sb_stdin helper input * @param[out] *sb_stdoutp helper output * @param failnonzero IS non-zero helper exit status a failure? * @param buildRoot buildRoot directory (or NULL) */ RPM_GNUC_INTERNAL int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp, int failnonzero, const std::string & buildRoot); /** \ingroup rpmbuild * Post-build processing for policies in binary package(s). * @param spec spec file control structure * @param test don't execute scripts or package if testing * @return 0 on success */ RPM_GNUC_INTERNAL rpmRC processBinaryPolicies(rpmSpec spec, int test); /** \ingroup rpmbuild * Post-build processing for source package. * @param spec spec file control structure * @param pkgFlags bit(s) to control package generation * @return 0 on success */ RPM_GNUC_INTERNAL rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags); /** \ingroup rpmbuild * Generate binary package(s). * @param spec spec file control structure * @param cookie build identifier "cookie" or NULL * @param cheating was build shortcircuited? * @return RPMRC_OK on success */ RPM_GNUC_INTERNAL rpmRC packageBinaries(rpmSpec spec, const char *cookie, int cheating); /** \ingroup rpmbuild * Generate source package. * @param spec spec file control structure * @param[out] cookie build identifier "cookie" or NULL * @return RPMRC_OK on success */ RPM_GNUC_INTERNAL rpmRC packageSources(rpmSpec spec, char **cookie); RPM_GNUC_INTERNAL int addLangTag(rpmSpec spec, Header h, rpmTagVal tag, const char *field, const char *lang); /** \ingroup rpmbuild * Add dependency to package, filtering duplicates. * @param pkg package * @param tagN tag, identifies type of dependency * @param N (e.g. Requires: foo < 0:1.2-3, "foo") * @param EVR (e.g. Requires: foo < 0:1.2-3, "0:1.2-3") * @param Flags (e.g. Requires: foo < 0:1.2-3, both "Requires:" and "<") * @param index (# trigger script for triggers, 0 for others) * @return 0 on success, 1 on error */ RPM_GNUC_INTERNAL int addReqProv(Package pkg, rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags, uint32_t index); RPM_GNUC_INTERNAL rpmRC addReqProvPkg(void *cbdata, rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags, int index); /** \ingroup rpmbuild * Add self-provides to package. * @param pkg package */ RPM_GNUC_INTERNAL void addPackageProvides(Package pkg); RPM_GNUC_INTERNAL int addSource(rpmSpec spec, int specline, const char *srcname, rpmTagVal tag); /** \ingroup rpmbuild * Add rpmlib feature dependency. * @param pkg package * @param feature rpm feature name (i.e. "rpmlib(Foo)" for feature Foo) * @param featureEVR rpm feature epoch/version/release * @return 0 always */ RPM_GNUC_INTERNAL int rpmlibNeedsFeature(Package pkg, const char * feature, const char * featureEVR); RPM_GNUC_INTERNAL rpmRC checkForEncoding(Header h, int addtag); /** \ingroup rpmbuild * Copy tags inherited by subpackages from the source header to the target header * @param h target header * @param fromh source header */ RPM_GNUC_INTERNAL void copyInheritedTags(Header h, Header fromh); RPM_GNUC_INTERNAL int specExpand(rpmSpec spec, int lineno, const char *sbuf, char **obuf); /* * Read expanded lines from a file into avp and/or sbp, controlled by * flags (STRIP_*, ALLOW_EMPTY) * Returns number or read lines, or -1 on error. */ RPM_GNUC_INTERNAL int readManifest(rpmSpec spec, const char *path, const char *descr, int flags, ARGV_t *avp, StringBuf *sbp); RPM_GNUC_INTERNAL void * specLuaInit(rpmSpec spec); RPM_GNUC_INTERNAL void * specLuaFini(rpmSpec spec); RPM_GNUC_INTERNAL void addLuaSource(const struct Source *p); RPM_GNUC_INTERNAL int isMemberInEntry(Header h, const char *name, rpmTagVal tag); RPM_GNUC_INTERNAL rpmRC checkForValidArchitectures(rpmSpec spec); RPM_GNUC_INTERNAL int checkForRequired(Header h); RPM_GNUC_INTERNAL int checkForDuplicates(Header h); RPM_GNUC_INTERNAL int checkBuildsystem(rpmSpec spec, const char *buildsys); RPM_GNUC_INTERNAL void fillOutMainPackage(Header h); RPM_GNUC_INTERNAL void copyInheritedTags(Header h, Header fromh); RPM_GNUC_INTERNAL void doSetupMacro(rpmMacroBuf mb, rpmMacroEntry me, ARGV_t margs, size_t *parsed); RPM_GNUC_INTERNAL void doPatchMacro(rpmMacroBuf mb, rpmMacroEntry me, ARGV_t margs, size_t *parsed); /* Return section number, -1 on error */ RPM_GNUC_INTERNAL const struct sectname_s *getSection(const char *name, int part); #endif /* _RPMBUILD_INTERNAL_H */ rpm-software-management-rpm-3c1f23f/build/rpmbuild_misc.hh000066400000000000000000000026011511627505500237360ustar00rootroot00000000000000#ifndef _RPMBUILD_MISC_H #define _RPMBUILD_MISC_H #include #include #include #include /** \ingroup rpmbuild * Truncate comment lines. * @param s skip white space, truncate line at '#' * @return 1 on comment lines, 0 otherwise */ RPM_GNUC_INTERNAL int handleComments(char * s); RPM_GNUC_INTERNAL struct Source *findSource(rpmSpec spec, uint32_t num, int flag); /** \ingroup rpmstring */ typedef struct StringBufRec *StringBuf; /** \ingroup rpmstring */ RPM_GNUC_INTERNAL StringBuf newStringBuf(void); /** \ingroup rpmstring */ RPM_GNUC_INTERNAL StringBuf freeStringBuf( StringBuf sb); /** \ingroup rpmstring */ RPM_GNUC_INTERNAL const char * getStringBuf(StringBuf sb); /** \ingroup rpmstring */ RPM_GNUC_INTERNAL void stripTrailingBlanksStringBuf(StringBuf sb); /** \ingroup rpmstring */ #define appendStringBuf(sb, s) appendStringBufAux(sb, s, 0) /** \ingroup rpmstring */ #define appendLineStringBuf(sb, s) appendStringBufAux(sb, s, 1) /** \ingroup rpmstring */ RPM_GNUC_INTERNAL void appendStringBufAux(StringBuf sb, const std::string & s, int nl); /** \ingroup rpmbuild * Parse an unsigned number. * @param line from spec file * @param[out] res pointer to uint32_t * @return 0 on success, -1 on failure */ RPM_GNUC_INTERNAL int parseUnsignedNum(const char * line, uint32_t * res); #endif /* _RPMBUILD_MISC_H */ rpm-software-management-rpm-3c1f23f/build/rpmfc.cc000066400000000000000000001326421511627505500222130ustar00rootroot00000000000000#include "system.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBELF #include #endif #include #include #include #include #include #include #include #include #include "rpmfi_internal.hh" /* rpmfiles stuff for now */ #include "rpmbuild_internal.hh" #include "rpmmacro_internal.hh" #include "debug.h" using std::string; using std::vector; using namespace rpm; struct matchRule { regex_t *path; regex_t *magic; regex_t *mime; ARGV_t flags; }; typedef struct rpmfcAttr_s { char *name; struct matchRule incl; struct matchRule excl; char *proto; } * rpmfcAttr; struct rpmfcFileDep { int fileIx; rpmds dep; bool operator < (const rpmfcFileDep & other) const { return fileIx < other.fileIx; } }; using rpmfcFileDeps = vector; using fattrHash = std::unordered_multimap; struct rpmfc_s { Package pkg; int nfiles; /*!< no. of files */ int fknown; /*!< no. of classified files */ int fwhite; /*!< no. of "white" files */ int skipProv; /*!< Don't auto-generate Provides:? */ int skipReq; /*!< Don't auto-generate Requires:? */ int rpmformat; /*!< Rpm package format */ string buildRoot; /*!< (Build) root dir */ vector atypes; /*!< known file attribute types */ vector fn; /*!< (no. files) file names */ vector fmime;/*!< (no. files) file mime types */ vector ftype;/*!< (no. files) file types */ ARGV_t *fattrs; /*!< (no. files) file attribute tokens */ vector fcolor; /*!< (no. files) file colors */ vector fmdictx;/*!< (no. files) file mime dictionary indices */ vector fcdictx;/*!< (no. files) file class dictionary indices */ vector fddictx;/*!< (no. files) file depends dictionary start */ vector fddictn;/*!< (no. files) file depends dictionary no. entries */ vector ddictx; /*!< (no. dependencies) file->dependency mapping */ rpmstrPool cdict; /*!< file class dictionary */ rpmstrPool mdict; /*!< file class dictionary */ rpmfcFileDeps fileDeps; /*!< file dependency mapping */ fattrHash fahash; /*!< attr:file mapping */ rpmstrPool pool; /*!< general purpose string storage */ }; struct rpmfcTokens_s { const char * token; rpm_color_t colors; }; typedef const struct rpmfcTokens_s * rpmfcToken; static int regMatch(regex_t *reg, const char *val) { return (reg && regexec(reg, val, 0, NULL, 0) == 0); } static regex_t * regFree(regex_t *reg) { if (reg) { regfree(reg); free(reg); } return NULL; } static void ruleFree(struct matchRule *rule) { regFree(rule->path); regFree(rule->magic); regFree(rule->mime); argvFree(rule->flags); } static char *rpmfcAttrMacroV(const char *arg, va_list args) { if (arg == NULL || rstreq(arg, "")) return NULL; string buf = "%{?__"; for (const char *s = arg; s != NULL; s = va_arg(args, const char *)) { if (s != arg) buf += '_'; buf += s; } buf += "}"; char *obuf = rpmExpand(buf.c_str(), NULL); if (rstreq(obuf, "")) obuf = _free(obuf); return obuf; } static char *rpmfcAttrMacro(const char *arg, ...) { va_list args; char *s; va_start(args, arg); s = rpmfcAttrMacroV(arg, args); va_end(args); return s; } static regex_t *rpmfcAttrReg(const char *arg, ...) { regex_t *reg = NULL; char *pattern; va_list args; va_start(args, arg); pattern = rpmfcAttrMacroV(arg, args); va_end(args); if (pattern) { reg = (regex_t *)xcalloc(1, sizeof(*reg)); if (regcomp(reg, pattern, REG_EXTENDED) != 0) { rpmlog(RPMLOG_WARNING, _("Ignoring invalid regex %s\n"), pattern); reg = _free(reg); } rfree(pattern); } return reg; } static rpmfcAttr rpmfcAttrNew(const char *name) { rpmfcAttr attr = new rpmfcAttr_s {}; struct matchRule *rules[] = { &attr->incl, &attr->excl, NULL }; attr->name = xstrdup(name); attr->proto = rpmfcAttrMacro(name, "protocol", NULL); for (struct matchRule **rule = rules; rule && *rule; rule++) { const char *prefix = (*rule == &attr->incl) ? NULL : "exclude"; char *flags; if (prefix) { flags = rpmfcAttrMacro(name, prefix, "flags", NULL); (*rule)->path = rpmfcAttrReg(name, prefix, "path", NULL); (*rule)->magic = rpmfcAttrReg(name, prefix, "magic", NULL); (*rule)->mime = rpmfcAttrReg(name, prefix, "mime", NULL); } else { flags = rpmfcAttrMacro(name, "flags", NULL); (*rule)->path = rpmfcAttrReg(name, "path", NULL); (*rule)->magic = rpmfcAttrReg(name, "magic", NULL); (*rule)->mime = rpmfcAttrReg(name, "mime", NULL); } if ((*rule)->magic && (*rule)->mime) { rpmlog(RPMLOG_WARNING, _("%s: mime and magic supplied, only mime will be used\n"), name); } (*rule)->flags = argvSplitString(flags, ",", ARGV_SKIPEMPTY); argvSort((*rule)->flags, NULL); free(flags); } return attr; } static rpmfcAttr rpmfcAttrFree(rpmfcAttr attr) { if (attr) { ruleFree(&attr->incl); ruleFree(&attr->excl); rfree(attr->name); rfree(attr->proto); delete attr; } return NULL; } static int rpmfcExpandAppend(ARGV_t * argvp, ARGV_const_t av) { ARGV_t argv = *argvp; int argc = argvCount(argv); int ac = argvCount(av); int i; argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv)); for (i = 0; i < ac; i++) argv[argc + i] = rpmExpand(av[i], NULL); argv[argc + ac] = NULL; *argvp = argv; return 0; } static rpmds rpmdsSingleNS(rpmstrPool pool, rpmTagVal tagN, const char *namespc, const char * N, const char * EVR, rpmsenseFlags Flags) { rpmds ds = NULL; if (namespc) { auto [ ign, NSN ] = macros().expand({namespc, "(", N, ")",}); ds = rpmdsSinglePool(pool, tagN, NSN.c_str(), EVR, Flags); } else { ds = rpmdsSinglePool(pool, tagN, N, EVR, Flags); } return ds; } #define max(x,y) ((x) > (y) ? (x) : (y)) static int getOutputFrom(ARGV_t argv, const char * writePtr, size_t writeBytesLeft, StringBuf sb_stdout, int failNonZero, const string & buildRoot) { pid_t child, reaped; int toProg[2] = { -1, -1 }; int fromProg[2] = { -1, -1 }; int status; int myerrno = 0; int ret = 1; /* assume failure */ int doio = (writePtr || sb_stdout); if (doio && (pipe(toProg) < 0 || pipe(fromProg) < 0)) { rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]); return -1; } struct sigaction act, oact; memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, &oact); child = fork(); if (child < 0) { rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"), argv[0], strerror(errno)); if (doio) { close(toProg[1]); close(toProg[0]); close(fromProg[0]); close(fromProg[1]); } ret = -1; goto exit; } if (child == 0) { act.sa_handler = SIG_DFL; sigaction(SIGPIPE, &act, NULL); close(toProg[1]); close(fromProg[0]); /* * When expecting input, make stdin the in pipe as you'd normally do. * Otherwise pass stdout(!) as the in pipe to cause reads to error * out. Just closing the fd breaks some software (eg libtool). */ if (writePtr) { dup2(toProg[0], STDIN_FILENO); close(toProg[0]); } else { dup2(fromProg[1], STDIN_FILENO); } dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */ close(fromProg[1]); rpmlog(RPMLOG_DEBUG, "\texecv(%s) pid %d\n", argv[0], (unsigned)getpid()); unsetenv("DEBUGINFOD_URLS"); if (!buildRoot.empty()) setenv("RPM_BUILD_ROOT", buildRoot.c_str(), 1); execvp(argv[0], (char *const *)argv); rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"), argv[0], strerror(errno)); _exit(EXIT_FAILURE); } if (!doio) goto reap; close(toProg[0]); close(fromProg[1]); while (1) { fd_set ibits, obits; int nfd = 0; ssize_t iorc; char buf[BUFSIZ+1]; FD_ZERO(&ibits); FD_ZERO(&obits); FD_SET(fromProg[0], &ibits); nfd = max(nfd, fromProg[0]); if (writeBytesLeft > 0) { FD_SET(toProg[1], &obits); nfd = max(nfd, toProg[1]); } else if (toProg[1] >= 0) { /* Close write-side pipe to notify child on EOF */ close(toProg[1]); toProg[1] = -1; } do { iorc = select(nfd + 1, &ibits, &obits, NULL, NULL); } while (iorc == -1 && errno == EINTR); if (iorc < 0) { myerrno = errno; break; } /* Write data to child */ if (writeBytesLeft > 0 && FD_ISSET(toProg[1], &obits)) { size_t nb = (1024 < writeBytesLeft) ? 1024 : writeBytesLeft; do { iorc = write(toProg[1], writePtr, nb); } while (iorc == -1 && errno == EINTR); if (iorc < 0) { myerrno = errno; break; } writeBytesLeft -= iorc; writePtr += iorc; } /* Read when we get data back from the child */ if (FD_ISSET(fromProg[0], &ibits)) { do { iorc = read(fromProg[0], buf, sizeof(buf)-1); } while (iorc == -1 && errno == EINTR); if (iorc == 0) break; /* EOF, we're done */ if (iorc < 0) { myerrno = errno; break; } buf[iorc] = '\0'; if (sb_stdout) appendStringBuf(sb_stdout, buf); } } /* Clean up */ if (toProg[1] >= 0) close(toProg[1]); if (fromProg[0] >= 0) close(fromProg[0]); reap: /* Collect status from prog */ do { reaped = waitpid(child, &status, 0); } while (reaped == -1 && errno == EINTR); /* * It's not allowed to call WIFEXITED or WEXITSTATUS if waitpid return -1. * Note, all bits set, since -1 == 0xFFFFFFFF */ if (reaped == -1) { rpmlog(RPMLOG_DEBUG, _("Failed to wait for exit status of %s: %s\n"), argv[0], strerror(errno)); goto exit; } rpmlog(RPMLOG_DEBUG, "\twaitpid(%d) rc %d status %x\n", (unsigned)child, (unsigned)reaped, status); if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) { rpmlog(RPMLOG_DEBUG, _("%s failed: %x\n"), argv[0], status); goto exit; } if (writeBytesLeft || myerrno) { rpmlog(RPMLOG_ERR, _("failed to write all data to %s: %s\n"), argv[0], strerror(myerrno)); goto exit; } ret = 0; exit: sigaction(SIGPIPE, &oact, NULL); return ret; } int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp, int failnonzero, const string & buildRoot) { char * s = NULL; ARGV_t xav = NULL; ARGV_t pav = NULL; int pac = 0; int ec = -1; StringBuf sb = NULL; const char * buf_stdin = NULL; size_t buf_stdin_len = 0; if (sb_stdoutp) *sb_stdoutp = NULL; if (!(av && *av)) goto exit; /* Find path to executable with (possible) args. */ s = rpmExpand(av[0], NULL); if (!(s && *s)) goto exit; /* Parse args buried within expanded executable. */ if (!(poptParseArgvString(s, &pac, (const char ***)&pav) == 0 && pac > 0 && pav != NULL)) goto exit; /* Build argv, appending args to the executable args. */ argvAppend(&xav, pav); if (av[1]) rpmfcExpandAppend(&xav, av + 1); if (sb_stdin != NULL) { buf_stdin = getStringBuf(sb_stdin); buf_stdin_len = strlen(buf_stdin); } if (_rpmfc_debug) { char *cmd = argvJoin(xav, " "); rpmlog(RPMLOG_DEBUG, "Executing %s on %s\n", cmd, buf_stdin); free(cmd); } /* Read output from exec'd helper. */ if (sb_stdoutp != NULL) { sb = newStringBuf(); } ec = getOutputFrom(xav, buf_stdin, buf_stdin_len, sb, failnonzero, buildRoot); if (ec) { sb = freeStringBuf(sb); goto exit; } if (sb_stdoutp != NULL) { *sb_stdoutp = sb; sb = NULL; /* XXX don't free */ } exit: freeStringBuf(sb); argvFree(xav); free(pav); /* XXX popt mallocs in single blob. */ free(s); return ec; } static void argvAddUniq(ARGV_t * argvp, const char * key) { if (argvSearch(*argvp, key, NULL) == NULL) { argvAdd(argvp, key); argvSort(*argvp, NULL); } } #define hasAttr(_a, _n) (argvSearch((_a), (_n), NULL) != NULL) static void rpmfcAddFileDep(rpmfcFileDeps & fileDeps, rpmds ds, int ix) { rpmfcFileDep dep { ix, ds }; fileDeps.push_back(dep); } static ARGV_t runCmd(const char *name, const string & buildRoot, ARGV_t fns) { ARGV_t output = NULL; ARGV_t av = NULL; StringBuf sb_stdout = NULL; StringBuf sb_stdin = newStringBuf(); char *cmd = rstrscat(NULL, "%{", name, "} %{?", name, "_opts}", NULL); argvAdd(&av, cmd); for (ARGV_t fn = fns; fn && *fn; fn++) appendLineStringBuf(sb_stdin, *fn); if (rpmfcExec(av, sb_stdin, &sb_stdout, 0, buildRoot) == 0) { argvSplit(&output, getStringBuf(sb_stdout), "\n\r"); } argvFree(av); freeStringBuf(sb_stdin); freeStringBuf(sb_stdout); free(cmd); return output; } static ARGV_t runCall(const char *name, const string & buildRoot, ARGV_t fns) { ARGV_t output = NULL; ARGV_t args = NULL; char *exp = NULL; char *opt = rpmExpand("%{?", name, "_opts}", NULL); if (*opt) argvAdd(&args, opt); argvAppend(&args, fns); if (_rpmfc_debug) { char *paths = argvJoin(fns, "\n"); rpmlog(RPMLOG_DEBUG, "Calling %s(%s) on %s\n", name, opt, paths); free(paths); } if (rpmExpandThisMacro(NULL, name, args, &exp, 0) >= 0) argvSplit(&output, exp, "\n\r"); free(exp); free(opt); argvFree(args); return output; } struct addReqProvDataFc { rpmfc fc; const char *namespc; regex_t *exclude; }; static rpmRC addReqProvFc(void *cbdata, rpmTagVal tagN, const char * N, const char * EVR, rpmsenseFlags Flags, int index) { struct addReqProvDataFc *data = (struct addReqProvDataFc *)cbdata; rpmfc fc = data->fc; const char *namespc = data->namespc; regex_t *exclude = data->exclude; rpmds ds = rpmdsSingleNS(fc->pool, tagN, namespc, N, EVR, Flags); /* Add to package and file dependencies unless filtered */ if (regMatch(exclude, rpmdsDNEVR(ds)+2) == 0) rpmfcAddFileDep(fc->fileDeps, ds, index); return RPMRC_OK; } struct exclreg_s { regex_t *exclude; regex_t *exclude_from; regex_t *global_exclude_from; }; static void exclInit(const char *depname, struct exclreg_s *excl) { excl->exclude = rpmfcAttrReg(depname, "exclude", NULL); excl->exclude_from = rpmfcAttrReg(depname, "exclude", "from", NULL); excl->global_exclude_from = rpmfcAttrReg("global", depname, "exclude", "from", NULL); } static void exclFini(struct exclreg_s *excl) { regFree(excl->exclude); regFree(excl->exclude_from); regFree(excl->global_exclude_from); memset(excl, 0, sizeof(*excl)); } static int genDeps(const char *mname, int multifile, rpmTagVal tagN, rpmsenseFlags dsContext, struct addReqProvDataFc *data, int *fnx, int nfn, int fx, ARGV_t paths) { rpmfc fc = data->fc; ARGV_t pav = NULL; int rc = 0; if (rpmMacroIsParametric(NULL, mname)) { pav = runCall(mname, fc->buildRoot, paths); } else { pav = runCmd(mname, fc->buildRoot, paths); } for (int px = 0, pac = argvCount(pav); px < pac; px++) { if (multifile && *pav[px] == ';') { int found = 0; /* Look forward to allow generators to omit files without deps */ do { fx++; if (rstreq(pav[px]+1, paths[fx])) found = 1; } while (!found && fx < nfn-1); if (!found) { rpmlog(RPMLOG_ERR, _("invalid or out of order path from generator: %s\n"), pav[px]); rc++; break; } continue; } if (parseRCPOT(NULL, fc->pkg, pav[px], tagN, fnx[fx], dsContext, addReqProvFc, data)) { rc++; } } argvFree(pav); return rc; } static int rpmfcHelper(rpmfc fc, int *fnx, int nfn, const char *proto, const struct exclreg_s *excl, rpmsenseFlags dsContext, rpmTagVal tagN, const char *namespc, const char *mname) { int rc = 0; struct addReqProvDataFc data; data.fc = fc; data.namespc = namespc; data.exclude = excl->exclude; if (proto && rstreq(proto, "multifile")) { const char **paths = (const char **)xcalloc(nfn + 1, sizeof(*paths)); for (int i = 0; i < nfn; i++) paths[i] = fc->fn[fnx[i]].c_str(); paths[nfn] = NULL; rc = genDeps(mname, 1, tagN, dsContext, &data, fnx, nfn, -1, (ARGV_t) paths); free(paths); } else { for (int i = 0; i < nfn; i++) { const char *fn = fc->fn[fnx[i]].c_str(); const char *paths[] = { fn, NULL }; rc += genDeps(mname, 0, tagN, dsContext, &data, fnx, nfn, i, (ARGV_t) paths); } } return rc; } /* Only used for controlling RPMTAG_FILECLASS inclusion now */ static const struct rpmfcTokens_s rpmfcTokens[] = { { "directory", RPMFC_INCLUDE }, { "ELF 32-bit", RPMFC_ELF32|RPMFC_INCLUDE }, { "ELF 64-bit", RPMFC_ELF64|RPMFC_INCLUDE }, { "troff or preprocessor input", RPMFC_INCLUDE }, { "GNU Info", RPMFC_INCLUDE }, { "perl ", RPMFC_INCLUDE }, { "Perl5 module source text", RPMFC_INCLUDE }, { "python ", RPMFC_INCLUDE }, { "libtool library ", RPMFC_INCLUDE }, { "pkgconfig ", RPMFC_INCLUDE }, { "Objective caml ", RPMFC_INCLUDE }, { "Mono/.Net assembly", RPMFC_INCLUDE }, { "current ar archive", RPMFC_INCLUDE }, { "Zip archive data", RPMFC_INCLUDE }, { "tar archive", RPMFC_INCLUDE }, { "cpio archive", RPMFC_INCLUDE }, { "RPM v3", RPMFC_INCLUDE }, { "RPM v4", RPMFC_INCLUDE }, { " image", RPMFC_INCLUDE }, { " font", RPMFC_INCLUDE }, { " Font", RPMFC_INCLUDE }, { " commands", RPMFC_INCLUDE }, { " script", RPMFC_INCLUDE }, { "empty", RPMFC_INCLUDE }, { "HTML", RPMFC_INCLUDE }, { "SGML", RPMFC_INCLUDE }, { "XML", RPMFC_INCLUDE }, { " source", RPMFC_INCLUDE }, { "GLS_BINARY_LSB_FIRST", RPMFC_INCLUDE }, { " DB ", RPMFC_INCLUDE }, { " text", RPMFC_INCLUDE }, { NULL, RPMFC_BLACK } }; static void argvAddTokens(ARGV_t *argv, const char *tnames) { if (tnames) { ARGV_t tokens = NULL; argvSplit(&tokens, tnames, ","); for (ARGV_t token = tokens; token && *token; token++) argvAddUniq(argv, *token); argvFree(tokens); } } static int matches(const struct matchRule *rule, const char *ftype, const char *fmime, const char *path, int executable) { const char *mtype = rule->mime ? fmime : ftype; regex_t *mreg = rule->mime ? rule->mime : rule->magic; if (!executable && hasAttr(rule->flags, "exeonly")) return 0; if (mreg && rule->path && hasAttr(rule->flags, "magic_and_path")) { return (regMatch(mreg, mtype) && regMatch(rule->path, path)); } else { return (regMatch(mreg, mtype) || regMatch(rule->path, path)); } } static void rpmfcAttributes(rpmfc fc, int ix, const char *ftype, const char *fmime, const char *fullpath) { const char *path = fullpath + fc->buildRoot.size(); int is_executable = 0; struct stat st; if (stat(fullpath, &st) == 0) { is_executable = (S_ISREG(st.st_mode)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)); } for (int i = 0; i < fc->atypes.size(); ++i) { rpmfcAttr attr = fc->atypes[i]; /* Filter out excludes */ if (matches(&(attr)->excl, ftype, fmime, path, is_executable)) continue; /* Add attributes on libmagic type & path pattern matches */ if (matches(&(attr)->incl, ftype, fmime, path, is_executable)) { argvAddTokens(&fc->fattrs[ix], (attr)->name); #pragma omp critical(fahash) fc->fahash.insert({i, ix}); } } } /* Return color for a given libmagic classification string */ static rpm_color_t rpmfcColor(const char * fmstr) { rpmfcToken fct; rpm_color_t fcolor = RPMFC_BLACK; for (fct = rpmfcTokens; fct->token != NULL; fct++) { if (strstr(fmstr, fct->token) == NULL) continue; fcolor |= fct->colors; if (fcolor & RPMFC_INCLUDE) break; } return fcolor; } void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp) { int ndx; int dx; int fx; if (fp == NULL) fp = stderr; if (msg) fprintf(fp, "===================================== %s\n", msg); if (fc) for (fx = 0; fx < fc->nfiles; fx++) { fprintf(fp, "%3d %s", fx, fc->fn[fx].c_str()); if (_rpmfc_debug) { rpmsid cx = fc->fcdictx[fx] + 1; /* id's are one off */ rpm_color_t fcolor = fc->fcolor[fx]; ARGV_t fattrs = fc->fattrs[fx]; if (fcolor != RPMFC_BLACK) fprintf(fp, "\t0x%x", fc->fcolor[fx]); else fprintf(fp, "\t%s", rpmstrPoolStr(fc->cdict, cx)); if (fattrs) { char *attrs = argvJoin(fattrs, ","); fprintf(fp, " [%s]", attrs); free(attrs); } else { fprintf(fp, " [none]"); } } fprintf(fp, "\n"); if (fc->fddictx.empty() || fc->fddictn.empty()) continue; assert(fx < fc->fddictx.size()); dx = fc->fddictx[fx]; assert(fx < fc->fddictn.size()); ndx = fc->fddictn[fx]; while (ndx-- > 0) { const char * depval; unsigned char deptype; unsigned ix; rpmds ds; ix = fc->ddictx[dx++]; deptype = ((ix >> 24) & 0xff); ix &= 0x00ffffff; ds = rpmfcDependencies(fc, rpmdsDToTagN(deptype)); (void) rpmdsSetIx(ds, ix); depval = rpmdsDNEVR(ds); if (depval) fprintf(fp, "\t%s\n", depval); } } } rpmfc rpmfcFree(rpmfc fc) { if (fc) { for (auto const & attr : fc->atypes) rpmfcAttrFree(attr); for (int i = 0; i < fc->nfiles; i++) { argvFree(fc->fattrs[i]); } free(fc->fattrs); freePackage(fc->pkg); for (auto & fd : fc->fileDeps) rpmdsFree(fd.dep); rpmstrPoolFree(fc->cdict); rpmstrPoolFree(fc->mdict); rpmstrPoolFree(fc->pool); delete fc; } return NULL; } rpmfc rpmfcCreate(const char *buildRoot, rpmFlags flags) { rpmfc fc = new rpmfc_s {}; if (buildRoot) { fc->buildRoot = buildRoot; } fc->pool = rpmstrPoolCreate(); fc->pkg = new Package_s {}; return fc; } rpmds rpmfcDependencies(rpmfc fc, rpmTagVal tag) { if (fc) { return *packageDependencies(fc->pkg, tag); } return NULL; } rpmds rpmfcProvides(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_PROVIDENAME); } rpmds rpmfcRequires(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_REQUIRENAME); } rpmds rpmfcRecommends(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_RECOMMENDNAME); } rpmds rpmfcSuggests(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_SUGGESTNAME); } rpmds rpmfcSupplements(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_SUPPLEMENTNAME); } rpmds rpmfcEnhances(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_ENHANCENAME); } rpmds rpmfcConflicts(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_CONFLICTNAME); } rpmds rpmfcObsoletes(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_OBSOLETENAME); } rpmds rpmfcOrderWithRequires(rpmfc fc) { return rpmfcDependencies(fc, RPMTAG_ORDERNAME); } /* Versioned deps are less than unversioned deps */ static bool verdepLess(const rpmfcFileDep & fDepA, const rpmfcFileDep & fDepB) { int aIsVersioned = rpmdsFlags(fDepA.dep) & RPMSENSE_SENSEMASK ? 1 : 0; int bIsVersioned = rpmdsFlags(fDepB.dep) & RPMSENSE_SENSEMASK ? 1 : 0; return bIsVersioned < aIsVersioned; } /* * Remove unversioned deps if corresponding versioned deps exist but only * if the versioned dependency has the same type and the same color as the versioned. */ static void rpmfcNormalizeFDeps(rpmfc fc) { rpmstrPool versionedDeps = rpmstrPoolCreate(); rpmfcFileDeps normalizedFDeps; char *depStr; /* Sort. Versioned dependencies first */ std::sort(fc->fileDeps.begin(), fc->fileDeps.end(), &verdepLess); for (auto const & fdep : fc->fileDeps) { switch (rpmdsTagN(fdep.dep)) { case RPMTAG_REQUIRENAME: case RPMTAG_RECOMMENDNAME: case RPMTAG_SUGGESTNAME: rasprintf(&depStr, "%08x_%c_%s", fc->fcolor[fdep.fileIx], rpmdsD(fdep.dep), rpmdsN(fdep.dep)); if (rpmdsFlags(fdep.dep) & RPMSENSE_SENSEMASK) { /* preserve versioned require dependency */ normalizedFDeps.push_back(fdep); rpmstrPoolId(versionedDeps, depStr, 1); } else if (!rpmstrPoolId(versionedDeps, depStr, 0)) { /* preserve unversioned require dep only if versioned dep doesn't exist */ normalizedFDeps.push_back(fdep); } else { rpmdsFree(fdep.dep); } free(depStr); break; default: /* Preserve all non-require dependencies */ normalizedFDeps.push_back(fdep); break; } } rpmstrPoolFree(versionedDeps); fc->fileDeps = normalizedFDeps; /* vector assignment */ } struct applyDep_s { rpmTagVal tag; int type; const char *name; }; static const struct applyDep_s applyDepTable[] = { { RPMTAG_PROVIDENAME, RPMSENSE_FIND_PROVIDES, "provides" }, { RPMTAG_REQUIRENAME, RPMSENSE_FIND_REQUIRES, "requires" }, { RPMTAG_RECOMMENDNAME, RPMSENSE_FIND_REQUIRES, "recommends" }, { RPMTAG_SUGGESTNAME, RPMSENSE_FIND_REQUIRES, "suggests" }, { RPMTAG_SUPPLEMENTNAME, RPMSENSE_FIND_REQUIRES, "supplements" }, { RPMTAG_ENHANCENAME, RPMSENSE_FIND_REQUIRES, "enhances" }, { RPMTAG_CONFLICTNAME, RPMSENSE_FIND_REQUIRES, "conflicts" }, { RPMTAG_OBSOLETENAME, RPMSENSE_FIND_REQUIRES, "obsoletes" }, { RPMTAG_ORDERNAME, RPMSENSE_FIND_REQUIRES, "orderwithrequires" }, { 0, 0, NULL }, }; static int applyAttr(rpmfc fc, int aix, const struct rpmfcAttr_s *attr, const struct exclreg_s *excl, const struct applyDep_s *dep) { int rc = 0; auto range = fc->fahash.equal_range(aix); if (range.first != range.second) { const char *aname = attr->name; char *mname = rstrscat(NULL, "__", aname, "_", dep->name, NULL); std::vector fnx; for (auto it = range.first; it != range.second; ++it) { int fx = it->second; const char *fn = fc->fn[fx].c_str()+fc->buildRoot.size(); /* If the entire path is filtered out, there's nothing more to do */ if (regMatch(excl->exclude_from, fn)) continue; if (regMatch(excl->global_exclude_from, fn)) continue; fnx.push_back(fx); } if (rpmMacroIsDefined(NULL, mname)) { char *ns = rpmfcAttrMacro(aname, "namespace", NULL); /* Sort for reproducibility - hashmap was constructed in parallel */ std::sort(fnx.begin(), fnx.end()); rc = rpmfcHelper(fc, fnx.data(), fnx.size(), attr->proto, excl, dep->type, dep->tag, ns, mname); free(ns); } free(mname); } return rc; } static rpmRC rpmfcApplyInternal(rpmfc fc) { rpmRC rc = RPMRC_OK; int previx; unsigned int val; int dix; int ix; const struct applyDep_s *dep; int skip = 0; struct exclreg_s excl; if (fc->skipProv) skip |= RPMSENSE_FIND_PROVIDES; if (fc->skipReq) skip |= RPMSENSE_FIND_REQUIRES; /* Generate package and per-file dependencies. */ for (dep = applyDepTable; dep->tag; dep++) { int aix = 0; if (skip & dep->type) continue; exclInit(dep->name, &excl); for (auto const & attr : fc->atypes) { if (applyAttr(fc, aix++, attr, &excl, dep)) rc = RPMRC_FAIL; } exclFini(&excl); } /* No more additions after this, freeze pool to minimize memory use */ rpmfcNormalizeFDeps(fc); for (auto const & fdep : fc->fileDeps) { rpmds ds = fdep.dep; rpmdsMerge(packageDependencies(fc->pkg, rpmdsTagN(ds)), ds); } /* Sort by index */ std::sort(fc->fileDeps.begin(), fc->fileDeps.end()); /* Generate per-file indices into package dependencies. */ previx = -1; for (auto const & fdep : fc->fileDeps) { rpmds ds = fdep.dep; ix = fdep.fileIx; rpmds *dsp = packageDependencies(fc->pkg, rpmdsTagN(ds)); dix = rpmdsFind(*dsp, ds); if (dix < 0) continue; val = (rpmdsD(ds) << 24) | (dix & 0x00ffffff); fc->ddictx.push_back(val); if (previx != ix) { previx = ix; fc->fddictx[ix] = fc->ddictx.size() - 1; } fc->fddictn[ix]++; } return rc; } static int initAttrs(rpmfc fc) { ARGV_t files = NULL; char * attrPath = rpmExpand("%{_fileattrsdir}/*.attr", NULL); int nattrs = 0; ARGV_t all_attrs = NULL; /* Discover known attributes from pathnames */ if (rpmGlob(attrPath, NULL, &files) == 0) { int nfiles = argvCount(files); for (int i = 0; i < nfiles; i++) { char *bn = basename(files[i]); bn[strlen(bn)-strlen(".attr")] = '\0'; argvAdd(&all_attrs, bn); } argvFree(files); } /* Get file attributes from _local_file_attrs macro */ char * local_attr_names = rpmExpand("%{?_local_file_attrs}", NULL); ARGV_t local_attrs = argvSplitString(local_attr_names, ":", ARGV_SKIPEMPTY); int nlocals = argvCount(local_attrs); for (int i = 0; i < nlocals; i++) { argvAddUniq(&all_attrs, local_attrs[i]); } /* Initialize attr objects */ nattrs = argvCount(all_attrs); for (int i = 0; i < nattrs; i++) { fc->atypes.push_back(rpmfcAttrNew(all_attrs[i])); } free(attrPath); free(local_attr_names); argvFree(local_attrs); argvFree(all_attrs); return nattrs; } static uint32_t getElfColor(const char *fn) { uint32_t color = 0; #ifdef HAVE_LIBELF int fd = open(fn, O_RDONLY); if (fd >= 0) { Elf *elf = elf_begin (fd, ELF_C_READ, NULL); GElf_Ehdr ehdr; if (elf && gelf_getehdr(elf, &ehdr)) { switch (ehdr.e_ident[EI_CLASS]) { case ELFCLASS64: color = RPMFC_ELF64; break; case ELFCLASS32: color = RPMFC_ELF32; break; } /* Exceptions to coloring */ switch (ehdr.e_machine) { case EM_BPF: color = 0; break; } } if (elf) elf_end(elf); close(fd); } #endif return color; } struct skipped_extension_s { const char *extension; const char *magic_ftype; const char *magic_fmime; }; static const struct skipped_extension_s skipped_extensions[] = { { ".pm", "Perl5 module source text", "text/plain" }, { ".c", "C Code", "text/x-c" }, { ".h", "C Header", "text/x-c" }, { ".la", "libtool library file", "text/plain" }, { ".pc", "pkgconfig file", "text/plain" }, { ".html", "HTML document", "text/html" }, { ".png", "PNG image data", "image/png" }, { ".svg", "SVG Scalable Vector Graphics image", "image/svg+xml" }, { NULL, NULL, NULL } }; rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode) { int msflags = MAGIC_CHECK | MAGIC_COMPRESS | MAGIC_NO_CHECK_TOKENS | MAGIC_ERROR; int mimeflags = msflags | MAGIC_MIME_TYPE; int nerrors = 0; rpmRC rc = RPMRC_FAIL; if (fc == NULL) { rpmlog(RPMLOG_ERR, _("Empty file classifier\n")); return RPMRC_FAIL; } /* It is OK when we have no files to classify. */ if (argv == NULL) return RPMRC_OK; if (initAttrs(fc) < 1) { rpmlog(RPMLOG_ERR, _("No file attributes configured\n")); goto exit; } fc->nfiles = argvCount(argv); fc->fn.assign(fc->nfiles, ""); fc->ftype.assign(fc->nfiles, ""); fc->fmime.assign(fc->nfiles, ""); fc->fattrs = (ARGV_t *)xcalloc(fc->nfiles, sizeof(*fc->fattrs)); fc->fcolor.assign(fc->nfiles, 0); fc->fcdictx.assign(fc->nfiles, 0); fc->fmdictx.assign(fc->nfiles, 0); /* Initialize the per-file dictionary indices. */ fc->fddictx.assign(fc->nfiles, 0); fc->fddictn.assign(fc->nfiles, 0); /* Build (sorted) file class and mime dictionaries. */ fc->cdict = rpmstrPoolCreate(); fc->mdict = rpmstrPoolCreate(); #pragma omp parallel { /* libmagic is not thread-safe, each thread needs to a private handle */ magic_t ms = magic_open(msflags); magic_t mime = magic_open(mimeflags); if (ms == NULL || mime == NULL) { rpmlog(RPMLOG_ERR, _("magic_open(0x%x) failed: %s\n"), msflags, strerror(errno)); #pragma omp cancel parallel } if (magic_load(ms, NULL) == -1) { rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(ms)); #pragma omp cancel parallel } if (magic_load(mime, NULL) == -1) { rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(mime)); #pragma omp cancel parallel } #pragma omp for reduction(+:nerrors) for (int ix = 0; ix < fc->nfiles; ix++) { const char * fmime = NULL; const char * ftype = NULL; const char * s = argv[ix]; size_t slen = strlen(s); int extension_index = 0; int fcolor = RPMFC_BLACK; rpm_mode_t mode = (fmode ? fmode[ix] : 0); int is_executable = (mode & (S_IXUSR|S_IXGRP|S_IXOTH)); switch (mode & S_IFMT) { case S_IFCHR: ftype = "character special"; break; case S_IFBLK: ftype = "block special"; break; case S_IFIFO: ftype = "fifo (named pipe)"; break; case S_IFSOCK: ftype = "socket"; break; case S_IFDIR: ftype = "directory"; break; case S_IFLNK: case S_IFREG: default: /* Skip libmagic for some well known file extensions. To avoid the costly calculcation but also to have a stable output for some */ for (; skipped_extensions[extension_index].extension; extension_index++) { if (rpmFileHasSuffix(s, skipped_extensions[extension_index].extension)) { ftype = skipped_extensions[extension_index].magic_ftype; fmime = skipped_extensions[extension_index].magic_fmime; break; } } /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */ size_t brlen = fc->buildRoot.size(); if (slen >= brlen+sizeof("/dev/") && rstreqn(s+brlen, "/dev/", sizeof("/dev/")-1)) ftype = ""; else if (ftype == NULL) { ftype = magic_file(ms, s); /* Silence errors from immaterial %ghosts */ if (ftype == NULL && errno == ENOENT) ftype = ""; } if (ftype == NULL) { rpmlog(is_executable ? RPMLOG_ERR : RPMLOG_WARNING, _("Recognition of file \"%s\" failed: mode %06o %s\n"), s, mode, magic_error(ms)); /* only executable files are critical to dep extraction */ if (is_executable) { nerrors++; } /* unrecognized non-executables get treated as "data" */ ftype = "data"; } } if (fmime == NULL) { /* not predefined */ fmime = magic_file(mime, s); /* Silence errors from immaterial %ghosts */ if (fmime == NULL && errno == ENOENT) fmime = ""; } if (fmime == NULL) { rpmlog(is_executable ? RPMLOG_ERR : RPMLOG_WARNING, _("Recognition of file mtype \"%s\" failed: mode %06o %s\n"), s, mode, magic_error(ms)); /* only executable files are critical to dep extraction */ if (is_executable) { nerrors++; } fmime = "application/octet-stream"; } rpmlog(RPMLOG_DEBUG, "%s: %s (%s)\n", s, fmime, ftype); /* Save the path. */ fc->fn[ix] = s; /* Add (filtered) file coloring */ fcolor |= rpmfcColor(ftype); /* Add attributes based on file type and/or path */ rpmfcAttributes(fc, ix, ftype, fmime, s); fc->fmime[ix] = fmime; if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE)) fc->ftype[ix] = ftype; /* Add ELF colors */ if (S_ISREG(mode) && is_executable) fc->fcolor[ix] = getElfColor(s); } if (ms != NULL) magic_close(ms); if (mime != NULL) magic_close(mime); } /* omp parallel */ /* Add to file class dictionary and index array */ for (int ix = 0; ix < fc->nfiles; ix++) { const string & ftype = fc->ftype[ix]; const string & fmime = fc->fmime[ix]; /* Pool id's start from 1, for headers we want it from 0 */ fc->fcdictx[ix] = rpmstrPoolId(fc->cdict, ftype.c_str(), 1) - 1; fc->fmdictx[ix] = rpmstrPoolId(fc->mdict, fmime.c_str(), 1) - 1; if (ftype.empty()) fc->fwhite++; else fc->fknown++; } if (nerrors == 0) rc = RPMRC_OK; exit: /* No more additions after this, freeze pool to minimize memory use */ rpmstrPoolFreeze(fc->cdict, 0); rpmstrPoolFreeze(fc->mdict, 0); return rc; } typedef struct DepMsg_s * DepMsg_t; struct DepMsg_s { const char * msg; const char * argv[4]; rpmTagVal ntag; rpmTagVal vtag; rpmTagVal ftag; int mask; int xormask; }; static struct DepMsg_s depMsgs[] = { { "Provides", { "%{?__find_provides}", NULL, NULL, NULL }, RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS, 0, -1 }, { "Requires(interp)", { NULL, "interp", NULL, NULL }, RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS, RPMSENSE_INTERP, 0 }, { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_RPMLIB, 0 }, { "Requires(verify)", { NULL, "verify", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_SCRIPT_VERIFY, 0 }, { "Requires(pre)", { NULL, "pre", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_SCRIPT_PRE, 0 }, { "Requires(post)", { NULL, "post", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_SCRIPT_POST, 0 }, { "Requires(preun)", { NULL, "preun", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_SCRIPT_PREUN, 0 }, { "Requires(postun)", { NULL, "postun", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_SCRIPT_POSTUN, 0 }, { "Requires(pretrans)", { NULL, "pretrans", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_PRETRANS, 0 }, { "Requires(posttrans)", { NULL, "posttrans", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_POSTTRANS, 0 }, { "Requires(preuntrans)", { NULL, "preuntrans", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_PREUNTRANS, 0 }, { "Requires(postuntrans)", { NULL, "postuntrans", NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, RPMSENSE_POSTUNTRANS, 0 }, { "Requires", { "%{?__find_requires}", NULL, NULL, NULL }, 0, 0, RPMTAG_REQUIREFLAGS, /* XXX inherit name/version arrays */ RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 }, { "Conflicts", { "%{?__find_conflicts}", NULL, NULL, NULL }, RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS, 0, -1 }, { "Obsoletes", { "%{?__find_obsoletes}", NULL, NULL, NULL }, RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS, 0, -1 }, { "Recommends", { "%{?__find_recommends}", NULL, NULL, NULL }, RPMTAG_RECOMMENDNAME, RPMTAG_RECOMMENDVERSION, RPMTAG_RECOMMENDFLAGS, 0, -1 }, { "Suggests", { "%{?__find_suggests}", NULL, NULL, NULL }, RPMTAG_SUGGESTNAME, RPMTAG_SUGGESTVERSION, RPMTAG_SUGGESTFLAGS, 0, -1 }, { "Supplements", { "%{?__find_supplements}", NULL, NULL, NULL }, RPMTAG_SUPPLEMENTNAME, RPMTAG_SUPPLEMENTVERSION, RPMTAG_SUPPLEMENTFLAGS, 0, -1 }, { "Enhances", { "%{?__find_enhances}", NULL, NULL, NULL }, RPMTAG_ENHANCENAME, RPMTAG_ENHANCEVERSION, RPMTAG_ENHANCEFLAGS, 0, -1 }, { "OrderWithRequires", { "%{?__find_orderwithrequires}", NULL, NULL, NULL }, RPMTAG_ORDERNAME, RPMTAG_ORDERVERSION, RPMTAG_ORDERFLAGS, 0, -1 }, { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 } }; static DepMsg_t DepMsgs = depMsgs; static void printDeps(rpmfc fc) { DepMsg_t dm; rpmds ds = NULL; const char * DNEVR; rpmsenseFlags Flags; int bingo = 0; for (dm = DepMsgs; dm->msg != NULL; dm++) { if (dm->ntag) { ds = rpmfcDependencies(fc, dm->ntag); } if (dm->ftag == 0) continue; ds = rpmdsInit(ds); if (ds == NULL) continue; /* XXX can't happen */ bingo = 0; while (rpmdsNext(ds) >= 0) { Flags = rpmdsFlags(ds); if (!((Flags & dm->mask) ^ dm->xormask)) continue; if (bingo == 0) { rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : "")); bingo = 1; } if ((DNEVR = rpmdsDNEVR(ds)) == NULL) continue; /* XXX can't happen */ rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2); } if (bingo) rpmlog(RPMLOG_NOTICE, "\n"); } } static rpmRC rpmfcApplyExternal(rpmfc fc) { StringBuf sb_stdin = newStringBuf(); rpmRC rc = RPMRC_OK; /* Create file manifest buffer to deliver to dependency finder. */ for (int i = 0; i < fc->nfiles; i++) appendLineStringBuf(sb_stdin, fc->fn[i]); for (DepMsg_t dm = DepMsgs; dm->msg != NULL; dm++) { rpmTagVal tag = (dm->ftag > 0) ? dm->ftag : dm->ntag; rpmsenseFlags tagflags; char * s = NULL; StringBuf sb_stdout = NULL; int failnonzero = (tag == RPMTAG_PROVIDEFLAGS); switch (tag) { case RPMTAG_PROVIDEFLAGS: if (fc->skipProv) continue; tagflags = RPMSENSE_FIND_PROVIDES; break; case RPMTAG_REQUIREFLAGS: case RPMTAG_RECOMMENDFLAGS: case RPMTAG_SUGGESTFLAGS: case RPMTAG_SUPPLEMENTFLAGS: case RPMTAG_ENHANCEFLAGS: case RPMTAG_CONFLICTFLAGS: case RPMTAG_OBSOLETEFLAGS: case RPMTAG_ORDERFLAGS: if (fc->skipReq) continue; tagflags = RPMSENSE_FIND_REQUIRES; break; default: continue; break; } s = rpmExpand(dm->argv[0], NULL); rpmlog(RPMLOG_NOTICE, _("Finding %s: %s\n"), dm->msg, s); free(s); if (rpmfcExec((ARGV_const_t)dm->argv, sb_stdin, &sb_stdout, failnonzero, fc->buildRoot) == -1) continue; if (sb_stdout == NULL) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg); break; } /* Parse dependencies into header */ rc = parseRCPOT(NULL, fc->pkg, getStringBuf(sb_stdout), dm->ntag ? dm->ntag : RPMTAG_REQUIRENAME, 0, tagflags, addReqProvPkg, NULL); freeStringBuf(sb_stdout); if (rc) { rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg); break; } } freeStringBuf(sb_stdin); return rc; } typedef const struct macroExport_s { const char * name; rpmTagVal tag; } * macroExport; static struct macroExport_s const macroExportList[] = { { "name", RPMTAG_NAME }, { "epoch", RPMTAG_EPOCH }, { "version", RPMTAG_VERSION }, { "release", RPMTAG_RELEASE }, { NULL, 0 } }; rpmRC rpmfcApply(rpmfc fc) { rpmRC rc; Package pkg = fc->pkg; macroExport me; for (me = macroExportList; me->name; me++) { char *val = headerGetAsString(pkg->header, me->tag); if (val) { rpmPushMacro(NULL, me->name, NULL, val, RMIL_SPEC); free(val); } } /* If new-fangled dependency generation is disabled ... */ if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) { if (fc->rpmformat < 6) { /* ... then generate dependencies using %{__find_requires} et al. */ rpmlog(RPMLOG_WARNING, _("Deprecated external dependency generator is used!\n")); rc = rpmfcApplyExternal(fc); } else { rpmlog(RPMLOG_ERR, _("External dependency generator is incompatible with v6 packages\n")); rc = RPMRC_FAIL; } } else { /* ... otherwise generate per-file dependencies */ rc = rpmfcApplyInternal(fc); } for (me = macroExportList; me->name; me++) if (headerIsEntry(pkg->header, me->tag)) rpmPopMacro(NULL, me->name); return rc; } rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg) { rpmfc fc = NULL; int ac = rpmfilesFC(pkg->cpioList); int genConfigDeps = 0; rpmRC rc = RPMRC_OK; int idx; /* Skip packages with no files. */ if (ac == 0) return rc; /* Extract absolute file paths in argv format. */ vector fmode(ac+1, 0); fc = rpmfcCreate(spec->buildRoot, 0); freePackage(fc->pkg); fc->pkg = pkg; fc->skipProv = !pkg->autoProv; fc->skipReq = !pkg->autoReq; fc->rpmformat = spec->rpmformat; rpmfi fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD); while ((idx = rpmfiNext(fi)) >= 0) { /* Does package have any %config files? */ genConfigDeps |= (rpmfiFFlags(fi) & RPMFILE_CONFIG); fmode[idx] = rpmfiFMode(fi); if (!fc->skipReq) { const char *user = rpmfiFUser(fi); const char *group = rpmfiFGroup(fi); rpmsenseFlags ugfl = (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_POSTUN); rpmTagVal deptag = RPMTAG_REQUIRENAME; if (rpmExpandNumeric("%{?_use_weak_usergroup_deps}")) deptag = RPMTAG_RECOMMENDNAME; /* filter out root user/group */ if (user && !rstreq(user, UID_0_USER)) { rpmds ds = rpmdsSingleNS(fc->pool, deptag, "user", user, NULL, ugfl); rpmdsMerge(packageDependencies(pkg, deptag), ds); rpmdsFree(ds); } if (group && !rstreq(group, GID_0_GROUP)) { rpmds ds = rpmdsSingleNS(fc->pool, deptag, "group", group, NULL, ugfl); rpmdsMerge(packageDependencies(pkg, deptag), ds); rpmdsFree(ds); } } } if (!fc->skipProv && genConfigDeps) { /* Add config dependency, Provides: config(N) = EVR */ rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_PROVIDENAME, "config", rpmdsN(pkg->ds), rpmdsEVR(pkg->ds), (RPMSENSE_EQUAL|RPMSENSE_CONFIG)); rpmdsMerge(packageDependencies(pkg, RPMTAG_PROVIDENAME), ds); rpmdsFree(ds); } if (!fc->skipReq && genConfigDeps) { rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_REQUIRENAME, "config", rpmdsN(pkg->ds), rpmdsEVR(pkg->ds), (RPMSENSE_EQUAL|RPMSENSE_CONFIG)); rpmdsMerge(packageDependencies(pkg, RPMTAG_REQUIRENAME), ds); rpmdsFree(ds); } /* Build file class dictionary. */ rc = rpmfcClassify(fc, pkg->dpaths, fmode.data()); if ( rc != RPMRC_OK ) goto exit; /* Build file/package dependency dictionary. */ rc = rpmfcApply(fc); if (rc != RPMRC_OK) goto exit; /* Add per-file colors(#files) */ headerPutUint32(pkg->header, RPMTAG_FILECOLORS, fc->fcolor.data(), fc->nfiles); if (spec->rpmformat >= 6) { /* Add mime types(#mime types) */ for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->mdict); id++) { headerPutString(pkg->header, RPMTAG_MIMEDICT, rpmstrPoolStr(fc->mdict, id)); } /* Add per-file mime types(#files) */ headerPutUint32(pkg->header, RPMTAG_FILEMIMEINDEX, fc->fmdictx.data(), fc->nfiles); } else { /* Add classes(#classes) */ for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->cdict); id++) { headerPutString(pkg->header, RPMTAG_CLASSDICT, rpmstrPoolStr(fc->cdict, id)); } /* Add per-file classes(#files) */ headerPutUint32(pkg->header, RPMTAG_FILECLASS, fc->fcdictx.data(), fc->nfiles); } /* Add dependency dictionary(#dependencies) */ if (!fc->ddictx.empty()) { headerPutUint32(pkg->header, RPMTAG_DEPENDSDICT, fc->ddictx.data(), fc->ddictx.size()); /* Add per-file dependency (start,number) pairs (#files) */ headerPutUint32(pkg->header, RPMTAG_FILEDEPENDSX, fc->fddictx.data(), fc->fddictx.size()); headerPutUint32(pkg->header, RPMTAG_FILEDEPENDSN, fc->fddictn.data(), fc->fddictn.size()); } if (_rpmfc_debug) { char *msg = NULL; rasprintf(&msg, "final: files %d cdict[%d] %d%% ddictx[%zu]", fc->nfiles, rpmstrPoolNumStr(fc->cdict), ((100 * fc->fknown)/fc->nfiles), fc->ddictx.size()); rpmfcPrint(msg, fc, NULL); free(msg); } exit: printDeps(fc); /* Clean up. */ if (fc) fc->pkg = NULL; rpmfcFree(fc); rpmfiFree(fi); return rc; } rpm-software-management-rpm-3c1f23f/build/spec.cc000066400000000000000000000261001511627505500220250ustar00rootroot00000000000000/** \ingroup rpmbuild * \file build/spec.c * Handle spec data structure. */ #include "system.h" #include #include #include #include #include #include #include #include "rpmfi_internal.hh" /* rpmfiles stuff */ #include "rpmbuild_internal.hh" #include "debug.h" #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; } static void freeTriggerFiles(std::vector &triggers) { for (auto & e : triggers) { free(e.fileName); free(e.script); free(e.prog); } triggers.clear(); } struct Source * freeSources(struct Source * s) { struct Source *r, *t = s; while (t != NULL) { r = t; t = t->next; r->fullSource = _free(r->fullSource); r->path = _free(r->path); delete r; } return NULL; } rpmRC lookupPackage(rpmSpec spec, const char *name, int flag,Package *pkg) { char *fullName = NULL; rpmsid nameid = 0; Package p; /* "main" package */ if (name == NULL) { if (pkg) *pkg = spec->packages; return RPMRC_OK; } /* Construct partial package name */ if (!(flag & PART_NAME)) { rasprintf(&fullName, "%s-%s", headerGetString(spec->packages->header, RPMTAG_NAME), name); name = fullName; } nameid = rpmstrPoolId(spec->pool, name, 1); /* Locate package the name */ for (p = spec->packages; p != NULL; p = p->next) { if (p->name && p->name == nameid) { break; } } if (!(flag & PART_QUIET)) { if (p == NULL && pkg != NULL) { rpmlog(RPMLOG_ERR, _("line %d: %s: package %s does not exist\n"), spec->lineNum, spec->line, name); } else if (p != NULL && pkg == NULL) { rpmlog(RPMLOG_ERR, _("line %d: %s: package %s already exists\n"), spec->lineNum, spec->line, name); } } if (fullName == name) free(fullName); if (pkg) *pkg = p; return ((p == NULL) ? RPMRC_FAIL : RPMRC_OK); } Package newPackage(const char *name, rpmstrPool pool, Package *pkglist) { Package p = new Package_s {}; p->header = headerNew(); p->autoProv = 1; p->autoReq = 1; p->fileList = NULL; p->fileExcludeList = NULL; p->fileFile = NULL; p->policyList = NULL; p->pool = rpmstrPoolLink(pool); p->dpaths = NULL; if (name) p->name = rpmstrPoolId(p->pool, name, 1); if (pkglist) { if (*pkglist == NULL) { *pkglist = p; } else { Package pp; /* Always add package to end of list */ for (pp = *pkglist; pp->next != NULL; pp = pp->next) {}; pp->next = p; } } p->next = NULL; return p; } Package freePackage(Package pkg) { if (pkg == NULL) return NULL; pkg->filename = _free(pkg->filename); pkg->preInFile = _free(pkg->preInFile); pkg->postInFile = _free(pkg->postInFile); pkg->preUnFile = _free(pkg->preUnFile); pkg->postUnFile = _free(pkg->postUnFile); pkg->verifyFile = _free(pkg->verifyFile); pkg->preTransFile = _free(pkg->preTransFile); pkg->postTransFile = _free(pkg->postTransFile); pkg->preunTransFile = _free(pkg->preunTransFile); pkg->postunTransFile = _free(pkg->postunTransFile); pkg->header = headerFree(pkg->header); pkg->ds = rpmdsFree(pkg->ds); for (int i=0; idependencies[i] = rpmdsFree(pkg->dependencies[i]); } pkg->fileList = argvFree(pkg->fileList); pkg->fileExcludeList = argvFree(pkg->fileExcludeList); pkg->fileFile = argvFree(pkg->fileFile); pkg->policyList = argvFree(pkg->policyList); pkg->removePostfixes = argvFree(pkg->removePostfixes); pkg->cpioList = rpmfilesFree(pkg->cpioList); pkg->dpaths = argvFree(pkg->dpaths); pkg->icon = freeSources(pkg->icon); freeTriggerFiles(pkg->triggerFiles); freeTriggerFiles(pkg->fileTriggerFiles); freeTriggerFiles(pkg->transFileTriggerFiles); pkg->pool = rpmstrPoolFree(pkg->pool); delete pkg; return NULL; } static Package freePackages(Package packages) { Package p; while ((p = packages) != NULL) { packages = p->next; p->next = NULL; freePackage(p); } return NULL; } rpmds * packageDependencies(Package pkg, rpmTagVal tag) { for (int i=0; idependencies[i] == NULL) { return &pkg->dependencies[i]; } rpmTagVal tagN = rpmdsTagN(pkg->dependencies[i]); if (tagN == tag || tagN == 0) { return &pkg->dependencies[i]; } } return NULL; } rpmSpec newSpec(void) { rpmSpec spec = new rpmSpec_s {}; spec->specFile = NULL; spec->fileStack = NULL; spec->lbufSize = BUFSIZ * 10; spec->lbuf = (char *)xmalloc(spec->lbufSize); spec->lbuf[0] = '\0'; spec->line = spec->lbuf; spec->nextline = NULL; spec->nextpeekc = '\0'; spec->lineNum = 0; spec->readStack = new ReadLevelEntry {}; spec->readStack->next = NULL; spec->readStack->reading = 1; spec->readStack->lastConditional = lineTypes; spec->readStack->readable = 1; spec->rootDir = NULL; spec->sources = NULL; spec->packages = NULL; spec->noSource = 0; spec->numSources = 0; spec->autonum_patch = -1; spec->autonum_source = -1; spec->sourceRpmName = NULL; spec->sourcePkgId = NULL; spec->sourcePackage = NULL; spec->buildRoot = NULL; spec->buildDir = NULL; spec->buildRestrictions = headerNew(); spec->BANames = NULL; spec->BACount = 0; spec->recursing = 0; spec->BASpecs = NULL; spec->flags = RPMSPEC_NONE; spec->macros = rpmGlobalMacroContext; spec->pool = rpmstrPoolCreate(); specLuaInit(spec); return spec; } rpmSpec rpmSpecFree(rpmSpec spec) { if (spec == NULL) return NULL; for (int i = 0; i < NR_SECT; i++) freeStringBuf(spec->sections[i]); freeStringBuf(spec->parsed); spec->buildRoot = _free(spec->buildRoot); spec->buildDir = _free(spec->buildDir); spec->specFile = _free(spec->specFile); closeSpec(spec); while (spec->readStack) { struct ReadLevelEntry *rl = spec->readStack; spec->readStack = rl->next; rl->next = NULL; delete rl; } spec->lbuf = _free(spec->lbuf); spec->sourceRpmName = _free(spec->sourceRpmName); spec->sourcePkgId = _free(spec->sourcePkgId); spec->sourcePackage = freePackage(spec->sourcePackage); spec->buildRestrictions = headerFree(spec->buildRestrictions); for (int i = 0; i < NR_SECT; i++) { argvFree(spec->buildopts[i]); argvFree(spec->sectionparts[i]); argiFree(spec->sectionops[i]); } if (!spec->recursing) { if (spec->BASpecs != NULL) while (spec->BACount--) { spec->BASpecs[spec->BACount] = rpmSpecFree(spec->BASpecs[spec->BACount]); } spec->BASpecs = _free(spec->BASpecs); } spec->BANames = _free(spec->BANames); // only destroy lua tables if there are no BASpecs left if (spec->recursing || spec->BACount == 0) { specLuaFini(spec); } spec->sources = freeSources(spec->sources); spec->packages = freePackages(spec->packages); spec->pool = rpmstrPoolFree(spec->pool); spec->buildHost = _free(spec->buildHost); delete spec; return NULL; } Header rpmSpecSourceHeader(rpmSpec spec) { return (spec && spec->sourcePackage) ? spec->sourcePackage->header : NULL; } rpmds rpmSpecDS(rpmSpec spec, rpmTagVal tag) { return (spec != NULL) ? rpmdsNew(spec->sourcePackage->header, tag, 0) : NULL; } rpmps rpmSpecCheckDeps(rpmts ts, rpmSpec spec) { /* Make a temporary immutable header to appease rpmtsAddInstallElement() */ Header h = headerReload(headerCopy(rpmSpecSourceHeader(spec)), RPMTAG_HEADERIMMUTABLE); rpmps probs = NULL; rpmtsEmpty(ts); rpmtsAddInstallElement(ts, h, NULL, 0, NULL); rpmtsCheck(ts); probs = rpmtsProblems(ts); rpmtsEmpty(ts); headerFree(h); return probs; } struct rpmSpecIter_s { void *next; }; #define SPEC_LISTITER_INIT(_itertype, _iteritem) \ _itertype iter = NULL; \ if (spec) { \ iter = new rpmSpecIter_s {}; \ iter->next = spec->_iteritem; \ } \ return iter #define SPEC_LISTITER_NEXT(_valuetype) \ _valuetype item = NULL; \ if (iter) { \ item = (_valuetype)iter->next; \ iter->next = (item) ? item->next : NULL; \ } \ return item #define SPEC_LISTITER_FREE() \ delete iter; \ return NULL rpmSpecPkgIter rpmSpecPkgIterInit(rpmSpec spec) { SPEC_LISTITER_INIT(rpmSpecPkgIter, packages); } rpmSpecPkgIter rpmSpecPkgIterFree(rpmSpecPkgIter iter) { SPEC_LISTITER_FREE(); } rpmSpecPkg rpmSpecPkgIterNext(rpmSpecPkgIter iter) { SPEC_LISTITER_NEXT(rpmSpecPkg); } Header rpmSpecPkgHeader(rpmSpecPkg pkg) { return (pkg != NULL) ? pkg->header : NULL; } char* rpmSpecPkgGetSection(rpmSpecPkg pkg, int section) { if (pkg) { switch (section) { case RPMBUILD_FILE_FILE: return argvJoin(pkg->fileFile, ""); case RPMBUILD_FILE_LIST: return argvJoin(pkg->fileList, ""); case RPMBUILD_POLICY: return argvJoin(pkg->policyList, ""); } } return NULL; } rpmSpecSrcIter rpmSpecSrcIterInit(rpmSpec spec) { SPEC_LISTITER_INIT(rpmSpecSrcIter, sources); } rpmSpecSrcIter rpmSpecSrcIterFree(rpmSpecSrcIter iter) { SPEC_LISTITER_FREE(); } rpmSpecSrc rpmSpecSrcIterNext(rpmSpecSrcIter iter) { SPEC_LISTITER_NEXT(rpmSpecSrc); } rpmSourceFlags rpmSpecSrcFlags(rpmSpecSrc src) { return (src != NULL) ? src->flags : 0; } int rpmSpecSrcNum(rpmSpecSrc src) { return (src != NULL) ? src->num : -1; } const char * rpmSpecSrcFilename(rpmSpecSrc src, int full) { const char *source = NULL; if (src) { source = full ? src->fullSource : src->source; } return source; } const char * rpmSpecGetSection(rpmSpec spec, int section) { if (spec) { switch (section) { case RPMBUILD_NONE: return getStringBuf(spec->parsed); case RPMBUILD_PREP: return getStringBuf(spec->sections[SECT_PREP]); case RPMBUILD_CONF: return getStringBuf(spec->sections[SECT_CONF]); case RPMBUILD_BUILDREQUIRES: return getStringBuf(spec->sections[SECT_BUILDREQUIRES]); case RPMBUILD_BUILD: return getStringBuf(spec->sections[SECT_BUILD]); case RPMBUILD_INSTALL: return getStringBuf(spec->sections[SECT_INSTALL]); case RPMBUILD_CHECK: return getStringBuf(spec->sections[SECT_CHECK]); case RPMBUILD_CLEAN: return getStringBuf(spec->sections[SECT_CLEAN]); } } return NULL; } int rpmspecQuery(rpmts ts, QVA_t qva, const char * arg) { rpmSpec spec = NULL; int res = 1; if (qva->qva_showPackage == NULL) goto exit; spec = rpmSpecParse(arg, (RPMSPEC_ANYARCH|RPMSPEC_FORCE), NULL); if (spec == NULL) { rpmlog(RPMLOG_ERR, _("query of specfile %s failed, can't parse\n"), arg); goto exit; } if (qva->qva_source == RPMQV_SPECRPMS || qva->qva_source == RPMQV_SPECBUILTRPMS) { res = 0; for (Package pkg = spec->packages; pkg != NULL; pkg = pkg->next) { if (qva->qva_source == RPMQV_SPECBUILTRPMS && pkg->fileList == NULL) continue; res += qva->qva_showPackage(qva, ts, pkg->header); } } else { Package sourcePkg = spec->sourcePackage; res = qva->qva_showPackage(qva, ts, sourcePkg->header); } exit: rpmSpecFree(spec); return res; } rpm-software-management-rpm-3c1f23f/build/speclua.cc000066400000000000000000000023351511627505500225330ustar00rootroot00000000000000 #include "system.h" #include "rpmlua.hh" #include "rpmbuild_internal.hh" #include "debug.h" static const char * luavars[] = { "patches", "sources", "patch_nums", "source_nums", NULL, }; void * specLuaInit(rpmSpec spec) { rpmlua lua = rpmluaGetGlobalState(); lua_State *L = (lua_State *)rpmluaGetLua(lua); for (const char **vp = luavars; vp && *vp; vp++) { lua_newtable(L); lua_setglobal(L, *vp); } return lua; } void * specLuaFini(rpmSpec spec) { rpmlua lua = rpmluaGetGlobalState(); lua_State *L = (lua_State *)rpmluaGetLua(lua); for (const char **vp = luavars; vp && *vp; vp++) { lua_pushnil(L); lua_setglobal(L, *vp); } return NULL; } void addLuaSource(const struct Source *p) { rpmlua lua = rpmluaGetGlobalState(); lua_State *L = (lua_State *)rpmluaGetLua(lua); const char * what = (p->flags & RPMBUILD_ISPATCH) ? "patches" : "sources"; lua_getglobal(L, what); lua_pushstring(L, p->path); lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); lua_pop(L, 1); what = (p->flags & RPMBUILD_ISPATCH) ? "patch_nums" : "source_nums"; lua_getglobal(L, what); lua_pushinteger(L, p->num); lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); lua_pop(L, 1); } rpm-software-management-rpm-3c1f23f/cmake/000077500000000000000000000000001511627505500205465ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/cmake/rpm-config.cmake.in000066400000000000000000000003201511627505500242110ustar00rootroot00000000000000@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/rpm-targets.cmake") check_required_components(rpm) set(RPM_CONFIGDIR @RPM_CONFIGDIR@) set(RPM_PLUGINDIR @RPM_PLUGINDIR@) set(RPM_MACROSDIR @RPM_MACROSDIR@) rpm-software-management-rpm-3c1f23f/config.h.in000066400000000000000000000125531511627505500215170ustar00rootroot00000000000000#cmakedefine DBUS @DBUS@ #cmakedefine ENABLE_INHIBIT_PLUGIN @ENABLE_INHIBIT_PLUGIN@ #cmakedefine ENABLE_NDB @ENABLE_NDB@ #cmakedefine ENABLE_NLS @ENABLE_NLS@ #cmakedefine ENABLE_OPENMP @ENABLE_OPENMP@ #cmakedefine ENABLE_PLUGINS @ENABLE_PLUGINS@ #cmakedefine ENABLE_ASAN @ENABLE_ASAN@ #cmakedefine HAVE_BASENAME @HAVE_BASENAME@ #cmakedefine HAVE_BN2BINPAD @HAVE_BN2BINPAD@ #cmakedefine HAVE_BZLIB_H @HAVE_BZLIB_H@ #cmakedefine HAVE_CAP_COMPARE @HAVE_CAP_COMPARE@ #cmakedefine HAVE_DECL_FDATASYNC @HAVE_DECL_FDATASYNC@ #cmakedefine HAVE_DIRENT_H @HAVE_DIRENT_H@ #cmakedefine HAVE_DIRNAME @HAVE_DIRNAME@ #cmakedefine HAVE_DLFCN_H @HAVE_DLFCN_H@ #cmakedefine HAVE_DSA_SET0_KEY @HAVE_DSA_SET0_KEY@ #cmakedefine HAVE_DSA_SET0_PQG @HAVE_DSA_SET0_PQG@ #cmakedefine HAVE_DSA_SIG_SET0 @HAVE_DSA_SIG_SET0@ #cmakedefine HAVE_DWELF_ELF_BEGIN @HAVE_DWELF_ELF_BEGIN@ #cmakedefine HAVE_ELFUTILS_LIBDWELF_H @HAVE_ELFUTILS_LIBDWELF_H@ #cmakedefine HAVE_EVP_MD_CTX_NEW @HAVE_EVP_MD_CTX_NEW@ #cmakedefine HAVE_FCHMODAT @HAVE_FCHMODAT@ #cmakedefine HAVE_FCHOWNAT @HAVE_FCHOWNAT@ #cmakedefine HAVE_FDATASYNC @HAVE_FDATASYNC@ #cmakedefine HAVE_FSTATAT @HAVE_FSTATAT@ #cmakedefine HAVE_GELF_H @HAVE_GELF_H@ #cmakedefine HAVE_GETADDRINFO @HAVE_GETADDRINFO@ #cmakedefine HAVE_GETAUXVAL @HAVE_GETAUXVAL@ #cmakedefine HAVE_GETCWD @HAVE_GETCWD@ #cmakedefine HAVE_GETLINE @HAVE_GETLINE@ #cmakedefine HAVE_GETOPT_H @HAVE_GETOPT_H@ #cmakedefine HAVE_GETTEXT @HAVE_GETTEXT@ #cmakedefine HAVE_GZSEEK @HAVE_GZSEEK@ #cmakedefine HAVE_ICONV @HAVE_ICONV@ #cmakedefine HAVE_INTTYPES_H @HAVE_INTTYPES_H@ #cmakedefine HAVE_LCHOWN @HAVE_LCHOWN@ #cmakedefine HAVE_LIBCRYPTO @HAVE_LIBCRYPTO@ #cmakedefine HAVE_LIBDW @HAVE_LIBDW@ #cmakedefine HAVE_LIBELF @HAVE_LIBELF@ #cmakedefine HAVE_LIBNSL @HAVE_LIBNSL@ #cmakedefine HAVE_LIBPTHREAD @HAVE_LIBPTHREAD@ #cmakedefine HAVE_LIBSELINUX @HAVE_LIBSELINUX@ #cmakedefine HAVE_LIBTHREAD @HAVE_LIBTHREAD@ #cmakedefine HAVE_LIMITS_H @HAVE_LIMITS_H@ #cmakedefine HAVE_LINKAT @HAVE_LINKAT@ #cmakedefine HAVE_LINUX_FSVERITY_H @HAVE_LINUX_FSVERITY_H@ #cmakedefine HAVE_LOCALTIME_R @HAVE_LOCALTIME_R@ #cmakedefine HAVE_LSETXATTR @HAVE_LSETXATTR@ #cmakedefine HAVE_IMAEVM_SIGNHASH @HAVE_IMAEVM_SIGNHASH@ #cmakedefine HAVE_LUTIMES @HAVE_LUTIMES@ #cmakedefine HAVE_LZMA_H @HAVE_LZMA_H@ #cmakedefine HAVE_MEMORY_H @HAVE_MEMORY_H@ #cmakedefine HAVE_MEMPCPY @HAVE_MEMPCPY@ #cmakedefine HAVE_MERGESORT @HAVE_MERGESORT@ #cmakedefine HAVE_MKDIRAT @HAVE_MKDIRAT@ #cmakedefine HAVE_MKFIFOAT @HAVE_MKFIFOAT@ #cmakedefine HAVE_MKNODAT @HAVE_MKNODAT@ #cmakedefine HAVE_MKSTEMP @HAVE_MKSTEMP@ #cmakedefine HAVE_MREMAP @HAVE_MREMAP@ #cmakedefine HAVE_NDIR_H @HAVE_NDIR_H@ #cmakedefine HAVE_NL_MSG_CAT_CNTR @HAVE_NL_MSG_CAT_CNTR@ #cmakedefine HAVE_OPENAT @HAVE_OPENAT@ #cmakedefine HAVE_OPENSSL_DSA_H @HAVE_OPENSSL_DSA_H@ #cmakedefine HAVE_OPENSSL_EVP_H @HAVE_OPENSSL_EVP_H@ #cmakedefine HAVE_OPENSSL_RSA_H @HAVE_OPENSSL_RSA_H@ #cmakedefine HAVE_PTHREAD_H @HAVE_PTHREAD_H@ #cmakedefine HAVE_PUTENV @HAVE_PUTENV@ #cmakedefine HAVE_READLINE @HAVE_READLINE@ #cmakedefine HAVE_REALPATH @HAVE_REALPATH@ #cmakedefine HAVE_REGCOMP @HAVE_REGCOMP@ #cmakedefine HAVE_RENAMEAT @HAVE_RENAMEAT@ #cmakedefine HAVE_RSA_SET0_KEY @HAVE_RSA_SET0_KEY@ #cmakedefine HAVE_SCHED_GETAFFINITY @HAVE_SCHED_GETAFFINITY@ #cmakedefine HAVE_SECURE_GETENV @HAVE_SECURE_GETENV@ #cmakedefine HAVE_SETENV @HAVE_SETENV@ #cmakedefine HAVE_SETEXECFILECON @HAVE_SETEXECFILECON@ #cmakedefine HAVE_SETPROGNAME @HAVE_SETPROGNAME@ #cmakedefine HAVE_STATVFS @HAVE_STATVFS@ #cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ #cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ #cmakedefine HAVE_STPCPY @HAVE_STPCPY@ #cmakedefine HAVE_STPNCPY @HAVE_STPNCPY@ #cmakedefine HAVE_STRINGS_H @HAVE_STRINGS_H@ #cmakedefine HAVE_STRING_H @HAVE_STRING_H@ #cmakedefine HAVE_STRUCT_DIRENT_D_TYPE @HAVE_STRUCT_DIRENT_D_TYPE@ #cmakedefine HAVE_SYMLINKAT @HAVE_SYMLINKAT@ #cmakedefine HAVE_SYNCFS @HAVE_SYNCFS@ #cmakedefine HAVE_SYS_AUXV_H @HAVE_SYS_AUXV_H@ #cmakedefine HAVE_SYS_DIR_H @HAVE_SYS_DIR_H@ #cmakedefine HAVE_SYS_NDIR_H @HAVE_SYS_NDIR_H@ #cmakedefine HAVE_SYS_PARAM_H @HAVE_SYS_PARAM_H@ #cmakedefine HAVE_SYS_STAT_H @HAVE_SYS_STAT_H@ #cmakedefine HAVE_SYS_SYSTEMCFG_H @HAVE_SYS_SYSTEMCFG_H@ #cmakedefine HAVE_SYS_TYPES_H @HAVE_SYS_TYPES_H@ #cmakedefine HAVE_SYS_UTSNAME_H @HAVE_SYS_UTSNAME_H@ #cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@ #cmakedefine HAVE_UNLINKAT @HAVE_UNLINKAT@ #cmakedefine HAVE_UNSETENV @HAVE_UNSETENV@ #cmakedefine HAVE_UNSHARE @HAVE_UNSHARE@ #cmakedefine HAVE_UTIMENSAT @HAVE_UTIMENSAT@ #cmakedefine HAVE_UTIMES @HAVE_UTIMES@ #cmakedefine HAVE_ZLIB_H @HAVE_ZLIB_H@ #cmakedefine HAVE_ZSTD @HAVE_ZSTD@ #cmakedefine HAVE___PROGNAME @HAVE___PROGNAME@ #cmakedefine HAVE___SECURE_GETENV @HAVE___SECURE_GETENV@ #cmakedefine HAVE_GLOB_ONLYDIR @HAVE_GLOB_ONLYDIR@ #cmakedefine MAJOR_IN_MKDEV @MAJOR_IN_MKDEV@ #cmakedefine MAJOR_IN_SYSMACROS @MAJOR_IN_SYSMACROS@ #cmakedefine RUNDIR @rundir@ #cmakedefine WITH_ACL @WITH_ACL@ #cmakedefine WITH_AUDIT @WITH_AUDIT@ #cmakedefine ENABLE_BDB_RO @ENABLE_BDB_RO@ #cmakedefine WITH_CAP @WITH_CAP@ #cmakedefine WITH_FSVERITY @WITH_FSVERITY@ #cmakedefine WITH_IMAEVM @WITH_IMAEVM@ #cmakedefine WITH_SELINUX @WITH_SELINUX@ #cmakedefine ENABLE_SQLITE @ENABLE_SQLITE@ #define UID_0_USER "@UID_0_USER@" #define GID_0_GROUP "@GID_0_GROUP@" #define C_LOCALE "@C_LOCALE@" #define PACKAGE "@PROJECT_NAME@" #define VERSION "@PROJECT_VERSION@" #define PACKAGE_BUGREPORT "@PROJECT_HOMEPAGE_URL@" #define RPM_VENDOR "@RPM_VENDOR@" rpm-software-management-rpm-3c1f23f/docs/000077500000000000000000000000001511627505500204165ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/docs/CMakeLists.txt000066400000000000000000000047521511627505500231660ustar00rootroot00000000000000set(site_dir ${CMAKE_BINARY_DIR}/site) # Generates Jekyll source pages add_custom_target(pages) if (WITH_DOXYGEN) find_package(Doxygen REQUIRED) endif() if (DOXYGEN_FOUND) # XXX API docs should be pre-built in tarballs file(GLOB headers ${CMAKE_SOURCE_DIR}/include/rpm/*.h) set(DOXYGEN_WARN_IF_UNDOCUMENTED NO) set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) set(DOXYGEN_HTML_OUTPUT ${site_dir}/api) if (ENABLE_WERROR) set(DOXYGEN_WARN_AS_ERROR YES) endif() doxygen_add_docs(apidoc librpm/Doxyheader.h ${headers} ALL USE_STAMP_FILE) add_dependencies(pages apidoc) install(DIRECTORY ${DOXYGEN_HTML_OUTPUT}/ DESTINATION ${CMAKE_INSTALL_DOCDIR}/API) endif() set(manuals manual/about.md manual/arch_dependencies.md manual/autosetup.md manual/boolean_dependencies.md manual/buildprocess.md manual/buildsystem.md manual/conditionalbuilds.md manual/dependencies.md manual/dependency_generators.md manual/devel_documentation.md manual/dynamic_specs.md manual/file_triggers.md manual/format_header.md manual/format_lead.md manual/format_v3.md manual/format_v4.md manual/format_v6.md manual/index.md manual/large_files.md manual/lua.md manual/macros.md manual/more_dependencies.md manual/philosophy.md manual/plugins.md manual/queryformat.md manual/relocatable.md manual/scriptlet_expansion.md manual/signatures_digests.md manual/spec.md manual/tags.md manual/triggers.md manual/tsort.md manual/users_and_groups.md ) install(FILES ${manuals} TYPE DOC) # Configure the Jekyll site to build set(site_files _layouts/default.html _layouts/redirected.html _layouts/favicon.ico assets/css/manpage.css man/index.md Gemfile ${manuals} ) file(MAKE_DIRECTORY ${site_dir}) configure_file(index.md.in ${site_dir}/index.md @ONLY) foreach(file ${site_files}) configure_file(${file} ${site_dir}/${file} COPYONLY) endforeach() if (PODMAN) option(JEKYLL_SERVE "Serve site locally when built" ON) set(JEKYLL_SOURCE_DIR ${site_dir} CACHE PATH "Directory to build from") find_program(PODMAN podman REQUIRED) mark_as_advanced(PODMAN) set(image rpm/jekyll) set(JEKYLL ${PODMAN} run -v ${JEKYLL_SOURCE_DIR}:/srv:z -it --rm -p 4000:4000 ${image}) if (JEKYLL_SERVE) set(JEKYLL ${JEKYLL} serve --host 0.0.0.0) else() set(JEKYLL ${JEKYLL} build) endif() add_custom_command(OUTPUT image COMMAND ${PODMAN} build -t ${image} ${CMAKE_CURRENT_SOURCE_DIR} COMMAND touch image DEPENDS Containerfile ) add_custom_target(site COMMAND ${JEKYLL} DEPENDS pages image) endif() add_subdirectory(man) rpm-software-management-rpm-3c1f23f/docs/Containerfile000066400000000000000000000004071511627505500231240ustar00rootroot00000000000000FROM registry.fedoraproject.org/fedora:41 RUN dnf install -y \ gcc \ gcc-c++ \ glibc-langpack-en \ make \ ruby-devel COPY Gemfile . RUN bundle install ENV LC_ALL en_US.UTF-8 EXPOSE 4000 WORKDIR /srv ENTRYPOINT ["bundle", "exec", "jekyll"] rpm-software-management-rpm-3c1f23f/docs/Gemfile000066400000000000000000000001231511627505500217050ustar00rootroot00000000000000source "https://rubygems.org" gem 'github-pages', '~> 232', group: :jekyll_plugins rpm-software-management-rpm-3c1f23f/docs/README.md000066400000000000000000000044571511627505500217070ustar00rootroot00000000000000RPM Documentation ================= There are multiple pieces of documentation that are processed and used differently. RPM.org ------- The [rpm.org web page](https://rpm.org/) is created from its own [GitHub repository](https://github.com/rpm-software-management/rpm-web). Man Pages --------- The man pages in *man/* are scdoc sources. They are rendered to the man page format during build. Consult *man/rpm-man-template.scd* for style guidance, and use it as a template for new man pages. The man pages are also rendered to HTML together with the Reference Manual (see below). API Reference ------------- The API reference is rendered with *Doxygen*. The content is almost exclusively created from the RPM sources and the doc strings there in. The rendered HTML is shipped with the release tarball to be able to build RPM from that without *Doxygen*. Reference Manual ---------------- The reference manual in the *manual/* dir is an GitHub Pages site. It is rendered automatically when pushed into the *master* branch in the GitHub repository. It is available online on [its own URL](https://rpm-software-management.github.io/rpm/manual/) but is intended to be used as part of the [RPM.org website](https://rpm.org/) were it is linked from the [Documentation page](https://rpm.org/documentation.html) The Reference Manual is currently not shipped in rendered form in the tarball. To render the site locally, run the following command from your CMake build directory (requires Podman): make site This builds the site in a container and then serves it locally at `http://0.0.0.0:4000` for preview. You can further tweak this behavior with the following CMake options: - `JEKYLL_SERVE`: Set to `OFF` to just build the site, not serve it. Default: `ON`. - `JEKYLL_SOURCE_DIR`: The source path to build, useful for building the [rpm-web](https://github.com/rpm-software-management/rpm-web) site locally (e.g. in a dedicated CMake build directory). Default: `${CMAKE_BINARY_DIR}/site`. If you wish to render the site manually, prepare the source pages by running `make pages` and then follow the steps in [this article](https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/testing-your-github-pages-site-locally-with-jekyll) (the source directory to build is located at `${CMAKE_BINARY_DIR}/site`). rpm-software-management-rpm-3c1f23f/docs/_layouts/000077500000000000000000000000001511627505500222555ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/docs/_layouts/default.html000066400000000000000000000040421511627505500245670ustar00rootroot00000000000000 {{ page.title }} {% if page.css %} {% endif %} rpm-software-management-rpm-3c1f23f/docs/_layouts/favicon.ico000066400000000000000000000050121511627505500243740ustar00rootroot00000000000000  ( & SehTg57ea2gZ8eb2g]77eb2g]7eb2g\07db0 hU%{%c{()9"))$'(())))))&{O @%efWBJKOMt! $"$"#!#!U$9Gx{M/#!D#!#!#!#!m #!w#!#!#!#!#!#!#!#!#!#! #!#!׬#!#!#!#!#!#!#! #!ײ#!#!#!פ#!)9" N#!#!#!#!#!f"'r+#!~#!#!#GlfC!!!!!?rpm-software-management-rpm-3c1f23f/docs/_layouts/redirected.html000066400000000000000000000006451511627505500252620ustar00rootroot00000000000000

Redirecting...

Click here if you are not redirected. rpm-software-management-rpm-3c1f23f/docs/assets/000077500000000000000000000000001511627505500217205ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/docs/assets/css/000077500000000000000000000000001511627505500225105ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/docs/assets/css/manpage.css000066400000000000000000000017771511627505500246460ustar00rootroot00000000000000body { font-family: monospace; font-size: 14px; } h1, h2, h3 { font-family: monospace; } h1 { font-size: 32px; } h2 { font-size: 21px; } h3 { font-size: 18px; } header p.header-date { display: none; } footer { height: 1em; background: url('https://rpm.org/images/bg_hr.png') repeat-x top; } footer p { float: left; } footer p#version { width: 25%; text-align: left; } footer p#index { width: 50%; text-align: center; } footer p#date { width: 25%; text-align: right; } blockquote { color: #000000; border: 0; margin-left: 0; padding-left: 4ch; } /* Remove empty line above option descriptions */ p + blockquote { margin-top: -0.8em; } table { width: 100%; } pre, code { margin: 0; padding: 0; box-shadow: none; } pre code { margin-left: 4ch; } /* Fancy section anchor links */ h2 a, h3 a { display: none; } h2:hover a, h3:hover a { display: inline; text-decoration: none; margin-left: -0.3em; } rpm-software-management-rpm-3c1f23f/docs/index.md.in000066400000000000000000000002631511627505500224550ustar00rootroot00000000000000--- layout: default title: rpm.org - Documentation --- # RPM @CMAKE_PROJECT_VERSION@ Documentation * [Man pages](man/) * [Reference Manual](manual/) * [API Documentation](api/) rpm-software-management-rpm-3c1f23f/docs/librpm/000077500000000000000000000000001511627505500217035ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/docs/librpm/Doxyheader.h000066400000000000000000000106711511627505500241550ustar00rootroot00000000000000/*! \mainpage librpm API Documentation. This documents the librpm API as available to rpm itself and to the various depsolvers or language bindings that rely upon it. It enables to build tools for: - \link rpmbuild creating \endlink, - \link rpmsign signing \endlink, - \link rpmtd querying \endlink, - or \link rpmts (un)installing \endlink RPM packages. Thread-safety: in general, data and object-like elements in the rpm API are only safe to be accessed and disposed of from the originating thread. Some central resources have rudimentary protection to support concurrent access though: - rpm configuration and macros - rpmlog subsystem - rpm string pool objects - rpm keyring and public keys */ /** \defgroup buildsign Building & signing packages: * * @{ */ /** \defgroup rpmbuild Build API. \brief API for building packages. */ /** \defgroup signature Signature Tags API. \brief List of signature tags. */ /** \defgroup rpmsign Signature API. \brief How to add or remove a signature from a package header. */ /** @}*/ /** \defgroup datatypes Data types: * * @{ */ /** \defgroup rpmtypes RPM data types. \brief The abstract RPM data types. */ /** \defgroup rpmstring String Manipulation API. \brief String Manipulation API. */ /** \defgroup rpmstrpool String Pool API. \brief How to store strings in pools. */ /** \defgroup rpmver RPM version API. \brief Rpm version comparison API. */ /** @} */ /** \defgroup install (un)Installing packages: * * @{ */ /** \defgroup rpmds Dependency Set API. \brief How to compare dependencies. */ /** \defgroup rpmcallback Callback signature & types. \brief The signature of function to register as callback and the cases where it can be called */ /** \defgroup rpmts Transaction Set API. \brief How to create, run & destroy a package transaction. */ /** \defgroup rpmte Transaction Element API. \brief How to retrieve information from a transaction element. */ /** \defgroup rpmps Problem Set API. \brief Problem Set API. */ /** \defgroup rpmprob Problem Element API. \brief Problem Element API. */ /** \defgroup rpmvf Verify API. \brief How to verify a package */ /** @} */ /** \defgroup rpmfiles File Info Set API. \brief File Info Set API. */ /** \defgroup rpmfi File Info Set Iterator API. \brief File Info Set Iterator API. */ /** \defgroup payload File archive (aka payload) API. \brief File archive (aka payload) API. */ /** \defgroup rpmfc File Classification API. \brief Structures and methods for build-time file classification */ /** \defgroup rpmkeyring RPM keyring API. \brief RPM keyring API. */ /** \defgroup rpmmacro Macro API. \brief Macro API. */ /** \defgroup rpmlog Logging API. \brief RPM Logging facilities. */ /** \defgroup rpmcrypto RPM crypto API. \brief RPM cryptography related APIs and constants. */ /** \defgroup rpmpgp OpenPGP API. \brief OpenPGP constants and structures from RFC-2440. */ /** \defgroup rpmplugin RPM plugins API \brief Structures and methods for developint RPM plugins */ /** \defgroup headquery Querying package headers: * * @{ */ /** \defgroup header Header API. \brief How to manipulate package headers (which carries all information about a package). */ /** \defgroup rpmtag RPM Tag API. \brief Manipulating RPM tags (accessing values, types, ...) */ /** \defgroup rpmtd RPM Tag Data Container API. \brief How to retrieve data from package headers. */ /** @} */ /** \defgroup io I/O * * @{ */ /** \defgroup rpmdb Database API. \brief Opening & accessing the RPM database */ /** \defgroup rpmio RPM IO API. \brief The RPM IO API (Fd_t is RPM equivalent to libc's FILE). */ /** \defgroup rpmfileutil File and Path Manipulation API. \brief File and path manipulation helper functions. */ /** \defgroup rpmurl URL Manipulation API. \brief A couple utils for URL Manipulation. */ /** \defgroup rpmargv Argument Manipulation API. \brief Argument Manipulation API. */ /** \defgroup rpmcli Command Line API. \brief Parsing RPM command line arguments. */ /** \defgroup rpmsq Signal Queue API. \brief Signal Queue API. */ /** \defgroup rpmsw Statistics API. \brief Statistics API. */ /** \defgroup rpmrc RPMRC. \brief Reading config files and getting some important configuration values. */ /** \defgroup rpmutil Miscellaneous utility APIs \brief Miscellaneous utility APIs */ /** @} */ rpm-software-management-rpm-3c1f23f/docs/man/000077500000000000000000000000001511627505500211715ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/docs/man/.gitignore000066400000000000000000000000101511627505500231500ustar00rootroot00000000000000*.1 *.8 rpm-software-management-rpm-3c1f23f/docs/man/CMakeLists.txt000066400000000000000000000042621511627505500237350ustar00rootroot00000000000000set(core gendiff.1 rpm2cpio.1 rpm2archive.1 rpmuncompress.1 rpm.8 rpmbuild.1 rpmdb.8 rpmkeys.8 rpmsign.1 rpmspec.1 rpmdeps.1 rpmgraph.1 rpmlua.1 rpm-common.8 rpmsort.1 rpm-macrofile.5 rpm-config.5 rpm-rpmrc.5 rpm-setup-autosign.1 rpm-payloadflags.7 rpmbuild-config.5 rpm-queryformat.7 rpm-macros.7 rpm-lua.7 rpm-manifest.5 rpm-version.7 ) set(extra rpm-plugins.8 rpm-plugin-prioreset.8 rpm-plugin-syslog.8 rpm-plugin-audit.8 rpm-plugin-dbus-announce.8 rpm-plugin-systemd-inhibit.8 rpm-plugin-fapolicyd.8 rpm-plugin-ima.8 rpm-plugin-selinux.8 rpm-plugin-unshare.8 ) # Build all manuals set(manuals ${core} ${extra}) foreach(man ${manuals}) add_custom_command(OUTPUT ${man} COMMAND sed '1 s/$$/ \"RPM ${CMAKE_PROJECT_VERSION}\"/' < ${CMAKE_CURRENT_SOURCE_DIR}/${man}.scd | ${SCDOC} > ${man} DEPENDS ${man}.scd) endforeach() add_custom_target(man ALL DEPENDS ${manuals}) # Render Jekyll-ready HTML pages from the manuals if (SCD2HTML) configure_file(mkpage.in mkpage @ONLY) set(manuals_html "") foreach(man ${manuals}) set(file ${site_dir}/man/${man}.html) list(APPEND manuals_html ${file}) add_custom_command(OUTPUT ${file} COMMAND ./mkpage ${CMAKE_CURRENT_SOURCE_DIR}/${man}.scd ${file} DEPENDS ${man}.scd mkpage) endforeach() add_custom_target(clidoc DEPENDS ${manuals_html}) add_dependencies(pages clidoc) endif() # Install manuals for the enabled features set(manuals ${core}) if (ENABLE_PLUGINS) list(APPEND manuals rpm-plugins.8 rpm-plugin-prioreset.8 rpm-plugin-syslog.8) if (WITH_AUDIT) list(APPEND manuals rpm-plugin-audit.8) endif() if (WITH_DBUS) list(APPEND manuals rpm-plugin-dbus-announce.8 rpm-plugin-systemd-inhibit.8) endif() if (WITH_FAPOLICYD) list(APPEND manuals rpm-plugin-fapolicyd.8) endif() if (WITH_IMAEVM) list(APPEND manuals rpm-plugin-ima.8) endif() if (WITH_SELINUX) list(APPEND manuals rpm-plugin-selinux.8) endif() if (HAVE_UNSHARE) list(APPEND manuals rpm-plugin-unshare.8) endif() endif() foreach(man ${manuals}) set(fn ${CMAKE_CURRENT_BINARY_DIR}/${man}) get_filename_component(ext ${fn} EXT) string(REPLACE "." "man" section ${ext}) install(FILES ${fn} DESTINATION ${CMAKE_INSTALL_MANDIR}/${section}) endforeach() rpm-software-management-rpm-3c1f23f/docs/man/gendiff.1.scd000066400000000000000000000026321511627505500234300ustar00rootroot00000000000000GENDIFF(1) # NAME gendiff - Utility to aid in error-free diff file generation # SYNOPSIS *gendiff* _DIRECTORY_ _DIFF-EXTENSION_ # DESCRIPTION *gendiff* is a tool for generating a _diff_ file from a single directory. The generated _diff_ can be applied with the *patch*(1) program to recreate the changes. *gendiff* looks for any files with _DIFF-EXTENSION_ as their extension in the specified _DIRECTORY_, and runs *diff*(1) on the files with _DIFF-EXTENSION_ and the counterpart without it, in that order. This allows for generating diffs from source code without making a full copy of the directory, which you'd otherwise need when using the *diff*(1) utility directly. *gendiff* is commonly used to creating patches for the purposes of packaging software with *rpmbuild*(1). # ARGUMENTS _DIRECTORY_ The directory to generate a diff from. _DIFF-EXTENSION_ The diff extension to look for. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES Assuming you need to modify a source file called _somefile.cpp_ and have chosen the extension _fix_, copy it to _somefile.cpp.fix_ before editing it. Then edit the original file (_somefile.cpp_). After editing all the files you need to edit in this fashion, enter the directory one level above where the source code resides, and run *gendiff* on it: ``` gendiff myproject-1.0 .fix > myproject-1.0-fix.patch ``` # SEE ALSO *diff*(1), *patch*(1) rpm-software-management-rpm-3c1f23f/docs/man/index.md000066400000000000000000000021151511627505500226210ustar00rootroot00000000000000--- layout: default title: rpm.org - RPM Manual Pages --- {% assign manpages = site.pages | where: 'topic', 'manpage' | where: 'dir', page.dir %} {% assign tools = manpages | where: 'type', 'tool' %} {% assign progs = manpages | where: 'type', 'program' %} {% assign configs = manpages | where: 'type', 'config' %} {% assign misc = manpages | where: 'type', 'misc' %} {% assign plugins = manpages | where: 'type', 'plugin' %} # RPM Manual Pages ## System Tools {% for page in tools -%} - [{{ page.title }}]({{ page.slug }}) - {{ page.summary }} {% endfor %} ## User Programs {% for page in progs -%} - [{{ page.title }}]({{ page.slug }}) - {{ page.summary }} {% endfor %} ## Configuration & File Formats {% for page in configs -%} - [{{ page.title }}]({{ page.slug }}) - {{ page.summary }} {% endfor %} ## Overview & Conventions {% for page in misc -%} - [{{ page.title }}]({{ page.slug }}) - {{ page.summary }} {% endfor %} ## Plugins {% for page in plugins -%} - [{{ page.title }}]({{ page.slug }}) - {{ page.summary }} {% endfor %} rpm-software-management-rpm-3c1f23f/docs/man/mkpage.in000077500000000000000000000036411511627505500227740ustar00rootroot00000000000000#!/bin/bash # # Create a Jekyll-ready HTML page from the given scdoc(5) file. # # Uses scd2html(1) with custom post-processing that does the following: # - Adds a Jekyll front matter block # - Adds a footer with the RPM version, index link and timestamp # - Extracts the man page summary for use on the index page # - Turns canonical man page references and URLs into links # - Tweaks section anchor links SCD_FILE=$1 OUT_FILE=$2 SCD_BASE=$(basename ${SCD_FILE%.*}) OUT_BASE=$(basename ${OUT_FILE%.*}) # Conventional name(SECTION) string MAN_NAME=$(echo $SCD_BASE | sed 's/\.\(.\)$/(\1)/') # Section number MAN_SECN=$(echo $SCD_BASE | sed 's/^.\+\(.\)$/\1/') # Matches all our manual names NAME_PAT="rpm[-\.[:alnum:]]*\|gendiff" get_summary() { # Takes lines from NAME section, joins them into one and strips program # prefix and any *bold* markup sed -n '/^# NAME$/,/^#/p' $SCD_FILE | sed -e '/^$/d' -e '/^#/d' | xargs | \ sed -e 's/^[^ - ]\+ - //' -e 's/\*//g' } get_type() { case $1 in rpm-plugin*) echo plugin; return ;; esac case $2 in 1) echo program ;; 5) echo config ;; 7) echo misc ;; 8) echo tool ;; esac } HEADER="\ --- topic: manpage layout: default title: $MAN_NAME slug: $OUT_BASE type: $(get_type $MAN_NAME $MAN_SECN) summary: $(get_summary $SCD_FILE) css: manpage.css --- " FOOTER="\ " add_header() { cat <(echo "$HEADER") - } add_footer() { cat - <(echo "$FOOTER") } add_links() { sed -e 's#\('$NAME_PAT'\)(\([1-8]\))#\1(\2)#g' \ -e 's#\(https\?://[^<]*\)#\1#g' } mod_anchors() { sed 's/¶/#/' } post_process() { add_header | add_footer | add_links | mod_anchors; } @SCD2HTML@ -f < $SCD_FILE | post_process > $OUT_FILE rpm-software-management-rpm-3c1f23f/docs/man/rpm-common.8.scd000066400000000000000000000107701511627505500241230ustar00rootroot00000000000000RPM-COMMON(8) # NAME rpm-common - Operations and options common to the main *rpm*(8) executables # SYNOPSIS _RPMCMD_ [options] {*-?*|*--help*} _RPMCMD_ [options] *--showrc* _RPMCMD_ [options] *--version* # DESCRIPTION The following _RPMCMD_ executables share common configuration and the options and operations documented in this manual: - *rpm*(8) - *rpmdb*(8) - *rpmkeys*(8) - *rpmbuild*(1) - *rpmsign*(1) - *rpmspec*(1) # OPERATIONS *-?*, *--help* Print a longer usage message than normal. *--version* Print a single line containing the version number of *rpm* being used. *--showrc* Dump information about *rpm* configuration to standard output. # OPTIONS *--color* <_MODE_> Use terminal colors for highlighting error and debug message, where _MODE_ is one of: - *always* - *auto* (use color output on terminals) - *never* (default) *--dbpath* _DIRECTORY_ Use the database in _DIRECTORY_ rather than the default path _/var/lib/rpm_. *-D* '_MACRO_ _EXPR_', *--define*='_MACRO_ _EXPR_' Defines _MACRO_ with value _EXPR_. *-E* '_EXPR_', *--eval*='_EXPR_' Prints *rpm-macros*(7) expansion of _EXPR_. *--load* _FILE_ Load an individual *rpm-macrofile*(5). *--macros* _FILELIST_ Replace the list of macro files to be loaded with _FILELIST_. See *Macro Configuration* for details. *--nodigest* Don't verify package or header digests when reading. *--nohdrchk* Don't verify database header(s) when retrieved. *--nosignature* Don't verify package or header signatures when reading. *--pipe* _CMD_ Pipes the output of *rpm* to the command _CMD_. *--predefine*='_MACRO_ _EXPR_' Defines _MACRO_ with value _EXPR_ before loading macro files. This is a very rare special-purpose switch, use *--define* for the common macro definition needs. *--target* _PLATFORM_ Use _PLATFORM_ configuration instead of detecting automatically. _PLATFORM_ is formed as _arch_[-_os_]. *--quiet* Print as little as possible - normally only error messages will be displayed. *--rcfile* _FILELIST_ Replace the default list of configuration files to be read with _FILELIST_. See *rpmrc Configuration* for details. *--root* _DIRECTORY_ Use the filesystem tree rooted at _DIRECTORY_ for all operations. Note that this means the database within _DIRECTORY_ will be used for dependency checks and any scriptlet(s) (e.g. *%post* if installing, or *%prep* if building, a package) will be run after a *chroot*(2) to _DIRECTORY_. Note that rpm assumes the environment inside the root is set up by the caller, such as any mounts needed for the operation inside the root directory. *--undefine*='_MACRO_' Undefines _MACRO_. *-v*, *--verbose* Print verbose information - normally routine progress messages will be displayed. Supply more than once to increase verbosity, eg. *-vv*. # DEBUG OPTIONS *--debug* Print lots of debug information. *--fsmdebug* Print debug information about payload handling. *--rpmiodebug* Print debug information about file IO. *--stats* Print runtime statistics of often used functions. # FTP/HTTP OPTIONS These options are deprecated. Use special purpose software such as *curl*(1) or *wget*(1) for fetching data from the internet. For some operations, *rpm* can act as an FTP and/or HTTP client so that packages can be queried or installed from the internet. Package files for install, upgrade, and query operations may be specified as an *ftp* or *http* style URL: _http://HOST[:PORT]/path/to/package.rpm_ _ftp://[USER:PASSWORD]@HOST[:PORT]/path/to/package.rpm_ If both the user and password are omitted, anonymous *ftp* is used. *rpm* allows the following options to be used with *http* and *ftp* URLs: *--httpproxy* _HOST_ Use _HOST_ as the proxy server for all *http* and *ftp* transfers. This option may also be specified by configuring the macro *%\_httpproxy*. *--httpport* _PORT_ The TCP _PORT_ number to use for the *http* connection on the proxy http server instead of the default port. This option may also be specified by configuring the macro *%\_httpport*. # ENVIRONMENT *RPM_CONFIGDIR* Used to override the default *rpm* configuration home, typically _/usr/lib/rpm_. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # BUGS Not all options are meaningful or implemented in all the executables they show in. In particular, *--root* and the various verify-related options are only honored by *rpm*(8) and *rpmdb*(8). # FILES ## Database ``` /var/lib/rpm/ ``` ## Temporary ``` /var/tmp/rpm\* ``` # SEE ALSO *popt*(3), *rpm*(8) *rpm-config*(5), *rpm-rpmrc*(5), *rpm-macros*(7) rpm-software-management-rpm-3c1f23f/docs/man/rpm-config.5.scd000066400000000000000000000202611511627505500240710ustar00rootroot00000000000000RPM-CONFIG(5) # NAME *rpm-config* - rpm runtime macro configuration # SYNOPSIS _NAME_ _VALUE_ # FILES _/usr/lib/rpm/macros_++ _/usr/lib/rpm/macros.d/macros.\*_++ _/usr/lib/rpm/platform/%{\_target}/macros_++ _/usr/lib/rpm/fileattrs/\*.attr_++ _/usr/lib/rpm//macros_++ _/etc/rpm/macros.\*_++ _/etc/rpm/macros_++ _/etc/rpm/%{\_target}/macros_++ _~/.config/rpm/macros_ # DESCRIPTION The primary configuration mechanism in *rpm* is *rpm-macros*(7). On startup, *rpm* reads a set of *rpm-macrofile*(5) files determined by the _macro path_. Each file or *glob*(7) pattern in the colon-separated _macro path_ is read sequentially by *rpm* for macro definitions. *%{\_target}* is expanded to the detected - platform. Tildes are expanded to the value of the environment variable *HOME*. If a macro is defined multiple times, the last entry wins. The default _macro path_ uses this to achieve the following hierarchy of settings: . Generic *rpm* factory defaults . Platform-specific *rpm* factory defaults . Vendor (distribution) specific settings . Host specific settings . User specific settings . Command-line override settings The default _macro path_ can be inspected with *rpm --showrc|grep ^Macro*. In older versions of rpm, the path of per-user macros was _~/.rpmmacros_. This is still processed if it exists and the new configuration directory does not exist. # CONFIGURATION The following configurables are supported for the *rpm* runtime (as opposed to just package building) parts: *%\_color_output* _MODE_ Output coloring mode. Valid values are *never* and *auto*. *%\_db_backend* _BACKEND_ The database backend to use. Possible values for _BACKEND_ are: - *dummy*: Dummy backend (no actual functionality) - *bdb_ro*: Berkeley DB (read-only) - *ndb*: Native database (no external dependencies) - *sqlite*: Sqlite database *%\_dbpath* _DIRECTORY_ The location of the rpm database file(s). *%\_excludedocs* _VALUE_ Boolean (i.e. 1 == "yes", 0 == "no") that controls whether files marked as %doc should be installed. *%\_flush_io* _VALUE_ Flush file IO during transactions (at a severe cost in performance for rotational disks). Possible values are 1 to enable, 0 to disable. *%\_group_path* _PATH_ Location of group(5) files as : separated list *%\_httpport* _PORT_ The port of HTTP proxy (used for FTP/HTTP). *%\_httpproxy* _HOSTNAME_ The hostname of HTTP proxy (used for FTP/HTTP). *%\_install_langs* _LOCALES_ A colon separated list of desired locales to be installed; *all* means install all locale specific files. *%\_install_script_path* _PATH_ The PATH put into the environment before running %pre/%post et al. *%\_keyring* _BACKEND_ The keyring type to use. Possible values for _BACKEND_ are: - *fs*: Plain ASCII files in a directory - *openpgp*: Shared OpenPGP certificate directory - *rpmdb*: Pseudo-packages in the rpmdb *%\_keyringpath* _DIRECTORY_ The location of the keyring path for non-rpmdb variants. *%\_minimize_writes* _VALUE_ Minimize writes during transactions (at the cost of more reads) to conserve eg SSD disks (EXPERIMENTAL). Possible values are: - *0*: disable - *1*: enable - *-1*: (or undefined) autodetect on platforms where supported, otherwise default to disabled *%\_netsharedpath* _PATH_ A colon separated list of paths where files should *not* be installed. Usually, these are network filesystem mount points. *%\_passwd_path* _PATH_ Location of passwd(5) files as : separated list *%\_pkgverify_digests* _HASHALGOS_ A colon separated list of hash algorithms to calculate digests on the entire package files during verification. The calculated digests are stored in the *Packagedigests* tag of packages in the rpmdb, and the corresponding algorithms in the *Packagedigestalgos* tag. No package digests are calculated or stored if *--noverify* is used during package installation. *%\_pkgverify_flags* _VSFLAGS_ Transaction package verification flags, used for fine-grained control of *%\_pkgverify_level* operation. Set to 0x0 for full compatibility with v4 packages. *%\_pkgverify_level* _MODE_ Enforced package verification mode in transactions, where _MODE_ is one of: - *all*: require valid digest(s) and signature(s) - *signature*: require valid signature(s) - *digest*: require valid digest(s) - *none*: legacy rpm behavior, nothing required *%\_prefer_color* _VALUE_ Package conflict resolution in bi-arch transactions. See also *%\_transaction_color*. Possible values are: - *0*: disabled - *1*: prefer 32-bit packages - *2*: prefer 64-bit packages *%\_\_plugindir* _DIRECTORY_ Transaction plugin directory. *%\_query_all_fmt* _FORMAT_ Default output format for *rpm*(8) query operations, as described by *rpm-queryformat*(7). Percent signs need to be escaped, for example *%%{nevra}*. *%\_rpmlock_path* _FILE_ The path of the file used for transaction fcntl lock. *%\_tmppath* _PATH_ The directory where temporary files are created. *%\_\_urlhelpercmd* _EXECUTABLE_ The executable to use for retrieving remote files. *%\_\_urlhelperopts* _OPTIONS_ Generic options to pass to the *%\_\_urlhelpercmd* command. *%\_\_urlhelper_localopts* _OPTIONS_ User/host specific options to pass to the *%\_\_urlhelpercmd* command. *%\_\_urlhelper_proxyopts* _OPTIONS_ Proxy options to pass to the *%\_\_urlhelpercmd* command. *%\_urlhelper* _COMMAND_ Full command (with options) to use when retrieving remote files. Normally pieced together from the double-underscore *%\_\_urlhelper\** macros. *%\_transaction_color* _VALUE_ Package and file conflict behavior in bi-arch transactions. See also *%\_prefer_color*. Possible values are: - *0*: do not consider "colors", only use arch compatibility map - *1*: only allow 32-bit packages - *2*: only allow 64-bit packages - *3*: allow 32- and 64-bit packages to share files *%\_vsflags_erase* _VSFLAGS_ Transaction verification flags used when erasing or updating packages. *%\_vsflags_install* _VSFLAGS_ Transaction verification flags used when installing packages. *%\_vsflags_query* _VSFLAGS_ Transaction verification flags used when querying packages. *%\_vsflags_rebuilddb* _VSFLAGS_ Transaction verification flags used when rebuilding the database. *%\_vsflags_verify* _VSFLAGS_ Transaction verification flags used when verifying packages. ## Verification flags Digest/signature verification flags for various rpm operations are controlled by a bitmask known as _VSFLAGS_. These flags control various aspects of digital checksum and signature verification when reading rpm package files and their headers. _VSFLAGS_ is formed by bitwise or'ing the individual flags: - *0x00001* (RPMVSF_NOHDRCHK): don't verify headers from rpmdb - *0x00100* (RPMVSF_NOSHA1HEADER): don't verify header SHA1 digest - *0x00200* (RPMVSF_NOSHA256HEADER): don't verify header SHA256 digest - *0x00400* (RPMVSF_NODSAHEADER): don't verify header DSA signature(s) - *0x00800* (RPMVSF_NORSAHEADER): don't verify header RSA signature(s) - *0x01000* (RPMVSF_NOOPENPGP): don't verify header OpenPGP signature(s) - *0x02000* (RPMVSF_NOSHA3_256HEADER): don't verify header SHA3_256 digest - *0x04000* (RPMVSF_NOSHA512PAYLOAD): don't verify package payload SHA512 digest - *0x08000* (RPMVSF_NOSHA3_256PAYLOAD): don't verify package payload SHA3_256 digest - *0x10000* (RPMVSF_NOSHA256PAYLOAD): don't verify package payload SHA256 digest - *0x20000* (RPMVSF_NOMD5): don't verify legacy header+payload MD5 digest - *0x40000* (RPMVSF_NODSA): don't verify legacy header+payload DSA signature - *0x80000* (RPMVSF_NORSA): don't verify legacy header+payload RSA signature RPM's Python bindings can be helpful for working with these values, for example: ``` >>> import rpm >>> hex(rpm.RPMVSF_NOSHA1HEADER) '0x100' >>> hex(rpm.RPMVSF_NOSHA1HEADER|rpm.RPMVSF_NOMD5) '0x20100' >>> ``` ## Hash algorithms The following hash algorithms (denoted by _HASHALGO_) are known to rpm, but availability can vary depending how rpm and it's underlying libraries have been built: - *1*: MD5 (obsolete) - *2*: SHA1 (obsolete) - *8*: SHA256 - *9*: SHA384 - *10*: SHA512 - *12*: SHA3-256 - *14*: SHA3-512 # ENVIRONMENT If *XDG_CONFIG_HOME* environment variable is set, it replaces _~/.config_ in the _macro path_. # SEE ALSO *rpm*(8), *rpm-common*(8), *rpm-macrofile*(5), *rpm-rpmrc*(5), *rpm-macros*(7) rpm-software-management-rpm-3c1f23f/docs/man/rpm-lua.7.scd000066400000000000000000000524571511627505500234230ustar00rootroot00000000000000RPM-LUA(7) # NAME rpm-lua - RPM embedded Lua interpreter # SYNOPSIS %scriptlet -p %{lua:...} # DESCRIPTION Lua is a general purpose programming language specifically designed for embedding in other programs, and RPM includes an embedded Lua interpreter for use in advanced *rpm-macros*(7) and transaction scriptlets. The embedded Lua interpreter makes possible various things that are hard or impossible with plain macros or external shell scripts, such as help eliminate dependency loops from package scriptlets. # MACROS ## Accessing macros The *rpm* extension has various functions for dealing with macros, but the most convenient way to access *rpm-macroproc*(7) from the RPM Lua environment is via the global *macros* table. Lua makes no difference between table index and field name syntaxes so *macros.foo* and *macros['foo']* are equivalent, use what better suits the purpose. Like any real Lua table, non-existent items are returned as *nil*, and assignment can be used to define or undefine macros. Example: ``` if not macros.yours then macros.my = 'my macro' end local v = { '_libdir', '_bindir', '_xbindir' } for _, v in ipairs(v) do if not macros[v] then macros[v] = 'default' end end ``` All macros share the same global Lua execution environment. ## Calling parametric macros Parametric macros (including all built-in macros) can be called in a Lua native manner via the *macros* table, with either *macros.*_name_*()* or *macros[*_name_*]()* syntax. Arguments are passed through a single argument, which is either - a single string, in which case it's expanded and split with the macro-native rules - a table, in which case the table contents are used as literal arguments that are not expanded in any way Example 1: ``` macros.with('foo') ``` Example 2: ``` macros.dostuff({'one', 'two', 'three'}) ``` ## Returning data By definition, anything *print()*'ed in Lua will end up in the macro expansion. Lua macros can also *return* their output, which makes programming helper macros look more natural. Example: ``` %sum() %{lua: local v = 0 for _, a in ipairs(arg) do v = v + tonumber(a) end return v } ``` ## Options and arguments Parametric Lua macros receive their options and arguments as two local tables *opt* and *arg*, where *opt* holds processed option values keyed by the option character, and *arg* contains arguments numerically indexed. These tables are always present regardless of whether options or arguments were actually passed to simplify use. Example: ``` %foo(a:b) %{lua: if opt.b then print('do b') else print('or not') end if opt.a == 's' then print('do s') end if #arg == 0 then print('no arguments :(') else for i = 1, #arg do print(arg[i]) end end } ``` # SCRIPTLETS The internal Lua can be used as the interpreter of RPM any transaction scriptlets, including triggers and file triggers: Example: ``` %pre -p print('Hello from Lua') ``` While the venerable _/bin/sh_ is usually more convenient for packaging related scripting activities, the embedded Lua interpreter has some unique advantages for transaction scriptlets: they add no extra dependencies to the packages and so can help eliminate dependency loops. This can be a crucial difference in the early "bootstrap" package set in an initial install. In particular, an embedded Lua script is the only generally usable option in *%pretrans* scriplets during the initial installation of a system. Embedded Lua is also much faster than executing a potentially heavyweight interpreter just to run a couple of lines of shell script. Note: scriptlets using the internal Lua should not make assumptions about sharing the execution environment with other scriptlets. ## Arguments Scriptlet arguments are accessible from a global *arg* table. Note: in Lua, indexes customarily start at 1 (one) instead of 0 (zero), and for the better or worse, the RPM implementation follows this practise. Thus the scriptlet arg indexes are off by one from general expectation based on traditional scriptlet arguments. The argument containing number of installed package instances is *arg[2]* and the similar argument for trigger targets is *arg[3]*, vs the traditional *$1* and *$2* in shell scripts. Example: ``` %postun -p if arg[2] == 0 then print("erasing") end ``` ## Relocatable packages Scriptlets of relocatable packages additionally carry a global *RPM_INSTALL_PREFIX* table containing all the possible prefixes of the package. Added: 4.18.0 ## Exit status While scriptlets shouldn't be allowed to fail normally, you can signal scriptlet failure status by using Lua's *error(*_msg_, [_level_]*)* function if you need to. # SPEC FILES In the context of a *rpm-spec*(5) file parse with *rpmbuild*(1) or *rpmspec*(1), the RPM Lua environment contains the following spec specific global tables: *patches* The filenames of the patches in the spec, in the order they appeared in the spec. *patch_nums* The numbers of the patches in the spec, in the order they appeared in the spec. *sources* The filenames of the sources in the spec, in the order they appeared in the spec. *source_nums* The numbers of the sources in the spec, in the order they appeared in the spec. Example: ``` for i, p in ipairs(patches) do print(string.format("echo %d: %s\n", patch_nums[i], patches[i])) end ``` # EXTENSIONS In addition to Lua standard libraries (subject to the Lua version RPM is linked to), the following extensions are available in the RPM internal Lua interpreter. These can be used in all contexts where the internal Lua can be used. ## rpm extension The following RPM specific functions are available: *b64decode(*_arg_*)* Perform base64 decoding on argument. See also b64encode(). Example: ``` blob = 'binary data' print(blob) e = rpm.b64encode(blob) print(e) d = rpm.b64decode(e) print(d) ``` *b64encode(*_arg_ [, _linelen_]*)* Perform base64 encoding on argument. Line length may be optionally specified via second argument. See also b64decode(). *define("*_name_ _body_*")* Define a global macro _name_ with _body_. See also *MACROS*. Example: ``` rpm.define('foo 1') ``` *execute(*_path_ [, _arg1_ [,...]*)* Execute an external command. This is handy for executing external helper commands without depending on the shell. _path_ is the command to execute, followed by optional number of arguments to pass to the command. For a better control over the process execution and output, see rpm.spawn(). Added: 4.15.0 Example: ``` rpm.execute('ls', '-l', '/') ``` *expand(*_arg_*)* Perform RPM macro expansion on _arg_ string. See also *MACROS*. Example: ``` rpm.expand('%{_libdir}/mydir') ``` *glob(*_pattern_, [_flags_]*)* Return a table of pathnames matching _pattern_. If _flags_ contains *c*, return _pattern_ in case of no matches. Example: ``` for i, p in ipairs(rpm.glob('*')) do print(p) end ``` *interactive()* Launch interactive session for testing and debugging. Use *rpmlua*(1) instead. Example: ``` rpm --eval "%{lua: rpm.interactive()}" ``` *isdefined(*_name_*)* Test whether a macro _name_ is defined and whether it's parametric, returned in two booleans. See also *MACROS*. (Added: 4.17.0) Example: ``` if rpm.isdefined('_libdir') then ... end ``` *load(*_path_*)* Load a macro file from given path. Same as the built-in *%{load:...}* macro. Example: ``` rpm.load('my.macros') ``` *open(*_path_, [_mode_[._flags_]]*)* Open a file stream using RPM IO facilities, with support for transparent compression and decompression. _path_ is filename string, optionally followed with _mode_ string to specify open behavior: - *a*: open for append - *w*: open for writing, truncate - *r*: open for reading (default) - *+*: open for reading and writing - *x*: fail if file exists and optionally followed by *rpm-payloadflags*(7) for compression and decompression. Added: 4.17.0 Example: ``` f = rpm.open('some.txt.gz', 'r.gzdio') print(f:read()) ``` The returned rpm.fd object has the following methods: *fd:close()* Close the file stream. Example: ``` f = rpm.open('file') f:close() ``` *fd:flush()* Flush the file stream. Example: ``` f = rpm.open('file', 'w') f:write('foo') f:flush() f:close() ``` *fd:read(*[_len_]*)* Read data from the file stream up to _len_ bytes or if not specified, the entire file. Example: ``` f = rpm.open('/some/file') print(f:read()) ``` *fd:seek(*_mode_, _offset_*)* Reposition the file offset of the stream. _mode_ is one of *set*, *cur* and *end*, and offset is relative to the mode: absolute, relative to current or relative to end. Not all streams support seeking. Returns file offset after the operation. See also *lseek*(3). Example: ``` f = rpm.open('newfile', 'w') f:seek('set', 555) f:close() ``` *fd:write(*_buf_ [, _len_]*)* Write data in _buf_ to the file stream, either in its entirety or up to _len_ bytes if specified. Example: ``` f = rpm.open('newfile', 'w') f:write('data data') f:close() ``` *fd:reopen(*_mode_*)* Reopen a stream with a new mode (see *rpm.open()*). Example: ``` rpm.open('some.txt.gz') f = f:reopen('r.gzdio') print(f:read())} ``` *redirect2null(*_fdno_*)* (OBSOLETE) Redirect file descriptor fdno to /dev/null (prior to 4.16 this was known as posix.redirect2null()) This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use `rpm.spawn()` or `rpm.execute()` instead. ``` pid = posix.fork() if pid == 0 then posix.redirect2null(2) assert(posix.exec('/bin/awk')) elseif pid > 0 then posix.wait(pid) end ``` *spawn(*{_command_} [, {_actions_}]*)* Spawn, aka execute, an external program. {_command_} is a table consisting of the command and its arguments. An optional second table can be used to pass various actions related to the command execution, currently supported are: ``` | Action | Argument(s) | Description |---------|---------------------- | *stdin* | path | Redirect standard input to path | *stdout*| path | Redirect standard output to path | *stderr*| path | Redirect standard error to path ``` Returns the command exit status: zero on success, or a tuplet of (*nil*, message, code) on failure. Added: 4.20 Example: ``` rpm.spawn({'systemctl', 'restart', 'httpd'}, {stderr='/dev/null'}) ``` *undefine(*_name_*)* Undefine a macro. See also *MACROS*. Note that this is only pops the most recent macro definition by the given name from stack, ie there may be still macro definitions by the same name after an undefine operation. Example: ``` rpm.undefine('zzz') ``` *vercmp(*_v1_, _v2_*)* Perform RPM version comparison on argument strings. Returns -1, 0 or 1 if _v1_ is smaller, equal or larger than _v2_. See *rpm-version*(7). Note: in RPM < 4.16 this operated on version segments only, which does not produce correct results on full _EVR_ strings. Example: ``` rpm.vercmp('1.2-1', '2.0-1') ``` *ver(*_evr_*)*, *ver(*_e_, _v_, _r_*)* Create RPM version object. This takes either an _evr_ string which is parsed to it's components, or epoch, version and release in separate arguments (which can be either strings or numbers). The object has three attributes: *e* for epoch, *v* for version and *r* for release, can be printed in it's EVR form and supports native comparison in Lua. Added: 4.17.0 Example: ``` v1 = rpm.ver('5:1.0-2) v2 = rpm.ver(3, '5a', 1) if v1 < v2 then ... end if v1.e then ... end ``` ## posix extension Lua standard library offers fairly limited set of io operations. The *posix* extension greatly enhances what can be done from Lua. The following functions are available in the *posix* namespace, ie to call them use *posix.function()*. This documentation concentrates on the Lua API conventions, for further information on the corresponding system calls refer to the system manual, eg *access*(3) for *posix.access()*. *access(*_path_ [, _mode_]*)* Test accessibility of file/directory _path_. See *access*(3). If _mode_ is omitted then existence is tested, otherwise it is a combination of the following tests: - *r*: readable - *w*: writable - *x*: executable - *f*: exists Example: ``` if posix.access('/bin/rpm', 'x') then ... end ``` *chdir(*_path_*)* Change current working directory to _path_. See *chdir*(1). Example: ``` posix.chdir('/tmp') ``` *chmod(*_path_, _mode_*)* Change file/directory mode. Mode can be either an octal number as for *chmod*(2) system call, or a string presentation similar to *chmod*(1). Example: ``` posix.chmod('aa', 600) posix.chmod('bb', 'rw-') posix.chmod('cc', 'u+x') ``` *chown(*_path_, _user_, _group_*)* Change file/directory owner/group of _path_. The _user_ and _group_ arguments may be either numeric id values or user/groupnames. See *chown*(2) and *chown*(1). Note: This is a privileged operation. Example: ``` posix.chown('aa', 0, 0) posix.chown('bb', 'nobody', 'nobody') ``` *ctermid()* Get controlling terminal name. See *ctermid*(3). Example: ``` print(posix.ctermid()) ``` *dir(*[_path_]*)* Get directory contents - like *readdir*(3). If _path_ is omitted, current directory is used. Example: ``` for i,p in pairs(posix.dir('/')) do print(p..'\n') end ``` *errno()* Get *strerror*(3) message and the corresponding number for current *errno*(3). Example: ``` f = '/zzz' if not posix.chmod(f, 100) then s, n = posix.errno() print(f, s) end ``` *exec(*_path_ [, _args_...]*)* (OBSOLETE) Execute a program. This may only be performed after posix.fork(). This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use *rpm.spawn()* or *rpm.execute()* instead. *files(*[_path_]*)* Iterate over directory contents. If path is omitted, current directory is used. Example: ``` for f in posix.files('/') do print(f..'\n') end ``` *fork()* (OBSOLETE) Fork a new process. See *fork*(2). This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use *rpm.spawn()* or *rpm.execute()* instead. Example: ``` pid = posix.fork() if pid == 0 then posix.exec('/foo/bar') elseif pid > 0 then posix.wait(pid) end ``` *getcwd()* Get current directory. See *getcwd*(3). Example: ``` if posix.getcwd() ~= '/' then ... endif ``` *getenv(*_name_*)* Get an environment variable. See *getenv*(3). Example: ``` if posix.getenv('HOME') ~= posix.getcwd() then print('not at home') end ``` *getgroup(*_group_*)* Get *group*(5) information for a group. _group_ may be either a numeric id or group name. If omitted, current group is used. Returns a table with fields *name* and *gid* set to group name and id respectively, and indexes from 1 onwards specifying group members. Example: ``` print(posix.getgroup('wheel').gid) ``` *getlogin()* Get login name. See *getlogin*(3). Example: ``` n = posix.getlogin() ``` *getpasswd*([_user_ [, _selector_]]*)* Get *passwd*(5) information for a user account. _user_ may be either a numeric id or username. If omitted, current user is used. The optional _selector_ argument may be one of: - *name* - *uid* - *gid* - *dir* - *shell* - *gecos* - *passwd* If omitted, a table with all these fields is returned. Example: ``` pw = posix.getpasswd(posix.getlogin(), 'shell')| ``` *getprocessid(*[_selector_]*)* Get information about current process. The optional _selector_ argument may be one of - *egid*: effective group id - *euid*: effective user id - *gid*: group id - *uid*: user id - *pgrp*: parent group id - *pid*: process id - *ppid*: parent pid If omitted, a table with all these fields is returned. Example: ``` if posix.getprocessid('pid') == 1 then ... end ``` *kill(*_pid_ [, _signal_]*)* Send a *signal*(7) to a process. _signal_ must be a numeric value, eg. *9* for *SIGKILL*. If omitted, *SIGTERM* is used. See also *kill*(2). Example: ``` posix.kill(posix.getprocessid('pid')) ``` *link(*_oldpath_, _newpath_*)* Create a new name at _newpath_ for a file at _oldpath_, aka hard link. See also *link*(2). Example: ``` f = rpm.open('aaa', 'w') posix.link('aaa', 'bbb') ``` *mkdir(*_path_*)* Create a new directory at _path_. See also *mkdir*(2). Example: ``` posix.mkdir('/tmp') ``` *mkfifo(*_path_*)* Create a FIFO aka named pipe at _path_. See also *mkfifo*(2). Example: ``` posix.mkfifo('/tmp/badplace') ``` *pathconf(*_path_ [, _selector_]*)* Get *pathconf*(3) information for _path_. The optional _selector_ may be one of - *link_max* - *max_canon* - *max_input* - *name_max* - *path_max* - *pipe_buf* - *chown_restricted* - *no_trunc* - *vdisable*. If omitted, a table with all these fields is returned. Example: ``` posix.pathconf('/', 'path_max') ``` *putenv(*_string_*)* Change or add an environment variable. See also *putenv*(3). Example: ``` posix.putenv('HOME=/me') ``` *readlink(*_path_*)* Read value of the symbolic link at _path_. See also *readlink*(2). Example: ``` posix.mkdir('aaa') posix.symlink('aaa', 'bbb') print(posix.readlink('bbb')) ``` *rmdir(*_path_*)* Remove a directory _path_. See also *rmdir*(2). Example: ``` posix.rmdir('/tmp') ``` *setgid(*_group_*)* Set group identity. _group_ may be specified either as a numeric id or group name. See also *setgid*(2). Note: This is a privileged operation. *setuid(*_user_*)* Set user identity. _user_ may be specified either as a numeric id or username. See also *setuid*(2). Note: This is a privileged operation. Example: ``` posix.setuid('nobody') ``` *sleep(*_seconds_*)* Sleep for the duration of _seconds_. See also *sleep*(3). Example: ``` posix.sleep(5) ``` *stat(*_path_ [, _selector_]*)* Get file *stat*(3) information about a file at _path_. The optional _selector_ may be one of - *mode* - *ino* - *dev* - *nlink* - *uid* - *gid* - *size* - *atime* - *mtime* - *ctime* - *type*. If omitted, a table with all these fields is returned. Example: ``` print(posix.stat('/tmp', 'mode'))| s1 = posix.stat('f1') s2 = posix.stat('f2') if s1.ino == s2.ino and s1.dev == s2.dev then ... end ``` *symlink(*_oldpath_, _newpath_*)* Create a symbolic link at _newpath_ to _oldpath_. See also *symlink*(2). Example: ``` posix.mkdir('aaa') posix.symlink('aaa', 'bbb') ``` *sysconf(*[_selector_]*)* Get *sysconf*(3) information. The optional _selector_ argument may be one of: - *arg_max* - *child_max* - *clk_tck* - *ngroups_max* - *stream_max* - *tzname_max* - *open_max* - *job_control* - *saved_ids* - *version*. If omitted, a table with all these fields is returned. Example: ``` posix.sysconf('open_max')| ``` *times(*[_selector_]*)* Get process and waited-for child process *times*(2). The optional _selector_ argument may be one of - *utime* - *stime* - *cutime* - *cstime* - *elapsed* If omitted, a table with all these fields is returned. Example: ``` t = posix.times() print(t.utime, t.stime) ``` *ttyname(*[_fd_]*)* Get name of a terminal associated with file descriptor _fd_. If _fd_ is omitted, *0* (aka standard input) is used. See *ttyname*(3). Example: ``` if not posix.ttyname() then ... endif ``` *umask(*[_mode_]*)* Get or set process *umask*(2). _mode_ may be specified as an octal number or mode string similarly to *posix.chmod()*. Example: ``` print(posix.umask()) posix.umask(222) posix.umask('ug-w') posix.umask('rw-rw-r--') ``` *uname(*_format_*)* Get *uname*(2) information about the current system. The following format directives are supported: - *%m*: Name of the hardware type - *%n*: Name of this node - *%r*: Current release level of this implementation - *%s*: Name of this operation system - *%v*: Current version level of this implementation Example: ``` print(posix.uname('%s %r')) ``` *utime(*_path_ [, _mtime_ [, _ctime_]]*)* Change last access and modification times. mtime and ctime are expressed seconds since epoch. See *utime*(2). If _mtime_ or _ctime_ are omitted, current time is used, similar to *touch*(1). Example: ``` posix.mkdir('aaa') posix.utime('aaa', 0, 0) ``` *wait(*[_pid_]*)* (DEPRECATED) Wait for a child process. If _pid_ is specified wait for that particular child. See also *wait*(2). This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use *rpm.spawn()* or *rpm.execute()* instead. Example: ``` pid = posix.fork() if pid == 0 then posix.exec('/bin/ls')) elseif pid > 0 then posix.wait(pid) end ``` *setenv(*_name_, _value_ [, _overwrite_]*)* Change or add environment variable _name_. The optional _overwrite_ is a boolean which defines behavior when a variable by the same name already exists. See also *setenv*(3). Example: ``` posix.setenv('HOME', '/me', true) ``` *unsetenv(*_name_*)* Remove a variable _name_ from environment. See also *unsetenv*(3). Example: ``` posix.unsetenv('HOME') ``` # EXTENDING AND CUSTOMIZING On initialization, RPM executes a global _init.lua_ Lua initialization script from the directory *%getconfdir* expands to, typically _/usr/lib/rpm/init.lua_. This can be used to customize the rpm Lua environment without recompiling RPM. For the embedded Lua interpreter, module's loaded with *require* are primarily searched from *%{getconfdir}/lua/*. *%\_rpmluadir* is a shorthand for this path. # SEE ALSO *rpm-macros*(7) *rpm-payloadflags*(7) *rpmlua*(1) *rpm-version*(7) *https://www.lua.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpm-macrofile.5.scd000066400000000000000000000030471511627505500245700ustar00rootroot00000000000000RPM-MACROFILE(5) # NAME *rpm-macrofile* - rpm macro file format # SYNOPSIS %_NAME_[([_OPTS_]) _BODY_ # DESCRIPTION Rpm macro files are used to define *rpm-macros*(7) in the global macro context. The two primary uses of macros are assisting packaging work, and configuring rpm behavior. A pre-determined set of macro files is read upon *rpm* library initialization as described in *rpm-config*(5) but they can also be loaded via the *%load* macro separately. The format of a macro file is: the macro _NAME_ prefixed with *%*, followed by whitespace and then the macro body, each new definition separated with a newline. The syntax is exactly the same as when using *%define*. See *rpm-macros*(7) for details on the macro syntax. A trailing *\\* indicates line continuation, but can be omitted inside macro bodies wrapped in a *%{macro:...}* block. Lines starting with *\#* or consisting solely of whitespace are ignored. Reading a macro file is always fully declarative: no macros are ever expanded when reading a macro file. # EXAMPLES ## Example 1. Simple macro ``` %mytool /usr/bin/mytool-with-annoying-name ``` ## Example 2. Multiline macro with a preceding comment ``` # Trailing %{nil} is handy for ensuring a newline at the end %mycmd %{mytool} \\ --one \\ --with \\ --too \\ --many arguments \\ %{nil} ``` ## Example 3. Multiline parametric Lua macro: ``` %myhelper() %{lua: function dostuff(arg) return 'do stuff to '..arg end print(dostuff(arg[1])) } ``` # SEE ALSO *rpm-config*(5) *rpmbuild-config*(5) *rpm-macros*(7) rpm-software-management-rpm-3c1f23f/docs/man/rpm-macros.7.scd000066400000000000000000000504261511627505500241200ustar00rootroot00000000000000RPM-MACROS(7) # NAME rpm-macros - RPM macro processor # SYNOPSIS ## Defining *%*_NAME_ _BODY_ *%*_NAME_*(*[_OPTIONS_]*)* _BODY_ ## Expanding *%*_NAME_ *%*_NAME_ [_OPTIONS_] [_ARGUMENTS_] *%{*_NAME_*}* *%{*_NAME_ [_OPTIONS_] _ARGUMENTS_*}* *%{*_NAME_*:*_ARGUMENT_*}* *%{?*_NAME_*}* *%{?*_NAME_*:*_VALUE-IF-DEFINED_*}* *%{!?*_NAME_*:*_VALUE-IF-NOT-DEFINED_*}* *%(*_SHELL-COMMAND_*)* *%[*_EXPRESSION_*]* *%[*_EXPRESSION_ *?* _VALUE-IF-TRUE_ *:* _VALUE-IF-FALSE_*]* *%{lua:*_LUA-CODE_*}* # DESCRIPTION RPM has a powerful built-in macro processor. The primary uses of macros are configuration and other utility functions for RPM itself, and as a packaging aid in spec files. In addition to simple text substitution, the macro processor supports the following facilities: - function-like *PARAMETRIC MACROS* with options and arguments processing and locally scoped automatic and user-defined macros - *Shell expansion* - *Expression expansion* - *Lua expansion* for embedded Lua processing - various *BUILT-IN MACROS* for string processing and OS interaction The syntax for defining simple macros is: _NAME_ _BODY_ All whitespace surrounding _BODY_ is removed. _NAME_ may be composed of alphanumeric characters and the underscore (*\_*), and must be at least two characters in length. The body is (re-)expanded on each macro invocation. Macro names and options are case-sensitive. See *PARAMETRIC MACROS* for the more advanced macro variant with options and arguments processing. Macros can be defined via *rpm-macrofile*(5) files, and fully managed with macro primitives *%define*, *%global* and *%undefine*, RPM command-line described in *rpm-common*(8) and the API (C, Python, Lua). Except for those defined inside parametric macros, macros are always global in scope. RPM macros are stacked, ie. when redefining an already existing macro, it shadows the previous definition instead of replacing it, and undefining a macro only pops the topmost definition, thus activating the previous macro definition. Note that this manual only describes the macro processor engine itself. On a normal RPM based system, there are a vast number of other macros defined through *rpm-macrofile*(5) files that will not be covered here. # EXPANSION To expand a macro, place *%* in front of it. Several forms are supported: *%*_NAME_ Expand the macro _NAME_. *%{*_NAME_*}* Expand the macro _NAME_, allowing placement adjacent to other text (similar to *${ENV}* in shell). *%*_NAME_ [_OPTIONS_] [_ARGUMENTS_] Expand the parametric/built-in macro _NAME_, using options and arguments parsed up to the string end or next newline. *--* can be used to separate options from arguments. *%{*_NAME_ [_OPTIONS_] [_ARGUMENTS_]*}* Expand the parametric/built-in macro _NAME_, using options and arguments parsed up to the closing *}*. Allows usage adjacent to other text. *%{*_NAME_*:*_ARGUMENT_*}* Expand the parametric/built-in macro _NAME_, using the string after *:* as the sole argument. Allows usage adjacent to other text. Note: The syntaxes for calling parametric and built-in macros are generally interchangeable now, but prior to 4.18, the *%{*_NAME_*:*_ARGUMENT_*}* syntax was exclusive to built-in macros. Macro expansion can be escaped by placing a second *%* in front of the macro, for example *%%{name}* would be expanded to *%{name}*. Attempting to expand an undefined macro expands to the literal invocation, e.g. *%\_undefined* expands to *%\_undefined*. If this is not desired, use conditionals. Macro expansions can recurse up to 64 levels. ## Shell expansion Shell expansion can be performed using *%(*_shell command_*)*. _shell_command_ is expanded before executing it with _/bin/sh_, whose output becomes the expansion of the macro. The trailing newline is deleted. Example: ``` %(echo aa-bb-cc | tr '-' '.') ``` ## Conditional expansion The macro processor supports testing whether a macro is defined or not. *%{?*_NAME_*:*_VALUE_*}* Expands to the expansion of _VALUE_ if _NAME_ is defined, otherwise to an empty string. *%{!?*_NAME_*:*_VALUE_*}* Expands to the expansion of _VALUE_ if _NAME_ is _not_ defined, otherwise it expands to an empty string. *%{?*_NAME_*}* Shorthand for *%{?*_NAME_*:%{*_NAME_*}}*. For more complex tests, use *Expression expansion* or *Lua expansion*. Note that *%if*, *%ifarch* and the like are not macros, they are spec directives and only usable in that context. Note that in RPM >= 4.17, conditionals on built-in macros simply test for existence of that built-in, just like with any other macros. In older versions, the behavior of conditionals on built-ins is undefined. ## Expression expansion Expression expansion can be performed using *%[*_EXPRESSION_*]*. An expression consists of terms that can be combined using operators. RPM supports three kinds of terms: - numbers made up from digits - strings enclosed in double quotes (e.g *"somestring"*) - versions enclosed in double quotes preceded by *v* (e.g *v"3:1.2-1"*) RPM will expand macros when evaluating terms. You can use the standard operators to combine terms: - logical operators *&&*, *||*, *!* - relational operators *!=*, *==*, *<*, *>*, *<=*, *>=* - arithmetic operators *+*, *-*, */*, *\**, - the ternary operator *? :* - parentheses For example, *%[ 3 + 4 \* (1 + %two) ]* will expand to *15* if *%two* expands to *2*. Version terms are compared using RPM version ([_epoch_:]_version_[-_release_]) comparison algorithm, rather than regular string comparison. Note that the *%[*_EXPRESSION_*]* expansion is different to the *%{expr:*_EXPRESSION_*}* macro. With the latter, the macros in the expression are expanded first and then the expression is evaluated (without re-expanding the terms). Thus ``` rpm --define 'foo 1 + 2' --eval '%{expr:%foo}' ``` will print *3*. Using *%[%foo]* instead will result in the error that "1 + 2" is not a number. Doing the macro expansion when evaluating the terms has two advantages. First, it allows RPM to do correct short-circuit processing when evaluating logical operators. Second, the expansion result does not influence the expression parsing, e.g. *%["%file"]* will even work if the *%file* macro expands to a string that contains a double quote. Added: 4.16.0 ## Lua expansion The most powerful of the macro expansion methods is using RPM's embedded Lua interpreter: *%{lua:*_LUA-CODE_*}* Execute _LUA-CODE_ using RPM's embedded Lua interpreter, expanding to the code's *print()*'ed output. See *rpm-lua*(7) for the details. # PARAMETRIC MACROS Parametric macros are a powerful mechanism that allows building function-like utility macros with option processing and accepting a variable number of arguments, much like common shell tools. The syntax for defining parametric macros is: _NAME_([_OPTIONS_]) _BODY_ If present, the _OPTIONS_ (i.e. the string between parentheses) are passed exactly as is to *getopt*(3) for argc/argv processing at the beginning of a macro invocation. Only short options are supported. *-* as the sole _OPTIONS_ field disables RPM's option processing. This allows macros to fully decide how to handle their input, e.g. if the arguments of the macro only/mostly consist of items starting with *-*, the default processing only gets in the way. ## Automatic macros While a parameterized macro is being expanded, the following shell-like automatic macros are available: [[ *Macro* :< *Description* | *%0* : the name of the macro being invoked | *%\** : all arguments (unlike shell, not including any processed flags) | *%\*\** : all arguments (including any processed flags) | *%#* : the number of arguments | *%{-f}* : if present at invocation, the last occurrence of flag *f* (flag and argument) | *%{-f\*}* : if present at invocation, the argument to the last occurrence of flag *f* | *%1*, *%2*, ... : the arguments themselves (after *getopt*(3) processing) If the built-in option processing was disabled with *-* as the _OPTIONS_ field, only the following automatic macros are available: [[ *Macro* :< *Description* | *%0* : the name of the macro being invoked | *%\**, *%\*\** : all arguments | *%#* : the number of arguments | *%1*, *%2*, ... : the arguments themselves Automatic macros are automatically defined and undefined on parametric macro entry and exit. ## Accessing options Within the body of a parametric macro, there are several constructs that permit testing for the presence of optional parameters. The simplest construct is *%{-f}* which expands (literally) to *-f* if *-f* was mentioned when the macro was invoked. There are also provisions for including text if a flag was present using *%{-f:X}*. This macro expands to (the expansion of) *X* if the flag was present. The negative form, *%{!-f:Y}*, expanding to (the expansion of)* Y* if *-f* was *not* present, is also supported. ## Scope and visibility In general, macros have a global scope, regardless of where and how they were defined. However, macros defined inside parametric macros have non-global scope as follows: - automatic macros have local scope, ie. are only visible on the call-level of the macro itself - user-defined local macros have nested scope, ie. are visible on the call-level of the macro itself and deeper That is, a parametric macro cannot see the options or arguments of another one, but a user-defined local macro in a calling macro can be accessed in the callee(s). To define a global macro inside a parametric macro, you _must_ use *%global* instead of *%define*. Also note that because such a macro may be referring to other macros only visible in the current scope, *%global* _expands the macro body once at the time of definition_. ## Calling convention When a parametric macro is expanded, the following calling convention is used: . any arguments to the macro are expanded on the call-level of the callee . any options to the macro are processed . automatic macros are set up for the options and the arguments . the macro body is recursively expanded . all macros defined on this call-level are discarded # BUILT-IN MACROS RPM supports the following built-in macros for various operations. Built-in macros cannot be undefined or overridden. Note: The *%{name:arg}* style is used here as it's the most backwards compatible and does not require quoting for whitespace, but it can generally be replaced with the other expansion forms too. Built-ins taking multiple arguments must use other styles, as indicated below. ## Macro manipulation The macro primitives are used for macro manipulation in spec files and other macros. Note that all these operate on the macro name _without_ the preceding *%*-character. *%define* _NAME_[([_OPTIONS_])] _BODY_ This is the primary way to define macros. A *%define* is always fully declarative: no macro expansion takes place, and it has no side-effects. Macros defined with it are in global scope, unless the definition occurs inside a parametric macro. Example: ``` %define mypath /usr/bin/mine ``` *%global* _NAME_[([_OPTIONS_])] _BODY_ The *%global* primitive is identical in syntax to *%define*, but has two critically important behavioral differences: as it's name suggests, a macro defined with *%global* always has a global scope regardless of where it's used. The second difference is that the _BODY_ is expanded once at the time of definition and the expansion becomes the actual macro body. Thus, arbitrary code execution and side-effects may occur when *%global* is used, depending on the contents and the other macros used in _BODY_. The latter can be handy for avoiding redundant, possibly expensive macro expansions if the value does not change, but be aware of the side-effects. Note that while *%global* technically accepts an _OPTIONS_ field, it is ill-suited for defining parametric macros because of the _BODY_ expansion behavior. Example: ``` %global snapver 0-0.48.20240616git ``` *%undefine* _NAME_ Note that *%undefine* only pops a macro definition from the stack, so using it does _not_ guarantee that _NAME_ is undefined after calling *%undefine* on it. Automatic macros and built-in macros cannot be undefined. Example: ``` %undefine mypath ``` *%{load:*_FILE_*}* Load an *rpm-macrofile*(5) file. (Added: 4.12.0) Example: ``` %{load:/some/dir/macros.foo} ``` ## Macro expansion *%{expand:*_BODY_*}* Expand _BODY_ as if it were a macro body. Useful for increased indirection, such as to expand a macro name constructed from two or more macros. Example: ``` %{expand:%{foo_prefix}%{foo_suffix}} ``` *%{expr:*_EXPRESSION_} Expand _EXPRESSION_. See *Expression expansion*. (Added: 4.15.0) Example: ``` %{expr:5\*1024} ``` *%{lua:*_LUA-CODE_*}* Expand to output of _LUA-CODE_ using the embedded Lua interpreter. See *Lua expansion*. Example: ``` %{lua:for i=65,90 do print(string.char(i)) end} ``` *%{macrobody:*_NAME_*}* Expand to the literal body of the macro _NAME_. (Added: 4.16.0) Example: ``` %{macrobody:\_libdir} ``` ## String operations *%dnl* Discard to next line (without expanding anything). *%dnl* is the recommended way to comment out things in spec files because it works everywhere and disables macro processing for that line. (Added: 4.15.0) Example: ``` %dnl This is a comment on %{mymacro} behavior ``` *%{gsub *_STRING_, _PATTERN_, _REPL_ [,_N_]*}* Replace all (or _N_ first if given) occurrences of _PATTERN_ in _STRING_ by _REPL_. Added: 4.19.0 Example: ``` %{gsub aabbaacc aa dd 1} ``` *%{len:*_STRING_*}* Expand to length of _STRING_. (Added: 4.19.0) Example: ``` %{len:9bf7da058a7c582878310e75be3d56a5a8b67f95} ``` *%{lower:*_STRING_*}* Expand to lowercase _STRING_. (Added: 4.19.0) Example: ``` %{lower:CamelCase}' ``` *%{quote:*_STRING_*}* Quote arguments for passing empty strings and strings with embedded whitespace as parametric macro arguments. (Added: 4.14.0) Example: ``` %myzip -x %{quote:empty spaces.zip} ``` *%{rep *_STRING_, _N_ [,_SEP_]*}* Expand to a string that is the concatenation of _N_ copies of _STRING_, separated by _SEP_ (if specified). Added: 4.19.0 Example: ``` %{rep a 5} ``` *%{reverse:*_STRING_*}* Reverse a string. (Added: 4.19.0) Example: ``` %{reverse:tac} ``` *%{shescape:*_STRING_*}* Single quote _STRING_ with escapes for use in shell. (Added: 4.18.0) Example: ``` %{shescape:foo's} ``` *%{shrink:*_STRING_*}* Trim leading and trailing whitespace from _STRING_, reduce intermediate whitespace to a single space. (Added: 4.14.0) Example: ``` %{shrink:aa bb ccc } ``` *%{span:*_STRING_*}* As-is string, handy for wrapping multiline macros. (Added: 6.0.0) Example: ``` %{span: %one thing %another thing } ``` *%{sub *_STRING_, _I_, [,_J_]*}* Expand to substring of _STRING_ that starts at *I* and continues until *J*. *I* and *J* can be negative to index from the string's end. If *J* is absent, then it is assumed to be equal to *-1* (ie. string end). Added: 4.19.0 Example: ``` *%{sub myfile.zip 3 6}* ``` *%{upper:*_STRING_*}* Expand to uppercase _STRING_. (Added: 4.19.0) Example: ``` %{upper:CamelCase}' ``` ## File and path operations *%{basename:*_PATH_*}* *basename*(1) macro analogue. Example: ``` %{basename:/some/dir/file.suf} ``` *%{dirname:*_PATH_*}* *dirname*(1) macro analogue. Example: ``` %{dirname:/some/dir/file.suf} ``` *%{exists:*_PATH_*}* Test file existence, expands to 1/0. (Added: 4.18.0) Example: ``` %{exists:%{builddir}/myflag.txt} ``` *%{suffix:*_PATH_*}* Expand to suffix part of a filename. Example: ``` %{suffix:myfile.zip} ``` *%{url2path:*_URL_*}* Convert url to a local path. Example: ``` %{url2path:http://rpm.org/not/there} ``` *%{uncompress:*_PATH_*}* Expand to a command for outputting argument _PATH_ to standard output, uncompressing as needed. Example: ``` %{uncompress /my/source.tar.gz} ``` *%{xdg:*_KIND_*}* Expand to XDG base directory paths. Supported values for _KIND_ are: - *cache*: user-specific non-essential (cached) data - *config*: user-specific configuration files - *data*: user-specific data files - *state*: user-specific state data Added: 6.0.0 Example: ``` %{xdg config} ``` ## Environment info *%getncpus* Expand to the number of available CPUs. (Added: 4.15.0) *%{getncpus:*_KIND_*}* Expand to the number of available CPUs, supported valued for _KIND_ are - *total*: total number of available CPUs (same as *%getncpus*) - *proc*: number of available CPUs for processes - *thread*: number of available CPUs for threads *proc* and *thread* account for available memory, including address space limitations for threads. Added: 4.19.0. Example: ``` %{getncpus proc} ``` *%getconfdir* Expand to RPM "home" directory (typically /usr/lib/rpm). *%{getenv:*_NAME_*}* *getenv*(3) macro analogue. Example: ``` %{getenv:HOME} ``` *%rpmversion* Expand to running RPM version. ## Output *%{echo:*_STRING_*}* Print _STRING_ to process standard output. Example: ``` %{echo:Building with foo} ``` *%{warn:*_STRING_*}* Print _STRING_ prefixed with "warning: " to process standard error. Example: ``` %{warning:Foo is deprecated} ``` *%{error:*_STRING_*}* Print _STRING_ prefixed with "error: " to process standard error and flag an error in the macro processor. Example: ``` %{error:Invalid argument} ``` *%verbose* Expand to 1/0 whether RPM is in verbose mode or not. (Added: 4.17.1) *%{verbose:*_STRING_*}* Expand to _STRING_ if RPM is in verbose mode, the empty string if not. (Added: 4.17.1) Example: ``` %{verbose:-x} ``` ## Spec specific macros *%{S:*_NUMBER_*}* Expand to the filename of the specified Source _NUMBER_. *%{S:n}* is equivalent to *%{SOURCEn}*. *%{P:*_NUMBER_*}* Expand to the filename of the specified Patch _NUMBER_. *%{P:n}* is equivalent to *%{PATCHn}*. ## Diagnostics *%trace* Toggle print of debugging information before/after expansion. *%dump* Print the active (i.e. non-covered) macro table. *%\_\_file_name* Expand to current filename (if parsing a file). (Added: 4.15) *%\_\_file_lineno* Expand to current line number in current file (if parsing a file). (Added: 4.15) # EXAMPLES ## Example 1. Define a simple macro Define macro *mylib* to a path relative to *%{\_libdir}* macro in a spec: ``` %define mylib %{_libdir}/mine ``` ## Example 2. Define a parametric macro Define parametric macro *myhelper* which executes the program specified by *%myprog* with it's first argument and always passing the option *--some-opt* to it, and additionally the *--xtra* option if it received the *-x* option itself: ``` %define myhelper(x) %{myprog} --some-opt %{?-x:--xtra} %{1} ``` ## Example 3. Define a macro utilizing shell expansion Define macro *%today* that expands to the current date in _YYMMDD_ format by calling the *date*(1) shell utility. Note the 2nd *%* needed to escape the arguments to *date*(1): ``` %define today %(date +%%y%%m%%d) ``` ## Example 4. Define a macro conditionally Define macro *mypath* if it wasn't previously defined: ``` %{!?mypath: %define mypath /some/where} ``` ## Example 5. Conditional expansion Expands to *1* if *use_foo* is defined and *0* otherwise: ``` %{?use_foo:1}%{!?use_foo:0} ``` ## Example 6. Expressions Calculate 5 \* 1024: ``` %[5 * 1024] ``` Expand to literal *true* or *false* depending on a condition: ``` %[1 < 2 ? "true" : "false"] ``` Compare versions, expanding to *1* or *0* on true/false: ``` %[ v"3.1.0-1" < v"1.0~alpha-2" ? 1 : 0] ``` Expands to *1* if *%aa* expands to *5*, otherwise expands to *2*: ``` %[ "%{aa}" == "5" ? 1 : 2] ``` # DEBUGGING Some useful tools for working with and troubleshooting macros: *rpm --eval "*_VALUE_*"* Expand _VALUE_ on the command line. *rpm --define "aa 11" --eval "%aa"* Define and evaluate a macro on the command line. *rpm --eval "%global unamer %(uname -r)" --eval "%{macrobody:unamer}"* Define and examine macro body using *%global* on the command line. *rpm --eval "%define unamer %(uname -r)" --eval "%{macrobody:unamer}"* Define and examine macro body using *%define* on the command line. *rpmlua* Run an interactive shell in the embedded Lua environment. *rpmlua -e 'print(macros.defined("\_libdir"))'* Using the embedded Lua interpreter in standalone form, print print 1/0 whether macro *\_libdir* is defined or not. *rpmspec --shell* Run an interactive shell in the macro environment. *rpmspec --shell telnet.spec* Run an interactive shell in the macro environment after parsing *telnet.spec*. # SEE ALSO *rpm*(8) *rpm-common*(8) *rpm-macrofile*(5) *rpm-config*(5) *rpm-lua*(7) *rpmspec*(1) *rpmlua*(1) rpm-software-management-rpm-3c1f23f/docs/man/rpm-man-template.scd000066400000000000000000000124771511627505500250570ustar00rootroot00000000000000RPMCOMMAND(1) # NAME *rpmcmd* - Example rpm man page # SYNOPSIS Use the generic form for one-purpose commands: *rpmcmd* [options] [_ARGUMENT_] ... Use one entry per operation for multi-operation (aka mode) commands. Use {} for the operation switches: *rpmcmd* {*-b*|*--bardize*} [options] [bardize-options] [_ARGUMENT_] ... *rpmcmd* {*-f*|*--frobnize*} [options] [_ARGUMENT_] ... # DESCRIPTION Describe what *rpmcmd* command does in plain, understandable English. Command-line switches belong to *OPTIONS* and *OPERATIONS* as appropriate. Below are *rpm* specific conventions. When in doubt, consult *man-pages*(7). ## Typographic conventions - Use imperative style when describing functionality - Use hard wrapping at 80 columns for all text - Use bold for literal values such as command name and options: *cmd* does X, with option *--do-this* does Y instead - Use UPPERCASE for first level sections, and Mixed case for second level sections (if present) - Use matching case and bold when referring to section names - Use uppercase RPM when referring to the RPM project - Use bold when referring to other man page names, eg *rpm*(8) - Use underline uppercase for user arguments placeholders: _ARGUMENT_ - Use surrounding `<>` for multi-value placeholders to differentiate from free-form placeholders: <_METHOD_> (eg one of http/ftp) - Use underline for literal file names: _hello-1.0-1.noarch.rpm_ - Use alphabetical order whenever multiple things are listed and there's no contextual reason to do otherwise - Put the short variant first when listing option aliases - Put an empty line before a new section/option/etc - Put text immediately below a section/option/etc (ie without an empty line) - Put optional elements into [] brackets - Do not emphasize separators like ",", "=", "[]", "<>", "|" etc - Use "Added: X.Y.Z." to indicate the version where a feature was added. Either in parentheses at the end of a line (inline variant) or on a line of it's own, surrounded by empty lines (standalone variant). ## Preferred terms |[ *Term* :[ *Avoid using* | standard input : stdin | standard output : stdout | standard error : stderr # OPERATIONS For multi-operation commands, describe each operation invocation using same notation as for *OPTIONS*. Delete this section if the command only has a single main operation. *-f*, *--frobnize*, Describe frobnization in detail. Multiple lines are the expectation. *-b*, *--bardize* Describe bardization in detail. Multiple lines are the expectation. # ARGUMENTS Describe positional _ARGUMENT_ placeholders from *SYNOPSIS* here. If the command takes no arguments, delete this section. _ARGUMENT_ File of type X. Use descriptive and obvious placeholder names: if it's a file, call the placeholder _FILE_. # OPTIONS Describe common options in this section. If the command takes no options, delete this section. Typographic conventions: - Put option aliases on separate lines - Put option arguments with pre-determined values into <> (see below) See *rpm-common*(8) for the options common to all *rpm* executables. *-d*, *--do-this* Do Y instead of default action. *-o* _FILE_, *--output*=_FILE_ Use _FILE_ for output. *--format*=<_FORMAT_> Use _FORMAT_ *aa* or *bb* for output. *--compress*=<_METHOD_> Use specified compression, where <_METHOD_> is one of the following (when there are too many to list directly in the option) - *bzip2*: bzip2 compression - *gzip*: zlib/gzip compression - *xz*: xz compression is a more complicated topic, requiring multiline explanation - *zstd*: zstd compression (Added: 3.1.2) Added: 2.1.3 # BARDIZE OPTIONS Put operation specific options into a top-level section of their own, matching [operation-options] placeholder in SYNOPSIS. # OUTPUT Describe program output to the degree needed to understand program operation. Do not make unnecessary promises about the exact format if the output is not intended to be machine parseable. Use formal notation in an non-indented literal text block for machine parseable output: ``` "" ``` Describe operation-specific output by their main invocation switch, if necessary. *--frobnize* ``` "" ``` # CONFIGURATION List main configurables affecting this program here, but don't describe. If the command has no relevant configuration, delete this section. See *rpm-config*(5) for details: - *%\_bar_mode* - *%\_foo_mode* # ENVIRONMENT Describe environment variables affecting program execution. If the command has no relevant environment variables, delete this section. # EXIT STATUS On success, 0 is returned, a non-zero failure code otherwise. # EXAMPLES Add at least one example for each major functionality of the command. Command first in bold, then description, similar to option descriptions. If command output is included in the description, use indented literal block. Use second level, numbered sections to separate longer examples from each other, or to group related ones together. ## Example 1. Basic commands *rpmcmd --do-this package-1.0-1.noarch.rpm* Do this thing to _package-1.0-1.noarch.rpm_. *rpmcmd --ls package-1.0-1.noarch.rpm* List contents of _package-1.0-1.noarch.rpm_. ``` /bin/one /lib/two ``` # FILES Describe files specific to this command. Delete this section if there are none. # SEE ALSO *rpm*(8) *rpm-common*(8) *rpm-config*(5) *popt*(3) *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpm-manifest.5.scd000066400000000000000000000034171511627505500244360ustar00rootroot00000000000000RPM-MANIFEST(5) # NAME *rpm-manifest* - rpm package manifest file format # SYNOPSIS _GLOB_ ... \# _COMMENT_ # DESCRIPTION RPM package manifest files are plaintext files containing one or more *glob*(7) entries that are expected to match either RPM package files or other package manifests. Package manifests can be used as a way to document installation of large package sets in an RPM-native way, without having to set up repositories and configure external dependency resolver programs to use them. For end-user purposes manifests are clumsy, but for example to quickly install a base image from a pre-determined set of package versions from local directory, a manifest may well be easier and faster than using a depsolver for the task. A manifest can be also used to work around command line length limitations. Relative globs are interpreted relative to the user's current directory. Multiple globs per line are permitted, but for readability it's recommended to use one per line instead. Lines beginning with *#* are considered comments. Empty lines and lines consisting of only whitespace are ignored. # EXAMPLES ## Example 1. Manifest of all myproj-1.0 releases and their dependencies ``` /mnt/myproj/rpm/myproj-1.0-*.x86_64.rpm /mnt/deplib/rpm/mylib-2.1-*.x86_64.rpm ``` ## Example 2. Manifest of all noarch packages in the default rpmbuild(1) location ``` ~/rpmbuild/RPMS/noarch/*.rpm ``` ## Example 3. Manifest as a simple base image Create a manifest called *mymanifest.mft* of currently installed packages, assuming a base path of */mnt/Packages* for the RPM package files, and install it to an alternative system root at */srv/test*: ``` rpm -qa --qf '/mnt/Packages/%{nevra}.rpm\\n' > mymanifest.mft rpm -Uv --root /srv/test mymanifest.mft ``` # SEE ALSO *rpm*(8) *rpm-common*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-payloadflags.7.scd000066400000000000000000000040751511627505500253010ustar00rootroot00000000000000RPM-PAYLOADFLAGS(7) # NAME *rpm-payloadflags* - RPM payload flags # SYNOPSIS *w*[_FLAGS_]._TYPE_ # DESCRIPTION The payload flags determine how the payload of an RPM package is compressed when the package is built. The flags are stored in the *Payloadcompressor* and *Payloadflags* tags of the package header, which are used to determine how to uncompress the payload contents during installation. The peculiar syntax is directly inherited from librpm's *Fopen()* IO API, where *w* means opening a file for writing. The following compression types are supported, but availability may vary depending on how RPM was compiled. [- _TYPE_ :< Description | *ufdio* : uncompressed | *gzdio* : gzip (aka zlib) | *bzdio* : bzip2 | *xzdio* : xz | *lzdio* : legacy lzma | *zstdio* : zstd The compression _FLAGS_ must be listed in the following order and can be any of: [- _FLAGS_ :< Description :- Types | <0-9> : compression level : all (*ufdio* ignores) | T[0-N] : number of threads (no number or 0 = autodetect) : *xzdio*, *zstdio* | L<0-9> : window size(see *--long* in *zstd*(1)) : *zstdio* If a flag is omitted, the compressor's default value will be used. A higher compression level generally means better compression ratio at the cost of increased resource use and compression times. *T* is equivalent to *T0* and causes the number of threads to be automatically detected. Note that while using threads can speed up compression considerably, it typically causes the compression ratio to go down, and make the output less predictable. # EXAMPLES [[ Mode :< Description | *w9.gzdio* : gzip level 9, default for package payload | *w9.bzdio* : bzip2 level 9, bzip2's default | *w.xzdio* : xz default level | *w7T16.xzdio* : xz level 7 using 16 threads | *w7T0.xzdio* : xz level 7, autodetect no. of threads | *w6.lzdio* : lzma (legacy) level 6, lzma's default | *w19T8.zstdio* : zstd level 19 using 8 threads | *w7T.zstdio* : zstd level 7, autodetect no. of threads | *w.ufdio* : uncompressed # SEE ALSO *rpmbuild-config*(5), *rpm-config*(5) *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-audit.8.scd000066400000000000000000000016541511627505500252360ustar00rootroot00000000000000RPM-PLUGIN-AUDIT(8) # NAME rpm-plugin-audit - Audit plugin for the RPM Package Manager # DESCRIPTION The plugin writes basic information about rpm transactions to the audit log - like packages installed or removed. The entries can be viewed with *ausearch -m SOFTWARE_UPDATE* ## Data fields The entries in the audit log have the following fields: [[ *Field* :< *Description* :- *Possible values* | *op* : Package operation : install/update/remove | *sw* : Package identifier : name-version-release.arch | *key_enforce* : Are signatures being enforced (no/yes) : 0/1 | *gpg_res* : Signature check result (fail/success) : 0/1 | *root_dir* : Root directory of the operation (normally "/") : | *sw_type* : Package format : rpm # CONFIGURATION There are currently no options for this plugin in particular. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *ausearch*(8), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-dbus-announce.8.scd000066400000000000000000000024341511627505500266660ustar00rootroot00000000000000RPM-PLUGIN-DBUS-ANNOUNCE(8) # NAME rpm-plugin-dbus-announce - D-Bus plugin for the RPM Package Manager # DESCRIPTION The plugin writes basic information about rpm transactions to the system D-Bus - like packages installed or removed. Other programs can subscribe to the signals to be notified of the packages on the system change. # D-BUS SIGNALS Sends *StartTransaction*, *EndTransaction*, *StartTransactionDetails* and *EndTransactionDetails* messages from the */org/rpm/Transaction* object with the *org.rpm.Transaction* interface. The first two signals pass the DB cookie as a string and the transaction id as an unsigned 32 bit integer. The second two signals, those "details", also pass the DB cookie as a string and the transaction id as an unsigned 32 bit integer and then in addition an array of strings containing space-separated string of values describing the operation and the package NEVRA which are part of the transaction. The last argument is a signed integer with the transaction result. The package operation can be one of "added", "removed", "rpmdb" or "restored" (quotes for clarity only). # CONFIGURATION There are currently no options for this plugin in particular. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *dbus-monitor*(1), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-fapolicyd.8.scd000066400000000000000000000010511511627505500260710ustar00rootroot00000000000000RPM-PLUGIN-FAPOLICYD(8) # NAME rpm-plugin-fapolicyd - Fapolicyd plugin for the RPM Package Manager # DESCRIPTION The plugin gathers metadata of currently installed files. It sends the information about files and about ongoing rpm transaction to the fapolicyd daemon. The information is written to Linux pipe which is placed in _/var/run/fapolicyd/fapolicyd.fifo_. # CONFIGURATION There are currently no options for this plugin in particular. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *fapolicyd*(8), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-ima.8.scd000066400000000000000000000013001511627505500246620ustar00rootroot00000000000000RPM-PLUGIN-IMA(8) # NAME rpm-plugin-ima - IMA plugin for the RPM Package Manager # DESCRIPTION Integrity Measurement Architecture (IMA) and the Linux Extended Verification Module (EVM) allow to detect when files have been accidentally or maliciously altered. This plugin puts IMA/EVM signatures in the *security.ima* extended file attribute during installation. This requires packages to contain the signatures - typically by being signed with *rpmsign --signfiles*. # CONFIGURATION The *%\_ima_sign_config_files* macro controls whether signatures should also be written for config files. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *evmctl*(1), *rpmsign*(1), *rpm*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-prioreset.8.scd000066400000000000000000000014531511627505500261410ustar00rootroot00000000000000RPM-PLUGIN-PRIORESET(8) # NAME rpm-plugin-prioreset - Plugin for the RPM Package Manager to fix issues with priorities of daemons on SysV init # DESCRIPTION In general scriptlets run with the same priority as *rpm*(8) itself. However on legacy SysV init systems, properties of the parent process can be inherited by the actual daemons on restart. As a result daemons may end up with unwanted nice or ionice values. This plugin resets the scriptlet process priorities after forking, and can be used to counter that effect. Should not be used with *systemd*(1) because it's not needed there, and the effect is counter-productive. # CONFIGURATION There are currently no options for this plugin in particular. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *rpm*(8), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-selinux.8.scd000066400000000000000000000010641511627505500256120ustar00rootroot00000000000000RPM-PLUGIN-SELINUX(8) # NAME rpm-plugin-selinux - SELinux plugin for the RPM Package Manager # DESCRIPTION The plugin sets SELinux contexts for installed files and executed scriptlets. It needs SELinux to be enabled to work but will work in both enforcing and permissive mode. # CONFIGURATION The plugin can be disabled temporarily by passing *--nocontexts* at the *rpm*(8) command line or setting the transaction flag *RPMTRANS_FLAG_NOCONTEXTS* in the API. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *rpm*(8), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-syslog.8.scd000066400000000000000000000006501511627505500254430ustar00rootroot00000000000000RPM-PLUGIN-SYSLOG(8) # NAME rpm-plugin-syslog - Syslog plugin for the RPM Package Manager # DESCRIPTION The plugin writes basic information about *rpm*(8) transactions to the syslog - like transactions run and packages installed or removed. # CONFIGURATION There are currently no options for this plugin in particular. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *rpm*(8), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-systemd-inhibit.8.scd000066400000000000000000000021201511627505500272310ustar00rootroot00000000000000RPM-PLUGIN-SYSTEMD-INHIBIT(8) # NAME rpm-plugin-systemd-inhibit - Plugin for the RPM Package Manager # DESCRIPTION This plugin for RPM prevents the system to enter shutdown, sleep or idle mode while there is a rpm transaction running to prevent system corruption that can occur if the transaction is interrupted by a reboot. This is achieved by using the inhibit DBUS interface of *systemd*(1). The call is roughly equivalent to executing *systemd-inhibit --mode=block --what=idle:sleep:shutdown --who=RPM --why="Transaction running"* See *systemd-inhibit*(1) for the details of this mechanism. It is strongly advised to have the plugin installed on all systemd based systems. # PREREQUISITES For the plugin to work systemd has to be used as init system and the DBUS system bus must be available. If the plugin cannot access the interface it gives a warning but does not stop the transaction. # CONFIGURATION There are currently no options for this plugin in particular. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *systemd-inhibit*(1), *rpm*(8), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugin-unshare.8.scd000066400000000000000000000020621511627505500255670ustar00rootroot00000000000000RPM-PLUGIN-UNSHARE(8) # NAME rpm-plugin-unshare - Unshare plugin for the RPM Package Manager # DESCRIPTION This plugin allows using various Linux-specific namespace-related technologies inside transactions, such as to harden and limit scriptlet access to resources. # CONFIGURATION This plugin implements the following configurables: %\_\_transaction_unshare_paths A colon-separated list of paths to privately mount during scriptlet execution. Typical examples would be `/tmp` to protect against insecure temporary file usage inside scriptlets, and `/home` to prevent scriptlets from accessing user home directories. When path unsharing is enabled, any mounts made from scriptlets are also private to the scriptlet (and vice versa, mount changes on the host are not visible to the scriptlet). Private mounts in chroot-operations is unimplemented. %\_\_transaction_unshare_nonet Non-zero value disables network access during scriptlet execution. See *rpm-plugins*(8) on how to control plugins in general. # SEE ALSO *dbus-monitor*(1), *rpm-plugins*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-plugins.8.scd000066400000000000000000000026641511627505500243170ustar00rootroot00000000000000RPM-PLUGINS(8) # NAME rpm-plugins - Plugins for the RPM Package Manager # DESCRIPTION RPM plugins provide functionality that is not suited to be used everywhere. They may not be built or shipped on some platforms or may not be installed or be disabled on some systems. This allows plugins to interface with systems that may not acceptable as a dependency for RPM and to provide functionality that may be unwanted under some circumstances. # CONFIGURATION Some plugins can be configured by specific macros or influenced by command line parameters. But most can only be turned on or off. See the plugin's man page for details. Plugins are controlled by a macro *%\_\_transaction_NAME* which is set to the location of the plugin file. Undefining the macro or setting it to *%{nil}* will prevent the plugin from being run. This can be done on the RPM command line e.g. with *--undefine=\_\_transaction_syslog*. To disable a plugin permanently, place a _macros.\*_ file in _/etc/rpm/_ that contains ``` %__transaction_NAME %{nil} ``` Another option is to remove the plugin from the system if it is packaged in its own sub package. For some operations it may be desirable to disable all plugins at once. This can be done by passing *--noplugins* to *rpm*(8) at the command line. # SEE ALSO *rpm*(8), *rpm-plugin-audit*(8), *rpm-plugin-ima*(8), *rpm-plugin-prioreset*(8), *rpm-plugin-selinux*(8), *rpm-plugin-syslog*(8), *rpm-plugin-systemd-inhibit*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm-queryformat.7.scd000066400000000000000000000233641511627505500252130ustar00rootroot00000000000000RPM-QUERYFORMAT(7) # NAME *rpm-queryformat* - RPM query mini-language # SYNOPSIS ## Query format \[_PLACEHOLDER_|_ITERATOR_|_EXPRESSION_|_TEXT_ ...] ## Placeholder *%*[_WIDTH_]*{*_TAG_[*:*_FORMAT_]*}* ## Iterator *[*_QUERYFORMAT_ ...*]* Note: Square brackets are literal and _QUERYFORMAT_ must contain an array placeholder. ## Expression *%|*_TAG_*?{*_PRESENT_*}:{*_ABSENT_*}|* Note: Vertical bars are literal, _PRESENT_ and _ABSENT_ are query formats. # DESCRIPTION RPM supports a simple query language for extracting header data to plain text. Its syntax resembles that of *printf*(3) where header tags enclosed in *%{* and *}*, for example *%{NAME}*, are used as placeholders. A string written in this syntax is called a _query format_. Query formats are currently accepted in the following places: . Query operations with *rpm*(8), as a way to control the output (see *--queryformat*) . Runtime scriptlets in *rpm-spec*(5) files, to access arbitrary header data at runtime . Configuration macros in *rpm-config*(5) and *rpmbuild-config*(5), such as *%\_query\_all\_fmt* or *%\_rpmfilename* See the *EXAMPLES* section for more details. # SYNTAX A valid query format is composed of optional literal text and zero or more _placeholders_. A placeholder has the following syntax: ``` %[WIDTH]{TAG[:FORMAT]} ``` When RPM produces output according to a query format, it substitutes each placeholder with the _TAG_ data from the queried package and formats the data according to the specified _WIDTH_ and _FORMAT_ (if given). The rest of the query format is copied to the output unchanged. Special characters such as *\\n* or *\\t*, with the exception of *\\0*, are also supported. Curly braces, the percent sign and the backslash may be escaped with a backslash. The individual components of a placeholder, as well as some additional language features, are described in the subsections below. ## Tags Every RPM package contains a key-value store (the _header_) that represents the package's properties, such as its name, version or file list. The keys in this store are called _tags_, and can be used in a query format as placeholders for the values (_data_) they point to. The data is either a single _scalar_ (string, integer or binary blob), or an _array_ (argv) of multiple scalars of the same type. To obtain a list of all tags supported by the installed version of RPM, run the following command: ``` rpm -v --querytags ``` This will print a list of available tags, along with their internal numbers and types, for example: ``` BASENAMES 1117 argv BUILDHOST 1007 string BUILDTIME 1006 int32 DESCRIPTION 1005 i18nstring EPOCH 1003 int32 INSTALLTIME 1008 int32 NAME 1000 string RELEASE 1002 string SIZE 1009 int32 SUMMARY 1004 i18nstring VERSION 1001 string ``` Tags are case-insensitive. Each tag also has an implicit variant with the *RPMTAG_* prefix, such as *RPMTAG_NAME*. Both variants can be used in a query format interchangeably. See the tags documentation in *SEE ALSO* for more details. ## Formats Each tag is printed in a specific output _FORMAT_. When no format is specified, the default *:string* is used which displays a bare-bones representation of the data. You can specify a different format by appending its name after a colon, for example: ``` %{INSTALLTIME:date} ``` This is useful for tags that are not stored in human-readable form, such as sizes, dates or signatures. Some formats allow for performing more complex transformations of the data, such as encoding it in base64 or wrapping it in JSON syntax. The following formats are currently available: |[ *Name* :< *Description* | *:armor* : Wrap a public key in ASCII armor | *:arraysize* : Display number of elements in array tags | *:base64* : Encode binary data using base64 | *:date* : Use *strftime*(3) "%c" format | *:day* : Use *strftime*(3) "%a %b %d %Y" format | *:depflags* : Format dependency comparison operator | *:deptype* : Format dependency type | *:expand* : Perform macro expansion | *:fflags* : Format file flags | *:fstate* : Format file state | *:fstatus* : Format file verify status | *:hashalgo* : Display hash algorithm name | *:hex* : Format in hexadecimal | *:octal* : Format in octal | *:humaniec* : Human readable number (in IEC 80000) where K = 1024, M = 1048576, ... | *:humansi* : Human readable number (in SI) where K = 1000, M = 1000000, ... | *:json* : Wrap data in JSON | *:perms* : Format file permissions | *:pgpsig* : Display signature fingerprint and time | *:shescape* : Escape single quotes for use in a script | *:string* : Display string format (default) | *:tagname* : Display tag name | *:tagnum* : Display tag number | *:triggertype* : Display trigger suffix | *:vflags* : Format file verification flags | *:xml* : Wrap data in simple XML markup Note that some formats are type specific, which means they can only be used with a specific tag type. If you attempt to use them with an incompatible type, RPM will replace them with a placeholder text. For example, the following query format will result in the output "(not a number)": ``` %{NAME:date} ``` Similarly, if a tag is not present in the queried package, it will be replaced with the text "(none)". ## Width You can specify the minimum _WIDTH_ of the data when printed, by placing a number in between the *%* and *{* characters, similarly to field width used in *printf*(3). Optionally, prefix the number with the minus sign to left-justify the text. ## Iterators Tags pointing to arrays may be expanded to the individual elements by using _iterators_. An iterator is a query (sub-)format enclosed in square brackets that contains a placeholder for an array, for example: ``` [%{FILENAMES}\\n] ``` RPM will loop through the enclosed array, outputting the (sub-)format each turn and substituting the placeholder with the current element. This example includes a newline character, which will result in one filename per line. Multiple arrays may be placed into a single iterator. RPM will then iterate over all of them in lockstep, meaning that the first iteration will substitute each placeholder with its array's first element, the second iteration will use each array's second element, and so on. For example: ``` [%10{FILESIZES} %{FILENAMES}\\n] ``` This is useful with _parallel_ arrays that RPM keeps internally. These are sets of arrays where all elements at the same index correspond to each other. One can think of parallel arrays as table columns that can be arbitrarily rearranged. The above example will output a table of files and their sizes. Sometimes, it is useful to combine an array with one or more scalars in the same iterator. This is possible since RPM internally treats all scalars as arrays of size one. However, iterators may only contain arrays of the same size, otherwise RPM will complain. To avoid that, you can "lock" an array so that only its first element is used in each iteration, by prefixing the tag with the equal sign, for example: ``` [%{=NAME} %{FILENAMES}\\n] ``` This will produce an "annotated" file list where each filename is preceded by the name of the package that contains it. ## Expressions Simple conditionals may be evaluated through query _expressions_ in the form of ``` %|EXPR| ``` where _EXPR_ is an expression. The only type of expression currently supported is a C-like ternary operator for simple if/then/else conditions, which has the syntax ``` %|TAG?{PRESENT}:{ABSENT}| ``` where _PRESENT_ and _ABSENT_ are query formats that should be expanded if the queried package does and doesn't have the _TAG_, respectively. Thus, expressions can be nested. # EXAMPLES ## Example 1. Query commands *rpm -qa --queryformat "%-30{NAME} %{SIZE:humaniec}\\n"* Print a table of all installed package names and their human-readable sizes. *rpm -q --queryformat "%{NAME} %{INSTALLTIME:date}\\n" fileutils* Print the *fileutils* package name followed by its installation date. *rpm -q --queryformat "[%{FILEMODES:perms} %{FILENAMES}\\n]" rpm* Print all filenames owned by the *rpm* package, one per line, with each name preceded by a file permissions string similar to the long listing format of *ls*(1). *rpm -q --queryformat "[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\\n]" vlock* Print all capabilities on which the *vlock* package depends in the form of version comparisons, one per line, with the capability name and the required version as the left and right operand, respectively. *rpm -qa --queryformat "%-30{NAME} %|PREINPROG?{ %{PREINPROG}}:{ no}|\\n"* Print a table of all installed package names in the left column and the program names used for their *prein* scriptlets (such as the default */bin/sh*) in the right column, with the latter being *no* if the given package has no *prein* scriptlet. ## Example 2. Command aliases _/usr/lib/rpm/rpmpopt-VERSION_ A *popt*(3) alias file used by *rpm*(8) itself to implement various shorthand options (such as *--scripts*) with the help of query formats. Replace _VERSION_ with the version of RPM installed on your system. ## Example 3. Runtime scriptlets Print the exact file list of a package after its installation: ``` %post -q for f in [%%{instfilenames} ]; do echo $f done ``` Note that the trailing space inside the square brackets is required in order for the query format to be expanded to separate words that the *for* statement can loop over. See the scriptlet expansion documentation in *SEE ALSO* for more details, and *rpm-spec*(5) for more information on scriptlets in general. # SEE ALSO *rpm*(8), *rpm-common*(8), *rpm-config*(5), *rpm-spec*(5), *popt*(3) *https://rpm.org/docs/latest/manual/tags*++ *https://rpm.org/docs/latest/manual/scriptlet_expansion* rpm-software-management-rpm-3c1f23f/docs/man/rpm-rpmrc.5.scd000066400000000000000000000064751511627505500237620ustar00rootroot00000000000000RPM-RPMRC(5) # NAME *rpm-rpmrc* - rpm platform compatibility configuration # SYNOPSIS _VARIABLE_: {_ARCH_|_OS_}: _VALUE_ ... _VARIABLE_: _ARCH_ _VALUE_ # FILES _/usr/lib/rpm/rpmrc_++ _/usr/lib/rpm//rpmrc_++ _/etc/rpmrc_++ _~/.config/rpm/rpmrc_ # DESCRIPTION The low-level machine architecture and OS configuration in *rpm* is managed via a set of rpmrc files as defined by the _rpmrc path_. Most users never need to look at, much less touch these files. Each file in the colon separated _rpmrc path_ is read sequentially by *rpm* for configuration information. Tildes will be expanded to the value of the environment variable *HOME*. The first file in the path must exist, others are considered optional. If a value is defined multiple times, the last entry wins. The default _rpmrc path_ uses this to achieve the following hierarchy of platform configuration: . Generic *rpm* factory defaults . Vendor (distribution) specific configuration . Host specific configuration . User specific configuration In older rpm versions the path of per-user rpmrc was _~/.rpmrc_. This is still processed if it exists and the new configuration directory does not exist. # CONFIGURATION _ARCH_ and _OS_ relate to *uname*(2) machine and operating system information, but are not 1:1 equivalent. The following directives are supported in the rpmrc files: *arch_canon* _ARCH_: _CANON_ARCH_ _ARCH_NUM_ Names and numbers of known architectures to alias different spellings to a canonical one. _CANON_ARCH_ is what the _ARCH_ entries in other rpmrc directives refer to. The number is not used for any calculations by *rpm* but must be present for historical reasons. *arch_compat*: _ARCH_: _COMPAT_ARCH_ ... Declare compatibility between machine architectures, ie. _ARCH_ machines can install packages for _COMPAT_ARCH_ architecture. *archcolor*: _ARCH_ _COLOR_ Declare the "color" of _ARCH_. The color relates to the word length aka bitness of the architecture: - *0* means none (noarch packages and similar) - *1* means 32-bit - *2* means 64-bit *buildarch_compat*: _ARCH_: _COMPAT_ARCH_ ... Declare compatibility between build architectures targets, ie. _ARCH_ machines can produce _COMPAT_ARCH_ binaries. *buildarchtranslate*: _ARCH_: _TRANSLATE_ARCH_ Automatically translate detected host architecture _ARCH_ to _TRANSLATE_ARCH_ when building packages. This is used to map sub-architectures to a main one, for example when building on a *x86_64_v2* host we typically want the generated package to be of the main *x86_64* architecture. *optflags*: _ARCH_ _OPTFLAGS_ Compiler flags to use when building packages for _ARCH_ architecture. The _OPTFLAGS_ value is available as *%{optflags}* macro in spec files. *os_canon*: _OS_: _CANON_OS_ _OS_NUM_ Names and numbers of known operating systems to alias different spellings to a canonical one. _CANON_OS_ is what the _OS_ entries in other rpmrc directives refer to. The number is not used for any calculations by *rpm* but must be present for historical reasons. *os_compat*: _OS_: _COMPAT_OS_ ... Declare compatibility between operating systems, ie. _OS_ machine can install packages for _COMPAT_OS_ operating system. # ENVIRONMENT If *XDG_CONFIG_HOME* environment variable is set, it replaces _~/.config_ in the _rpmrc path_. # SEE ALSO *rpm*(8), *rpm-common*(8), *rpm-config*(5), *rpm-macros*(7) rpm-software-management-rpm-3c1f23f/docs/man/rpm-setup-autosign.1.scd000066400000000000000000000023701511627505500256100ustar00rootroot00000000000000RPM-SETUP-AUTOSIGN(1) # NAME *rpm-setup-autosign* - Set up automatic signing for rpmbuild # SYNOPSIS *rpm-setup-autosign* [options] # DESCRIPTION *rpm-setup-autosign* is used to set up automatic signing from *rpmbuild*(1). It generates a user- and host-specific, passwordless OpenPGP key, configures *rpmbuild*(1) to use that key and exports the public key (aka certificate) for importing to the persistent *rpm*(8) keyring. The purpose of automatic signing is to make testing local builds painless. For distributing packages, it's recommended to use a separate signing account that cannot be compromised by a build. # OPTIONS *-p* <*gpg*|*sq*>, *--prog* <*gpg*|*sq*> Specify the signing program to use: GnuPG or Sequoia PGP's sq. # CONFIGURATION On successful completion, one or more of the following macros will be configured in the user's macro configuration file. See *rpm-config*(5) for details: - *%\_openpgp_autosign_id* - *%\_openpgp_sign* # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # FILES _~/.config/rpm/rpmbuild-\*.asc_++ _~/.config/rpm/macros_ # EXAMPLES */usr/lib/rpm/rpm-setup-autosign -p sq* Set up *rpmbuild*(1) autosigning using Sequoia-sq. # SEE ALSO *rpm*(8) *rpmsign*(1) *rpmbuild*(1) *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpm-version.7.scd000066400000000000000000000121571511627505500243200ustar00rootroot00000000000000RPM-VERSION(7) # NAME *rpm-version* - RPM version system # SYNOPSIS \[_EPOCH_*:*]_VERSION_[*-*_RELEASE_] # DESCRIPTION A label known as _EVR_ is used to refer to software versions in RPM, consisting of up to three _components_: - _VERSION_ reflects the actual packaged software version. - _RELEASE_ reflects packaging revisions within that software version. - _EPOCH_ is an artificial override to allow working around versioning anomalies. Only the _VERSION_ component is mandatory in an _EVR_ label. All RPM packages have a _VERSION_ and a _RELEASE_, however. ## Version The _VERSION_ string reflects the actual packaged software version. The string consists of ASCII alphanumeric characters, optionally _segmented_ with the _separators_ period (*.*), underscore (*\_*) and the plus sign (*+*), and _operators_ tilde (*~*) and caret (*^*). The _operators_ are used to map pre- and post-release version strings into a coherent upgrade. Notably, the dash character (*-*) can NOT be used in _VERSION_ or _RELEASE_ as it is the component separator. ## Release The _RELEASE_ reflects a revision within a single software version. Ideally, _RELEASE_ is a simple integer that is incremented whenever changes are made to a package, and should be reset (to 1) whenever the software version changes. Technically, the format is exactly the same as for _VERSION_. ## Epoch The _EPOCH_ is a non-negative integer, separated from the version with a colon (*:*). It's the most significant part of an _EVR_, skewing version comparison to make an older version to appear newer. It's sometimes necessary to work around version anomalies such as a software project changing its versioning scheme, but also packaging errors. An omitted _EPOCH _ has an implicit value of zero. The epoch should be only used as a last resort. It violates the principle of least surprise, and changing it requires all related versioned dependencies in other packages to be updated accordingly. ## Comparing Two _EVR_\s are compared left to right, one component at a time. The components are compared left to right, a _segment_ at a time. The comparison stops as soon as a segment or component level difference is found, so if eg. the epochs differ, the rest of the _EVR_ is not considered at all. Within components, consecutive alphabetic characters and consecutive numbers form _implicit segments_. _Explicit segments_ are denoted by _separators_ and _operators_. Numeric segments are compared numerically as integers with leading zeros ignored, otherwise lexicographical comparison is used. That is, *abc123* consists of two segments: *abc* and *123* and is equal to *abc0123*, *abc.123* and *abc.000123* despite difference in appearance. Numeric segments are considered newer than alphabetic segments regardless of the actual content. When otherwise equal, the component with more segments is considered newer, and similarly an _EVR_ with more components is considered newer. For example, *0.0* is newer than *0* and *1.xyz* is older than *1.0* but newer than *1*. The segment _separator_ characters are not compared, so they can be used interchangeably, and multiple consecutive separators are treated as if only one separator was used. Thus, *1.0* is equal to *1+0* and *1+.+0*. The tilde operator causes a segment to sort older, and is used for _pre-release versions_. For example *2.0~beta1* is older than *2.0* or *2.0~rc1*, and newer than *1.0*. The caret operator is essentially the opposite of tilde. It causes a segment to sort newer, and is used for _post-release snapshots_ that exist between actual software releases. For example, *2.0^150825* is newer than *2.0* but older than *2.0.1*. # EXAMPLES *123* A simple one-segment version *123*. Newer than *99*, older than *321*. *1.0.1* A segmented version string *1.0.1*, such as commonly used in software projects to indicate major.minor.micro semantics. Newer than *1.0*, older than *1.0.2*. *2.60.1-1* First release of version *2.60.1*. Newer than *2.0* or *2.60*, but older than *3.0*. *1.0-5* Fifth release of version *1.0*, newer than *1.0* or *1.0-1*, older than *1.0.1*. *5:3.0-1* First release of version *3.0*, with epoch of *5*. Newer than for *6.0-1*, or *4:6.0-1*, older than *5:3.1-1*. *1.0~beta2* Pre-release *beta2* of version *1.0*. Newer than *0.99* and *1.0~beta1*, older than *1.0*. *2.0^20250611* Post-release snapshot *20250611* of version *2.0*. Newer than *2.0*, older than *2.0.1*. # BUGS Various non-obvious behaviors and dark corners exist within the version comparison algorithm, but are difficult to address due to high risk of breaking existing packages: - Non-ASCII characters are ignored and thus equal: *1.1.α* equals *1.1.β* and even *1.1.ββ*. *rpmbuild*(1) rejects such EVR\s but it's possible to encounter "illegal" values via API usage, very old packages and packages created with 3rd party tools. - Implicit segments can be deceptive: *1.f* is newer than *1c.f*. The result becomes more obvious by making the segments explicit: *1.f* is newer than *1.c.f* because the segments are compared one by one, and *c* sorts lower than *f* lexicographically. *rpm*(8) *rpmbuild*(1) *rpmsort*(1) *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpm.8.scd000066400000000000000000000426161511627505500226410ustar00rootroot00000000000000RPM(8) # NAME rpm - RPM Package Manager # SYNOPSIS ## Querying and verifying packages *rpm* {*-q*|*--query*} [select-options] [query-options] [_PACKAGE_FILE_ ...] [_PACKAGE_NAME_ ...] *rpm* {*-V*|*--verify*} [select-options] [verify-options] [_PACKAGE_NAME_ ...] ## Installing, upgrading and removing packages *rpm* {*-i*|*--install*} [transaction-options] [install-options] _PACKAGE_FILE_ ... *rpm* {*-U*|*--upgrade*} [transaction-options] [install-options] _PACKAGE_FILE_ ... *rpm* {*-F*|*--freshen*} [transaction-options] [install-options] _PACKAGE_FILE_ ... *rpm* *--reinstall* [transaction-options] [install-options] _PACKAGE_FILE_ ... *rpm* *--restore* [transaction-options] [select-options] _PACKAGE_NAME_ ... *rpm* {*-e*|*--erase*} [transaction-options] [erase-options] _PACKAGE_NAME_ ... ## Misc operations *rpm* *--querytags* See *rpm-common*(8) for operations common to all rpm executables. # DESCRIPTION *rpm* is a powerful *Package Manager*, which can be used to build, install, query, verify, update, and erase individual software packages. A *package* consists of an archive of files and meta-data used to install and erase the archive files. The meta-data includes helper scripts, file attributes, and descriptive information about the package. *Packages* come in two varieties: binary packages, used to encapsulate software to be installed, and source packages, containing the source code and recipe necessary to produce binary packages. # OPERATIONS ## Querying and verifying packages *-V*, *--verify* Verify package(s), comparing information about the installed files in the package with information about the files taken from the package metadata stored in the *rpm* database. Among other things, verifying compares the size, digest, permissions, type, owner and group of each file. Any discrepancies are displayed. *-q*, *--query* Query package files or installed package(s). ## Installing, upgrading and removing packages *-i*, *--install* Install new package(s) without considering upgrade scenarios. This is considered special usage, normally installations should be done with *--upgrade*. *-U*, *--upgrade* Install or upgrade package(s) to a newer version. Old and/or obsoleted package(s) are removed after the new package is installed. *-F*, *--freshen* Upgrade package(s), but only ones for which an earlier version is installed. *--reinstall* Reinstall previously installed package(s). *--restore* Restore file metadata such as timestamp, owner, group, permissions and capabilities of files of installed package(s). *-e*, *--erase* Erase installed packages. ## Misc operations *--querytags* Dump known querytags. Useful with the *--queryformat* option. ## Obsolete compatibility aliases These are obsolete *popt*(3) aliases for backwards compatibility only, and their use is discouraged. *--initdb*, *--rebuilddb*, *--verifydb* See *rpmdb*(8). *--addsign*, *--delsign*, *--resign* See *rpmsign*(1). *-K*, *--checksig*, *--import* See *rpmkeys*(8). *--specfile* See *rpmspec*(1). See *rpm-common*(8) for the operations common to all rpm executables. # ARGUMENTS _PACKAGE_FILE_ Either an *rpm* package or an *rpm-manifest*(5) file. May also be specified as an *ftp* or *http* URL, in which case the package will be downloaded before being installed. See *FTP/HTTP OPTIONS* for information on *rpm*'s *ftp* and *http* client support. _PACKAGE_NAME_ Installed package named _PACKAGE_NAME_. To specify the package more precisely the package name may be followed by the version or version and release both separated by a dash or an architecture name separated by a dot. See the output of *rpm -qa* or *rpm -qp* _PACKAGE_FILE_ as an example. # OPTIONS See *rpm-common*(8) for the options common to all operations. # INSTALL AND UPGRADE OPTIONS *--allfiles* Installs or upgrades all the *missingok* files in the package, regardless if they exist. *--badreloc* Used with *--relocate*, permit relocations on all file paths, not just those _OLDPATH_'s included in the binary package relocation hint(s). *--excludepath* _OLDPATH_ Don't install files whose name begins with _OLDPATH_. *--excludeartifacts* Don't install any files which are marked as artifacts, such as build-id links. *--excludeconfigs*, *--noconfigs* Do not install configuration files. *--excludedocs* Don't install any files which are marked as documentation (which includes man pages and texinfo documents). *--force* Same as using *--replacepkgs*, *--replacefiles*, and *--oldpackage*. *--ignoresize* Don't check mount filesystems for sufficient disk space before installing this package. *--ignorearch* Allow installation or upgrading even if the architectures of the binary package and host don't match. *--ignoreos* Allow installation or upgrading even if the operating systems of the binary package and host don't match. *--includedocs* Install documentation files. This is the default behavior. *--nocontexts* Don't set SELinux contexts for files and scriptlets. Only effective if *rpm-plugin-selinux*(8) is installed and active. *--nocaps* Don't set file capabilities. *--noverify* Don't perform verify package files prior to installation. *--nosysusers* Don't create sysusers from packages *--oldpackage* Allow an upgrade to replace a newer package with an older one. *--prefix* _NEWPATH_ For relocatable binary packages, translate all file paths that start with the installation prefix in the package relocation hint(s) to _NEWPATH_. *--relocate* _OLDPATH_=_NEWPATH_ For relocatable binary packages, translate all file paths that start with _OLDPATH_ in the package relocation hint(s) to _NEWPATH_. This option can be used repeatedly if several _OLDPATH_'s in the package are to be relocated. *--replacefiles* Install the packages even if they replace files from other, already installed, packages. *--replacepkgs* Install the packages even if some of them are already installed on this system. # ERASE OPTIONS *--allmatches* Remove all versions of the package which match _PACKAGE_NAME_. Normally an error is issued if _PACKAGE_NAME_ matches multiple packages. # TRANSACTION OPTIONS The transaction options are common to *--install*, *--upgrade*, *--reinstall* and *--erase* operations. *--deploops* Print dependency loops as warnings. *-h*, *--hash* Print 50 hash marks as the package archive is unpacked. Use with *-v*|*--verbose* for a nicer display. *--justdb* Update only the database, not the filesystem. *--nodb* Update only the filesystem, not the database. *--nodeps* Don't check dependencies before uninstalling the packages. *--noorder* Don't reorder the packages for an install. The list of packages would normally be reordered to satisfy dependencies. *--noplugins* Do not load and execute plugins. *--noscripts* Disables execution of package scriptlets. Equivalent to *--nopre* *--nopost* *--nopreun* *--nopostun* *--nopretrans* *--noposttrans* *--nopreuntrans* *--nopostuntrans* *--nopre* *--nopost* *--nopreun* *--nopostun* *--nopretrans* *--noposttrans* *--nopreuntrans* *--nopostuntrans* Don't execute the scriptlets of the named type. *--notriggers* Disable execution of package triggers. Equivalent to *--notriggerprein* *--notriggerin* *--notriggerun* *--notriggerpostun* *--notriggerin* *--notriggerun* *--notriggerprein* *--notriggerpostun* Disable execution of the trigger scriptlets of the named type. *--percent* Print percentages as files are unpacked from the package archive. This is intended to make *rpm* easy to run from other tools. *--test* Don't really execute anything, just go through the motions. Useful in conjunction with the *-vv* option for debugging. # QUERY OPTIONS *--qf* _QUERYFMT_, *--queryformat* _QUERYFMT_ Output format of each queried package, as described by *rpm-queryformat*(7). If omitted, uses the format defined by *%\_query\_all\_fmt* in *rpm-config*(5). There are three subsets of options for querying: package selection, file selection and information selection. # PACKAGE SELECTION OPTIONS *-a*, *--all* [_SELECTOR_ ...] Query all installed packages. Optional _SELECTOR_'s in the form of tag=pattern can be provided to narrow the selection, for example name="b\*" to query packages whose name starts with "b". *--dupes* List duplicated packages. *-f*, *--file* _FILE_ Query package owning installed _FILE_. *--filecaps* List filenames with POSIX1.e capabilities. *--fileclass* List filenames with their classes (libmagic classification). *--filecolor* List filenames with their colors (0 for noarch, 1 for 32bit, 2 for 64 bit). *--fileprovide* List filenames with their provides. *--filerequire* List filenames with their requires. *-g*, *--group* _GROUP_ Query packages with the group of _GROUP_. *--noglob* Do not glob arguments when installing package files. *--nomanifest* Don't process non-package files as *rpm-manifest*(5) files. *-p*, *--package* _PACKAGE_FILE_ Query an (uninstalled) package _PACKAGE_FILE_. *--path* _PATH_ Query package(s) owning _PATH_, whether the file is installed or not. Multiple packages may own a _PATH_, but the file is only owned by the package installed last. *--querybynumber* _HDRNUM_ Query the _HDRNUM_ database entry directly; this is useful only for debugging. *--specfile* _SPECFILE_ Obsolete, use *rpmspec*(1) instead. *--tid* _TID_ Query package(s) that have a given _TID_ transaction identifier. A UNIX timestamp is currently used as a transaction identifier. All package(s) installed or erased within a single transaction have a common identifier. *--triggeredby* _PACKAGE_NAME_ Query packages that are triggered by package(s) _PACKAGE_NAME_. *--whatobsoletes* _CAPABILITY_ Query all packages that obsolete _CAPABILITY_ for proper functioning. *--whatprovides* _CAPABILITY_ Query all packages that provide the _CAPABILITY_ capability. *--whatrequires* _CAPABILITY_ Query all packages that require _CAPABILITY_ for proper functioning. *--whatconflicts* _CAPABILITY_ Query all packages that conflict with _CAPABILITY_. *--whatrecommends* _CAPABILITY_ Query all packages that recommend _CAPABILITY_. *--whatsuggests* _CAPABILITY_ Query all packages that suggest _CAPABILITY_. *--whatsupplements* _CAPABILITY_ Query all packages that supplement _CAPABILITY_. *--whatenhances* _CAPABILITY_ Query all packages that enhance _CAPABILITY_. # PACKAGE QUERY OPTIONS *--changelog* Display change information for the package. *--changes* Display change information for the package with full timestamps. *--conflicts* List capabilities this package conflicts with. *--dump* Dump file information as follows (implies *-l*): path size mtime digest mode owner group isconfig isdoc rdev symlink *--enhances* List capabilities enhanced by package(s). *--filesbypkg* List all the files in each selected package. *--filetriggers* List filetrigger scriptlets from package(s). *-i*, *--info* Display package information, including name, version, and description. This uses the *--queryformat* if one was specified. *--last* Orders the package listing by install time such that the latest packages are at the top. *-l*, *--list* List files in package. *--obsoletes* List packages this package obsoletes. *--provides* List capabilities this package provides. *--recommends* List capabilities recommended by package(s). *-R*, *--requires* List capabilities on which this package depends. *--suggests* List capabilities suggested by package(s). *--supplements* List capabilities supplemented by package(s). *--scripts* List the package specific scriptlet(s) that are used as part of the installation and uninstallation processes. *-s*, *--state* Display the *states* of files in the package (implies *-l*). The state of each file is one of *normal*, *not installed*, or *replaced*. *--triggers*, *--triggerscripts* Display the trigger scripts, if any, which are contained in the package. *--xml* Format package headers as XML. # FILE SELECTION OPTIONS *-A*, *--artifactfiles* Only include artifact files (implies *-l*). *-c*, *--configfiles* Only include configuration files (implies *-l*). *-d*, *--docfiles* Only include documentation files (implies *-l*). *-L*, *--licensefiles* Only include license files (implies *-l*). *--noartifact* Exclude artifact files. *--noconfig* Exclude config files. *--noghost* Exclude ghost files. # VERIFY OPTIONS The package and file selection options are the same as for package querying (including *rpm-manifest*(5) files as arguments). Other options unique to verify mode are: *--nodeps* Don't verify dependencies of packages. *--nodigest* Don't verify package or header digests when reading. *--nofiles* Don't verify any attributes of package files. *--noscripts* Don't execute the *%verifyscript* scriptlet (if any). *--nosignature* Don't verify package or header signatures when reading. *--nolinkto* *--nofiledigest* (formerly *--nomd5*) *--nosize* *--nomtime* *--nomode* *--nordev* Don't verify the corresponding file attribute. *--nouser* *--nogroup* Don't verify file user/group ownership. Note that only local *passwd*(5) and *group*(5) databases are consulted. *--nocaps* Don't verify file capabilities. # OUTPUT *--verify* The format of the output is a string of 9 _result_ characters a possible _attribute_ from the package, followed by the filename. Each of the 9 characters denotes the result of a comparison of attribute(s) of the file to the value of those attribute(s) recorded in the database. A single "*.*" (period) means the test passed, while a single "*?*" (question mark) indicates the test could not be performed (e.g. file permissions prevent reading). Otherwise, the (mnemonically em*B*oldened) character denotes failure of the corresponding *--verify* test: |[ *Result* :< *Description* | *S* : file *S*ize differs | *M* : *M*ode differs (includes permissions and file type) | *5* : digest (formerly MD*5* sum) differs | *D* : *D*evice major/minor number mismatch | *L* : read*L*ink(2) path mismatch | *U* : *U*ser ownership differs | *G* : *G*roup ownership differs | *T* : m*T*ime differs | *P* : ca*P*abilities differ |[ *Attribute* :< *Description* | *a* : *%artifact* - an implicit side-effect file (eg. build-id links) | *c* : *%config*uration file | *d* : *%doc*umentation file | *g* : *%ghost* file | *l* : *%license* file | *m* : *%missingok* - file missing is not a verify failure | *n* : %config(*noreplace*) - do not replace (a %config file) | *r* : *%readme* file | *s* : *rpm-spec*(5) file in a source package # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpm -Uvh hello-2.0-1.noarch.rpm* Install or upgrade _hello-2.0-1.noarch.rpm_ package with verbose output and progress meters. *-Uvh* is probably the most common of all the *rpm* operations. *rpm -Uvh --excludedocs --root /srv/test mydist-23.mft* Install packages described by the _mydist-23.mft_ *rpm-manifest*(5) file into the alternative system root at _/srv/test_, excluding all documentation files. *rpm -Fvh \*.rpm* Freshen installed packages from a local directory, with verbose output and progress meters. *rpm -i kernel-6.15.4-200.x86_64.rpm* Install _kernel-6.15.4-200.x86_64.rpm_ package quietly. On some Linux distributions, different kernel versions are intended to be installed in parallel like this, _without_ erasing older ones to ensure there's a bootable kernel in case of problems. It's one of the few cases where *-i* is the right thing to use. *rpm -evh --allmatches libhello* Erase all versions of package *libhello* on the system, with verbose output and progress meters. Typically there is only one version of a package installed at a time, but in some cases it's possible to have different versions or different architectures of a package installed in parallel, in which case *--allmatches* can be handy. *rpm -vh --reinstall hello-2.0-1.noarch.rpm* Reinstall previously installed _hello-2.0-1.noarch.rpm_ package, with verbose output and progress labels. Useful if eg. the files of a package get corrupted or erased accidentally. *rpm -v --restore --all* Restore permissions of files in all installed packages to their packaged specifications, with verbose output. *rpm --verify --noconfig openssh-server* Verify the integrity of the installed *openssh-server* package, ignoring changes in configuration. *rpm -qa* List all installed packages, using the default formatting. *rpm -qlv --noartifact glibc* List the files of the *glibc* package in style similar to *ls*(1) command output with *ls -l*, omitting "artifact" files such as build-id's which are not usually the main interest. *rpm -q --qf "[%{filenames} %{filedigests}\\n]" openssh-server* List filenames and their corresponding digests of installed *openssh-server* package. *rpm -qp --scripts --triggers --filetriggers myserver-1.0-1.x86_64.rpm* Show all scriptlets and triggers from the _myserver-1.0-1.x86_64.rpm_ package file. Before installing unknown packages, at least do this! *rpm --target ppc64le --eval "%optflags"* Print the expansion of the *%optflags* macro for the *ppc64le* architecture. # FILES See *rpm-common*(8), *rpm-config*(5) and *rpm-rpmrc*(5). # SEE ALSO *rpm-common*(8), *popt*(3), *rpm2cpio*(1), *rpmbuild*(1), *rpmdb*(8), *rpmkeys*(8), *rpmsign*(1), *rpmspec*(1), *rpm-queryformat*(7) *rpm-manifest*(5) *rpm-version*(7) *rpm-plugins*(8) *rpm --help* - as *rpm* supports customizing the options via popt aliases it's impossible to guarantee that what's described in the manual matches what's available. *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpm2archive.1.scd000066400000000000000000000041471511627505500242530ustar00rootroot00000000000000RPM2ARCHIVE(1) # NAME rpm2archive - Create tar or cpio archive from RPM Package Manager (RPM) package # SYNOPSIS *rpm2archive* [options] [_PACKAGE_FILE_] ... # DESCRIPTION *rpm2archive* converts RPM package files to other archive formats. If the standard output is a regular file or a pipe, the archive is written to the standard output. If the standard output is a terminal, the output is written to a file by the same name, appended with a _.tgz_ or _.cpio.gz_ suffix when compressed or _.tar_ or _.cpio_ otherwise depending on the format. The output is compressed in the *gzip*(1) format by default. *rpm2archive* does not verify package-level signatures or checksums, but it does verify the per-file checksums. Supports RPM package formats 3, 4 and 6. # ARGUMENTS _PACKAGE_FILE_ A binary or source RPM package. If no arguments are present, or a dash (*-*) is given as an argument, data is read from the standard input. # OPTIONS *-n*, *--nocompression* Generate an uncompressed archive. If in the file creation mode, use _.tar_ as postfix of the filename. *-f*, *--format*=<_FORMAT_> Generate archive in specified _FORMAT_: - *pax*: Restricted Portable Archive Exchange as used by *tar*(1) (default) - *cpio*: The "new" ASCII *cpio*(5) format. Note that this format has a 4GB limit on the individual file size. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpm2archive glint-1.0-1.i386.rpm | tar -xvz* Extract _glint-1.0-1.i386.rpm_ package contents with *tar*(1). *rpm2archive --nocompression --format=cpio glint-1.0-1.i386.rpm | cpio -idv* Extract _glint-1.0-1.i386.rpm_ package contents as an uncompressed *cpio*(5), compatible with *rpm2cpio*(1) output. *rpm2archive glint-1.0-1.i386.rpm ; tar -xvz glint-1.0-1.i386.rpm.tgz* Extract _glint-1.0-1.i386.rpm_ package contents into an archive named _glint-1.0-1.i386.rpm.tgz_ and extract the written archive with *tar*(1). *cat glint-1.0-1.i386.rpm | rpm2archive - | tar -tvz* Feed _glint-1.0-1.i386.rpm_ package into *rpm2archive* via standard input, and list contents with *tar*(1). # SEE ALSO *rpm2cpio*(1), *rpm*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpm2cpio.1.scd000066400000000000000000000016111511627505500235550ustar00rootroot00000000000000RPM2CPIO(1) # NAME rpm2cpio - Extract cpio archive from RPM Package Manager (RPM) package # SYNOPSIS *rpm2cpio* _PACKAGE_FILE_ *rpm2cpio -* # DESCRIPTION *rpm2cpio* converts the specified *rpm* package to a *cpio*(1) archive on the standard output. *Note:* the *cpio*(5) format cannot host individual files over 4GB in size, and so this tool is considered obsolete. Use *rpm2archive*(1) instead. # ARGUMENTS _PACKAGE_FILE_ A binary or source RPM package. If dash (*-*) is given as the argument, data is read from the standard input. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpm2cpio glint-1.0-1.i386.rpm | cpio -dium* Extract _glint-1.0-1.i386.rpm_ contents relative to current directory. *cat glint-1.0-1.i386.rpm | rpm2cpio - | cpio -tv* View _glint-1.0-1.i386.rpm_ file list with *cpio*(1). # SEE ALSO *cpio*(1), *rpm*(8), *rpm2archive*(1) rpm-software-management-rpm-3c1f23f/docs/man/rpmbuild-config.5.scd000066400000000000000000000261221511627505500251130ustar00rootroot00000000000000RPMBUILD-CONFIG(5) # NAME *rpmbuild-config* - rpmbuild macro configuration # SYNOPSIS _NAME_ _VALUE_ # DESCRIPTION The primary configuration mechanism in *rpmbuild* is *rpm-macros*(7). See *rpm-config*(5) for the description of the general mechanism, this manual only describes the configurables affecting *rpmbuild* operation. # CONFIGURATION The following configurables are supported for the *rpm* build operation, typically invoked through the *rpmbuild* command. ## Locations and filenames *%\_builddir* _DIRECTORY_ The directory where the per-package build directories will be placed when building. *%\_fileattrsdir* _DIRECTORY_ The directory where the file classifier configuration is located. *%\_rpmdir* _DIRECTORY_ The directory where newly built binary packages will be written. *%\_rpmfilename* _TEMPLATE_ The output binary package filename query format used when building binary packages, as described by *rpm-queryformat*(7). The filename is considered relative to *%\_rpmdir*. *%\_sourcedir* _DIRECTORY_ The directory where the sources and patches are found when building. *%\_srcrpmdir* _DIRECTORY_ The directory where newly built source packages will be written. *%\_topdir* _DIRECTORY_ The toplevel directory under which the package build directories reside by default. ## Informational tags These are a special group of macros that if defined, behave as if the corresponding tag had been used in a specfile. This allows changing the values without modifying thousands of specfiles. *%bugurl* _URL_ Configurable bug URL, same as BugURL: tag in a specfile. If set, should point to a website for searching for existing and filing new issues for this package. *%distribution* _STRING_ Configurable distribution informationsame as Distribution: tag in a specfile. *%disturl* _URL_ Configurable distribution URL, same as DistURL: tag in a specfile. *%disttag* _STRING_ Configurable distribution tag, same as DistTag: tag in a specfile. *%modularitylabel* _STRING_ Configurable modularity label, same as ModularityLabel: in a specfile. Obsolete. *%packager* _STRING_ Configurable packager information, same as Packager: in a specfile. *%translationurl* _URL_ Configurable translation URL, same as TranslationURL: in a specfile. *%upstreamreleases* _URL_ Configurable URL for checking for newer upstream releases, same as UpstreamReleases: in a specfile. *%vendor* _STRING_ Configurable vendor information, same as Vendor: in a specfile. ## Build operation These settings affect various aspects of the build and can cause a build to fail or succeed, but have no direct impact on the produced packages. *%\_build_pkgcheck* _EXECUTABLE_ A program to call for each successfully built and written binary package, such as *rpmlint*. The path to the package is passed to the program as a command-line argument. *%\_build_pkgcheck_set* _EXECUTABLE_ A program to call for the whole binary package set after a successfully build, such as *rpmlint*. The paths to the package set are passed to the program as command-line arguments. *%\_build_pkgcheck_srpm* _EXECUTABLE_ A program to call for each successfully built and written source package, such as *rpmlint*. The path to the package is passed to the program as a command-line argument. *%\_default_patch_flags* _STRING_ Set of default options on all *%patch* applications. *%\_default_patch_fuzz* _NUMBER_ Default fuzz level for patch application in spec file. See *patch*(1) for details. *%\_smp_ncpus_max* _NUMBER_ A hard limit for maximum number of CPU's to use in parallel during a package build. Zero means unlimited. *%\_smp_nthreads_max* _NUMBER_ A hard limit for maximum number of threads to use in parallel during a package build. Zero means unlimited. *%\_smp_tasksize_proc* _NUMBER_ Assumed task size of build processes (during compilation). Used for tuning the amount of parallelism based on available memory. *%\_smp_tasksize_thread* _NUMBER_ Assumed task size of threads (during package generation). Used for tuning the amount of parallelism based on available memory. ## Package generation These settings affect various aspects of the produced binary and source packages. *%build_mtime_policy* _POLICY_ Define how file timestamps are handled in built packages. Clamping means ensuring the value does not exceed the threshold value. - *clamp_to_buildtime*: Clamp file timestamps to package buildtime. - *clamp_to_source_date_epoch*: Clamp file timestamps to the *SOURCE_DATE_EPOCH* environment variable. *%clamp_mtime_to_source_date_epoch* _BOOLEAN_ Alias for *%build_mtime_policy* *clamp_to_source_date_epoch*. Do not use. Deprecated and scheduled for removal. *%source_date_epoch_from_changelog* _BOOLEAN_ Whether to set the *SOURCE_DATE_EPOCH* environment variable from the timestamp of the topmost *%changelog* entry. *%use_source_date_epoch_as_buildtime* _BOOLEAN_ Whether to use the *SOURCE_DATE_EPOCH* environment variable value as the buildtime of packages. *%\_binary_filedigest_algorithm* _HASHALGO_ The algorithm to use for generating per-file checksums (aka digests) of binary packages. See *rpm-config*(5) for the supported values. *%\_binary_payload* _IOFLAGS_ The IO method and compression to use for generating the payload of binary packages. See *rpm-payloadflags*(7). *%\_buildhost* _HOSTNAME_ Use _HOSTNAME_ as the package buildhost instead of acquiring the value from *gethostname*(2). *%\_buildtime* _TIMESTAMP_ Use _TIMESTAMP_ as the package buildtime instead of acquiring the value from *time*(2). *%\_changelog_trimage* _TIMESTAMP_ Maximum age of preserved changelog entries in binary packages, relative to newest existing entry. Unix timestamp format. The value of zero disables trimming by age. *%\_changelog_trimtime* _TIMESTAMP_ An alternative strategy for changelog trimming: trim all changelog entries older than the specified timestamp. Unix timestamp format. The value of zero disables trimming by time. *%\_docdir_fmt* _TEMPLATE_ A template for the per-package documentation directory name, ie. the files packaged with *%doc*. *%\_\_docdir_path* _PATH_ A colon separated list of directories whose contents should be always considered as documentation. *%\_openpgp_autosign_id* _KEYID_ The OpenPGP key id or fingerprint to use for automatically signing packages after a successful build. See also *rpmsign*(1). *%\_rpmformat* _VERSION_ The RPM package format to produce. Supported values are: - *4*: RPM v4 format - *6*: RPM v6 format *%\_source_filedigest_algorithm* _HASHALGO_ The algorithm to use for generating per-file checksums (aka digests) of source packages. See *rpm-config*(5) for the supported values. *%\_source_payload* The IO method and compression to use for generating the payload of source packages. See *rpm-payloadflags*(7). *%\_\_gpg_reserved_space* _NUMBER_ The number of bytes to reserve for signatures in the signature header. This reserve helps to speed up package signing significantly. ## Debuginfo generation Debuginfo sub-packages contain information necessary to debug otherwise release-specification builds with a debugger. These settings determine whether debuginfo packages are generated and various aspects of the generated debugging information. *%\_build_id_links* _MODE_ Defines how and if build_id links are generated for ELF files. The following settings are supported: - *none*: No build_id links are generated. - *alldebug*: Generate rpm < 4.14 style build_id links, ie everything in the -debuginfo package. - *separate*: Generate build_id links in binary packages. - *compat*: Same as *separate* but with a compatibility link(s) in the -debuginfo packages. *%\_debuginfo_subpackages* _BOOLEAN_ Whether rpm should create separate debuginfo packages for each subpackage. *%\_debugsource_packages* _BOOLEAN_ Whether rpm should put debug source files into their own subpackage. *%\_enable_debug_packages* _BOOLEAN_ Whether rpm should generate debuginfo subpackages. *%\_include_gdb_index* _BOOLEAN_ Include a .gdb_index section in the .debug files. Requires *%\_enable_debug_packages* to be enabled and *gdb-add-index*(1) to be installed. *%\_include_minidebuginfo* _BOOLEAN_ Include minimal debug information in build binaries. Requires *%\_enable_debug_packages* to be enabled. *%\_no_recompute_build_ids* _BOOLEAN_ Disable recomputation of build-ids. Cannot be used with *%\_unique_build_ids*. *%\_unique_build_ids* _BOOLEAN_ Whether build-ids should be made unique between package version/releases when generating debuginfo packages. *%\_unique_debug_names* _BOOLEAN_ Whether .debug files should be made unique between package version, release and architecture. Requires *%\_unique_build_ids* to be enabled. *%\_unique_debug_srcs* _BOOLEAN_ Whether the /usr/debug/src/ directories should be unique between package version, release and architecture. *%\_\_find_debuginfo* _EXECUTABLE_ The location of the debuginfo helper executable. ## Legacy compatibility These settings deal with several decades of backwards compatibility. Most of them determine whether a particular issue is considered an error or just a warning. Sometimes also useful for temporarily working around issues while packaging. *%\_binaries_in_noarch_packages_terminate_build* _BOOLEAN_ Whether ELF binaries in noarch packages should terminate a build. *%\_duplicate_files_terminate_build* _BOOLEAN_ Whether duplicate files in *%files* section should terminate a build. *%\_empty_manifest_terminate_build* _BOOLEAN_ Whether an empty *%files* manifest file should terminate a build. *%\_invalid_encoding_terminates_build* _BOOLEAN_ Whether a non-UTF8 encoding in package data should terminate a build. *%\_\_find_provides* _EXECUTABLE_ The executable to use for rpm 3.x style provides generation. Do not use. Deprecated and scheduled for removal. *%\_\_find_requires* _EXECUTABLE_ The executable to use for rpm 3.x style requires generation. Do not use. Deprecated and scheduled for removal. *%\_missing_build_ids_terminate_build* _BOOLEAN_ Whether ELF files without build-ids should terminate a build. For historical reasons, this is only applicable if *%\_enable_debug_packages* is also enabled. *%\_missing_doc_files_terminate_build* _BOOLEAN_ Whether missing %doc files in the build directory should terminate a build. *%\_unpackaged_files_terminate_build* _BOOLEAN_ Whether unpackaged files in a build root should terminate a build. *%\_nonzero_exit_pkgcheck_terminate_build* _BOOLEAN_ Whether the build of packages should fail if package checker (if defined) returns an error? See the *%\_build_pkgcheck_\** macros. *%\_use_internal_dependency_generator* _BOOLEAN_ If enabled, use rpm 3.x style dependency generation. Do not use. Deprecated and scheduled for removal. *%\_use_weak_usergroup_deps* _BOOLEAN_ If enabled, dilute user() and group() requires into recommends. Useful when transitioning to sysusers.d based user/group management. *%\_wrong_version_format_terminate_build* _BOOLEAN_ Whether invalid version format in dependencies etc should terminate a build. # SEE ALSO *rpmbuild*(1), *rpm-common*(8), *rpm-macrofile*(5), *rpm-rpmrc*(5), *rpm-config*(5), *rpm-payloadflags*(7) *rpm-macros*(7) rpm-software-management-rpm-3c1f23f/docs/man/rpmbuild.1.scd000066400000000000000000000210251511627505500236410ustar00rootroot00000000000000RPMBUILD(1) # NAME rpmbuild - Build RPM Package(s) # SYNOPSIS *rpmbuild* *-b*​_STAGE_ [options] _SPEC_FILE_ ... *rpmbuild* *-r*​_STAGE_ [options] _SOURCE_PACKAGE_ ... *rpmbuild* *-t*​_STAGE_ [options] _TAR_ARCHIVE_ ... *rpmbuild* {*--rebuild*|*--recompile*} [options] _SOURCE_PACKAGE_ ... # DESCRIPTION *rpmbuild* is used to build software packages in the RPM format, in an automated and repeatable manner. A _package_ consists of an archive of files and meta-data used to install and erase the archive files. The meta-data includes helper scripts, file attributes, and descriptive information about the package. Packages come in two varieties: binary packages, used to encapsulate software to be installed, and source packages, containing the source code and recipe necessary to produce binary packages. ; scdoc doesn't currently render underline adjacent to bolding, so we need ; a little unicode hack to put something invisible between the operation ; letter and STAGE: the U200b character works nicely for the purpose. ; vim shows this as <200b> with other editors, YMMV. # OPERATIONS *-b*​_STAGE_ Build _STAGE_ from a spec file. *-r*​_STAGE_ Build _STAGE_ from a source RPM package. *-t*​_STAGE_ Build _STAGE_ from a *tar*(5) archive. *--rebuild*, *--recompile* Compatibility aliases for *-ra*. Packages are built in four phases: parse, build, assembly and cleanup. The middle two are further divided into _stages_, listed below, which can be built separately. Building a _STAGE_ means executing all the preceding stages up to (and including) the one specified, unless stated otherwise. ## Assembly stages The assembly stages produce packages and are the primary way of interacting with *rpmbuild*. If in doubt, choose one of these. *a* Build both source and binary packages. This is not an actual stage but a combination of *b* and *s*, with the difference that packages built this way carry a "cookie" to indicate they came from the same build. On success, the build directory is removed. *b* Build just the binary packages. On success, the build directory is removed. *r* Build just the source package, checking for dynamic build dependencies. Executes the *%prep* and *%generate_buildrequires* stages before creating a package. See the *DYNAMIC BUILD DEPENDENCIES* section for details. *s* Build just the source package. No build stages are executed. ## Build stages The build stages produce the artifacts to be packaged, typically by patching and compiling the sources, and installing the binaries into the buildroot. These stages generally correspond to spec sections such as *%prep*, *%build* or *%install*, but there are some which are implicit. Building these stages separately is generally only useful when packaging new software in RPM format and/or troubleshooting. They are listed below in the order of execution, with the corresponding spec section in parenthesis where applicable. *p* (%prep) Unpack the sources and apply any patches. *d* (%generate_buildrequires) Check dynamic build dependencies and build the _buildreqs.nosrc.rpm_ package if any are missing. Don't build anything else. *f* (%conf) Configure the sources. This generally involves the equivalent of *./configure*. *c* (%build) Compile the sources. This generally involves the equivalent of *make*. *i* (%install and %check) Install the binaries into the build root. This generally involves the equivalent of a *make install* and *make check*. *l* Do a "list check" - the *%files* section from the spec file is macro expanded, and checks are made to verify that each file exists. This requires a previous build up to the *%install* stage to have taken place. # ARGUMENTS _SPEC_FILE_ An RPM spec file. _SOURCE_PACKAGE_ An RPM source package (with a _.src.rpm_ extension) _TAR_ARCHIVE_ A *tar*(5) archive, optionally compressed. To be directly buildable with *rpmbuild*, an archive must contain a spec file either by the name _Specfile_ or one with a _.spec_ extension. # OPTIONS *--build-in-place* Build from locally checked out sources in the current working directory. The build tree is set up as if *%setup* was used, but *%builddir*/*%buildsubdir* points back to the current working directory. *%prep* is skipped entirely. *--clean* Remove the build tree after the packages are made (default). *--nobuild* Do not execute any build stages. Useful for testing out spec files. *--nocheck* Do not execute *%check* build stage even if present in spec. *--noclean* Do not execute *%clean* build stage even if present in spec. *--nodebuginfo* Do not generate debuginfo packages. *--nodeps* Do not verify build dependencies. *--noprep* Do not execute *%prep* build stage even if present in spec. This assumes there has been another *rpmbuild* run in which *%prep* has been already executed. *--rmsource* Remove the sources after the build (may also be used standalone, e.g. *rpmbuild* *--rmsource foo.spec*). Note that by definition, *NoSource* and *NoPatch* files are not sources and so, are not affected by this option. *--rmspec* Remove the spec file after the build (may also be used standalone, e.g. *rpmbuild* *--rmspec foo.spec*). *--rpmfcdebug* Print debug information on file classification and dependency generation. *--scm*=_SCM_ Select the _SCM_ to use with *%autosetup*, if one is not set in the spec file. Note that not all values for _SCM_, e.g., *patch* (the default) and *gendiff*, *git*, or *quilt* work interchangeably with all other patches and options stated in the %autosetup line, especially option *-p**N*. *--short-circuit* Skip straight to specified stage (i.e., skip all stages leading up to the specified stage). Only valid with *-bc*, *-bi*, and *-bb*. Useful for local testing only. Packages built this way will be marked with an unsatisfiable dependency to prevent their accidental use. *--with* _OPTION_ Enable configure _OPTION_ for build. *--without* _OPTION_ Disable configure _OPTION_ for build. See *rpm-common*(8) for the options common to all operations. # DYNAMIC BUILD DEPENDENCIES When the *%generate_buildrequires* stage runs and some of the newly generated BuildRequires are not satisfied, *rpmbuild* creates an intermediate source package ending in _buildreqs.nosrc.rpm_, which has the new BuildRequires, and exits with code 11. This package can then be used in place of the original source package to resolve and install the missing build dependencies in the usual way, such as with *dnf-builddep*(8). Multiple layers of dynamic build dependencies may exist in a spec file; the presence of specific BuildRequires on the system may yield new BuildRequires next time a build is performed with the same source package. The easiest way to ensure that all dynamic build dependencies are satisfied is to run the *-br* option, install the new dependencies of the _buildreqs.nosrc.rpm_ package and repeat the whole procedure until *rpmbuild* no longer exits with code 11. If the *-br* option is coupled with *--nodeps*, exit code 11 is always returned and a _buildreqs.nosrc.rpm_ package is always created. # ENVIRONMENT *RPM_BUILD_NCPUS* Overrides autodetection for the number of CPUs to use for parallelized sections of the build. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. Packages with a *%generate_buildrequires* section will return with code 11 when there are unmet *DYNAMIC BUILD DEPENDENCIES*. # EXAMPLES *rpmbuild --rebuild hello-1.0-1.src.rpm* Build binary and source packages from the _hello-1.0-1.src.rpm_ source package. *rpmbuild -bb --nocheck --with openssl hello.spec* Build binary packages from the _hello.spec_ spec file, skipping the *%check* stage if present and enabling support for *openssl* build conditional (assuming one is specified in the spec). *rpmbuild -ta hello-2.0.tar.gz* Build binary and source packages from the compressed _hello-2.0.tar.gz_ tarball, assuming the archive contains a legitimate spec file (see _TAR_ARCHIVE_ for details). *rpmbuild -bc hello.spec* Build _hello.spec_ up to and including the *%build* stage, ie. without producing actual packages. *rpmbuild -bi --short-circuit hello.spec* Only execute the *%install* stage of _hello.spec_, skipping all previous stages. This assumes a preceding run of at least up to the *%build* stage, see previous example. Useful to avoid full rebuilds when working on the *%files* section of a package. # FILES See *rpm-common*(8) # SEE ALSO *gendiff*(1), *popt*(3), *rpm*(8), *rpm-common*(8), *rpmbuild-config*(5), *rpm2cpio*(1), *rpmkeys*(8), *rpmspec*(1), *rpmsign*(1), *rpm-setup-autosign*(1) *rpm-macros*(7) *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpmdb.8.scd000066400000000000000000000043411511627505500231400ustar00rootroot00000000000000RPMDB(8) # NAME rpmdb - RPM Database Tool # SYNOPSIS *rpmdb* [options] {*--initdb*|*--rebuilddb*} *rpmdb* [options] {*--verifydb*} *rpmdb* [options] {*--exportdb*|*--importdb*} # DESCRIPTION The *rpmdb* is used for *rpm* database maintenance operations. # OPERATIONS *--initdb* Create a new database if one doesn't already exist. An existing database is not overwritten. *--rebuilddb* Rebuild database from the installed package headers. Rebuilding discards any unreadable (corrupt) headers from the database, but also compacts the database in case it has grown large over time. Can also be used to convert between different *rpmdb* formats. *--verifydb* Perform a low-level integrity check on the database. Rarely useful, for system health *rpm --verify -a* is a far more meaningful operation. *--exportdb* Export the database in header-list format, suitable for transporting to another host or database type. The data is output to standard output. *--importdb* Imports a database from a header-list format as created by *--exportdb*. The data is read from standard input. # OPTIONS See *rpm-common*(8) for the options common to all *rpm* executables. # CONFIGURATION There are several configurables affecting the rpmdb operations, but in particular the following (see *rpm-config*(5) for details): - *%\_dbpath* - *%\_db_backend* # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpmdb --initdb --dbpath /tmp/testdb* Initialize a new database in _/tmp/testdb_ directory. *rpmdb --rebuilddb* Rebuild the system rpmdb. *rpmdb --verifydb --root /mnt* Verify the system database of a system image mounted at _/mnt_. *rpmdb --exportdb > /tmp/headers* Export the system database to _/tmp/headers_ file. *rpmdb --importdb --define "\_db\_backend ndb" --dbpath /tmp/newdb < /tmp/headers* Import contents of _/tmp/headers_ header list to a (new) *ndb*-format database in _/tmp/newdb_. # OPTIONS See *rpm-common*(8) for the options common to all operations. # SEE ALSO *popt*(3), *rpm*(8), *rpm-common*(8) *rpmdb --help* - as *rpm*(8) supports customizing the options via popt aliases it's impossible to guarantee that what's described in the manual matches what's available. *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpmdeps.1.scd000066400000000000000000000034771511627505500235100ustar00rootroot00000000000000RPMDEPS(1) # NAME rpmdeps - Generate RPM Package Dependencies # SYNOPSIS *rpmdeps* [options] *rpmdeps* [options] _FILE_ ... # DESCRIPTION *rpmdeps* generates package dependencies for one or more files, received either as a list of filenames on the standard input, or command line arguments. *rpmdeps* is not normally invoked directly, but doing so can be useful for troubleshooting dependency generation and developing new dependency generators. The installation directory of *rpmdeps* can be determined with *rpm -E "%{\_rpmconfigdir}"*. Note that the actual *rpmdeps* command is only used for automatic dependency generation in a legacy compatibility mode of *rpmbuild*(1). # OPERATIONS *--alldeps* Print all the dependencies. *--conflicts* Print the conflicts dependencies. *--enhances* Print the enhances dependencies. *--obsoletes* Print the obsoletes dependencies. *--orderwithrequires* Print the orderwithrequires dependencies. *-P, --provides* Print the provides dependencies. *--recommends* Print the recommends dependencies. *-R, --requires* Print the requires dependencies. *--suggests* Print the suggests dependencies. *--supplements* Print the supplements dependencies. # ARGUMENTS _FILE_ Any regular file. # OPTIONS See *rpm-common*(8) for the options common to all operations. # ENVIRONMENT *RPM_BUILD_ROOT* The prefix path to strip out when generating dependencies based on actual paths. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpmdeps --requires /bin/ls* Output the automatic requires dependencies of the *ls*(1) binary. *find popt-1.19-build/BUILDROOT -type f | /usr/lib/rpm/rpmdeps -P* Output the automatic provides of all files in the _popt-1.19-build/BUILDROOT_ directory # SEE ALSO *rpm*(8), *rpmbuild*(1), *rpm-common*(8) rpm-software-management-rpm-3c1f23f/docs/man/rpmgraph.1.scd000066400000000000000000000020511511627505500236410ustar00rootroot00000000000000RPMGRAPH(1) # NAME rpmgraph - Generate an RPM Package Dependency Graph # SYNOPSIS *rpmgraph* [options] _PACKAGE_FILE_ ... # DESCRIPTION *rpmgraph* reads *rpm* packages passed as arguments, sorts them as for installation with *rpm*(8), and produces a dependency graph on the standard output. Nodes in the dependency graph are package names, and edges in the directed graph point to the parent of each node. The parent node is defined as the last predecessor of a package when partially ordered using the package dependencies as a relation. That means that the parent of a given package is the package's last prerequisite. The output is in *dot*(1) directed graph format, and can be displayed or printed using the *dotty*(1) graph editor from the *Graphviz* project. # ARGUMENTS _PACKAGE_FILE_ Either an *rpm* package or an *rpm-manifest*(5) file. # OPTIONS See *rpm-common*(8) for the options common to all rpm commands. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # SEE ALSO *dot*(1), *dotty*(1) *http://www.graphviz.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpmkeys.8.scd000066400000000000000000000060121511627505500235230ustar00rootroot00000000000000RPMKEYS(8) # NAME rpmkeys - RPM Keyring # SYNOPSIS *rpmkeys* {*-K*|*--checksig*} [options] _PACKAGE_FILE_ ... *rpmkeys* {*-d*|*--delete*|*-e*|*--erase*} [options] _FINGERPRINT_ ... *rpmkeys* {*-x*|*--export*} [options] [_FINGERPRINT_ ...] *rpmkeys* {*-i*|*--import*} [options] _PUBKEY_ ... *rpmkeys* {*-l*|*--list*} [options] [_FINGERPRINT_ ...] *rpmkeys* *--rebuild* [options] [rebuild-options] # DESCRIPTION *rpmkeys* is used for manipulating the *rpm* keyring and verifying package digital signatures with the contained keys. For all available operations, see *OPERATIONS*. # OPERATIONS *-K*, *--checksig* Verify the digests and signatures contained in _PACKAGE_FILE_ to ensure the integrity and origin of the package. *-d*, *--delete*, *-e*, *--erase* Erase the key(s) designated by _FINGERPRINT_. The *--delete* and *-d* options are deprecated. *-x*, *--export* Output the key(s) designated by _FINGERPRINT_ using an ASCII-armor encoding. If _FINGERPRINT_ is not specified, output all keys. *--import* Import ASCII-armored public keys. Digital signatures cannot be verified without the corresponding public key (aka certificate). *-l*, *--list* List currently imported public key(s) (aka certificates) by their fingerprint and user ID. If no fingerprints are specified, list all keys. *--rebuild* Recreate the public key storage. Update to the latest format and drop unreadable keys. # ARGUMENTS _FINGERPRINT_ The handle used for all operations on the keys. _PACKAGE_FILE_ An *rpm* package file or a manifest. _PUBKEY_ An ASCII-armored OpenPGP public key (aka certificate). # OPTIONS See *rpm-common*(8) for the options common to all *rpm* executables. # REBUILD OPTIONS *--from* <*fs*|*openpgp*|*rpmdb*> Use the keys from the specified backend to rebuild the currently configured keystore backend. This can be used to convert from one key storage to another. # OUTPUT *--checksig* ``` <_PACKAGE_FILE_>: ``` With *--verbose*: ``` <_PACKAGE_FILE_>: : ... ``` *--list* ``` public key ``` # CONFIGURATION There are several configurables affecting the behavior of this verification, see *rpm-config*(5) for details: - *%\_keyring* - *%\_keyringpath* - *%\_pkgverify_flags* - *%\_pkgverify_level* # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpmkeys --export 771b18d3d7baa28734333c424344591e1964c5fc | sq inspect* Export key 771b18d3d7baa28734333c424344591e1964c5fc for inspecting with sequoia-sq. *rpmkeys --erase 771b18d3d7baa28734333c424344591e1964c5fc* Erase key 771b18d3d7baa28734333c424344591e1964c5fc from the keyring. *rpmkeys -K hello-2.0-1.x86_64.rpm* Verify hello-2.0-1.x86_64.rpm package file. # SEE ALSO *popt*(3), *rpm*(8), *rpm-common*(8), *rpm-config*(5), *rpmsign*(1) *rpmkeys --help* - as *rpm*(8) supports customizing the options via popt aliases it's impossible to guarantee that what's described in the manual matches what's available. *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpmlua.1.scd000066400000000000000000000022661511627505500233310ustar00rootroot00000000000000RPMLUA(1) # NAME rpmlua - RPM Lua interpreter # SYNOPSIS *rpmlua* [options] [_LUA_SCRIPT_] [-- _ARG_ ...] # DESCRIPTION Run RPM internal Lua interpreter. Note: indexes start at 1 in Lua, so the program name is at arg[1] instead of the more customary index zero. # ARGUMENTS _LUA SCRIPT_ A Lua script. _ARG_ Options and arguments to be passed to _SCRIPT_FILE_. *rpmlua* stops processing at *--*. # OPTIONS *-i*, *--interactive* Run an interactive session after executing optional script or statement. *--opts*=_OPTSTRING_ Perform *getopt*(3) option processing on the passed arguments according to _OPTSTRING_. *-e* "_STATEMENT_", *--execute* "_STATEMENT_" Execute a Lua statement before executing optional script. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpmlua test.lua* Execute test.lua script file. *rpmlua --opts=ab:c args.lua -- 1 2 3 -c -b5* Execute args.lua script file with option processing. *rpmlua -e "print(rpm.ver('1.0') < rpm.ver('2.0'))"* Execute single statement to compare rpm versions. *rpmlua -i* Run an interactive session. # SEE ALSO *lua*(1), *popt*(3), *getopt*(3), *rpm*(8) *rpm-lua*(7) *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpmsign.1.scd000066400000000000000000000126341511627505500235100ustar00rootroot00000000000000RPMSIGN(1) # NAME rpmsign - RPM Package Signing # SYNOPSIS *rpmsign* {*--addsign*|*--resign*} [options] [sign-options] _PACKAGE_FILE_ ... *rpmsign* *--delsign* [options] _PACKAGE_FILE_ ... *rpmsign* *--delfilesign* [options] _PACKAGE_FILE_ ... # DESCRIPTION *rpmsign* is used to manipulate digital OpenPGP signatures on *rpm* package files. To create a signature rpmsign needs to verify the package's checksum. As a result V4 packages with MD5/SHA1 checksums cannot be signed in FIPS mode. # OPERATIONS *--addsign* Generate and insert a new OpenPGP signature for each _PACKAGE_FILE_ given unless a signature with identical parameters already exists, in which case no action is taken. Arbitrary number of V6 signatures can be added. *--resign* Generates and inserts a new OpenPGP signature for each _PACKAGE_FILE_, replacing any and all previous signatures. *--delsign* Delete all OpenPGP signatures from each package _PACKAGE_FILE_ given. *--delfilesign* Delete all IMA and fsverity file signatures from each package _PACKAGE_FILE_ given. # ARGUMENTS _PACKAGE_FILE_ An *rpm* package file. # OPTIONS See *rpm-common*(8) for the options common to all *rpm* executables. # SIGN OPTIONS *--certpath* _CERT_ Used with *--signverity*, use file signing certificate _CERT_. *--fskpath* _KEY_ Used with *--signfiles*, use file signing key _KEY_. *--key-id* _KEYID_ Use key _KEYID_ for signing. Overrides *%\_openpgp_sign_id* configuration. *--rpmv3* Request RPM V3 header+payload signature addition on V4 packages. These signatures are expensive and redundant baggage on packages where a separate payload digest exists (packages built with rpm >= 4.14). Rpmsign will automatically detect the need for V3 signatures, but this option can be used to request their creation if the packages must be fully signature verifiable with rpm < 4.14 or other interoperability reasons. Has no effect when signing V6 packages. *--rpmv4* Request RPM V4 header signature addition on V6 packages. Useful for making V6 packages signature verifiable with rpm 4.x versions. V4 compatibility signatures are only ever added if the signing algorithm is one of those known to V4: RSA, EcDSA, EdDSA (and original DSA). Only one V4 signature can be present in a package, so this is added only on the first *--addsign* with a V4 compatible algorithm, and ignored otherwise. Has no effect when signing V4 packages. *--rpmv6* Request RPM V6 header signature addition on V4 packages. This generally always succeeds as there can be arbitrary number of V6 signatures on a package. A V3/V4 compatibility signatures are added using the same logic as *--rpmv4* on a V6 package. Has no effect when signing V6 packages. *--signfiles* Sign package files. The file signing key (RSA private key) must be set before signing the package, it can be configured on the command line with *--fskpath* or the macro %\_file_signing_key. *--signverity* Sign package files with fsverity signatures. The file signing key (RSA private key) and the signing certificate must be set before signing the package. The key can be configured on the command line with *--fskpath* or the macro %\_file_signing_key, and the cert can be configured on the command line with *--certpath* or the macro %\_file_signing_cert. *--verityalgo* _ALG_ Used with *--signverity*, to specify the signing algorithm. sha256 and sha512 are supported, with sha256 being the default if this argument is not specified. This can also be specified with the macro *%\_verity_algorithm*. # CONFIGURATION In order to sign packages, you need to create your own OpenPGP key pair (aka certificate) and configure *rpm*(8) to use it. The following macros are available: *%\_openpgp_sign_id* The fingerprint or keyid of the signing key to use. Typically this is the only configuration needed. If omitted, *--key-id* must be explicitly specified when signing. *%\_openpgp_sign* The OpenPGP implementation to use for signing. Supported values are "gpg" for GnuPG (default and traditional) and "sq" for Sequoia PGP. Implementation specific macros: *%\_gpg_path* The location of your GnuPG keyring if not the default *$GNUPGHOME*. *%\_gpg_name* Legacy macro for configuring user id with GnuPG. Use the implementation independent and non-ambiguous *%\_openpgp_sign_id* instead. *%\_sq_path* The location of your Sequoia configuration if not the default. # EXAMPLES ## Example 1. Basic setup Configure RPM to sign packages with Sequoia PGP and a specific key by adding the following contents to the user's *rpm-config*(5) file (typically _~/.config/rpm/macros_): ``` %\_openpgp_sign sq %\_openpgp_sign_id 7B36C3EE0CCE86EDBC3EFF2685B274E29F798E08 ``` ## Example 2. Basic operations *rpmsign --addsign hello-2.0-1.x64_rpm* Add a signature to _hello-2.0-1.x64_rpm_ package. *rpmsign --resign --key-id 771b18d3d7baa28734333c424344591e1964c5fc hello-2.0-1.x64_rpm* Replace all signatures in _hello-2.0-1.x64_rpm_ package by a signature using key *771b18d3d7baa28734333c424344591e1964c5fc*. *rpmsign --delsign --delfilesign hello-2.0-1.x64_rpm* Delete all signatures from _hello-2.0-1.x64_rpm_ package. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # SEE ALSO *popt*(3), *rpm*(8), *rpm-common*(8), *rpmkeys*(8), *rpmbuild*(1) *rpmsign --help* - as rpm supports customizing the options via popt aliases it's impossible to guarantee that what's described in the manual matches what's available. *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpmsort.1.scd000066400000000000000000000013131511627505500235270ustar00rootroot00000000000000RPMSORT(1) # NAME rpmsort - Sort input by RPM Package Manager (RPM) versioning # SYNOPSIS *rpmsort* [ _FILE_ ... ] # DESCRIPTION *rpmsort*(1) sorts the content of the input files, and writes a sorted list to standard out - like *sort*(1), but aware of RPM versioning. # ARGUMENTS _FILE_ A text-file containing lines to sort. If _FILE_ is omitted or *-* given, data is read from standard in and written to standard out. # OPTIONS *rpmsort* has no options. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *echo -e 'rpm-4.18.0-3.fc38.x86_64\\nrpm-4.18.0-1.fc38.x86_64' | rpmsort* ``` rpm-4.18.0-1.fc38.x86_64 rpm-4.18.0-3.fc38.x86_64 ``` # SEE ALSO *rpm-version*(7) rpm-software-management-rpm-3c1f23f/docs/man/rpmspec.1.scd000066400000000000000000000064241511627505500235020ustar00rootroot00000000000000RPMSPEC(1) # NAME rpmspec - RPM Spec Tool # SYNOPSIS *rpmspec* {*-q*|*--query*} [options] [query-options] _SPEC_FILE_ ... *rpmspec* {*-P*|*--parse*} [options] _SPEC_FILE_ ... *rpmspec* *--shell* [options] [_SPEC_FILE_ ...] # DESCRIPTION *rpmspec* is a tool for querying a spec file. More specifically for querying hypothetical packages which would be created from the given spec file. So querying a spec file with *rpmspec* is similar to querying a package built from that spec file. But it is not identical. With *rpmspec* you can't query all fields which you can query from a built package. E. g. you can't query BUILDTIME with *rpmspec* for obvious reasons. You also cannot query other fields automatically generated during a build of a package like auto generated dependencies. # OPERATIONS *-q*, *--query* Query parsed spec header, similarly to querying packages. *-P*, *--parse* Output parsed spec file to the standard output. That is, conditionals are handled, macros parsed and so on. *--shell* Invoke an interactive shell for inspecting macros, optionally after parsing a spec. Mainly useful for troubleshooting. # ARGUMENTS _SPECFILE_ RPM spec file used for building packages with rpmbuild. # OPTIONS See *rpm-common*(8) for the options common to all operations. # QUERY OPTIONS *--qf* _QUERYFMT_, *--queryformat* _QUERYFMT_ Specify output format for spec queries. See *rpm-queryformat*(7) for details. *--rpms* Operate on the all binary package headers generated from spec. *--builtrpms* Operate only on the binary package headers of packages which would be built from spec. That means ignoring package headers of packages that won't be built from spec i. e. ignoring package headers of packages without file section. *--srpm* Operate on the source package header(s) generated from spec. # ENVIRONMENT See *rpm-common*(8). # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpmspec -q rpm.spec* Get list of binary packages which would be generated from the rpm spec file, eg: ``` rpm-4.11.3-3.fc20.x86_64 rpm-libs-4.11.3-3.fc20.x86_64 rpm-build-libs-4.11.3-3.fc20.x86_64 ... ``` *rpmspec -q --qf "%{name}: %{summary}n" rpm.spec* Get summary infos for single binary packages generated from the rpm spec file, eg: ``` rpm: The RPM package management system rpm-libs: Libraries for manipulating RPM packages rpm-build-libs: Libraries for building and signing RPM packages ... ``` *rpmspec -q --srpm rpm.spec* Get the source package which would be generated from the rpm spec file, eg: ``` rpm-4.11.3-3.fc20.x86_64 ``` *rpmspec -P rpm.spec* Parse the rpm spec file to standard output, eg: ``` Summary: The RPM package management system Name: rpm Version: 4.14.0 ... ``` *rpmspec --shell* Run interactive macro shell for debugging macros, eg: ``` > %define foo bar > %foo bar > %(date) Tue Apr 13 03:55:37 PM EEST 2021 > %getncpus 8 ``` *rpmspec --shell popt.spec* Run interactive macros shell in spec context, eg: ``` %name popt %version 1.18 ``` # SEE ALSO *popt*(3), *rpm*(8), *rpmbuild*(1), *rpm-queryformat*(7), *rpm-macros*(7) *rpmspec --help* - as rpm supports customizing the options via popt aliases it's impossible to guarantee that what's described in the manual matches what's available. *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/man/rpmuncompress.1.scd000066400000000000000000000035751511627505500247520ustar00rootroot00000000000000RPMUNCOMPRESS(1) # NAME *rpmuncompress* - RPM utility for compressed files and archives # SYNOPSIS *rpmuncompress* [options] _FILE_ *rpmuncompress* [options] [extract-options] {*-x*|*--extract*} _ARCHIVE_ # DESCRIPTION *rpmuncompress* is a utility for transparently outputting compressed and uncompressed files, and extracting archives. It's normally invoked internally by *rpmbuild*(1) to handle *%setup* and *%patch* related tasks in spec files, but can be useful manually invoked as well. The installation directory of *rpmuncompress* can be determined with *rpm -E "%{\_rpmconfigdir}"*. # ARGUMENTS _FILE_ A regular file, optionally compressed. _ARCHIVE_ A *tar*(1), *zip*(1) or Ruby GEM archive. *tar* archives may be compressed. # OPERATIONS By default, *rpmuncompress* outputs _FILE_ to standard output similar to *cat*(1), but transparently decompressing many popular compression formats, similar to *zcat*(1). *-x*, *--extract* Extract the _ARCHIVE_ passed as argument into the current directory, transparently uncompressing *tar*(1) archives if needed. # OPTIONS *-n*, *--dry-run* Output the shell command that would be executed without this option. # EXTRACT OPTIONS *-C* _PATH_, *--path*=_PATH_ Extract into to _PATH_ regardless of the archive's internal layout. *-v*, *--verbose* Provide verbose extraction output. # EXIT STATUS On success, 0 is returned, a nonzero failure code otherwise. # EXAMPLES *rpmuncompress /tmp/3911.patch.gz | patch -p1* Uncompress and output _/tmp/3911.patch.gz_, similar to *zcat*(1), and pipe to *patch*(1). *rpmuncompress -x myproject.zip* Extract _myproject.zip_ archive into the current directory. *rpmuncompress -x -C myproject myproject-1.0.7rc1.tar.gz* Uncompress and extract _myproject-1.0.7rc1.tar.gz_ archive into _myproject_ directory, regardless of it's internal structure. # SEE ALSO *rpmbuild*(1), *popt*(3) *http://www.rpm.org/* rpm-software-management-rpm-3c1f23f/docs/manual/000077500000000000000000000000001511627505500216735ustar00rootroot00000000000000rpm-software-management-rpm-3c1f23f/docs/manual/about.md000066400000000000000000000025611511627505500233330ustar00rootroot00000000000000--- layout: default title: rpm.org - About the Reference Manual --- # About this manual This is the Reference Manual for the RPM Package Manager. Unfortunately it is still incomplete. Recently added features are described with decent detail but more basic properties are only being added step by step. While this manual describes how rpm and rpmbuild are working it does not teach how to use them properly - especially when it comes to packaging. This is similar to how a reference manual for a programming language does not teach you programming or good coding style. Though the RPM Reference Manual needs to be used in conjunction with Packaging Guidelines from the distribution the packages are aimed for. For enterprise distributions this may mean looking at the guidelines from the upstream (community) distribution. If you are not packaging for a distribution or one that doesn't have guideline choose one from a distribution that is similar to your target environment. This manual describes RPM as it is setup in the upstream release. Many distribution alter RPM in various ways. Either by changing macro values or by disabling - or forbidding the use of - some features. This is especially true for new features that may require some effort to enable properly throughout a distribution. The distributions decisions overwrite the upstream defaults and though this reference manual. rpm-software-management-rpm-3c1f23f/docs/manual/arch_dependencies.md000066400000000000000000000124451511627505500256460ustar00rootroot00000000000000--- layout: default title: rpm.org - Architecture-specific Dependencies --- # Architecture-specific Dependencies On multiarch systems such as x86_64 it would be often desirable to express that a package of compatible architecture is needed to satisfy a dependency. In most of the cases this is already handled by the automatically extracted soname dependencies, but this is not always the case: sometimes it's necessary to disable the automatic dependency generation, and then there are cases where the information cannot be automatically generated, such as -devel package dependencies on other -devel packages and build dependencies. Consider the following: ``` Name: foo ... BuildRequires: libbar-devel >= 2.2 %package devel Requires: libbar-devel >= 2.2 ... ``` This works fine on single-arch systems such as i386, but it's not sufficient on multiarch systems: when building a 32bit package on a 64bit system, a 32bit version of the libbar-devel would be needed, but the above lets libbar-devel.x86_64 satisfy the build dependency too, leading to obscure build failure. Similarly a 32bit libbar-devel would incorrectly satisfy the dependency for a 64bit package. ## ISA Dependencies In rpm 4.6.0, the concept of ISA (Instruction Set Architecture) was introduced to permit differentiating between 32- and 64-bit versions without resorting to file dependencies on obscure and/or library-version dependent paths. To declare a dependency on a package name architecture specific, append %{?_isa} to the dependency name, eg ``` Requires: libbar-devel%{?_isa} >= 2.2 ``` This will expand to libbar-devel(archfamily-bitness) depending on the build target architecture, for example a native build on x86_64 would give ``` Requires: libbar-devel(x86-64) >= 2.2 ``` but with --target i386 (or i586, i686 etc) it would become ``` Requires: libbar-devel(x86-32) >= 2.2 ``` Note that this requires all the involved packages must have been built with rpm >= 4.6.0, older versions do not add the necessary name(isa) provides to packages. ## Anatomy of ISA macros The ISA of a system in rpm consists of two parts: the generic architecture family, and the bitness of the architecture. These are declared in the platform specific macro files: %{!__isa_name} holds the architecture family and %{!__isa_bits} is the bitness. The %{_isa} macro is just a convenience wrapper to format this to "(arch-bits)", and using the conditional format %{?_isa} allows for backwards compatible use in spec files. Besides the common-case use of just "Requires: foo%{_isa} >= 1.2.3", this two-part scheme permits some rare special cases such as 64bit package also requiring 32bit version of the same architecture family: ``` Requires: foo%{_isa} %if %{__isa_bits} == 64 Requires: foo(%{__isa_name}-32) %endif ``` Note that there are systems with 64bit ISA which are not multiarch, so simply testing for %{!__isa_bits} in the above example is not correct in all cases. ## Rationale The ISA-scheme is not what people typically think of when speaking of architecture specific dependencies, instead the general expectation is something like: ``` Requires: %{name}.%{_target_cpu} ``` While this would appear to be an obvious choice, it's not as useful as it initially seems: many architecture families have several "sub-architectures" (such as i386, i586 and i686) and it's sometimes desirable to offer more than one version of a package, optimized for a different architecture. For example, a package might come in i386 and i686 flavors. You'll want the best version for a given system, but if some other package had hardwired the literal %{_target_cpu} in a dependency, this could cause a) suboptimal package to be pulled in b) failure to install the package at all. "But make it a regular expression" doesn't fly either: you would end up with specs full of constructs like ``` %ifarch %ix86 Requires: %{name}.(i?86|athlon|geode) %endif %ifarch x86_64 amd64 ia32e Requires: %{name}.(x86_64|amd64|ia32e) %endif ... ``` So why not just use the "base arch", ie %{_arch} for it? The problem with this is that eg. i386 is already overloaded with meanings: it is used to mean the x86-32 architecture family, and also the physical i386 CPU. So if you had i386 and i686 flavors of a package, the "base architecture" of the i686 variant would be i386, and ... oops, it's not possible to tell whether it means an actual i386 CPU or i386-compatible architecture. The ISA naming scheme avoids this ambiguity: ISA always means architecture family, not any specific architecture flavor. The other important aspect of the implementation is compatibility: by adding the ISA information as additional regular provides, packages remain 100% compatible with older rpm versions and depsolvers such as yum and urpmi without any code changes. This wouldn't have been the case if the implementation relied on %{arch} tag from headers. Also the conditional macro syntax permits spec files to be backwards compatible: when built with an rpm without ISA support, it'll just fall back to former behavior. It's possible that the expected "Requires: %{name}.%{_target_cpu}" style dependencies become supported too at some point, but the need for this is rare and can be already accomplished by adding manual arch-specific provides+requires to the packages that need it (or by arranging arch-specific filename(s) in packages and depending on those). rpm-software-management-rpm-3c1f23f/docs/manual/autosetup.md000066400000000000000000000102531511627505500242470ustar00rootroot00000000000000--- layout: default title: rpm.org - Automating patch application in specs --- # Automating patch application in specs ## %autosetup description Starting with version 4.11, RPM has a set of new macros to further automate source unpacking and patch applications. Previously one had to manually specify each patch to be applied, eg ``` %prep %setup -q %patch 0 %patch 1 %patch 2 ... %patch 149 ``` This can get rather tedious when the number of patches is large. The new `%autosetup` macro allows taking care of all this in a single step: the following applies all the patches declared in the spec, ordered by the patch number: ``` %prep %autosetup ``` In addition to automating plain old `patch` command invocations, `%autosetup` allows utilizing various version control systems such as git, mercurial (aka hg), quilt and bzr for managing the build directory source. For example this unpacks the vanilla source, initializes a git repository in the build directory and then applies all the patches defined in the spec using individual git apply + commits: ``` %autosetup -S git ``` The resulting build directory can be used for bisecting problems introduced in patches, and developing new patches from the build directory is more natural than with gendiff. ## %autosetup options Generally `%autosetup` accepts the same arguments as [%setup](spec.md#setup) does. The notable exceptions are * `%autosetup` defaults to quiet operation, so `-q` is not needed or accepted. Use `-v` to enable verbose source unpacking if needed. * `-N` disables automatic patch application if necessary for some reason. If `%autosetup` is called with `-N`, the patch-application phase can be manually invoked with `%autopatch` macro. * `-S` specifies the VCS to use. Currently supported VCSes are: `git`, `hg` (for mercurial), `bzr`, `quilt`, `patch`, `git_am` (rpm >= 4.12) and `gendiff` (rpm >= 4.14). If `S` is omitted, `%autosetup` defaults to `patch` * `-p` argument to control patch prefix stripping (same as `-p` to `%patch`) * `-b` (for creating patch backups) is accepted but currently ignored - this is not meaningful for a full-blown VCS anyway. If you need backups for `gendiff` use, use `gendiff` backend. Note that the exact behavior of `-S` option depends on the used VCS: for example quilt only controls patches whereas git and mercurial control the entire source repository. ## %autopatch Sometimes you need more control than just "apply all", in which case you can call `%autopatch` directly. By default it simply applies all patches in the order declared in the spec, but you can additionally control the range with options, or pass patch numbers as arguments. The supported options are * `-v` verbose operation * `-q` don't warn if there are no matching patches * `-p` argument to control patch prefix stripping (same as `-p` to `%patch`, normally passed down from `%autosetup`) * `-m` Apply patches starting from `` range * `-M` Apply patches up to `` range Some examples: ### Apply patches with number >= 100 `%autopatch -m 100` ### Apply patches with number <= 400 `%autopatch -M 400` ### Apply patches 80 to 99, inclusive `%autopatch -m 80 -M 99` ### Apply patches 1, 4 and 6 `%autopatch 1 4 6` ## Automating patch (and source) declarations While typically patch and source names tend to be descriptive for humans, making automating the declarations impossible, some upstreams (for example bash and vim) provide bugfixes by serially numbered patches. In such cases automation can be taken one step further by programmatically generating the patch declarations as well. As of this writing there are no specific helper macros for performing this, but for example the embedded Lua interpreter can be used for the purpose: ``` %{lua:for i=1,45 do print(string.format("Patch%u: bash42-%03u\n", i, i)) end} ``` On spec parse, the above expands to as many patch declarations (best inspected with `rpmspec --parse `): ``` Patch1: bash42-001 Patch2: bash42-002 Patch3: bash42-003 Patch4: bash42-004 ... Patch45: bash42-045 ``` Combined with `%autosetup`, this can eliminate a very large number of repetitive spec lines, making package maintenance that little bit easier. rpm-software-management-rpm-3c1f23f/docs/manual/boolean_dependencies.md000066400000000000000000000072611511627505500263500ustar00rootroot00000000000000--- layout: default title: rpm.org - Boolean Dependencies --- ## Boolean Dependencies Starting with rpm-4.13, RPM is able to process boolean expressions in all dependencies (Requires, Recommends, Suggests, Supplements, Enhances, Conflicts). Boolean Expressions are always enclosed with parenthesis. They are build out of "normal" dependencies: either name only or name, comparison and version description. ## Boolean Operators The following operators were introduced in RPM 4.13: * `and` - requires all operands to be fulfilled for the term to be True. * `Conflicts: (pkgA and pkgB)` * `or` - requires one of the operands to be fulfilled * `Requires: (pkgA >= 3.2 or pkgB)` * `if` - requires the first operand to be fulfilled if the second is (reverse implication) * `Recommends: (myPkg-langCZ if langsupportCZ)` * `if else` - same as above but requires the third operand to be fulfilled if the second is not * `Requires: (myPkg-backend-mariaDB if mariaDB else sqlite)` The following additional operators were introduced in RPM 4.14: * `with` - requires all operands to be fulfilled by the same package for the term to be True. * `Requires: (pkgA-foo with pkgA-bar)` * `without` - requires a single package that satisfies the first operand but not the second (set subtraction) * `Requires: (pkgA-foo without pkgA-bar)` * `unless` - requires the first operand to be fulfilled if the second is not (reverse negative implication) * `Conflicts: (myPkg-driverA unless driverB)` * `unless else` - same as above but requires the third operand to be fulfilled if the second is * `Conflicts: (myPkg-backend-SDL1 unless myPkg-backend-SDL2 else SDL2)` The `if` operator cannot be used in the same context with `or` and the `unless` operator cannot be used in the same context with `and`. ## Nesting Operands can be Boolean Expressions themselves. They also need to be surrounded by parenthesis. `and` and `or` operators may be chained together repeating the same operator with only one set of surrounding parenthesis. Examples: `Requires: (pkgA or pkgB or pkgC)` `Requires: (pkgA or (pkgB and pkgC))` `Supplements: (foo and (lang-support-cz or lang-support-all))` `Requires: ((pkgA with capB) or (pkgB without capA))` `Supplements: ((driverA and driverA-tools) unless driverB)` `Recommends: ((myPkg-langCZ and (font1-langCZ or font2-langCZ)) if langsupportCZ)` ## Semantics The Semantic of the dependencies stay unchanged but instead checking for one match all names are checked and the Boolean value of there being a match is then aggregated over the Boolean operators. For a conflict to not prevent an install the result has to be False for everything else it has to be True. Note that '''Provides''' are not dependencies and '''cannot contain Boolean Expressions'''. ### Incompatible nesting of operators Note that the `if` operator is also returning a Boolean value. This is close to what the intuitive reading in most cases. E.g: `Requires: (pkgA if pkgB)` is True (and everything is OK) if pkgB is not installed. But if the same term is used where the "default" is False things become complicated: `Conflicts: (pkgA if pkgB)` is a Conflict unless pkgB is installed and pkgA is not. So one might rather want to use `Conflicts: (pkgA and pkgB)` in this case. The same is true if the `if` operator is nested in `or` terms. `Requires: ((pkgA if pkgB) or pkgC or pkg)` As the `if` term is True if pkgB is not installed this also makes the whole term True. If pkgA only helps if pkgB is installed you should use `and` instead: `Requires: ((pkgA and pkgB) or pkgC or pkg)` To avoid this confusion rpm refuses nesting `if` in such `or` like contexts (including `Conflicts:`) and `unless` in `and` like contexts. rpm-software-management-rpm-3c1f23f/docs/manual/buildprocess.md000066400000000000000000000131501511627505500247130ustar00rootroot00000000000000--- layout: default title: rpm.org - Package Build Process --- # Package Build Process ## Overview * Unpack srpm/tar (optional) * Parse the [spec](spec.md) * If buildarch detected parse spec multiple times - once for each arch with `_target_cpu` macro set * Build will iterate over all the spec variants and build multiple versions * Execute internal `%mkbuilddir` script to create `%builddir` * Check static build requires * Execute present [build scriptlets](spec.md#build-scriptlets) * `%prep` * `%generate_buildrequires` * re-check build requires - stop build on errors * `%conf` * `%build` * `%install` * Read [dynamic spec parts](dynamic_specs.md) * `%check` * Process files * Turn [%files](spec.md#files-section) lines into actual files (evaluate globs) * Read from -f param * Run [file classifier](dependency_generators.md#file-attributes) * Run [dependency generators](dependency_generators.md) * Check packaged files against install root * Create packages * Clean up * %clean ## Tunables The build process can be tuned and tweaked in lots of ways via macro configurables. ### Parallelism Various aspects of run in parallel on SMP systems. Rpm estimates the number of processes/threads to use based on detected resources and normally there's no need to touch these but sometimes it's necessary to work around buggy make-files, tune the parameters for an unusual compilation resource usage or such. Macro name | Description ------------------------|------------ `%_smp_ncpus_max` | Maximum number of CPUs `%_smp_nthreads_max` | Maximum number of threads `%_smp_build_ncpus` | Number of CPUs to use for building `%_smp_build_nthreads` | Number of threads to use for building `%_smp_tasksize_proc` | Assumed task size of a process `%_smp_tasksize_thread` | Assumed task size of a thread ### Package generation Macro name | Description --------------------------------|------------ `%_binary_filedigest_algorithm` | File digest algorithm to use for binary packages `%_source_filedigest_algorithm` | File digest algorithm to use for source packages `%_changelog_trimage` | Maximum age of `%changelog` entries `%_changelog_trimtime` | Cut-off date of `%changelog` entries ### Debuginfo Macro name | Description ---------------------------|------------- `%_enable_debug_packages` | Enable generation of debuginfo packages `%_include_minidebuginfo` | Include minimal debuginfo in binaries directly `%_include_gdb_index` | Include a `.gdb_index` section to `.debug` files `%_build_id_links` | BuildID link generation: `none`/`alldebug`/`separate`/`compat` `%_unique_build_ids` | Use version info to seed the BuildIDs `%_unique_debug_names` | Use version info to differentiate .debug files `%_unique_debug_srcs` | Use version info to differentiate debug sources `%_debugsource_packages` | Generate a separate sub-package for debug sources `%_debuginfo_subpackages` | Generate debuginfo packages per sub-package `%_no_recompute_build_ids` | Preserve pre-existing BuildIDs ### Packaging hygiene Various packaging sanity check overrides for dealing with legacy content. Macro name | Description ------------------------------------------------|------------ `%_unpackaged_files_terminate_build` | Excess files in buildroot terminates build `%_duplicate_files_terminate_build` | Duplicates in `%files` terminates build `%_missing_doc_files_terminate_build` | Missing `%doc` files terminates build `%_empty_manifest_terminate_build` | Empty `%files -f` manifest terminates build `%_binaries_in_noarch_packages_terminate_build` | ELF binaries in noarch packages terminates build `%_invalid_encoding_terminates_build` | Non-UTF8 encoding in package data terminates build `%_wrong_version_format_terminate_build` | Invalid version format in dependencies terminates build `%_missing_build_ids_terminate_build` | ELF files without BuildIDs terminates builds (if debuginfo creation enabled) `%_nonzero_exit_pkgcheck_terminate_build` | Package check program failing terminates build `%_build_pkgcheck` | Program to run on each generated binary package `%_build_pkcheck_set` | Program to run on the generated binary package set ### Reproducibility Macro name | Description --------------------------------------|----------- `%source_date_epoch_from_changelog` | Set `SOURCE_DATE_EPOCH` from latest `%changelog` entry `%use_source_date_epoch_as_buildtime` | Set package BuildTime to `SOURCE_DATE_EPOCH` `%clamp_mtime_to_source_date_epoch` | Ensure file timestamps are not newer than `SOURCE_DATE_EPOCH` (deprecated) `%build_mtime_policy` | Defines file timestamp handling The supported values for `%build_mtime_policy` are: * (unset) leave the mtimes as is * `clamp_to_source_date_epoch` clamp the mtimes so that they are not bigger than the `SOURCE_DATE_EPOCH` * `clamp_to_buildtime` clamp the mtimes so that they are not bigger than the package build time (i.e. the time that is used for the `BUILDTIME` tag) ### Vendor defaults These can be used to automatically populate some common spec tags without changing the spec itself. Macro name | Description ----------------|------------ `%bugurl` | BugURL tag `%distribution` | Distribution tag `%disturl` | DistURL tag `%disttag` | DistTag tag `%packager` | Packager tag `%vendor` | Vendor tag ### Dependencies See [dependency generators](dependency_generators.md). rpm-software-management-rpm-3c1f23f/docs/manual/buildsystem.md000066400000000000000000000105601511627505500245630ustar00rootroot00000000000000--- layout: default title: rpm.org - Declarative builds --- # Declarative builds Most software follows common patterns as to how it should be prepared and built, such as the classic `./configure && make && make install` case. In a spec, these all go to their own sections and when combined with the steps to unpack sources, it creates a lot of boilerplate that is practically identical across a lot of specs. With the odd tweak to this or that detail for distro preferences and the like. To reduce the repetitive boilerplate, rpm >= 4.20 adds support for a declarative build system mechanism where these common steps can be defined centrally per each build system. Packagers then only need to declare which build system they use, and optionally supply additional switches and/or arguments for the steps where needed. ## Spec usage In the most simple case, a spec will have an buildsystem declaration such as `BuildSystem: cmake` and no manually specified build scripts at all. However it's common to have to manually tweak a thing or two. There are several ways how to accomplish this, what's appropriate depends on the case. 1) Use append and/or prepend as necessary. Need to delete a file after the install step? Add a `%install -a` section with the appropriate `rm`. Need to `sed` something just before the build step? Add a `%build -p` section with the necessary steps. 2) Another very common need is to pass extra arguments to the build commands, build configuration in particular. This is done with the `BuildOption` tag, which can appear arbitrary number of times in the spec for each section. ``` BuildOption(
):