(root)/
glibc-2.38/
support/
support_descriptors.c
       1  /* Monitoring file descriptor usage.
       2     Copyright (C) 2018-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <dirent.h>
      20  #include <stdio.h>
      21  #include <stdlib.h>
      22  #include <string.h>
      23  #include <support/check.h>
      24  #include <support/descriptors.h>
      25  #include <support/support.h>
      26  #include <sys/stat.h>
      27  #include <sys/sysmacros.h>
      28  #include <xunistd.h>
      29  
      30  struct procfs_descriptor
      31  {
      32    int fd;
      33    char *link_target;
      34    dev_t dev;
      35    ino64_t ino;
      36  };
      37  
      38  /* Used with qsort.  */
      39  static int
      40  descriptor_compare (const void *l, const void *r)
      41  {
      42    const struct procfs_descriptor *left = l;
      43    const struct procfs_descriptor *right = r;
      44    /* Cannot overflow due to limited file descriptor range.  */
      45    return left->fd - right->fd;
      46  }
      47  
      48  #define DYNARRAY_STRUCT descriptor_list
      49  #define DYNARRAY_ELEMENT struct procfs_descriptor
      50  #define DYNARRAY_PREFIX descriptor_list_
      51  #define DYNARRAY_ELEMENT_FREE(e) free ((e)->link_target)
      52  #define DYNARRAY_INITIAL_SIZE 0
      53  #include <malloc/dynarray-skeleton.c>
      54  
      55  struct support_descriptors
      56  {
      57    struct descriptor_list list;
      58  };
      59  
      60  struct support_descriptors *
      61  support_descriptors_list (void)
      62  {
      63    struct support_descriptors *result = xmalloc (sizeof (*result));
      64    descriptor_list_init (&result->list);
      65  
      66    DIR *fds = opendir ("/proc/self/fd");
      67    if (fds == NULL)
      68      FAIL_EXIT1 ("opendir (\"/proc/self/fd\"): %m");
      69  
      70    while (true)
      71      {
      72        errno = 0;
      73        struct dirent64 *e = readdir64 (fds);
      74        if (e == NULL)
      75          {
      76            if (errno != 0)
      77              FAIL_EXIT1 ("readdir: %m");
      78            break;
      79          }
      80  
      81        if (e->d_name[0] == '.')
      82          continue;
      83  
      84        char *endptr;
      85        long int fd = strtol (e->d_name, &endptr, 10);
      86        if (*endptr != '\0' || fd < 0 || fd > INT_MAX)
      87          FAIL_EXIT1 ("readdir: invalid file descriptor name: /proc/self/fd/%s",
      88                      e->d_name);
      89  
      90        /* Skip the descriptor which is used to enumerate the
      91           descriptors.  */
      92        if (fd == dirfd (fds))
      93          continue;
      94  
      95        char *target;
      96        {
      97          char *path = xasprintf ("/proc/self/fd/%ld", fd);
      98          target = xreadlink (path);
      99          free (path);
     100        }
     101        struct stat64 st;
     102        if (fstat64 (fd, &st) != 0)
     103          FAIL_EXIT1 ("readdir: fstat64 (%ld) failed: %m", fd);
     104  
     105        struct procfs_descriptor *item = descriptor_list_emplace (&result->list);
     106        if (item == NULL)
     107          FAIL_EXIT1 ("descriptor_list_emplace: %m");
     108        item->fd = fd;
     109        item->link_target = target;
     110        item->dev = st.st_dev;
     111        item->ino = st.st_ino;
     112      }
     113  
     114    closedir (fds);
     115  
     116    /* Perform a merge join between descrs and current.  This assumes
     117       that the arrays are sorted by file descriptor.  */
     118  
     119    qsort (descriptor_list_begin (&result->list),
     120           descriptor_list_size (&result->list),
     121           sizeof (struct procfs_descriptor), descriptor_compare);
     122  
     123    return result;
     124  }
     125  
     126  void
     127  support_descriptors_free (struct support_descriptors *descrs)
     128  {
     129    descriptor_list_free (&descrs->list);
     130    free (descrs);
     131  }
     132  
     133  void
     134  support_descriptors_dump (struct support_descriptors *descrs,
     135                            const char *prefix, FILE *fp)
     136  {
     137    struct procfs_descriptor *end = descriptor_list_end (&descrs->list);
     138    for (struct procfs_descriptor *d = descriptor_list_begin (&descrs->list);
     139         d != end; ++d)
     140      {
     141        char *quoted = support_quote_string (d->link_target);
     142        fprintf (fp, "%s%d: target=\"%s\" major=%lld minor=%lld ino=%lld\n",
     143                 prefix, d->fd, quoted,
     144                 (long long int) major (d->dev),
     145                 (long long int) minor (d->dev),
     146                 (long long int) d->ino);
     147        free (quoted);
     148      }
     149  }
     150  
     151  static void
     152  dump_mismatch (bool *first,
     153                 struct support_descriptors *descrs,
     154                 struct support_descriptors *current)
     155  {
     156    if (*first)
     157      *first = false;
     158    else
     159      return;
     160  
     161    puts ("error: Differences found in descriptor set");
     162    puts ("Reference descriptor set:");
     163    support_descriptors_dump (descrs, "  ", stdout);
     164    puts ("Current descriptor set:");
     165    support_descriptors_dump (current, "  ", stdout);
     166    puts ("Differences:");
     167  }
     168  
     169  static void
     170  report_closed_descriptor (bool *first,
     171                            struct support_descriptors *descrs,
     172                            struct support_descriptors *current,
     173                            struct procfs_descriptor *left)
     174  {
     175    support_record_failure ();
     176    dump_mismatch (first, descrs, current);
     177    printf ("error: descriptor %d was closed\n", left->fd);
     178  }
     179  
     180  static void
     181  report_opened_descriptor (bool *first,
     182                            struct support_descriptors *descrs,
     183                            struct support_descriptors *current,
     184                            struct procfs_descriptor *right)
     185  {
     186    support_record_failure ();
     187    dump_mismatch (first, descrs, current);
     188    char *quoted = support_quote_string (right->link_target);
     189    printf ("error: descriptor %d was opened (\"%s\")\n", right->fd, quoted);
     190    free (quoted);
     191  }
     192  
     193  void
     194  support_descriptors_check (struct support_descriptors *descrs)
     195  {
     196    struct support_descriptors *current = support_descriptors_list ();
     197  
     198    /* Perform a merge join between descrs and current.  This assumes
     199       that the arrays are sorted by file descriptor.  */
     200  
     201    struct procfs_descriptor *left = descriptor_list_begin (&descrs->list);
     202    struct procfs_descriptor *left_end = descriptor_list_end (&descrs->list);
     203    struct procfs_descriptor *right = descriptor_list_begin (&current->list);
     204    struct procfs_descriptor *right_end = descriptor_list_end (&current->list);
     205  
     206    bool first = true;
     207    while (left != left_end && right != right_end)
     208      {
     209        if (left->fd == right->fd)
     210          {
     211            if (strcmp (left->link_target, right->link_target) != 0)
     212              {
     213                support_record_failure ();
     214                char *left_quoted = support_quote_string (left->link_target);
     215                char *right_quoted = support_quote_string (right->link_target);
     216                dump_mismatch (&first, descrs, current);
     217                printf ("error: descriptor %d changed from \"%s\" to \"%s\"\n",
     218                        left->fd, left_quoted, right_quoted);
     219                free (left_quoted);
     220                free (right_quoted);
     221              }
     222            if (left->dev != right->dev)
     223              {
     224                support_record_failure ();
     225                dump_mismatch (&first, descrs, current);
     226                printf ("error: descriptor %d changed device"
     227                        " from %lld:%lld to %lld:%lld\n",
     228                        left->fd,
     229                        (long long int) major (left->dev),
     230                        (long long int) minor (left->dev),
     231                        (long long int) major (right->dev),
     232                        (long long int) minor (right->dev));
     233              }
     234            if (left->ino != right->ino)
     235              {
     236                support_record_failure ();
     237                dump_mismatch (&first, descrs, current);
     238                printf ("error: descriptor %d changed ino from %lld to %lld\n",
     239                        left->fd,
     240                        (long long int) left->ino, (long long int) right->ino);
     241              }
     242            ++left;
     243            ++right;
     244          }
     245        else if (left->fd < right->fd)
     246          {
     247            /* Gap on the right.  */
     248            report_closed_descriptor (&first, descrs, current, left);
     249            ++left;
     250          }
     251        else
     252          {
     253            /* Gap on the left.  */
     254            TEST_VERIFY_EXIT (left->fd > right->fd);
     255            report_opened_descriptor (&first, descrs, current, right);
     256            ++right;
     257          }
     258      }
     259  
     260    while (left != left_end)
     261      {
     262        /* Closed descriptors (more descriptors on the left).  */
     263        report_closed_descriptor (&first, descrs, current, left);
     264        ++left;
     265      }
     266  
     267    while (right != right_end)
     268      {
     269        /* Opened descriptors (more descriptors on the right).  */
     270        report_opened_descriptor (&first, descrs, current, right);
     271        ++right;
     272      }
     273  
     274    support_descriptors_free (current);
     275  }