LCOV - code coverage report
Current view: top level - coredump - coredump-vacuum.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 127 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 7 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 121 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <sys/statvfs.h>
       4                 :            : #include <sys/stat.h>
       5                 :            : #include <sys/types.h>
       6                 :            : #include <unistd.h>
       7                 :            : 
       8                 :            : #include "alloc-util.h"
       9                 :            : #include "coredump-vacuum.h"
      10                 :            : #include "dirent-util.h"
      11                 :            : #include "fd-util.h"
      12                 :            : #include "fs-util.h"
      13                 :            : #include "hashmap.h"
      14                 :            : #include "macro.h"
      15                 :            : #include "memory-util.h"
      16                 :            : #include "string-util.h"
      17                 :            : #include "time-util.h"
      18                 :            : #include "user-util.h"
      19                 :            : 
      20                 :            : #define DEFAULT_MAX_USE_LOWER (uint64_t) (1ULL*1024ULL*1024ULL)           /* 1 MiB */
      21                 :            : #define DEFAULT_MAX_USE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL)   /* 4 GiB */
      22                 :            : #define DEFAULT_KEEP_FREE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
      23                 :            : #define DEFAULT_KEEP_FREE (uint64_t) (1024ULL*1024ULL)                    /* 1 MB */
      24                 :            : 
      25                 :            : struct vacuum_candidate {
      26                 :            :         unsigned n_files;
      27                 :            :         char *oldest_file;
      28                 :            :         usec_t oldest_mtime;
      29                 :            : };
      30                 :            : 
      31                 :          0 : static void vacuum_candidate_free(struct vacuum_candidate *c) {
      32         [ #  # ]:          0 :         if (!c)
      33                 :          0 :                 return;
      34                 :            : 
      35                 :          0 :         free(c->oldest_file);
      36                 :          0 :         free(c);
      37                 :            : }
      38                 :            : 
      39         [ #  # ]:          0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate*, vacuum_candidate_free);
      40                 :            : 
      41                 :          0 : static void vacuum_candidate_hashmap_free(Hashmap *h) {
      42         [ #  # ]:          0 :         hashmap_free_with_destructor(h, vacuum_candidate_free);
      43                 :          0 : }
      44                 :            : 
      45         [ #  # ]:          0 : DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, vacuum_candidate_hashmap_free);
      46                 :            : 
      47                 :          0 : static int uid_from_file_name(const char *filename, uid_t *uid) {
      48                 :            :         const char *p, *e, *u;
      49                 :            : 
      50                 :          0 :         p = startswith(filename, "core.");
      51         [ #  # ]:          0 :         if (!p)
      52                 :          0 :                 return -EINVAL;
      53                 :            : 
      54                 :            :         /* Skip the comm field */
      55                 :          0 :         p = strchr(p, '.');
      56         [ #  # ]:          0 :         if (!p)
      57                 :          0 :                 return -EINVAL;
      58                 :          0 :         p++;
      59                 :            : 
      60                 :            :         /* Find end up UID */
      61                 :          0 :         e = strchr(p, '.');
      62         [ #  # ]:          0 :         if (!e)
      63                 :          0 :                 return -EINVAL;
      64                 :            : 
      65                 :          0 :         u = strndupa(p, e-p);
      66                 :          0 :         return parse_uid(u, uid);
      67                 :            : }
      68                 :            : 
      69                 :          0 : static bool vacuum_necessary(int fd, uint64_t sum, uint64_t keep_free, uint64_t max_use) {
      70                 :          0 :         uint64_t fs_size = 0, fs_free = (uint64_t) -1;
      71                 :            :         struct statvfs sv;
      72                 :            : 
      73         [ #  # ]:          0 :         assert(fd >= 0);
      74                 :            : 
      75         [ #  # ]:          0 :         if (fstatvfs(fd, &sv) >= 0) {
      76                 :          0 :                 fs_size = sv.f_frsize * sv.f_blocks;
      77                 :          0 :                 fs_free = sv.f_frsize * sv.f_bfree;
      78                 :            :         }
      79                 :            : 
      80         [ #  # ]:          0 :         if (max_use == (uint64_t) -1) {
      81                 :            : 
      82         [ #  # ]:          0 :                 if (fs_size > 0) {
      83                 :          0 :                         max_use = PAGE_ALIGN(fs_size / 10); /* 10% */
      84                 :            : 
      85         [ #  # ]:          0 :                         if (max_use > DEFAULT_MAX_USE_UPPER)
      86                 :          0 :                                 max_use = DEFAULT_MAX_USE_UPPER;
      87                 :            : 
      88         [ #  # ]:          0 :                         if (max_use < DEFAULT_MAX_USE_LOWER)
      89                 :          0 :                                 max_use = DEFAULT_MAX_USE_LOWER;
      90                 :            :                 } else
      91                 :          0 :                         max_use = DEFAULT_MAX_USE_LOWER;
      92                 :            :         } else
      93                 :          0 :                 max_use = PAGE_ALIGN(max_use);
      94                 :            : 
      95   [ #  #  #  # ]:          0 :         if (max_use > 0 && sum > max_use)
      96                 :          0 :                 return true;
      97                 :            : 
      98         [ #  # ]:          0 :         if (keep_free == (uint64_t) -1) {
      99                 :            : 
     100         [ #  # ]:          0 :                 if (fs_size > 0) {
     101                 :          0 :                         keep_free = PAGE_ALIGN((fs_size * 3) / 20); /* 15% */
     102                 :            : 
     103         [ #  # ]:          0 :                         if (keep_free > DEFAULT_KEEP_FREE_UPPER)
     104                 :          0 :                                 keep_free = DEFAULT_KEEP_FREE_UPPER;
     105                 :            :                 } else
     106                 :          0 :                         keep_free = DEFAULT_KEEP_FREE;
     107                 :            :         } else
     108                 :          0 :                 keep_free = PAGE_ALIGN(keep_free);
     109                 :            : 
     110   [ #  #  #  # ]:          0 :         if (keep_free > 0 && fs_free < keep_free)
     111                 :          0 :                 return true;
     112                 :            : 
     113                 :          0 :         return false;
     114                 :            : }
     115                 :            : 
     116                 :          0 : int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
     117                 :          0 :         _cleanup_closedir_ DIR *d = NULL;
     118                 :            :         struct stat exclude_st;
     119                 :            :         int r;
     120                 :            : 
     121   [ #  #  #  # ]:          0 :         if (keep_free == 0 && max_use == 0)
     122                 :          0 :                 return 0;
     123                 :            : 
     124         [ #  # ]:          0 :         if (exclude_fd >= 0) {
     125         [ #  # ]:          0 :                 if (fstat(exclude_fd, &exclude_st) < 0)
     126         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to fstat(): %m");
     127                 :            :         }
     128                 :            : 
     129                 :            :         /* This algorithm will keep deleting the oldest file of the
     130                 :            :          * user with the most coredumps until we are back in the size
     131                 :            :          * limits. Note that vacuuming for journal files is different,
     132                 :            :          * because we rely on rate-limiting of the messages there,
     133                 :            :          * to avoid being flooded. */
     134                 :            : 
     135                 :          0 :         d = opendir("/var/lib/systemd/coredump");
     136         [ #  # ]:          0 :         if (!d) {
     137         [ #  # ]:          0 :                 if (errno == ENOENT)
     138                 :          0 :                         return 0;
     139                 :            : 
     140         [ #  # ]:          0 :                 return log_error_errno(errno, "Can't open coredump directory: %m");
     141                 :            :         }
     142                 :            : 
     143                 :          0 :         for (;;) {
     144   [ #  #  #  #  :          0 :                 _cleanup_(vacuum_candidate_hashmap_freep) Hashmap *h = NULL;
                      # ]
     145                 :          0 :                 struct vacuum_candidate *worst = NULL;
     146                 :            :                 struct dirent *de;
     147                 :          0 :                 uint64_t sum = 0;
     148                 :            : 
     149                 :          0 :                 rewinddir(d);
     150                 :            : 
     151   [ #  #  #  #  :          0 :                 FOREACH_DIRENT(de, d, goto fail) {
                   #  # ]
     152                 :            :                         struct vacuum_candidate *c;
     153                 :            :                         struct stat st;
     154                 :            :                         uid_t uid;
     155                 :            :                         usec_t t;
     156                 :            : 
     157                 :          0 :                         r = uid_from_file_name(de->d_name, &uid);
     158         [ #  # ]:          0 :                         if (r < 0)
     159                 :          0 :                                 continue;
     160                 :            : 
     161         [ #  # ]:          0 :                         if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW) < 0) {
     162         [ #  # ]:          0 :                                 if (errno == ENOENT)
     163                 :          0 :                                         continue;
     164                 :            : 
     165         [ #  # ]:          0 :                                 log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name);
     166                 :          0 :                                 continue;
     167                 :            :                         }
     168                 :            : 
     169         [ #  # ]:          0 :                         if (!S_ISREG(st.st_mode))
     170                 :          0 :                                 continue;
     171                 :            : 
     172         [ #  # ]:          0 :                         if (exclude_fd >= 0 &&
     173         [ #  # ]:          0 :                             exclude_st.st_dev == st.st_dev &&
     174         [ #  # ]:          0 :                             exclude_st.st_ino == st.st_ino)
     175                 :          0 :                                 continue;
     176                 :            : 
     177                 :          0 :                         r = hashmap_ensure_allocated(&h, NULL);
     178         [ #  # ]:          0 :                         if (r < 0)
     179                 :          0 :                                 return log_oom();
     180                 :            : 
     181                 :          0 :                         t = timespec_load(&st.st_mtim);
     182                 :            : 
     183                 :          0 :                         c = hashmap_get(h, UID_TO_PTR(uid));
     184         [ #  # ]:          0 :                         if (c) {
     185                 :            : 
     186         [ #  # ]:          0 :                                 if (t < c->oldest_mtime) {
     187                 :            :                                         char *n;
     188                 :            : 
     189                 :          0 :                                         n = strdup(de->d_name);
     190         [ #  # ]:          0 :                                         if (!n)
     191                 :          0 :                                                 return log_oom();
     192                 :            : 
     193                 :          0 :                                         free(c->oldest_file);
     194                 :          0 :                                         c->oldest_file = n;
     195                 :          0 :                                         c->oldest_mtime = t;
     196                 :            :                                 }
     197                 :            : 
     198                 :            :                         } else {
     199         [ #  # ]:          0 :                                 _cleanup_(vacuum_candidate_freep) struct vacuum_candidate *n = NULL;
     200                 :            : 
     201                 :          0 :                                 n = new0(struct vacuum_candidate, 1);
     202         [ #  # ]:          0 :                                 if (!n)
     203                 :          0 :                                         return log_oom();
     204                 :            : 
     205                 :          0 :                                 n->oldest_file = strdup(de->d_name);
     206         [ #  # ]:          0 :                                 if (!n->oldest_file)
     207                 :          0 :                                         return log_oom();
     208                 :            : 
     209                 :          0 :                                 n->oldest_mtime = t;
     210                 :            : 
     211                 :          0 :                                 r = hashmap_put(h, UID_TO_PTR(uid), n);
     212         [ #  # ]:          0 :                                 if (r < 0)
     213                 :          0 :                                         return log_oom();
     214                 :            : 
     215                 :          0 :                                 c = TAKE_PTR(n);
     216                 :            :                         }
     217                 :            : 
     218                 :          0 :                         c->n_files++;
     219                 :            : 
     220         [ #  # ]:          0 :                         if (!worst ||
     221         [ #  # ]:          0 :                             worst->n_files < c->n_files ||
     222   [ #  #  #  # ]:          0 :                             (worst->n_files == c->n_files && c->oldest_mtime < worst->oldest_mtime))
     223                 :          0 :                                 worst = c;
     224                 :            : 
     225                 :          0 :                         sum += st.st_blocks * 512;
     226                 :            :                 }
     227                 :            : 
     228         [ #  # ]:          0 :                 if (!worst)
     229                 :          0 :                         break;
     230                 :            : 
     231                 :          0 :                 r = vacuum_necessary(dirfd(d), sum, keep_free, max_use);
     232         [ #  # ]:          0 :                 if (r <= 0)
     233                 :          0 :                         return r;
     234                 :            : 
     235                 :          0 :                 r = unlinkat_deallocate(dirfd(d), worst->oldest_file, 0);
     236         [ #  # ]:          0 :                 if (r == -ENOENT)
     237                 :          0 :                         continue;
     238         [ #  # ]:          0 :                 if (r < 0)
     239         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to remove file %s: %m", worst->oldest_file);
     240                 :            : 
     241         [ #  # ]:          0 :                 log_info("Removed old coredump %s.", worst->oldest_file);
     242                 :            :         }
     243                 :            : 
     244                 :          0 :         return 0;
     245                 :            : 
     246                 :          0 : fail:
     247         [ #  # ]:          0 :         return log_error_errno(errno, "Failed to read directory: %m");
     248                 :            : }

Generated by: LCOV version 1.14