LCOV - code coverage report
Current view: top level - debuginfod - debuginfod-client.c (source / functions) Hit Total Coverage
Test: elfutils-0.189 Lines: 705 879 80.2 %
Date: 2023-03-03 17:35:23 Functions: 23 25 92.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 395 643 61.4 %

           Branch data     Line data    Source code
       1                 :            : /* Retrieve ELF / DWARF / source files from the debuginfod.
       2                 :            :    Copyright (C) 2019-2021 Red Hat, Inc.
       3                 :            :    Copyright (C) 2021, 2022 Mark J. Wielaard <mark@klomp.org>
       4                 :            :    This file is part of elfutils.
       5                 :            : 
       6                 :            :    This file is free software; you can redistribute it and/or modify
       7                 :            :    it under the terms of either
       8                 :            : 
       9                 :            :      * the GNU Lesser General Public License as published by the Free
      10                 :            :        Software Foundation; either version 3 of the License, or (at
      11                 :            :        your option) any later version
      12                 :            : 
      13                 :            :    or
      14                 :            : 
      15                 :            :      * the GNU General Public License as published by the Free
      16                 :            :        Software Foundation; either version 2 of the License, or (at
      17                 :            :        your option) any later version
      18                 :            : 
      19                 :            :    or both in parallel, as here.
      20                 :            : 
      21                 :            :    elfutils is distributed in the hope that it will be useful, but
      22                 :            :    WITHOUT ANY WARRANTY; without even the implied warranty of
      23                 :            :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      24                 :            :    General Public License for more details.
      25                 :            : 
      26                 :            :    You should have received copies of the GNU General Public License and
      27                 :            :    the GNU Lesser General Public License along with this program.  If
      28                 :            :    not, see <http://www.gnu.org/licenses/>.  */
      29                 :            : 
      30                 :            : 
      31                 :            : /* cargo-cult from libdwfl linux-kernel-modules.c */
      32                 :            : /* In case we have a bad fts we include this before config.h because it
      33                 :            :    can't handle _FILE_OFFSET_BITS.
      34                 :            :    Everything we need here is fine if its declarations just come first.
      35                 :            :    Also, include sys/types.h before fts. On some systems fts.h is not self
      36                 :            :    contained. */
      37                 :            : #ifdef BAD_FTS
      38                 :            :   #include <sys/types.h>
      39                 :            :   #include <fts.h>
      40                 :            : #endif
      41                 :            : 
      42                 :            : #include "config.h"
      43                 :            : #include "debuginfod.h"
      44                 :            : #include "system.h"
      45                 :            : #include <ctype.h>
      46                 :            : #include <errno.h>
      47                 :            : #include <stdlib.h>
      48                 :            : #include <gelf.h>
      49                 :            : 
      50                 :            : /* We might be building a bootstrap dummy library, which is really simple. */
      51                 :            : #ifdef DUMMY_LIBDEBUGINFOD
      52                 :            : 
      53                 :            : debuginfod_client *debuginfod_begin (void) { errno = ENOSYS; return NULL; }
      54                 :            : int debuginfod_find_debuginfo (debuginfod_client *c, const unsigned char *b,
      55                 :            :                                int s, char **p) { return -ENOSYS; }
      56                 :            : int debuginfod_find_executable (debuginfod_client *c, const unsigned char *b,
      57                 :            :                                 int s, char **p) { return -ENOSYS; }
      58                 :            : int debuginfod_find_source (debuginfod_client *c, const unsigned char *b,
      59                 :            :                             int s, const char *f, char **p)  { return -ENOSYS; }
      60                 :            : int debuginfod_find_section (debuginfod_client *c, const unsigned char *b,
      61                 :            :                              int s, const char *scn, char **p)
      62                 :            :                               { return -ENOSYS; }
      63                 :            : void debuginfod_set_progressfn(debuginfod_client *c,
      64                 :            :                                debuginfod_progressfn_t fn) { }
      65                 :            : void debuginfod_set_verbose_fd(debuginfod_client *c, int fd) { }
      66                 :            : void debuginfod_set_user_data (debuginfod_client *c, void *d) { }
      67                 :            : void* debuginfod_get_user_data (debuginfod_client *c) { return NULL; }
      68                 :            : const char* debuginfod_get_url (debuginfod_client *c) { return NULL; }
      69                 :            : int debuginfod_add_http_header (debuginfod_client *c,
      70                 :            :                                 const char *h) { return -ENOSYS; }
      71                 :            : const char* debuginfod_get_headers (debuginfod_client *c) { return NULL; }
      72                 :            : 
      73                 :            : void debuginfod_end (debuginfod_client *c) { }
      74                 :            : 
      75                 :            : #else /* DUMMY_LIBDEBUGINFOD */
      76                 :            : 
      77                 :            : #include <assert.h>
      78                 :            : #include <dirent.h>
      79                 :            : #include <stdio.h>
      80                 :            : #include <errno.h>
      81                 :            : #include <unistd.h>
      82                 :            : #include <fcntl.h>
      83                 :            : #include <fts.h>
      84                 :            : #include <regex.h>
      85                 :            : #include <string.h>
      86                 :            : #include <stdbool.h>
      87                 :            : #include <linux/limits.h>
      88                 :            : #include <time.h>
      89                 :            : #include <utime.h>
      90                 :            : #include <sys/syscall.h>
      91                 :            : #include <sys/types.h>
      92                 :            : #include <sys/stat.h>
      93                 :            : #include <sys/utsname.h>
      94                 :            : #include <curl/curl.h>
      95                 :            : 
      96                 :            : /* If fts.h is included before config.h, its indirect inclusions may not
      97                 :            :    give us the right LFS aliases of these functions, so map them manually.  */
      98                 :            : #ifdef BAD_FTS
      99                 :            :   #ifdef _FILE_OFFSET_BITS
     100                 :            :     #define open open64
     101                 :            :     #define fopen fopen64
     102                 :            :   #endif
     103                 :            : #else
     104                 :            :   #include <sys/types.h>
     105                 :            :   #include <fts.h>
     106                 :            : #endif
     107                 :            : 
     108                 :            : /* Older curl.h don't define CURL_AT_LEAST_VERSION.  */
     109                 :            : #ifndef CURL_AT_LEAST_VERSION
     110                 :            :   #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
     111                 :            :   #define CURL_AT_LEAST_VERSION(x,y,z) \
     112                 :            :     (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
     113                 :            : #endif
     114                 :            : 
     115                 :            : #include <pthread.h>
     116                 :            : 
     117                 :            : static pthread_once_t init_control = PTHREAD_ONCE_INIT;
     118                 :            : 
     119                 :            : static void
     120                 :        187 : libcurl_init(void)
     121                 :            : {
     122                 :        187 :   curl_global_init(CURL_GLOBAL_DEFAULT);
     123                 :        187 : }
     124                 :            : 
     125                 :            : struct debuginfod_client
     126                 :            : {
     127                 :            :   /* Progress/interrupt callback function. */
     128                 :            :   debuginfod_progressfn_t progressfn;
     129                 :            : 
     130                 :            :   /* Stores user data. */
     131                 :            :   void* user_data;
     132                 :            : 
     133                 :            :   /* Stores current/last url, if any. */
     134                 :            :   char* url;
     135                 :            : 
     136                 :            :   /* Accumulates outgoing http header names/values. */
     137                 :            :   int user_agent_set_p; /* affects add_default_headers */
     138                 :            :   struct curl_slist *headers;
     139                 :            : 
     140                 :            :   /* Flags the default_progressfn having printed something that
     141                 :            :      debuginfod_end needs to terminate. */
     142                 :            :   int default_progressfn_printed_p;
     143                 :            : 
     144                 :            :   /* Indicates whether the last query was cancelled by progressfn.  */
     145                 :            :   bool progressfn_cancel;
     146                 :            : 
     147                 :            :   /* File descriptor to output any verbose messages if > 0.  */
     148                 :            :   int verbose_fd;
     149                 :            : 
     150                 :            :   /* Maintain a long-lived curl multi-handle, which keeps a
     151                 :            :      connection/tls/dns cache to recently seen servers. */
     152                 :            :   CURLM *server_mhandle;
     153                 :            :   
     154                 :            :   /* Can contain all other context, like cache_path, server_urls,
     155                 :            :      timeout or other info gotten from environment variables, the
     156                 :            :      handle data, etc. So those don't have to be reparsed and
     157                 :            :      recreated on each request.  */
     158                 :            :   char * winning_headers;
     159                 :            : };
     160                 :            : 
     161                 :            : /* The cache_clean_interval_s file within the debuginfod cache specifies
     162                 :            :    how frequently the cache should be cleaned. The file's st_mtime represents
     163                 :            :    the time of last cleaning.  */
     164                 :            : static const char *cache_clean_interval_filename = "cache_clean_interval_s";
     165                 :            : static const long cache_clean_default_interval_s = 86400; /* 1 day */
     166                 :            : 
     167                 :            : /* The cache_miss_default_s within the debuginfod cache specifies how
     168                 :            :    frequently the empty file should be released.*/
     169                 :            : static const long cache_miss_default_s = 600; /* 10 min */
     170                 :            : static const char *cache_miss_filename = "cache_miss_s";
     171                 :            : 
     172                 :            : /* The cache_max_unused_age_s file within the debuginfod cache specifies the
     173                 :            :    the maximum time since last access that a file will remain in the cache.  */
     174                 :            : static const char *cache_max_unused_age_filename = "max_unused_age_s";
     175                 :            : static const long cache_default_max_unused_age_s = 604800; /* 1 week */
     176                 :            : 
     177                 :            : /* Location of the cache of files downloaded from debuginfods.
     178                 :            :    The default parent directory is $HOME, or '/' if $HOME doesn't exist.  */
     179                 :            : static const char *cache_default_name = ".debuginfod_client_cache";
     180                 :            : static const char *cache_xdg_name = "debuginfod_client";
     181                 :            : 
     182                 :            : /* URLs of debuginfods, separated by url_delim. */
     183                 :            : static const char *url_delim =  " ";
     184                 :            : 
     185                 :            : /* Timeout for debuginfods, in seconds (to get at least 100K). */
     186                 :            : static const long default_timeout = 90;
     187                 :            : 
     188                 :            : /* Default retry count for download error. */
     189                 :            : static const long default_retry_limit = 2;
     190                 :            : 
     191                 :            : /* Data associated with a particular CURL easy handle. Passed to
     192                 :            :    the write callback.  */
     193                 :            : struct handle_data
     194                 :            : {
     195                 :            :   /* Cache file to be written to in case query is successful.  */
     196                 :            :   int fd;
     197                 :            : 
     198                 :            :   /* URL queried by this handle.  */
     199                 :            :   char url[PATH_MAX];
     200                 :            : 
     201                 :            :   /* error buffer for this handle.  */
     202                 :            :   char errbuf[CURL_ERROR_SIZE];
     203                 :            : 
     204                 :            :   /* This handle.  */
     205                 :            :   CURL *handle;
     206                 :            : 
     207                 :            :   /* The client object whom we're serving. */
     208                 :            :   debuginfod_client *client;
     209                 :            : 
     210                 :            :   /* Pointer to handle that should write to fd. Initially points to NULL,
     211                 :            :      then points to the first handle that begins writing the target file
     212                 :            :      to the cache. Used to ensure that a file is not downloaded from
     213                 :            :      multiple servers unnecessarily.  */
     214                 :            :   CURL **target_handle;
     215                 :            :   /* Response http headers for this client handle, sent from the server */
     216                 :            :   char *response_data;
     217                 :            :   size_t response_data_size;
     218                 :            : };
     219                 :            : 
     220                 :            : static size_t
     221                 :       3231 : debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data)
     222                 :            : {
     223                 :       3231 :   ssize_t count = size * nmemb;
     224                 :            : 
     225                 :       3231 :   struct handle_data *d = (struct handle_data*)data;
     226                 :            : 
     227                 :            :   /* Indicate to other handles that they can abort their transfer.  */
     228         [ +  + ]:       3231 :   if (*d->target_handle == NULL)
     229                 :            :     {
     230                 :        121 :       *d->target_handle = d->handle;
     231                 :            :       /* update the client object */
     232                 :        121 :       const char *url = NULL;
     233                 :        121 :       CURLcode curl_res = curl_easy_getinfo (d->handle,
     234                 :            :                                              CURLINFO_EFFECTIVE_URL, &url);
     235   [ +  -  +  - ]:        121 :       if (curl_res == CURLE_OK && url)
     236                 :            :         {
     237                 :        121 :           free (d->client->url);
     238                 :        121 :           d->client->url = strdup(url); /* ok if fails */
     239                 :            :         }
     240                 :            :     }
     241                 :            : 
     242                 :            :   /* If this handle isn't the target handle, abort transfer.  */
     243         [ +  - ]:       3231 :   if (*d->target_handle != d->handle)
     244                 :            :     return -1;
     245                 :            : 
     246                 :       3231 :   return (size_t) write(d->fd, (void*)ptr, count);
     247                 :            : }
     248                 :            : 
     249                 :            : /* handle config file read and write */
     250                 :            : static int
     251                 :        296 : debuginfod_config_cache(char *config_path,
     252                 :            :                         long cache_config_default_s,
     253                 :            :                         struct stat *st)
     254                 :            : {
     255                 :        296 :   int fd = open(config_path, O_CREAT | O_RDWR, DEFFILEMODE);
     256         [ -  + ]:        296 :   if (fd < 0)
     257                 :          0 :     return -errno;
     258                 :            : 
     259         [ -  + ]:        296 :   if (fstat (fd, st) < 0)
     260                 :            :     {
     261                 :          0 :       int ret = -errno;
     262                 :          0 :       close (fd);
     263                 :          0 :       return ret;
     264                 :            :     }
     265                 :            : 
     266         [ +  + ]:        296 :   if (st->st_size == 0)
     267                 :            :     {
     268         [ -  + ]:         38 :       if (dprintf(fd, "%ld", cache_config_default_s) < 0)
     269                 :            :         {
     270                 :          0 :           int ret = -errno;
     271                 :          0 :           close (fd);
     272                 :          0 :           return ret;
     273                 :            :         }
     274                 :            : 
     275                 :         38 :       close (fd);
     276                 :         38 :       return cache_config_default_s;
     277                 :            :     }
     278                 :            : 
     279                 :        258 :   long cache_config;
     280                 :        258 :   FILE *config_file = fdopen(fd, "r");
     281         [ +  - ]:        258 :   if (config_file)
     282                 :            :     {
     283         [ -  + ]:        258 :       if (fscanf(config_file, "%ld", &cache_config) != 1)
     284                 :          0 :         cache_config = cache_config_default_s;
     285                 :        258 :       fclose(config_file);
     286                 :            :     }
     287                 :            :   else
     288                 :          0 :     cache_config = cache_config_default_s;
     289                 :            : 
     290                 :        258 :   close (fd);
     291                 :        258 :   return cache_config;
     292                 :            : }
     293                 :            : 
     294                 :            : /* Delete any files that have been unmodied for a period
     295                 :            :    longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S.  */
     296                 :            : static int
     297                 :        293 : debuginfod_clean_cache(debuginfod_client *c,
     298                 :            :                        char *cache_path, char *interval_path,
     299                 :            :                        char *max_unused_path)
     300                 :            : {
     301                 :        293 :   time_t clean_interval, max_unused_age;
     302                 :        293 :   int rc = -1;
     303                 :        293 :   struct stat st;
     304                 :            : 
     305                 :            :   /* Create new interval file.  */
     306                 :        293 :   rc = debuginfod_config_cache(interval_path,
     307                 :            :                                cache_clean_default_interval_s, &st);
     308         [ +  - ]:        293 :   if (rc < 0)
     309                 :            :     return rc;
     310                 :        293 :   clean_interval = (time_t)rc;
     311                 :            : 
     312                 :            :   /* Check timestamp of interval file to see whether cleaning is necessary.  */
     313         [ +  + ]:        293 :   if (time(NULL) - st.st_mtime < clean_interval)
     314                 :            :     /* Interval has not passed, skip cleaning.  */
     315                 :            :     return 0;
     316                 :            : 
     317                 :            :   /* Update timestamp representing when the cache was last cleaned.
     318                 :            :      Do it at the start to reduce the number of threads trying to do a
     319                 :            :      cleanup simultaneously.  */
     320                 :          1 :   utime (interval_path, NULL);
     321                 :            : 
     322                 :            :   /* Read max unused age value from config file.  */
     323                 :          1 :   rc = debuginfod_config_cache(max_unused_path,
     324                 :            :                                cache_default_max_unused_age_s, &st);
     325         [ +  - ]:          1 :   if (rc < 0)
     326                 :            :     return rc;
     327                 :          1 :   max_unused_age = (time_t)rc;
     328                 :            : 
     329                 :          1 :   char * const dirs[] = { cache_path, NULL, };
     330                 :            : 
     331                 :          1 :   FTS *fts = fts_open(dirs, 0, NULL);
     332         [ -  + ]:          1 :   if (fts == NULL)
     333                 :          0 :     return -errno;
     334                 :            : 
     335                 :          1 :   regex_t re;
     336                 :          1 :   const char * pattern = ".*/[a-f0-9]+(/debuginfo|/executable|/source.*|)$"; /* include dirs */
     337         [ +  - ]:          1 :   if (regcomp (&re, pattern, REG_EXTENDED | REG_NOSUB) != 0)
     338                 :            :     return -ENOMEM;
     339                 :            : 
     340                 :          1 :   FTSENT *f;
     341                 :          1 :   long files = 0;
     342                 :          1 :   time_t now = time(NULL);
     343         [ +  + ]:         12 :   while ((f = fts_read(fts)) != NULL)
     344                 :            :     {
     345                 :            :       /* ignore any files that do not match the pattern.  */
     346         [ +  + ]:         10 :       if (regexec (&re, f->fts_path, 0, NULL, 0) != 0)
     347                 :          8 :         continue;
     348                 :            : 
     349                 :          2 :       files++;
     350         [ -  + ]:          2 :       if (c->progressfn) /* inform/check progress callback */
     351         [ #  # ]:          0 :         if ((c->progressfn) (c, files, 0))
     352                 :            :           break;
     353                 :            : 
     354      [ +  -  + ]:          2 :       switch (f->fts_info)
     355                 :            :         {
     356                 :          0 :         case FTS_F:
     357                 :            :           /* delete file if max_unused_age has been met or exceeded w.r.t. atime.  */
     358         [ #  # ]:          0 :           if (now - f->fts_statp->st_atime >= max_unused_age)
     359                 :          0 :             (void) unlink (f->fts_path);
     360                 :            :           break;
     361                 :            : 
     362                 :          1 :         case FTS_DP:
     363                 :            :           /* Remove if old & empty.  Weaken race against concurrent creation by 
     364                 :            :              checking mtime. */
     365         [ -  + ]:          1 :           if (now - f->fts_statp->st_mtime >= max_unused_age)
     366                 :          1 :             (void) rmdir (f->fts_path);
     367                 :            :           break;
     368                 :            : 
     369                 :            :         default:
     370                 :         11 :           ;
     371                 :            :         }
     372                 :            :     }
     373                 :          1 :   fts_close (fts);
     374                 :          1 :   regfree (&re);
     375                 :            : 
     376                 :          1 :   return 0;
     377                 :            : }
     378                 :            : 
     379                 :            : 
     380                 :            : #define MAX_BUILD_ID_BYTES 64
     381                 :            : 
     382                 :            : 
     383                 :            : static void
     384                 :        293 : add_default_headers(debuginfod_client *client)
     385                 :            : {
     386         [ +  + ]:        293 :   if (client->user_agent_set_p)
     387                 :        118 :     return;
     388                 :            : 
     389                 :            :   /* Compute a User-Agent: string to send.  The more accurately this
     390                 :            :      describes this host, the likelier that the debuginfod servers
     391                 :            :      might be able to locate debuginfo for us. */
     392                 :            : 
     393                 :        175 :   char* utspart = NULL;
     394                 :        175 :   struct utsname uts;
     395                 :        175 :   int rc = 0;
     396                 :        175 :   rc = uname (&uts);
     397         [ +  - ]:        175 :   if (rc == 0)
     398                 :        175 :     rc = asprintf(& utspart, "%s/%s", uts.sysname, uts.machine);
     399         [ -  + ]:        175 :   if (rc < 0)
     400                 :          0 :     utspart = NULL;
     401                 :            : 
     402                 :        175 :   FILE *f = fopen ("/etc/os-release", "r");
     403         [ -  + ]:        175 :   if (f == NULL)
     404                 :          0 :     f = fopen ("/usr/lib/os-release", "r");
     405                 :        175 :   char *id = NULL;
     406                 :        175 :   char *version = NULL;
     407         [ +  - ]:        175 :   if (f != NULL)
     408                 :            :     {
     409         [ +  + ]:        875 :       while (id == NULL || version == NULL)
     410                 :            :         {
     411                 :        700 :           char buf[128];
     412                 :        700 :           char *s = &buf[0];
     413         [ +  - ]:        700 :           if (fgets (s, sizeof(buf), f) == NULL)
     414                 :            :             break;
     415                 :            : 
     416                 :        700 :           int len = strlen (s);
     417         [ -  + ]:        700 :           if (len < 3)
     418                 :          0 :             continue;
     419         [ +  - ]:        700 :           if (s[len - 1] == '\n')
     420                 :            :             {
     421                 :        700 :               s[len - 1] = '\0';
     422                 :        700 :               len--;
     423                 :            :             }
     424                 :            : 
     425                 :        700 :           char *v = strchr (s, '=');
     426   [ +  -  -  + ]:        700 :           if (v == NULL || strlen (v) < 2)
     427                 :          0 :             continue;
     428                 :            : 
     429                 :            :           /* Split var and value. */
     430                 :        700 :           *v = '\0';
     431                 :        700 :           v++;
     432                 :            : 
     433                 :            :           /* Remove optional quotes around value string. */
     434         [ +  + ]:        700 :           if (*v == '"' || *v == '\'')
     435                 :            :             {
     436                 :        350 :               v++;
     437                 :        350 :               s[len - 1] = '\0';
     438                 :            :             }
     439         [ +  + ]:        700 :           if (strcmp (s, "ID") == 0)
     440                 :        175 :             id = strdup (v);
     441         [ +  + ]:        700 :           if (strcmp (s, "VERSION_ID") == 0)
     442                 :        175 :             version = strdup (v);
     443                 :            :         }
     444                 :        175 :       fclose (f);
     445                 :            :     }
     446                 :            : 
     447                 :        175 :   char *ua = NULL;
     448   [ +  -  +  - ]:        350 :   rc = asprintf(& ua, "User-Agent: %s/%s,%s,%s/%s",
     449                 :            :                 PACKAGE_NAME, PACKAGE_VERSION,
     450         [ -  + ]:        175 :                 utspart ?: "",
     451                 :            :                 id ?: "",
     452                 :            :                 version ?: "");
     453         [ -  + ]:        175 :   if (rc < 0)
     454                 :          0 :     ua = NULL;
     455                 :            : 
     456         [ +  - ]:        175 :   if (ua)
     457                 :        175 :     (void) debuginfod_add_http_header (client, ua);
     458                 :            : 
     459                 :        175 :   free (ua);
     460                 :        175 :   free (id);
     461                 :        175 :   free (version);
     462                 :        175 :   free (utspart);
     463                 :            : }
     464                 :            : 
     465                 :            : /* Add HTTP headers found in the given file, one per line. Blank lines or invalid
     466                 :            :  * headers are ignored.
     467                 :            :  */
     468                 :            : static void
     469                 :          0 : add_headers_from_file(debuginfod_client *client, const char* filename)
     470                 :            : {
     471                 :          0 :   int vds = client->verbose_fd;
     472                 :          0 :   FILE *f = fopen (filename, "r");
     473         [ #  # ]:          0 :   if (f == NULL)
     474                 :            :     {
     475         [ #  # ]:          0 :       if (vds >= 0)
     476                 :          0 :         dprintf(vds, "header file %s: %s\n", filename, strerror(errno));
     477                 :          0 :       return;
     478                 :            :     }
     479                 :            : 
     480                 :          0 :   while (1)
     481                 :          0 :     {
     482                 :          0 :       char buf[8192];
     483                 :          0 :       char *s = &buf[0];
     484         [ #  # ]:          0 :       if (feof(f))
     485                 :            :         break;
     486         [ #  # ]:          0 :       if (fgets (s, sizeof(buf), f) == NULL)
     487                 :            :         break;
     488         [ #  # ]:          0 :       for (char *c = s; *c != '\0'; ++c)
     489         [ #  # ]:          0 :         if (!isspace(*c))
     490                 :          0 :           goto nonempty;
     491                 :          0 :       continue;
     492                 :          0 :     nonempty:
     493                 :          0 :       ;
     494                 :          0 :       size_t last = strlen(s)-1;
     495         [ #  # ]:          0 :       if (s[last] == '\n')
     496                 :          0 :         s[last] = '\0';
     497                 :          0 :       int rc = debuginfod_add_http_header(client, s);
     498         [ #  # ]:          0 :       if (rc < 0 && vds >= 0)
     499                 :          0 :         dprintf(vds, "skipping bad header: %s\n", strerror(-rc));
     500                 :            :     }
     501                 :          0 :   fclose (f);
     502                 :            : }
     503                 :            : 
     504                 :            : 
     505                 :            : #define xalloc_str(p, fmt, args...)        \
     506                 :            :   do                                       \
     507                 :            :     {                                      \
     508                 :            :       if (asprintf (&p, fmt, args) < 0)    \
     509                 :            :         {                                  \
     510                 :            :           p = NULL;                        \
     511                 :            :           rc = -ENOMEM;                    \
     512                 :            :           goto out;                        \
     513                 :            :         }                                  \
     514                 :            :     } while (0)
     515                 :            : 
     516                 :            : 
     517                 :            : /* Offer a basic form of progress tracing */
     518                 :            : static int
     519                 :          4 : default_progressfn (debuginfod_client *c, long a, long b)
     520                 :            : {
     521                 :          4 :   const char* url = debuginfod_get_url (c);
     522                 :          4 :   int len = 0;
     523                 :            : 
     524                 :            :   /* We prefer to print the host part of the URL to keep the
     525                 :            :      message short. */
     526                 :          4 :   if (url != NULL)
     527                 :            :     {
     528                 :          2 :       const char* buildid = strstr(url, "buildid/");
     529         [ +  - ]:          2 :       if (buildid != NULL)
     530                 :          2 :         len = (buildid - url);
     531                 :            :       else
     532                 :          0 :         len = strlen(url);
     533                 :            :     }
     534                 :            : 
     535         [ +  + ]:          4 :   if (b == 0 || url==NULL) /* early stage */
     536                 :          6 :     dprintf(STDERR_FILENO,
     537                 :          2 :             "\rDownloading %c", "-/|\\"[a % 4]);
     538         [ -  + ]:          2 :   else if (b < 0) /* download in progress but unknown total length */
     539                 :          0 :     dprintf(STDERR_FILENO,
     540                 :            :             "\rDownloading from %.*s %ld",
     541                 :            :             len, url, a);
     542                 :            :   else /* download in progress, and known total length */
     543                 :          2 :     dprintf(STDERR_FILENO,
     544                 :            :             "\rDownloading from %.*s %ld/%ld",
     545                 :            :             len, url, a, b);
     546                 :          4 :   c->default_progressfn_printed_p = 1;
     547                 :            : 
     548                 :          4 :   return 0;
     549                 :            : }
     550                 :            : 
     551                 :            : /* This is a callback function that receives http response headers in buffer for use
     552                 :            :  * in this program. https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html is the
     553                 :            :  * online documentation.
     554                 :            :  */
     555                 :            : static size_t
     556                 :       1303 : header_callback (char * buffer, size_t size, size_t numitems, void * userdata)
     557                 :            : {
     558                 :       1303 :   struct handle_data *data = (struct handle_data *) userdata;
     559         [ -  + ]:       1303 :   if (size != 1)
     560                 :            :     return 0;
     561   [ +  -  +  + ]:       1303 :   if (data->client && data->client->verbose_fd >= 0)
     562                 :        101 :     dprintf (data->client->verbose_fd, "header %.*s", (int)numitems, buffer);
     563                 :            :   // Some basic checks to ensure the headers received are of the expected format
     564         [ +  + ]:       1303 :   if (strncasecmp(buffer, "X-DEBUGINFOD", 11)
     565         [ +  + ]:        324 :       || buffer[numitems-2] != '\r'
     566         [ +  - ]:        323 :       || buffer[numitems-1] != '\n'
     567         [ -  + ]:        323 :       || (buffer == strstr(buffer, ":")) ){
     568                 :            :     return numitems;
     569                 :            :   }
     570                 :            :   /* Temporary buffer for realloc */
     571                 :        323 :   char *temp = NULL;
     572         [ +  + ]:        323 :   if (data->response_data == NULL)
     573                 :            :     {
     574                 :        120 :       temp = malloc(numitems);
     575         [ -  + ]:        120 :       if (temp == NULL)
     576                 :            :         return 0;
     577                 :            :     }
     578                 :            :   else
     579                 :            :     {
     580                 :        203 :       temp = realloc(data->response_data, data->response_data_size + numitems);
     581         [ -  + ]:        203 :       if (temp == NULL)
     582                 :            :         return 0;
     583                 :            :     }
     584                 :            : 
     585                 :        323 :   memcpy(temp + data->response_data_size, buffer, numitems-1);
     586                 :        323 :   data->response_data = temp;
     587                 :        323 :   data->response_data_size += numitems-1;
     588                 :        323 :   data->response_data[data->response_data_size-1] = '\n';
     589                 :        323 :   data->response_data[data->response_data_size] = '\0';
     590                 :        323 :   return numitems;
     591                 :            : }
     592                 :            : 
     593                 :            : /* Copy SRC to DEST, s,/,#,g */
     594                 :            : 
     595                 :            : static void
     596                 :         42 : path_escape (const char *src, char *dest)
     597                 :            : {
     598                 :         42 :   unsigned q = 0;
     599                 :            : 
     600         [ +  - ]:       1326 :   for (unsigned fi=0; q < PATH_MAX-2; fi++) /* -2, escape is 2 chars.  */
     601   [ +  +  +  + ]:       1326 :     switch (src[fi])
     602                 :            :       {
     603                 :         42 :       case '\0':
     604                 :         42 :         dest[q] = '\0';
     605                 :         42 :         return;
     606                 :        180 :       case '/': /* escape / to prevent dir escape */
     607                 :        180 :         dest[q++]='#';
     608                 :        180 :         dest[q++]='#';
     609                 :        180 :         break;
     610                 :          1 :       case '#': /* escape # to prevent /# vs #/ collisions */
     611                 :          1 :         dest[q++]='#';
     612                 :          1 :         dest[q++]='_';
     613                 :          1 :         break;
     614                 :       1103 :       default:
     615                 :       1103 :         dest[q++]=src[fi];
     616                 :            :       }
     617                 :            : 
     618                 :          0 :   dest[q] = '\0';
     619                 :            : }
     620                 :            : 
     621                 :            : /* Attempt to read an ELF/DWARF section with name SECTION from FD and write
     622                 :            :    it to a separate file in the debuginfod cache.  If successful the absolute
     623                 :            :    path of the separate file containing SECTION will be stored in USR_PATH.
     624                 :            :    FD_PATH is the absolute path for FD.
     625                 :            : 
     626                 :            :    If the section cannot be extracted, then return a negative error code.
     627                 :            :    -ENOENT indicates that the parent file was able to be read but the
     628                 :            :    section name was not found.  -EEXIST indicates that the section was
     629                 :            :    found but had type SHT_NOBITS.  */
     630                 :            : 
     631                 :            : static int
     632                 :          6 : extract_section (int fd, const char *section, char *fd_path, char **usr_path)
     633                 :            : {
     634                 :          6 :   elf_version (EV_CURRENT);
     635                 :            : 
     636                 :          6 :   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
     637         [ +  - ]:          6 :   if (elf == NULL)
     638                 :            :     return -EIO;
     639                 :            : 
     640                 :          6 :   size_t shstrndx;
     641                 :          6 :   int rc = elf_getshdrstrndx (elf, &shstrndx);
     642         [ -  + ]:          6 :   if (rc < 0)
     643                 :            :     {
     644                 :          0 :       rc = -EIO;
     645                 :          0 :       goto out;
     646                 :            :     }
     647                 :            : 
     648                 :          6 :   int sec_fd = -1;
     649                 :          6 :   char *escaped_name = NULL;
     650                 :          6 :   char *sec_path_tmp = NULL;
     651                 :          6 :   Elf_Scn *scn = NULL;
     652                 :            : 
     653                 :            :   /* Try to find the target section and copy the contents into a
     654                 :            :      separate file.  */
     655                 :        206 :   while (true)
     656                 :        100 :     {
     657                 :        106 :       scn = elf_nextscn (elf, scn);
     658         [ -  + ]:        106 :       if (scn == NULL)
     659                 :            :         {
     660                 :          0 :           rc = -ENOENT;
     661                 :          2 :           goto out;
     662                 :            :         }
     663                 :        106 :       GElf_Shdr shdr_storage;
     664                 :        106 :       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_storage);
     665         [ -  + ]:        106 :       if (shdr == NULL)
     666                 :            :         {
     667                 :          0 :           rc = -EIO;
     668                 :          0 :           goto out;
     669                 :            :         }
     670                 :            : 
     671                 :        106 :       const char *scn_name = elf_strptr (elf, shstrndx, shdr->sh_name);
     672         [ -  + ]:        106 :       if (scn_name == NULL)
     673                 :            :         {
     674                 :          0 :           rc = -EIO;
     675                 :          0 :           goto out;
     676                 :            :         }
     677         [ +  + ]:        106 :       if (strcmp (scn_name, section) == 0)
     678                 :            :         {
     679                 :            :           /* We found the desired section.  */
     680         [ +  + ]:          6 :           if (shdr->sh_type == SHT_NOBITS)
     681                 :            :             {
     682                 :          2 :               rc = -EEXIST;
     683                 :          2 :               goto out;
     684                 :            :             }
     685                 :            : 
     686                 :          4 :           Elf_Data *data = NULL;
     687                 :          4 :           data = elf_rawdata (scn, NULL);
     688         [ -  + ]:          4 :           if (data == NULL)
     689                 :            :             {
     690                 :          0 :               rc = -EIO;
     691                 :          0 :               goto out;
     692                 :            :             }
     693                 :            : 
     694         [ -  + ]:          4 :           if (data->d_buf == NULL)
     695                 :            :             {
     696                 :          0 :               rc = -EIO;
     697                 :          0 :               goto out;
     698                 :            :             }
     699                 :            : 
     700                 :            :           /* Compute the absolute filename we'll write the section to.
     701                 :            :              Replace the last component of FD_PATH with the path-escaped
     702                 :            :              section filename.  */
     703                 :          4 :           int i = strlen (fd_path);
     704         [ +  - ]:         46 :           while (i >= 0)
     705                 :            :             {
     706         [ +  + ]:         46 :               if (fd_path[i] == '/')
     707                 :            :                 {
     708                 :          4 :                   fd_path[i] = '\0';
     709                 :          4 :                   break;
     710                 :            :                 }
     711                 :         42 :               --i;
     712                 :            :             }
     713                 :            : 
     714                 :          4 :           escaped_name = malloc (strlen (section) * 2 + 1);
     715         [ -  + ]:          4 :           if (escaped_name == NULL)
     716                 :            :             {
     717                 :          0 :               rc = -ENOMEM;
     718                 :          0 :               goto out;
     719                 :            :             }
     720                 :          4 :           path_escape (section, escaped_name);
     721                 :            : 
     722                 :          4 :           rc = asprintf (&sec_path_tmp, "%s/section-%s.XXXXXX",
     723                 :            :                          fd_path, escaped_name);
     724         [ -  + ]:          4 :           if (rc == -1)
     725                 :            :             {
     726                 :          0 :               rc = -ENOMEM;
     727                 :          0 :               goto out1;
     728                 :            :             }
     729                 :            : 
     730                 :          4 :           sec_fd = mkstemp (sec_path_tmp);
     731         [ -  + ]:          4 :           if (sec_fd < 0)
     732                 :            :             {
     733                 :          0 :               rc = -EIO;
     734                 :          4 :               goto out2;
     735                 :            :             }
     736                 :            : 
     737                 :          4 :           ssize_t res = write_retry (sec_fd, data->d_buf, data->d_size);
     738   [ +  -  -  + ]:          4 :           if (res < 0 || (size_t) res != data->d_size)
     739                 :            :             {
     740                 :          0 :               rc = -EIO;
     741                 :          0 :               goto out3;
     742                 :            :             }
     743                 :            : 
     744                 :            :           /* Success.  Rename tmp file and update USR_PATH.  */
     745                 :          4 :           char *sec_path;
     746         [ -  + ]:          4 :           if (asprintf (&sec_path, "%s/section-%s", fd_path, section) == -1)
     747                 :            :             {
     748                 :          0 :               rc = -ENOMEM;
     749                 :          0 :               goto out3;
     750                 :            :             }
     751                 :            : 
     752                 :          4 :           rc = rename (sec_path_tmp, sec_path);
     753         [ -  + ]:          4 :           if (rc < 0)
     754                 :            :             {
     755                 :          0 :               free (sec_path);
     756                 :          0 :               rc = -EIO;
     757                 :          0 :               goto out3;
     758                 :            :             }
     759                 :            : 
     760         [ +  - ]:          4 :           if (usr_path != NULL)
     761                 :          4 :             *usr_path = sec_path;
     762                 :            :           else
     763                 :          0 :             free (sec_path);
     764                 :          4 :           rc = sec_fd;
     765                 :          4 :           goto out2;
     766                 :            :         }
     767                 :            :     }
     768                 :            : 
     769                 :          0 : out3:
     770                 :          0 :   close (sec_fd);
     771                 :          0 :   unlink (sec_path_tmp);
     772                 :            : 
     773                 :          4 : out2:
     774                 :          4 :   free (sec_path_tmp);
     775                 :            : 
     776                 :          4 : out1:
     777                 :          4 :   free (escaped_name);
     778                 :            : 
     779                 :          6 : out:
     780                 :          6 :   elf_end (elf);
     781                 :          6 :   return rc;
     782                 :            : }
     783                 :            : 
     784                 :            : /* Search TARGET_CACHE_DIR for a debuginfo or executable file containing
     785                 :            :    an ELF/DWARF section with name SCN_NAME.  If found, extract the section
     786                 :            :    to a separate file in TARGET_CACHE_DIR and return a file descriptor
     787                 :            :    for the section file. The path for this file will be stored in USR_PATH.
     788                 :            :    Return a negative errno if unsuccessful.  -ENOENT indicates that SCN_NAME
     789                 :            :    is confirmed to not exist.  */
     790                 :            : 
     791                 :            : static int
     792                 :          8 : cache_find_section (const char *scn_name, const char *target_cache_dir,
     793                 :            :                     char **usr_path)
     794                 :            : {
     795                 :          8 :   int debug_fd;
     796                 :          8 :   int rc = -EEXIST;
     797                 :          8 :   char parent_path[PATH_MAX];
     798                 :            : 
     799                 :            :   /* Check the debuginfo first.  */
     800                 :          8 :   snprintf (parent_path, PATH_MAX, "%s/debuginfo", target_cache_dir);
     801                 :          8 :   debug_fd = open (parent_path, O_RDONLY);
     802         [ +  + ]:          8 :   if (debug_fd >= 0)
     803                 :            :     {
     804                 :          4 :       rc = extract_section (debug_fd, scn_name, parent_path, usr_path);
     805                 :          4 :       close (debug_fd);
     806                 :            :     }
     807                 :            : 
     808                 :            :   /* If the debuginfo file couldn't be found or the section type was
     809                 :            :      SHT_NOBITS, check the executable.  */
     810         [ +  + ]:          4 :   if (rc == -EEXIST)
     811                 :            :     {
     812                 :          6 :       snprintf (parent_path, PATH_MAX, "%s/executable", target_cache_dir);
     813                 :          6 :       int exec_fd = open (parent_path, O_RDONLY);
     814                 :            : 
     815         [ +  + ]:          6 :       if (exec_fd >= 0)
     816                 :            :         {
     817                 :          2 :           rc = extract_section (exec_fd, scn_name, parent_path, usr_path);
     818                 :          2 :           close (exec_fd);
     819                 :            : 
     820                 :            :           /* Don't return -ENOENT if the debuginfo wasn't opened.  The
     821                 :            :              section may exist in the debuginfo but not the executable.  */
     822         [ -  + ]:          2 :           if (debug_fd < 0 && rc == -ENOENT)
     823                 :          0 :             rc = -EREMOTE;
     824                 :            :         }
     825                 :            :     }
     826                 :            : 
     827                 :          8 :   return rc;
     828                 :            : }
     829                 :            : 
     830                 :            : /* Query each of the server URLs found in $DEBUGINFOD_URLS for the file
     831                 :            :    with the specified build-id and type (debuginfo, executable, source or
     832                 :            :    section).  If type is source, then type_arg should be a filename.  If
     833                 :            :    type is section, then type_arg should be the name of an ELF/DWARF
     834                 :            :    section.  Otherwise type_arg may be NULL.  Return a file descriptor
     835                 :            :    for the target if successful, otherwise return an error code.
     836                 :            : */
     837                 :            : static int
     838                 :        412 : debuginfod_query_server (debuginfod_client *c,
     839                 :            :                          const unsigned char *build_id,
     840                 :            :                          int build_id_len,
     841                 :            :                          const char *type,
     842                 :            :                          const char *type_arg,
     843                 :            :                          char **path)
     844                 :            : {
     845                 :        412 :   char *server_urls;
     846                 :        412 :   char *urls_envvar;
     847                 :        412 :   const char *section = NULL;
     848                 :        412 :   const char *filename = NULL;
     849                 :        412 :   char *cache_path = NULL;
     850                 :        412 :   char *maxage_path = NULL;
     851                 :        412 :   char *interval_path = NULL;
     852                 :        412 :   char *cache_miss_path = NULL;
     853                 :        412 :   char *target_cache_dir = NULL;
     854                 :        412 :   char *target_cache_path = NULL;
     855                 :        412 :   char *target_cache_tmppath = NULL;
     856                 :        412 :   char suffix[PATH_MAX + 1]; /* +1 for zero terminator.  */
     857                 :        412 :   char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1];
     858                 :        412 :   int vfd = c->verbose_fd;
     859                 :        412 :   int rc;
     860                 :            : 
     861                 :        412 :   c->progressfn_cancel = false;
     862                 :            : 
     863         [ +  + ]:        412 :   if (strcmp (type, "source") == 0)
     864                 :            :     filename = type_arg;
     865         [ +  + ]:        380 :   else if (strcmp (type, "section") == 0)
     866                 :            :     {
     867                 :          8 :       section = type_arg;
     868         [ +  - ]:          8 :       if (section == NULL)
     869                 :            :         return -EINVAL;
     870                 :            :     }
     871                 :            : 
     872         [ +  + ]:        412 :   if (vfd >= 0)
     873                 :            :     {
     874                 :         18 :       dprintf (vfd, "debuginfod_find_%s ", type);
     875         [ +  + ]:         18 :       if (build_id_len == 0) /* expect clean hexadecimal */
     876                 :         13 :         dprintf (vfd, "%s", (const char *) build_id);
     877                 :            :       else
     878         [ +  + ]:        105 :         for (int i = 0; i < build_id_len; i++)
     879                 :        100 :           dprintf (vfd, "%02x", build_id[i]);
     880         [ +  + ]:         18 :       if (filename != NULL)
     881                 :          1 :         dprintf (vfd, " %s\n", filename);
     882                 :         18 :       dprintf (vfd, "\n");
     883                 :            :     }
     884                 :            : 
     885                 :            :   /* Is there any server we can query?  If not, don't do any work,
     886                 :            :      just return with ENOSYS.  Don't even access the cache.  */
     887                 :        412 :   urls_envvar = getenv(DEBUGINFOD_URLS_ENV_VAR);
     888         [ +  + ]:        412 :   if (vfd >= 0)
     889         [ -  + ]:         18 :     dprintf (vfd, "server urls \"%s\"\n",
     890                 :            :              urls_envvar != NULL ? urls_envvar : "");
     891   [ +  +  +  + ]:        412 :   if (urls_envvar == NULL || urls_envvar[0] == '\0')
     892                 :            :     {
     893                 :        119 :       rc = -ENOSYS;
     894                 :        119 :       goto out;
     895                 :            :     }
     896                 :            : 
     897                 :            :   /* Clear the obsolete data from a previous _find operation. */
     898                 :        293 :   free (c->url);
     899                 :        293 :   c->url = NULL;
     900                 :        293 :   free (c->winning_headers);
     901                 :        293 :   c->winning_headers = NULL;
     902                 :            : 
     903                 :            :   /* PR 27982: Add max size if DEBUGINFOD_MAXSIZE is set. */
     904                 :        293 :   long maxsize = 0;
     905                 :        293 :   const char *maxsize_envvar;
     906                 :        293 :   maxsize_envvar = getenv(DEBUGINFOD_MAXSIZE_ENV_VAR);
     907         [ +  + ]:        293 :   if (maxsize_envvar != NULL)
     908                 :          1 :     maxsize = atol (maxsize_envvar);
     909                 :            : 
     910                 :            :   /* PR 27982: Add max time if DEBUGINFOD_MAXTIME is set. */
     911                 :        293 :   long maxtime = 0;
     912                 :        293 :   const char *maxtime_envvar;
     913                 :        293 :   maxtime_envvar = getenv(DEBUGINFOD_MAXTIME_ENV_VAR);
     914         [ +  + ]:        293 :   if (maxtime_envvar != NULL)
     915                 :          1 :     maxtime = atol (maxtime_envvar);
     916         [ +  + ]:        293 :   if (maxtime && vfd >= 0)
     917                 :          1 :     dprintf(vfd, "using max time %lds\n", maxtime);
     918                 :            : 
     919                 :        293 :   const char *headers_file_envvar;
     920                 :        293 :   headers_file_envvar = getenv(DEBUGINFOD_HEADERS_FILE_ENV_VAR);
     921         [ -  + ]:        293 :   if (headers_file_envvar != NULL)
     922                 :          0 :     add_headers_from_file(c, headers_file_envvar);
     923                 :            : 
     924                 :            :   /* Maxsize is valid*/
     925         [ +  + ]:        293 :   if (maxsize > 0)
     926                 :            :     {
     927         [ +  - ]:          1 :       if (vfd)
     928                 :          1 :         dprintf (vfd, "using max size %ldB\n", maxsize);
     929                 :          1 :       char *size_header = NULL;
     930                 :          1 :       rc = asprintf (&size_header, "X-DEBUGINFOD-MAXSIZE: %ld", maxsize);
     931         [ -  + ]:          1 :       if (rc < 0)
     932                 :            :         {
     933                 :          0 :           rc = -ENOMEM;
     934                 :          0 :           goto out;
     935                 :            :         }
     936                 :          1 :       rc = debuginfod_add_http_header(c, size_header);
     937                 :          1 :       free(size_header);
     938         [ -  + ]:          1 :       if (rc < 0)
     939                 :          0 :         goto out;
     940                 :            :     }
     941                 :        293 :   add_default_headers(c);
     942                 :            : 
     943                 :            :   /* Copy lowercase hex representation of build_id into buf.  */
     944         [ +  + ]:        293 :   if (vfd >= 0)
     945                 :         18 :     dprintf (vfd, "checking build-id\n");
     946   [ +  -  +  + ]:        293 :   if ((build_id_len >= MAX_BUILD_ID_BYTES) ||
     947                 :        287 :       (build_id_len == 0 &&
     948         [ -  + ]:        287 :        strlen ((const char *) build_id) > MAX_BUILD_ID_BYTES*2))
     949                 :            :     {
     950                 :          0 :       rc = -EINVAL;
     951                 :          0 :       goto out;
     952                 :            :     }
     953                 :            : 
     954         [ +  + ]:        293 :   if (build_id_len == 0) /* expect clean hexadecimal */
     955                 :        287 :     strcpy (build_id_bytes, (const char *) build_id);
     956                 :            :   else
     957         [ +  + ]:        126 :     for (int i = 0; i < build_id_len; i++)
     958                 :        120 :       sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]);
     959                 :            : 
     960         [ +  + ]:        293 :   if (filename != NULL)
     961                 :            :     {
     962         [ +  + ]:         30 :       if (vfd >= 0)
     963                 :          1 :         dprintf (vfd, "checking filename\n");
     964         [ -  + ]:         30 :       if (filename[0] != '/') // must start with /
     965                 :            :         {
     966                 :          0 :           rc = -EINVAL;
     967                 :          0 :           goto out;
     968                 :            :         }
     969                 :            : 
     970                 :         30 :       path_escape (filename, suffix);
     971                 :            :       /* If the DWARF filenames are super long, this could exceed
     972                 :            :          PATH_MAX and truncate/collide.  Oh well, that'll teach
     973                 :            :          them! */
     974                 :            :     }
     975         [ +  + ]:        263 :   else if (section != NULL)
     976                 :          8 :     path_escape (section, suffix);
     977                 :            :   else
     978                 :        255 :     suffix[0] = '\0';
     979                 :            : 
     980   [ +  +  +  + ]:        293 :   if (suffix[0] != '\0' && vfd >= 0)
     981                 :          9 :     dprintf (vfd, "suffix %s\n", suffix);
     982                 :            : 
     983                 :            :   /* set paths needed to perform the query
     984                 :            : 
     985                 :            :      example format
     986                 :            :      cache_path:        $HOME/.cache
     987                 :            :      target_cache_dir:  $HOME/.cache/0123abcd
     988                 :            :      target_cache_path: $HOME/.cache/0123abcd/debuginfo
     989                 :            :      target_cache_path: $HOME/.cache/0123abcd/source#PATH#TO#SOURCE ?
     990                 :            : 
     991                 :            :      $XDG_CACHE_HOME takes priority over $HOME/.cache.
     992                 :            :      $DEBUGINFOD_CACHE_PATH takes priority over $HOME/.cache and $XDG_CACHE_HOME.
     993                 :            :   */
     994                 :            : 
     995                 :            :   /* Determine location of the cache. The path specified by the debuginfod
     996                 :            :      cache environment variable takes priority.  */
     997                 :        293 :   char *cache_var = getenv(DEBUGINFOD_CACHE_PATH_ENV_VAR);
     998   [ +  -  +  + ]:        293 :   if (cache_var != NULL && strlen (cache_var) > 0)
     999         [ -  + ]:        289 :     xalloc_str (cache_path, "%s", cache_var);
    1000                 :            :   else
    1001                 :            :     {
    1002                 :            :       /* If a cache already exists in $HOME ('/' if $HOME isn't set), then use
    1003                 :            :          that. Otherwise use the XDG cache directory naming format.  */
    1004   [ -  +  -  + ]:          8 :       xalloc_str (cache_path, "%s/%s", getenv ("HOME") ?: "/", cache_default_name);
    1005                 :            : 
    1006                 :          4 :       struct stat st;
    1007         [ +  + ]:          4 :       if (stat (cache_path, &st) < 0)
    1008                 :            :         {
    1009                 :          3 :           char cachedir[PATH_MAX];
    1010                 :          3 :           char *xdg = getenv ("XDG_CACHE_HOME");
    1011                 :            : 
    1012   [ +  -  +  + ]:          3 :           if (xdg != NULL && strlen (xdg) > 0)
    1013                 :          1 :             snprintf (cachedir, PATH_MAX, "%s", xdg);
    1014                 :            :           else
    1015         [ -  + ]:          2 :             snprintf (cachedir, PATH_MAX, "%s/.cache", getenv ("HOME") ?: "/");
    1016                 :            : 
    1017                 :            :           /* Create XDG cache directory if it doesn't exist.  */
    1018         [ +  + ]:          3 :           if (stat (cachedir, &st) == 0)
    1019                 :            :             {
    1020         [ -  + ]:          1 :               if (! S_ISDIR (st.st_mode))
    1021                 :            :                 {
    1022                 :          0 :                   rc = -EEXIST;
    1023                 :          0 :                   goto out;
    1024                 :            :                 }
    1025                 :            :             }
    1026                 :            :           else
    1027                 :            :             {
    1028                 :          2 :               rc = mkdir (cachedir, 0700);
    1029                 :            : 
    1030                 :            :               /* Also check for EEXIST and S_ISDIR in case another client just
    1031                 :            :                  happened to create the cache.  */
    1032         [ -  + ]:          2 :               if (rc < 0
    1033         [ #  # ]:          0 :                   && (errno != EEXIST
    1034         [ #  # ]:          0 :                       || stat (cachedir, &st) != 0
    1035         [ #  # ]:          0 :                       || ! S_ISDIR (st.st_mode)))
    1036                 :            :                 {
    1037                 :          0 :                   rc = -errno;
    1038                 :          0 :                   goto out;
    1039                 :            :                 }
    1040                 :            :             }
    1041                 :            : 
    1042                 :          3 :           free (cache_path);
    1043         [ -  + ]:          3 :           xalloc_str (cache_path, "%s/%s", cachedir, cache_xdg_name);
    1044                 :            :         }
    1045                 :            :     }
    1046                 :            : 
    1047         [ -  + ]:        293 :   xalloc_str (target_cache_dir, "%s/%s", cache_path, build_id_bytes);
    1048         [ +  + ]:        293 :   if (section != NULL)
    1049         [ -  + ]:          8 :     xalloc_str (target_cache_path, "%s/%s-%s", target_cache_dir, type, suffix);
    1050                 :            :   else
    1051         [ -  + ]:        285 :     xalloc_str (target_cache_path, "%s/%s%s", target_cache_dir, type, suffix);
    1052         [ -  + ]:        293 :   xalloc_str (target_cache_tmppath, "%s.XXXXXX", target_cache_path);
    1053                 :            : 
    1054                 :            :   /* XXX combine these */
    1055         [ -  + ]:        293 :   xalloc_str (interval_path, "%s/%s", cache_path, cache_clean_interval_filename);
    1056         [ -  + ]:        293 :   xalloc_str (cache_miss_path, "%s/%s", cache_path, cache_miss_filename);
    1057         [ -  + ]:        293 :   xalloc_str (maxage_path, "%s/%s", cache_path, cache_max_unused_age_filename);
    1058                 :            : 
    1059         [ +  + ]:        293 :   if (vfd >= 0)
    1060                 :         18 :     dprintf (vfd, "checking cache dir %s\n", cache_path);
    1061                 :            : 
    1062                 :            :   /* Make sure cache dir exists. debuginfo_clean_cache will then make
    1063                 :            :      sure the interval, cache_miss and maxage files exist.  */
    1064         [ +  + ]:        293 :   if (mkdir (cache_path, ACCESSPERMS) != 0
    1065         [ -  + ]:        263 :       && errno != EEXIST)
    1066                 :            :     {
    1067                 :          0 :       rc = -errno;
    1068                 :          0 :       goto out;
    1069                 :            :     }
    1070                 :            : 
    1071                 :        293 :   rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path);
    1072         [ -  + ]:        293 :   if (rc != 0)
    1073                 :          0 :     goto out;
    1074                 :            : 
    1075                 :            :   /* Check if the target is already in the cache. */
    1076                 :        293 :   int fd = open(target_cache_path, O_RDONLY);
    1077         [ +  + ]:        293 :   if (fd >= 0)
    1078                 :            :     {
    1079                 :         37 :       struct stat st;
    1080         [ -  + ]:         37 :       if (fstat(fd, &st) != 0)
    1081                 :            :         {
    1082                 :          0 :           rc = -errno;
    1083                 :          0 :           close (fd);
    1084                 :         36 :           goto out;
    1085                 :            :         }
    1086                 :            : 
    1087                 :            :       /* If the file is non-empty, then we are done. */
    1088         [ +  + ]:         37 :       if (st.st_size > 0)
    1089                 :            :         {
    1090         [ +  - ]:         35 :           if (path != NULL)
    1091                 :            :             {
    1092                 :         35 :               *path = strdup(target_cache_path);
    1093         [ -  + ]:         35 :               if (*path == NULL)
    1094                 :            :                 {
    1095                 :          0 :                   rc = -errno;
    1096                 :          0 :                   close (fd);
    1097                 :          0 :                   goto out;
    1098                 :            :                 }
    1099                 :            :             }
    1100                 :            :           /* Success!!!! */
    1101                 :         35 :           rc = fd;
    1102                 :         35 :           goto out;
    1103                 :            :         }
    1104                 :            :       else
    1105                 :            :         {
    1106                 :            :           /* The file is empty. Attempt to download only if enough time
    1107                 :            :              has passed since the last attempt. */
    1108                 :          2 :           time_t cache_miss;
    1109                 :          2 :           time_t target_mtime = st.st_mtime;
    1110                 :            : 
    1111                 :          2 :           close(fd); /* no need to hold onto the negative-hit file descriptor */
    1112                 :            :           
    1113                 :          2 :           rc = debuginfod_config_cache(cache_miss_path,
    1114                 :            :                                        cache_miss_default_s, &st);
    1115         [ -  + ]:          2 :           if (rc < 0)
    1116                 :          0 :             goto out;
    1117                 :            : 
    1118                 :          2 :           cache_miss = (time_t)rc;
    1119         [ +  + ]:          2 :           if (time(NULL) - target_mtime <= cache_miss)
    1120                 :            :             {
    1121                 :          1 :               rc = -ENOENT;
    1122                 :          1 :               goto out;
    1123                 :            :             }
    1124                 :            :           else
    1125                 :            :             /* TOCTOU non-problem: if another task races, puts a working
    1126                 :            :                download or an empty file in its place, unlinking here just
    1127                 :            :                means WE will try to download again as uncached. */
    1128                 :          1 :             unlink(target_cache_path);
    1129                 :            :         }
    1130                 :            :     }
    1131         [ -  + ]:        256 :   else if (errno == EACCES)
    1132                 :            :     /* Ensure old 000-permission files are not lingering in the cache. */
    1133                 :          0 :     unlink(target_cache_path);
    1134                 :            : 
    1135         [ +  + ]:        257 :   if (section != NULL)
    1136                 :            :     {
    1137                 :            :       /* Try to extract the section from a cached file before querying
    1138                 :            :          any servers.  */
    1139                 :          8 :       rc = cache_find_section (section, target_cache_dir, path);
    1140                 :            : 
    1141                 :            :       /* If the section was found or confirmed to not exist, then we
    1142                 :            :          are done.  */
    1143         [ +  + ]:          8 :       if (rc >= 0 || rc == -ENOENT)
    1144                 :          4 :         goto out;
    1145                 :            :     }
    1146                 :            : 
    1147                 :        253 :   long timeout = default_timeout;
    1148                 :        253 :   const char* timeout_envvar = getenv(DEBUGINFOD_TIMEOUT_ENV_VAR);
    1149         [ -  + ]:        253 :   if (timeout_envvar != NULL)
    1150                 :          0 :     timeout = atoi (timeout_envvar);
    1151                 :            : 
    1152         [ +  + ]:        253 :   if (vfd >= 0)
    1153                 :         14 :     dprintf (vfd, "using timeout %ld\n", timeout);
    1154                 :            : 
    1155                 :            :   /* make a copy of the envvar so it can be safely modified.  */
    1156                 :        253 :   server_urls = strdup(urls_envvar);
    1157         [ -  + ]:        253 :   if (server_urls == NULL)
    1158                 :            :     {
    1159                 :          0 :       rc = -ENOMEM;
    1160                 :          0 :       goto out;
    1161                 :            :     }
    1162                 :            :   /* thereafter, goto out0 on error*/
    1163                 :            : 
    1164                 :            :   /* Because of a race with cache cleanup / rmdir, try to mkdir/mkstemp up to twice. */
    1165         [ +  - ]:        253 :   for(int i=0; i<2; i++) {
    1166                 :            :     /* (re)create target directory in cache */
    1167                 :        253 :     (void) mkdir(target_cache_dir, 0700); /* files will be 0400 later */
    1168                 :            : 
    1169                 :            :     /* NB: write to a temporary file first, to avoid race condition of
    1170                 :            :        multiple clients checking the cache, while a partially-written or empty
    1171                 :            :        file is in there, being written from libcurl. */
    1172                 :        253 :     fd = mkstemp (target_cache_tmppath);
    1173         [ -  + ]:        253 :     if (fd >= 0) break;
    1174                 :            :   }
    1175         [ -  + ]:        253 :   if (fd < 0) /* Still failed after two iterations. */
    1176                 :            :     {
    1177                 :          0 :       rc = -errno;
    1178                 :          0 :       goto out0;
    1179                 :            :     }
    1180                 :            : 
    1181                 :            :   /* Initialize the memory to zero */
    1182                 :        253 :   char *strtok_saveptr;
    1183                 :        253 :   char **server_url_list = NULL;
    1184                 :        253 :   char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr);
    1185                 :            :   /* Count number of URLs.  */
    1186                 :        253 :   int num_urls = 0;
    1187                 :            : 
    1188         [ +  + ]:        533 :   while (server_url != NULL)
    1189                 :            :     {
    1190                 :            :       /* PR 27983: If the url is already set to be used use, skip it */
    1191                 :        280 :       char *slashbuildid;
    1192   [ +  -  +  + ]:        280 :       if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/')
    1193                 :            :         slashbuildid = "buildid";
    1194                 :            :       else
    1195                 :        250 :         slashbuildid = "/buildid";
    1196                 :            : 
    1197                 :        280 :       char *tmp_url;
    1198         [ -  + ]:        280 :       if (asprintf(&tmp_url, "%s%s", server_url, slashbuildid) == -1)
    1199                 :            :         {
    1200                 :          0 :           rc = -ENOMEM;
    1201                 :          0 :           goto out1;
    1202                 :            :         }
    1203                 :            :       int url_index;
    1204         [ +  + ]:        341 :       for (url_index = 0; url_index < num_urls; ++url_index)
    1205                 :            :         {
    1206         [ +  + ]:         63 :           if(strcmp(tmp_url, server_url_list[url_index]) == 0)
    1207                 :            :             {
    1208                 :            :               url_index = -1;
    1209                 :            :               break;
    1210                 :            :             }
    1211                 :            :         }
    1212         [ +  + ]:        280 :       if (url_index == -1)
    1213                 :            :         {
    1214         [ +  - ]:          2 :           if (vfd >= 0)
    1215                 :          2 :             dprintf(vfd, "duplicate url: %s, skipping\n", tmp_url);
    1216                 :          2 :           free(tmp_url);
    1217                 :            :         }
    1218                 :            :       else
    1219                 :            :         {
    1220                 :        278 :           num_urls++;
    1221                 :        278 :           char ** realloc_ptr;
    1222                 :        278 :           realloc_ptr = reallocarray(server_url_list, num_urls,
    1223                 :            :                                          sizeof(char*));
    1224         [ -  + ]:        278 :           if (realloc_ptr == NULL)
    1225                 :            :             {
    1226                 :          0 :               free (tmp_url);
    1227                 :          0 :               rc = -ENOMEM;
    1228                 :          0 :               goto out1;
    1229                 :            :             }
    1230                 :        278 :           server_url_list = realloc_ptr;
    1231                 :        278 :           server_url_list[num_urls-1] = tmp_url;
    1232                 :            :         }
    1233                 :        280 :       server_url = strtok_r(NULL, url_delim, &strtok_saveptr);
    1234                 :            :     }
    1235                 :            : 
    1236                 :        253 :   int retry_limit = default_retry_limit;
    1237                 :        253 :   const char* retry_limit_envvar = getenv(DEBUGINFOD_RETRY_LIMIT_ENV_VAR);
    1238         [ +  + ]:        253 :   if (retry_limit_envvar != NULL)
    1239                 :          2 :     retry_limit = atoi (retry_limit_envvar);
    1240                 :            : 
    1241                 :        253 :   CURLM *curlm = c->server_mhandle;
    1242         [ -  + ]:        253 :   assert (curlm != NULL);
    1243                 :            : 
    1244                 :            :   /* Tracks which handle should write to fd. Set to the first
    1245                 :            :      handle that is ready to write the target file to the cache.  */
    1246                 :        253 :   CURL *target_handle = NULL;
    1247                 :        253 :   struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls);
    1248         [ -  + ]:        253 :   if (data == NULL)
    1249                 :            :     {
    1250                 :          0 :       rc = -ENOMEM;
    1251                 :          0 :       goto out1;
    1252                 :            :     }
    1253                 :            : 
    1254                 :            :   /* thereafter, goto out2 on error.  */
    1255                 :            : 
    1256                 :            :  /*The beginning of goto block query_in_parallel.*/
    1257                 :        253 :  query_in_parallel:
    1258                 :        483 :   rc = -ENOENT; /* Reset rc to default.*/
    1259                 :            : 
    1260                 :            :   /* Initialize handle_data with default values. */
    1261         [ +  + ]:        991 :   for (int i = 0; i < num_urls; i++)
    1262                 :            :     {
    1263                 :        508 :       data[i].handle = NULL;
    1264                 :        508 :       data[i].fd = -1;
    1265                 :        508 :       data[i].errbuf[0] = '\0';
    1266                 :        508 :       data[i].response_data = NULL;
    1267                 :        508 :       data[i].response_data_size = 0;
    1268                 :            :     }
    1269                 :            : 
    1270                 :        483 :   char *escaped_string = NULL;
    1271                 :        483 :   size_t escaped_strlen = 0;
    1272         [ +  + ]:        483 :   if (filename)
    1273                 :            :     {
    1274                 :         29 :       escaped_string = curl_easy_escape(&target_handle, filename+1, 0);
    1275         [ -  + ]:         29 :       if (!escaped_string)
    1276                 :            :         {
    1277                 :          0 :           rc = -ENOMEM;
    1278                 :          0 :           goto out2;
    1279                 :            :         }
    1280                 :         29 :       char *loc = escaped_string;
    1281                 :         29 :       escaped_strlen = strlen(escaped_string);
    1282         [ +  + ]:        175 :       while ((loc = strstr(loc, "%2F")))
    1283                 :            :         {
    1284                 :        146 :           loc[0] = '/';
    1285                 :            :           //pull the string back after replacement
    1286                 :            :           // loc-escaped_string finds the distance from the origin to the new location
    1287                 :            :           // - 2 accounts for the 2F which remain and don't need to be measured.
    1288                 :            :           // The two above subtracted from escaped_strlen yields the remaining characters
    1289                 :            :           // in the string which we want to pull back
    1290                 :        146 :           memmove(loc+1, loc+3,escaped_strlen - (loc-escaped_string) - 2);
    1291                 :            :           //Because the 2F was overwritten in the memmove (as desired) escaped_strlen is
    1292                 :            :           // now two shorter.
    1293                 :        146 :           escaped_strlen -= 2;
    1294                 :            :         }
    1295                 :            :     }
    1296                 :            :   /* Initialize each handle.  */
    1297         [ +  + ]:        991 :   for (int i = 0; i < num_urls; i++)
    1298                 :            :     {
    1299         [ +  - ]:        508 :       if ((server_url = server_url_list[i]) == NULL)
    1300                 :            :         break;
    1301         [ +  + ]:        508 :       if (vfd >= 0)
    1302                 :         25 :         dprintf (vfd, "init server %d %s\n", i, server_url);
    1303                 :            : 
    1304                 :        508 :       data[i].fd = fd;
    1305                 :        508 :       data[i].target_handle = &target_handle;
    1306                 :        508 :       data[i].handle = curl_easy_init();
    1307         [ -  + ]:        508 :       if (data[i].handle == NULL)
    1308                 :            :         {
    1309         [ #  # ]:          0 :           if (filename) curl_free (escaped_string);
    1310                 :          0 :           rc = -ENETUNREACH;
    1311                 :          0 :           goto out2;
    1312                 :            :         }
    1313                 :        508 :       data[i].client = c;
    1314                 :            : 
    1315         [ +  + ]:        508 :       if (filename) /* must start with / */
    1316                 :            :         {
    1317                 :            :           /* PR28034 escape characters in completed url to %hh format. */
    1318                 :         29 :           snprintf(data[i].url, PATH_MAX, "%s/%s/%s/%s", server_url,
    1319                 :            :                    build_id_bytes, type, escaped_string);
    1320                 :            :         }
    1321         [ +  + ]:        479 :       else if (section)
    1322                 :          4 :         snprintf(data[i].url, PATH_MAX, "%s/%s/%s/%s", server_url,
    1323                 :            :                  build_id_bytes, type, section);
    1324                 :            :       else
    1325                 :        475 :         snprintf(data[i].url, PATH_MAX, "%s/%s/%s", server_url, build_id_bytes, type);
    1326         [ +  + ]:        508 :       if (vfd >= 0)
    1327                 :         25 :         dprintf (vfd, "url %d %s\n", i, data[i].url);
    1328                 :            : 
    1329                 :            :       /* Some boilerplate for checking curl_easy_setopt.  */
    1330                 :            : #define curl_easy_setopt_ck(H,O,P) do {                 \
    1331                 :            :       CURLcode curl_res = curl_easy_setopt (H,O,P);     \
    1332                 :            :       if (curl_res != CURLE_OK)                         \
    1333                 :            :         {                                               \
    1334                 :            :           if (vfd >= 0)                                      \
    1335                 :            :             dprintf (vfd,                               \
    1336                 :            :                      "Bad curl_easy_setopt: %s\n",    \
    1337                 :            :                      curl_easy_strerror(curl_res));     \
    1338                 :            :           rc = -EINVAL;                                 \
    1339                 :            :           goto out2;                                    \
    1340                 :            :         }                                               \
    1341                 :            :       } while (0)
    1342                 :            : 
    1343                 :            :       /* Only allow http:// + https:// + file:// so we aren't being
    1344                 :            :          redirected to some unsupported protocol.  */
    1345                 :            : #if CURL_AT_LEAST_VERSION(7, 85, 0)
    1346   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_PROTOCOLS_STR,
    1347                 :            :                           "http,https,file");
    1348                 :            : #else
    1349                 :            :       curl_easy_setopt_ck(data[i].handle, CURLOPT_PROTOCOLS,
    1350                 :            :                           (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FILE));
    1351                 :            : #endif
    1352   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_URL, data[i].url);
    1353         [ +  + ]:        508 :       if (vfd >= 0)
    1354         [ -  + ]:         25 :         curl_easy_setopt_ck(data[i].handle, CURLOPT_ERRORBUFFER,
    1355                 :            :                             data[i].errbuf);
    1356   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle,
    1357                 :            :                           CURLOPT_WRITEFUNCTION,
    1358                 :            :                           debuginfod_write_callback);
    1359   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]);
    1360         [ +  - ]:        508 :       if (timeout > 0)
    1361                 :            :         {
    1362                 :            :           /* Make sure there is at least some progress,
    1363                 :            :              try to get at least 100K per timeout seconds.  */
    1364   [ -  +  -  - ]:        508 :           curl_easy_setopt_ck (data[i].handle, CURLOPT_LOW_SPEED_TIME,
    1365                 :            :                                timeout);
    1366   [ -  +  -  - ]:        508 :           curl_easy_setopt_ck (data[i].handle, CURLOPT_LOW_SPEED_LIMIT,
    1367                 :            :                                100 * 1024L);
    1368                 :            :         }
    1369   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_FILETIME, (long) 1);
    1370   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1);
    1371   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_FAILONERROR, (long) 1);
    1372   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_NOSIGNAL, (long) 1);
    1373   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_HEADERFUNCTION,
    1374                 :            :                           header_callback);
    1375   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_HEADERDATA,
    1376                 :            :                           (void *) &(data[i]));
    1377                 :            : #if LIBCURL_VERSION_NUM >= 0x072a00 /* 7.42.0 */
    1378   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_PATH_AS_IS, (long) 1);
    1379                 :            : #else
    1380                 :            :       /* On old curl; no big deal, canonicalization here is almost the
    1381                 :            :          same, except perhaps for ? # type decorations at the tail. */
    1382                 :            : #endif
    1383   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_AUTOREFERER, (long) 1);
    1384   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_ACCEPT_ENCODING, "");
    1385   [ -  +  -  - ]:        508 :       curl_easy_setopt_ck(data[i].handle, CURLOPT_HTTPHEADER, c->headers);
    1386                 :            : 
    1387                 :        508 :       curl_multi_add_handle(curlm, data[i].handle);
    1388                 :            :     }
    1389                 :            : 
    1390         [ +  + ]:        483 :   if (filename) curl_free(escaped_string);
    1391                 :            :   /* Query servers in parallel.  */
    1392         [ +  + ]:        483 :   if (vfd >= 0)
    1393                 :         24 :     dprintf (vfd, "query %d urls in parallel\n", num_urls);
    1394                 :        483 :   int still_running;
    1395                 :        483 :   long loops = 0;
    1396                 :        483 :   int committed_to = -1;
    1397                 :        483 :   bool verbose_reported = false;
    1398                 :        483 :   struct timespec start_time, cur_time;
    1399                 :            : 
    1400                 :        483 :   free (c->winning_headers);
    1401                 :        483 :   c->winning_headers = NULL;
    1402   [ +  +  -  + ]:        483 :   if ( maxtime > 0 && clock_gettime(CLOCK_MONOTONIC_RAW, &start_time) == -1)
    1403                 :            :     {
    1404                 :          0 :       rc = -errno;
    1405                 :          0 :       goto out2;
    1406                 :            :     }
    1407                 :       3800 :   long delta = 0;
    1408                 :       3800 :   do
    1409                 :            :     {
    1410                 :            :       /* Check to see how long querying is taking. */
    1411         [ +  + ]:       3800 :       if (maxtime > 0)
    1412                 :            :         {
    1413         [ -  + ]:          2 :           if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time) == -1)
    1414                 :            :             {
    1415                 :          0 :               rc = -errno;
    1416                 :          0 :               goto out2;
    1417                 :            :             }
    1418                 :          2 :           delta = cur_time.tv_sec - start_time.tv_sec;
    1419         [ -  + ]:          2 :           if ( delta >  maxtime)
    1420                 :            :             {
    1421                 :          0 :               dprintf(vfd, "Timeout with max time=%lds and transfer time=%lds\n", maxtime, delta );
    1422                 :          0 :               rc = -ETIME;
    1423                 :          0 :               goto out2;
    1424                 :            :             }
    1425                 :            :         }
    1426                 :            :       /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT.  */
    1427                 :       3800 :       curl_multi_wait(curlm, NULL, 0, 1000, NULL);
    1428                 :       3800 :       CURLMcode curlm_res = curl_multi_perform(curlm, &still_running);
    1429                 :            : 
    1430                 :            :       /* If the target file has been found, abort the other queries.  */
    1431         [ +  + ]:       3800 :       if (target_handle != NULL)
    1432                 :            :         {
    1433         [ +  + ]:      12666 :           for (int i = 0; i < num_urls; i++)
    1434         [ +  + ]:       9435 :             if (data[i].handle != target_handle)
    1435                 :       6204 :               curl_multi_remove_handle(curlm, data[i].handle);
    1436                 :            :             else
    1437                 :            :               {
    1438                 :       3231 :                 committed_to = i;
    1439         [ +  + ]:       3231 :                 if (c->winning_headers == NULL)
    1440                 :            :                   {
    1441                 :        121 :                     c->winning_headers = data[committed_to].response_data;
    1442                 :        121 :                     data[committed_to].response_data = NULL;
    1443                 :        121 :                     data[committed_to].response_data_size = 0;
    1444                 :            :                   }
    1445                 :            : 
    1446                 :            :               }
    1447                 :            :         }
    1448                 :            : 
    1449   [ +  +  +  + ]:       3800 :       if (vfd >= 0 && !verbose_reported && committed_to >= 0)
    1450                 :            :         {
    1451   [ -  +  -  - ]:         18 :           bool pnl = (c->default_progressfn_printed_p && vfd == STDERR_FILENO);
    1452                 :          9 :           dprintf (vfd, "%scommitted to url %d\n", pnl ? "\n" : "",
    1453                 :            :                    committed_to);
    1454         [ -  + ]:          9 :           if (pnl)
    1455                 :          0 :             c->default_progressfn_printed_p = 0;
    1456                 :            :           verbose_reported = true;
    1457                 :            :         }
    1458                 :            : 
    1459         [ -  + ]:       3800 :       if (curlm_res != CURLM_OK)
    1460                 :            :         {
    1461      [ #  #  # ]:          0 :           switch (curlm_res)
    1462                 :            :             {
    1463                 :          0 :             case CURLM_CALL_MULTI_PERFORM: continue;
    1464                 :            :             case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break;
    1465                 :          0 :             default: rc = -ENETUNREACH; break;
    1466                 :            :             }
    1467                 :          0 :           goto out2;
    1468                 :            :         }
    1469                 :            : 
    1470                 :       3800 :       long dl_size = 0;
    1471   [ +  +  +  +  :       3800 :       if (target_handle && (c->progressfn || maxsize > 0))
                   -  + ]
    1472                 :            :         {
    1473                 :            :           /* Get size of file being downloaded. NB: If going through
    1474                 :            :              deflate-compressing proxies, this number is likely to be
    1475                 :            :              unavailable, so -1 may show. */
    1476                 :         18 :           CURLcode curl_res;
    1477                 :            : #if CURL_AT_LEAST_VERSION(7, 55, 0)
    1478                 :         18 :           curl_off_t cl;
    1479                 :         18 :           curl_res = curl_easy_getinfo(target_handle,
    1480                 :            :                                        CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
    1481                 :            :                                        &cl);
    1482         [ +  - ]:         18 :           if (curl_res == CURLE_OK && cl >= 0)
    1483                 :            :             dl_size = (cl > LONG_MAX ? LONG_MAX : (long)cl);
    1484                 :            : #else
    1485                 :            :           double cl;
    1486                 :            :           curl_res = curl_easy_getinfo(target_handle,
    1487                 :            :                                        CURLINFO_CONTENT_LENGTH_DOWNLOAD,
    1488                 :            :                                        &cl);
    1489                 :            :           if (curl_res == CURLE_OK)
    1490                 :            :             dl_size = (cl >= (double)(LONG_MAX+1UL) ? LONG_MAX : (long)cl);
    1491                 :            : #endif
    1492                 :            :           /* If Content-Length is -1, try to get the size from
    1493                 :            :              X-Debuginfod-Size */
    1494                 :            :           if (dl_size == -1 && c->winning_headers != NULL)
    1495                 :            :             {
    1496                 :            :               long xdl;
    1497                 :            :               char *hdr = strcasestr(c->winning_headers, "x-debuginfod-size");
    1498                 :            : 
    1499                 :            :               if (hdr != NULL
    1500                 :            :                   && sscanf(hdr, "x-debuginfod-size: %ld", &xdl) == 1)
    1501                 :            :                 dl_size = xdl;
    1502                 :            :             }
    1503                 :            :         }
    1504                 :            : 
    1505         [ +  + ]:       3800 :       if (c->progressfn) /* inform/check progress callback */
    1506                 :            :         {
    1507                 :        392 :           loops ++;
    1508                 :        392 :           long pa = loops; /* default param for progress callback */
    1509         [ +  + ]:        392 :           if (target_handle) /* we've committed to a server; report its download progress */
    1510                 :            :             {
    1511                 :         18 :               CURLcode curl_res;
    1512                 :            : #if CURL_AT_LEAST_VERSION(7, 55, 0)
    1513                 :         18 :               curl_off_t dl;
    1514                 :         18 :               curl_res = curl_easy_getinfo(target_handle,
    1515                 :            :                                            CURLINFO_SIZE_DOWNLOAD_T,
    1516                 :            :                                            &dl);
    1517   [ -  +  -  + ]:         18 :               if (curl_res == 0 && dl >= 0)
    1518                 :         18 :                 pa = (dl > LONG_MAX ? LONG_MAX : (long)dl);
    1519                 :            : #else
    1520                 :            :               double dl;
    1521                 :            :               curl_res = curl_easy_getinfo(target_handle,
    1522                 :            :                                            CURLINFO_SIZE_DOWNLOAD,
    1523                 :            :                                            &dl);
    1524                 :            :               if (curl_res == 0)
    1525                 :            :                 pa = (dl >= (double)(LONG_MAX+1UL) ? LONG_MAX : (long)dl);
    1526                 :            : #endif
    1527                 :            : 
    1528                 :            :             }
    1529                 :            : 
    1530         [ -  + ]:        392 :           if ((*c->progressfn) (c, pa, dl_size))
    1531                 :            :             {
    1532                 :          0 :               c->progressfn_cancel = true;
    1533                 :          0 :               break;
    1534                 :            :             }
    1535                 :            :         }
    1536                 :            : 
    1537                 :            :       /* Check to see if we are downloading something which exceeds maxsize, if set.*/
    1538   [ +  +  -  + ]:       3800 :       if (target_handle && dl_size > maxsize && maxsize > 0)
    1539                 :            :         {
    1540         [ #  # ]:          0 :           if (vfd >=0)
    1541                 :          0 :             dprintf(vfd, "Content-Length too large.\n");
    1542                 :          0 :           rc = -EFBIG;
    1543                 :          0 :           goto out2;
    1544                 :            :         }
    1545         [ +  + ]:       3800 :     } while (still_running);
    1546                 :            : 
    1547                 :            :   /* Check whether a query was successful. If so, assign its handle
    1548                 :            :      to verified_handle.  */
    1549                 :            :   int num_msg;
    1550                 :            :   rc = -ENOENT;
    1551                 :        484 :   CURL *verified_handle = NULL;
    1552                 :        484 :   do
    1553                 :            :     {
    1554                 :        484 :       CURLMsg *msg;
    1555                 :            : 
    1556                 :        484 :       msg = curl_multi_info_read(curlm, &num_msg);
    1557   [ +  -  +  - ]:        484 :       if (msg != NULL && msg->msg == CURLMSG_DONE)
    1558                 :            :         {
    1559         [ +  + ]:        484 :           if (vfd >= 0)
    1560                 :            :             {
    1561                 :         50 :               bool pnl = (c->default_progressfn_printed_p
    1562   [ -  +  -  - ]:         25 :                           && vfd == STDERR_FILENO);
    1563         [ +  - ]:         25 :               dprintf (vfd, "%sserver response %s\n", pnl ? "\n" : "",
    1564                 :            :                        curl_easy_strerror (msg->data.result));
    1565         [ -  + ]:         25 :               if (pnl)
    1566                 :          0 :                 c->default_progressfn_printed_p = 0;
    1567         [ +  - ]:         26 :               for (int i = 0; i < num_urls; i++)
    1568         [ +  + ]:         26 :                 if (msg->easy_handle == data[i].handle)
    1569                 :            :                   {
    1570         [ +  + ]:         25 :                     if (strlen (data[i].errbuf) > 0)
    1571                 :          4 :                       dprintf (vfd, "url %d %s\n", i, data[i].errbuf);
    1572                 :            :                     break;
    1573                 :            :                   }
    1574                 :            :             }
    1575                 :            : 
    1576         [ +  + ]:        484 :           if (msg->data.result != CURLE_OK)
    1577                 :            :             {
    1578                 :        363 :               long resp_code;
    1579                 :        363 :               CURLcode ok0;
    1580                 :            :               /* Unsuccessful query, determine error code.  */
    1581   [ -  +  -  -  :        363 :               switch (msg->data.result)
          -  -  -  -  +  
                   +  - ]
    1582                 :            :                 {
    1583                 :            :                 case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN
    1584                 :          0 :                 case CURLE_URL_MALFORMAT: rc = -EINVAL; break;
    1585                 :        342 :                 case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break;
    1586                 :        342 :                 case CURLE_PEER_FAILED_VERIFICATION: rc = -ECONNREFUSED; break;
    1587                 :          0 :                 case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break;
    1588                 :          0 :                 case CURLE_WRITE_ERROR: rc = -EIO; break;
    1589                 :          0 :                 case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break;
    1590                 :          0 :                 case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break;
    1591                 :          0 :                 case CURLE_SEND_ERROR: rc = -ECONNRESET; break;
    1592                 :          0 :                 case CURLE_RECV_ERROR: rc = -ECONNRESET; break;
    1593                 :          0 :                 case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break;
    1594                 :            :                 case CURLE_HTTP_RETURNED_ERROR:
    1595                 :         20 :                   ok0 = curl_easy_getinfo (msg->easy_handle,
    1596                 :            :                                           CURLINFO_RESPONSE_CODE,
    1597                 :            :                                           &resp_code);
    1598                 :            :                   /* 406 signals that the requested file was too large */
    1599   [ +  -  +  + ]:         20 :                   if ( ok0 == CURLE_OK && resp_code == 406)
    1600                 :            :                     rc = -EFBIG;
    1601   [ -  +  -  - ]:         19 :                   else if (section != NULL && resp_code == 503)
    1602                 :            :                     rc = -EINVAL;
    1603                 :            :                   else
    1604                 :         20 :                     rc = -ENOENT;
    1605                 :            :                   break;
    1606                 :         20 :                 default: rc = -ENOENT; break;
    1607                 :            :                 }
    1608                 :            :             }
    1609                 :            :           else
    1610                 :            :             {
    1611                 :            :               /* Query completed without an error. Confirm that the
    1612                 :            :                  response code is 200 when using HTTP/HTTPS and 0 when
    1613                 :            :                  using file:// and set verified_handle.  */
    1614                 :            : 
    1615         [ +  - ]:        121 :               if (msg->easy_handle != NULL)
    1616                 :            :                 {
    1617                 :        121 :                   char *effective_url = NULL;
    1618                 :        121 :                   long resp_code = 500;
    1619                 :        121 :                   CURLcode ok1 = curl_easy_getinfo (target_handle,
    1620                 :            :                                                     CURLINFO_EFFECTIVE_URL,
    1621                 :            :                                                     &effective_url);
    1622                 :        121 :                   CURLcode ok2 = curl_easy_getinfo (target_handle,
    1623                 :            :                                                     CURLINFO_RESPONSE_CODE,
    1624                 :            :                                                     &resp_code);
    1625   [ +  -  +  - ]:        121 :                   if(ok1 == CURLE_OK && ok2 == CURLE_OK && effective_url)
    1626                 :            :                     {
    1627         [ +  + ]:        121 :                       if (strncasecmp (effective_url, "HTTP", 4) == 0)
    1628         [ +  - ]:        120 :                         if (resp_code == 200)
    1629                 :            :                           {
    1630                 :        120 :                             verified_handle = msg->easy_handle;
    1631                 :        121 :                             break;
    1632                 :            :                           }
    1633         [ +  - ]:          1 :                       if (strncasecmp (effective_url, "FILE", 4) == 0)
    1634         [ +  - ]:          1 :                         if (resp_code == 0)
    1635                 :            :                           {
    1636                 :          1 :                             verified_handle = msg->easy_handle;
    1637                 :          1 :                             break;
    1638                 :            :                           }
    1639                 :            :                     }
    1640                 :            :                   /* - libcurl since 7.52.0 version start to support
    1641                 :            :                        CURLINFO_SCHEME;
    1642                 :            :                      - before 7.61.0, effective_url would give us a
    1643                 :            :                        url with upper case SCHEME added in the front;
    1644                 :            :                      - effective_url between 7.61 and 7.69 can be lack
    1645                 :            :                        of scheme if the original url doesn't include one;
    1646                 :            :                      - since version 7.69 effective_url will be provide
    1647                 :            :                        a scheme in lower case.  */
    1648                 :            :                   #if LIBCURL_VERSION_NUM >= 0x073d00 /* 7.61.0 */
    1649                 :            :                   #if LIBCURL_VERSION_NUM <= 0x074500 /* 7.69.0 */
    1650                 :            :                   char *scheme = NULL;
    1651                 :            :                   CURLcode ok3 = curl_easy_getinfo (target_handle,
    1652                 :            :                                                     CURLINFO_SCHEME,
    1653                 :            :                                                     &scheme);
    1654                 :            :                   if(ok3 == CURLE_OK && scheme)
    1655                 :            :                     {
    1656                 :            :                       if (startswith (scheme, "HTTP"))
    1657                 :            :                         if (resp_code == 200)
    1658                 :            :                           {
    1659                 :            :                             verified_handle = msg->easy_handle;
    1660                 :            :                             break;
    1661                 :            :                           }
    1662                 :            :                     }
    1663                 :            :                   #endif
    1664                 :            :                   #endif
    1665                 :            :                 }
    1666                 :            :             }
    1667                 :            :         }
    1668         [ +  + ]:        363 :     } while (num_msg > 0);
    1669                 :            : 
    1670                 :            :   /* Create an empty file named as $HOME/.cache if the query fails
    1671                 :            :      with ENOENT.*/
    1672         [ +  + ]:        483 :   if (rc == -ENOENT)
    1673                 :            :     {
    1674                 :        141 :       int efd = open (target_cache_path, O_CREAT|O_EXCL, DEFFILEMODE);
    1675         [ +  + ]:        141 :       if (efd >= 0)
    1676                 :        140 :         close(efd);
    1677                 :            :     }
    1678         [ +  + ]:        342 :   else if (rc == -EFBIG)
    1679                 :          1 :     goto out2;
    1680                 :            : 
    1681                 :            :   /* If the verified_handle is NULL and rc != -ENOENT, the query fails with
    1682                 :            :    * an error code other than 404, then do several retry within the retry_limit.
    1683                 :            :    * Clean up all old handles and jump back to the beginning of query_in_parallel,
    1684                 :            :    * reinitialize handles and query again.*/
    1685         [ +  + ]:        482 :   if (verified_handle == NULL)
    1686                 :            :     {
    1687   [ +  +  +  + ]:        361 :       if (rc != -ENOENT && retry_limit-- > 0)
    1688                 :            :         {
    1689         [ +  + ]:        230 :           if (vfd >= 0)
    1690                 :         10 :             dprintf (vfd, "Retry failed query, %d attempt(s) remaining\n", retry_limit);
    1691                 :            :           /* remove all handles from multi */
    1692         [ +  + ]:        460 :           for (int i = 0; i < num_urls; i++)
    1693                 :            :             {
    1694                 :        230 :               curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    1695                 :        230 :               curl_easy_cleanup (data[i].handle);
    1696                 :        230 :               free(data[i].response_data);
    1697                 :            :             }
    1698                 :        230 :             free(c->winning_headers);
    1699                 :        230 :             c->winning_headers = NULL;
    1700                 :        230 :             goto query_in_parallel;
    1701                 :            :         }
    1702                 :            :       else
    1703                 :        131 :         goto out2;
    1704                 :            :     }
    1705                 :            : 
    1706         [ +  + ]:        121 :   if (vfd >= 0)
    1707                 :            :     {
    1708   [ +  -  -  - ]:         18 :       bool pnl = c->default_progressfn_printed_p && vfd == STDERR_FILENO;
    1709                 :          9 :       dprintf (vfd, "%sgot file from server\n", pnl ? "\n" : "");
    1710         [ -  + ]:          9 :       if (pnl)
    1711                 :          0 :         c->default_progressfn_printed_p = 0;
    1712                 :            :     }
    1713                 :            : 
    1714                 :            :   /* we've got one!!!! */
    1715                 :        121 :   time_t mtime;
    1716                 :            : #if defined(_TIME_BITS) && _TIME_BITS == 64
    1717                 :            :   CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME_T, (void*) &mtime);
    1718                 :            : #else
    1719                 :        121 :   CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime);
    1720                 :            : #endif
    1721         [ -  + ]:        121 :   if (curl_res != CURLE_OK)
    1722                 :          0 :     mtime = time(NULL); /* fall back to current time */
    1723                 :            : 
    1724                 :        121 :   struct timeval tvs[2];
    1725                 :        121 :   tvs[0].tv_sec = tvs[1].tv_sec = mtime;
    1726                 :        121 :   tvs[0].tv_usec = tvs[1].tv_usec = 0;
    1727                 :        121 :   (void) futimes (fd, tvs);  /* best effort */
    1728                 :            : 
    1729                 :            :   /* PR27571: make cache files casually unwriteable; dirs are already 0700 */
    1730                 :        121 :   (void) fchmod(fd, 0400);
    1731                 :            :                 
    1732                 :            :   /* rename tmp->real */
    1733                 :        121 :   rc = rename (target_cache_tmppath, target_cache_path);
    1734         [ -  + ]:        121 :   if (rc < 0)
    1735                 :            :     {
    1736                 :          0 :       rc = -errno;
    1737                 :          0 :       goto out2;
    1738                 :            :       /* Perhaps we need not give up right away; could retry or something ... */
    1739                 :            :     }
    1740                 :            : 
    1741                 :            :   /* remove all handles from multi */
    1742         [ +  + ]:        266 :   for (int i = 0; i < num_urls; i++)
    1743                 :            :     {
    1744                 :        145 :       curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    1745                 :        145 :       curl_easy_cleanup (data[i].handle);
    1746                 :        145 :       free (data[i].response_data);
    1747                 :            :     }
    1748                 :            : 
    1749         [ +  + ]:        266 :   for (int i = 0; i < num_urls; ++i)
    1750                 :        145 :     free(server_url_list[i]);
    1751                 :        121 :   free(server_url_list);
    1752                 :        121 :   free (data);
    1753                 :        121 :   free (server_urls);
    1754                 :            : 
    1755                 :            :   /* don't close fd - we're returning it */
    1756                 :            :   /* don't unlink the tmppath; it's already been renamed. */
    1757         [ +  + ]:        121 :   if (path != NULL)
    1758                 :        119 :    *path = strdup(target_cache_path);
    1759                 :            : 
    1760                 :        121 :   rc = fd;
    1761                 :        121 :   goto out;
    1762                 :            : 
    1763                 :            : /* error exits */
    1764                 :        132 :  out2:
    1765                 :            :   /* remove all handles from multi */
    1766         [ +  + ]:        265 :   for (int i = 0; i < num_urls; i++)
    1767                 :            :     {
    1768         [ +  - ]:        133 :       if (data[i].handle != NULL)
    1769                 :            :         {
    1770                 :        133 :           curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    1771                 :        133 :           curl_easy_cleanup (data[i].handle);
    1772                 :        133 :           free (data[i].response_data);
    1773                 :            :         }
    1774                 :            :     }
    1775                 :            : 
    1776                 :        132 :   unlink (target_cache_tmppath);
    1777                 :        132 :   close (fd); /* before the rmdir, otherwise it'll fail */
    1778                 :        132 :   (void) rmdir (target_cache_dir); /* nop if not empty */
    1779                 :        132 :   free(data);
    1780                 :            : 
    1781                 :        132 :  out1:
    1782         [ +  + ]:        265 :   for (int i = 0; i < num_urls; ++i)
    1783                 :        133 :     free(server_url_list[i]);
    1784                 :        132 :   free(server_url_list);
    1785                 :            : 
    1786                 :        132 :  out0:
    1787                 :        132 :   free (server_urls);
    1788                 :            : 
    1789                 :            : /* general purpose exit */
    1790                 :        412 :  out:
    1791                 :            :   /* Reset sent headers */
    1792                 :        412 :   curl_slist_free_all (c->headers);
    1793                 :        412 :   c->headers = NULL;
    1794                 :        412 :   c->user_agent_set_p = 0;
    1795                 :            :   
    1796                 :            :   /* Conclude the last \r status line */
    1797                 :            :   /* Another possibility is to use the ANSI CSI n K EL "Erase in Line"
    1798                 :            :      code.  That way, the previously printed messages would be erased,
    1799                 :            :      and without a newline. */
    1800         [ +  + ]:        412 :   if (c->default_progressfn_printed_p)
    1801                 :          1 :     dprintf(STDERR_FILENO, "\n");
    1802                 :            : 
    1803         [ +  + ]:        412 :   if (vfd >= 0)
    1804                 :            :     {
    1805         [ +  + ]:         18 :       if (rc < 0)
    1806                 :          5 :         dprintf (vfd, "not found %s (err=%d)\n", strerror (-rc), rc);
    1807                 :            :       else
    1808                 :         13 :         dprintf (vfd, "found %s (fd=%d)\n", target_cache_path, rc);
    1809                 :            :     }
    1810                 :            : 
    1811                 :        412 :   free (cache_path);
    1812                 :        412 :   free (maxage_path);
    1813                 :        412 :   free (interval_path);
    1814                 :        412 :   free (cache_miss_path);
    1815                 :        412 :   free (target_cache_dir);
    1816                 :        412 :   free (target_cache_path);
    1817                 :        412 :   free (target_cache_tmppath);
    1818                 :        412 :   return rc;
    1819                 :            : }
    1820                 :            : 
    1821                 :            : 
    1822                 :            : 
    1823                 :            : /* See debuginfod.h  */
    1824                 :            : debuginfod_client  *
    1825                 :        189 : debuginfod_begin (void)
    1826                 :            : {
    1827                 :            :   /* Initialize libcurl lazily, but only once.  */
    1828                 :        189 :   pthread_once (&init_control, libcurl_init);
    1829                 :            : 
    1830                 :        189 :   debuginfod_client *client;
    1831                 :        189 :   size_t size = sizeof (struct debuginfod_client);
    1832                 :        189 :   client = calloc (1, size);
    1833                 :            : 
    1834         [ +  - ]:        189 :   if (client != NULL)
    1835                 :            :     {
    1836         [ +  + ]:        189 :       if (getenv(DEBUGINFOD_PROGRESS_ENV_VAR))
    1837                 :          1 :         client->progressfn = default_progressfn;
    1838         [ +  + ]:        189 :       if (getenv(DEBUGINFOD_VERBOSE_ENV_VAR))
    1839                 :          1 :         client->verbose_fd = STDERR_FILENO;
    1840                 :            :       else
    1841                 :        188 :         client->verbose_fd = -1;
    1842                 :            : 
    1843                 :            :       // allocate 1 curl multi handle
    1844                 :        189 :       client->server_mhandle = curl_multi_init ();
    1845         [ -  + ]:        189 :       if (client->server_mhandle == NULL)
    1846                 :          0 :         goto out1;
    1847                 :            :     }
    1848                 :            : 
    1849                 :            :   // extra future initialization
    1850                 :            :   
    1851                 :        189 :   goto out;
    1852                 :            : 
    1853                 :          0 :  out1:
    1854                 :          0 :   free (client);
    1855                 :          0 :   client = NULL;
    1856                 :            : 
    1857                 :        189 :  out:  
    1858                 :        189 :   return client;
    1859                 :            : }
    1860                 :            : 
    1861                 :            : void
    1862                 :        176 : debuginfod_set_user_data(debuginfod_client *client,
    1863                 :            :                          void *data)
    1864                 :            : {
    1865                 :        176 :   client->user_data = data;
    1866                 :        176 : }
    1867                 :            : 
    1868                 :            : void *
    1869                 :          0 : debuginfod_get_user_data(debuginfod_client *client)
    1870                 :            : {
    1871                 :          0 :   return client->user_data;
    1872                 :            : }
    1873                 :            : 
    1874                 :            : const char *
    1875                 :         22 : debuginfod_get_url(debuginfod_client *client)
    1876                 :            : {
    1877         [ +  + ]:          4 :   return client->url;
    1878                 :            : }
    1879                 :            : 
    1880                 :            : const char *
    1881                 :         20 : debuginfod_get_headers(debuginfod_client *client)
    1882                 :            : {
    1883                 :         20 :   return client->winning_headers;
    1884                 :            : }
    1885                 :            : 
    1886                 :            : void
    1887                 :        189 : debuginfod_end (debuginfod_client *client)
    1888                 :            : {
    1889         [ +  - ]:        189 :   if (client == NULL)
    1890                 :            :     return;
    1891                 :            : 
    1892                 :        189 :   curl_multi_cleanup (client->server_mhandle);
    1893                 :        189 :   curl_slist_free_all (client->headers);
    1894                 :        189 :   free (client->winning_headers);
    1895                 :        189 :   free (client->url);
    1896                 :        189 :   free (client);
    1897                 :            : }
    1898                 :            : 
    1899                 :            : int
    1900                 :        105 : debuginfod_find_debuginfo (debuginfod_client *client,
    1901                 :            :                            const unsigned char *build_id, int build_id_len,
    1902                 :            :                            char **path)
    1903                 :            : {
    1904                 :        105 :   return debuginfod_query_server(client, build_id, build_id_len,
    1905                 :            :                                  "debuginfo", NULL, path);
    1906                 :            : }
    1907                 :            : 
    1908                 :            : 
    1909                 :            : /* See debuginfod.h  */
    1910                 :            : int
    1911                 :        267 : debuginfod_find_executable(debuginfod_client *client,
    1912                 :            :                            const unsigned char *build_id, int build_id_len,
    1913                 :            :                            char **path)
    1914                 :            : {
    1915                 :        267 :   return debuginfod_query_server(client, build_id, build_id_len,
    1916                 :            :                                  "executable", NULL, path);
    1917                 :            : }
    1918                 :            : 
    1919                 :            : /* See debuginfod.h  */
    1920                 :         32 : int debuginfod_find_source(debuginfod_client *client,
    1921                 :            :                            const unsigned char *build_id, int build_id_len,
    1922                 :            :                            const char *filename, char **path)
    1923                 :            : {
    1924                 :         32 :   return debuginfod_query_server(client, build_id, build_id_len,
    1925                 :            :                                  "source", filename, path);
    1926                 :            : }
    1927                 :            : 
    1928                 :            : int
    1929                 :          8 : debuginfod_find_section (debuginfod_client *client,
    1930                 :            :                          const unsigned char *build_id, int build_id_len,
    1931                 :            :                          const char *section, char **path)
    1932                 :            : {
    1933                 :          8 :   int rc = debuginfod_query_server(client, build_id, build_id_len,
    1934                 :            :                                    "section", section, path);
    1935         [ -  + ]:          8 :   if (rc != -EINVAL)
    1936                 :            :     return rc;
    1937                 :            : 
    1938                 :            :   /* The servers may have lacked support for section queries.  Attempt to
    1939                 :            :      download the debuginfo or executable containing the section in order
    1940                 :            :      to extract it.  */
    1941                 :          0 :   rc = -EEXIST;
    1942                 :          0 :   int fd = -1;
    1943                 :          0 :   char *tmp_path = NULL;
    1944                 :            : 
    1945                 :          0 :   fd = debuginfod_find_debuginfo (client, build_id, build_id_len, &tmp_path);
    1946         [ #  # ]:          0 :   if (client->progressfn_cancel)
    1947                 :            :     {
    1948         [ #  # ]:          0 :       if (fd >= 0)
    1949                 :            :         {
    1950                 :            :           /* This shouldn't happen, but we'll check this condition
    1951                 :            :              just in case.  */
    1952                 :          0 :           close (fd);
    1953                 :          0 :           free (tmp_path);
    1954                 :            :         }
    1955                 :          0 :       return -ENOENT;
    1956                 :            :     }
    1957         [ #  # ]:          0 :   if (fd >= 0)
    1958                 :            :     {
    1959                 :          0 :       rc = extract_section (fd, section, tmp_path, path);
    1960                 :          0 :       close (fd);
    1961                 :            :     }
    1962                 :            : 
    1963         [ #  # ]:          0 :   if (rc == -EEXIST)
    1964                 :            :     {
    1965                 :            :       /* Either the debuginfo couldn't be found or the section should
    1966                 :            :          be in the executable.  */
    1967                 :          0 :       fd = debuginfod_find_executable (client, build_id,
    1968                 :            :                                        build_id_len, &tmp_path);
    1969         [ #  # ]:          0 :       if (fd >= 0)
    1970                 :            :         {
    1971                 :          0 :           rc = extract_section (fd, section, tmp_path, path);
    1972                 :          0 :           close (fd);
    1973                 :            :         }
    1974                 :            :       else
    1975                 :            :         /* Update rc so that we return the most recent error code.  */
    1976                 :            :         rc = fd;
    1977                 :            :     }
    1978                 :            : 
    1979                 :          0 :   free (tmp_path);
    1980                 :          0 :   return rc;
    1981                 :            : }
    1982                 :            : 
    1983                 :            : /* Add an outgoing HTTP header.  */
    1984                 :        650 : int debuginfod_add_http_header (debuginfod_client *client, const char* header)
    1985                 :            : {
    1986                 :            :   /* Sanity check header value is of the form Header: Value.
    1987                 :            :      It should contain at least one colon that isn't the first or
    1988                 :            :      last character.  */
    1989                 :        650 :   char *colon = strchr (header, ':'); /* first colon */
    1990                 :        650 :   if (colon == NULL /* present */
    1991         [ +  - ]:        650 :       || colon == header /* not at beginning - i.e., have a header name */
    1992         [ +  - ]:        650 :       || *(colon + 1) == '\0') /* not at end - i.e., have a value */
    1993                 :            :     /* NB: but it's okay for a value to contain other colons! */
    1994                 :            :     return -EINVAL;
    1995                 :            : 
    1996                 :        650 :   struct curl_slist *temp = curl_slist_append (client->headers, header);
    1997         [ +  - ]:        650 :   if (temp == NULL)
    1998                 :            :     return -ENOMEM;
    1999                 :            : 
    2000                 :            :   /* Track if User-Agent: is being set.  If so, signal not to add the
    2001                 :            :      default one. */
    2002         [ +  + ]:        650 :   if (startswith (header, "User-Agent:"))
    2003                 :        413 :     client->user_agent_set_p = 1;
    2004                 :            : 
    2005                 :        650 :   client->headers = temp;
    2006                 :        650 :   return 0;
    2007                 :            : }
    2008                 :            : 
    2009                 :            : 
    2010                 :            : void
    2011                 :        288 : debuginfod_set_progressfn(debuginfod_client *client,
    2012                 :            :                           debuginfod_progressfn_t fn)
    2013                 :            : {
    2014                 :        288 :   client->progressfn = fn;
    2015                 :        288 : }
    2016                 :            : 
    2017                 :            : void
    2018                 :         32 : debuginfod_set_verbose_fd(debuginfod_client *client, int fd)
    2019                 :            : {
    2020                 :         32 :   client->verbose_fd = fd;
    2021                 :         32 : }
    2022                 :            : 
    2023                 :            : #endif /* DUMMY_LIBDEBUGINFOD */

Generated by: LCOV version 1.14