| #!/bin/sh -e | 
 |  | 
 | # Upload a created tarball to Coverity Scan, as per | 
 | # https://scan.coverity.com/projects/qemu/builds/new | 
 |  | 
 | # This work is licensed under the terms of the GNU GPL version 2, | 
 | # or (at your option) any later version. | 
 | # See the COPYING file in the top-level directory. | 
 | # | 
 | # Copyright (c) 2017-2020 Linaro Limited | 
 | # Written by Peter Maydell | 
 |  | 
 | # Note that this script will automatically download and | 
 | # run the (closed-source) coverity build tools, so don't | 
 | # use it if you don't trust them! | 
 |  | 
 | # This script assumes that you're running it from a QEMU source | 
 | # tree, and that tree is a fresh clean one, because we do an in-tree | 
 | # build. (This is necessary so that the filenames that the Coverity | 
 | # Scan server sees are relative paths that match up with the component | 
 | # regular expressions it uses; an out-of-tree build won't work for this.) | 
 | # The host machine should have as many of QEMU's dependencies | 
 | # installed as possible, for maximum coverity coverage. | 
 |  | 
 | # To do an upload you need to be a maintainer in the Coverity online | 
 | # service, and you will need to know the "Coverity token", which is a | 
 | # secret 8 digit hex string. You can find that from the web UI in the | 
 | # project settings, if you have maintainer access there. | 
 |  | 
 | # Command line options: | 
 | #   --check-upload-only : return success if upload is possible | 
 | #   --dry-run : run the tools, but don't actually do the upload | 
 | #   --docker : create and work inside a container | 
 | #   --docker-engine : specify the container engine to use (docker/podman/auto); | 
 | #                     implies --docker | 
 | #   --update-tools-only : update the cached copy of the tools, but don't run them | 
 | #   --no-update-tools : do not update the cached copy of the tools | 
 | #   --tokenfile : file to read Coverity token from | 
 | #   --version ver : specify version being analyzed (default: ask git) | 
 | #   --description desc : specify description of this version (default: ask git) | 
 | #   --srcdir : QEMU source tree to analyze (default: current working dir) | 
 | #   --results-tarball : path to copy the results tarball to (default: don't | 
 | #                       copy it anywhere, just upload it) | 
 | #   --src-tarball : tarball to untar into src dir (default: none); this | 
 | #                   is intended mainly for internal use by the Docker support | 
 | # | 
 | # User-specifiable environment variables: | 
 | #  COVERITY_TOKEN -- Coverity token (default: looks at your | 
 | #                    coverity.token config) | 
 | #  COVERITY_EMAIL -- the email address to use for uploads (default: | 
 | #                    looks at your git coverity.email or user.email config) | 
 | #  COVERITY_BUILD_CMD -- make command (default: 'make -jN' where N is | 
 | #                    number of CPUs as determined by 'nproc') | 
 | #  COVERITY_TOOL_BASE -- set to directory to put coverity tools | 
 | #                        (default: /tmp/coverity-tools) | 
 | # | 
 | # You must specify the token, either by environment variable or by | 
 | # putting it in a file and using --tokenfile. Everything else has | 
 | # a reasonable default if this is run from a git tree. | 
 |  | 
 | upload_permitted() { | 
 |     # Check whether we can do an upload to the server; will exit *the script* | 
 |     # with status 99 if the check failed (usually a bad token); | 
 |     # will return from the function with status 1 if the check indicated | 
 |     # that we can't upload yet (ie we are at quota) | 
 |     # Assumes that COVERITY_TOKEN and PROJNAME have been initialized. | 
 |  | 
 |     echo "Checking upload permissions..." | 
 |  | 
 |     if ! up_perm="$(wget https://scan.coverity.com/api/upload_permitted --post-data "token=$COVERITY_TOKEN&project=$PROJNAME" -q -O -)"; then | 
 |         echo "Coverity Scan API access denied: bad token?" | 
 |         exit 99 | 
 |     fi | 
 |  | 
 |     # Really up_perm is a JSON response with either | 
 |     # {upload_permitted:true} or {next_upload_permitted_at:<date>} | 
 |     # We do some hacky string parsing instead of properly parsing it. | 
 |     case "$up_perm" in | 
 |         *upload_permitted*true*) | 
 |             return 0 | 
 |             ;; | 
 |         *next_upload_permitted_at*) | 
 |             return 1 | 
 |             ;; | 
 |         *) | 
 |             echo "Coverity Scan upload check: unexpected result $up_perm" | 
 |             exit 99 | 
 |             ;; | 
 |     esac | 
 | } | 
 |  | 
 |  | 
 | check_upload_permissions() { | 
 |     # Check whether we can do an upload to the server; will exit the script | 
 |     # with status 99 if the check failed (usually a bad token); | 
 |     # will exit the script with status 0 if the check indicated that we | 
 |     # can't upload yet (ie we are at quota) | 
 |     # Assumes that COVERITY_TOKEN, PROJNAME and DRYRUN have been initialized. | 
 |  | 
 |     if upload_permitted; then | 
 |         echo "Coverity Scan: upload permitted" | 
 |     else | 
 |         if [ "$DRYRUN" = yes ]; then | 
 |             echo "Coverity Scan: upload quota reached, continuing dry run" | 
 |         else | 
 |             echo "Coverity Scan: upload quota reached; stopping here" | 
 |             # Exit success as this isn't a build error. | 
 |             exit 0 | 
 |         fi | 
 |     fi | 
 | } | 
 |  | 
 |  | 
 | build_docker_image() { | 
 |     # build docker container including the coverity-scan tools | 
 |     echo "Building docker container..." | 
 |     # TODO: This re-unpacks the tools every time, rather than caching | 
 |     # and reusing the image produced by the COPY of the .tgz file. | 
 |     # Not sure why. | 
 |     tests/docker/docker.py --engine ${DOCKER_ENGINE} build \ | 
 |                    -t coverity-scanner -f scripts/coverity-scan/coverity-scan.docker \ | 
 |                    --extra-files scripts/coverity-scan/run-coverity-scan \ | 
 |                                  "$COVERITY_TOOL_BASE"/coverity_tool.tgz | 
 | } | 
 |  | 
 | update_coverity_tools () { | 
 |     # Check for whether we need to download the Coverity tools | 
 |     # (either because we don't have a copy, or because it's out of date) | 
 |     # Assumes that COVERITY_TOOL_BASE, COVERITY_TOKEN and PROJNAME are set. | 
 |  | 
 |     mkdir -p "$COVERITY_TOOL_BASE" | 
 |     cd "$COVERITY_TOOL_BASE" | 
 |  | 
 |     echo "Checking for new version of coverity build tools..." | 
 |     wget https://scan.coverity.com/download/cxx/linux64 --post-data "token=$COVERITY_TOKEN&project=$PROJNAME&md5=1" -O coverity_tool.md5.new | 
 |  | 
 |     if ! cmp -s coverity_tool.md5 coverity_tool.md5.new; then | 
 |         # out of date md5 or no md5: download new build tool | 
 |         # blow away the old build tool | 
 |         echo "Downloading coverity build tools..." | 
 |         rm -rf coverity_tool coverity_tool.tgz | 
 |         wget https://scan.coverity.com/download/cxx/linux64 --post-data "token=$COVERITY_TOKEN&project=$PROJNAME" -O coverity_tool.tgz | 
 |         if ! (cat coverity_tool.md5.new; echo "  coverity_tool.tgz") | md5sum -c --status; then | 
 |             echo "Downloaded tarball didn't match md5sum!" | 
 |             exit 1 | 
 |         fi | 
 |  | 
 |         if [ "$DOCKER" != yes ]; then | 
 |             # extract the new one, keeping it corralled in a 'coverity_tool' directory | 
 |             echo "Unpacking coverity build tools..." | 
 |             mkdir -p coverity_tool | 
 |             cd coverity_tool | 
 |             tar xf ../coverity_tool.tgz | 
 |             cd .. | 
 |             mv coverity_tool.md5.new coverity_tool.md5 | 
 |         fi | 
 |     fi | 
 |     rm -f coverity_tool.md5.new | 
 |     cd "$SRCDIR" | 
 |  | 
 |     if [ "$DOCKER" = yes ]; then | 
 |         build_docker_image | 
 |     fi | 
 | } | 
 |  | 
 |  | 
 | # Check user-provided environment variables and arguments | 
 | DRYRUN=no | 
 | UPDATE=yes | 
 | DOCKER=no | 
 | PROJNAME=QEMU | 
 |  | 
 | while [ "$#" -ge 1 ]; do | 
 |     case "$1" in | 
 |         --check-upload-only) | 
 |             shift | 
 |             DRYRUN=check | 
 |             ;; | 
 |         --dry-run) | 
 |             shift | 
 |             DRYRUN=yes | 
 |             ;; | 
 |         --no-update-tools) | 
 |             shift | 
 |             UPDATE=no | 
 |             ;; | 
 |         --update-tools-only) | 
 |             shift | 
 |             UPDATE=only | 
 |             ;; | 
 |         --version) | 
 |             shift | 
 |             if [ $# -eq 0 ]; then | 
 |                 echo "--version needs an argument" | 
 |                 exit 1 | 
 |             fi | 
 |             VERSION="$1" | 
 |             shift | 
 |             ;; | 
 |         --description) | 
 |             shift | 
 |             if [ $# -eq 0 ]; then | 
 |                 echo "--description needs an argument" | 
 |                 exit 1 | 
 |             fi | 
 |             DESCRIPTION="$1" | 
 |             shift | 
 |             ;; | 
 |         --tokenfile) | 
 |             shift | 
 |             if [ $# -eq 0 ]; then | 
 |                 echo "--tokenfile needs an argument" | 
 |                 exit 1 | 
 |             fi | 
 |             COVERITY_TOKEN="$(cat "$1")" | 
 |             shift | 
 |             ;; | 
 |         --srcdir) | 
 |             shift | 
 |             if [ $# -eq 0 ]; then | 
 |                 echo "--srcdir needs an argument" | 
 |                 exit 1 | 
 |             fi | 
 |             SRCDIR="$1" | 
 |             shift | 
 |             ;; | 
 |         --results-tarball) | 
 |             shift | 
 |             if [ $# -eq 0 ]; then | 
 |                 echo "--results-tarball needs an argument" | 
 |                 exit 1 | 
 |             fi | 
 |             RESULTSTARBALL="$1" | 
 |             shift | 
 |             ;; | 
 |         --src-tarball) | 
 |             shift | 
 |             if [ $# -eq 0 ]; then | 
 |                 echo "--src-tarball needs an argument" | 
 |                 exit 1 | 
 |             fi | 
 |             SRCTARBALL="$1" | 
 |             shift | 
 |             ;; | 
 |         --docker) | 
 |             DOCKER=yes | 
 |             DOCKER_ENGINE=auto | 
 |             shift | 
 |             ;; | 
 |         --docker-engine) | 
 |             shift | 
 |             if [ $# -eq 0 ]; then | 
 |                 echo "--docker-engine needs an argument" | 
 |                 exit 1 | 
 |             fi | 
 |             DOCKER=yes | 
 |             DOCKER_ENGINE="$1" | 
 |             shift | 
 |             ;; | 
 |         *) | 
 |             echo "Unexpected argument '$1'" | 
 |             exit 1 | 
 |             ;; | 
 |     esac | 
 | done | 
 |  | 
 | if [ -z "$COVERITY_TOKEN" ]; then | 
 |     COVERITY_TOKEN="$(git config coverity.token)" | 
 | fi | 
 | if [ -z "$COVERITY_TOKEN" ]; then | 
 |     echo "COVERITY_TOKEN environment variable not set" | 
 |     exit 1 | 
 | fi | 
 |  | 
 | if [ "$DRYRUN" = check ]; then | 
 |     upload_permitted | 
 |     exit $? | 
 | fi | 
 |  | 
 | if [ -z "$COVERITY_BUILD_CMD" ]; then | 
 |     NPROC=$(nproc) | 
 |     COVERITY_BUILD_CMD="make -j$NPROC" | 
 |     echo "COVERITY_BUILD_CMD: using default '$COVERITY_BUILD_CMD'" | 
 | fi | 
 |  | 
 | if [ -z "$COVERITY_TOOL_BASE" ]; then | 
 |     echo "COVERITY_TOOL_BASE: using default /tmp/coverity-tools" | 
 |     COVERITY_TOOL_BASE=/tmp/coverity-tools | 
 | fi | 
 |  | 
 | if [ -z "$SRCDIR" ]; then | 
 |     SRCDIR="$PWD" | 
 | fi | 
 |  | 
 | TARBALL=cov-int.tar.xz | 
 |  | 
 | if [ "$UPDATE" = only ]; then | 
 |     # Just do the tools update; we don't need to check whether | 
 |     # we are in a source tree or have upload rights for this, | 
 |     # so do it before some of the command line and source tree checks. | 
 |  | 
 |     if [ "$DOCKER" = yes ] && [ ! -z "$SRCTARBALL" ]; then | 
 |         echo --update-tools-only --docker is incompatible with --src-tarball. | 
 |         exit 1 | 
 |     fi | 
 |  | 
 |     update_coverity_tools | 
 |     exit 0 | 
 | fi | 
 |  | 
 | if [ ! -e "$SRCDIR" ]; then | 
 |     mkdir "$SRCDIR" | 
 | fi | 
 |  | 
 | cd "$SRCDIR" | 
 |  | 
 | if [ ! -z "$SRCTARBALL" ]; then | 
 |     echo "Untarring source tarball into $SRCDIR..." | 
 |     tar xvf "$SRCTARBALL" | 
 | fi | 
 |  | 
 | echo "Checking this is a QEMU source tree..." | 
 | if ! [ -e "$SRCDIR/VERSION" ]; then | 
 |     echo "Not in a QEMU source tree?" | 
 |     exit 1 | 
 | fi | 
 |  | 
 | # Fill in defaults used by the non-update-only process | 
 | if [ -z "$VERSION" ]; then | 
 |     VERSION="$(git describe --always HEAD)" | 
 | fi | 
 |  | 
 | if [ -z "$DESCRIPTION" ]; then | 
 |     DESCRIPTION="$(git rev-parse HEAD)" | 
 | fi | 
 |  | 
 | if [ -z "$COVERITY_EMAIL" ]; then | 
 |     COVERITY_EMAIL="$(git config coverity.email)" | 
 | fi | 
 | if [ -z "$COVERITY_EMAIL" ]; then | 
 |     COVERITY_EMAIL="$(git config user.email)" | 
 | fi | 
 |  | 
 | # Otherwise, continue with the full build and upload process. | 
 |  | 
 | check_upload_permissions | 
 |  | 
 | if [ "$UPDATE" != no ]; then | 
 |     update_coverity_tools | 
 | fi | 
 |  | 
 | # Run ourselves inside docker if that's what the user wants | 
 | if [ "$DOCKER" = yes ]; then | 
 |     # Put the Coverity token into a temporary file that only | 
 |     # we have read access to, and then pass it to docker build | 
 |     # using a volume.  A volume is enough for the token not to | 
 |     # leak into the Docker image. | 
 |     umask 077 | 
 |     SECRETDIR=$(mktemp -d) | 
 |     if [ -z "$SECRETDIR" ]; then | 
 |         echo "Failed to create temporary directory" | 
 |         exit 1 | 
 |     fi | 
 |     trap 'rm -rf "$SECRETDIR"' INT TERM EXIT | 
 |     echo "Created temporary directory $SECRETDIR" | 
 |     SECRET="$SECRETDIR/token" | 
 |     echo "$COVERITY_TOKEN" > "$SECRET" | 
 |     echo "Archiving sources to be analyzed..." | 
 |     ./scripts/archive-source.sh "$SECRETDIR/qemu-sources.tgz" | 
 |     ARGS="--no-update-tools" | 
 |     if [ "$DRYRUN" = yes ]; then | 
 |         ARGS="$ARGS --dry-run" | 
 |     fi | 
 |     echo "Running scanner..." | 
 |     # If we need to capture the output tarball, get the inner run to | 
 |     # save it to the secrets directory so we can copy it out before the | 
 |     # directory is cleaned up. | 
 |     if [ ! -z "$RESULTSTARBALL" ]; then | 
 |         ARGS="$ARGS --results-tarball /work/cov-int.tar.xz" | 
 |     fi | 
 |     # Arrange for this docker run to get access to the sources with -v. | 
 |     # We pass through all the configuration from the outer script to the inner. | 
 |     export COVERITY_EMAIL COVERITY_BUILD_CMD | 
 |     tests/docker/docker.py run -it --env COVERITY_EMAIL --env COVERITY_BUILD_CMD \ | 
 |            -v "$SECRETDIR:/work" coverity-scanner \ | 
 |            ./run-coverity-scan --version "$VERSION" \ | 
 |            --description "$DESCRIPTION" $ARGS --tokenfile /work/token \ | 
 |            --srcdir /qemu --src-tarball /work/qemu-sources.tgz | 
 |     if [ ! -z "$RESULTSTARBALL" ]; then | 
 |         echo "Copying results tarball to $RESULTSTARBALL..." | 
 |         cp "$SECRETDIR/cov-int.tar.xz" "$RESULTSTARBALL" | 
 |     fi | 
 |     echo "Docker work complete." | 
 |     exit 0 | 
 | fi | 
 |  | 
 | TOOLBIN="$(cd "$COVERITY_TOOL_BASE" && echo $PWD/coverity_tool/cov-analysis-*/bin)" | 
 |  | 
 | if ! test -x "$TOOLBIN/cov-build"; then | 
 |     echo "Couldn't find cov-build in the coverity build-tool directory??" | 
 |     exit 1 | 
 | fi | 
 |  | 
 | export PATH="$TOOLBIN:$PATH" | 
 |  | 
 | cd "$SRCDIR" | 
 |  | 
 | echo "Nuking build directory..." | 
 | rm -rf +build | 
 | mkdir +build | 
 | cd +build | 
 |  | 
 | echo "Configuring..." | 
 | # We configure with a fixed set of enables here to ensure that we don't | 
 | # accidentally reduce the scope of the analysis by doing the build on | 
 | # the system that's missing a dependency that we need to build part of | 
 | # the codebase. | 
 | ../configure --disable-modules --enable-sdl --enable-gtk \ | 
 |     --enable-opengl --enable-vte --enable-gnutls \ | 
 |     --enable-nettle --enable-curses --enable-curl \ | 
 |     --audio-drv-list=oss,alsa,sdl,pa --enable-virtfs \ | 
 |     --enable-vnc --enable-vnc-sasl --enable-vnc-jpeg --enable-png \ | 
 |     --enable-xen --enable-brlapi \ | 
 |     --enable-linux-aio --enable-attr \ | 
 |     --enable-cap-ng --enable-trace-backends=log --enable-spice --enable-rbd \ | 
 |     --enable-libusb --enable-usb-redir \ | 
 |     --enable-libiscsi --enable-libnfs --enable-seccomp \ | 
 |     --enable-tpm --enable-libssh --enable-lzo --enable-snappy --enable-bzip2 \ | 
 |     --enable-numa --enable-rdma --enable-smartcard --enable-virglrenderer \ | 
 |     --enable-mpath --enable-glusterfs \ | 
 |     --enable-virtfs --enable-zstd | 
 |  | 
 | echo "Running cov-build..." | 
 | rm -rf cov-int | 
 | mkdir cov-int | 
 | cov-build --dir cov-int $COVERITY_BUILD_CMD | 
 |  | 
 | echo "Creating results tarball..." | 
 | tar cvf - cov-int | xz > "$TARBALL" | 
 |  | 
 | if [ ! -z "$RESULTSTARBALL" ]; then | 
 |     echo "Copying results tarball to $RESULTSTARBALL..." | 
 |     cp "$TARBALL" "$RESULTSTARBALL" | 
 | fi | 
 |  | 
 | echo "Uploading results tarball..." | 
 |  | 
 | if [ "$DRYRUN" = yes ]; then | 
 |     echo "Dry run only, not uploading $TARBALL" | 
 |     exit 0 | 
 | fi | 
 |  | 
 | curl --form token="$COVERITY_TOKEN" --form email="$COVERITY_EMAIL" \ | 
 |      --form file=@"$TARBALL" --form version="$VERSION" \ | 
 |      --form description="$DESCRIPTION" \ | 
 |      https://scan.coverity.com/builds?project="$PROJNAME" | 
 |  | 
 | echo "Done." |